From 5dc2599e55fee94a245a47e1a0f7cddd63829040 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Tue, 26 Dec 2023 21:47:27 +0100 Subject: [PATCH] implemented furi from flipper zero added cmsis_core, furi, mlib and nanobake implemented basic app structure from furi implemented basic placeholder apps --- CMakeLists.txt | 2 + components/cmsis_core/CMakeLists.txt | 3 + components/cmsis_core/cmsis_armcc.h | 894 +++ components/cmsis_core/cmsis_armclang.h | 1510 +++++ components/cmsis_core/cmsis_armclang_ltm.h | 1934 ++++++ components/cmsis_core/cmsis_compiler.h | 304 + components/cmsis_core/cmsis_gcc.h | 2217 +++++++ components/cmsis_core/cmsis_gcc_esp32.h | 2221 +++++++ components/cmsis_core/cmsis_iccarm.h | 1008 ++++ components/cmsis_core/cmsis_tiarmclang.h | 1510 +++++ components/cmsis_core/cmsis_version.h | 39 + components/cmsis_core/core_cm4.h | 2170 +++++++ components/cmsis_core/mpu_armv7.h | 275 + components/furi/CMakeLists.txt | 5 + components/furi/LICENSE.md | 636 ++ components/furi/src/base.h | 44 + components/furi/src/check.c | 194 + components/furi/src/check.h | 109 + components/furi/src/common_defines.h | 60 + components/furi/src/core_defines.h | 116 + components/furi/src/critical.c | 34 + components/furi/src/event_flag.c | 140 + components/furi/src/event_flag.h | 70 + components/furi/src/furi_config.h | 3 + components/furi/src/furi_hal_console.c | 109 + components/furi/src/furi_hal_console.h | 37 + components/furi/src/furi_string.c | 304 + components/furi/src/furi_string.h | 738 +++ components/furi/src/kernel.c | 202 + components/furi/src/kernel.h | 128 + components/furi/src/m_cstr_dup.h | 26 + components/furi/src/message_queue.c | 182 + components/furi/src/message_queue.h | 95 + components/furi/src/mutex.c | 125 + components/furi/src/mutex.h | 62 + components/furi/src/pubsub.c | 94 + components/furi/src/pubsub.h | 68 + components/furi/src/record.c | 145 + components/furi/src/record.h | 67 + components/furi/src/semaphore.c | 116 + components/furi/src/semaphore.h | 58 + components/furi/src/stream_buffer.c | 86 + components/furi/src/stream_buffer.h | 152 + components/furi/src/thread.c | 655 ++ components/furi/src/thread.h | 338 ++ components/furi/src/timer.c | 172 + components/furi/src/timer.h | 106 + components/mlib/CMakeLists.txt | 3 + components/mlib/LICENSE | 25 + components/mlib/README.md | 3 + components/mlib/m-algo.h | 1240 ++++ components/mlib/m-array.h | 1132 ++++ components/mlib/m-atomic.h | 342 ++ components/mlib/m-bitset.h | 981 +++ components/mlib/m-bptree.h | 1499 +++++ components/mlib/m-buffer.h | 1350 +++++ components/mlib/m-c-mempool.h | 835 +++ components/mlib/m-concurrent.h | 925 +++ components/mlib/m-core.h | 5297 +++++++++++++++++ components/mlib/m-deque.h | 1147 ++++ components/mlib/m-dict.h | 1988 +++++++ components/mlib/m-funcobj.h | 415 ++ components/mlib/m-genint.h | 247 + components/mlib/m-i-list.h | 679 +++ components/mlib/m-i-shared.h | 255 + components/mlib/m-list.h | 1527 +++++ components/mlib/m-mempool.h | 206 + components/mlib/m-mutex.h | 28 + components/mlib/m-prioqueue.h | 520 ++ components/mlib/m-rbtree.h | 1186 ++++ components/mlib/m-serial-bin.h | 552 ++ components/mlib/m-serial-json.h | 1199 ++++ components/mlib/m-shared.h | 553 ++ components/mlib/m-snapshot.h | 814 +++ components/mlib/m-string.h | 2787 +++++++++ components/mlib/m-thread.h | 748 +++ components/mlib/m-tree.h | 1603 +++++ components/mlib/m-try.h | 578 ++ components/mlib/m-tuple.h | 784 +++ components/mlib/m-variant.h | 819 +++ components/mlib/m-worker.h | 698 +++ components/nanobake/CMakeLists.txt | 6 +- components/nanobake/inc/nanobake.h | 20 +- components/nanobake/inc/nb_app.h | 49 +- components/nanobake/inc/nb_config.h | 26 + components/nanobake/inc/nb_display.h | 13 +- components/nanobake/inc/nb_hardware.h | 24 + components/nanobake/inc/nb_platform.h | 42 - components/nanobake/inc/nb_touch.h | 13 +- .../main/system_info/system_info.c | 47 +- .../main/system_info/system_info.h | 11 +- .../src/applications/nb_applications.c | 28 + .../src/applications/nb_applications.h | 22 + .../applications/services/desktop/desktop.c | 35 + .../applications/services/desktop/desktop.h | 13 + .../src/applications/services/gui/gui.c | 18 + .../src/applications/services/gui/gui.h | 13 + .../src/applications/services/loader/loader.c | 17 + .../src/applications/services/loader/loader.h | 13 + components/nanobake/src/nanobake.c | 107 +- components/nanobake/src/nb_app.c | 32 - components/nanobake/src/nb_assert.h | 20 - components/nanobake/src/nb_display.c | 6 +- components/nanobake/src/nb_hardware.c | 32 + components/nanobake/src/nb_internal.c | 3 - components/nanobake/src/nb_internal.h | 6 - components/nanobake/src/nb_lvgl.c | 54 + components/nanobake/src/nb_lvgl.h | 21 + components/nanobake/src/nb_platform.c | 81 - components/nanobake/src/nb_touch.c | 7 +- main/CMakeLists.txt | 1 + main/src/hello_world/hello_world.c | 47 +- main/src/hello_world/hello_world.h | 7 +- main/src/main.c | 4 +- 114 files changed, 53069 insertions(+), 297 deletions(-) create mode 100644 components/cmsis_core/CMakeLists.txt create mode 100644 components/cmsis_core/cmsis_armcc.h create mode 100644 components/cmsis_core/cmsis_armclang.h create mode 100644 components/cmsis_core/cmsis_armclang_ltm.h create mode 100644 components/cmsis_core/cmsis_compiler.h create mode 100644 components/cmsis_core/cmsis_gcc.h create mode 100644 components/cmsis_core/cmsis_gcc_esp32.h create mode 100644 components/cmsis_core/cmsis_iccarm.h create mode 100644 components/cmsis_core/cmsis_tiarmclang.h create mode 100644 components/cmsis_core/cmsis_version.h create mode 100644 components/cmsis_core/core_cm4.h create mode 100644 components/cmsis_core/mpu_armv7.h create mode 100644 components/furi/CMakeLists.txt create mode 100644 components/furi/LICENSE.md create mode 100644 components/furi/src/base.h create mode 100644 components/furi/src/check.c create mode 100644 components/furi/src/check.h create mode 100644 components/furi/src/common_defines.h create mode 100644 components/furi/src/core_defines.h create mode 100644 components/furi/src/critical.c create mode 100644 components/furi/src/event_flag.c create mode 100644 components/furi/src/event_flag.h create mode 100644 components/furi/src/furi_config.h create mode 100644 components/furi/src/furi_hal_console.c create mode 100644 components/furi/src/furi_hal_console.h create mode 100644 components/furi/src/furi_string.c create mode 100644 components/furi/src/furi_string.h create mode 100644 components/furi/src/kernel.c create mode 100644 components/furi/src/kernel.h create mode 100644 components/furi/src/m_cstr_dup.h create mode 100644 components/furi/src/message_queue.c create mode 100644 components/furi/src/message_queue.h create mode 100644 components/furi/src/mutex.c create mode 100644 components/furi/src/mutex.h create mode 100644 components/furi/src/pubsub.c create mode 100644 components/furi/src/pubsub.h create mode 100644 components/furi/src/record.c create mode 100644 components/furi/src/record.h create mode 100644 components/furi/src/semaphore.c create mode 100644 components/furi/src/semaphore.h create mode 100644 components/furi/src/stream_buffer.c create mode 100644 components/furi/src/stream_buffer.h create mode 100644 components/furi/src/thread.c create mode 100644 components/furi/src/thread.h create mode 100644 components/furi/src/timer.c create mode 100644 components/furi/src/timer.h create mode 100644 components/mlib/CMakeLists.txt create mode 100644 components/mlib/LICENSE create mode 100644 components/mlib/README.md create mode 100644 components/mlib/m-algo.h create mode 100644 components/mlib/m-array.h create mode 100644 components/mlib/m-atomic.h create mode 100644 components/mlib/m-bitset.h create mode 100644 components/mlib/m-bptree.h create mode 100644 components/mlib/m-buffer.h create mode 100644 components/mlib/m-c-mempool.h create mode 100644 components/mlib/m-concurrent.h create mode 100644 components/mlib/m-core.h create mode 100644 components/mlib/m-deque.h create mode 100644 components/mlib/m-dict.h create mode 100644 components/mlib/m-funcobj.h create mode 100644 components/mlib/m-genint.h create mode 100644 components/mlib/m-i-list.h create mode 100644 components/mlib/m-i-shared.h create mode 100644 components/mlib/m-list.h create mode 100644 components/mlib/m-mempool.h create mode 100644 components/mlib/m-mutex.h create mode 100644 components/mlib/m-prioqueue.h create mode 100644 components/mlib/m-rbtree.h create mode 100644 components/mlib/m-serial-bin.h create mode 100644 components/mlib/m-serial-json.h create mode 100644 components/mlib/m-shared.h create mode 100644 components/mlib/m-snapshot.h create mode 100644 components/mlib/m-string.h create mode 100644 components/mlib/m-thread.h create mode 100644 components/mlib/m-tree.h create mode 100644 components/mlib/m-try.h create mode 100644 components/mlib/m-tuple.h create mode 100644 components/mlib/m-variant.h create mode 100644 components/mlib/m-worker.h create mode 100644 components/nanobake/inc/nb_config.h create mode 100644 components/nanobake/inc/nb_hardware.h delete mode 100644 components/nanobake/inc/nb_platform.h create mode 100644 components/nanobake/src/applications/nb_applications.c create mode 100644 components/nanobake/src/applications/nb_applications.h create mode 100644 components/nanobake/src/applications/services/desktop/desktop.c create mode 100644 components/nanobake/src/applications/services/desktop/desktop.h create mode 100644 components/nanobake/src/applications/services/gui/gui.c create mode 100644 components/nanobake/src/applications/services/gui/gui.h create mode 100644 components/nanobake/src/applications/services/loader/loader.c create mode 100644 components/nanobake/src/applications/services/loader/loader.h delete mode 100644 components/nanobake/src/nb_app.c delete mode 100644 components/nanobake/src/nb_assert.h create mode 100644 components/nanobake/src/nb_hardware.c delete mode 100644 components/nanobake/src/nb_internal.c delete mode 100644 components/nanobake/src/nb_internal.h create mode 100644 components/nanobake/src/nb_lvgl.c create mode 100644 components/nanobake/src/nb_lvgl.h delete mode 100644 components/nanobake/src/nb_platform.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a8f6433..464588f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.16) +add_definitions(-DFURI_DEBUG) + set(COMPONENTS main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) diff --git a/components/cmsis_core/CMakeLists.txt b/components/cmsis_core/CMakeLists.txt new file mode 100644 index 00000000..3dd9945e --- /dev/null +++ b/components/cmsis_core/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register( + INCLUDE_DIRS "." +) \ No newline at end of file diff --git a/components/cmsis_core/cmsis_armcc.h b/components/cmsis_core/cmsis_armcc.h new file mode 100644 index 00000000..2edff5af --- /dev/null +++ b/components/cmsis_core/cmsis_armcc.h @@ -0,0 +1,894 @@ +/**************************************************************************//** + * @file cmsis_armcc.h + * @brief CMSIS compiler ARMCC (Arm Compiler 5) header file + * @version V5.4.0 + * @date 20. January 2023 + ******************************************************************************/ +/* + * Copyright (c) 2009-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_ARMCC_H +#define __CMSIS_ARMCC_H + + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677) + #error "Please use Arm Compiler Toolchain V4.0.677 or later!" +#endif + +/* CMSIS compiler control architecture macros */ +#if ((defined (__TARGET_ARCH_6_M ) && (__TARGET_ARCH_6_M == 1)) || \ + (defined (__TARGET_ARCH_6S_M ) && (__TARGET_ARCH_6S_M == 1)) ) + #define __ARM_ARCH_6M__ 1 +#endif + +#if (defined (__TARGET_ARCH_7_M ) && (__TARGET_ARCH_7_M == 1)) + #define __ARM_ARCH_7M__ 1 +#endif + +#if (defined (__TARGET_ARCH_7E_M) && (__TARGET_ARCH_7E_M == 1)) + #define __ARM_ARCH_7EM__ 1 +#endif + + /* __ARM_ARCH_8M_BASE__ not applicable */ + /* __ARM_ARCH_8M_MAIN__ not applicable */ + /* __ARM_ARCH_8_1M_MAIN__ not applicable */ + +/* CMSIS compiler control DSP macros */ +#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + #define __ARM_FEATURE_DSP 1 +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE static __forceinline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __declspec(noreturn) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed)) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT __packed struct +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION __packed union +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #define __UNALIGNED_UINT32(x) (*((__packed uint32_t *)(x))) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #define __UNALIGNED_UINT16_WRITE(addr, val) ((*((__packed uint16_t *)(addr))) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #define __UNALIGNED_UINT16_READ(addr) (*((const __packed uint16_t *)(addr))) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #define __UNALIGNED_UINT32_WRITE(addr, val) ((*((__packed uint32_t *)(addr))) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #define __UNALIGNED_UINT32_READ(addr) (*((const __packed uint32_t *)(addr))) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __memory_changed() +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"), zero_init)) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START +#define __PROGRAM_START __main +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP Image$$ARM_LIB_STACK$$ZI$$Limit +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT Image$$ARM_LIB_STACK$$ZI$$Base +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section("RESET"))) +#endif + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __nop + + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI __wfi + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __wfe + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __sev + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +#define __ISB() __isb(0xF) + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __dsb(0xF) + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __dmb(0xF) + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV __rev + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(uint32_t value) +{ + rev16 r0, r0 + bx lr +} +#endif + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int16_t __REVSH(int16_t value) +{ + revsh r0, r0 + bx lr +} +#endif + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +#define __ROR __ror + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __breakpoint(value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + #define __RBIT __rbit +#else +__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ + return result; +} +#endif + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +#define __CLZ __clz + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __LDREXB(ptr) ((uint8_t ) __ldrex(ptr)) +#else + #define __LDREXB(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint8_t ) __ldrex(ptr)) _Pragma("pop") +#endif + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __LDREXH(ptr) ((uint16_t) __ldrex(ptr)) +#else + #define __LDREXH(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint16_t) __ldrex(ptr)) _Pragma("pop") +#endif + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __LDREXW(ptr) ((uint32_t ) __ldrex(ptr)) +#else + #define __LDREXW(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint32_t ) __ldrex(ptr)) _Pragma("pop") +#endif + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __STREXB(value, ptr) __strex(value, ptr) +#else + #define __STREXB(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") +#endif + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __STREXH(value, ptr) __strex(value, ptr) +#else + #define __STREXH(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") +#endif + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __STREXW(value, ptr) __strex(value, ptr) +#else + #define __STREXW(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") +#endif + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +#define __CLREX __clrex + + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __ssat + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __usat + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint32_t value) +{ + rrx r0, r0 + bx lr +} +#endif + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDRBT(ptr) ((uint8_t ) __ldrt(ptr)) + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDRHT(ptr) ((uint16_t) __ldrt(ptr)) + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDRT(ptr) ((uint32_t ) __ldrt(ptr)) + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRBT(value, ptr) __strt(value, ptr) + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRHT(value, ptr) __strt(value, ptr) + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRT(value, ptr) __strt(value, ptr) + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__attribute__((always_inline)) __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +/* intrinsic void __enable_irq(); */ + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +/* intrinsic void __disable_irq(); */ + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_INLINE uint32_t __get_CONTROL(void) +{ + register uint32_t __regControl __ASM("control"); + return(__regControl); +} + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + register uint32_t __regControl __ASM("control"); + __regControl = control; + __ISB(); +} + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_INLINE uint32_t __get_IPSR(void) +{ + register uint32_t __regIPSR __ASM("ipsr"); + return(__regIPSR); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_INLINE uint32_t __get_APSR(void) +{ + register uint32_t __regAPSR __ASM("apsr"); + return(__regAPSR); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_INLINE uint32_t __get_xPSR(void) +{ + register uint32_t __regXPSR __ASM("xpsr"); + return(__regXPSR); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_INLINE uint32_t __get_PSP(void) +{ + register uint32_t __regProcessStackPointer __ASM("psp"); + return(__regProcessStackPointer); +} + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + register uint32_t __regProcessStackPointer __ASM("psp"); + __regProcessStackPointer = topOfProcStack; +} + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_INLINE uint32_t __get_MSP(void) +{ + register uint32_t __regMainStackPointer __ASM("msp"); + return(__regMainStackPointer); +} + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + register uint32_t __regMainStackPointer __ASM("msp"); + __regMainStackPointer = topOfMainStack; +} + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + register uint32_t __regPriMask __ASM("primask"); + return(__regPriMask); +} + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + register uint32_t __regPriMask __ASM("primask"); + __regPriMask = (priMask); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +#define __enable_fault_irq __enable_fiq + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +#define __disable_fault_irq __disable_fiq + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + register uint32_t __regBasePri __ASM("basepri"); + return(__regBasePri); +} + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_INLINE void __set_BASEPRI(uint32_t basePri) +{ + register uint32_t __regBasePri __ASM("basepri"); + __regBasePri = (basePri & 0xFFU); +} + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_INLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + register uint32_t __regBasePriMax __ASM("basepri_max"); + __regBasePriMax = (basePri & 0xFFU); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + register uint32_t __regFaultMask __ASM("faultmask"); + return(__regFaultMask); +} + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + register uint32_t __regFaultMask __ASM("faultmask"); + __regFaultMask = (faultMask & (uint32_t)1U); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) + register uint32_t __regfpscr __ASM("fpscr"); + return(__regfpscr); +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) + register uint32_t __regfpscr __ASM("fpscr"); + __regfpscr = (fpscr); +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + +#define __SADD8 __sadd8 +#define __QADD8 __qadd8 +#define __SHADD8 __shadd8 +#define __UADD8 __uadd8 +#define __UQADD8 __uqadd8 +#define __UHADD8 __uhadd8 +#define __SSUB8 __ssub8 +#define __QSUB8 __qsub8 +#define __SHSUB8 __shsub8 +#define __USUB8 __usub8 +#define __UQSUB8 __uqsub8 +#define __UHSUB8 __uhsub8 +#define __SADD16 __sadd16 +#define __QADD16 __qadd16 +#define __SHADD16 __shadd16 +#define __UADD16 __uadd16 +#define __UQADD16 __uqadd16 +#define __UHADD16 __uhadd16 +#define __SSUB16 __ssub16 +#define __QSUB16 __qsub16 +#define __SHSUB16 __shsub16 +#define __USUB16 __usub16 +#define __UQSUB16 __uqsub16 +#define __UHSUB16 __uhsub16 +#define __SASX __sasx +#define __QASX __qasx +#define __SHASX __shasx +#define __UASX __uasx +#define __UQASX __uqasx +#define __UHASX __uhasx +#define __SSAX __ssax +#define __QSAX __qsax +#define __SHSAX __shsax +#define __USAX __usax +#define __UQSAX __uqsax +#define __UHSAX __uhsax +#define __USAD8 __usad8 +#define __USADA8 __usada8 +#define __SSAT16 __ssat16 +#define __USAT16 __usat16 +#define __UXTB16 __uxtb16 +#define __UXTAB16 __uxtab16 +#define __SXTB16 __sxtb16 +#define __SXTAB16 __sxtab16 +#define __SMUAD __smuad +#define __SMUADX __smuadx +#define __SMLAD __smlad +#define __SMLADX __smladx +#define __SMLALD __smlald +#define __SMLALDX __smlaldx +#define __SMUSD __smusd +#define __SMUSDX __smusdx +#define __SMLSD __smlsd +#define __SMLSDX __smlsdx +#define __SMLSLD __smlsld +#define __SMLSLDX __smlsldx +#define __SEL __sel +#define __QADD __qadd +#define __QSUB __qsub + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SMMLA(ARG1,ARG2,ARG3) ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \ + ((int64_t)(ARG3) << 32U) ) >> 32U)) + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +#endif /* ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#endif /* __CMSIS_ARMCC_H */ diff --git a/components/cmsis_core/cmsis_armclang.h b/components/cmsis_core/cmsis_armclang.h new file mode 100644 index 00000000..139923da --- /dev/null +++ b/components/cmsis_core/cmsis_armclang.h @@ -0,0 +1,1510 @@ +/**************************************************************************//** + * @file cmsis_armclang.h + * @brief CMSIS compiler armclang (Arm Compiler 6) header file + * @version V5.5.0 + * @date 20. January 2023 + ******************************************************************************/ +/* + * Copyright (c) 2009-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */ + +#ifndef __CMSIS_ARMCLANG_H +#define __CMSIS_ARMCLANG_H + +#pragma clang system_header /* treat file as system include file */ + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */ + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */ + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START +#define __PROGRAM_START __main +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP Image$$ARM_LIB_STACK$$ZI$$Limit +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT Image$$ARM_LIB_STACK$$ZI$$Base +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section("RESET"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL Image$$STACKSEAL$$ZI$$Base +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __builtin_arm_nop + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI __builtin_arm_wfi + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __builtin_arm_wfe + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __builtin_arm_sev + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +#define __ISB() __builtin_arm_isb(0xF) + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __builtin_arm_dsb(0xF) + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __builtin_arm_dmb(0xF) + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV(value) __builtin_bswap32(value) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV16(value) __ROR(__REV(value), 16) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REVSH(value) (int16_t)__builtin_bswap16(value) + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __builtin_arm_rbit + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB (uint8_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH (uint16_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW (uint32_t)__builtin_arm_ldrex + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW (uint32_t)__builtin_arm_strex + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +#define __CLREX __builtin_arm_clrex + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __builtin_arm_ssat + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __builtin_arm_usat + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDAEXB (uint8_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDAEXH (uint16_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDAEX (uint32_t)__builtin_arm_ldaex + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXB (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXH (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEX (uint32_t)__builtin_arm_stlex + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** @}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} +#endif + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} +#endif + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __get_FPSCR (uint32_t)__builtin_arm_get_fpscr +#else +#define __get_FPSCR() ((uint32_t)0U) +#endif + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __set_FPSCR __builtin_arm_set_fpscr +#else +#define __set_FPSCR(fpscr) ((void)(fpscr)) +#endif + + +/** @} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +#define __SADD8 __builtin_arm_sadd8 +#define __QADD8 __builtin_arm_qadd8 +#define __SHADD8 __builtin_arm_shadd8 +#define __UADD8 __builtin_arm_uadd8 +#define __UQADD8 __builtin_arm_uqadd8 +#define __UHADD8 __builtin_arm_uhadd8 +#define __SSUB8 __builtin_arm_ssub8 +#define __QSUB8 __builtin_arm_qsub8 +#define __SHSUB8 __builtin_arm_shsub8 +#define __USUB8 __builtin_arm_usub8 +#define __UQSUB8 __builtin_arm_uqsub8 +#define __UHSUB8 __builtin_arm_uhsub8 +#define __SADD16 __builtin_arm_sadd16 +#define __QADD16 __builtin_arm_qadd16 +#define __SHADD16 __builtin_arm_shadd16 +#define __UADD16 __builtin_arm_uadd16 +#define __UQADD16 __builtin_arm_uqadd16 +#define __UHADD16 __builtin_arm_uhadd16 +#define __SSUB16 __builtin_arm_ssub16 +#define __QSUB16 __builtin_arm_qsub16 +#define __SHSUB16 __builtin_arm_shsub16 +#define __USUB16 __builtin_arm_usub16 +#define __UQSUB16 __builtin_arm_uqsub16 +#define __UHSUB16 __builtin_arm_uhsub16 +#define __SASX __builtin_arm_sasx +#define __QASX __builtin_arm_qasx +#define __SHASX __builtin_arm_shasx +#define __UASX __builtin_arm_uasx +#define __UQASX __builtin_arm_uqasx +#define __UHASX __builtin_arm_uhasx +#define __SSAX __builtin_arm_ssax +#define __QSAX __builtin_arm_qsax +#define __SHSAX __builtin_arm_shsax +#define __USAX __builtin_arm_usax +#define __UQSAX __builtin_arm_uqsax +#define __UHSAX __builtin_arm_uhsax +#define __USAD8 __builtin_arm_usad8 +#define __USADA8 __builtin_arm_usada8 +#define __SSAT16 __builtin_arm_ssat16 +#define __USAT16 __builtin_arm_usat16 +#define __UXTB16 __builtin_arm_uxtb16 +#define __UXTAB16 __builtin_arm_uxtab16 +#define __SXTB16 __builtin_arm_sxtb16 +#define __SXTAB16 __builtin_arm_sxtab16 +#define __SMUAD __builtin_arm_smuad +#define __SMUADX __builtin_arm_smuadx +#define __SMLAD __builtin_arm_smlad +#define __SMLADX __builtin_arm_smladx +#define __SMLALD __builtin_arm_smlald +#define __SMLALDX __builtin_arm_smlaldx +#define __SMUSD __builtin_arm_smusd +#define __SMUSDX __builtin_arm_smusdx +#define __SMLSD __builtin_arm_smlsd +#define __SMLSDX __builtin_arm_smlsdx +#define __SMLSLD __builtin_arm_smlsld +#define __SMLSLDX __builtin_arm_smlsldx +#define __SEL __builtin_arm_sel +#define __QADD __builtin_arm_qadd +#define __QSUB __builtin_arm_qsub + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/** @} end of group CMSIS_SIMD_intrinsics */ + + +#endif /* __CMSIS_ARMCLANG_H */ diff --git a/components/cmsis_core/cmsis_armclang_ltm.h b/components/cmsis_core/cmsis_armclang_ltm.h new file mode 100644 index 00000000..477136e8 --- /dev/null +++ b/components/cmsis_core/cmsis_armclang_ltm.h @@ -0,0 +1,1934 @@ +/**************************************************************************//** + * @file cmsis_armclang_ltm.h + * @brief CMSIS compiler armclang (Arm Compiler 6) header file + * @version V1.6.0 + * @date 20. January 2023 + ******************************************************************************/ +/* + * Copyright (c) 2018-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */ + +#ifndef __CMSIS_ARMCLANG_H +#define __CMSIS_ARMCLANG_H + +#pragma clang system_header /* treat file as system include file */ + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */ + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */ + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START +#define __PROGRAM_START __main +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP Image$$ARM_LIB_STACK$$ZI$$Limit +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT Image$$ARM_LIB_STACK$$ZI$$Base +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section("RESET"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL Image$$STACKSEAL$$ZI$$Base +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __builtin_arm_nop + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI __builtin_arm_wfi + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __builtin_arm_wfe + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __builtin_arm_sev + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +#define __ISB() __builtin_arm_isb(0xF) + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __builtin_arm_dsb(0xF) + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __builtin_arm_dmb(0xF) + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV(value) __builtin_bswap32(value) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV16(value) __ROR(__REV(value), 16) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REVSH(value) (int16_t)__builtin_bswap16(value) + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __builtin_arm_rbit + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB (uint8_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH (uint16_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW (uint32_t)__builtin_arm_ldrex + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW (uint32_t)__builtin_arm_strex + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +#define __CLREX __builtin_arm_clrex + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __builtin_arm_ssat + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __builtin_arm_usat + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDAEXB (uint8_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDAEXH (uint16_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDAEX (uint32_t)__builtin_arm_ldaex + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXB (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXH (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEX (uint32_t)__builtin_arm_stlex + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} +#endif + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} +#endif + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __get_FPSCR (uint32_t)__builtin_arm_get_fpscr +#else +#define __get_FPSCR() ((uint32_t)0U) +#endif + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __set_FPSCR __builtin_arm_set_fpscr +#else +#define __set_FPSCR(x) ((void)(x)) +#endif + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1,ARG2) \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +#define __USAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#endif /* __CMSIS_ARMCLANG_H */ diff --git a/components/cmsis_core/cmsis_compiler.h b/components/cmsis_core/cmsis_compiler.h new file mode 100644 index 00000000..f2663f60 --- /dev/null +++ b/components/cmsis_core/cmsis_compiler.h @@ -0,0 +1,304 @@ +/**************************************************************************//** + * @file cmsis_compiler.h + * @brief CMSIS compiler generic header file + * @version V5.3.0 + * @date 04. April 2023 + ******************************************************************************/ +/* + * Copyright (c) 2009-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_COMPILER_H +#define __CMSIS_COMPILER_H + +#include + +/* + * Arm Compiler 4/5 + */ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + + +/* + * Arm Compiler 6.6 LTM (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) && (__ARMCC_VERSION < 6100100) + #include "cmsis_armclang_ltm.h" + + /* + * Arm Compiler above 6.10.1 (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100) + #include "cmsis_armclang.h" + +/* + * TI Arm Clang Compiler (tiarmclang) + */ +#elif defined (__ti__) + #include "cmsis_tiarmclang.h" + +/* + * GNU Compiler + */ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc_esp32.h" +// #include "cmsis_gcc.h" + + +/* + * IAR Compiler + */ +#elif defined ( __ICCARM__ ) + #include + + +/* + * TI Arm Compiler (armcl) + */ +#elif defined ( __TI_ARM__ ) + #include + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __attribute__((packed)) + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed)) + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed)) + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) + #endif + #ifndef __RESTRICT + #define __RESTRICT __restrict + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + #ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) + #endif + #ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) + #endif + +/* + * TASKING Compiler + */ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __packed__ + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __packed__ + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __packed__ + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __packed__ T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __align(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + #ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) + #endif + #ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) + #endif + +/* + * COSMIC Compiler + */ +#elif defined ( __CSMC__ ) + #include + + #ifndef __ASM + #define __ASM _asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + // NO RETURN is automatically detected hence no warning here + #define __NO_RETURN + #endif + #ifndef __USED + #warning No compiler specific solution for __USED. __USED is ignored. + #define __USED + #endif + #ifndef __WEAK + #define __WEAK __weak + #endif + #ifndef __PACKED + #define __PACKED @packed + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT @packed struct + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION @packed union + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + @packed struct T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored. + #define __ALIGNED(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + #ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) + #endif + #ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) + #endif + +#else + #error Unknown compiler. +#endif + + +#endif /* __CMSIS_COMPILER_H */ + diff --git a/components/cmsis_core/cmsis_gcc.h b/components/cmsis_core/cmsis_gcc.h new file mode 100644 index 00000000..4f0762d6 --- /dev/null +++ b/components/cmsis_core/cmsis_gcc.h @@ -0,0 +1,2217 @@ +/**************************************************************************//** + * @file cmsis_gcc.h + * @brief CMSIS compiler GCC header file + * @version V5.4.2 + * @date 17. December 2022 + ******************************************************************************/ +/* + * Copyright (c) 2009-2021 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_GCC_H +#define __CMSIS_GCC_H + +/* ignore some GCC warnings */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* Fallback for __has_builtin */ +#ifndef __has_builtin + #define __has_builtin(x) (0) +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START + +/** + \brief Initializes data and bss sections + \details This default implementations initialized all data and additional bss + sections relying on .copy.table and .zero.table specified properly + in the used linker script. + + */ +__STATIC_FORCEINLINE __NO_RETURN void __cmsis_start(void) +{ + extern void _start(void) __NO_RETURN; + + typedef struct __copy_table { + uint32_t const* src; + uint32_t* dest; + uint32_t wlen; + } __copy_table_t; + + typedef struct __zero_table { + uint32_t* dest; + uint32_t wlen; + } __zero_table_t; + + extern const __copy_table_t __copy_table_start__; + extern const __copy_table_t __copy_table_end__; + extern const __zero_table_t __zero_table_start__; + extern const __zero_table_t __zero_table_end__; + + for (__copy_table_t const* pTable = &__copy_table_start__; pTable < &__copy_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = pTable->src[i]; + } + } + + for (__zero_table_t const* pTable = &__zero_table_start__; pTable < &__zero_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = 0u; + } + } + + _start(); +} + +#define __PROGRAM_START __cmsis_start +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP __StackTop +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT __StackLimit +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section(".vectors"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL __StackSeal +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP() __ASM volatile ("nop") + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI() __ASM volatile ("wfi":::"memory") + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE() __ASM volatile ("wfe":::"memory") + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV() __ASM volatile ("sev") + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +__STATIC_FORCEINLINE void __ISB(void) +{ + __ASM volatile ("isb 0xF":::"memory"); +} + + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); +} + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__STATIC_FORCEINLINE void __DMB(void) +{ + __ASM volatile ("dmb 0xF":::"memory"); +} + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE int16_t __REVSH(int16_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (int16_t)__builtin_bswap16(value); +#else + int16_t result; + + __ASM ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM ("rbit %0, %1" : "=r" (result) : "r" (value) ); +#else + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ +#endif + return result; +} + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM GCC 7.3 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +__STATIC_FORCEINLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1, ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1, ARG2) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAEXB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexb %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAEXH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexh %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDAEX(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaex %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexb %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexh %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlex %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_FORCEINLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_get_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + return __builtin_arm_get_fpscr(); +#else + uint32_t result; + + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + return(result); +#endif +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_FORCEINLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_set_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + __builtin_arm_set_fpscr(fpscr); +#else + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc", "memory"); +#endif +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1, ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + +#define __USAT16(ARG1, ARG2) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16_RORn(uint32_t op1, uint32_t rotate) +{ + uint32_t result; + if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) { + __ASM volatile ("sxtb16 %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) ); + } else { + result = __SXTB16(__ROR(op1, rotate)) ; + } + return result; +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16_RORn(uint32_t op1, uint32_t op2, uint32_t rotate) +{ + uint32_t result; + if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) { + __ASM volatile ("sxtab16 %0, %1, %2, ROR %3" : "=r" (result) : "r" (op1) , "r" (op2) , "i" (rotate)); + } else { + result = __SXTAB16(op1, __ROR(op2, rotate)); + } + return result; +} + + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +#define __PKHBT(ARG1,ARG2,ARG3) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#pragma GCC diagnostic pop + +#endif /* __CMSIS_GCC_H */ diff --git a/components/cmsis_core/cmsis_gcc_esp32.h b/components/cmsis_core/cmsis_gcc_esp32.h new file mode 100644 index 00000000..12fdcb89 --- /dev/null +++ b/components/cmsis_core/cmsis_gcc_esp32.h @@ -0,0 +1,2221 @@ +/**************************************************************************//** + * @file cmsis_gcc.h + * @brief CMSIS compiler GCC header file + * @version V5.4.2 + * @date 17. December 2022 + ******************************************************************************/ +/* + * Copyright (c) 2009-2021 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_GCC_H +#define __CMSIS_GCC_H + +/* ignore some GCC warnings */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* Fallback for __has_builtin */ +#ifndef __has_builtin + #define __has_builtin(x) (0) +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START + +/** + \brief Initializes data and bss sections + \details This default implementations initialized all data and additional bss + sections relying on .copy.table and .zero.table specified properly + in the used linker script. + + */ +__STATIC_FORCEINLINE __NO_RETURN void __cmsis_start(void) +{ + extern void _start(void) __NO_RETURN; + + typedef struct __copy_table { + uint32_t const* src; + uint32_t* dest; + uint32_t wlen; + } __copy_table_t; + + typedef struct __zero_table { + uint32_t* dest; + uint32_t wlen; + } __zero_table_t; + + extern const __copy_table_t __copy_table_start__; + extern const __copy_table_t __copy_table_end__; + extern const __zero_table_t __zero_table_start__; + extern const __zero_table_t __zero_table_end__; + + for (__copy_table_t const* pTable = &__copy_table_start__; pTable < &__copy_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = pTable->src[i]; + } + } + + for (__zero_table_t const* pTable = &__zero_table_start__; pTable < &__zero_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = 0u; + } + } + + _start(); +} + +#define __PROGRAM_START __cmsis_start +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP __StackTop +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT __StackLimit +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section(".vectors"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL __StackSeal +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP() __ASM volatile ("nop") + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI() __ASM volatile ("wfi":::"memory") + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE() __ASM volatile ("wfe":::"memory") + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV() __ASM volatile ("sev") + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +__STATIC_FORCEINLINE void __ISB(void) +{ + __ASM volatile ("isb 0xF":::"memory"); +} + + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); +} + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__STATIC_FORCEINLINE void __DMB(void) +{ + __ASM volatile ("dmb 0xF":::"memory"); +} + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE int16_t __REVSH(int16_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (int16_t)__builtin_bswap16(value); +#else + int16_t result; + + __ASM ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM ("rbit %0, %1" : "=r" (result) : "r" (value) ); +#else + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ +#endif + return result; +} + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM GCC 7.3 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +__STATIC_FORCEINLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1, ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1, ARG2) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAEXB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexb %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAEXH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexh %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDAEX(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaex %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexb %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexh %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlex %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_irq(void) +{ + // TODO esp +// __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_irq(void) +{ + // TODO esp +// __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + result = 0; // TODO esp +// __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + result = 1U; // TODO esp +// __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_FORCEINLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_get_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + return __builtin_arm_get_fpscr(); +#else + uint32_t result; + + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + return(result); +#endif +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_FORCEINLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_set_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + __builtin_arm_set_fpscr(fpscr); +#else + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc", "memory"); +#endif +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1, ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + +#define __USAT16(ARG1, ARG2) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16_RORn(uint32_t op1, uint32_t rotate) +{ + uint32_t result; + if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) { + __ASM volatile ("sxtb16 %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) ); + } else { + result = __SXTB16(__ROR(op1, rotate)) ; + } + return result; +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16_RORn(uint32_t op1, uint32_t op2, uint32_t rotate) +{ + uint32_t result; + if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) { + __ASM volatile ("sxtab16 %0, %1, %2, ROR %3" : "=r" (result) : "r" (op1) , "r" (op2) , "i" (rotate)); + } else { + result = __SXTAB16(op1, __ROR(op2, rotate)); + } + return result; +} + + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +#define __PKHBT(ARG1,ARG2,ARG3) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#pragma GCC diagnostic pop + +#endif /* __CMSIS_GCC_H */ diff --git a/components/cmsis_core/cmsis_iccarm.h b/components/cmsis_core/cmsis_iccarm.h new file mode 100644 index 00000000..47d6a859 --- /dev/null +++ b/components/cmsis_core/cmsis_iccarm.h @@ -0,0 +1,1008 @@ +/**************************************************************************//** + * @file cmsis_iccarm.h + * @brief CMSIS compiler ICCARM (IAR Compiler for Arm) header file + * @version V5.4.0 + * @date 20. January 2023 + ******************************************************************************/ + +//------------------------------------------------------------------------------ +// +// Copyright (c) 2017-2021 IAR Systems +// Copyright (c) 2017-2023 Arm Limited. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//------------------------------------------------------------------------------ + + +#ifndef __CMSIS_ICCARM_H__ +#define __CMSIS_ICCARM_H__ + +#ifndef __ICCARM__ + #error This file should only be compiled by ICCARM +#endif + +#pragma system_include + +#define __IAR_FT _Pragma("inline=forced") __intrinsic + +#if (__VER__ >= 8000000) + #define __ICCARM_V8 1 +#else + #define __ICCARM_V8 0 +#endif + +#ifndef __ALIGNED + #if __ICCARM_V8 + #define __ALIGNED(x) __attribute__((aligned(x))) + #elif (__VER__ >= 7080000) + /* Needs IAR language extensions */ + #define __ALIGNED(x) __attribute__((aligned(x))) + #else + #warning No compiler specific solution for __ALIGNED.__ALIGNED is ignored. + #define __ALIGNED(x) + #endif +#endif + + +/* Define compiler macros for CPU architecture, used in CMSIS 5. + */ +#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__ || __ARM_ARCH_8M_BASE__ || __ARM_ARCH_8M_MAIN__ +/* Macros already defined */ +#else + #if defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #elif defined(__ARM8M_BASELINE__) + #define __ARM_ARCH_8M_BASE__ 1 + #elif defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' + #if __ARM_ARCH == 6 + #define __ARM_ARCH_6M__ 1 + #elif __ARM_ARCH == 7 + #if __ARM_FEATURE_DSP + #define __ARM_ARCH_7EM__ 1 + #else + #define __ARM_ARCH_7M__ 1 + #endif + #endif /* __ARM_ARCH */ + #endif /* __ARM_ARCH_PROFILE == 'M' */ +#endif + +/* Alternativ core deduction for older ICCARM's */ +#if !defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_7M__) && !defined(__ARM_ARCH_7EM__) && \ + !defined(__ARM_ARCH_8M_BASE__) && !defined(__ARM_ARCH_8M_MAIN__) + #if defined(__ARM6M__) && (__CORE__ == __ARM6M__) + #define __ARM_ARCH_6M__ 1 + #elif defined(__ARM7M__) && (__CORE__ == __ARM7M__) + #define __ARM_ARCH_7M__ 1 + #elif defined(__ARM7EM__) && (__CORE__ == __ARM7EM__) + #define __ARM_ARCH_7EM__ 1 + #elif defined(__ARM8M_BASELINE__) && (__CORE == __ARM8M_BASELINE__) + #define __ARM_ARCH_8M_BASE__ 1 + #elif defined(__ARM8M_MAINLINE__) && (__CORE == __ARM8M_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #elif defined(__ARM8EM_MAINLINE__) && (__CORE == __ARM8EM_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #else + #error "Unknown target." + #endif +#endif + + + +#if defined(__ARM_ARCH_6M__) && __ARM_ARCH_6M__==1 + #define __IAR_M0_FAMILY 1 +#elif defined(__ARM_ARCH_8M_BASE__) && __ARM_ARCH_8M_BASE__==1 + #define __IAR_M0_FAMILY 1 +#else + #define __IAR_M0_FAMILY 0 +#endif + +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +#ifndef __ASM + #define __ASM __asm +#endif + +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif + +#ifndef __INLINE + #define __INLINE inline +#endif + +#ifndef __NO_RETURN + #if __ICCARM_V8 + #define __NO_RETURN __attribute__((__noreturn__)) + #else + #define __NO_RETURN _Pragma("object_attribute=__noreturn") + #endif +#endif + +#ifndef __PACKED + #if __ICCARM_V8 + #define __PACKED __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED __packed + #endif +#endif + +#ifndef __PACKED_STRUCT + #if __ICCARM_V8 + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED_STRUCT __packed struct + #endif +#endif + +#ifndef __PACKED_UNION + #if __ICCARM_V8 + #define __PACKED_UNION union __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED_UNION __packed union + #endif +#endif + +#ifndef __RESTRICT + #if __ICCARM_V8 + #define __RESTRICT __restrict + #else + /* Needs IAR language extensions */ + #define __RESTRICT restrict + #endif +#endif + +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif + +#ifndef __FORCEINLINE + #define __FORCEINLINE _Pragma("inline=forced") +#endif + +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __FORCEINLINE __STATIC_INLINE +#endif + +#ifndef __UNALIGNED_UINT16_READ +#pragma language=save +#pragma language=extended +__IAR_FT uint16_t __iar_uint16_read(void const *ptr) +{ + return *(__packed uint16_t*)(ptr); +} +#pragma language=restore +#define __UNALIGNED_UINT16_READ(PTR) __iar_uint16_read(PTR) +#endif + + +#ifndef __UNALIGNED_UINT16_WRITE +#pragma language=save +#pragma language=extended +__IAR_FT void __iar_uint16_write(void const *ptr, uint16_t val) +{ + *(__packed uint16_t*)(ptr) = val;; +} +#pragma language=restore +#define __UNALIGNED_UINT16_WRITE(PTR,VAL) __iar_uint16_write(PTR,VAL) +#endif + +#ifndef __UNALIGNED_UINT32_READ +#pragma language=save +#pragma language=extended +__IAR_FT uint32_t __iar_uint32_read(void const *ptr) +{ + return *(__packed uint32_t*)(ptr); +} +#pragma language=restore +#define __UNALIGNED_UINT32_READ(PTR) __iar_uint32_read(PTR) +#endif + +#ifndef __UNALIGNED_UINT32_WRITE +#pragma language=save +#pragma language=extended +__IAR_FT void __iar_uint32_write(void const *ptr, uint32_t val) +{ + *(__packed uint32_t*)(ptr) = val;; +} +#pragma language=restore +#define __UNALIGNED_UINT32_WRITE(PTR,VAL) __iar_uint32_write(PTR,VAL) +#endif + +#ifndef __UNALIGNED_UINT32 /* deprecated */ +#pragma language=save +#pragma language=extended +__packed struct __iar_u32 { uint32_t v; }; +#pragma language=restore +#define __UNALIGNED_UINT32(PTR) (((struct __iar_u32 *)(PTR))->v) +#endif + +#ifndef __USED + #if __ICCARM_V8 + #define __USED __attribute__((used)) + #else + #define __USED _Pragma("__root") + #endif +#endif + +#undef __WEAK /* undo the definition from DLib_Defaults.h */ +#ifndef __WEAK + #if __ICCARM_V8 + #define __WEAK __attribute__((weak)) + #else + #define __WEAK _Pragma("__weak") + #endif +#endif + +#ifndef __PROGRAM_START +#define __PROGRAM_START __iar_program_start +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP CSTACK$$Limit +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT CSTACK$$Base +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __vector_table +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE @".intvec" +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL STACKSEAL$$Base +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + +#ifndef __ICCARM_INTRINSICS_VERSION__ + #define __ICCARM_INTRINSICS_VERSION__ 0 +#endif + +#if __ICCARM_INTRINSICS_VERSION__ == 2 + + #if defined(__CLZ) + #undef __CLZ + #endif + #if defined(__REVSH) + #undef __REVSH + #endif + #if defined(__RBIT) + #undef __RBIT + #endif + #if defined(__SSAT) + #undef __SSAT + #endif + #if defined(__USAT) + #undef __USAT + #endif + + #include "iccarm_builtin.h" + + #define __disable_fault_irq __iar_builtin_disable_fiq + #define __disable_irq __iar_builtin_disable_interrupt + #define __enable_fault_irq __iar_builtin_enable_fiq + #define __enable_irq __iar_builtin_enable_interrupt + #define __arm_rsr __iar_builtin_rsr + #define __arm_wsr __iar_builtin_wsr + + + #define __get_APSR() (__arm_rsr("APSR")) + #define __get_BASEPRI() (__arm_rsr("BASEPRI")) + #define __get_CONTROL() (__arm_rsr("CONTROL")) + #define __get_FAULTMASK() (__arm_rsr("FAULTMASK")) + + #if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) + #define __get_FPSCR() (__arm_rsr("FPSCR")) + #define __set_FPSCR(VALUE) (__arm_wsr("FPSCR", (VALUE))) + #else + #define __get_FPSCR() ( 0 ) + #define __set_FPSCR(VALUE) ((void)VALUE) + #endif + + #define __get_IPSR() (__arm_rsr("IPSR")) + #define __get_MSP() (__arm_rsr("MSP")) + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + #define __get_MSPLIM() (0U) + #else + #define __get_MSPLIM() (__arm_rsr("MSPLIM")) + #endif + #define __get_PRIMASK() (__arm_rsr("PRIMASK")) + #define __get_PSP() (__arm_rsr("PSP")) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __get_PSPLIM() (0U) + #else + #define __get_PSPLIM() (__arm_rsr("PSPLIM")) + #endif + + #define __get_xPSR() (__arm_rsr("xPSR")) + + #define __set_BASEPRI(VALUE) (__arm_wsr("BASEPRI", (VALUE))) + #define __set_BASEPRI_MAX(VALUE) (__arm_wsr("BASEPRI_MAX", (VALUE))) + +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __arm_wsr("CONTROL", control); + __iar_builtin_ISB(); +} + + #define __set_FAULTMASK(VALUE) (__arm_wsr("FAULTMASK", (VALUE))) + #define __set_MSP(VALUE) (__arm_wsr("MSP", (VALUE))) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + #define __set_MSPLIM(VALUE) ((void)(VALUE)) + #else + #define __set_MSPLIM(VALUE) (__arm_wsr("MSPLIM", (VALUE))) + #endif + #define __set_PRIMASK(VALUE) (__arm_wsr("PRIMASK", (VALUE))) + #define __set_PSP(VALUE) (__arm_wsr("PSP", (VALUE))) + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __set_PSPLIM(VALUE) ((void)(VALUE)) + #else + #define __set_PSPLIM(VALUE) (__arm_wsr("PSPLIM", (VALUE))) + #endif + + #define __TZ_get_CONTROL_NS() (__arm_rsr("CONTROL_NS")) + +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __arm_wsr("CONTROL_NS", control); + __iar_builtin_ISB(); +} + + #define __TZ_get_PSP_NS() (__arm_rsr("PSP_NS")) + #define __TZ_set_PSP_NS(VALUE) (__arm_wsr("PSP_NS", (VALUE))) + #define __TZ_get_MSP_NS() (__arm_rsr("MSP_NS")) + #define __TZ_set_MSP_NS(VALUE) (__arm_wsr("MSP_NS", (VALUE))) + #define __TZ_get_SP_NS() (__arm_rsr("SP_NS")) + #define __TZ_set_SP_NS(VALUE) (__arm_wsr("SP_NS", (VALUE))) + #define __TZ_get_PRIMASK_NS() (__arm_rsr("PRIMASK_NS")) + #define __TZ_set_PRIMASK_NS(VALUE) (__arm_wsr("PRIMASK_NS", (VALUE))) + #define __TZ_get_BASEPRI_NS() (__arm_rsr("BASEPRI_NS")) + #define __TZ_set_BASEPRI_NS(VALUE) (__arm_wsr("BASEPRI_NS", (VALUE))) + #define __TZ_get_FAULTMASK_NS() (__arm_rsr("FAULTMASK_NS")) + #define __TZ_set_FAULTMASK_NS(VALUE)(__arm_wsr("FAULTMASK_NS", (VALUE))) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __TZ_get_PSPLIM_NS() (0U) + #define __TZ_set_PSPLIM_NS(VALUE) ((void)(VALUE)) + #else + #define __TZ_get_PSPLIM_NS() (__arm_rsr("PSPLIM_NS")) + #define __TZ_set_PSPLIM_NS(VALUE) (__arm_wsr("PSPLIM_NS", (VALUE))) + #endif + + #define __TZ_get_MSPLIM_NS() (__arm_rsr("MSPLIM_NS")) + #define __TZ_set_MSPLIM_NS(VALUE) (__arm_wsr("MSPLIM_NS", (VALUE))) + + #define __NOP __iar_builtin_no_operation + + #define __CLZ __iar_builtin_CLZ + #define __CLREX __iar_builtin_CLREX + + #define __DMB __iar_builtin_DMB + #define __DSB __iar_builtin_DSB + #define __ISB __iar_builtin_ISB + + #define __LDREXB __iar_builtin_LDREXB + #define __LDREXH __iar_builtin_LDREXH + #define __LDREXW __iar_builtin_LDREX + + #define __RBIT __iar_builtin_RBIT + #define __REV __iar_builtin_REV + #define __REV16 __iar_builtin_REV16 + + __IAR_FT int16_t __REVSH(int16_t val) + { + return (int16_t) __iar_builtin_REVSH(val); + } + + #define __ROR __iar_builtin_ROR + #define __RRX __iar_builtin_RRX + + #define __SEV __iar_builtin_SEV + + #if !__IAR_M0_FAMILY + #define __SSAT __iar_builtin_SSAT + #endif + + #define __STREXB __iar_builtin_STREXB + #define __STREXH __iar_builtin_STREXH + #define __STREXW __iar_builtin_STREX + + #if !__IAR_M0_FAMILY + #define __USAT __iar_builtin_USAT + #endif + + #define __WFE __iar_builtin_WFE + #define __WFI __iar_builtin_WFI + + #if __ARM_MEDIA__ + #define __SADD8 __iar_builtin_SADD8 + #define __QADD8 __iar_builtin_QADD8 + #define __SHADD8 __iar_builtin_SHADD8 + #define __UADD8 __iar_builtin_UADD8 + #define __UQADD8 __iar_builtin_UQADD8 + #define __UHADD8 __iar_builtin_UHADD8 + #define __SSUB8 __iar_builtin_SSUB8 + #define __QSUB8 __iar_builtin_QSUB8 + #define __SHSUB8 __iar_builtin_SHSUB8 + #define __USUB8 __iar_builtin_USUB8 + #define __UQSUB8 __iar_builtin_UQSUB8 + #define __UHSUB8 __iar_builtin_UHSUB8 + #define __SADD16 __iar_builtin_SADD16 + #define __QADD16 __iar_builtin_QADD16 + #define __SHADD16 __iar_builtin_SHADD16 + #define __UADD16 __iar_builtin_UADD16 + #define __UQADD16 __iar_builtin_UQADD16 + #define __UHADD16 __iar_builtin_UHADD16 + #define __SSUB16 __iar_builtin_SSUB16 + #define __QSUB16 __iar_builtin_QSUB16 + #define __SHSUB16 __iar_builtin_SHSUB16 + #define __USUB16 __iar_builtin_USUB16 + #define __UQSUB16 __iar_builtin_UQSUB16 + #define __UHSUB16 __iar_builtin_UHSUB16 + #define __SASX __iar_builtin_SASX + #define __QASX __iar_builtin_QASX + #define __SHASX __iar_builtin_SHASX + #define __UASX __iar_builtin_UASX + #define __UQASX __iar_builtin_UQASX + #define __UHASX __iar_builtin_UHASX + #define __SSAX __iar_builtin_SSAX + #define __QSAX __iar_builtin_QSAX + #define __SHSAX __iar_builtin_SHSAX + #define __USAX __iar_builtin_USAX + #define __UQSAX __iar_builtin_UQSAX + #define __UHSAX __iar_builtin_UHSAX + #define __USAD8 __iar_builtin_USAD8 + #define __USADA8 __iar_builtin_USADA8 + #define __SSAT16 __iar_builtin_SSAT16 + #define __USAT16 __iar_builtin_USAT16 + #define __UXTB16 __iar_builtin_UXTB16 + #define __UXTAB16 __iar_builtin_UXTAB16 + #define __SXTB16 __iar_builtin_SXTB16 + #define __SXTAB16 __iar_builtin_SXTAB16 + #define __SMUAD __iar_builtin_SMUAD + #define __SMUADX __iar_builtin_SMUADX + #define __SMMLA __iar_builtin_SMMLA + #define __SMLAD __iar_builtin_SMLAD + #define __SMLADX __iar_builtin_SMLADX + #define __SMLALD __iar_builtin_SMLALD + #define __SMLALDX __iar_builtin_SMLALDX + #define __SMUSD __iar_builtin_SMUSD + #define __SMUSDX __iar_builtin_SMUSDX + #define __SMLSD __iar_builtin_SMLSD + #define __SMLSDX __iar_builtin_SMLSDX + #define __SMLSLD __iar_builtin_SMLSLD + #define __SMLSLDX __iar_builtin_SMLSLDX + #define __SEL __iar_builtin_SEL + #define __QADD __iar_builtin_QADD + #define __QSUB __iar_builtin_QSUB + #define __PKHBT __iar_builtin_PKHBT + #define __PKHTB __iar_builtin_PKHTB + #endif + +#else /* __ICCARM_INTRINSICS_VERSION__ == 2 */ + + #if __IAR_M0_FAMILY + /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */ + #define __CLZ __cmsis_iar_clz_not_active + #define __SSAT __cmsis_iar_ssat_not_active + #define __USAT __cmsis_iar_usat_not_active + #define __RBIT __cmsis_iar_rbit_not_active + #define __get_APSR __cmsis_iar_get_APSR_not_active + #endif + + + #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) )) + #define __get_FPSCR __cmsis_iar_get_FPSR_not_active + #define __set_FPSCR __cmsis_iar_set_FPSR_not_active + #endif + + #ifdef __INTRINSICS_INCLUDED + #error intrinsics.h is already included previously! + #endif + + #include + + #if __IAR_M0_FAMILY + /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */ + #undef __CLZ + #undef __SSAT + #undef __USAT + #undef __RBIT + #undef __get_APSR + + __STATIC_INLINE uint8_t __CLZ(uint32_t data) + { + if (data == 0U) { return 32U; } + + uint32_t count = 0U; + uint32_t mask = 0x80000000U; + + while ((data & mask) == 0U) + { + count += 1U; + mask = mask >> 1U; + } + return count; + } + + __STATIC_INLINE uint32_t __RBIT(uint32_t v) + { + uint8_t sc = 31U; + uint32_t r = v; + for (v >>= 1U; v; v >>= 1U) + { + r <<= 1U; + r |= v & 1U; + sc--; + } + return (r << sc); + } + + __STATIC_INLINE uint32_t __get_APSR(void) + { + uint32_t res; + __asm("MRS %0,APSR" : "=r" (res)); + return res; + } + + #endif + + #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) )) + #undef __get_FPSCR + #undef __set_FPSCR + #define __get_FPSCR() (0) + #define __set_FPSCR(VALUE) ((void)VALUE) + #endif + + #pragma diag_suppress=Pe940 + #pragma diag_suppress=Pe177 + + #define __enable_irq __enable_interrupt + #define __disable_irq __disable_interrupt + #define __NOP __no_operation + + #define __get_xPSR __get_PSR + + #if (!defined(__ARM_ARCH_6M__) || __ARM_ARCH_6M__==0) + + __IAR_FT uint32_t __LDREXW(uint32_t volatile *ptr) + { + return __LDREX((unsigned long *)ptr); + } + + __IAR_FT uint32_t __STREXW(uint32_t value, uint32_t volatile *ptr) + { + return __STREX(value, (unsigned long *)ptr); + } + #endif + + + /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */ + #if (__CORTEX_M >= 0x03) + + __IAR_FT uint32_t __RRX(uint32_t value) + { + uint32_t result; + __ASM volatile("RRX %0, %1" : "=r"(result) : "r" (value)); + return(result); + } + + __IAR_FT void __set_BASEPRI_MAX(uint32_t value) + { + __asm volatile("MSR BASEPRI_MAX,%0"::"r" (value)); + } + + + #define __enable_fault_irq __enable_fiq + #define __disable_fault_irq __disable_fiq + + + #endif /* (__CORTEX_M >= 0x03) */ + + __IAR_FT uint32_t __ROR(uint32_t op1, uint32_t op2) + { + return (op1 >> op2) | (op1 << ((sizeof(op1)*8)-op2)); + } + + #if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + + __IAR_FT uint32_t __get_MSPLIM(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,MSPLIM" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __set_MSPLIM(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR MSPLIM,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __get_PSPLIM(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,PSPLIM" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __set_PSPLIM(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR PSPLIM,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __TZ_get_CONTROL_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,CONTROL_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_CONTROL_NS(uint32_t value) + { + __asm volatile("MSR CONTROL_NS,%0" :: "r" (value)); + __iar_builtin_ISB(); + } + + __IAR_FT uint32_t __TZ_get_PSP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,PSP_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_PSP_NS(uint32_t value) + { + __asm volatile("MSR PSP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_MSP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,MSP_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_MSP_NS(uint32_t value) + { + __asm volatile("MSR MSP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_SP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,SP_NS" : "=r" (res)); + return res; + } + __IAR_FT void __TZ_set_SP_NS(uint32_t value) + { + __asm volatile("MSR SP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PRIMASK_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,PRIMASK_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_PRIMASK_NS(uint32_t value) + { + __asm volatile("MSR PRIMASK_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_BASEPRI_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,BASEPRI_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_BASEPRI_NS(uint32_t value) + { + __asm volatile("MSR BASEPRI_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_FAULTMASK_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,FAULTMASK_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_FAULTMASK_NS(uint32_t value) + { + __asm volatile("MSR FAULTMASK_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PSPLIM_NS(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,PSPLIM_NS" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __TZ_set_PSPLIM_NS(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR PSPLIM_NS,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __TZ_get_MSPLIM_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,MSPLIM_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_MSPLIM_NS(uint32_t value) + { + __asm volatile("MSR MSPLIM_NS,%0" :: "r" (value)); + } + + #endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ + +#endif /* __ICCARM_INTRINSICS_VERSION__ == 2 */ + +#define __BKPT(value) __asm volatile ("BKPT %0" : : "i"(value)) + +#if __IAR_M0_FAMILY + __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat) + { + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; + } + + __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat) + { + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; + } +#endif + +#if (__CORTEX_M >= 0x03) /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */ + + __IAR_FT uint8_t __LDRBT(volatile uint8_t *addr) + { + uint32_t res; + __ASM volatile ("LDRBT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDRHT(volatile uint16_t *addr) + { + uint32_t res; + __ASM volatile ("LDRHT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDRT(volatile uint32_t *addr) + { + uint32_t res; + __ASM volatile ("LDRT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return res; + } + + __IAR_FT void __STRBT(uint8_t value, volatile uint8_t *addr) + { + __ASM volatile ("STRBT %1, [%0]" : : "r" (addr), "r" ((uint32_t)value) : "memory"); + } + + __IAR_FT void __STRHT(uint16_t value, volatile uint16_t *addr) + { + __ASM volatile ("STRHT %1, [%0]" : : "r" (addr), "r" ((uint32_t)value) : "memory"); + } + + __IAR_FT void __STRT(uint32_t value, volatile uint32_t *addr) + { + __ASM volatile ("STRT %1, [%0]" : : "r" (addr), "r" (value) : "memory"); + } + +#endif /* (__CORTEX_M >= 0x03) */ + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + + + __IAR_FT uint8_t __LDAB(volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAB %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDAH(volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAH %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDA(volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("LDA %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return res; + } + + __IAR_FT void __STLB(uint8_t value, volatile uint8_t *ptr) + { + __ASM volatile ("STLB %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT void __STLH(uint16_t value, volatile uint16_t *ptr) + { + __ASM volatile ("STLH %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT void __STL(uint32_t value, volatile uint32_t *ptr) + { + __ASM volatile ("STL %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT uint8_t __LDAEXB(volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEXB %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDAEXH(volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEXH %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDAEX(volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEX %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEXB %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEXH %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEX %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + +#endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ + +#undef __IAR_FT +#undef __IAR_M0_FAMILY +#undef __ICCARM_V8 + +#pragma diag_default=Pe940 +#pragma diag_default=Pe177 + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +#endif /* __CMSIS_ICCARM_H__ */ diff --git a/components/cmsis_core/cmsis_tiarmclang.h b/components/cmsis_core/cmsis_tiarmclang.h new file mode 100644 index 00000000..4d799c27 --- /dev/null +++ b/components/cmsis_core/cmsis_tiarmclang.h @@ -0,0 +1,1510 @@ +/**************************************************************************//** + * @file cmsis_tiarmclang.h + * @brief CMSIS compiler tiarmclang header file + * @version V1.0.0 + * @date 04. April 2023 + ******************************************************************************/ +/* + * Copyright (c) 2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */ + +#ifndef __CMSIS_TIARMCLANG_H +#define __CMSIS_TIARMCLANG_H + +#pragma clang system_header /* treat file as system include file */ + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */ + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */ + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START +#define __PROGRAM_START _c_int00 +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP __STACK_END +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT __STACK_SIZE +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section(".intvecs"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL Image$$STACKSEAL$$ZI$$Base +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __builtin_arm_nop + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI __builtin_arm_wfi + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __builtin_arm_wfe + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __builtin_arm_sev + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +#define __ISB() __builtin_arm_isb(0xF) + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __builtin_arm_dsb(0xF) + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __builtin_arm_dmb(0xF) + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV(value) __builtin_bswap32(value) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV16(value) __ROR(__REV(value), 16) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REVSH(value) (int16_t)__builtin_bswap16(value) + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __builtin_arm_rbit + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB (uint8_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH (uint16_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW (uint32_t)__builtin_arm_ldrex + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW (uint32_t)__builtin_arm_strex + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +#define __CLREX __builtin_arm_clrex + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __builtin_arm_ssat + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __builtin_arm_usat + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDAEXB (uint8_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDAEXH (uint16_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDAEX (uint32_t)__builtin_arm_ldaex + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXB (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXH (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEX (uint32_t)__builtin_arm_stlex + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** @}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} +#endif + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} +#endif + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __get_FPSCR (uint32_t)__builtin_arm_get_fpscr +#else +#define __get_FPSCR() ((uint32_t)0U) +#endif + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __set_FPSCR __builtin_arm_set_fpscr +#else +#define __set_FPSCR(fpscr) ((void)(fpscr)) +#endif + + +/** @} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +#define __SADD8 __builtin_arm_sadd8 +#define __QADD8 __builtin_arm_qadd8 +#define __SHADD8 __builtin_arm_shadd8 +#define __UADD8 __builtin_arm_uadd8 +#define __UQADD8 __builtin_arm_uqadd8 +#define __UHADD8 __builtin_arm_uhadd8 +#define __SSUB8 __builtin_arm_ssub8 +#define __QSUB8 __builtin_arm_qsub8 +#define __SHSUB8 __builtin_arm_shsub8 +#define __USUB8 __builtin_arm_usub8 +#define __UQSUB8 __builtin_arm_uqsub8 +#define __UHSUB8 __builtin_arm_uhsub8 +#define __SADD16 __builtin_arm_sadd16 +#define __QADD16 __builtin_arm_qadd16 +#define __SHADD16 __builtin_arm_shadd16 +#define __UADD16 __builtin_arm_uadd16 +#define __UQADD16 __builtin_arm_uqadd16 +#define __UHADD16 __builtin_arm_uhadd16 +#define __SSUB16 __builtin_arm_ssub16 +#define __QSUB16 __builtin_arm_qsub16 +#define __SHSUB16 __builtin_arm_shsub16 +#define __USUB16 __builtin_arm_usub16 +#define __UQSUB16 __builtin_arm_uqsub16 +#define __UHSUB16 __builtin_arm_uhsub16 +#define __SASX __builtin_arm_sasx +#define __QASX __builtin_arm_qasx +#define __SHASX __builtin_arm_shasx +#define __UASX __builtin_arm_uasx +#define __UQASX __builtin_arm_uqasx +#define __UHASX __builtin_arm_uhasx +#define __SSAX __builtin_arm_ssax +#define __QSAX __builtin_arm_qsax +#define __SHSAX __builtin_arm_shsax +#define __USAX __builtin_arm_usax +#define __UQSAX __builtin_arm_uqsax +#define __UHSAX __builtin_arm_uhsax +#define __USAD8 __builtin_arm_usad8 +#define __USADA8 __builtin_arm_usada8 +#define __SSAT16 __builtin_arm_ssat16 +#define __USAT16 __builtin_arm_usat16 +#define __UXTB16 __builtin_arm_uxtb16 +#define __UXTAB16 __builtin_arm_uxtab16 +#define __SXTB16 __builtin_arm_sxtb16 +#define __SXTAB16 __builtin_arm_sxtab16 +#define __SMUAD __builtin_arm_smuad +#define __SMUADX __builtin_arm_smuadx +#define __SMLAD __builtin_arm_smlad +#define __SMLADX __builtin_arm_smladx +#define __SMLALD __builtin_arm_smlald +#define __SMLALDX __builtin_arm_smlaldx +#define __SMUSD __builtin_arm_smusd +#define __SMUSDX __builtin_arm_smusdx +#define __SMLSD __builtin_arm_smlsd +#define __SMLSDX __builtin_arm_smlsdx +#define __SMLSLD __builtin_arm_smlsld +#define __SMLSLDX __builtin_arm_smlsldx +#define __SEL __builtin_arm_sel +#define __QADD __builtin_arm_qadd +#define __QSUB __builtin_arm_qsub + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/** @} end of group CMSIS_SIMD_intrinsics */ + + +#endif /* __CMSIS_TIARMCLANG_H */ diff --git a/components/cmsis_core/cmsis_version.h b/components/cmsis_core/cmsis_version.h new file mode 100644 index 00000000..8b4765f1 --- /dev/null +++ b/components/cmsis_core/cmsis_version.h @@ -0,0 +1,39 @@ +/**************************************************************************//** + * @file cmsis_version.h + * @brief CMSIS Core(M) Version definitions + * @version V5.0.5 + * @date 02. February 2022 + ******************************************************************************/ +/* + * Copyright (c) 2009-2022 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CMSIS_VERSION_H +#define __CMSIS_VERSION_H + +/* CMSIS Version definitions */ +#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */ +#define __CM_CMSIS_VERSION_SUB ( 6U) /*!< [15:0] CMSIS Core(M) sub version */ +#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \ + __CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */ +#endif diff --git a/components/cmsis_core/core_cm4.h b/components/cmsis_core/core_cm4.h new file mode 100644 index 00000000..711c1132 --- /dev/null +++ b/components/cmsis_core/core_cm4.h @@ -0,0 +1,2170 @@ +/**************************************************************************//** + * @file core_cm4.h + * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File + * @version V5.2.0 + * @date 04. April 2023 + ******************************************************************************/ +/* + * Copyright (c) 2009-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM4_H_GENERIC +#define __CORE_CM4_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M4 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM4 definitions */ +#define __CM4_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM4_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16U) | \ + __CM4_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (4U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_FP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined (__ti__) + #if defined (__ARM_FP) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM4_H_DEPENDANT +#define __CORE_CM4_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM4_REV + #define __CM4_REV 0x0000U + #warning "__CM4_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __VTOR_PRESENT + #define __VTOR_PRESENT 1U + #warning "__VTOR_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M4 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24U]; + __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RESERVED1[24U]; + __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24U]; + __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24U]; + __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56U]; + __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5U]; + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_CFSR_MEMFAULTSR_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_CFSR_MEMFAULTSR_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_CFSR_MEMFAULTSR_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_CFSR_MEMFAULTSR_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_CFSR_MEMFAULTSR_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_CFSR_MEMFAULTSR_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_DISOOFP_Pos 9U /*!< ACTLR: DISOOFP Position */ +#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ + +#define SCnSCB_ACTLR_DISFPCA_Pos 8U /*!< ACTLR: DISFPCA Position */ +#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1U /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[32U]; + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TRACEBUSID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TRACEBUSID_Msk (0x7FUL << ITM_TCR_TRACEBUSID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPRESCALE_Pos 8U /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPRESCALE_Msk (3UL << ITM_TCR_TSPRESCALE_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_BYTEACC_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_BYTEACC_Msk (1UL << ITM_LSR_BYTEACC_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_ACCESS_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_ACCESS_Msk (1UL << ITM_LSR_ACCESS_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_PRESENT_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_PRESENT_Msk (1UL /*<< ITM_LSR_PRESENT_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x1UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x1UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x1UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x1UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register Definitions */ +#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x018 (R/ ) Media and FP Feature Register 2 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/* Media and FP Feature Register 2 Definitions */ + +#define FPU_MVFR2_VFP_Misc_Pos 4U /*!< MVFR2: VFP Misc bits Position */ +#define FPU_MVFR2_VFP_Misc_Msk (0xFUL << FPU_MVFR2_VFP_Misc_Pos) /*!< MVFR2: VFP Misc bits Mask */ + +/*@} end of group CMSIS_FPU */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +#define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ +#define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ + +/*@} */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_register_aliases Backwards Compatibility Aliases + \brief Register alias definitions for backwards compatibility. + @{ + */ + +/* Capitalize ITM_TCR Register Definitions */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_TraceBusID_Pos (ITM_TCR_TRACEBUSID_Pos) /*!< \deprecated ITM_TCR_TraceBusID_Pos */ +#define ITM_TCR_TraceBusID_Msk (ITM_TCR_TRACEBUSID_Msk) /*!< \deprecated ITM_TCR_TraceBusID_Msk */ + +#define ITM_TCR_TSPrescale_Pos (ITM_TCR_TSPRESCALE_Pos) /*!< \deprecated ITM_TCR_TSPrescale_Pos */ +#define ITM_TCR_TSPrescale_Msk (ITM_TCR_TSPRESCALE_Msk) /*!< \deprecated ITM_TCR_TSPrescale_Msk */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos (ITM_LSR_BYTEACC_Pos) /*!< \deprecated ITM_LSR_ByteAcc_Pos */ +#define ITM_LSR_ByteAcc_Msk (ITM_LSR_BYTEACC_Msk) /*!< \deprecated ITM_LSR_ByteAcc_Msk */ + +#define ITM_LSR_Access_Pos (ITM_LSR_ACCESS_Pos) /*!< \deprecated ITM_LSR_Access_Pos */ +#define ITM_LSR_Access_Msk (ITM_LSR_ACCESS_Msk) /*!< \deprecated ITM_LSR_Access_Msk */ + +#define ITM_LSR_Present_Pos (ITM_LSR_PRESENT_Pos) /*!< \deprecated ITM_LSR_Present_Pos */ +#define ITM_LSR_Present_Msk (ITM_LSR_PRESENT_Msk) /*!< \deprecated ITM_LSR_Present_Msk */ + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ +#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */ + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + __COMPILER_BARRIER(); + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __COMPILER_BARRIER(); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; + /* ARM Application Note 321 states that the M4 does not require the architectural barrier */ +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/components/cmsis_core/mpu_armv7.h b/components/cmsis_core/mpu_armv7.h new file mode 100644 index 00000000..d9eedf81 --- /dev/null +++ b/components/cmsis_core/mpu_armv7.h @@ -0,0 +1,275 @@ +/****************************************************************************** + * @file mpu_armv7.h + * @brief CMSIS MPU API for Armv7-M MPU + * @version V5.1.2 + * @date 25. May 2020 + ******************************************************************************/ +/* + * Copyright (c) 2017-2020 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef ARM_MPU_ARMV7_H +#define ARM_MPU_ARMV7_H + +#define ARM_MPU_REGION_SIZE_32B ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes +#define ARM_MPU_REGION_SIZE_64B ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes +#define ARM_MPU_REGION_SIZE_128B ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes +#define ARM_MPU_REGION_SIZE_256B ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes +#define ARM_MPU_REGION_SIZE_512B ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes +#define ARM_MPU_REGION_SIZE_1KB ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte +#define ARM_MPU_REGION_SIZE_2KB ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes +#define ARM_MPU_REGION_SIZE_4KB ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes +#define ARM_MPU_REGION_SIZE_8KB ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes +#define ARM_MPU_REGION_SIZE_16KB ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes +#define ARM_MPU_REGION_SIZE_32KB ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes +#define ARM_MPU_REGION_SIZE_64KB ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes +#define ARM_MPU_REGION_SIZE_128KB ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes +#define ARM_MPU_REGION_SIZE_256KB ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes +#define ARM_MPU_REGION_SIZE_512KB ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes +#define ARM_MPU_REGION_SIZE_1MB ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte +#define ARM_MPU_REGION_SIZE_2MB ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes +#define ARM_MPU_REGION_SIZE_4MB ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes +#define ARM_MPU_REGION_SIZE_8MB ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes +#define ARM_MPU_REGION_SIZE_16MB ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes +#define ARM_MPU_REGION_SIZE_32MB ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes +#define ARM_MPU_REGION_SIZE_64MB ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes +#define ARM_MPU_REGION_SIZE_128MB ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes +#define ARM_MPU_REGION_SIZE_256MB ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes +#define ARM_MPU_REGION_SIZE_512MB ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes +#define ARM_MPU_REGION_SIZE_1GB ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte +#define ARM_MPU_REGION_SIZE_2GB ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes +#define ARM_MPU_REGION_SIZE_4GB ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes + +#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access +#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only +#define ARM_MPU_AP_URO 2U ///!< MPU Access Permission unprivileged access read-only +#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access +#define ARM_MPU_AP_PRO 5U ///!< MPU Access Permission privileged access read-only +#define ARM_MPU_AP_RO 6U ///!< MPU Access Permission read-only access + +/** MPU Region Base Address Register Value +* +* \param Region The region to be configured, number 0 to 15. +* \param BaseAddress The base address for the region. +*/ +#define ARM_MPU_RBAR(Region, BaseAddress) \ + (((BaseAddress) & MPU_RBAR_ADDR_Msk) | \ + ((Region) & MPU_RBAR_REGION_Msk) | \ + (MPU_RBAR_VALID_Msk)) + +/** +* MPU Memory Access Attributes +* +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +*/ +#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable) \ + ((((TypeExtField) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) | \ + (((IsShareable) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) | \ + (((IsCacheable) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) | \ + (((IsBufferable) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk)) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param AccessAttributes Memory access attribution, see \ref ARM_MPU_ACCESS_. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size) \ + ((((DisableExec) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | \ + (((AccessPermission) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | \ + (((AccessAttributes) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk))) | \ + (((SubRegionDisable) << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk) | \ + (((Size) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk) | \ + (((MPU_RASR_ENABLE_Msk)))) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \ + ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size) + +/** +* MPU Memory Access Attribute for strongly ordered memory. +* - TEX: 000b +* - Shareable +* - Non-cacheable +* - Non-bufferable +*/ +#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U) + +/** +* MPU Memory Access Attribute for device memory. +* - TEX: 000b (if shareable) or 010b (if non-shareable) +* - Shareable or non-shareable +* - Non-cacheable +* - Bufferable (if shareable) or non-bufferable (if non-shareable) +* +* \param IsShareable Configures the device memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U)) + +/** +* MPU Memory Access Attribute for normal memory. +* - TEX: 1BBb (reflecting outer cacheability rules) +* - Shareable or non-shareable +* - Cacheable or non-cacheable (reflecting inner cacheability rules) +* - Bufferable or non-bufferable (reflecting inner cacheability rules) +* +* \param OuterCp Configures the outer cache policy. +* \param InnerCp Configures the inner cache policy. +* \param IsShareable Configures the memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) >> 1U), ((InnerCp) & 1U)) + +/** +* MPU Memory Access Attribute non-cacheable policy. +*/ +#define ARM_MPU_CACHEP_NOCACHE 0U + +/** +* MPU Memory Access Attribute write-back, write and read allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_WRA 1U + +/** +* MPU Memory Access Attribute write-through, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WT_NWA 2U + +/** +* MPU Memory Access Attribute write-back, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_NWA 3U + + +/** +* Struct for a single MPU Region +*/ +typedef struct { + uint32_t RBAR; //!< The region base address register value (RBAR) + uint32_t RASR; //!< The region attribute and size register value (RASR) \ref MPU_RASR +} ARM_MPU_Region_t; + +/** Enable the MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control) +{ + __DMB(); + MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif + __DSB(); + __ISB(); +} + +/** Disable the MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable(void) +{ + __DMB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; + __DSB(); + __ISB(); +} + +/** Clear and disable the given MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr) +{ + MPU->RNR = rnr; + MPU->RASR = 0U; +} + +/** Configure an MPU region. +* \param rbar Value for RBAR register. +* \param rasr Value for RASR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr) +{ + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Configure the given MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rasr Value for RASR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr) +{ + MPU->RNR = rnr; + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Memcpy with strictly ordered memory access, e.g. used by code in ARM_MPU_Load(). +* \param dst Destination data is copied to. +* \param src Source data is copied from. +* \param len Amount of data words to be copied. +*/ +__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len) +{ + uint32_t i; + for (i = 0U; i < len; ++i) + { + dst[i] = src[i]; + } +} + +/** Load the given number of MPU regions from a table. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt) +{ + const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U; + while (cnt > MPU_TYPE_RALIASES) { + ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize); + table += MPU_TYPE_RALIASES; + cnt -= MPU_TYPE_RALIASES; + } + ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize); +} + +#endif diff --git a/components/furi/CMakeLists.txt b/components/furi/CMakeLists.txt new file mode 100644 index 00000000..f4232975 --- /dev/null +++ b/components/furi/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "src" + INCLUDE_DIRS "src" + REQUIRES mlib cmsis_core +) diff --git a/components/furi/LICENSE.md b/components/furi/LICENSE.md new file mode 100644 index 00000000..85c7c696 --- /dev/null +++ b/components/furi/LICENSE.md @@ -0,0 +1,636 @@ +# GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 [Free Software Foundation, Inc.](http://fsf.org/) + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for software and +other kinds of works. + +The licenses for most software and other practical works are designed to take +away your freedom to share and change the works. By contrast, the GNU General +Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. +We, the Free Software Foundation, use the GNU General Public License for most +of our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for them if you wish), that you +receive source code or can get it if you want it, that you can change the +software or use pieces of it in new free programs, and that you know you can do +these things. + +To protect your rights, we need to prevent others from denying you these rights +or asking you to surrender the rights. Therefore, you have certain +responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must pass on to the recipients the same freedoms that you received. +You must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: + + 1. assert copyright on the software, and + 2. offer you this License giving you legal permission to copy, distribute + and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that +there is no warranty for this free software. For both users' and authors' sake, +the GPL requires that modified versions be marked as changed, so that their +problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified +versions of the software inside them, although the manufacturer can do so. This +is fundamentally incompatible with the aim of protecting users' freedom to +change the software. The systematic pattern of such abuse occurs in the area of +products for individuals to use, which is precisely where it is most +unacceptable. Therefore, we have designed this version of the GPL to prohibit +the practice for those products. If such problems arise substantially in other +domains, we stand ready to extend this provision to those domains in future +versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States +should not allow patents to restrict development and use of software on +general-purpose computers, but in those that do, we wish to avoid the special +danger that patents applied to a free program could make it effectively +proprietary. To prevent this, the GPL assures that patents cannot be used to +render the program non-free. + +The precise terms and conditions for copying, distribution and modification +follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +*This License* refers to version 3 of the GNU General Public License. + +*Copyright* also means copyright-like laws that apply to other kinds of works, +such as semiconductor masks. + +*The Program* refers to any copyrightable work licensed under this License. +Each licensee is addressed as *you*. *Licensees* and *recipients* may be +individuals or organizations. + +To *modify* a work means to copy from or adapt all or part of the work in a +fashion requiring copyright permission, other than the making of an exact copy. +The resulting work is called a *modified version* of the earlier work or a work +*based on* the earlier work. + +A *covered work* means either the unmodified Program or a work based on the +Program. + +To *propagate* a work means to do anything with it that, without permission, +would make you directly or secondarily liable for infringement under applicable +copyright law, except executing it on a computer or modifying a private copy. +Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To *convey* a work means any kind of propagation that enables other parties to +make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays *Appropriate Legal Notices* to the +extent that it includes a convenient and prominently visible feature that + + 1. displays an appropriate copyright notice, and + 2. tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the work + under this License, and how to view a copy of this License. + +If the interface presents a list of user commands or options, such as a menu, a +prominent item in the list meets this criterion. + +### 1. Source Code. + +The *source code* for a work means the preferred form of the work for making +modifications to it. *Object code* means any non-source form of a work. + +A *Standard Interface* means an interface that either is an official standard +defined by a recognized standards body, or, in the case of interfaces specified +for a particular programming language, one that is widely used among developers +working in that language. + +The *System Libraries* of an executable work include anything, other than the +work as a whole, that (a) is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and (b) serves only +to enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code +form. A *Major Component*, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on +which the executable work runs, or a compiler used to produce the work, or an +object code interpreter used to run it. + +The *Corresponding Source* for a work in object code form means all the source +code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose +tools or generally available free programs which are used unmodified in +performing those activities but which are not part of the work. For example, +Corresponding Source includes interface definition files associated with source +files for the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, such as +by intimate data communication or control flow between those subprograms and +other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on +the Program, and are irrevocable provided the stated conditions are met. This +License explicitly affirms your unlimited permission to run the unmodified +Program. The output from running a covered work is covered by this License only +if the output, given its content, constitutes a covered work. This License +acknowledges your rights of fair use or other equivalent, as provided by +copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey +covered works to others for the sole purpose of having them make modifications +exclusively for you, or provide you with facilities for running those works, +provided that you comply with the terms of this License in conveying all +material for which you do not control copyright. Those thus making or running +the covered works for you must do so exclusively on your behalf, under your +direction and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes it +unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure +under any applicable law fulfilling obligations under article 11 of the WIPO +copyright treaty adopted on 20 December 1996, or similar laws prohibiting or +restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention is +effected by exercising rights under this License with respect to the covered +work, and you disclaim any intention to limit operation or modification of the +work as a means of enforcing, against the work's users, your or third parties' +legal rights to forbid circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on +each copy an appropriate copyright notice; keep intact all notices stating that +this License and any non-permissive terms added in accord with section 7 apply +to the code; keep intact all notices of the absence of any warranty; and give +all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may +offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it +from the Program, in the form of source code under the terms of section 4, +provided that you also meet all of these conditions: + + - a) The work must carry prominent notices stating that you modified it, and + giving a relevant date. + - b) The work must carry prominent notices stating that it is released under + this License and any conditions added under section 7. This requirement + modifies the requirement in section 4 to *keep intact all notices*. + - c) You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will therefore + apply, along with any applicable section 7 additional terms, to the whole + of the work, and all its parts, regardless of how they are packaged. This + License gives no permission to license the work in any other way, but it + does not invalidate such permission if you have separately received it. + - d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your work need + not make them do so. + +A compilation of a covered work with other separate and independent works, +which are not by their nature extensions of the covered work, and which are not +combined with it such as to form a larger program, in or on a volume of a +storage or distribution medium, is called an *aggregate* if the compilation and +its resulting copyright are not used to limit the access or legal rights of the +compilation's users beyond what the individual works permit. Inclusion of a +covered work in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 +and 5, provided that you also convey the machine-readable Corresponding Source +under the terms of this License, in one of these ways: + + - a) Convey the object code in, or embodied in, a physical product (including + a physical distribution medium), accompanied by the Corresponding Source + fixed on a durable physical medium customarily used for software + interchange. + - b) Convey the object code in, or embodied in, a physical product (including + a physical distribution medium), accompanied by a written offer, valid for + at least three years and valid for as long as you offer spare parts or + customer support for that product model, to give anyone who possesses the + object code either + 1. a copy of the Corresponding Source for all the software in the product + that is covered by this License, on a durable physical medium + customarily used for software interchange, for a price no more than your + reasonable cost of physically performing this conveying of source, or + 2. access to copy the Corresponding Source from a network server at no + charge. + - c) Convey individual copies of the object code with a copy of the written + offer to provide the Corresponding Source. This alternative is allowed only + occasionally and noncommercially, and only if you received the object code + with such an offer, in accord with subsection 6b. + - d) Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the Corresponding + Source in the same way through the same place at no further charge. You + need not require recipients to copy the Corresponding Source along with the + object code. If the place to copy the object code is a network server, the + Corresponding Source may be on a different server operated by you or a + third party) that supports equivalent copying facilities, provided you + maintain clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the Corresponding + Source, you remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + - e) Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of the + work are being offered to the general public at no charge under subsection + 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A *User Product* is either + + 1. a *consumer product*, which means any tangible personal property which is + normally used for personal, family, or household purposes, or + 2. anything designed or sold for incorporation into a dwelling. + +In determining whether a product is a consumer product, doubtful cases shall be +resolved in favor of coverage. For a particular product received by a +particular user, *normally used* refers to a typical or common use of that +class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, +the product. A product is a consumer product regardless of whether the product +has substantial commercial, industrial or non-consumer uses, unless such uses +represent the only significant mode of use of the product. + +*Installation Information* for a User Product means any methods, procedures, +authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified +version of its Corresponding Source. The information must suffice to ensure +that the continued functioning of the modified object code is in no case +prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as part of a +transaction in which the right of possession and use of the User Product is +transferred to the recipient in perpetuity or for a fixed term (regardless of +how the transaction is characterized), the Corresponding Source conveyed under +this section must be accompanied by the Installation Information. But this +requirement does not apply if neither you nor any third party retains the +ability to install modified object code on the User Product (for example, the +work has been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates for a +work that has been modified or installed by the recipient, or for the User +Product in which it has been modified or installed. Access to a network may be +denied when the modification itself materially and adversely affects the +operation of the network or violates the rules and protocols for communication +across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord +with this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require +no special password or key for unpacking, reading or copying. + +### 7. Additional Terms. + +*Additional permissions* are terms that supplement the terms of this License by +making exceptions from one or more of its conditions. Additional permissions +that are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may +be used separately under those permissions, but the entire Program remains +governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when +you modify the work.) You may place additional permissions on material, added +by you to a covered work, for which you have or can give appropriate copyright +permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + + - a) Disclaiming warranty or limiting liability differently from the terms of + sections 15 and 16 of this License; or + - b) Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices displayed + by works containing it; or + - c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in reasonable + ways as different from the original version; or + - d) Limiting the use for publicity purposes of names of licensors or authors + of the material; or + - e) Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or + - f) Requiring indemnification of licensors and authors of that material by + anyone who conveys the material (or modified versions of it) with + contractual assumptions of liability to the recipient, for any liability + that these contractual assumptions directly impose on those licensors and + authors. + +All other non-permissive additional terms are considered *further restrictions* +within the meaning of section 10. If the Program as you received it, or any +part of it, contains a notice stating that it is governed by this License along +with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or +conveying under this License, you may add to a covered work material governed +by the terms of that license document, provided that the further restriction +does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, +in the relevant source files, a statement of the additional terms that apply to +those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements +apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly provided +under this License. Any attempt otherwise to propagate or modify it is void, +and will automatically terminate your rights under this License (including any +patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated + + - a) provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and + - b) permanently, if the copyright holder fails to notify you of the + violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated +permanently if the copyright holder notifies you of the violation by some +reasonable means, this is the first time you have received notice of violation +of this License (for any work) from that copyright holder, and you cure the +violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses +of parties who have received copies or rights from you under this License. If +your rights have been terminated and not permanently reinstated, you do not +qualify to receive new licenses for the same material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy +of the Program. Ancillary propagation of a covered work occurring solely as a +consequence of using peer-to-peer transmission to receive a copy likewise does +not require acceptance. However, nothing other than this License grants you +permission to propagate or modify any covered work. These actions infringe +copyright if you do not accept this License. Therefore, by modifying or +propagating a covered work, you indicate your acceptance of this License to do +so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a +license from the original licensors, to run, modify and propagate that work, +subject to this License. You are not responsible for enforcing compliance by +third parties with this License. + +An *entity transaction* is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered work +results from an entity transaction, each party to that transaction who receives +a copy of the work also receives whatever licenses to the work the party's +predecessor in interest had or could give under the previous paragraph, plus a +right to possession of the Corresponding Source of the work from the +predecessor in interest, if the predecessor has it or can get it with +reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights +granted or affirmed under this License. For example, you may not impose a +license fee, royalty, or other charge for exercise of rights granted under this +License, and you may not initiate litigation (including a cross-claim or +counterclaim in a lawsuit) alleging that any patent claim is infringed by +making, using, selling, offering for sale, or importing the Program or any +portion of it. + +### 11. Patents. + +A *contributor* is a copyright holder who authorizes use under this License of +the Program or a work on which the Program is based. The work thus licensed is +called the contributor's *contributor version*. + +A contributor's *essential patent claims* are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, +that would be infringed by some manner, permitted by this License, of making, +using, or selling its contributor version, but do not include claims that would +be infringed only as a consequence of further modification of the contributor +version. For purposes of this definition, *control* includes the right to grant +patent sublicenses in a manner consistent with the requirements of this +License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent +license under the contributor's essential patent claims, to make, use, sell, +offer for sale, import and otherwise run, modify and propagate the contents of +its contributor version. + +In the following three paragraphs, a *patent license* is any express agreement +or commitment, however denominated, not to enforce a patent (such as an express +permission to practice a patent or covenant not to sue for patent +infringement). To *grant* such a patent license to a party means to make such +an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of +charge and under the terms of this License, through a publicly available +network server or other readily accessible means, then you must either + + 1. cause the Corresponding Source to be so available, or + 2. arrange to deprive yourself of the benefit of the patent license for this + particular work, or + 3. arrange, in a manner consistent with the requirements of this License, to + extend the patent license to downstream recipients. + +*Knowingly relying* means you have actual knowledge that, but for the patent +license, your conveying the covered work in a country, or your recipient's use +of the covered work in a country, would infringe one or more identifiable +patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you +convey, or propagate by procuring conveyance of, a covered work, and grant a +patent license to some of the parties receiving the covered work authorizing +them to use, propagate, modify or convey a specific copy of the covered work, +then the patent license you grant is automatically extended to all recipients +of the covered work and works based on it. + +A patent license is *discriminatory* if it does not include within the scope of +its coverage, prohibits the exercise of, or is conditioned on the non-exercise +of one or more of the rights that are specifically granted under this License. +You may not convey a covered work if you are a party to an arrangement with a +third party that is in the business of distributing software, under which you +make payment to the third party based on the extent of your activity of +conveying the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory patent +license + + - a) in connection with copies of the covered work conveyed by you (or copies + made from those copies), or + - b) primarily for and in connection with specific products or compilations + that contain the covered work, unless you entered into that arrangement, or + that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to +you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not excuse +you from the conditions of this License. If you cannot convey a covered work so +as to satisfy simultaneously your obligations under this License and any other +pertinent obligations, then as a consequence you may not convey it at all. For +example, if you agree to terms that obligate you to collect a royalty for +further conveying from those to whom you convey the Program, the only way you +could satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to +link or combine any covered work with a work licensed under version 3 of the +GNU Affero General Public License into a single combined work, and to convey +the resulting work. The terms of this License will continue to apply to the +part which is the covered work, but the special requirements of the GNU Affero +General Public License, section 13, concerning interaction through a network +will apply to the combination as such. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program specifies +that a certain numbered version of the GNU General Public License *or any later +version* applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by +the Free Software Foundation. If the Program does not specify a version number +of the GNU General Public License, you may choose any version ever published by +the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the +GNU General Public License can be used, that proxy's public statement of +acceptance of a version permanently authorizes you to choose that version for +the Program. + +Later license versions may give you additional or different permissions. +However, no additional obligations are imposed on any author or copyright +holder as a result of your choosing to follow a later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE +LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER +PARTIES PROVIDE THE PROGRAM *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE +THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot +be given local legal effect according to their terms, reviewing courts shall +apply local law that most closely approximates an absolute waiver of all civil +liability in connection with the Program, unless a warranty or assumption of +liability accompanies a copy of the Program in return for a fee. + +## END OF TERMS AND CONDITIONS ### + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively state the exclusion +of warranty; and each file should have at least the *copyright* line and a +pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like +this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w` and `show c` should show the appropriate +parts of the General Public License. Of course, your program's commands might +be different; for a GUI interface, you would use an *about box*. + +You should also get your employer (if you work as a programmer) or school, if +any, to sign a *copyright disclaimer* for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see +[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider +it more useful to permit linking proprietary applications with the library. If +this is what you want to do, use the GNU Lesser General Public License instead +of this License. But first, please read +[http://www.gnu.org/philosophy/why-not-lgpl.html](http://www.gnu.org/philosophy/why-not-lgpl.html). diff --git a/components/furi/src/base.h b/components/furi/src/base.h new file mode 100644 index 00000000..642ff2b6 --- /dev/null +++ b/components/furi/src/base.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FuriWaitForever = 0xFFFFFFFFU, +} FuriWait; + +typedef enum { + FuriFlagWaitAny = 0x00000000U, ///< Wait for any flag (default). + FuriFlagWaitAll = 0x00000001U, ///< Wait for all flags. + FuriFlagNoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for. + + FuriFlagError = 0x80000000U, ///< Error indicator. + FuriFlagErrorUnknown = 0xFFFFFFFFU, ///< FuriStatusError (-1). + FuriFlagErrorTimeout = 0xFFFFFFFEU, ///< FuriStatusErrorTimeout (-2). + FuriFlagErrorResource = 0xFFFFFFFDU, ///< FuriStatusErrorResource (-3). + FuriFlagErrorParameter = 0xFFFFFFFCU, ///< FuriStatusErrorParameter (-4). + FuriFlagErrorISR = 0xFFFFFFFAU, ///< FuriStatusErrorISR (-6). +} FuriFlag; + +typedef enum { + FuriStatusOk = 0, ///< Operation completed successfully. + FuriStatusError = + -1, ///< Unspecified RTOS error: run-time error but no other error message fits. + FuriStatusErrorTimeout = -2, ///< Operation not completed within the timeout period. + FuriStatusErrorResource = -3, ///< Resource not available. + FuriStatusErrorParameter = -4, ///< Parameter error. + FuriStatusErrorNoMemory = + -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation. + FuriStatusErrorISR = + -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines. + FuriStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization. +} FuriStatus; + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/check.c b/components/furi/src/check.c new file mode 100644 index 00000000..c43ea2ec --- /dev/null +++ b/components/furi/src/check.c @@ -0,0 +1,194 @@ +#include "check.h" +#include "common_defines.h" + +#include "furi_hal_console.h" +#include +#include +#include + +PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; +PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[13] = {0}; + +/** Load r12 value to __furi_check_message and store registers to __furi_check_registers */ +/*#define GET_MESSAGE_AND_STORE_REGISTERS() \ + asm volatile("ldr r11, =__furi_check_message \n" \ + "str r12, [r11] \n" \ + "ldr r12, =__furi_check_registers \n" \ + "stm r12, {r0-r11} \n" \ + "str lr, [r12, #48] \n" \ + : \ + : \ + : "memory");*/ + +/** Restore registers and halt MCU + * + * - Always use it with GET_MESSAGE_AND_STORE_REGISTERS + * - If debugger is(was) connected this routine will raise bkpt + * - If debugger is not connected then endless loop + * + */ +/*#define RESTORE_REGISTERS_AND_HALT_MCU(debug) \ + register bool a0 asm("a0") = debug; \ + asm volatile("cbnz a0, with_debugger%= \n" \ + "ldr a12, =__furi_check_registers\n" \ + "ldm a12, {a0-a11} \n" \ + "loop%=: \n" \ + "wfi \n" \ + "b loop%= \n" \ + "with_debugger%=: \n" \ + "ldr a12, =__furi_check_registers\n" \ + "ldm a12, {a0-a11} \n" \ + "debug_loop%=: \n" \ + "bkpt 0x00 \n" \ + "wfi \n" \ + "b debug_loop%= \n" \ + : \ + : "a"(a0) \ + : "memory");*/ + +extern size_t xPortGetTotalHeapSize(void); + +static void __furi_put_uint32_as_text(uint32_t data) { + char tmp_str[] = "-2147483648"; + itoa(data, tmp_str, 10); + furi_hal_console_puts(tmp_str); +} + +static void __furi_put_uint32_as_hex(uint32_t data) { + char tmp_str[] = "0xFFFFFFFF"; + itoa(data, tmp_str, 16); + furi_hal_console_puts(tmp_str); +} + +static void __furi_print_register_info() { + // Print registers + for(uint8_t i = 0; i < 12; i++) { + furi_hal_console_puts("\r\n\tr"); + __furi_put_uint32_as_text(i); + furi_hal_console_puts(" : "); + __furi_put_uint32_as_hex(__furi_check_registers[i]); + } + + furi_hal_console_puts("\r\n\tlr : "); + __furi_put_uint32_as_hex(__furi_check_registers[12]); +} + +static void __furi_print_stack_info() { + furi_hal_console_puts("\r\n\tstack watermark: "); + __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); +} + +static void __furi_print_bt_stack_info() { +// const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info(); +// if(fault_info == NULL) { +// furi_hal_console_puts("\r\n\tcore2: not faulted"); +// } else { +// furi_hal_console_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); +// __furi_put_uint32_as_hex(fault_info->source_pc); +// furi_hal_console_puts("\r\n\tLR: "); +// __furi_put_uint32_as_hex(fault_info->source_lr); +// furi_hal_console_puts("\r\n\tSP: "); +// __furi_put_uint32_as_hex(fault_info->source_sp); +// } +} + +static void __furi_print_heap_info() { +// furi_hal_console_puts("\r\n\t heap total: "); +// __furi_put_uint32_as_text(xPortGetTotalHeapSize()); + furi_hal_console_puts("\r\n\t heap free: "); + __furi_put_uint32_as_text(xPortGetFreeHeapSize()); + furi_hal_console_puts("\r\n\t heap watermark: "); + __furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize()); +} + +static void __furi_print_name(bool isr) { + if(isr) { + furi_hal_console_puts("[ISR "); + __furi_put_uint32_as_text(__get_IPSR()); + furi_hal_console_puts("] "); + } else { + const char* name = pcTaskGetName(NULL); + if(name == NULL) { + furi_hal_console_puts("[main] "); + } else { + furi_hal_console_puts("["); + furi_hal_console_puts(name); + furi_hal_console_puts("] "); + } + } +} + +FURI_NORETURN void __furi_crash_implementation() { + __disable_irq(); +// GET_MESSAGE_AND_STORE_REGISTERS(); + + bool isr = FURI_IS_IRQ_MODE(); + + if(__furi_check_message == NULL) { + __furi_check_message = "Fatal Error"; + } else if(__furi_check_message == (void*)__FURI_ASSERT_MESSAGE_FLAG) { + __furi_check_message = "furi_assert failed"; + } else if(__furi_check_message == (void*)__FURI_CHECK_MESSAGE_FLAG) { + __furi_check_message = "furi_check failed"; + } + + furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); + __furi_print_name(isr); + furi_hal_console_puts(__furi_check_message); + + __furi_print_register_info(); + if(!isr) { + __furi_print_stack_info(); + } + __furi_print_heap_info(); + __furi_print_bt_stack_info(); + + // Check if debug enabled by DAP + // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en +// bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; + bool debug = true; +#ifdef FURI_NDEBUG + if(debug) { +#endif + furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); + furi_hal_console_puts("\033[0m\r\n"); +// furi_hal_debug_enable(); + + esp_system_abort("crash"); +#ifdef FURI_NDEBUG + } else { + uint32_t ptr = (uint32_t)__furi_check_message; + if(ptr < FLASH_BASE || ptr > (FLASH_BASE + FLASH_SIZE)) { + ptr = (uint32_t) "Check serial logs"; + } + furi_hal_rtc_set_fault_data(ptr); + furi_hal_console_puts("\r\nRebooting system.\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + esp_system_abort("crash"); + } +#endif + __builtin_unreachable(); +} +FURI_NORETURN void __furi_halt_implementation() { + __disable_irq(); +// GET_MESSAGE_AND_STORE_REGISTERS(); + + bool isr = FURI_IS_IRQ_MODE(); + + if(__furi_check_message == NULL) { + __furi_check_message = "System halt requested."; + } + + furi_hal_console_puts("\r\n\033[0;31m[HALT]"); + __furi_print_name(isr); + furi_hal_console_puts(__furi_check_message); + furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + + // Check if debug enabled by DAP + // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en +// bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; +// RESTORE_REGISTERS_AND_HALT_MCU(true); + + __builtin_unreachable(); +} diff --git a/components/furi/src/check.h b/components/furi/src/check.h new file mode 100644 index 00000000..2f43ae9f --- /dev/null +++ b/components/furi/src/check.h @@ -0,0 +1,109 @@ +/** + * @file check.h + * + * Furi crash and assert functions. + * + * The main problem with crashing is that you can't do anything without disturbing registers, + * and if you disturb registers, you won't be able to see the correct register values in the debugger. + * + * Current solution works around it by passing the message through r12 and doing some magic with registers in crash function. + * r0-r10 are stored in the ram2 on crash routine start and restored at the end. + * The only register that is going to be lost is r11. + * + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#define FURI_NORETURN [[noreturn]] +#else +#include +#define FURI_NORETURN noreturn +#endif + +// Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls. +#define __FURI_ASSERT_MESSAGE_FLAG (0x01) +#define __FURI_CHECK_MESSAGE_FLAG (0x02) + +/** Crash system */ +FURI_NORETURN void __furi_crash_implementation(); + +/** Halt system */ +FURI_NORETURN void __furi_halt_implementation(); + +/** Crash system with message. */ +#define __furi_crash(message) \ + do { \ + ESP_LOGE("crash", "%s\n\tat %s:%d", (message) ? (message) : "", __FILE__, __LINE__); \ + __furi_crash_implementation(); \ + } while(0) + +/** Crash system + * + * @param optional message (const char*) + */ +#define furi_crash(...) M_APPLY(__furi_crash, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) + +/** Halt system with message. */ +#define __furi_halt(message) \ + do { \ + ESP_LOGE("halt", "%s\n\tat %s:%d", (message) ? (message) : "", __FILE__, __LINE__); \ + __furi_halt_implementation(); \ + } while(0) + +/** Halt system + * + * @param optional message (const char*) + */ +#define furi_halt(...) M_APPLY(__furi_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) + +/** Check condition and crash if check failed */ +#define __furi_check(__e, __m) \ + do { \ + if(!(__e)) { \ + ESP_LOGE("check", "%s", #__e); \ + __furi_crash(__m); \ + } \ + } while(0) + +/** Check condition and crash if failed + * + * @param condition to check + * @param optional message (const char*) + */ +#define furi_check(...) \ + M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__)) + +/** Only in debug build: Assert condition and crash if assert failed */ +#ifdef FURI_DEBUG +#define __furi_assert(__e, __m) \ + do { \ + if(!(__e)) { \ + ESP_LOGE("assert", "%s", #__e); \ + __furi_crash(__m); \ + } \ + } while(0) +#else +#define __furi_assert(__e, __m) \ + do { \ + ((void)(__e)); \ + ((void)(__m)); \ + } while(0) +#endif + +/** Assert condition and crash if failed + * + * @warning only will do check if firmware compiled in debug mode + * + * @param condition to check + * @param optional message (const char*) + */ +#define furi_assert(...) \ + M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__)) + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/common_defines.h b/components/furi/src/common_defines.h new file mode 100644 index 00000000..b0062e65 --- /dev/null +++ b/components/furi/src/common_defines.h @@ -0,0 +1,60 @@ +#pragma once + +#include "core_defines.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef FURI_WARN_UNUSED +#define FURI_WARN_UNUSED __attribute__((warn_unused_result)) +#endif + +#ifndef FURI_WEAK +#define FURI_WEAK __attribute__((weak)) +#endif + +#ifndef FURI_PACKED +#define FURI_PACKED __attribute__((packed)) +#endif + +#ifndef FURI_IS_IRQ_MASKED +#define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U) +#endif + +#ifndef FURI_IS_IRQ_MODE +#define FURI_IS_IRQ_MODE() (__get_IPSR() != 0U) +#endif + +#ifndef FURI_IS_ISR +#define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) +#endif + +typedef struct { + uint32_t isrm; + bool from_isr; + bool kernel_running; +} __FuriCriticalInfo; + +__FuriCriticalInfo __furi_critical_enter(void); + +void __furi_critical_exit(__FuriCriticalInfo info); + +#ifndef FURI_CRITICAL_ENTER +#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter(); +#endif + +#ifndef FURI_CRITICAL_EXIT +#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info); +#endif + +#ifndef FURI_CHECK_RETURN +#define FURI_CHECK_RETURN __attribute__((__warn_unused_result__)) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/core_defines.h b/components/furi/src/core_defines.h new file mode 100644 index 00000000..4309c20c --- /dev/null +++ b/components/furi/src/core_defines.h @@ -0,0 +1,116 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define FURI_RETURNS_NONNULL __attribute__((returns_nonnull)) + +#ifndef MAX +#define MAX(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif + +#ifndef MIN +#define MIN(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#endif + +#ifndef ABS +#define ABS(a) ({ (a) < 0 ? -(a) : (a); }) +#endif + +#ifndef ROUND_UP_TO +#define ROUND_UP_TO(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a / _b + !!(_a % _b); \ + }) +#endif + +#ifndef CLAMP +#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) +#endif + +#ifndef COUNT_OF +#define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) +#endif + +#ifndef FURI_SWAP +#define FURI_SWAP(x, y) \ + do { \ + typeof(x) SWAP = x; \ + x = y; \ + y = SWAP; \ + } while(0) +#endif + +#ifndef PLACE_IN_SECTION +#define PLACE_IN_SECTION(x) __attribute__((section(x))) +#endif + +#ifndef ALIGN +#define ALIGN(n) __attribute__((aligned(n))) +#endif + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +#ifndef UNUSED +#define UNUSED(X) (void)(X) +#endif + +#ifndef STRINGIFY +#define STRINGIFY(x) #x +#endif + +#ifndef TOSTRING +#define TOSTRING(x) STRINGIFY(x) +#endif + +#ifndef CONCATENATE +#define CONCATENATE(a, b) CONCATENATE_(a, b) +#define CONCATENATE_(a, b) a##b +#endif + +#ifndef REVERSE_BYTES_U32 +#define REVERSE_BYTES_U32(x) \ + ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ + (((x)&0xFF000000) >> 24)) +#endif + +#ifndef FURI_BIT +#define FURI_BIT(x, n) (((x) >> (n)) & 1) +#endif + +#ifndef FURI_BIT_SET +#define FURI_BIT_SET(x, n) \ + ({ \ + __typeof__(x) _x = (1); \ + (x) |= (_x << (n)); \ + }) +#endif + +#ifndef FURI_BIT_CLEAR +#define FURI_BIT_CLEAR(x, n) \ + ({ \ + __typeof__(x) _x = (1); \ + (x) &= ~(_x << (n)); \ + }) +#endif + +#define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/critical.c b/components/furi/src/critical.c new file mode 100644 index 00000000..f3d57930 --- /dev/null +++ b/components/furi/src/critical.c @@ -0,0 +1,34 @@ +#include "common_defines.h" + +#include +#include + +static portMUX_TYPE prv_critical_mutex; + +__FuriCriticalInfo __furi_critical_enter(void) { + __FuriCriticalInfo info; + + info.isrm = 0; + info.from_isr = FURI_IS_ISR(); + info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); + + if(info.from_isr) { + info.isrm = taskENTER_CRITICAL_FROM_ISR(); + } else if(info.kernel_running) { + taskENTER_CRITICAL(&prv_critical_mutex); + } else { + __disable_irq(); + } + + return info; +} + +void __furi_critical_exit(__FuriCriticalInfo info) { + if(info.from_isr) { + taskEXIT_CRITICAL_FROM_ISR(info.isrm); + } else if(info.kernel_running) { + taskEXIT_CRITICAL(&prv_critical_mutex); + } else { + __enable_irq(); + } +} \ No newline at end of file diff --git a/components/furi/src/event_flag.c b/components/furi/src/event_flag.c new file mode 100644 index 00000000..5d4e0d5d --- /dev/null +++ b/components/furi/src/event_flag.c @@ -0,0 +1,140 @@ +#include "event_flag.h" +#include "common_defines.h" +#include "check.h" + +#include +#include + +#define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U +#define FURI_EVENT_FLAG_INVALID_BITS (~((1UL << FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U)) + +FuriEventFlag* furi_event_flag_alloc() { + furi_assert(!FURI_IS_IRQ_MODE()); + + EventGroupHandle_t handle = xEventGroupCreate(); + furi_check(handle); + + return ((FuriEventFlag*)handle); +} + +void furi_event_flag_free(FuriEventFlag* instance) { + furi_assert(!FURI_IS_IRQ_MODE()); + vEventGroupDelete((EventGroupHandle_t)instance); +} + +uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { + furi_assert(instance); + furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U); + + EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; + uint32_t rflags; + BaseType_t yield; + + if(FURI_IS_IRQ_MODE()) { + yield = pdFALSE; + if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { + rflags = (uint32_t)FuriFlagErrorResource; + } else { + rflags = flags; + portYIELD_FROM_ISR(yield); + } + } else { + rflags = xEventGroupSetBits(hEventGroup, (EventBits_t)flags); + } + + /* Return event flags after setting */ + return (rflags); +} + +uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { + furi_assert(instance); + furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U); + + EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; + uint32_t rflags; + + if(FURI_IS_IRQ_MODE()) { + rflags = xEventGroupGetBitsFromISR(hEventGroup); + + if(xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) { + rflags = (uint32_t)FuriStatusErrorResource; + } else { + /* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */ + /* Yield is required here otherwise clear operation might not execute in the right order. */ + /* See https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/93 for more info. */ + portYIELD_FROM_ISR(pdTRUE); + } + } else { + rflags = xEventGroupClearBits(hEventGroup, (EventBits_t)flags); + } + + /* Return event flags before clearing */ + return (rflags); +} + +uint32_t furi_event_flag_get(FuriEventFlag* instance) { + furi_assert(instance); + + EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; + uint32_t rflags; + + if(FURI_IS_IRQ_MODE()) { + rflags = xEventGroupGetBitsFromISR(hEventGroup); + } else { + rflags = xEventGroupGetBits(hEventGroup); + } + + /* Return current event flags */ + return (rflags); +} + +uint32_t furi_event_flag_wait( + FuriEventFlag* instance, + uint32_t flags, + uint32_t options, + uint32_t timeout) { + furi_assert(!FURI_IS_IRQ_MODE()); + furi_assert(instance); + furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U); + + EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; + BaseType_t wait_all; + BaseType_t exit_clr; + uint32_t rflags; + + if(options & FuriFlagWaitAll) { + wait_all = pdTRUE; + } else { + wait_all = pdFAIL; + } + + if(options & FuriFlagNoClear) { + exit_clr = pdFAIL; + } else { + exit_clr = pdTRUE; + } + + rflags = xEventGroupWaitBits( + hEventGroup, (EventBits_t)flags, exit_clr, wait_all, (TickType_t)timeout); + + if(options & FuriFlagWaitAll) { + if((flags & rflags) != flags) { + if(timeout > 0U) { + rflags = (uint32_t)FuriStatusErrorTimeout; + } else { + rflags = (uint32_t)FuriStatusErrorResource; + } + } + } else { + if((flags & rflags) == 0U) { + if(timeout > 0U) { + rflags = (uint32_t)FuriStatusErrorTimeout; + } else { + rflags = (uint32_t)FuriStatusErrorResource; + } + } + } + + /* Return event flags before clearing */ + return (rflags); +} diff --git a/components/furi/src/event_flag.h b/components/furi/src/event_flag.h new file mode 100644 index 00000000..133c95e7 --- /dev/null +++ b/components/furi/src/event_flag.h @@ -0,0 +1,70 @@ +/** + * @file event_flag.h + * Furi Event Flag + */ +#pragma once + +#include "base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void FuriEventFlag; + +/** Allocate FuriEventFlag + * + * @return pointer to FuriEventFlag + */ +FuriEventFlag* furi_event_flag_alloc(); + +/** Deallocate FuriEventFlag + * + * @param instance pointer to FuriEventFlag + */ +void furi_event_flag_free(FuriEventFlag* instance); + +/** Set flags + * + * @param instance pointer to FuriEventFlag + * @param[in] flags The flags + * + * @return Resulting flags or error (FuriStatus) + */ +uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags); + +/** Clear flags + * + * @param instance pointer to FuriEventFlag + * @param[in] flags The flags + * + * @return Resulting flags or error (FuriStatus) + */ +uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags); + +/** Get flags + * + * @param instance pointer to FuriEventFlag + * + * @return Resulting flags + */ +uint32_t furi_event_flag_get(FuriEventFlag* instance); + +/** Wait flags + * + * @param instance pointer to FuriEventFlag + * @param[in] flags The flags + * @param[in] options The option flags + * @param[in] timeout The timeout + * + * @return Resulting flags or error (FuriStatus) + */ +uint32_t furi_event_flag_wait( + FuriEventFlag* instance, + uint32_t flags, + uint32_t options, + uint32_t timeout); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/furi_config.h b/components/furi/src/furi_config.h new file mode 100644 index 00000000..c935611a --- /dev/null +++ b/components/furi/src/furi_config.h @@ -0,0 +1,3 @@ +#pragma once + +#define FURI_CONFIG_THREAD_MAX_PRIORITIES (32) \ No newline at end of file diff --git a/components/furi/src/furi_hal_console.c b/components/furi/src/furi_hal_console.c new file mode 100644 index 00000000..3d5ae9a6 --- /dev/null +++ b/components/furi/src/furi_hal_console.c @@ -0,0 +1,109 @@ +#include "furi_hal_console.h" +#include "common_defines.h" +#include "furi_string.h" + +#include +#include +#include + +#define TAG "FuriHalConsole" + +#ifdef HEAP_PRINT_DEBUG +#define CONSOLE_BAUDRATE 1843200 +#else +#define CONSOLE_BAUDRATE 230400 +#endif + +typedef struct { + bool alive; + FuriHalConsoleTxCallback tx_callback; + void* tx_callback_context; +} FuriHalConsole; + +FuriHalConsole furi_hal_console = { + .alive = false, + .tx_callback = NULL, + .tx_callback_context = NULL, +}; + +void furi_hal_console_init() { +// furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); + furi_hal_console.alive = true; +} + +void furi_hal_console_enable() { +// furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); +// while(!LL_USART_IsActiveFlag_TC(USART1)) +// ; +// furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); + furi_hal_console.alive = true; +} + +void furi_hal_console_disable() { +// while(!LL_USART_IsActiveFlag_TC(USART1)) +// ; + furi_hal_console.alive = false; +} + +void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context) { + FURI_CRITICAL_ENTER(); + furi_hal_console.tx_callback = callback; + furi_hal_console.tx_callback_context = context; + FURI_CRITICAL_EXIT(); +} + +void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { + if(!furi_hal_console.alive) return; + + FURI_CRITICAL_ENTER(); + // Transmit data + + if(furi_hal_console.tx_callback) { + furi_hal_console.tx_callback(buffer, buffer_size, furi_hal_console.tx_callback_context); + } + + char safe_buffer[buffer_size + 1]; + memcpy(safe_buffer, buffer, buffer_size); + safe_buffer[buffer_size] = 0; + + ESP_LOGI(TAG, "%s", safe_buffer); +// furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); +//// Wait for TC flag to be raised for last char +// while(!LL_USART_IsActiveFlag_TC(USART1)) +// ; + FURI_CRITICAL_EXIT(); +} + +void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) { + if(!furi_hal_console.alive) return; + + FURI_CRITICAL_ENTER(); + + char safe_buffer[buffer_size + 1]; + memcpy(safe_buffer, buffer, buffer_size); + safe_buffer[buffer_size] = 0; + ESP_LOGI(TAG, "%s", safe_buffer); + + // Transmit data +// furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); + // Transmit new line symbols +// furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2); + // Wait for TC flag to be raised for last char +// while(!LL_USART_IsActiveFlag_TC(USART1)) +// ; + FURI_CRITICAL_EXIT(); +} + +void furi_hal_console_printf(const char format[], ...) { + FuriString* string; + va_list args; + va_start(args, format); + string = furi_string_alloc_vprintf(format, args); + va_end(args); + furi_hal_console_tx((const uint8_t*)furi_string_get_cstr(string), furi_string_size(string)); + furi_string_free(string); +} + +void furi_hal_console_puts(const char* data) { + furi_hal_console_tx((const uint8_t*)data, strlen(data)); +} diff --git a/components/furi/src/furi_hal_console.h b/components/furi/src/furi_hal_console.h new file mode 100644 index 00000000..ce31a66b --- /dev/null +++ b/components/furi/src/furi_hal_console.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*FuriHalConsoleTxCallback)(const uint8_t* buffer, size_t size, void* context); + +void furi_hal_console_init(); + +void furi_hal_console_enable(); + +void furi_hal_console_disable(); + +void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context); + +void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); + +void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size); + +/** + * Printf-like plain uart interface + * @warning Will not work in ISR context + * @param format + * @param ... + */ +void furi_hal_console_printf(const char format[], ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); + +void furi_hal_console_puts(const char* data); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/furi_string.c b/components/furi/src/furi_string.c new file mode 100644 index 00000000..b5612565 --- /dev/null +++ b/components/furi/src/furi_string.c @@ -0,0 +1,304 @@ +#include "furi_string.h" +#include + +struct FuriString { + string_t string; +}; + +#undef furi_string_alloc_set +#undef furi_string_set +#undef furi_string_cmp +#undef furi_string_cmpi +#undef furi_string_search +#undef furi_string_search_str +#undef furi_string_equal +#undef furi_string_replace +#undef furi_string_replace_str +#undef furi_string_replace_all +#undef furi_string_start_with +#undef furi_string_end_with +#undef furi_string_search_char +#undef furi_string_search_rchar +#undef furi_string_trim +#undef furi_string_cat + +FuriString* furi_string_alloc() { + FuriString* string = malloc(sizeof(FuriString)); + string_init(string->string); + return string; +} + +FuriString* furi_string_alloc_set(const FuriString* s) { + FuriString* string = malloc(sizeof(FuriString)); //-V799 + string_init_set(string->string, s->string); + return string; +} //-V773 + +FuriString* furi_string_alloc_set_str(const char cstr[]) { + FuriString* string = malloc(sizeof(FuriString)); //-V799 + string_init_set(string->string, cstr); + return string; +} //-V773 + +FuriString* furi_string_alloc_printf(const char format[], ...) { + va_list args; + va_start(args, format); + FuriString* string = furi_string_alloc_vprintf(format, args); + va_end(args); + return string; +} + +FuriString* furi_string_alloc_vprintf(const char format[], va_list args) { + FuriString* string = malloc(sizeof(FuriString)); + string_init_vprintf(string->string, format, args); + return string; +} + +FuriString* furi_string_alloc_move(FuriString* s) { + FuriString* string = malloc(sizeof(FuriString)); + string_init_move(string->string, s->string); + free(s); + return string; +} + +void furi_string_free(FuriString* s) { + string_clear(s->string); + free(s); +} + +void furi_string_reserve(FuriString* s, size_t alloc) { + string_reserve(s->string, alloc); +} + +void furi_string_reset(FuriString* s) { + string_reset(s->string); +} + +void furi_string_swap(FuriString* v1, FuriString* v2) { + string_swap(v1->string, v2->string); +} + +void furi_string_move(FuriString* v1, FuriString* v2) { + string_clear(v1->string); + string_init_move(v1->string, v2->string); + free(v2); +} + +size_t furi_string_hash(const FuriString* v) { + return string_hash(v->string); +} + +char furi_string_get_char(const FuriString* v, size_t index) { + return string_get_char(v->string, index); +} + +const char* furi_string_get_cstr(const FuriString* s) { + return string_get_cstr(s->string); +} + +void furi_string_set(FuriString* s, FuriString* source) { + string_set(s->string, source->string); +} + +void furi_string_set_str(FuriString* s, const char cstr[]) { + string_set(s->string, cstr); +} + +void furi_string_set_strn(FuriString* s, const char str[], size_t n) { + string_set_strn(s->string, str, n); +} + +void furi_string_set_char(FuriString* s, size_t index, const char c) { + string_set_char(s->string, index, c); +} + +int furi_string_cmp(const FuriString* s1, const FuriString* s2) { + return string_cmp(s1->string, s2->string); +} + +int furi_string_cmp_str(const FuriString* s1, const char str[]) { + return string_cmp(s1->string, str); +} + +int furi_string_cmpi(const FuriString* v1, const FuriString* v2) { + return string_cmpi(v1->string, v2->string); +} + +int furi_string_cmpi_str(const FuriString* v1, const char p2[]) { + return string_cmpi_str(v1->string, p2); +} + +size_t furi_string_search(const FuriString* v, const FuriString* needle, size_t start) { + return string_search(v->string, needle->string, start); +} + +size_t furi_string_search_str(const FuriString* v, const char needle[], size_t start) { + return string_search(v->string, needle, start); +} + +bool furi_string_equal(const FuriString* v1, const FuriString* v2) { + return string_equal_p(v1->string, v2->string); +} + +bool furi_string_equal_str(const FuriString* v1, const char v2[]) { + return string_equal_p(v1->string, v2); +} + +void furi_string_push_back(FuriString* v, char c) { + string_push_back(v->string, c); +} + +size_t furi_string_size(const FuriString* s) { + return string_size(s->string); +} + +int furi_string_printf(FuriString* v, const char format[], ...) { + va_list args; + va_start(args, format); + int result = furi_string_vprintf(v, format, args); + va_end(args); + return result; +} + +int furi_string_vprintf(FuriString* v, const char format[], va_list args) { + return string_vprintf(v->string, format, args); +} + +int furi_string_cat_printf(FuriString* v, const char format[], ...) { + va_list args; + va_start(args, format); + int result = furi_string_cat_vprintf(v, format, args); + va_end(args); + return result; +} + +int furi_string_cat_vprintf(FuriString* v, const char format[], va_list args) { + FuriString* string = furi_string_alloc(); + int ret = furi_string_vprintf(string, format, args); + furi_string_cat(v, string); + furi_string_free(string); + return ret; +} + +bool furi_string_empty(const FuriString* v) { + return string_empty_p(v->string); +} + +void furi_string_replace_at(FuriString* v, size_t pos, size_t len, const char str2[]) { + string_replace_at(v->string, pos, len, str2); +} + +size_t + furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start) { + return string_replace(string->string, needle->string, replace->string, start); +} + +size_t furi_string_replace_str(FuriString* v, const char str1[], const char str2[], size_t start) { + return string_replace_str(v->string, str1, str2, start); +} + +void furi_string_replace_all_str(FuriString* v, const char str1[], const char str2[]) { + string_replace_all_str(v->string, str1, str2); +} + +void furi_string_replace_all(FuriString* v, const FuriString* str1, const FuriString* str2) { + string_replace_all(v->string, str1->string, str2->string); +} + +bool furi_string_start_with(const FuriString* v, const FuriString* v2) { + return string_start_with_string_p(v->string, v2->string); +} + +bool furi_string_start_with_str(const FuriString* v, const char str[]) { + return string_start_with_str_p(v->string, str); +} + +bool furi_string_end_with(const FuriString* v, const FuriString* v2) { + return string_end_with_string_p(v->string, v2->string); +} + +bool furi_string_end_with_str(const FuriString* v, const char str[]) { + return string_end_with_str_p(v->string, str); +} + +size_t furi_string_search_char(const FuriString* v, char c, size_t start) { + return string_search_char(v->string, c, start); +} + +size_t furi_string_search_rchar(const FuriString* v, char c, size_t start) { + return string_search_rchar(v->string, c, start); +} + +void furi_string_left(FuriString* v, size_t index) { + string_left(v->string, index); +} + +void furi_string_right(FuriString* v, size_t index) { + string_right(v->string, index); +} + +void furi_string_mid(FuriString* v, size_t index, size_t size) { + string_mid(v->string, index, size); +} + +void furi_string_trim(FuriString* v, const char charac[]) { + string_strim(v->string, charac); +} + +void furi_string_cat(FuriString* v, const FuriString* v2) { + string_cat(v->string, v2->string); +} + +void furi_string_cat_str(FuriString* v, const char str[]) { + string_cat(v->string, str); +} + +void furi_string_set_n(FuriString* v, const FuriString* ref, size_t offset, size_t length) { + string_set_n(v->string, ref->string, offset, length); +} + +size_t furi_string_utf8_length(FuriString* str) { + return string_length_u(str->string); +} + +void furi_string_utf8_push(FuriString* str, FuriStringUnicodeValue u) { + string_push_u(str->string, u); +} + +static m_str1ng_utf8_state_e furi_state_to_state(FuriStringUTF8State state) { + switch(state) { + case FuriStringUTF8StateStarting: + return M_STR1NG_UTF8_STARTING; + case FuriStringUTF8StateDecoding1: + return M_STR1NG_UTF8_DECODING_1; + case FuriStringUTF8StateDecoding2: + return M_STR1NG_UTF8_DECODING_2; + case FuriStringUTF8StateDecoding3: + return M_STR1NG_UTF8_DECODING_3; + default: + return M_STR1NG_UTF8_ERROR; + } +} + +static FuriStringUTF8State state_to_furi_state(m_str1ng_utf8_state_e state) { + switch(state) { + case M_STR1NG_UTF8_STARTING: + return FuriStringUTF8StateStarting; + case M_STR1NG_UTF8_DECODING_1: + return FuriStringUTF8StateDecoding1; + case M_STR1NG_UTF8_DECODING_2: + return FuriStringUTF8StateDecoding2; + case M_STR1NG_UTF8_DECODING_3: + return FuriStringUTF8StateDecoding3; + default: + return FuriStringUTF8StateError; + } +} + +void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode) { + string_unicode_t m_u = *unicode; + m_str1ng_utf8_state_e m_state = furi_state_to_state(*state); + m_str1ng_utf8_decode(c, &m_state, &m_u); + *state = state_to_furi_state(m_state); + *unicode = m_u; +} diff --git a/components/furi/src/furi_string.h b/components/furi/src/furi_string.h new file mode 100644 index 00000000..7529deac --- /dev/null +++ b/components/furi/src/furi_string.h @@ -0,0 +1,738 @@ +/** + * @file string.h + * Furi string primitive + */ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Furi string failure constant. + */ +#define FURI_STRING_FAILURE ((size_t)-1) + +/** + * @brief Furi string primitive. + */ +typedef struct FuriString FuriString; + +//--------------------------------------------------------------------------- +// Constructors +//--------------------------------------------------------------------------- + +/** + * @brief Allocate new FuriString. + * @return FuriString* + */ +FuriString* furi_string_alloc(); + +/** + * @brief Allocate new FuriString and set it to string. + * Allocate & Set the string a to the string. + * @param source + * @return FuriString* + */ +FuriString* furi_string_alloc_set(const FuriString* source); + +/** + * @brief Allocate new FuriString and set it to C string. + * Allocate & Set the string a to the C string. + * @param cstr_source + * @return FuriString* + */ +FuriString* furi_string_alloc_set_str(const char cstr_source[]); + +/** + * @brief Allocate new FuriString and printf to it. + * Initialize and set a string to the given formatted value. + * @param format + * @param ... + * @return FuriString* + */ +FuriString* furi_string_alloc_printf(const char format[], ...) + _ATTRIBUTE((__format__(__printf__, 1, 2))); + +/** + * @brief Allocate new FuriString and printf to it. + * Initialize and set a string to the given formatted value. + * @param format + * @param args + * @return FuriString* + */ +FuriString* furi_string_alloc_vprintf(const char format[], va_list args); + +/** + * @brief Allocate new FuriString and move source string content to it. + * Allocate the string, set it to the other one, and destroy the other one. + * @param source + * @return FuriString* + */ +FuriString* furi_string_alloc_move(FuriString* source); + +//--------------------------------------------------------------------------- +// Destructors +//--------------------------------------------------------------------------- + +/** + * @brief Free FuriString. + * @param string + */ +void furi_string_free(FuriString* string); + +//--------------------------------------------------------------------------- +// String memory management +//--------------------------------------------------------------------------- + +/** + * @brief Reserve memory for string. + * Modify the string capacity to be able to handle at least 'alloc' characters (including final null char). + * @param string + * @param size + */ +void furi_string_reserve(FuriString* string, size_t size); + +/** + * @brief Reset string. + * Make the string empty. + * @param s + */ +void furi_string_reset(FuriString* string); + +/** + * @brief Swap two strings. + * Swap the two strings string_1 and string_2. + * @param string_1 + * @param string_2 + */ +void furi_string_swap(FuriString* string_1, FuriString* string_2); + +/** + * @brief Move string_2 content to string_1. + * Set the string to the other one, and destroy the other one. + * @param string_1 + * @param string_2 + */ +void furi_string_move(FuriString* string_1, FuriString* string_2); + +/** + * @brief Compute a hash for the string. + * @param string + * @return size_t + */ +size_t furi_string_hash(const FuriString* string); + +/** + * @brief Get string size (usually length, but not for UTF-8) + * @param string + * @return size_t + */ +size_t furi_string_size(const FuriString* string); + +/** + * @brief Check that string is empty or not + * @param string + * @return bool + */ +bool furi_string_empty(const FuriString* string); + +//--------------------------------------------------------------------------- +// Getters +//--------------------------------------------------------------------------- + +/** + * @brief Get the character at the given index. + * Return the selected character of the string. + * @param string + * @param index + * @return char + */ +char furi_string_get_char(const FuriString* string, size_t index); + +/** + * @brief Return the string view a classic C string. + * @param string + * @return const char* + */ +const char* furi_string_get_cstr(const FuriString* string); + +//--------------------------------------------------------------------------- +// Setters +//--------------------------------------------------------------------------- + +/** + * @brief Set the string to the other string. + * Set the string to the source string. + * @param string + * @param source + */ +void furi_string_set(FuriString* string, FuriString* source); + +/** + * @brief Set the string to the other C string. + * Set the string to the source C string. + * @param string + * @param source + */ +void furi_string_set_str(FuriString* string, const char source[]); + +/** + * @brief Set the string to the n first characters of the C string. + * @param string + * @param source + * @param length + */ +void furi_string_set_strn(FuriString* string, const char source[], size_t length); + +/** + * @brief Set the character at the given index. + * @param string + * @param index + * @param c + */ +void furi_string_set_char(FuriString* string, size_t index, const char c); + +/** + * @brief Set the string to the n first characters of other one. + * @param string + * @param source + * @param offset + * @param length + */ +void furi_string_set_n(FuriString* string, const FuriString* source, size_t offset, size_t length); + +/** + * @brief Format in the string the given printf format + * @param string + * @param format + * @param ... + * @return int + */ +int furi_string_printf(FuriString* string, const char format[], ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); + +/** + * @brief Format in the string the given printf format + * @param string + * @param format + * @param args + * @return int + */ +int furi_string_vprintf(FuriString* string, const char format[], va_list args); + +//--------------------------------------------------------------------------- +// Appending +//--------------------------------------------------------------------------- + +/** + * @brief Append a character to the string. + * @param string + * @param c + */ +void furi_string_push_back(FuriString* string, char c); + +/** + * @brief Append a string to the string. + * Concatenate the string with the other string. + * @param string_1 + * @param string_2 + */ +void furi_string_cat(FuriString* string_1, const FuriString* string_2); + +/** + * @brief Append a C string to the string. + * Concatenate the string with the C string. + * @param string_1 + * @param cstring_2 + */ +void furi_string_cat_str(FuriString* string_1, const char cstring_2[]); + +/** + * @brief Append to the string the formatted string of the given printf format. + * @param string + * @param format + * @param ... + * @return int + */ +int furi_string_cat_printf(FuriString* string, const char format[], ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); + +/** + * @brief Append to the string the formatted string of the given printf format. + * @param string + * @param format + * @param args + * @return int + */ +int furi_string_cat_vprintf(FuriString* string, const char format[], va_list args); + +//--------------------------------------------------------------------------- +// Comparators +//--------------------------------------------------------------------------- + +/** + * @brief Compare two strings and return the sort order. + * @param string_1 + * @param string_2 + * @return int + */ +int furi_string_cmp(const FuriString* string_1, const FuriString* string_2); + +/** + * @brief Compare string with C string and return the sort order. + * @param string_1 + * @param cstring_2 + * @return int + */ +int furi_string_cmp_str(const FuriString* string_1, const char cstring_2[]); + +/** + * @brief Compare two strings (case insensitive according to the current locale) and return the sort order. + * Note: doesn't work with UTF-8 strings. + * @param string_1 + * @param string_2 + * @return int + */ +int furi_string_cmpi(const FuriString* string_1, const FuriString* string_2); + +/** + * @brief Compare string with C string (case insensitive according to the current locale) and return the sort order. + * Note: doesn't work with UTF-8 strings. + * @param string_1 + * @param cstring_2 + * @return int + */ +int furi_string_cmpi_str(const FuriString* string_1, const char cstring_2[]); + +//--------------------------------------------------------------------------- +// Search +//--------------------------------------------------------------------------- + +/** + * @brief Search the first occurrence of the needle in the string from the position start. + * Return STRING_FAILURE if not found. + * By default, start is zero. + * @param string + * @param needle + * @param start + * @return size_t + */ +size_t furi_string_search(const FuriString* string, const FuriString* needle, size_t start); + +/** + * @brief Search the first occurrence of the needle in the string from the position start. + * Return STRING_FAILURE if not found. + * @param string + * @param needle + * @param start + * @return size_t + */ +size_t furi_string_search_str(const FuriString* string, const char needle[], size_t start); + +/** + * @brief Search for the position of the character c from the position start (include) in the string. + * Return STRING_FAILURE if not found. + * By default, start is zero. + * @param string + * @param c + * @param start + * @return size_t + */ +size_t furi_string_search_char(const FuriString* string, char c, size_t start); + +/** + * @brief Reverse search for the position of the character c from the position start (include) in the string. + * Return STRING_FAILURE if not found. + * By default, start is zero. + * @param string + * @param c + * @param start + * @return size_t + */ +size_t furi_string_search_rchar(const FuriString* string, char c, size_t start); + +//--------------------------------------------------------------------------- +// Equality +//--------------------------------------------------------------------------- + +/** + * @brief Test if two strings are equal. + * @param string_1 + * @param string_2 + * @return bool + */ +bool furi_string_equal(const FuriString* string_1, const FuriString* string_2); + +/** + * @brief Test if the string is equal to the C string. + * @param string_1 + * @param cstring_2 + * @return bool + */ +bool furi_string_equal_str(const FuriString* string_1, const char cstring_2[]); + +//--------------------------------------------------------------------------- +// Replace +//--------------------------------------------------------------------------- + +/** + * @brief Replace in the string the sub-string at position 'pos' for 'len' bytes into the C string 'replace'. + * @param string + * @param pos + * @param len + * @param replace + */ +void furi_string_replace_at(FuriString* string, size_t pos, size_t len, const char replace[]); + +/** + * @brief Replace a string 'needle' to string 'replace' in a string from 'start' position. + * By default, start is zero. + * Return STRING_FAILURE if 'needle' not found or replace position. + * @param string + * @param needle + * @param replace + * @param start + * @return size_t + */ +size_t + furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start); + +/** + * @brief Replace a C string 'needle' to C string 'replace' in a string from 'start' position. + * By default, start is zero. + * Return STRING_FAILURE if 'needle' not found or replace position. + * @param string + * @param needle + * @param replace + * @param start + * @return size_t + */ +size_t furi_string_replace_str( + FuriString* string, + const char needle[], + const char replace[], + size_t start); + +/** + * @brief Replace all occurrences of 'needle' string into 'replace' string. + * @param string + * @param needle + * @param replace + */ +void furi_string_replace_all( + FuriString* string, + const FuriString* needle, + const FuriString* replace); + +/** + * @brief Replace all occurrences of 'needle' C string into 'replace' C string. + * @param string + * @param needle + * @param replace + */ +void furi_string_replace_all_str(FuriString* string, const char needle[], const char replace[]); + +//--------------------------------------------------------------------------- +// Start / End tests +//--------------------------------------------------------------------------- + +/** + * @brief Test if the string starts with the given string. + * @param string + * @param start + * @return bool + */ +bool furi_string_start_with(const FuriString* string, const FuriString* start); + +/** + * @brief Test if the string starts with the given C string. + * @param string + * @param start + * @return bool + */ +bool furi_string_start_with_str(const FuriString* string, const char start[]); + +/** + * @brief Test if the string ends with the given string. + * @param string + * @param end + * @return bool + */ +bool furi_string_end_with(const FuriString* string, const FuriString* end); + +/** + * @brief Test if the string ends with the given C string. + * @param string + * @param end + * @return bool + */ +bool furi_string_end_with_str(const FuriString* string, const char end[]); + +//--------------------------------------------------------------------------- +// Trim +//--------------------------------------------------------------------------- + +/** + * @brief Trim the string left to the first 'index' bytes. + * @param string + * @param index + */ +void furi_string_left(FuriString* string, size_t index); + +/** + * @brief Trim the string right from the 'index' position to the last position. + * @param string + * @param index + */ +void furi_string_right(FuriString* string, size_t index); + +/** + * @brief Trim the string from position index to size bytes. + * See also furi_string_set_n. + * @param string + * @param index + * @param size + */ +void furi_string_mid(FuriString* string, size_t index, size_t size); + +/** + * @brief Trim a string from the given set of characters (default is " \n\r\t"). + * @param string + * @param chars + */ +void furi_string_trim(FuriString* string, const char chars[]); + +//--------------------------------------------------------------------------- +// UTF8 +//--------------------------------------------------------------------------- + +/** + * @brief An unicode value. + */ +typedef unsigned int FuriStringUnicodeValue; + +/** + * @brief Compute the length in UTF8 characters in the string. + * @param string + * @return size_t + */ +size_t furi_string_utf8_length(FuriString* string); + +/** + * @brief Push unicode into string, encoding it in UTF8. + * @param string + * @param unicode + */ +void furi_string_utf8_push(FuriString* string, FuriStringUnicodeValue unicode); + +/** + * @brief State of the UTF8 decoding machine state. + */ +typedef enum { + FuriStringUTF8StateStarting, + FuriStringUTF8StateDecoding1, + FuriStringUTF8StateDecoding2, + FuriStringUTF8StateDecoding3, + FuriStringUTF8StateError +} FuriStringUTF8State; + +/** + * @brief Main generic UTF8 decoder. + * It takes a character, and the previous state and the previous value of the unicode value. + * It updates the state and the decoded unicode value. + * A decoded unicode encoded value is valid only when the state is FuriStringUTF8StateStarting. + * @param c + * @param state + * @param unicode + */ +void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode); + +//--------------------------------------------------------------------------- +// Lasciate ogne speranza, voi ch’entrate +//--------------------------------------------------------------------------- + +/** + * + * Select either the string function or the str function depending on + * the b operand to the function. + * func1 is the string function / func2 is the str function. + */ + +/** + * @brief Select for 1 argument + */ +#define FURI_STRING_SELECT1(func1, func2, a) \ + _Generic((a), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a) + +/** + * @brief Select for 2 arguments + */ +#define FURI_STRING_SELECT2(func1, func2, a, b) \ + _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b) + +/** + * @brief Select for 3 arguments + */ +#define FURI_STRING_SELECT3(func1, func2, a, b, c) \ + _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b, c) + +/** + * @brief Select for 4 arguments + */ +#define FURI_STRING_SELECT4(func1, func2, a, b, c, d) \ + _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b, c, d) + +/** + * @brief Allocate new FuriString and set it content to string (or C string). + * ([c]string) + */ +#define furi_string_alloc_set(a) \ + FURI_STRING_SELECT1(furi_string_alloc_set, furi_string_alloc_set_str, a) + +/** + * @brief Set the string content to string (or C string). + * (string, [c]string) + */ +#define furi_string_set(a, b) FURI_STRING_SELECT2(furi_string_set, furi_string_set_str, a, b) + +/** + * @brief Compare string with string (or C string) and return the sort order. + * Note: doesn't work with UTF-8 strings. + * (string, [c]string) + */ +#define furi_string_cmp(a, b) FURI_STRING_SELECT2(furi_string_cmp, furi_string_cmp_str, a, b) + +/** + * @brief Compare string with string (or C string) (case insensitive according to the current locale) and return the sort order. + * Note: doesn't work with UTF-8 strings. + * (string, [c]string) + */ +#define furi_string_cmpi(a, b) FURI_STRING_SELECT2(furi_string_cmpi, furi_string_cmpi_str, a, b) + +/** + * @brief Test if the string is equal to the string (or C string). + * (string, [c]string) + */ +#define furi_string_equal(a, b) FURI_STRING_SELECT2(furi_string_equal, furi_string_equal_str, a, b) + +/** + * @brief Replace all occurrences of string into string (or C string to another C string) in a string. + * (string, [c]string, [c]string) + */ +#define furi_string_replace_all(a, b, c) \ + FURI_STRING_SELECT3(furi_string_replace_all, furi_string_replace_all_str, a, b, c) + +/** + * @brief Search for a string (or C string) in a string + * (string, [c]string[, start=0]) + */ +#define furi_string_search(...) \ + M_APPLY( \ + FURI_STRING_SELECT3, \ + furi_string_search, \ + furi_string_search_str, \ + M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) +/** + * @brief Search for a C string in a string + * (string, cstring[, start=0]) + */ +#define furi_string_search_str(...) furi_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/** + * @brief Test if the string starts with the given string (or C string). + * (string, [c]string) + */ +#define furi_string_start_with(a, b) \ + FURI_STRING_SELECT2(furi_string_start_with, furi_string_start_with_str, a, b) + +/** + * @brief Test if the string ends with the given string (or C string). + * (string, [c]string) + */ +#define furi_string_end_with(a, b) \ + FURI_STRING_SELECT2(furi_string_end_with, furi_string_end_with_str, a, b) + +/** + * @brief Append a string (or C string) to the string. + * (string, [c]string) + */ +#define furi_string_cat(a, b) FURI_STRING_SELECT2(furi_string_cat, furi_string_cat_str, a, b) + +/** + * @brief Trim a string from the given set of characters (default is " \n\r\t"). + * (string[, set=" \n\r\t"]) + */ +#define furi_string_trim(...) furi_string_trim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__)) + +/** + * @brief Search for a character in a string. + * (string, character[, start=0]) + */ +#define furi_string_search_char(...) furi_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/** + * @brief Reverse Search for a character in a string. + * (string, character[, start=0]) + */ +#define furi_string_search_rchar(...) furi_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/** + * @brief Replace a string to another string (or C string to another C string) in a string. + * (string, [c]string, [c]string[, start=0]) + */ +#define furi_string_replace(...) \ + M_APPLY( \ + FURI_STRING_SELECT4, \ + furi_string_replace, \ + furi_string_replace_str, \ + M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) + +/** + * @brief Replace a C string to another C string in a string. + * (string, cstring, cstring[, start=0]) + */ +#define furi_string_replace_str(...) furi_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) + +/** + * @brief INIT OPLIST for FuriString. + */ +#define F_STR_INIT(a) ((a) = furi_string_alloc()) + +/** + * @brief INIT SET OPLIST for FuriString. + */ +#define F_STR_INIT_SET(a, b) ((a) = furi_string_alloc_set(b)) + +/** + * @brief INIT MOVE OPLIST for FuriString. + */ +#define F_STR_INIT_MOVE(a, b) ((a) = furi_string_alloc_move(b)) + +/** + * @brief OPLIST for FuriString. + */ +#define FURI_STRING_OPLIST \ + (INIT(F_STR_INIT), \ + INIT_SET(F_STR_INIT_SET), \ + SET(furi_string_set), \ + INIT_MOVE(F_STR_INIT_MOVE), \ + MOVE(furi_string_move), \ + SWAP(furi_string_swap), \ + RESET(furi_string_reset), \ + EMPTY_P(furi_string_empty), \ + CLEAR(furi_string_free), \ + HASH(furi_string_hash), \ + EQUAL(furi_string_equal), \ + CMP(furi_string_cmp), \ + TYPE(FuriString*)) + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/kernel.c b/components/furi/src/kernel.c new file mode 100644 index 00000000..c5fefd9b --- /dev/null +++ b/components/furi/src/kernel.c @@ -0,0 +1,202 @@ +#include "kernel.h" +#include "base.h" +#include "check.h" +#include "common_defines.h" + +#include +#include +#include + +bool furi_kernel_is_irq_or_masked() { + bool irq = false; + BaseType_t state; + + if(FURI_IS_IRQ_MODE()) { + /* Called from interrupt context */ + irq = true; + } else { + /* Get FreeRTOS scheduler state */ + state = xTaskGetSchedulerState(); + + if(state != taskSCHEDULER_NOT_STARTED) { + /* Scheduler was started */ + if(FURI_IS_IRQ_MASKED()) { + /* Interrupts are masked */ + irq = true; + } + } + } + + /* Return context, 0: thread context, 1: IRQ context */ + return (irq); +} + +bool furi_kernel_is_running() { + return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING; +} + +int32_t furi_kernel_lock() { + furi_assert(!furi_kernel_is_irq_or_masked()); + + int32_t lock; + + switch(xTaskGetSchedulerState()) { + case taskSCHEDULER_SUSPENDED: + lock = 1; + break; + + case taskSCHEDULER_RUNNING: + vTaskSuspendAll(); + lock = 0; + break; + + case taskSCHEDULER_NOT_STARTED: + default: + lock = (int32_t)FuriStatusError; + break; + } + + /* Return previous lock state */ + return (lock); +} + +int32_t furi_kernel_unlock() { + furi_assert(!furi_kernel_is_irq_or_masked()); + + int32_t lock; + + switch(xTaskGetSchedulerState()) { + case taskSCHEDULER_SUSPENDED: + lock = 1; + + if(xTaskResumeAll() != pdTRUE) { + if(xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) { + lock = (int32_t)FuriStatusError; + } + } + break; + + case taskSCHEDULER_RUNNING: + lock = 0; + break; + + case taskSCHEDULER_NOT_STARTED: + default: + lock = (int32_t)FuriStatusError; + break; + } + + /* Return previous lock state */ + return (lock); +} + +int32_t furi_kernel_restore_lock(int32_t lock) { + furi_assert(!furi_kernel_is_irq_or_masked()); + + switch(xTaskGetSchedulerState()) { + case taskSCHEDULER_SUSPENDED: + case taskSCHEDULER_RUNNING: + if(lock == 1) { + vTaskSuspendAll(); + } else { + if(lock != 0) { + lock = (int32_t)FuriStatusError; + } else { + if(xTaskResumeAll() != pdTRUE) { + if(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { + lock = (int32_t)FuriStatusError; + } + } + } + } + break; + + case taskSCHEDULER_NOT_STARTED: + default: + lock = (int32_t)FuriStatusError; + break; + } + + /* Return new lock state */ + return (lock); +} + +uint32_t furi_kernel_get_tick_frequency() { + /* Return frequency in hertz */ + return (configTICK_RATE_HZ_RAW); +} + +void furi_delay_tick(uint32_t ticks) { + furi_assert(!furi_kernel_is_irq_or_masked()); + if(ticks == 0U) { + taskYIELD(); + } else { + vTaskDelay(ticks); + } +} + +FuriStatus furi_delay_until_tick(uint32_t tick) { + furi_assert(!furi_kernel_is_irq_or_masked()); + + TickType_t tcnt, delay; + FuriStatus stat; + + stat = FuriStatusOk; + tcnt = xTaskGetTickCount(); + + /* Determine remaining number of tick to delay */ + delay = (TickType_t)tick - tcnt; + + /* Check if target tick has not expired */ + if((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) { + if(xTaskDelayUntil(&tcnt, delay) == pdFALSE) { + /* Did not delay */ + stat = FuriStatusError; + } + } else { + /* No delay or already expired */ + stat = FuriStatusErrorParameter; + } + + /* Return execution status */ + return (stat); +} + +uint32_t furi_get_tick() { + TickType_t ticks; + + if(furi_kernel_is_irq_or_masked() != 0U) { + ticks = xTaskGetTickCountFromISR(); + } else { + ticks = xTaskGetTickCount(); + } + + return ticks; +} + +uint32_t furi_ms_to_ticks(uint32_t milliseconds) { +#if configTICK_RATE_HZ_RAW == 1000 + return milliseconds; +#else + return (uint32_t)((float)configTICK_RATE_HZ_RAW) / 1000.0f * (float)milliseconds; +#endif +} + +void furi_delay_ms(uint32_t milliseconds) { + if(!FURI_IS_ISR() && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { + if(milliseconds > 0 && milliseconds < portMAX_DELAY - 1) { + milliseconds += 1; + } +#if configTICK_RATE_HZ_RAW == 1000 + furi_delay_tick(milliseconds); +#else + furi_delay_tick(furi_ms_to_ticks(milliseconds)); +#endif + } else if(milliseconds > 0) { + furi_delay_us(milliseconds * 1000); + } +} + +void furi_delay_us(uint32_t microseconds) { + ets_delay_us(microseconds); +} diff --git a/components/furi/src/kernel.h b/components/furi/src/kernel.h new file mode 100644 index 00000000..b094c303 --- /dev/null +++ b/components/furi/src/kernel.h @@ -0,0 +1,128 @@ +/** + * @file kernel.h + * Furi Kernel primitives + */ +#pragma once + +#include "base.h" + +#define configTICK_RATE_HZ_RAW 1000 + +#ifdef __cplusplus +extern "C" { +#endif + +/** Check if CPU is in IRQ or kernel running and IRQ is masked + * + * Originally this primitive was born as a workaround for FreeRTOS kernel primitives shenanigans with PRIMASK. + * + * Meaningful use cases are: + * + * - When kernel is started and you want to ensure that you are not in IRQ or IRQ is not masked(like in critical section) + * - When kernel is not started and you want to make sure that you are not in IRQ mode, ignoring PRIMASK. + * + * As you can see there will be edge case when kernel is not started and PRIMASK is not 0 that may cause some funky behavior. + * Most likely it will happen after kernel primitives being used, but control not yet passed to kernel. + * It's up to you to figure out if it is safe for your code or not. + * + * @return true if CPU is in IRQ or kernel running and IRQ is masked + */ +bool furi_kernel_is_irq_or_masked(); + +/** Check if kernel is running + * + * @return true if running, false otherwise + */ +bool furi_kernel_is_running(); + +/** Lock kernel, pause process scheduling + * + * @warning This should never be called in interrupt request context. + * + * @return previous lock state(0 - unlocked, 1 - locked) + */ +int32_t furi_kernel_lock(); + +/** Unlock kernel, resume process scheduling + * + * @warning This should never be called in interrupt request context. + * + * @return previous lock state(0 - unlocked, 1 - locked) + */ +int32_t furi_kernel_unlock(); + +/** Restore kernel lock state + * + * @warning This should never be called in interrupt request context. + * + * @param[in] lock The lock state + * + * @return new lock state or error + */ +int32_t furi_kernel_restore_lock(int32_t lock); + +/** Get kernel systick frequency + * + * @return systick counts per second + */ +uint32_t furi_kernel_get_tick_frequency(); + +/** Delay execution + * + * @warning This should never be called in interrupt request context. + * + * Also keep in mind delay is aliased to scheduler timer intervals. + * + * @param[in] ticks The ticks count to pause + */ +void furi_delay_tick(uint32_t ticks); + +/** Delay until tick + * + * @warning This should never be called in interrupt request context. + * + * @param[in] ticks The tick until which kerel should delay task execution + * + * @return The furi status. + */ +FuriStatus furi_delay_until_tick(uint32_t tick); + +/** Get current tick counter + * + * System uptime, may overflow. + * + * @return Current ticks in milliseconds + */ +uint32_t furi_get_tick(void); + +/** Convert milliseconds to ticks + * + * @param[in] milliseconds time in milliseconds + * @return time in ticks + */ +uint32_t furi_ms_to_ticks(uint32_t milliseconds); + +/** Delay in milliseconds + * + * This method uses kernel ticks on the inside, which causes delay to be aliased to scheduler timer intervals. + * Real wait time will be between X+ milliseconds. + * Special value: 0, will cause task yield. + * Also if used when kernel is not running will fall back to `furi_delay_us`. + * + * @warning Cannot be used from ISR + * + * @param[in] milliseconds milliseconds to wait + */ +void furi_delay_ms(uint32_t milliseconds); + +/** Delay in microseconds + * + * Implemented using Cortex DWT counter. Blocking and non aliased. + * + * @param[in] microseconds microseconds to wait + */ +void furi_delay_us(uint32_t microseconds); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/m_cstr_dup.h b/components/furi/src/m_cstr_dup.h new file mode 100644 index 00000000..40a6dbc5 --- /dev/null +++ b/components/furi/src/m_cstr_dup.h @@ -0,0 +1,26 @@ +// File originated from Flipper Zero / Furi +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define M_INIT_DUP(a) ((a) = strdup("")) +#define M_INIT_SET_DUP(a, b) ((a) = strdup(b)) +#define M_SET_DUP(a, b) (free((void*)a), (a) = strdup(b)) +#define M_CLEAR_DUP(a) (free((void*)a)) + +#define M_CSTR_DUP_OPLIST \ + (INIT(M_INIT_DUP), \ + INIT_SET(M_INIT_SET_DUP), \ + SET(M_SET_DUP), \ + CLEAR(M_CLEAR_DUP), \ + HASH(m_core_cstr_hash), \ + EQUAL(M_CSTR_EQUAL), \ + CMP(strcmp), \ + TYPE(const char*)) + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/message_queue.c b/components/furi/src/message_queue.c new file mode 100644 index 00000000..c636a2c9 --- /dev/null +++ b/components/furi/src/message_queue.c @@ -0,0 +1,182 @@ +#include "kernel.h" +#include "message_queue.h" +#include "check.h" + +#include +#include + +FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { + furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U)); + + QueueHandle_t handle = xQueueCreate(msg_count, msg_size); + furi_check(handle); + + return ((FuriMessageQueue*)handle); +} + +void furi_message_queue_free(FuriMessageQueue* instance) { + furi_assert(furi_kernel_is_irq_or_masked() == 0U); + furi_assert(instance); + + vQueueDelete((QueueHandle_t)instance); +} + +FuriStatus + furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout) { + QueueHandle_t hQueue = (QueueHandle_t)instance; + FuriStatus stat; + BaseType_t yield; + + stat = FuriStatusOk; + + if(furi_kernel_is_irq_or_masked() != 0U) { + if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { + stat = FuriStatusErrorParameter; + } else { + yield = pdFALSE; + + if(xQueueSendToBackFromISR(hQueue, msg_ptr, &yield) != pdTRUE) { + stat = FuriStatusErrorResource; + } else { + portYIELD_FROM_ISR(yield); + } + } + } else { + if((hQueue == NULL) || (msg_ptr == NULL)) { + stat = FuriStatusErrorParameter; + } else { + if(xQueueSendToBack(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) { + if(timeout != 0U) { + stat = FuriStatusErrorTimeout; + } else { + stat = FuriStatusErrorResource; + } + } + } + } + + /* Return execution status */ + return (stat); +} + +FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout) { + QueueHandle_t hQueue = (QueueHandle_t)instance; + FuriStatus stat; + BaseType_t yield; + + stat = FuriStatusOk; + + if(furi_kernel_is_irq_or_masked() != 0U) { + if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { + stat = FuriStatusErrorParameter; + } else { + yield = pdFALSE; + + if(xQueueReceiveFromISR(hQueue, msg_ptr, &yield) != pdPASS) { + stat = FuriStatusErrorResource; + } else { + portYIELD_FROM_ISR(yield); + } + } + } else { + if((hQueue == NULL) || (msg_ptr == NULL)) { + stat = FuriStatusErrorParameter; + } else { + if(xQueueReceive(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) { + if(timeout != 0U) { + stat = FuriStatusErrorTimeout; + } else { + stat = FuriStatusErrorResource; + } + } + } + } + + /* Return execution status */ + return (stat); +} + +uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) { + StaticQueue_t* mq = (StaticQueue_t*)instance; + uint32_t capacity; + + if(mq == NULL) { + capacity = 0U; + } else { + /* capacity = pxQueue->uxLength */ + capacity = mq->uxDummy4[1]; + } + + /* Return maximum number of messages */ + return (capacity); +} + +uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) { + StaticQueue_t* mq = (StaticQueue_t*)instance; + uint32_t size; + + if(mq == NULL) { + size = 0U; + } else { + /* size = pxQueue->uxItemSize */ + size = mq->uxDummy4[2]; + } + + /* Return maximum message size */ + return (size); +} + +uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { + QueueHandle_t hQueue = (QueueHandle_t)instance; + UBaseType_t count; + + if(hQueue == NULL) { + count = 0U; + } else if(furi_kernel_is_irq_or_masked() != 0U) { + count = uxQueueMessagesWaitingFromISR(hQueue); + } else { + count = uxQueueMessagesWaiting(hQueue); + } + + /* Return number of queued messages */ + return ((uint32_t)count); +} + +uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { + StaticQueue_t* mq = (StaticQueue_t*)instance; + uint32_t space; + uint32_t isrm; + + if(mq == NULL) { + space = 0U; + } else if(furi_kernel_is_irq_or_masked() != 0U) { + isrm = taskENTER_CRITICAL_FROM_ISR(); + + /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ + space = mq->uxDummy4[1] - mq->uxDummy4[0]; + + taskEXIT_CRITICAL_FROM_ISR(isrm); + } else { + space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)mq); + } + + /* Return number of available slots */ + return (space); +} + +FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { + QueueHandle_t hQueue = (QueueHandle_t)instance; + FuriStatus stat; + + if(furi_kernel_is_irq_or_masked() != 0U) { + stat = FuriStatusErrorISR; + } else if(hQueue == NULL) { + stat = FuriStatusErrorParameter; + } else { + stat = FuriStatusOk; + (void)xQueueReset(hQueue); + } + + /* Return execution status */ + return (stat); +} diff --git a/components/furi/src/message_queue.h b/components/furi/src/message_queue.h new file mode 100644 index 00000000..59cb4925 --- /dev/null +++ b/components/furi/src/message_queue.h @@ -0,0 +1,95 @@ +/** + * @file message_queue.h + * FuriMessageQueue + */ +#pragma once + +#include "base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void FuriMessageQueue; + +/** Allocate furi message queue + * + * @param[in] msg_count The message count + * @param[in] msg_size The message size + * + * @return pointer to FuriMessageQueue instance + */ +FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size); + +/** Free queue + * + * @param instance pointer to FuriMessageQueue instance + */ +void furi_message_queue_free(FuriMessageQueue* instance); + +/** Put message into queue + * + * @param instance pointer to FuriMessageQueue instance + * @param[in] msg_ptr The message pointer + * @param[in] timeout The timeout + * @param[in] msg_prio The message prio + * + * @return The furi status. + */ +FuriStatus + furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout); + +/** Get message from queue + * + * @param instance pointer to FuriMessageQueue instance + * @param msg_ptr The message pointer + * @param msg_prio The message prioority + * @param[in] timeout The timeout + * + * @return The furi status. + */ +FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout); + +/** Get queue capacity + * + * @param instance pointer to FuriMessageQueue instance + * + * @return capacity in object count + */ +uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance); + +/** Get message size + * + * @param instance pointer to FuriMessageQueue instance + * + * @return Message size in bytes + */ +uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance); + +/** Get message count in queue + * + * @param instance pointer to FuriMessageQueue instance + * + * @return Message count + */ +uint32_t furi_message_queue_get_count(FuriMessageQueue* instance); + +/** Get queue available space + * + * @param instance pointer to FuriMessageQueue instance + * + * @return Message count + */ +uint32_t furi_message_queue_get_space(FuriMessageQueue* instance); + +/** Reset queue + * + * @param instance pointer to FuriMessageQueue instance + * + * @return The furi status. + */ +FuriStatus furi_message_queue_reset(FuriMessageQueue* instance); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/mutex.c b/components/furi/src/mutex.c new file mode 100644 index 00000000..7e775563 --- /dev/null +++ b/components/furi/src/mutex.c @@ -0,0 +1,125 @@ +#include "mutex.h" +#include "check.h" +#include "common_defines.h" + +#include +#include + +FuriMutex* furi_mutex_alloc(FuriMutexType type) { + furi_assert(!FURI_IS_IRQ_MODE()); + + SemaphoreHandle_t hMutex = NULL; + + if(type == FuriMutexTypeNormal) { + hMutex = xSemaphoreCreateMutex(); + } else if(type == FuriMutexTypeRecursive) { + hMutex = xSemaphoreCreateRecursiveMutex(); + } else { + furi_crash("Programming error"); + } + + furi_check(hMutex != NULL); + + if(type == FuriMutexTypeRecursive) { + /* Set LSB as 'recursive mutex flag' */ + hMutex = (SemaphoreHandle_t)((uint32_t)hMutex | 1U); + } + + /* Return mutex ID */ + return ((FuriMutex*)hMutex); +} + +void furi_mutex_free(FuriMutex* instance) { + furi_assert(!FURI_IS_IRQ_MODE()); + furi_assert(instance); + + vSemaphoreDelete((SemaphoreHandle_t)((uint32_t)instance & ~1U)); +} + +FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) { + SemaphoreHandle_t hMutex; + FuriStatus stat; + uint32_t rmtx; + + hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); + + /* Extract recursive mutex flag */ + rmtx = (uint32_t)instance & 1U; + + stat = FuriStatusOk; + + if(FURI_IS_IRQ_MODE()) { + stat = FuriStatusErrorISR; + } else if(hMutex == NULL) { + stat = FuriStatusErrorParameter; + } else { + if(rmtx != 0U) { + if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) { + if(timeout != 0U) { + stat = FuriStatusErrorTimeout; + } else { + stat = FuriStatusErrorResource; + } + } + } else { + if(xSemaphoreTake(hMutex, timeout) != pdPASS) { + if(timeout != 0U) { + stat = FuriStatusErrorTimeout; + } else { + stat = FuriStatusErrorResource; + } + } + } + } + + /* Return execution status */ + return (stat); +} + +FuriStatus furi_mutex_release(FuriMutex* instance) { + SemaphoreHandle_t hMutex; + FuriStatus stat; + uint32_t rmtx; + + hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); + + /* Extract recursive mutex flag */ + rmtx = (uint32_t)instance & 1U; + + stat = FuriStatusOk; + + if(FURI_IS_IRQ_MODE()) { + stat = FuriStatusErrorISR; + } else if(hMutex == NULL) { + stat = FuriStatusErrorParameter; + } else { + if(rmtx != 0U) { + if(xSemaphoreGiveRecursive(hMutex) != pdPASS) { + stat = FuriStatusErrorResource; + } + } else { + if(xSemaphoreGive(hMutex) != pdPASS) { + stat = FuriStatusErrorResource; + } + } + } + + /* Return execution status */ + return (stat); +} + +FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { + SemaphoreHandle_t hMutex; + FuriThreadId owner; + + hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); + + if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { + owner = 0; + } else { + owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); + } + + /* Return owner thread ID */ + return (owner); +} diff --git a/components/furi/src/mutex.h b/components/furi/src/mutex.h new file mode 100644 index 00000000..aa55fa7b --- /dev/null +++ b/components/furi/src/mutex.h @@ -0,0 +1,62 @@ +/** + * @file mutex.h + * FuriMutex + */ +#pragma once + +#include "base.h" +#include "thread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FuriMutexTypeNormal, + FuriMutexTypeRecursive, +} FuriMutexType; + +typedef void FuriMutex; + +/** Allocate FuriMutex + * + * @param[in] type The mutex type + * + * @return pointer to FuriMutex instance + */ +FuriMutex* furi_mutex_alloc(FuriMutexType type); + +/** Free FuriMutex + * + * @param instance The pointer to FuriMutex instance + */ +void furi_mutex_free(FuriMutex* instance); + +/** Acquire mutex + * + * @param instance The pointer to FuriMutex instance + * @param[in] timeout The timeout + * + * @return The furi status. + */ +FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout); + +/** Release mutex + * + * @param instance The pointer to FuriMutex instance + * + * @return The furi status. + */ +FuriStatus furi_mutex_release(FuriMutex* instance); + +/** Get mutex owner thread id + * + * @param instance The pointer to FuriMutex instance + * + * @return The furi thread identifier. + */ +FuriThreadId furi_mutex_get_owner(FuriMutex* instance); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/pubsub.c b/components/furi/src/pubsub.c new file mode 100644 index 00000000..e4e1eb88 --- /dev/null +++ b/components/furi/src/pubsub.c @@ -0,0 +1,94 @@ +#include "pubsub.h" +#include "check.h" +#include "mutex.h" + +#include + +struct FuriPubSubSubscription { + FuriPubSubCallback callback; + void* callback_context; +}; + +LIST_DEF(FuriPubSubSubscriptionList, FuriPubSubSubscription, M_POD_OPLIST); + +struct FuriPubSub { + FuriPubSubSubscriptionList_t items; + FuriMutex* mutex; +}; + +FuriPubSub* furi_pubsub_alloc() { + FuriPubSub* pubsub = malloc(sizeof(FuriPubSub)); + + pubsub->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + furi_assert(pubsub->mutex); + + FuriPubSubSubscriptionList_init(pubsub->items); + + return pubsub; +} + +void furi_pubsub_free(FuriPubSub* pubsub) { + furi_assert(pubsub); + + furi_check(FuriPubSubSubscriptionList_size(pubsub->items) == 0); + + FuriPubSubSubscriptionList_clear(pubsub->items); + + furi_mutex_free(pubsub->mutex); + + free(pubsub); +} + +FuriPubSubSubscription* + furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context) { + furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk); + // put uninitialized item to the list + FuriPubSubSubscription* item = FuriPubSubSubscriptionList_push_raw(pubsub->items); + + // initialize item + item->callback = callback; + item->callback_context = callback_context; + + furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk); + + return item; +} + +void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription) { + furi_assert(pubsub); + furi_assert(pubsub_subscription); + + furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk); + bool result = false; + + // iterate over items + FuriPubSubSubscriptionList_it_t it; + for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it); + FuriPubSubSubscriptionList_next(it)) { + const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it); + + // if the iterator is equal to our element + if(item == pubsub_subscription) { + FuriPubSubSubscriptionList_remove(pubsub->items, it); + result = true; + break; + } + } + + furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk); + furi_check(result); +} + +void furi_pubsub_publish(FuriPubSub* pubsub, void* message) { + furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk); + + // iterate over subscribers + FuriPubSubSubscriptionList_it_t it; + for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it); + FuriPubSubSubscriptionList_next(it)) { + const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it); + item->callback(message, item->callback_context); + } + + furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk); +} diff --git a/components/furi/src/pubsub.h b/components/furi/src/pubsub.h new file mode 100644 index 00000000..69ca574a --- /dev/null +++ b/components/furi/src/pubsub.h @@ -0,0 +1,68 @@ +/** + * @file pubsub.h + * FuriPubSub + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** FuriPubSub Callback type */ +typedef void (*FuriPubSubCallback)(const void* message, void* context); + +/** FuriPubSub type */ +typedef struct FuriPubSub FuriPubSub; + +/** FuriPubSubSubscription type */ +typedef struct FuriPubSubSubscription FuriPubSubSubscription; + +/** Allocate FuriPubSub + * + * Reentrable, Not threadsafe, one owner + * + * @return pointer to FuriPubSub instance + */ +FuriPubSub* furi_pubsub_alloc(); + +/** Free FuriPubSub + * + * @param pubsub FuriPubSub instance + */ +void furi_pubsub_free(FuriPubSub* pubsub); + +/** Subscribe to FuriPubSub + * + * Threadsafe, Reentrable + * + * @param pubsub pointer to FuriPubSub instance + * @param[in] callback The callback + * @param callback_context The callback context + * + * @return pointer to FuriPubSubSubscription instance + */ +FuriPubSubSubscription* + furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context); + +/** Unsubscribe from FuriPubSub + * + * No use of `pubsub_subscription` allowed after call of this method + * Threadsafe, Reentrable. + * + * @param pubsub pointer to FuriPubSub instance + * @param pubsub_subscription pointer to FuriPubSubSubscription instance + */ +void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription); + +/** Publish message to FuriPubSub + * + * Threadsafe, Reentrable. + * + * @param pubsub pointer to FuriPubSub instance + * @param message message pointer to publish + */ +void furi_pubsub_publish(FuriPubSub* pubsub, void* message); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/record.c b/components/furi/src/record.c new file mode 100644 index 00000000..31938814 --- /dev/null +++ b/components/furi/src/record.c @@ -0,0 +1,145 @@ +#include "record.h" +#include "check.h" +#include "mutex.h" +#include "event_flag.h" + +#include +#include "m_cstr_dup.h" + +#define FURI_RECORD_FLAG_READY (0x1) + +typedef struct { + FuriEventFlag* flags; + void* data; + size_t holders_count; +} FuriRecordData; + +DICT_DEF2(FuriRecordDataDict, const char*, M_CSTR_DUP_OPLIST, FuriRecordData, M_POD_OPLIST) + +typedef struct { + FuriMutex* mutex; + FuriRecordDataDict_t records; +} FuriRecord; + +static FuriRecord* furi_record = NULL; + +static FuriRecordData* furi_record_get(const char* name) { + return FuriRecordDataDict_get(furi_record->records, name); +} + +static void furi_record_put(const char* name, FuriRecordData* record_data) { + FuriRecordDataDict_set_at(furi_record->records, name, *record_data); +} + +static void furi_record_erase(const char* name, FuriRecordData* record_data) { + furi_event_flag_free(record_data->flags); + FuriRecordDataDict_erase(furi_record->records, name); +} + +void furi_record_init() { + furi_record = malloc(sizeof(FuriRecord)); + furi_record->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + furi_check(furi_record->mutex); + FuriRecordDataDict_init(furi_record->records); +} + +static FuriRecordData* furi_record_data_get_or_create(const char* name) { + furi_assert(furi_record); + FuriRecordData* record_data = furi_record_get(name); + if(!record_data) { + FuriRecordData new_record; + new_record.flags = furi_event_flag_alloc(); + new_record.data = NULL; + new_record.holders_count = 0; + furi_record_put(name, &new_record); + record_data = furi_record_get(name); + } + return record_data; +} + +static void furi_record_lock() { + furi_check(furi_mutex_acquire(furi_record->mutex, FuriWaitForever) == FuriStatusOk); +} + +static void furi_record_unlock() { + furi_check(furi_mutex_release(furi_record->mutex) == FuriStatusOk); +} + +bool furi_record_exists(const char* name) { + furi_assert(furi_record); + furi_assert(name); + + bool ret = false; + + furi_record_lock(); + ret = (furi_record_get(name) != NULL); + furi_record_unlock(); + + return ret; +} + +void furi_record_create(const char* name, void* data) { + furi_assert(furi_record); + + furi_record_lock(); + + // Get record data and fill it + FuriRecordData* record_data = furi_record_data_get_or_create(name); + furi_assert(record_data->data == NULL); + record_data->data = data; + furi_event_flag_set(record_data->flags, FURI_RECORD_FLAG_READY); + + furi_record_unlock(); +} + +bool furi_record_destroy(const char* name) { + furi_assert(furi_record); + + bool ret = false; + + furi_record_lock(); + + FuriRecordData* record_data = furi_record_get(name); + furi_assert(record_data); + if(record_data->holders_count == 0) { + furi_record_erase(name, record_data); + ret = true; + } + + furi_record_unlock(); + + return ret; +} + +void* furi_record_open(const char* name) { + furi_assert(furi_record); + + furi_record_lock(); + + FuriRecordData* record_data = furi_record_data_get_or_create(name); + record_data->holders_count++; + + furi_record_unlock(); + + // Wait for record to become ready + furi_check( + furi_event_flag_wait( + record_data->flags, + FURI_RECORD_FLAG_READY, + FuriFlagWaitAny | FuriFlagNoClear, + FuriWaitForever) == FURI_RECORD_FLAG_READY); + + return record_data->data; +} + +void furi_record_close(const char* name) { + furi_assert(furi_record); + + furi_record_lock(); + + FuriRecordData* record_data = furi_record_get(name); + furi_assert(record_data); + record_data->holders_count--; + + furi_record_unlock(); +} diff --git a/components/furi/src/record.h b/components/furi/src/record.h new file mode 100644 index 00000000..4819123e --- /dev/null +++ b/components/furi/src/record.h @@ -0,0 +1,67 @@ +/** + * @file record.h + * Furi: record API + */ + +#pragma once + +#include +#include "core_defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize record storage For internal use only. + */ +void furi_record_init(); + +/** Check if record exists + * + * @param name record name + * @note Thread safe. Create and destroy must be executed from the same + * thread. + */ +bool furi_record_exists(const char* name); + +/** Create record + * + * @param name record name + * @param data data pointer + * @note Thread safe. Create and destroy must be executed from the same + * thread. + */ +void furi_record_create(const char* name, void* data); + +/** Destroy record + * + * @param name record name + * + * @return true if successful, false if still have holders or thread is not + * owner. + * @note Thread safe. Create and destroy must be executed from the same + * thread. + */ +bool furi_record_destroy(const char* name); + +/** Open record + * + * @param name record name + * + * @return pointer to the record + * @note Thread safe. Open and close must be executed from the same + * thread. Suspends caller thread till record is available + */ +FURI_RETURNS_NONNULL void* furi_record_open(const char* name); + +/** Close record + * + * @param name record name + * @note Thread safe. Open and close must be executed from the same + * thread. + */ +void furi_record_close(const char* name); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/semaphore.c b/components/furi/src/semaphore.c new file mode 100644 index 00000000..37847527 --- /dev/null +++ b/components/furi/src/semaphore.c @@ -0,0 +1,116 @@ +#include "semaphore.h" +#include "check.h" +#include "common_defines.h" + +#include +#include + +FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count) { + furi_assert(!FURI_IS_IRQ_MODE()); + furi_assert((max_count > 0U) && (initial_count <= max_count)); + + SemaphoreHandle_t hSemaphore = NULL; + if(max_count == 1U) { + hSemaphore = xSemaphoreCreateBinary(); + if((hSemaphore != NULL) && (initial_count != 0U)) { + if(xSemaphoreGive(hSemaphore) != pdPASS) { + vSemaphoreDelete(hSemaphore); + hSemaphore = NULL; + } + } + } else { + hSemaphore = xSemaphoreCreateCounting(max_count, initial_count); + } + + furi_check(hSemaphore); + + /* Return semaphore ID */ + return ((FuriSemaphore*)hSemaphore); +} + +void furi_semaphore_free(FuriSemaphore* instance) { + furi_assert(instance); + furi_assert(!FURI_IS_IRQ_MODE()); + + SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; + + vSemaphoreDelete(hSemaphore); +} + +FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { + furi_assert(instance); + + SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; + FuriStatus stat; + BaseType_t yield; + + stat = FuriStatusOk; + + if(FURI_IS_IRQ_MODE()) { + if(timeout != 0U) { + stat = FuriStatusErrorParameter; + } else { + yield = pdFALSE; + + if(xSemaphoreTakeFromISR(hSemaphore, &yield) != pdPASS) { + stat = FuriStatusErrorResource; + } else { + portYIELD_FROM_ISR(yield); + } + } + } else { + if(xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) { + if(timeout != 0U) { + stat = FuriStatusErrorTimeout; + } else { + stat = FuriStatusErrorResource; + } + } + } + + /* Return execution status */ + return (stat); +} + +FuriStatus furi_semaphore_release(FuriSemaphore* instance) { + furi_assert(instance); + + SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; + FuriStatus stat; + BaseType_t yield; + + stat = FuriStatusOk; + + if(FURI_IS_IRQ_MODE()) { + yield = pdFALSE; + + if(xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) { + stat = FuriStatusErrorResource; + } else { + portYIELD_FROM_ISR(yield); + } + } else { + if(xSemaphoreGive(hSemaphore) != pdPASS) { + stat = FuriStatusErrorResource; + } + } + + /* Return execution status */ + return (stat); +} + +//uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { +// furi_assert(instance); +// +// SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; +// uint32_t count; +// +// if(FURI_IS_IRQ_MODE()) { +// count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore); +// } else { +// count = (uint32_t)uxSemaphoreGetCount(hSemaphore); +// } +// +// /* Return number of tokens */ +// return (count); +//} diff --git a/components/furi/src/semaphore.h b/components/furi/src/semaphore.h new file mode 100644 index 00000000..30d3acc7 --- /dev/null +++ b/components/furi/src/semaphore.h @@ -0,0 +1,58 @@ +/** + * @file semaphore.h + * FuriSemaphore + */ +#pragma once + +#include "base.h" +#include "thread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void FuriSemaphore; + +/** Allocate semaphore + * + * @param[in] max_count The maximum count + * @param[in] initial_count The initial count + * + * @return pointer to FuriSemaphore instance + */ +FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count); + +/** Free semaphore + * + * @param instance The pointer to FuriSemaphore instance + */ +void furi_semaphore_free(FuriSemaphore* instance); + +/** Acquire semaphore + * + * @param instance The pointer to FuriSemaphore instance + * @param[in] timeout The timeout + * + * @return The furi status. + */ +FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout); + +/** Release semaphore + * + * @param instance The pointer to FuriSemaphore instance + * + * @return The furi status. + */ +FuriStatus furi_semaphore_release(FuriSemaphore* instance); + +///** Get semaphore count +// * +// * @param instance The pointer to FuriSemaphore instance +// * +// * @return Semaphore count +// */ +//uint32_t furi_semaphore_get_count(FuriSemaphore* instance); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/stream_buffer.c b/components/furi/src/stream_buffer.c new file mode 100644 index 00000000..e6c80929 --- /dev/null +++ b/components/furi/src/stream_buffer.c @@ -0,0 +1,86 @@ +#include "base.h" +#include "check.h" +#include "stream_buffer.h" +#include "common_defines.h" + +#include +#include + +FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) { + furi_assert(size != 0); + + StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level); + furi_check(handle); + + return handle; +}; + +void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) { + furi_assert(stream_buffer); + vStreamBufferDelete(stream_buffer); +}; + +bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level) { + furi_assert(stream_buffer); + return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE; +}; + +size_t furi_stream_buffer_send( + FuriStreamBuffer* stream_buffer, + const void* data, + size_t length, + uint32_t timeout) { + size_t ret; + + if(FURI_IS_IRQ_MODE()) { + BaseType_t yield; + ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield); + portYIELD_FROM_ISR(yield); + } else { + ret = xStreamBufferSend(stream_buffer, data, length, timeout); + } + + return ret; +}; + +size_t furi_stream_buffer_receive( + FuriStreamBuffer* stream_buffer, + void* data, + size_t length, + uint32_t timeout) { + size_t ret; + + if(FURI_IS_IRQ_MODE()) { + BaseType_t yield; + ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield); + portYIELD_FROM_ISR(yield); + } else { + ret = xStreamBufferReceive(stream_buffer, data, length, timeout); + } + + return ret; +} + +size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer) { + return xStreamBufferBytesAvailable(stream_buffer); +}; + +size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer) { + return xStreamBufferSpacesAvailable(stream_buffer); +}; + +bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer) { + return xStreamBufferIsFull(stream_buffer) == pdTRUE; +}; + +bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer) { + return (xStreamBufferIsEmpty(stream_buffer) == pdTRUE); +}; + +FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer) { + if(xStreamBufferReset(stream_buffer) == pdPASS) { + return FuriStatusOk; + } else { + return FuriStatusError; + } +} \ No newline at end of file diff --git a/components/furi/src/stream_buffer.h b/components/furi/src/stream_buffer.h new file mode 100644 index 00000000..d07f7e60 --- /dev/null +++ b/components/furi/src/stream_buffer.h @@ -0,0 +1,152 @@ +/** + * @file stream_buffer.h + * Furi stream buffer primitive. + * + * Stream buffers are used to send a continuous stream of data from one task or + * interrupt to another. Their implementation is light weight, making them + * particularly suited for interrupt to task and core to core communication + * scenarios. + * + * ***NOTE***: Stream buffer implementation assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). + */ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void FuriStreamBuffer; + +/** + * @brief Allocate stream buffer instance. + * Stream buffer implementation assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). + * + * @param size The total number of bytes the stream buffer will be able to hold at any one time. + * @param trigger_level The number of bytes that must be in the stream buffer + * before a task that is blocked on the stream buffer to wait for data is moved out of the blocked state. + * @return The stream buffer instance. + */ +FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level); + +/** + * @brief Free stream buffer instance + * + * @param stream_buffer The stream buffer instance. + */ +void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer); + +/** + * @brief Set trigger level for stream buffer. + * A stream buffer's trigger level is the number of bytes that must be in the + * stream buffer before a task that is blocked on the stream buffer to + * wait for data is moved out of the blocked state. + * + * @param stream_buffer The stream buffer instance + * @param trigger_level The new trigger level for the stream buffer. + * @return true if trigger level can be be updated (new trigger level was less than or equal to the stream buffer's length). + * @return false if trigger level can't be be updated (new trigger level was greater than the stream buffer's length). + */ +bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level); + +/** + * @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer. + * Wakes up task waiting for data to become available if called from ISR. + * + * @param stream_buffer The stream buffer instance. + * @param data A pointer to the data that is to be copied into the stream buffer. + * @param length The maximum number of bytes to copy from data into the stream buffer. + * @param timeout The maximum amount of time the task should remain in the + * Blocked state to wait for space to become available if the stream buffer is full. + * Will return immediately if timeout is zero. + * Setting timeout to FuriWaitForever will cause the task to wait indefinitely. + * Ignored if called from ISR. + * @return The number of bytes actually written to the stream buffer. + */ +size_t furi_stream_buffer_send( + FuriStreamBuffer* stream_buffer, + const void* data, + size_t length, + uint32_t timeout); + +/** + * @brief Receives bytes from a stream buffer. + * Wakes up task waiting for space to become available if called from ISR. + * + * @param stream_buffer The stream buffer instance. + * @param data A pointer to the buffer into which the received bytes will be + * copied. + * @param length The length of the buffer pointed to by the data parameter. + * @param timeout The maximum amount of time the task should remain in the + * Blocked state to wait for data to become available if the stream buffer is empty. + * Will return immediately if timeout is zero. + * Setting timeout to FuriWaitForever will cause the task to wait indefinitely. + * Ignored if called from ISR. + * @return The number of bytes read from the stream buffer, if any. + */ +size_t furi_stream_buffer_receive( + FuriStreamBuffer* stream_buffer, + void* data, + size_t length, + uint32_t timeout); + +/** + * @brief Queries a stream buffer to see how much data it contains, which is equal to + * the number of bytes that can be read from the stream buffer before the stream + * buffer would be empty. + * + * @param stream_buffer The stream buffer instance. + * @return The number of bytes that can be read from the stream buffer before + * the stream buffer would be empty. + */ +size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer); + +/** + * @brief Queries a stream buffer to see how much free space it contains, which is + * equal to the amount of data that can be sent to the stream buffer before it + * is full. + * + * @param stream_buffer The stream buffer instance. + * @return The number of bytes that can be written to the stream buffer before + * the stream buffer would be full. + */ +size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer); + +/** + * @brief Queries a stream buffer to see if it is full. + * + * @param stream_buffer stream buffer instance. + * @return true if the stream buffer is full. + * @return false if the stream buffer is not full. + */ +bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer); + +/** + * @brief Queries a stream buffer to see if it is empty. + * + * @param stream_buffer The stream buffer instance. + * @return true if the stream buffer is empty. + * @return false if the stream buffer is not empty. + */ +bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer); + +/** + * @brief Resets a stream buffer to its initial, empty, state. Any data that was + * in the stream buffer is discarded. A stream buffer can only be reset if there + * are no tasks blocked waiting to either send to or receive from the stream buffer. + * + * @param stream_buffer The stream buffer instance. + * @return FuriStatusOk if the stream buffer is reset. + * @return FuriStatusError if there was a task blocked waiting to send to or read + * from the stream buffer then the stream buffer is not reset. + */ +FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/furi/src/thread.c b/components/furi/src/thread.c new file mode 100644 index 00000000..f3fd72f5 --- /dev/null +++ b/components/furi/src/thread.c @@ -0,0 +1,655 @@ +#include "thread.h" +#include "kernel.h" +#include "check.h" +#include "common_defines.h" +#include "furi_string.h" + +#include + +#include + +#include +#include + +#define TAG "FuriThread" + +#define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers + +typedef struct FuriThreadStdout FuriThreadStdout; + +struct FuriThreadStdout { + FuriThreadStdoutWriteCallback write_callback; + FuriString* buffer; +}; + +struct FuriThread { + FuriThreadState state; + int32_t ret; + + FuriThreadCallback callback; + void* context; + + FuriThreadStateCallback state_callback; + void* state_context; + + char* name; + char* appid; + + FuriThreadPriority priority; + + TaskHandle_t task_handle; + size_t heap_size; + + FuriThreadStdout output; + + // Keep all non-alignable byte types in one place, + // this ensures that the size of this structure is minimal + bool is_service; + bool heap_trace_enabled; + + configSTACK_DEPTH_TYPE stack_size; +}; + +static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size); +static int32_t __furi_thread_stdout_flush(FuriThread* thread); + +/** Catch threads that are trying to exit wrong way */ +__attribute__((__noreturn__)) void furi_thread_catch() { //-V1082 + // If you're here it means you're probably doing something wrong + // with critical sections or with scheduler state + asm volatile("nop"); // extra magic + furi_crash("You are doing it wrong"); //-V779 + __builtin_unreachable(); +} + +static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) { + furi_assert(thread); + thread->state = state; + if(thread->state_callback) { + thread->state_callback(state, thread->state_context); + } +} + +static void furi_thread_body(void* context) { + furi_assert(context); + FuriThread* thread = context; + + // store thread instance to thread local storage + furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) == NULL); + vTaskSetThreadLocalStoragePointer(NULL, 0, thread); + + furi_assert(thread->state == FuriThreadStateStarting); + furi_thread_set_state(thread, FuriThreadStateRunning); + + TaskHandle_t task_handle = xTaskGetCurrentTaskHandle(); +// if(thread->heap_trace_enabled == true) { +// memmgr_heap_enable_thread_trace((FuriThreadId)task_handle); +// } + + thread->ret = thread->callback(thread->context); + +// if(thread->heap_trace_enabled == true) { +// furi_delay_ms(33); +// thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); +// furi_log_print_format( +// thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, +// TAG, +// "%s allocation balance: %zu", +// thread->name ? thread->name : "Thread", +// thread->heap_size); +// memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); +// } + + furi_assert(thread->state == FuriThreadStateRunning); + + if(thread->is_service) { + ESP_LOGI( + TAG, + "%s service thread TCB memory will not be reclaimed", + thread->name ? thread->name : ""); + } + + // flush stdout + __furi_thread_stdout_flush(thread); + + furi_thread_set_state(thread, FuriThreadStateStopped); + + vTaskDelete(NULL); + furi_thread_catch(); +} + +FuriThread* furi_thread_alloc() { + FuriThread* thread = malloc(sizeof(FuriThread)); + // TODO: create default struct instead of using memset() + memset(thread, 0, sizeof(FuriThread)); + thread->output.buffer = furi_string_alloc(); + thread->is_service = false; + + FuriThread* parent = NULL; + if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { + // TLS is not available, if we called not from thread context + parent = pvTaskGetThreadLocalStoragePointer(NULL, 0); + + if(parent && parent->appid) { + furi_thread_set_appid(thread, parent->appid); + } else { + furi_thread_set_appid(thread, "unknown"); + } + } else { + // if scheduler is not started, we are starting driver thread + furi_thread_set_appid(thread, "driver"); + } + + /*FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode == FuriHalRtcHeapTrackModeAll) { + thread->heap_trace_enabled = true; + } else if(mode == FuriHalRtcHeapTrackModeTree && furi_thread_get_current_id()) { + if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled; + } else */{ + thread->heap_trace_enabled = false; + } + + return thread; +} + +FuriThread* furi_thread_alloc_ex( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context) { + FuriThread* thread = furi_thread_alloc(); + furi_thread_set_name(thread, name); + furi_thread_set_stack_size(thread, stack_size); + furi_thread_set_callback(thread, callback); + furi_thread_set_context(thread, context); + return thread; +} + +void furi_thread_free(FuriThread* thread) { + furi_assert(thread); + + // Ensure that use join before free + furi_assert(thread->state == FuriThreadStateStopped); + furi_assert(thread->task_handle == NULL); + + if(thread->name) free(thread->name); + if(thread->appid) free(thread->appid); + furi_string_free(thread->output.buffer); + + free(thread); +} + +void furi_thread_set_name(FuriThread* thread, const char* name) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + if(thread->name) free(thread->name); + thread->name = name ? strdup(name) : NULL; +} + +void furi_thread_set_appid(FuriThread* thread, const char* appid) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + if(thread->appid) free(thread->appid); + thread->appid = appid ? strdup(appid) : NULL; +} + +void furi_thread_mark_as_service(FuriThread* thread) { + thread->is_service = true; +} + +bool furi_thread_mark_is_service(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + assert(!FURI_IS_IRQ_MODE() && (hTask != NULL)); + FuriThread* thread = (FuriThread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); + assert(thread != NULL); + return thread->is_service; +} + +void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + furi_assert(stack_size % 4 == 0); + thread->stack_size = stack_size; +} + +void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + thread->callback = callback; +} + +void furi_thread_set_context(FuriThread* thread, void* context) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + thread->context = context; +} + +void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + furi_assert(priority >= FuriThreadPriorityIdle && priority <= FuriThreadPriorityIsr); + thread->priority = priority; +} + +void furi_thread_set_current_priority(FuriThreadPriority priority) { + UBaseType_t new_priority = priority ? priority : FuriThreadPriorityNormal; + vTaskPrioritySet(NULL, new_priority); +} + +FuriThreadPriority furi_thread_get_current_priority() { + return (FuriThreadPriority)uxTaskPriorityGet(NULL); +} + +void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + thread->state_callback = callback; +} + +void furi_thread_set_state_context(FuriThread* thread, void* context) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + thread->state_context = context; +} + +FuriThreadState furi_thread_get_state(FuriThread* thread) { + furi_assert(thread); + return thread->state; +} + +void furi_thread_start(FuriThread* thread) { + furi_assert(thread); + furi_assert(thread->callback); + furi_assert(thread->state == FuriThreadStateStopped); + furi_assert(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t))); + + furi_thread_set_state(thread, FuriThreadStateStarting); + + uint32_t stack = thread->stack_size / sizeof(StackType_t); + UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal; + if(thread->is_service) { + thread->task_handle = xTaskCreateStatic( + furi_thread_body, + thread->name, + stack, + thread, + priority, + malloc(sizeof(StackType_t) * stack), + malloc(sizeof(StaticTask_t))); + } else { + BaseType_t ret = xTaskCreate( + furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle); + furi_check(ret == pdPASS); + } + + furi_check(thread->task_handle); +} + +void furi_thread_cleanup_tcb_event(TaskHandle_t task) { + FuriThread* thread = pvTaskGetThreadLocalStoragePointer(task, 0); + if(thread) { + // clear thread local storage + vTaskSetThreadLocalStoragePointer(task, 0, NULL); + furi_assert(thread->task_handle == task); + thread->task_handle = NULL; + } +} + +bool furi_thread_join(FuriThread* thread) { + furi_assert(thread); + + furi_check(furi_thread_get_current() != thread); + + // !!! IMPORTANT NOTICE !!! + // + // If your thread exited, but your app stuck here: some other thread uses + // all cpu time, which delays kernel from releasing task handle + while(thread->task_handle) { + furi_delay_ms(10); + } + + return true; +} + +FuriThreadId furi_thread_get_id(FuriThread* thread) { + furi_assert(thread); + return thread->task_handle; +} + +void furi_thread_enable_heap_trace(FuriThread* thread) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + thread->heap_trace_enabled = true; +} + +void furi_thread_disable_heap_trace(FuriThread* thread) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + thread->heap_trace_enabled = false; +} + +size_t furi_thread_get_heap_size(FuriThread* thread) { + furi_assert(thread); + furi_assert(thread->heap_trace_enabled == true); + return thread->heap_size; +} + +int32_t furi_thread_get_return_code(FuriThread* thread) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + return thread->ret; +} + +FuriThreadId furi_thread_get_current_id() { + return xTaskGetCurrentTaskHandle(); +} + +FuriThread* furi_thread_get_current() { + FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0); + return thread; +} + +void furi_thread_yield() { + furi_assert(!FURI_IS_IRQ_MODE()); + taskYIELD(); +} + +/* Limits */ +#define MAX_BITS_TASK_NOTIFY 31U +#define MAX_BITS_EVENT_GROUPS 24U + +#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U)) +#define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U)) + +uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + uint32_t rflags; + BaseType_t yield; + + if((hTask == NULL) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) { + rflags = (uint32_t)FuriStatusErrorParameter; + } else { + rflags = (uint32_t)FuriStatusError; + + if(FURI_IS_IRQ_MODE()) { + yield = pdFALSE; + + (void)xTaskNotifyIndexedFromISR(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits, &yield); + (void)xTaskNotifyAndQueryIndexedFromISR( + hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, NULL); + + portYIELD_FROM_ISR(yield); + } else { + (void)xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits); + (void)xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags); + } + } + /* Return flags after setting */ + return (rflags); +} + +uint32_t furi_thread_flags_clear(uint32_t flags) { + TaskHandle_t hTask; + uint32_t rflags, cflags; + + if(FURI_IS_IRQ_MODE()) { + rflags = (uint32_t)FuriStatusErrorISR; + } else if((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { + rflags = (uint32_t)FuriStatusErrorParameter; + } else { + hTask = xTaskGetCurrentTaskHandle(); + + if(xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &cflags) == + pdPASS) { + rflags = cflags; + cflags &= ~flags; + + if(xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) != + pdPASS) { + rflags = (uint32_t)FuriStatusError; + } + } else { + rflags = (uint32_t)FuriStatusError; + } + } + + /* Return flags before clearing */ + return (rflags); +} + +uint32_t furi_thread_flags_get(void) { + TaskHandle_t hTask; + uint32_t rflags; + + if(FURI_IS_IRQ_MODE()) { + rflags = (uint32_t)FuriStatusErrorISR; + } else { + hTask = xTaskGetCurrentTaskHandle(); + + if(xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) != + pdPASS) { + rflags = (uint32_t)FuriStatusError; + } + } + + return (rflags); +} + +uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { + uint32_t rflags, nval; + uint32_t clear; + TickType_t t0, td, tout; + BaseType_t rval; + + if(FURI_IS_IRQ_MODE()) { + rflags = (uint32_t)FuriStatusErrorISR; + } else if((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { + rflags = (uint32_t)FuriStatusErrorParameter; + } else { + if((options & FuriFlagNoClear) == FuriFlagNoClear) { + clear = 0U; + } else { + clear = flags; + } + + rflags = 0U; + tout = timeout; + + t0 = xTaskGetTickCount(); + do { + rval = xTaskNotifyWaitIndexed(THREAD_NOTIFY_INDEX, 0, clear, &nval, tout); + + if(rval == pdPASS) { + rflags &= flags; + rflags |= nval; + + if((options & FuriFlagWaitAll) == FuriFlagWaitAll) { + if((flags & rflags) == flags) { + break; + } else { + if(timeout == 0U) { + rflags = (uint32_t)FuriStatusErrorResource; + break; + } + } + } else { + if((flags & rflags) != 0) { + break; + } else { + if(timeout == 0U) { + rflags = (uint32_t)FuriStatusErrorResource; + break; + } + } + } + + /* Update timeout */ + td = xTaskGetTickCount() - t0; + + if(td > tout) { + tout = 0; + } else { + tout -= td; + } + } else { + if(timeout == 0) { + rflags = (uint32_t)FuriStatusErrorResource; + } else { + rflags = (uint32_t)FuriStatusErrorTimeout; + } + } + } while(rval != pdFAIL); + } + + /* Return flags before clearing */ + return (rflags); +} + +uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_items) { + uint32_t i, count; + TaskStatus_t* task; + + if(FURI_IS_IRQ_MODE() || (thread_array == NULL) || (array_items == 0U)) { + count = 0U; + } else { + vTaskSuspendAll(); + + count = uxTaskGetNumberOfTasks(); + task = pvPortMalloc(count * sizeof(TaskStatus_t)); + + if(task != NULL) { + count = uxTaskGetSystemState(task, count, NULL); + + for(i = 0U; (i < count) && (i < array_items); i++) { + thread_array[i] = (FuriThreadId)task[i].xHandle; + } + count = i; + } + (void)xTaskResumeAll(); + + vPortFree(task); + } + + return (count); +} + +const char* furi_thread_get_name(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + const char* name; + + if(FURI_IS_IRQ_MODE() || (hTask == NULL)) { + name = NULL; + } else { + name = pcTaskGetName(hTask); + } + + return (name); +} + +const char* furi_thread_get_appid(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + const char* appid = "system"; + + if(!FURI_IS_IRQ_MODE() && (hTask != NULL)) { + FuriThread* thread = (FuriThread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); + if(thread) { + appid = thread->appid; + } + } + + return (appid); +} + +uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + uint32_t sz; + + if(FURI_IS_IRQ_MODE() || (hTask == NULL)) { + sz = 0U; + } else { + sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t)); + } + + return (sz); +} + +static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) { + if(thread->output.write_callback != NULL) { + thread->output.write_callback(data, size); + } else { + furi_hal_console_tx((const uint8_t*)data, size); + } + return size; +} + +static int32_t __furi_thread_stdout_flush(FuriThread* thread) { + FuriString* buffer = thread->output.buffer; + size_t size = furi_string_size(buffer); + if(size > 0) { + __furi_thread_stdout_write(thread, furi_string_get_cstr(buffer), size); + furi_string_reset(buffer); + } + return 0; +} + +void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { + FuriThread* thread = furi_thread_get_current(); + furi_assert(thread); + __furi_thread_stdout_flush(thread); + thread->output.write_callback = callback; +} + +FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback() { + FuriThread* thread = furi_thread_get_current(); + furi_assert(thread); + return thread->output.write_callback; +} + +size_t furi_thread_stdout_write(const char* data, size_t size) { + FuriThread* thread = furi_thread_get_current(); + furi_assert(thread); + if(size == 0 || data == NULL) { + return __furi_thread_stdout_flush(thread); + } else { + if(data[size - 1] == '\n') { + // if the last character is a newline, we can flush buffer and write data as is, wo buffers + __furi_thread_stdout_flush(thread); + __furi_thread_stdout_write(thread, data, size); + } else { + // string_cat doesn't work here because we need to write the exact size data + for(size_t i = 0; i < size; i++) { + furi_string_push_back(thread->output.buffer, data[i]); + if(data[i] == '\n') { + __furi_thread_stdout_flush(thread); + } + } + } + } + + return size; +} + +int32_t furi_thread_stdout_flush() { + FuriThread* thread = furi_thread_get_current(); + furi_assert(thread); + return __furi_thread_stdout_flush(thread); +} + +void furi_thread_suspend(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + vTaskSuspend(hTask); +} + +void furi_thread_resume(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + if(FURI_IS_IRQ_MODE()) { + xTaskResumeFromISR(hTask); + } else { + vTaskResume(hTask); + } +} + +bool furi_thread_is_suspended(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + return eTaskGetState(hTask) == eSuspended; +} diff --git a/components/furi/src/thread.h b/components/furi/src/thread.h new file mode 100644 index 00000000..c294bee1 --- /dev/null +++ b/components/furi/src/thread.h @@ -0,0 +1,338 @@ +/** + * @file thread.h + * Furi: Furi Thread API + */ + +#pragma once + +#include "base.h" +#include "common_defines.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** FuriThreadState */ +typedef enum { + FuriThreadStateStopped, + FuriThreadStateStarting, + FuriThreadStateRunning, +} FuriThreadState; + +/** FuriThreadPriority */ +typedef enum { + FuriThreadPriorityNone = 0, /**< Uninitialized, choose system default */ + FuriThreadPriorityIdle = 1, /**< Idle priority */ + FuriThreadPriorityLowest = 14, /**< Lowest */ + FuriThreadPriorityLow = 15, /**< Low */ + FuriThreadPriorityNormal = 16, /**< Normal */ + FuriThreadPriorityHigh = 17, /**< High */ + FuriThreadPriorityHighest = 18, /**< Highest */ + FuriThreadPriorityIsr = + (FURI_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */ +} FuriThreadPriority; + +/** FuriThread anonymous structure */ +typedef struct FuriThread FuriThread; + +/** FuriThreadId proxy type to OS low level functions */ +typedef void* FuriThreadId; + +/** FuriThreadCallback Your callback to run in new thread + * @warning never use osThreadExit in FuriThread + */ +typedef int32_t (*FuriThreadCallback)(void* context); + +/** Write to stdout callback + * @param data pointer to data + * @param size data size @warning your handler must consume everything + */ +typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); + +/** FuriThread state change callback called upon thread state change + * @param state new thread state + * @param context callback context + */ +typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context); + +/** Allocate FuriThread + * + * @return FuriThread instance + */ +FuriThread* furi_thread_alloc(); + +/** Allocate FuriThread, shortcut version + * + * @param name + * @param stack_size + * @param callback + * @param context + * @return FuriThread* + */ +FuriThread* furi_thread_alloc_ex( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context); + +/** Release FuriThread + * + * @warning see furi_thread_join + * + * @param thread FuriThread instance + */ +void furi_thread_free(FuriThread* thread); + +/** Set FuriThread name + * + * @param thread FuriThread instance + * @param name string + */ +void furi_thread_set_name(FuriThread* thread, const char* name); + +/** + * @brief Set FuriThread appid + * Technically, it is like a "process id", but it is not a system-wide unique identifier. + * All threads spawned by the same app will have the same appid. + * + * @param thread + * @param appid + */ +void furi_thread_set_appid(FuriThread* thread, const char* appid); + +/** Mark thread as service + * The service cannot be stopped or removed, and cannot exit from the thread body + * + * @param thread + */ +void furi_thread_mark_as_service(FuriThread* thread); + +/** Set FuriThread stack size + * + * @param thread FuriThread instance + * @param stack_size stack size in bytes + */ +void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size); + +/** Set FuriThread callback + * + * @param thread FuriThread instance + * @param callback FuriThreadCallback, called upon thread run + */ +void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback); + +/** Set FuriThread context + * + * @param thread FuriThread instance + * @param context pointer to context for thread callback + */ +void furi_thread_set_context(FuriThread* thread, void* context); + +/** Set FuriThread priority + * + * @param thread FuriThread instance + * @param priority FuriThreadPriority value + */ +void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority); + +/** Set current thread priority + * + * @param priority FuriThreadPriority value + */ +void furi_thread_set_current_priority(FuriThreadPriority priority); + +/** Get current thread priority + * + * @return FuriThreadPriority value + */ +FuriThreadPriority furi_thread_get_current_priority(); + +/** Set FuriThread state change callback + * + * @param thread FuriThread instance + * @param callback state change callback + */ +void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback); + +/** Set FuriThread state change context + * + * @param thread FuriThread instance + * @param context pointer to context + */ +void furi_thread_set_state_context(FuriThread* thread, void* context); + +/** Get FuriThread state + * + * @param thread FuriThread instance + * + * @return thread state from FuriThreadState + */ +FuriThreadState furi_thread_get_state(FuriThread* thread); + +/** Start FuriThread + * + * @param thread FuriThread instance + */ +void furi_thread_start(FuriThread* thread); + +/** Join FuriThread + * + * @warning Use this method only when CPU is not busy(Idle task receives + * control), otherwise it will wait forever. + * + * @param thread FuriThread instance + * + * @return bool + */ +bool furi_thread_join(FuriThread* thread); + +/** Get FreeRTOS FuriThreadId for FuriThread instance + * + * @param thread FuriThread instance + * + * @return FuriThreadId or NULL + */ +FuriThreadId furi_thread_get_id(FuriThread* thread); + +/** Enable heap tracing + * + * @param thread FuriThread instance + */ +void furi_thread_enable_heap_trace(FuriThread* thread); + +/** Disable heap tracing + * + * @param thread FuriThread instance + */ +void furi_thread_disable_heap_trace(FuriThread* thread); + +/** Get thread heap size + * + * @param thread FuriThread instance + * + * @return size in bytes + */ +size_t furi_thread_get_heap_size(FuriThread* thread); + +/** Get thread return code + * + * @param thread FuriThread instance + * + * @return return code + */ +int32_t furi_thread_get_return_code(FuriThread* thread); + +/** Thread related methods that doesn't involve FuriThread directly */ + +/** Get FreeRTOS FuriThreadId for current thread + * + * @param thread FuriThread instance + * + * @return FuriThreadId or NULL + */ +FuriThreadId furi_thread_get_current_id(); + +/** Get FuriThread instance for current thread + * + * @return pointer to FuriThread or NULL if this thread doesn't belongs to Furi + */ +FuriThread* furi_thread_get_current(); + +/** Return control to scheduler */ +void furi_thread_yield(); + +uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags); + +uint32_t furi_thread_flags_clear(uint32_t flags); + +uint32_t furi_thread_flags_get(void); + +uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout); + +/** + * @brief Enumerate threads + * + * @param thread_array array of FuriThreadId, where thread ids will be stored + * @param array_items array size + * @return uint32_t threads count + */ +uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_items); + +/** + * @brief Get thread name + * + * @param thread_id + * @return const char* name or NULL + */ +const char* furi_thread_get_name(FuriThreadId thread_id); + +/** + * @brief Get thread appid + * + * @param thread_id + * @return const char* appid + */ +const char* furi_thread_get_appid(FuriThreadId thread_id); + +/** + * @brief Get thread stack watermark + * + * @param thread_id + * @return uint32_t + */ +uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); + +/** Get STDOUT callback for thead + * + * @return STDOUT callback + */ +FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(); + +/** Set STDOUT callback for thread + * + * @param callback callback or NULL to clear + */ +void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); + +/** Write data to buffered STDOUT + * + * @param data input data + * @param size input data size + * + * @return size_t written data size + */ +size_t furi_thread_stdout_write(const char* data, size_t size); + +/** Flush data to STDOUT + * + * @return int32_t error code + */ +int32_t furi_thread_stdout_flush(); + +/** Suspend thread + * + * @param thread_id thread id + */ +void furi_thread_suspend(FuriThreadId thread_id); + +/** Resume thread + * + * @param thread_id thread id + */ +void furi_thread_resume(FuriThreadId thread_id); + +/** Get thread suspended state + * + * @param thread_id thread id + * @return true if thread is suspended + */ +bool furi_thread_is_suspended(FuriThreadId thread_id); + +bool furi_thread_mark_is_service(FuriThreadId thread_id); + +#ifdef __cplusplus +} +#endif diff --git a/components/furi/src/timer.c b/components/furi/src/timer.c new file mode 100644 index 00000000..8b6d6c0c --- /dev/null +++ b/components/furi/src/timer.c @@ -0,0 +1,172 @@ +#include "timer.h" +#include "check.h" +#include "kernel.h" + +#include +#include + +typedef struct { + FuriTimerCallback func; + void* context; +} TimerCallback_t; + +static void TimerCallback(TimerHandle_t hTimer) { + TimerCallback_t* callb; + + /* Retrieve pointer to callback function and context */ + callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); + + /* Remove dynamic allocation flag */ + callb = (TimerCallback_t*)((uint32_t)callb & ~1U); + + if(callb != NULL) { + callb->func(callb->context); + } +} + +FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { + furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL)); + + TimerHandle_t hTimer; + TimerCallback_t* callb; + UBaseType_t reload; + + hTimer = NULL; + + /* Dynamic memory allocation is available: if memory for callback and */ + /* its context is not provided, allocate it from dynamic memory pool */ + callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t)); + + callb->func = func; + callb->context = context; + + if(type == FuriTimerTypeOnce) { + reload = pdFALSE; + } else { + reload = pdTRUE; + } + + /* Store callback memory dynamic allocation flag */ + callb = (TimerCallback_t*)((uint32_t)callb | 1U); + // TimerCallback function is always provided as a callback and is used to call application + // specified function with its context both stored in structure callb. + hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, TimerCallback); + furi_check(hTimer); + + /* Return timer ID */ + return ((FuriTimer*)hTimer); +} + +void furi_timer_free(FuriTimer* instance) { + furi_assert(!furi_kernel_is_irq_or_masked()); + furi_assert(instance); + + TimerHandle_t hTimer = (TimerHandle_t)instance; + TimerCallback_t* callb; + + callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); + + furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); + + while(furi_timer_is_running(instance)) furi_delay_tick(2); + + if((uint32_t)callb & 1U) { + /* Callback memory was allocated from dynamic pool, clear flag */ + callb = (TimerCallback_t*)((uint32_t)callb & ~1U); + + /* Return allocated memory to dynamic pool */ + free(callb); + } +} + +FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { + furi_assert(!furi_kernel_is_irq_or_masked()); + furi_assert(instance); + furi_assert(ticks < portMAX_DELAY); + + TimerHandle_t hTimer = (TimerHandle_t)instance; + FuriStatus stat; + + if(xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS) { + stat = FuriStatusOk; + } else { + stat = FuriStatusErrorResource; + } + + /* Return execution status */ + return (stat); +} + +FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) { + furi_assert(!furi_kernel_is_irq_or_masked()); + furi_assert(instance); + furi_assert(ticks < portMAX_DELAY); + + TimerHandle_t hTimer = (TimerHandle_t)instance; + FuriStatus stat; + + if(xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS && + xTimerReset(hTimer, portMAX_DELAY) == pdPASS) { + stat = FuriStatusOk; + } else { + stat = FuriStatusErrorResource; + } + + /* Return execution status */ + return (stat); +} + +FuriStatus furi_timer_stop(FuriTimer* instance) { + furi_assert(!furi_kernel_is_irq_or_masked()); + furi_assert(instance); + + TimerHandle_t hTimer = (TimerHandle_t)instance; + + furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS); + + return FuriStatusOk; +} + +uint32_t furi_timer_is_running(FuriTimer* instance) { + furi_assert(!furi_kernel_is_irq_or_masked()); + furi_assert(instance); + + TimerHandle_t hTimer = (TimerHandle_t)instance; + + /* Return 0: not running, 1: running */ + return (uint32_t)xTimerIsTimerActive(hTimer); +} + +uint32_t furi_timer_get_expire_time(FuriTimer* instance) { + furi_assert(!furi_kernel_is_irq_or_masked()); + furi_assert(instance); + + TimerHandle_t hTimer = (TimerHandle_t)instance; + + return (uint32_t)xTimerGetExpiryTime(hTimer); +} + +void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg) { + BaseType_t ret = pdFAIL; + if(furi_kernel_is_irq_or_masked()) { + ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL); + } else { + ret = xTimerPendFunctionCall(callback, context, arg, FuriWaitForever); + } + furi_check(ret == pdPASS); +} + +void furi_timer_set_thread_priority(FuriTimerThreadPriority priority) { + furi_assert(!furi_kernel_is_irq_or_masked()); + + TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); + furi_check(task_handle); // Don't call this method before timer task start + + if(priority == FuriTimerThreadPriorityNormal) { + vTaskPrioritySet(task_handle, configTIMER_TASK_PRIORITY); + } else if(priority == FuriTimerThreadPriorityElevated) { + vTaskPrioritySet(task_handle, configMAX_PRIORITIES - 1); + } else { + furi_crash(); + } +} \ No newline at end of file diff --git a/components/furi/src/timer.h b/components/furi/src/timer.h new file mode 100644 index 00000000..9dac874a --- /dev/null +++ b/components/furi/src/timer.h @@ -0,0 +1,106 @@ +#pragma once + +#include "base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*FuriTimerCallback)(void* context); + +typedef enum { + FuriTimerTypeOnce = 0, ///< One-shot timer. + FuriTimerTypePeriodic = 1 ///< Repeating timer. +} FuriTimerType; + +typedef void FuriTimer; + +/** Allocate timer + * + * @param[in] func The callback function + * @param[in] type The timer type + * @param context The callback context + * + * @return The pointer to FuriTimer instance + */ +FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context); + +/** Free timer + * + * @param instance The pointer to FuriTimer instance + */ +void furi_timer_free(FuriTimer* instance); + +/** Start timer + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. + * + * @param instance The pointer to FuriTimer instance + * @param[in] ticks The interval in ticks + * + * @return The furi status. + */ +FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks); + +/** Restart timer with previous timeout value + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. + * + * @param instance The pointer to FuriTimer instance + * @param[in] ticks The interval in ticks + * + * @return The furi status. + */ +FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks); + +/** Stop timer + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. + * + * @param instance The pointer to FuriTimer instance + * + * @return The furi status. + */ +FuriStatus furi_timer_stop(FuriTimer* instance); + +/** Is timer running + * + * @warning This cal may and will return obsolete timer state if timer + * commands are still in the queue. Please read FreeRTOS timer + * documentation first. + * + * @param instance The pointer to FuriTimer instance + * + * @return 0: not running, 1: running + */ +uint32_t furi_timer_is_running(FuriTimer* instance); + +/** Get timer expire time + * + * @param instance The Timer instance + * + * @return expire tick + */ +uint32_t furi_timer_get_expire_time(FuriTimer* instance); + +typedef void (*FuriTimerPendigCallback)(void* context, uint32_t arg); + +void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg); + +typedef enum { + FuriTimerThreadPriorityNormal, /**< Lower then other threads */ + FuriTimerThreadPriorityElevated, /**< Same as other threads */ +} FuriTimerThreadPriority; + +/** Set Timer thread priority + * + * @param[in] priority The priority + */ +void furi_timer_set_thread_priority(FuriTimerThreadPriority priority); + +#ifdef __cplusplus +} +#endif diff --git a/components/mlib/CMakeLists.txt b/components/mlib/CMakeLists.txt new file mode 100644 index 00000000..16472f46 --- /dev/null +++ b/components/mlib/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register( + INCLUDE_DIRS "." +) diff --git a/components/mlib/LICENSE b/components/mlib/LICENSE new file mode 100644 index 00000000..27766ef5 --- /dev/null +++ b/components/mlib/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2017-2023, Patrick Pelissier +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/mlib/README.md b/components/mlib/README.md new file mode 100644 index 00000000..85a39303 --- /dev/null +++ b/components/mlib/README.md @@ -0,0 +1,3 @@ +This folder is a partial git clone from: +https://github.com/P-p-H-d/mlib/commit/d9401371a6bc1c0f240161514549976bcdd98999 + diff --git a/components/mlib/m-algo.h b/components/mlib/m-algo.h new file mode 100644 index 00000000..46470bde --- /dev/null +++ b/components/mlib/m-algo.h @@ -0,0 +1,1240 @@ +/* + * M*LIB - ALGO module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_ALGO_H +#define MSTARLIB_ALGO_H + +#include "m-core.h" + +/* Define different kind of basic algorithms named 'name' over the container + which oplist is 'cont_oplist'. + USAGE: + ALGO_DEF(algogName, containerOplist|type if oplist has been registered) */ +#define M_ALGO_DEF(name, cont_oplist) \ + M_BEGIN_PROTECTED_CODE \ + M_ALG0_DEF_P1(name, M_GLOBAL_OPLIST(cont_oplist)) \ + M_END_PROTECTED_CODE + + +/* Map a function (or a macro) to all elements of a container. + USAGE: + ALGO_FOR_EACH(container, containerOplist|type_if_registered_oplist, function[, extra arguments of function]) */ +#define M_ALGO_FOR_EACH(container, cont_oplist, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (M_ALG0_FOR_EACH, M_ALG0_FOR_EACH_ARG) \ + (container, M_GLOBAL_OPLIST(cont_oplist), __VA_ARGS__) + + +/* Map a function (or a macro) to all elements of a container + and store it into another container. + USAGE: + ALGO_TRANSFORM(contDst, contDOplist|type_if_registered_oplist, contSrc, contSrcOplist|type_if_registered_oplist, + function[, extra arguments of function]) */ +#define M_ALGO_TRANSFORM(contD, contDop, contS, contSop, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (M_ALG0_TRANSFORM, M_ALG0_TRANSFORM_ARG) \ + (contD, M_GLOBAL_OPLIST(contDop), contS, M_GLOBAL_OPLIST(contSop), __VA_ARGS__) + + +/* Extract a subset of a container to copy into another container. + USAGE: + ALGO_EXTRACT(contDst, contDstOplist|type_if_registered_oplist, contSrc, contSrcOplist|type_if_registered_oplist + [, function [, extra arguments of function]]) */ +#define M_ALGO_EXTRACT(contD, contDop, contS, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (M_ALG0_EXTRACT, \ + M_IF_NARGS_EQ2(__VA_ARGS__)(M_ALG0_EXTRACT_FUNC, M_ALG0_EXTRACT_ARG)) \ + (contD, contDop, contS, __VA_ARGS__) + + +/* Perform a Reduce operation over a container. + USAGE: + ALGO_REDUCE(dstVar, container, contOplist|type_if_registered_oplist, reduceFunc + [, mapFunc[, extraParameters of map function]]) + or + ALGO_REDUCE( (dstVar, dstOplist|type_if_registered_oplist), container, contOplist|type_if_registered_oplist, reduceFunc + [, mapFunc[, extraParameters of map function]]) + if the destination variable is not of the same type than the elements of the containers. +*/ +#define M_ALGO_REDUCE(dest, cont, contOp, ...) \ + M_IF(M_PARENTHESIS_P(dest)) \ + (M_ALG0_REDUCE_DISPATCH(M_PAIR_1 dest, M_GLOBAL_OPLIST(M_PAIR_2 dest), M_GLOBAL_TYPE(M_PAIR_2 dest),cont, M_GLOBAL_OPLIST(contOp), __VA_ARGS__), \ + M_ALG0_REDUCE_DISPATCH(dest, M_GET_OPLIST M_GLOBAL_OPLIST(contOp), M_GET_SUBTYPE M_GLOBAL_OPLIST(contOp), cont, contOp, __VA_ARGS__)) \ + + +/* Insert into the container 'contDst' at position 'position' all the values + of container 'contSrc'. + USAGE: + ALGO_INSERT_AT(containerDst, containerDstOPLIST|type_if_registered_oplist, containerDstIterator, containerSrc, containerSrcOPLIST|type_if_registered_oplist) + */ +#define M_ALGO_INSERT_AT(contDst, contDstOp, position, contSrc, contSrcOp) \ + M_ALG0_INSERT_AT(contDst, M_GLOBAL_OPLIST(contDstOp), position, contSrc, M_GLOBAL_OPLIST(contSrcOp) ) + + +/*****************************************************************************/ +/******************************** INTERNAL ***********************************/ +/*****************************************************************************/ + +/* Try to expand the algorithms */ +#define M_ALG0_DEF_P1(name, cont_oplist) \ + M_ALG0_DEF_P2(name, M_GET_TYPE cont_oplist, cont_oplist, \ + M_GET_SUBTYPE cont_oplist, M_GET_OPLIST cont_oplist, \ + M_GET_IT_TYPE cont_oplist) + +/* First validate the first oplist */ +#define M_ALG0_DEF_P2(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + M_IF_OPLIST(cont_oplist)(M_ALG0_DEF_P3, M_ALG0_DEF_FAILURE)(name, container_t, cont_oplist, type_t, type_oplist, it_t) + +/* Then validate the second oplist */ +#define M_ALG0_DEF_P3(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + M_IF_OPLIST(type_oplist)(M_ALG0_DEF_P4, M_ALG0_DEF_FAILURE)(name, container_t, cont_oplist, type_t, type_oplist, it_t) + +/* Stop processing with a compilation failure if an oplist was invalid */ +#define M_ALG0_DEF_FAILURE(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ALGO_DEF): one of the given argument is not a valid oplist: " M_AS_STR(cont_oplist) " / " M_AS_STR(type_oplist) ) + + +/* Expand all algorithms */ +#define M_ALG0_DEF_P4(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + M_CHECK_COMPATIBLE_OPLIST(name, 10, container_t, cont_oplist) \ + M_CHECK_COMPATIBLE_OPLIST(name, 11, type_t, type_oplist) \ + \ + M_ALG0_DEF_CALLBACK(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + M_IF_FUNCOBJ(M_ALG0_DEF_FUNCOBJ(name, container_t, cont_oplist, type_t, type_oplist, it_t)) \ + \ + M_IF_METHOD(EQUAL, type_oplist)( \ + M_ALG0_DEF_FIND(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + , /* NO EQUAL */) \ + \ + M_ALG0_DEF_FIND_IF(name, container_t, cont_oplist, type_t, type_oplist, it_t, \ + if, M_F(name,_test_cb_ct), M_F(name,_eq_cb_ct), M_APPLY, M_APPLY) \ + M_IF_FUNCOBJ(M_ALG0_DEF_FIND_IF(name, container_t, cont_oplist, type_t, type_oplist, it_t, \ + fo, M_F(name,_test_obj_t), M_F(name,_eq_obj_t), M_F(name, _test_obj_call), M_F(name, _eq_obj_call))) \ + \ + M_ALG0_DEF_MAP(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + M_ALG0_DEF_ALL_OF(name, container_t, cont_oplist, type_t, type_oplist, it_t, \ + _, M_F(name,_test_cb_ct), M_APPLY) \ + M_IF_FUNCOBJ(M_ALG0_DEF_ALL_OF(name, container_t, cont_oplist, type_t, type_oplist, it_t, \ + _fo_, M_F(name,_test_obj_t), M_F(name,_test_obj_call)) ) \ + \ + /* If there is a IT_REF method, we consider the container as modifiable through iterator */ \ + M_IF_METHOD(IT_REF, cont_oplist)( \ + M_ALG0_DEF_FILL(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + M_ALG0_DEF_VECTOR(name, container_t, cont_oplist, type_t, type_oplist, it_t)\ + \ + M_IF_METHOD(CMP, type_oplist)( \ + M_ALG0_DEF_MINMAX(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + M_ALG0_DEF_SORT(name, container_t, cont_oplist, type_t, type_oplist, it_t, +, _sort) \ + M_ALG0_DEF_SORT(name, container_t, cont_oplist, type_t, type_oplist, it_t, -, _sort_dsc) \ + M_IF_METHOD(IT_REMOVE, cont_oplist)( \ + M_ALG0_DEF_REMOVE(name, container_t, cont_oplist, type_t, type_oplist, it_t)\ + , /* No IT_REMOVE method */) \ + , /* No CMP method */) \ + \ + M_IF_FUNCOBJ(M_ALG0_DEF_SORT_AUX(name, container_t, cont_oplist, type_t, type_oplist, it_t, \ + _sort_fo, M_ALG0_SORT_CALL_OBJ_P4, M_ALG0_SORT_PARAM_OBJ_P4, M_ALG0_SORT_ARG_OBJ_P4) ) \ + , /* No IT_REF method */) \ + \ + M_IF_METHOD(EXT_ALGO, type_oplist)( \ + M_GET_EXT_ALGO type_oplist (name, cont_oplist, type_oplist) \ + , /* No EXT_ALGO method */ ) \ + + +/* Define the types of the callbacks associated to the algorithms. + * Types remain internal */ +#define M_ALG0_DEF_CALLBACK(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + typedef bool (*M_F(name, _test_cb_ct))(type_t const); \ + typedef bool (*M_F(name, _eq_cb_ct))(type_t const, type_t const); \ + typedef int (*M_F(name, _cmp_cb_ct))(type_t const, type_t const); \ + typedef void (*M_F(name, _transform_cb_ct))(type_t *, type_t const); \ + typedef void (*M_F(name, _apply_cb_ct))(type_t); \ + + +/* Define the function objects associated to the algorithms. + * Created Function objects are part of the public interface */ +#define M_ALG0_DEF_FUNCOBJ(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + FUNC_OBJ_ITF_DEF(M_F(name, _test_obj), bool, type_t const) \ + FUNC_OBJ_ITF_DEF(M_F(name, _eq_obj), bool, type_t const, type_t const ) \ + FUNC_OBJ_ITF_DEF(M_F(name, _cmp_obj), int, type_t const, type_t const ) \ + FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), void, type_t *, type_t const ) \ + FUNC_OBJ_ITF_DEF(M_F(name, _apply_obj), void, type_t * ) \ + + +/* Define the sort functions with the CMP operator using the order selected */ +#define M_ALG0_DEF_SORT(name, container_t, cont_oplist, type_t, type_oplist, it_t, order, sort_name) \ + \ + /* Define the encapsulation function that perform the selected order */ \ + M_INLINE int M_C3(name,sort_name,_cmp)(type_t const*a,type_t const*b) \ + { \ + return order M_CALL_CMP(type_oplist, *a, *b); \ + } \ + \ + M_ALG0_DEF_SORT_AUX(name, container_t, cont_oplist, type_t, type_oplist, it_t, sort_name, M_ALG0_SORT_CALL_CMP_P4, M_EAT, /*empty*/ ) + +// Call the comparaison function of the type oplist (Using CMP operator) +#define M_ALG0_SORT_CALL_CMP_P4(name, sort_name, ref1, ref2) \ + M_C3(name, sort_name,_cmp)(ref1, ref2) + + +// Call the created function object +#define M_ALG0_SORT_CALL_OBJ_P4(name, sort_name, ref1, ref2) \ + M_F(name, _cmp_obj_call)(funcobj, *ref1, *ref2) +// Adding a parameter named 'funcobj' to the algorithm functions +#define M_ALG0_SORT_PARAM_OBJ_P4(name) M_DEFERRED_COMMA M_F(name, _cmp_obj_t) funcobj +#define M_ALG0_SORT_ARG_OBJ_P4 M_DEFERRED_COMMA funcobj + + +/* Define the sort funtions using either the CMP operator or a function object + - name: prefix of algorithms + - container_t: type of the container + - cont_oplist: oplist of the container + - type_t: type of the data within the container + - type_oplist: oplist of such type + - it_t: type of the iterator of the container + - sort_name: suffix used for creating the sort functions + - cmp_func: macro to call to get the CMP order + - cmp_param: macro to use to generate an extra argument for the function (needed for function object). + It is needed to add another argument to the function. + - cmp_arg: Name of such argument. + */ +#define M_ALG0_DEF_SORT_AUX(name, container_t, cont_oplist, type_t, type_oplist, it_t, sort_name, cmp_func, cmp_param, cmp_arg) \ + \ + /* Test if the container is sorted */ \ + M_INLINE bool \ + M_C3(name,sort_name,_p)(const container_t l cmp_param(name)) \ + { \ + it_t it1; \ + it_t it2; \ + /* Linear comparaison of TAB[N] & TAB[N+1] to test if the order is correct */ \ + M_CALL_IT_FIRST(cont_oplist, it1, l); \ + M_CALL_IT_SET(cont_oplist, it2, it1); \ + M_CALL_IT_NEXT(cont_oplist, it2); \ + while (!M_CALL_IT_END_P(cont_oplist, it2)) { \ + type_t const *ref1 = M_CALL_IT_CREF(cont_oplist, it1); \ + type_t const *ref2 = M_CALL_IT_CREF(cont_oplist, it2); \ + if (!(cmp_func(name, sort_name, ref1, ref2) <= 0)) { \ + return false; \ + } \ + M_CALL_IT_SET(cont_oplist, it1, it2); \ + M_CALL_IT_NEXT(cont_oplist, it2); \ + } \ + return true; \ + } \ + \ + /* Sort function. It can be generated from 3 algorithms: */ \ + /* - a specialized version defined by the container */ \ + /* - an unstable merge sort (need 'splice_back' method) */ \ + /* - an insertion sort (need 'previous' method) */ \ + /* - a selection sort */ \ + M_IF(M_AND(M_TEST_METHOD_P(SORT, cont_oplist), M_EMPTY_P(cmp_arg)))( \ + /******** OPTIMIZED SORT FOR CONTAINER *********/ \ + M_INLINE void M_F(name,sort_name)(container_t l) \ + { \ + M_CALL_SORT(cont_oplist, l, M_C3(name, sort_name,_cmp)); \ + } \ + , \ + \ + M_IF_METHOD2(SPLICE_BACK, SPLICE_AT, cont_oplist)( \ + /******** MERGE SORT (unstable) ********/ \ + /* NOTE: Only reasonable for lists (To move in m-list.h ?) */ \ + \ + /* Split the container 'l' into near even size l1 and l2 * \ + using odd/even splitting */ \ + M_INLINE void \ + M_C3(name,sort_name,_split)(container_t l1, container_t l2, container_t l) \ + { \ + it_t it; \ + bool b = false; \ + /* Split 'l' into 'l1' and 'l2' */ \ + for (M_CALL_IT_FIRST(cont_oplist,it, l); \ + !M_CALL_IT_END_P(cont_oplist, it);) { \ + M_CALL_SPLICE_BACK(cont_oplist, (b ? l1 : l2), l, it); \ + b = !b; \ + } \ + /* M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); */ \ + } \ + \ + /* Merge in empty 'l' the sorted container 'l1' and 'l2' */ \ + M_INLINE void \ + M_C3(name,sort_name,_merge)(container_t l, container_t l1, container_t l2 cmp_param(name)) \ + { \ + /* M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); */ \ + it_t it; \ + it_t it1; \ + it_t it2; \ + M_CALL_IT_END(cont_oplist, it, l); \ + M_CALL_IT_FIRST(cont_oplist,it1, l1); \ + M_CALL_IT_FIRST(cont_oplist,it2, l2); \ + while (true) { \ + /* Compare current elements of the containers l1 and l2 */ \ + int c = cmp_func(name, sort_name, M_CALL_IT_CREF(cont_oplist, it1), \ + M_CALL_IT_CREF(cont_oplist, it2)); \ + if (c <= 0) { \ + /* Move the element of l1 in the new container */ \ + M_CALL_SPLICE_AT(cont_oplist, l, it, l1, it1); \ + if (M_UNLIKELY (M_CALL_IT_END_P(cont_oplist, it1))) { \ + /* Move all remaining elements of l2 in 'l' */ \ + while (!M_CALL_IT_END_P(cont_oplist, it2)) { \ + M_CALL_SPLICE_AT(cont_oplist, l, it, l2, it2); \ + } \ + return; \ + } \ + } else { \ + /* Move the element of l2 in the new container */ \ + M_CALL_SPLICE_AT(cont_oplist, l, it, l2, it2); \ + if (M_UNLIKELY (M_CALL_IT_END_P(cont_oplist, it2))) { \ + /* Move all remaining elements of l1 in 'l' */ \ + while (!M_CALL_IT_END_P(cont_oplist, it1)) { \ + M_CALL_SPLICE_AT(cont_oplist, l, it, l1, it1); \ + } \ + return; \ + } \ + } \ + } \ + /* Cannot occur */ \ + } \ + \ + /* Sort the container 'l' */ \ + M_INLINE void \ + M_F(name,sort_name)(container_t l cmp_param(name)) \ + { \ + container_t l1; \ + container_t l2; \ + it_t it; \ + it_t it1; \ + it_t it2; \ + /* First deal with 0, 1, or 2 size container */ \ + M_CALL_IT_FIRST(cont_oplist, it, l); \ + if (M_UNLIKELY (M_CALL_IT_END_P(cont_oplist, it))) \ + return; \ + M_CALL_IT_SET(cont_oplist, it1, it); \ + M_CALL_IT_NEXT(cont_oplist, it); \ + if (M_UNLIKELY (M_CALL_IT_END_P(cont_oplist, it))) \ + return; \ + M_CALL_IT_SET(cont_oplist, it2, it); \ + M_CALL_IT_NEXT(cont_oplist, it); \ + if (M_UNLIKELY (M_CALL_IT_END_P(cont_oplist, it))) { \ + /* Two elements */ \ + int c = cmp_func(name, sort_name, \ + M_CALL_IT_CREF(cont_oplist, it1), \ + M_CALL_IT_CREF(cont_oplist, it2)); \ + if (c > 0) { \ + /* SWAP */ \ + M_CALL_SPLICE_BACK(cont_oplist, l, l, it2); \ + } \ + return; \ + } \ + /* Container length is greater than 2: split, sort & merge */ \ + M_CALL_INIT(cont_oplist, l1); \ + M_CALL_INIT(cont_oplist, l2); \ + M_C3(name,sort_name,_split)(l1, l2, l); \ + M_F(name,sort_name)(l1 cmp_arg); \ + M_F(name,sort_name)(l2 cmp_arg); \ + M_C3(name,sort_name,_merge)(l, l1, l2 cmp_arg); \ + /* l1 & l2 shall be empty now */ \ + M_CALL_CLEAR(cont_oplist, l2); \ + M_CALL_CLEAR(cont_oplist, l1); \ + } \ + , \ + M_IF_METHOD(IT_PREVIOUS, cont_oplist)( \ + /******** GENERIC INSERTION SORT *********/ \ + M_INLINE void M_F(name,sort_name)(container_t l cmp_param(name)) \ + { \ + it_t it1; \ + it_t it2; \ + it_t it2p1; \ + /* NOTE: Do not use SET, this is a MOVE operation */ \ + /* Start from i := 1 */ \ + M_CALL_IT_FIRST(cont_oplist, it1, l); \ + M_CALL_IT_NEXT(cont_oplist, it1); \ + while (!M_CALL_IT_END_P(cont_oplist, it1) ) { \ + type_t x; \ + /* x := TAB[i] */ \ + memcpy (&x, M_CALL_IT_CREF(cont_oplist, it1), sizeof (type_t)); \ + /* j := i -1 // jp1 := i (= j +1) */ \ + M_CALL_IT_SET(cont_oplist, it2, it1); \ + M_CALL_IT_PREVIOUS(cont_oplist, it2); \ + M_CALL_IT_SET(cont_oplist, it2p1, it1); \ + while (!M_CALL_IT_END_P(cont_oplist, it2) \ + && !(cmp_func(name, sort_name, \ + M_CALL_IT_CREF(cont_oplist, it2), \ + M_CONST_CAST(type_t, &x)) <= 0)) { \ + /* TAB[jp1=j+1] := TAB[j] */ \ + memcpy(M_CALL_IT_REF(cont_oplist, it2p1), \ + M_CALL_IT_CREF(cont_oplist, it2), sizeof (type_t) ); \ + /* jp1 := j (= jp1-1) */ \ + M_CALL_IT_SET(cont_oplist, it2p1, it2); \ + M_CALL_IT_PREVIOUS(cont_oplist, it2); \ + } \ + /* TAB[jp1] := x */ \ + memcpy(M_CALL_IT_REF(cont_oplist, it2p1), &x, sizeof (type_t) ); \ + /* i := i + 1 */ \ + M_CALL_IT_NEXT(cont_oplist, it1); \ + } \ + } \ + \ + , \ + /********** GENERIC SELECTION SORT ************/ \ + M_INLINE void M_F(name,sort_name)(container_t l cmp_param(name)) \ + { \ + it_t it1; \ + it_t it2; \ + for(M_CALL_IT_FIRST(cont_oplist, it1, l); \ + !M_CALL_IT_LAST_P(cont_oplist, it1); \ + M_CALL_IT_NEXT(cont_oplist, it1)) { \ + it_t it_min; \ + M_CALL_IT_SET(cont_oplist, it_min, it1); \ + M_CALL_IT_SET(cont_oplist, it2, it1); \ + for(M_CALL_IT_NEXT(cont_oplist, it2) ; \ + !M_CALL_IT_END_P(cont_oplist, it2); \ + M_CALL_IT_NEXT(cont_oplist, it2)) { \ + if (cmp_func(name, sort_name, M_CALL_IT_CREF(cont_oplist, it2), \ + M_CALL_IT_CREF(cont_oplist, it_min)) < 0) { \ + M_CALL_IT_SET(cont_oplist, it_min, it2); \ + } \ + } \ + if (M_CALL_IT_EQUAL_P(cont_oplist, it_min, it1) == false) { \ + /* TODO: Use SWAP method of type_oplist if available */ \ + type_t x; /* Do not use SET, as it is a MOVE operation */ \ + memcpy (&x, M_CALL_IT_CREF(cont_oplist, it1), sizeof (type_t)); \ + memcpy (M_CALL_IT_REF(cont_oplist, it1), \ + M_CALL_IT_CREF(cont_oplist, it_min), sizeof (type_t)); \ + memcpy (M_CALL_IT_REF(cont_oplist, it_min), &x, sizeof (type_t)); \ + } \ + } \ + } \ + ) /* IF IT_PREVIOUS METHOD */ \ + ) /* SPLICE BACK METHOD */ \ + ) /* IF SORT METHOD */ \ + /* Compute the union of two ***sorted*** containers */ \ + M_IF_METHOD(IT_INSERT, cont_oplist)( \ + M_INLINE void \ + M_C3(name,sort_name,_union)(container_t dst, const container_t src cmp_param(name)) \ + { \ + it_t itSrc; \ + it_t itDst; \ + it_t itIns; \ + M_ASSERT(M_C3(name,sort_name,_p)(dst cmp_arg)); \ + M_ASSERT(M_C3(name,sort_name,_p)(src cmp_arg)); \ + /* Iterate over both dst & src containers */ \ + M_CALL_IT_FIRST(cont_oplist, itSrc, src); \ + M_CALL_IT_FIRST(cont_oplist, itDst, dst); \ + M_CALL_IT_END(cont_oplist, itIns, dst); \ + while (!M_CALL_IT_END_P(cont_oplist, itSrc) \ + && !M_CALL_IT_END_P(cont_oplist, itDst)) { \ + type_t const *objSrc = M_CALL_IT_CREF(cont_oplist, itSrc); \ + type_t const *objDst = M_CALL_IT_CREF(cont_oplist, itDst); \ + /* Compare the current element of src and dst */ \ + int cmp = cmp_func(name, sort_name, objDst, objSrc); \ + if (cmp <= 0) { \ + /* The element of dst is before. Go to next element of dst */ \ + M_CALL_IT_SET(cont_oplist, itIns, itDst); \ + M_CALL_IT_NEXT(cont_oplist, itDst); \ + if (cmp == 0) { \ + /* Skip same arguments in both lists */ \ + M_CALL_IT_NEXT(cont_oplist, itSrc); \ + } \ + } else { \ + /* The element of src is before. */ \ + /* insert objSrc before */ \ + /* NOTE: IT_INSERT insert after ==> Need of another iterator */ \ + M_CALL_IT_INSERT(cont_oplist, dst, itIns, *objSrc); \ + M_CALL_IT_NEXT(cont_oplist, itSrc); \ + } \ + } \ + while (!M_CALL_IT_END_P(cont_oplist, itSrc)) { \ + /* Finish inserting the element of src in dst */ \ + type_t *objSrc = M_CALL_IT_REF(cont_oplist, itSrc); \ + M_CALL_IT_INSERT(cont_oplist, dst, itIns, *objSrc); \ + M_CALL_IT_NEXT(cont_oplist, itSrc); \ + } \ + } \ + , /* NO IT_INSERT */ ) \ + \ + /* Compute the intersection of two ***sorted*** containers */ \ + M_IF_METHOD(IT_REMOVE, cont_oplist)( \ + M_INLINE void \ + M_C3(name,sort_name,_intersect)(container_t dst, const container_t src cmp_param(name)) \ + { \ + it_t itSrc; \ + it_t itDst; \ + M_ASSERT(M_C3(name,sort_name,_p)(dst cmp_arg)); \ + M_ASSERT(M_C3(name,sort_name,_p)(src cmp_arg)); \ + M_CALL_IT_FIRST(cont_oplist, itSrc, src); \ + M_CALL_IT_FIRST(cont_oplist, itDst, dst); \ + /* TODO: Not optimized at all for array ! O(n^2) */ \ + while (!M_CALL_IT_END_P(cont_oplist, itSrc) \ + && !M_CALL_IT_END_P(cont_oplist, itDst)) { \ + type_t const *objSrc = M_CALL_IT_CREF(cont_oplist, itSrc); \ + type_t const *objDst = M_CALL_IT_CREF(cont_oplist, itDst); \ + int cmp = cmp_func(name, sort_name, objDst, objSrc); \ + if (cmp == 0) { \ + /* Keep it */ \ + M_CALL_IT_NEXT(cont_oplist, itSrc); \ + M_CALL_IT_NEXT(cont_oplist, itDst); \ + } else if (cmp < 0) { \ + M_CALL_IT_REMOVE(cont_oplist, dst, itDst); \ + } else { \ + M_CALL_IT_NEXT(cont_oplist, itSrc); \ + } \ + } \ + while (!M_CALL_IT_END_P(cont_oplist, itDst)) { \ + M_CALL_IT_REMOVE(cont_oplist, dst, itDst); \ + } \ + } \ + , /* NO IT_REMOVE */ ) + + +/* Define the find like algorithms of a given data + TODO: Define _find_sorted that find in a sorted random access container + (binary search) + */ +#define M_ALG0_DEF_FIND(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + /* It supposes that the container is not sorted */ \ + /* Find the next occurrence from it (included) of data */ \ + M_INLINE void \ + M_F(name, _find_again) (it_t it, type_t const data) \ + { \ + for ( /*nothing*/ ; !M_CALL_IT_END_P(cont_oplist, it) ; \ + M_CALL_IT_NEXT(cont_oplist, it)) { \ + if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \ + return ; \ + } \ + } \ + \ + /* Find the first occurrence of data */ \ + M_INLINE void \ + M_F(name, _find) (it_t it, container_t const l, type_t const data) \ + { \ + M_CALL_IT_FIRST(cont_oplist, it, l); \ + M_F(name, _find_again)(it, data); \ + } \ + \ + /* Test if data is within the container */ \ + M_INLINE bool \ + M_F(name, _contain_p) (container_t const l, type_t const data) \ + { \ + it_t it; \ + M_F(name,_find)(it, l, data); \ + return !M_CALL_IT_END_P(cont_oplist, it); \ + } \ + \ + /* Find the last occurrence of data in the container */ \ + /* For the definition of _find_last, if the methods \ + PREVIOUS & IT_LAST are defined, then search backwards */ \ + M_IF_METHOD2(PREVIOUS, IT_LAST, cont_oplist) \ + ( \ + M_INLINE void \ + M_F(name, _find_last) (it_t it, container_t const l, type_t const data) \ + { \ + for (M_CALL_IT_LAST(cont_oplist, it, l); \ + !M_CALL_IT_END_P(cont_oplist, it) ; \ + M_CALL_IT_PREVIOUS(cont_oplist, it)) { \ + if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \ + /* We can stop as soon as a match is found */ \ + return; \ + } \ + } \ + , \ + /* Otherwise search forward, but don't stop on the first occurrence */ \ + M_INLINE void \ + M_F(name, _find_last) (it_t it, container_t const l, type_t const data) \ + { \ + M_CALL_IT_END(cont_oplist, it, l); \ + it_t it2; \ + for (M_CALL_IT_FIRST(cont_oplist, it2, l); \ + !M_CALL_IT_END_P(cont_oplist, it2) ; \ + M_CALL_IT_NEXT(cont_oplist, it2)) { \ + if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it2), data)) \ + /* We cannot stop as soon as a match is found */ \ + M_CALL_IT_SET(cont_oplist, it, it2) ; \ + } \ + } \ + ) /* End of alternative of _find_last */ \ + \ + /* Count the number of occurrence of data in the container */ \ + M_INLINE size_t \ + M_F(name, _count) (container_t const l, type_t const data) \ + { \ + it_t it; \ + size_t count = 0; \ + for (M_CALL_IT_FIRST(cont_oplist, it, l); \ + !M_CALL_IT_END_P(cont_oplist, it) ; \ + M_CALL_IT_NEXT(cont_oplist, it)) { \ + if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \ + count++ ; \ + } \ + return count; \ + } \ + \ + \ + /* Find the next mismatch between the containers */ \ + M_INLINE void \ + M_F(name, _mismatch_again) (it_t it1, it_t it2) \ + { \ + for (/* nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \ + !M_CALL_IT_END_P(cont_oplist, it2); \ + M_CALL_IT_NEXT(cont_oplist, it1), \ + M_CALL_IT_NEXT(cont_oplist, it2)) { \ + if (!M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it1), \ + *M_CALL_IT_CREF(cont_oplist, it2))) \ + break; \ + } \ + } \ + \ + /* Find the first mismatch between the containers */ \ + M_INLINE void \ + M_F(name, _mismatch) (it_t it1, it_t it2, container_t const l1, container_t const l2 ) \ + { \ + M_CALL_IT_FIRST(cont_oplist, it1, l1); \ + M_CALL_IT_FIRST(cont_oplist, it2, l2); \ + M_F(name, _mismatch_again)(it1, it2); \ + } \ + + + +/* Define the find like algorithms with a given callback of function object + TODO: Define _find_sorted that find in a sorted random access container + (binary search) + */ +#define M_ALG0_DEF_FIND_IF(name, container_t, cont_oplist, type_t, type_oplist, it_t, suffix, test_t, eq_t, call_test, call_eq) \ + \ + /* Find the next occurrence that matches the condition */ \ + M_INLINE void \ + M_C3(name, _find_again_, suffix) (it_t it, test_t func) \ + { \ + for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it) ; \ + M_CALL_IT_NEXT(cont_oplist, it)) { \ + if (call_test(func, *M_CALL_IT_CREF(cont_oplist, it))) \ + return ; \ + } \ + } \ + \ + /* Find the first occurrence that matches the condition */ \ + M_INLINE void \ + M_C3(name, _find_, suffix) (it_t it, container_t l, test_t func) \ + { \ + M_CALL_IT_FIRST(cont_oplist, it, l); \ + M_C3(name, _find_again_, suffix)(it, func); \ + } \ + \ + /* Count the number of occurrence that matches the condition */ \ + M_INLINE size_t \ + M_C3(name, _count_, suffix) (container_t const l, test_t func) \ + { \ + it_t it; \ + size_t count = 0; \ + for (M_CALL_IT_FIRST(cont_oplist, it, l); \ + !M_CALL_IT_END_P(cont_oplist, it) ; \ + M_CALL_IT_NEXT(cont_oplist, it)) { \ + if (call_test(func, *M_CALL_IT_CREF(cont_oplist, it))) { \ + count++ ; \ + } \ + } \ + return count; \ + } \ + \ + /* Find the next mismatch between the containers according to the condition */ \ + M_INLINE void \ + M_C3(name, _mismatch_again_, suffix) (it_t it1, it_t it2, eq_t func) \ + { \ + for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \ + !M_CALL_IT_END_P(cont_oplist, it2); \ + M_CALL_IT_NEXT(cont_oplist, it1), \ + M_CALL_IT_NEXT(cont_oplist, it2)) { \ + if (!call_eq(func, *M_CALL_IT_CREF(cont_oplist, it1), \ + *M_CALL_IT_CREF(cont_oplist, it2))) \ + break; \ + } \ + } \ + \ + /* Find the first mismatch between the containers according to the condition */ \ + M_INLINE void \ + M_C3(name, _mismatch_, suffix) (it_t it1, it_t it2, container_t const l1, \ + container_t l2, eq_t func) \ + { \ + M_CALL_IT_FIRST(cont_oplist, it1, l1); \ + M_CALL_IT_FIRST(cont_oplist, it2, l2); \ + M_C3(name, _mismatch_again_, suffix)(it1, it2, func); \ + } \ + + +/* Define the FILL algorithms */ +#define M_ALG0_DEF_FILL(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + /* Fill all the container with value (overwritten) */ \ + M_INLINE void \ + M_F(name, _fill) (container_t l, type_t const value) \ + { \ + for M_EACH(item, l, cont_oplist) { \ + M_CALL_SET(type_oplist, *item, value); \ + } \ + } \ + \ + /* Fill the container with exactly 'n' occurrence of 'value' */ \ + M_IF_METHOD(PUSH, cont_oplist)( \ + M_INLINE void \ + M_F(name, _fill_n) (container_t l, size_t n, type_t const value) \ + { \ + M_CALL_RESET(cont_oplist, l); \ + for(size_t i = 0; i < n; i++) { \ + M_CALL_PUSH(cont_oplist, l, value); \ + } \ + } \ + , /* PUSH method */ ) \ + \ + /* Fill the container with FOR('value'; 'value'+'inc') */ \ + M_IF_METHOD(ADD, type_oplist)( \ + M_INLINE void \ + M_F(name, _fill_a) (container_t l, type_t const value, type_t const inc) \ + { \ + type_t tmp; \ + M_CALL_INIT_SET(type_oplist, tmp, value); \ + for M_EACH(item, l, cont_oplist) { \ + M_CALL_SET(type_oplist, *item, tmp); \ + M_CALL_ADD(type_oplist, tmp, tmp, inc); \ + } \ + M_CALL_CLEAR(type_oplist, tmp); \ + } \ + \ + /* Fill the container with n occurences of FOR('value'; 'value'+'inc') */ \ + M_IF_METHOD(PUSH, cont_oplist)( \ + M_INLINE void \ + M_F(name, _fill_an) (container_t l, size_t n, type_t const value, type_t const inc) \ + { \ + type_t tmp; \ + M_CALL_INIT_SET(type_oplist, tmp, value); \ + M_CALL_RESET(cont_oplist, l); \ + for(size_t i = 0; i < n; i++) { \ + M_CALL_PUSH(cont_oplist, l, tmp); \ + M_CALL_ADD(type_oplist, tmp, tmp, inc); \ + } \ + M_CALL_CLEAR(type_oplist, tmp); \ + } \ + , /* PUSH method */ ) \ + , /* ADD method */ ) \ + + +/* Define MAP algorithms */ +#define M_ALG0_DEF_MAP(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + /* Apply func for all elements of the container */ \ + M_INLINE void \ + M_F(name, _for_each) (container_t l, M_F(name, _apply_cb_ct) func) \ + { \ + for M_EACH(item, l, cont_oplist) { \ + func(*item); \ + } \ + } \ + \ + M_IF_METHOD(INIT, type_oplist)( \ + M_IF_METHOD(PUSH_MOVE, cont_oplist)( \ + \ + /* Apply func for all elements of the container src and push the result in dst */ \ + M_INLINE void \ + M_F(name, _transform) (container_t dst, \ + container_t src, \ + M_F(name, _transform_cb_ct) func) \ + { \ + M_ASSERT(dst != src); \ + M_CALL_RESET(cont_oplist, dst); \ + for M_EACH(item, src, cont_oplist) { \ + type_t tmp; \ + M_CALL_INIT(type_oplist, tmp); \ + func(&tmp, *item); \ + M_CALL_PUSH_MOVE(cont_oplist, dst, &tmp); \ + } \ + M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \ + } \ + , /* END PUSH_MOVE */), /* END INIT */ ) \ + \ + /* Reduce all elements of the container in dst in function of func */ \ + M_IF_METHOD(SET, type_oplist)( \ + M_INLINE void \ + M_F(name, _reduce) (type_t *dest, container_t const l, \ + M_F(name, _transform_cb_ct) func) \ + { \ + bool initDone = false; \ + for M_EACH(item, l, cont_oplist) { \ + if (initDone) { \ + func(dest, *item); \ + } else { \ + M_CALL_SET(type_oplist, *dest, *item); \ + initDone = true; \ + } \ + } \ + } \ + , /* END SET */) \ + \ + /* Reduce all transformed elements of the container in dst in function of func */ \ + M_IF_METHOD(INIT, type_oplist)( \ + M_INLINE \ + void M_F(name, _map_reduce) (type_t *dest, \ + const container_t l, \ + M_F(name, _transform_cb_ct) redFunc, \ + M_F(name, _transform_cb_ct) mapFunc) \ + { \ + bool initDone = false; \ + type_t tmp; \ + M_CALL_INIT(type_oplist, tmp); \ + for M_EACH(item, l, cont_oplist) { \ + if (initDone) { \ + mapFunc(&tmp, *item); \ + redFunc(dest, tmp); \ + } else { \ + mapFunc(dest, *item); \ + initDone = true; \ + } \ + } \ + M_CALL_CLEAR(type_oplist, tmp); \ + } \ + , ) \ + + +/* Define ALL_OF algorithms */ +#define M_ALG0_DEF_ALL_OF(name, container_t, cont_oplist, type_t, type_oplist, it_t, suffix, func_t, call) \ + \ + M_INLINE bool \ + M_C4(name, _any_of, suffix, p) (container_t const l, \ + func_t func ) \ + { \ + for M_EACH(item, l, cont_oplist) { \ + if (call(func, *item)) \ + return true; \ + } \ + return false; \ + } \ + \ + M_INLINE bool \ + M_C4(name, _all_of, suffix, p) (container_t const l, \ + func_t func ) \ + { \ + for M_EACH(item, l, cont_oplist) { \ + if (!call(func, *item)) \ + return false; \ + } \ + return true; \ + } \ + \ + M_INLINE bool \ + M_C4(name, _none_of, suffix, p) (container_t l, \ + func_t func ) \ + { \ + for M_EACH(item, l, cont_oplist) { \ + if (call(func, *item)) \ + return false; \ + } \ + return true; \ + } \ + + +/* Define MIN / MAX algorithms */ +#define M_ALG0_DEF_MINMAX(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + M_INLINE type_t * \ + M_F(name, _min) (const container_t l) \ + { \ + type_t *min = NULL; \ + for M_EACH(cref, l, cont_oplist) { \ + if (min == NULL || \ + M_CALL_CMP(type_oplist, *min, *cref) > 0) { \ + min = cref; \ + } \ + } \ + return min; \ + } \ + \ + M_INLINE type_t * \ + M_F(name, _max) (const container_t l) \ + { \ + type_t *max = NULL; \ + for M_EACH(cref, l, cont_oplist) { \ + if (max == NULL || \ + M_CALL_CMP(type_oplist, *max, *cref) < 0) { \ + max = cref; \ + } \ + } \ + return max; \ + } \ + \ + M_INLINE void \ + M_F(name, _minmax) (type_t **min_p, type_t **max_p, \ + const container_t l) \ + { \ + type_t *min = NULL; \ + type_t *max = NULL; \ + for M_EACH(cref, l, cont_oplist) { \ + if (min == NULL || \ + M_CALL_CMP(type_oplist, *min, *cref) > 0) { \ + min = cref; \ + } \ + if (max == NULL || \ + M_CALL_CMP(type_oplist, *max, *cref) < 0) { \ + max = cref; \ + } \ + } \ + *min_p = min; \ + *max_p = max; \ + } \ + +/* Define functions based on remove method */ +#define M_ALG0_DEF_REMOVE(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + M_INLINE void \ + M_F(name, _uniq)(container_t l) \ + { \ + it_t it1; \ + it_t it2; \ + M_ASSERT(M_F(name, _sort_p)(l)); \ + M_CALL_IT_FIRST(cont_oplist, it1, l); \ + M_CALL_IT_SET(cont_oplist, it2, it1); \ + M_CALL_IT_NEXT(cont_oplist, it2); \ + /* Not efficient for array! */ \ + while (!M_CALL_IT_END_P(cont_oplist, it2)) { \ + type_t const *ref1 = M_CALL_IT_CREF(cont_oplist, it1); \ + type_t const *ref2 = M_CALL_IT_CREF(cont_oplist, it2); \ + if (M_CALL_CMP(type_oplist, *ref1, *ref2) == 0) { \ + M_CALL_IT_REMOVE(cont_oplist, l, it2); \ + } else { \ + M_CALL_IT_SET(cont_oplist, it1, it2); \ + M_CALL_IT_NEXT(cont_oplist, it2); \ + } \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _remove_val)(container_t l, type_t const val) \ + { \ + it_t it1; \ + M_CALL_IT_FIRST(cont_oplist, it1, l); \ + while (!M_CALL_IT_END_P(cont_oplist, it1)) { \ + type_t const *ref1 = M_CALL_IT_CREF(cont_oplist, it1); \ + if (M_CALL_EQUAL(type_oplist, *ref1, val)) { \ + M_CALL_IT_REMOVE(cont_oplist, l, it1); \ + } else { \ + M_CALL_IT_NEXT(cont_oplist, it1); \ + } \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _remove_if)(container_t l, M_F(name, _test_cb_ct) func) \ + { \ + it_t it1; \ + M_CALL_IT_FIRST(cont_oplist, it1, l); \ + while (!M_CALL_IT_END_P(cont_oplist, it1)) { \ + type_t const *ref1 = M_CALL_IT_CREF(cont_oplist, it1); \ + if (func(*ref1)) { \ + M_CALL_IT_REMOVE(cont_oplist, l, it1); \ + } else { \ + M_CALL_IT_NEXT(cont_oplist, it1); \ + } \ + } \ + } \ + +/* Define vector algorithms */ +#define M_ALG0_DEF_VECTOR(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + M_IF_METHOD(ADD, type_oplist)( \ + M_INLINE void M_F(name, _add) (container_t dst, const container_t src) \ + { \ + it_t itSrc; \ + it_t itDst; \ + for (M_CALL_IT_FIRST(cont_oplist, itSrc, src) , \ + M_CALL_IT_FIRST(cont_oplist, itDst, dst) ; \ + !M_CALL_IT_END_P(cont_oplist, itSrc) \ + && !M_CALL_IT_END_P(cont_oplist, itDst) ; \ + M_CALL_IT_NEXT(cont_oplist, itSrc), \ + M_CALL_IT_NEXT(cont_oplist, itDst) ) { \ + type_t *dstItem = M_CALL_IT_REF(cont_oplist, itDst); \ + type_t const *srcItem = M_CALL_IT_CREF(cont_oplist, itSrc); \ + M_CALL_ADD(type_oplist, *dstItem, *dstItem, *srcItem); \ + } \ + } \ + , /* NO_ADD METHOD */ ) \ + \ + M_IF_METHOD(SUB, type_oplist)( \ + M_INLINE void M_F(name, _sub) (container_t dst, const container_t src) \ + { \ + it_t itSrc; \ + it_t itDst; \ + for (M_CALL_IT_FIRST(cont_oplist, itSrc, src) , \ + M_CALL_IT_FIRST(cont_oplist, itDst, dst) ; \ + !M_CALL_IT_END_P(cont_oplist, itSrc) \ + && !M_CALL_IT_END_P(cont_oplist, itDst) ; \ + M_CALL_IT_NEXT(cont_oplist, itSrc), \ + M_CALL_IT_NEXT(cont_oplist, itDst) ) { \ + type_t *dstItem = M_CALL_IT_REF(cont_oplist, itDst); \ + type_t const *srcItem = M_CALL_IT_CREF(cont_oplist, itSrc); \ + M_CALL_SUB(type_oplist, *dstItem, *dstItem, *srcItem); \ + } \ + } \ + , /* NO_SUB METHOD */ ) \ + \ + M_IF_METHOD(MUL, type_oplist)( \ + M_INLINE void M_F(name, _mul) (container_t dst, const container_t src) \ + { \ + it_t itSrc; \ + it_t itDst; \ + for (M_CALL_IT_FIRST(cont_oplist, itSrc, src) , \ + M_CALL_IT_FIRST(cont_oplist, itDst, dst) ; \ + !M_CALL_IT_END_P(cont_oplist, itSrc) \ + && !M_CALL_IT_END_P(cont_oplist, itDst) ; \ + M_CALL_IT_NEXT(cont_oplist, itSrc), \ + M_CALL_IT_NEXT(cont_oplist, itDst) ) { \ + type_t *dstItem = M_CALL_IT_REF(cont_oplist, itDst); \ + type_t const *srcItem = M_CALL_IT_CREF(cont_oplist, itSrc); \ + M_CALL_MUL(type_oplist, *dstItem, *dstItem, *srcItem); \ + } \ + } \ + , /* NO_MUL METHOD */ ) \ + \ + M_IF_METHOD(DIV, type_oplist)( \ + M_INLINE void M_F(name, _div) (container_t dst, const container_t src) \ + { \ + it_t itSrc; \ + it_t itDst; \ + for (M_CALL_IT_FIRST(cont_oplist, itSrc, src) , \ + M_CALL_IT_FIRST(cont_oplist, itDst, dst) ; \ + !M_CALL_IT_END_P(cont_oplist, itSrc) \ + && !M_CALL_IT_END_P(cont_oplist, itDst) ; \ + M_CALL_IT_NEXT(cont_oplist, itSrc), \ + M_CALL_IT_NEXT(cont_oplist, itDst) ) { \ + type_t *dstItem = M_CALL_IT_REF(cont_oplist, itDst); \ + type_t const *srcItem = M_CALL_IT_CREF(cont_oplist, itSrc); \ + M_CALL_DIV(type_oplist, *dstItem, *dstItem, *srcItem); \ + } \ + } \ + , /* NO_DIV METHOD */ ) \ + \ + + +//TODO: Algorithm missing +// _nth_element ( http://softwareengineering.stackexchange.com/questions/284767/kth-selection-routine-floyd-algorithm-489 ) +// _average +// _sort_uniq (_sort + _uniq) +// fast _uniq for array +// _flatten (takes a set of containers and returns a new container containing all flatten objects) + +/******************************** INTERNAL ***********************************/ + +#define M_ALG0_FOR_EACH(container, cont_oplist, func) do { \ + for M_EACH(_item, container, cont_oplist) { \ + func(*_item); \ + } \ + } while (0) + +#define M_ALG0_FOR_EACH_ARG(container, cont_oplist, func, ...) do { \ + for M_EACH(_item, container, cont_oplist) { \ + func(__VA_ARGS__, *_item); \ + } \ + } while (0) + + +/******************************** INTERNAL ***********************************/ + +#define M_ALG0_TRANSFORM(contD, contDop, contS, contSop, func) do { \ + M_CALL_RESET(contDop, contD); \ + for M_EACH(_item, contS, contSop) { \ + M_GET_SUBTYPE contDop _tmp; \ + M_CALL_INIT(M_GET_OPLIST contDop, _tmp); \ + func(_tmp, *_item); \ + M_CALL_PUSH_MOVE(contDop, contD, &_tmp); \ + } \ + M_IF_METHOD(REVERSE, contDop) (M_CALL_REVERSE(contDop, contD);, ) \ + } while (0) + +#define M_ALG0_TRANSFORM_ARG(contD, contDop, contS, contSop, func, ...) do { \ + M_CALL_RESET(contDop, contD); \ + for M_EACH(_item, contS, contSop) { \ + M_GET_SUBTYPE contDop _tmp; \ + M_CALL_INIT(M_GET_OPLIST contDop, _tmp); \ + func(_tmp, *_item, __VA_ARGS__); \ + M_CALL_PUSH_MOVE(contDop, contD, &_tmp); \ + } \ + M_IF_METHOD(REVERSE, contDop) (M_CALL_REVERSE(contDop, contD);, ) \ + } while (0) + + +/******************************** INTERNAL ***********************************/ + +#define M_ALG0_EXTRACT(contDst, contDstOplist, contSrc, contSrcOplist) \ + M_ALG0_EXTRACT_P1(contDst, M_GLOBAL_OPLIST(contDstOplist), contSrc, M_GLOBAL_OPLIST(contSrcOplist)) +#define M_ALG0_EXTRACT_P1(contDst, contDstOplist, contSrc, contSrcOplist) do{ \ + M_CALL_RESET(contDstOplist, contDst); \ + for M_EACH(_item, contSrc, contSrcOplist) { \ + M_IF_METHOD(PUSH, contDstOplist)( \ + M_CALL_PUSH(contDstOplist, contDst, *_item); \ + , \ + M_CALL_SET_KEY(contDstOplist, contDst, (*_item).key, (*_item).value); \ + ) \ + } \ + M_IF_METHOD(REVERSE, contDstOplist) (M_CALL_REVERSE(contDstOplist, contDst);, ) \ + } while (0) + +#define M_ALG0_EXTRACT_FUNC(contDst, contDstOplist, contSrc, contSrcOplist, condFunc) \ + M_ALG0_EXTRACT_FUNC_P1(contDst, M_GLOBAL_OPLIST(contDstOplist), contSrc, M_GLOBAL_OPLIST(contSrcOplist), condFunc) +#define M_ALG0_EXTRACT_FUNC_P1(contDst, contDstOplist, contSrc, contSrcOplist, \ + condFunc) do { \ + M_CALL_RESET(contDstOplist, contDst); \ + for M_EACH(_item, contSrc, contSrcOplist) { \ + if (condFunc (*_item)) { \ + M_IF_METHOD(PUSH, contDstOplist)( \ + M_CALL_PUSH(contDstOplist, contDst, *_item); \ + , \ + M_CALL_SET_KEY(contDstOplist, contDst, (*_item).key, (*_item).value); \ + ) \ + } \ + } \ + M_IF_METHOD(REVERSE, contDstOplist) (M_CALL_REVERSE(contDstOplist, contDst);, ) \ + } while (0) + +#define M_ALG0_EXTRACT_ARG(contDst, contDstOplist, contSrc, contSrcOplist, \ + condFunc, ...) \ + M_ALG0_EXTRACT_ARG_P1(contDst, M_GLOBAL_OPLIST(contDstOplist), contSrc, M_GLOBAL_OPLIST(contSrcOplist), \ + condFunc, __VA_ARGS__) +#define M_ALG0_EXTRACT_ARG_P1(contDst, contDstOplist, contSrc, contSrcOplist, \ + condFunc, ...) do { \ + M_CALL_RESET(contDstOplist, contDst); \ + for M_EACH(_item, contSrc, contSrcOplist) { \ + if (condFunc (__VA_ARGS__, *_item)) { \ + M_IF_METHOD(PUSH, contDstOplist)( \ + M_CALL_PUSH(contDstOplist, contDst, *_item); \ + , \ + M_CALL_SET_KEY(contDstOplist, contDst, (*_item).key, (*_item).value); \ + ) \ + } \ + } \ + M_IF_METHOD(REVERSE, contDstOplist) (M_CALL_REVERSE(contDstOplist, contDstOplist);, ) \ + } while (0) + + +/******************************** INTERNAL ***********************************/ + +#define M_ALG0_REDUCE_DISPATCH(dest, destOp, dest_t, cont, contOp, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (M_ALG0_REDUCE_BASIC(dest, dest_t, destOp, cont, contOp, __VA_ARGS__), \ + M_IF_NARGS_EQ2(__VA_ARGS__) \ + (M_ALG0_REDUCE_FOR_EACH(dest, dest_t, destOp, cont, contOp, __VA_ARGS__), \ + M_ALG0_REDUCE_FOR_EACH_ARG(dest, dest_t, destOp, cont, contOp, __VA_ARGS__) ) ) + +/* The special functions handled by ALGO_REDUCE */ +#define M_ALG0_REDUCE_AND(a,b) ((a) &= (b)) +#define M_ALG0_REDUCE_OR(a,b) ((a) |= (b)) +#define M_ALG0_REDUCE_SUM(a,b) ((a) += (b)) +#define M_ALG0_REDUCE_PRODUCT(a,b) ((a) *= (b)) + +/* Return the method associated to a reduce operation. + It returns the special function handler if function is and, or, sum or add. + Otherwise it returns the original function */ +#define M_ALG0_REDUCE_FUNC(reduceFunc) \ + M_IF(M_KEYWORD_P(and, reduceFunc)) \ + (M_ALG0_REDUCE_AND, \ + M_IF(M_KEYWORD_P(or, reduceFunc)) \ + (M_ALG0_REDUCE_OR, \ + M_IF(M_KEYWORD_P(sum, reduceFunc)) \ + (M_ALG0_REDUCE_SUM, \ + M_IF(M_KEYWORD_P(product, reduceFunc)) \ + (M_ALG0_REDUCE_PRODUCT, \ + reduceFunc) \ + ) \ + ) \ + ) + +#define M_ALG0_REDUCE_BASIC(dest, dest_t, destOp, cont, cont_oplist, reduceFunc) do { \ + bool _init_done = false; \ + for M_EACH(_item, cont, cont_oplist) { \ + if (_init_done) { \ + M_ALG0_REDUCE_FUNC(reduceFunc) (dest, *_item); \ + } else { \ + M_CALL_SET(destOp, dest, *_item); \ + _init_done = true; \ + } \ + } \ + } while (0) + +#define M_ALG0_REDUCE_FOR_EACH(dest, dest_t, destOp, cont, cont_oplist, reduceFunc, mapFunc) do { \ + bool _init_done = false; \ + dest_t _tmp; \ + M_CALL_INIT(destOp, _tmp); \ + for M_EACH(_item, cont, cont_oplist) { \ + mapFunc(_tmp, *_item); \ + if (_init_done) { \ + M_ALG0_REDUCE_FUNC(reduceFunc) (dest, _tmp); \ + } else { \ + M_CALL_SET(destOp, dest, _tmp); \ + _init_done = true; \ + } \ + } \ + M_CALL_CLEAR(destOp, _tmp); \ + } while (0) + +#define M_ALG0_REDUCE_FOR_EACH_ARG(dest, dest_t, destOp, cont, cont_oplist, reduceFunc, mapFunc, ...) do { \ + bool _init_done = false; \ + dest_t _tmp; \ + M_CALL_INIT(destOp, _tmp); \ + for M_EACH(_item, cont, cont_oplist) { \ + mapFunc(_tmp, __VA_ARGS__, *_item); \ + if (_init_done) { \ + M_ALG0_REDUCE_FUNC(reduceFunc) (dest, _tmp); \ + } else { \ + M_CALL_SET(destOp, dest, _tmp); \ + _init_done = true; \ + } \ + } \ + M_CALL_CLEAR(destOp, _tmp); \ + } while (0) + + +/******************************** INTERNAL ***********************************/ + +#define M_ALG0_INSERT_AT(contDst, contDstOp, position, contSrc, contSrcOp) do { \ + M_GET_IT_TYPE contSrcOp _itSrc; \ + M_GET_IT_TYPE contDstOp _itDst; \ + M_CALL_IT_SET(contDstOp, _itDst, position); \ + for (M_CALL_IT_FIRST(contSrcOp, _itSrc, contSrc) ; \ + !M_CALL_IT_END_P(contSrcOp, _itSrc) ; \ + M_CALL_IT_NEXT(contSrcOp, _itSrc) ) { \ + M_CALL_IT_INSERT(contDstOp, contDst, _itDst, \ + *M_CALL_IT_CREF(contSrcOp, _itSrc)); \ + } \ + } while (0) + + +/******************************** INTERNAL ***********************************/ + +#if M_USE_SMALL_NAME +#define ALGO_DEF M_ALGO_DEF +#define ALGO_FOR_EACH M_ALGO_FOR_EACH +#define ALGO_TRANSFORM M_ALGO_TRANSFORM +#define ALGO_EXTRACT M_ALGO_EXTRACT +#define ALGO_REDUCE M_ALGO_REDUCE +#define ALGO_INSERT_AT M_ALGO_INSERT_AT +#endif + +#endif diff --git a/components/mlib/m-array.h b/components/mlib/m-array.h new file mode 100644 index 00000000..77bbc7f7 --- /dev/null +++ b/components/mlib/m-array.h @@ -0,0 +1,1132 @@ +/* + * M*LIB - dynamic ARRAY module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_ARRAY_H +#define MSTARLIB_ARRAY_H + +#include "m-core.h" + +/* Define a dynamic array of the given type and its associated functions. + USAGE: ARRAY_DEF(name, type [, oplist_of_the_type]) */ +#define M_ARRAY_DEF(name, ...) \ + M_ARRAY_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) + + +/* Define a dynamic array of the given type and its associated functions + as the provided type name_t with the iterator named it_t + USAGE: ARRAY_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */ +#define M_ARRAY_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_ARRA4_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \ + (name, __VA_ARGS__, name_t, it_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a dynamic array given its name and its oplist. + If no oplist is given it is assumed to be M_BASIC_OPLIST + USAGE: ARRAY_OPLIST(name[, oplist of the type]) */ +#define M_ARRAY_OPLIST(...) \ + M_ARRA4_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST), \ + (__VA_ARGS__ ))) + + +/* Define an init value to init global variables of type array. + USAGE: + array_t global_variable = ARRAY_INIT_VALUE(); + */ +#define M_ARRAY_INIT_VALUE() \ + { { 0, 0, NULL } } + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* Deferred evaluation for the oplist definition, + so that all arguments are evaluated before further expansion */ +#define M_ARRA4_OPLIST_P1(arg) M_ARRA4_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_ARRA4_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_ARRA4_OPLIST_P3, M_ARRA4_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_ARRA4_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_ARRAY_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* OPLIST definition of a dynamic array */ +/* FIXME: Do we want to export some methods as they are slow and + are not fit to be used for building other methods (like _it_remove)? */ +#define M_ARRA4_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)) \ + ,M_IF_METHOD2(INIT_SET,SET, oplist)(INIT_SET(M_F(name, _init_set)),) \ + ,M_IF_METHOD(INIT_SET, oplist)(INIT_WITH(API_1(M_INIT_WITH_VAI)),) \ + ,M_IF_METHOD2(INIT_SET,SET, oplist)(SET(M_F(name, _set)), ) \ + ,CLEAR(M_F(name, _clear)) \ + ,INIT_MOVE(M_F(name, _init_move)) \ + ,MOVE(M_F(name, _move)) \ + ,SWAP(M_F(name, _swap)) \ + ,TYPE(M_F(name,_ct)) \ + ,NAME(name) \ + ,SUBTYPE(M_F(name, _subtype_ct)) \ + ,EMPTY_P(M_F(name,_empty_p)) \ + ,IT_TYPE(M_F(name,_it_ct)), \ + ,IT_FIRST(M_F(name,_it)) \ + ,IT_LAST(M_F(name,_it_last)) \ + ,IT_END(M_F(name,_it_end)) \ + ,IT_SET(M_F(name,_it_set)) \ + ,IT_END_P(M_F(name,_end_p)) \ + ,IT_LAST_P(M_F(name,_last_p)) \ + ,IT_EQUAL_P(M_F(name,_it_equal_p)) \ + ,IT_NEXT(M_F(name,_next)) \ + ,IT_PREVIOUS(M_F(name,_previous)) \ + ,IT_REF(M_F(name,_ref)) \ + ,IT_CREF(M_F(name,_cref)) \ + ,M_IF_METHOD(INIT_SET, oplist)(IT_INSERT(M_F(name,_insert)) ,) \ + ,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(IT_REMOVE(M_F(name,_remove)),) \ + ,RESET(M_F(name,_reset)) \ + ,KEY_TYPE(size_t) \ + ,VALUE_TYPE(M_F(name, _subtype_ct)) \ + ,KEY_OPLIST(M_BASIC_OPLIST) \ + ,VALUE_OPLIST(oplist) \ + ,M_IF_METHOD(SET, oplist)(SET_KEY(M_F(name, _set_at)) ,) \ + ,GET_KEY(M_F(name, _get)) \ + ,M_IF_METHOD(INIT, oplist)(SAFE_GET_KEY(M_F(name, _safe_get)) ,) \ + ,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(ERASE_KEY(M_F(name, _erase)),) \ + ,GET_SIZE(M_F(name, _size)) \ + ,M_IF_METHOD(INIT_SET, oplist)(PUSH(M_F(name,_push_back)) ,) \ + ,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(POP(M_F(name,_pop_back)) ,) \ + ,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(PUSH_MOVE(M_F(name,_push_move)) ,) \ + ,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(POP_MOVE(M_F(name,_pop_move)) ,) \ + ,OPLIST(oplist) \ + ,M_IF_METHOD(CMP, oplist)(SORT(M_F(name, _special_sort)),) \ + ,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \ + ,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \ + ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \ + ,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \ + ,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \ + ,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \ + ,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \ + ,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \ + ) + + +/********************************** INTERNAL *********************************/ + +/* Define the internal contract of an array */ +#define M_ARRA4_CONTRACT(a) do { \ + M_ASSERT (a != NULL); \ + M_ASSERT (a->size <= a->alloc); \ + M_ASSERT (a->size == 0 || a->ptr != NULL); \ + M_ASSERT (a->alloc == 0 || a->ptr != NULL); \ + } while (0) + + +/* Deferred evaluation for the array definition, + so that all arguments are fully evaluated before further expansion + (ensuring good performance) + and performed a final step evaluation of the returning expansion + (ensuring delayed evaluation are still expanded) +*/ +#define M_ARRA4_DEF_P1(arg) M_ID( M_ARRA4_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_ARRA4_DEF_P2(name, type, oplist, array_t, it_t) \ + M_IF_OPLIST(oplist)(M_ARRA4_DEF_P3, M_ARRA4_DEF_FAILURE)(name, type, oplist, array_t, it_t) + +/* Stop processing with a compilation failure */ +#define M_ARRA4_DEF_FAILURE(name, type, oplist, array_t, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ARRAY_DEF): the given argument is not a valid oplist: " #oplist) + +/* Internal definition: + - name: prefix to be used + - type: type of the elements of the array + - oplist: oplist of the type of the elements of the array + - array_t: alias for the type of the array + - it_t: alias for the iterator of the array +*/ +#define M_ARRA4_DEF_P3(name, type, oplist, array_t, it_t) \ + M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t) \ + M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \ + M_EMPLACE_QUEUE_DEF(name, array_t, M_F(name, _emplace_back), oplist, M_ARRA4_EMPLACE_DEF) + +/* Define the types */ +#define M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t) \ + \ + /* Define a dynamic array */ \ + typedef struct M_F(name, _s) { \ + size_t size; /* Number of elements in the array */ \ + size_t alloc; /* Allocated size for the array base */ \ + type *ptr; /* Pointer to the array base */ \ + } array_t[1]; \ + \ + /* Define an iterator over an array */ \ + typedef struct M_F(name, _it_s) { \ + size_t index; /* Index of the element */ \ + const struct M_F(name, _s) *array; /* Reference of the array */ \ + } it_t[1]; \ + \ + /* Definition of the synonyms of the type */ \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + typedef array_t M_F(name, _ct); \ + typedef it_t M_F(name, _it_ct); \ + typedef type M_F(name, _subtype_ct); \ + +/* Define the core functions */ +#define M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t) \ + \ + M_INLINE void \ + M_F(name, _init)(array_t v) \ + { \ + M_ASSERT (v != NULL); \ + /* Initially, the array is empty with nothing allocated */ \ + v->size = 0; \ + v->alloc = 0; \ + v->ptr = NULL; \ + M_ARRA4_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + for(size_t i = 0; i < v->size; i++) \ + M_CALL_CLEAR(oplist, v->ptr[i]); \ + v->size = 0; \ + M_ARRA4_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_F(name, _reset)(v); \ + M_CALL_FREE(oplist, v->ptr); \ + /* This is so reusing the object implies an assertion failure */ \ + v->alloc = 1; \ + v->ptr = NULL; \ + } \ + \ + M_IF_METHOD2(INIT_SET, SET, oplist)( \ + M_INLINE void \ + M_F(name, _set)(array_t d, const array_t s) \ + { \ + M_ARRA4_CONTRACT(d); \ + M_ARRA4_CONTRACT(s); \ + if (M_UNLIKELY (d == s)) return; \ + if (s->size > d->alloc) { \ + const size_t alloc = s->size; \ + type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, alloc); \ + if (M_UNLIKELY_NOMEM (ptr == NULL)) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return ; \ + } \ + d->ptr = ptr; \ + d->alloc = alloc; \ + } \ + size_t i; \ + size_t step1 = M_MIN(s->size, d->size); \ + for(i = 0; i < step1; i++) \ + M_CALL_SET(oplist, d->ptr[i], s->ptr[i]); \ + for( ; i < s->size; i++) \ + M_CALL_INIT_SET(oplist, d->ptr[i], s->ptr[i]); \ + for( ; i < d->size; i++) \ + M_CALL_CLEAR(oplist, d->ptr[i]); \ + d->size = s->size; \ + M_ARRA4_CONTRACT(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(array_t d, const array_t s) \ + { \ + M_ASSERT (d != s); \ + M_F(name, _init)(d); \ + M_F(name, _set)(d, s); \ + } \ + , /* No SET & INIT_SET */) \ + \ + M_INLINE void \ + M_F(name, _init_move)(array_t d, array_t s) \ + { \ + M_ASSERT (d != s); \ + M_ARRA4_CONTRACT(s); \ + d->size = s->size; \ + d->alloc = s->alloc; \ + d->ptr = s->ptr; \ + /* Robustness */ \ + s->alloc = 1; \ + s->ptr = NULL; \ + M_ARRA4_CONTRACT(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(array_t d, array_t s) \ + { \ + M_ASSERT (d != s); \ + M_F(name, _clear)(d); \ + M_F(name, _init_move)(d, s); \ + } \ + \ + M_IF_METHOD(SET, oplist)( \ + M_INLINE void \ + M_F(name, _set_at)(array_t v, size_t i, type const x) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT(v->size > 0 && v->ptr != NULL); \ + M_ASSERT_INDEX(i, v->size); \ + M_CALL_SET(oplist, v->ptr[i], x); \ + } \ + , /* No SET */) \ + \ + M_INLINE type * \ + M_F(name, _back)(array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT(v->ptr != NULL); \ + M_ASSERT_INDEX(0, v->size); \ + return &v->ptr[v->size-1]; \ + } \ + \ + M_INLINE type * \ + M_F(name, _push_raw)(array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + if (M_UNLIKELY (v->size >= v->alloc)) { \ + M_ASSERT(v->size == v->alloc); \ + size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \ + if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return NULL; \ + } \ + M_ASSERT (alloc > v->size); \ + type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return NULL; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + M_ASSERT(v->ptr != NULL); \ + type *ret = &v->ptr[v->size]; \ + v->size++; \ + M_ARRA4_CONTRACT(v); \ + M_ASSUME(ret != NULL); \ + return ret; \ + } \ + \ + M_IF_METHOD(INIT_SET, oplist)( \ + M_INLINE void \ + M_F(name, _push_back)(array_t v, type const x) \ + { \ + type *data = M_F(name, _push_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return; \ + M_CALL_INIT_SET(oplist, *data, x); \ + } \ + , /* No INIT_SET */ ) \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE type * \ + M_F(name, _push_new)(array_t v) \ + { \ + type *data = M_F(name, _push_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return NULL; \ + M_CALL_INIT(oplist, *data); \ + return data; \ + } \ + , /* No INIT */ ) \ + \ + M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _push_move)(array_t v, type *x) \ + { \ + M_ASSERT (x != NULL); \ + type *data = M_F(name, _push_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return; \ + M_DO_INIT_MOVE (oplist, *data, *x); \ + } \ + , /* INIT_SET | INIT_MOVE */ ) \ + \ + M_IF_METHOD(INIT_SET, oplist)( \ + M_INLINE void \ + M_F(name, _push_at)(array_t v, size_t key, type const x) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT_INDEX(key, v->size+1); \ + if (M_UNLIKELY (v->size >= v->alloc) ) { \ + M_ASSERT(v->size == v->alloc); \ + size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \ + if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return ; \ + } \ + M_ASSERT (alloc > v->size); \ + type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + M_ASSERT(v->ptr != NULL); \ + memmove(&v->ptr[key+1], &v->ptr[key], (v->size-key)*sizeof(type)); \ + v->size++; \ + M_CALL_INIT_SET(oplist, v->ptr[key], x); \ + M_ARRA4_CONTRACT(v); \ + } \ + , /* No INIT_SET */ ) \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE void \ + M_F(name, _resize)(array_t v, size_t size) \ + { \ + M_ARRA4_CONTRACT(v); \ + if (v->size > size) { \ + /* Decrease size of array */ \ + for(size_t i = size ; i < v->size; i++) \ + M_CALL_CLEAR(oplist, v->ptr[i]); \ + v->size = size; \ + } else if (v->size < size) { \ + /* Increase size of array */ \ + if (size > v->alloc) { \ + size_t alloc = size ; \ + type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + for(size_t i = v->size ; i < size; i++) \ + M_CALL_INIT(oplist, v->ptr[i]); \ + v->size = size; \ + } \ + M_ARRA4_CONTRACT(v); \ + } \ + , /* No INIT */ ) \ + \ + M_INLINE void \ + M_F(name, _reserve)(array_t v, size_t alloc) \ + { \ + M_ARRA4_CONTRACT(v); \ + /* NOTE: Reserve below needed size to perform a shrink to fit */ \ + if (v->size > alloc) { \ + alloc = v->size; \ + } \ + if (M_UNLIKELY (alloc == 0)) { \ + M_CALL_FREE(oplist, v->ptr); \ + v->size = v->alloc = 0; \ + v->ptr = NULL; \ + } else { \ + type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + M_ARRA4_CONTRACT(v); \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE type * \ + M_F(name, _safe_get)(array_t v, size_t idx) \ + { \ + M_ARRA4_CONTRACT(v); \ + const size_t size = idx + 1; \ + /* resize if needed */ \ + if (v->size <= size) { \ + /* Increase size of array */ \ + if (M_UNLIKELY (size > v->alloc) ) { \ + size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \ + /* In case of overflow of size_t */ \ + if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return NULL; \ + } \ + type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return NULL; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + for(size_t i = v->size ; i < size; i++) \ + M_CALL_INIT(oplist, v->ptr[i]); \ + v->size = size; \ + } \ + M_ASSERT (idx < v->size); \ + M_ARRA4_CONTRACT(v); \ + return &v->ptr[idx]; \ + } \ + , /* No INIT */) \ + \ + M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _pop_back)(type *dest, array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (v->ptr != NULL); \ + M_ASSERT_INDEX(0, v->size); \ + v->size--; \ + if (dest) { \ + M_DO_MOVE (oplist, *dest, v->ptr[v->size]); \ + } else { \ + M_CALL_CLEAR(oplist, v->ptr[v->size]); \ + } \ + M_ARRA4_CONTRACT(v); \ + } \ + , /* SET | INIT_MOVE */ ) \ + \ + M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _pop_move)(type *dest, array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (v->ptr != NULL); \ + M_ASSERT_INDEX(0, v->size); \ + M_ASSERT (dest != NULL); \ + v->size--; \ + M_DO_INIT_MOVE (oplist, *dest, v->ptr[v->size]); \ + M_ARRA4_CONTRACT(v); \ + } \ + , /* INIT_SET | INIT_MOVE */ ) \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE void \ + M_F(name, _pop_until)(array_t v, it_t pos) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (v == pos->array); \ + M_ASSERT_INDEX(pos->index, v->size+1); \ + M_F(name, _resize)(v, pos->index); \ + } \ + , /* No INIT */ ) \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(const array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + return v->size == 0; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(const array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + return v->size; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity)(const array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + return v->alloc; \ + } \ + \ + M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _pop_at)(type *dest, array_t v, size_t i) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (v->size > 0 && v->ptr != NULL); \ + M_ASSERT_INDEX(i, v->size); \ + if (dest) \ + M_DO_MOVE (oplist, *dest, v->ptr[i]); \ + else \ + M_CALL_CLEAR(oplist, v->ptr[i]); \ + memmove(&v->ptr[i], &v->ptr[i+1], sizeof(type)*(v->size-1-i)); \ + v->size--; \ + M_ARRA4_CONTRACT(v); \ + } \ + \ + M_INLINE bool \ + M_F(name, _erase)(array_t a, size_t i) \ + { \ + M_ARRA4_CONTRACT(a); \ + if (i >= a->size) return false; \ + M_F(name, _pop_at)(NULL, a, i); \ + return true; \ + } \ + , /* SET | INIT_MOVE */ ) \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE void \ + M_F(name, _insert_v)(array_t v, size_t i, size_t num) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT_INDEX(i, v->size+1); \ + size_t size = v->size + num; \ + /* Test for overflow of variable size */ \ + if (M_UNLIKELY_NOMEM (size <= v->size)) { \ + /* Unlikely case of nothing to do */ \ + if (num == 0) return; \ + M_MEMORY_FULL(sizeof (type) * v->size); \ + return ; \ + } \ + /* Test if alloc array is sufficient */ \ + if (size > v->alloc) { \ + size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \ + if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return ; \ + } \ + type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + memmove(&v->ptr[i+num], &v->ptr[i], sizeof(type)*(v->size - i) ); \ + for(size_t k = i ; k < i+num; k++) \ + M_CALL_INIT(oplist, v->ptr[k]); \ + v->size = size; \ + M_ARRA4_CONTRACT(v); \ + } \ + , /* No INIT */) \ + \ + M_INLINE void \ + M_F(name, _remove_v)(array_t v, size_t i, size_t j) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT(i < j && v->ptr != NULL); \ + M_ASSERT_INDEX(i, v->size); \ + M_ASSERT_INDEX(j, v->size+1); \ + for(size_t k = i ; k < j; k++) \ + M_CALL_CLEAR(oplist, v->ptr[k]); \ + memmove(&v->ptr[i], &v->ptr[j], sizeof(type)*(v->size - j) ); \ + v->size -= (j-i); \ + M_ARRA4_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(array_t v1, array_t v2) \ + { \ + M_ARRA4_CONTRACT(v1); \ + M_ARRA4_CONTRACT(v2); \ + M_SWAP(size_t, v1->size, v2->size); \ + M_SWAP(size_t, v1->alloc, v2->alloc); \ + M_SWAP(type *, v1->ptr, v2->ptr); \ + M_ARRA4_CONTRACT(v1); \ + M_ARRA4_CONTRACT(v2); \ + } \ + \ + M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist) ( \ + M_INLINE void \ + M_F(name, _swap_at)(array_t v, size_t i, size_t j) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT(v->ptr != NULL); \ + M_ASSERT_INDEX(i, v->size); \ + M_ASSERT_INDEX(j, v->size); \ + type tmp; \ + M_DO_INIT_MOVE(oplist, tmp, v->ptr[i]); \ + M_DO_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \ + M_DO_INIT_MOVE(oplist, v->ptr[j], tmp); \ + M_ARRA4_CONTRACT(v); \ + } \ + , /* INIT_SET | INIT_MOVE */ ) \ + \ + M_INLINE type * \ + M_F(name, _get)(const array_t v, size_t i) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (v->ptr != NULL); \ + M_ASSERT_INDEX(i, v->size); \ + return &v->ptr[i]; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cget)(const array_t v, size_t i) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (v->ptr != NULL); \ + M_ASSERT_INDEX(i, v->size); \ + return M_CONST_CAST(type, &v->ptr[i]); \ + } \ + \ + M_INLINE type * \ + M_F(name, _front)(const array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT_INDEX(0, v->size); \ + return M_F(name, _get)(v, 0); \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(it_t it, const array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (it != NULL); \ + it->index = 0; \ + it->array = v; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_last)(it_t it, const array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (it != NULL); \ + /* If size is 0, index is -1 as unsigned, so it is greater than end */ \ + it->index = v->size - 1; \ + it->array = v; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(it_t it, const array_t v) \ + { \ + M_ARRA4_CONTRACT(v); \ + M_ASSERT (it != NULL); \ + it->index = v->size; \ + it->array = v; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t it, const it_t org) \ + { \ + M_ASSERT (it != NULL && org != NULL); \ + it->index = org->index; \ + it->array = org->array; \ + M_ARRA4_CONTRACT(it->array); \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(const it_t it) \ + { \ + M_ASSERT(it != NULL && it->array != NULL); \ + return it->index >= it->array->size; \ + } \ + \ + M_INLINE bool \ + M_F(name, _last_p)(const it_t it) \ + { \ + M_ASSERT(it != NULL && it->array != NULL); \ + /* NOTE: Can not compute 'size-1' due to potential overflow \ + if size is 0 */ \ + return it->index + 1 >= it->array->size; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const it_t it1, \ + const it_t it2) \ + { \ + M_ASSERT(it1 != NULL && it2 != NULL); \ + return it1->array == it2->array && it1->index == it2->index; \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(it_t it) \ + { \ + M_ASSERT(it != NULL && it->array != NULL); \ + it->index ++; \ + } \ + \ + M_INLINE void \ + M_F(name, _previous)(it_t it) \ + { \ + M_ASSERT(it != NULL && it->array != NULL); \ + /* NOTE: In the case index=0, it will be set to (unsigned) -1 \ + ==> it will be greater than size ==> end_p will return true */ \ + it->index --; \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(const it_t it) \ + { \ + M_ASSERT(it != NULL); \ + return M_F(name, _get)(it->array, it->index); \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(const it_t it) \ + { \ + M_ASSERT(it != NULL); \ + return M_F(name, _cget)(it->array, it->index); \ + } \ + \ + M_IF_METHOD(INIT_SET, oplist)( \ + M_INLINE void \ + M_F(name, _insert)(array_t a, it_t it, type const x) \ + { \ + M_ASSERT (it != NULL && a == it->array); \ + size_t index = M_F(name, _end_p)(it) ? 0 : it->index+1; \ + M_F(name, _push_at)(a, index, x); \ + it->index = index; \ + } \ + , /* End of INIT_SET */ ) \ + \ + M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _remove)(array_t a, it_t it) \ + { \ + M_ASSERT (it != NULL && a == it->array); \ + M_F(name, _pop_at)(NULL, a, it->index); \ + /* NOTE: it->index will naturaly point to the next element */ \ + } \ + , /* End of SET | INIT_SET */ ) \ + \ + M_IF_METHOD(CMP, oplist) \ + ( \ + M_INLINE void M_F(name, _special_sort)(array_t l, \ + int (*func_type) (type const *a, type const *b)) \ + { \ + /* Using qsort is more compact but slower than a full templated \ + version which can be twice faster */ \ + int (*func_void)(const void*, const void*); \ + /* There is no way (?) to avoid the cast */ \ + func_void = (int (*)(const void*, const void*))func_type; \ + qsort (l->ptr, l->size, sizeof(type), func_void); \ + } \ + \ + M_IF_METHOD2(SWAP, SET, oplist)( \ + M_INLINE void \ + M_C3(m_arra4_,name,_stable_sort_noalloc)(type tab[], size_t size, type tmp[]) \ + { \ + size_t th = 4; \ + M_IF_DEBUG(type *org_tab = tab;) \ + M_ASSERT (size > 1); \ + /* Let's select the threshold of the pass 1 to be sure \ + the final result is in tab.*/ \ + if (m_core_clz64(size-1) & 1) \ + th += th; \ + \ + /* Pass 1: Partial insertion sort (stable) up to 'th' size */ \ + for(size_t k = 0 ; k < size; ) { \ + size_t max = size - k < 2*th ? size - k : th; \ + for(size_t i = 1; i < max; i++) { \ + size_t j = i; \ + while (j > 0 && M_CALL_CMP(oplist, tab[k+j-1], tab[k+j]) > 0) { \ + M_CALL_SWAP(oplist, tab[k+j-1], tab[k+j]); \ + j = j - 1; \ + } \ + } \ + k += max; \ + } \ + \ + /* N Pass of merge sort (stable) */ \ + while (th < size) { \ + type *dest = tmp; \ + /* Pass n: Merge 2 sections of 'th' elements */ \ + for(size_t k = 0 ; k < size; ) { \ + type *el1 = &tab[k]; \ + type *el2 = &tab[k+th]; \ + size_t n1 = th; \ + size_t n2 = size-k <= 3*th ? size-k-th : th; \ + M_ASSERT (size-k > th); \ + M_ASSERT (0 < n1 && n1 <= size); \ + M_ASSERT (0 < n2 && n2 <= size); \ + k += n1+n2; \ + for (;;) { \ + if (M_CALL_CMP(oplist, *el1, *el2) <= 0) { \ + M_DO_INIT_MOVE(oplist, *dest, *el1); \ + dest++; \ + el1++; \ + if (M_UNLIKELY(-- n1 == 0)) { \ + if (n2 > 0) { \ + memcpy (dest, el2, n2 * sizeof (type)); \ + dest += n2; \ + } \ + break; \ + } \ + } else { \ + M_DO_INIT_MOVE(oplist, *dest, *el2); \ + dest++; \ + el2++; \ + if (M_UNLIKELY(-- n2 == 0)) { \ + if (n1 > 0) { \ + memcpy (dest, el1, n1 * sizeof (type)); \ + dest += n1; \ + } \ + break; \ + } \ + } \ + } \ + } \ + /* Swap t & tab */ \ + M_SWAP(type *, tab, tmp); \ + /* Increase th for next pass */ \ + th += th; \ + } \ + M_ASSERT (org_tab == tab); \ + } \ + \ + M_INLINE void \ + M_F(name, _special_stable_sort)(array_t l) \ + { \ + if (M_UNLIKELY (l->size < 2)) \ + return; \ + /* NOTE: if size is <= 4, no need to perform an allocation */ \ + type *temp = M_CALL_REALLOC(oplist, type, NULL, l->size); \ + if (M_UNLIKELY_NOMEM (temp == NULL)) { \ + M_MEMORY_FULL(sizeof (type) * l->size); \ + return ; \ + } \ + M_C3(m_arra4_,name,_stable_sort_noalloc)(l->ptr, l->size, temp); \ + M_CALL_FREE(oplist, temp); \ + } \ + ,) /* IF SWAP & SET methods */ \ + \ + ,) /* IF CMP oplist */ \ + \ + M_IF_METHOD(EQUAL, oplist)( \ + M_INLINE bool \ + M_F(name, _equal_p)(const array_t array1, \ + const array_t array2) \ + { \ + M_ARRA4_CONTRACT(array1); \ + M_ARRA4_CONTRACT(array2); \ + if (array1->size != array2->size) return false; \ + size_t i; \ + for(i = 0; i < array1->size; i++) { \ + type const *item1 = M_F(name, _cget)(array1, i); \ + type const *item2 = M_F(name, _cget)(array2, i); \ + bool b = M_CALL_EQUAL(oplist, *item1, *item2); \ + if (!b) return false; \ + } \ + return i == array1->size; \ + } \ + , /* no EQUAL */ ) \ + \ + M_IF_METHOD(HASH, oplist)( \ + M_INLINE size_t \ + M_F(name, _hash)(const array_t array) \ + { \ + M_ARRA4_CONTRACT(array); \ + M_HASH_DECL(hash); \ + for(size_t i = 0 ; i < array->size; i++) { \ + size_t hi = M_CALL_HASH(oplist, array->ptr[i]); \ + M_HASH_UP(hash, hi); \ + } \ + return M_HASH_FINAL (hash); \ + } \ + , /* no HASH */ ) \ + \ + M_INLINE void \ + M_F(name, _splice)(array_t a1, array_t a2) \ + { \ + M_ARRA4_CONTRACT(a1); \ + M_ARRA4_CONTRACT(a2); \ + if (M_LIKELY (a2->size > 0)) { \ + size_t newSize = a1->size + a2->size; \ + /* To overflow newSize, we need to a1 and a2 a little bit above \ + SIZE_MAX/2, which is not possible in the classic memory model as we \ + should have exhausted all memory before reaching such sizes. */ \ + M_ASSERT(newSize > a1->size); \ + if (newSize > a1->alloc) { \ + type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, newSize); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (type) * newSize); \ + } \ + a1->ptr = ptr; \ + a1->alloc = newSize; \ + } \ + M_ASSERT(a1->ptr != NULL); \ + M_ASSERT(a2->ptr != NULL); \ + memcpy(&a1->ptr[a1->size], &a2->ptr[0], a2->size * sizeof (type)); \ + /* a2 is now empty */ \ + a2->size = 0; \ + /* a1 has been expanded with the items of a2 */ \ + a1->size = newSize; \ + } \ + } \ + +/* Define the I/O functions */ +#define M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \ + \ + M_IF_METHOD(GET_STR, oplist)( \ + M_INLINE void \ + M_F(name, _get_str)(m_string_t str, array_t const array, \ + bool append) \ + { \ + M_ARRA4_CONTRACT(array); \ + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \ + it_t it; \ + for (M_F(name, _it)(it, array) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + type const *item = M_F(name, _cref)(it); \ + M_CALL_GET_STR(oplist, str, *item, true); \ + if (!M_F(name, _last_p)(it)) \ + m_string_push_back (str, M_GET_SEPARATOR oplist); \ + } \ + m_string_push_back (str, ']'); \ + } \ + , /* no GET_STR */ ) \ + \ + M_IF_METHOD(OUT_STR, oplist)( \ + M_INLINE void \ + M_F(name, _out_str)(FILE *file, const array_t array) \ + { \ + M_ARRA4_CONTRACT(array); \ + M_ASSERT (file != NULL); \ + fputc ('[', file); \ + for (size_t i = 0; i < array->size; i++) { \ + type const *item = M_F(name, _cget)(array, i); \ + M_CALL_OUT_STR(oplist, file, *item); \ + if (i != array->size-1) \ + fputc (M_GET_SEPARATOR oplist, file); \ + } \ + fputc (']', file); \ + } \ + , /* no OUT_STR */ ) \ + \ + M_IF_METHOD2(PARSE_STR, INIT, oplist)( \ + M_INLINE bool \ + M_F(name, _parse_str)(array_t array, const char str[], const char**endp) \ + { \ + M_ARRA4_CONTRACT(array); \ + M_ASSERT (str != NULL); \ + M_F(name,_reset)(array); \ + int c = *str++; \ + if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \ + c = m_core_str_nospace(&str); \ + if (M_UNLIKELY (c == ']')) { goto exit; } \ + if (M_UNLIKELY (c == 0)) { goto exit; } \ + str--; \ + M_QLET(item, type, oplist) { \ + do { \ + bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \ + c = m_core_str_nospace(&str); \ + if (b == false || c == 0) { c = 0; break; } \ + M_F(name, _push_back)(array, item); \ + } while (c == M_GET_SEPARATOR oplist); \ + } \ + exit: \ + M_ARRA4_CONTRACT(array); \ + if (endp) *endp = str; \ + return c == ']'; \ + } \ + , /* no PARSE_STR & INIT */ ) \ + \ + M_IF_METHOD2(IN_STR, INIT, oplist)( \ + M_INLINE bool \ + M_F(name, _in_str)(array_t array, FILE *file) \ + { \ + M_ARRA4_CONTRACT(array); \ + M_ASSERT (file != NULL); \ + M_F(name,_reset)(array); \ + int c = fgetc(file); \ + if (M_UNLIKELY (c != '[')) return false; \ + c = fgetc(file); \ + if (M_UNLIKELY (c == ']')) return true; \ + if (M_UNLIKELY (c == EOF)) return false; \ + ungetc(c, file); \ + M_QLET(item, type, oplist) { \ + do { \ + bool b = M_CALL_IN_STR(oplist, item, file); \ + c = m_core_fgetc_nospace(file); \ + if (b == false || c == EOF) { c = 0; break; } \ + M_F(name, _push_back)(array, item); \ + } while (c == M_GET_SEPARATOR oplist); \ + } \ + M_ARRA4_CONTRACT(array); \ + return c == ']'; \ + } \ + , /* no IN_STR & INIT */ ) \ + \ + M_IF_METHOD(OUT_SERIAL, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, const array_t array) \ + { \ + M_ARRA4_CONTRACT(array); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_return_code_t ret; \ + m_serial_local_t local; \ + ret = f->m_interface->write_array_start(local, f, array->size); \ + for (size_t i = 0; i < array->size; i++) { \ + type const *item = M_F(name, _cget)(array, i); \ + if (i != 0) { \ + ret |= f->m_interface->write_array_next(local, f); \ + } \ + ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \ + } \ + ret |= f->m_interface->write_array_end(local, f); \ + return ret & M_SERIAL_FAIL; \ + } \ + , /* no OUT_SERIAL */ ) \ + \ + M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(array_t array, m_serial_read_t f) \ + { \ + M_ARRA4_CONTRACT(array); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_return_code_t ret; \ + m_serial_local_t local; \ + size_t estimated_size = 0; \ + M_F(name,_reset)(array); \ + ret = f->m_interface->read_array_start(local, f, &estimated_size); \ + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) { \ + return ret; \ + } \ + M_F(name, _reserve)(array, estimated_size); \ + M_QLET(item, type, oplist) { \ + do { \ + ret = M_CALL_IN_SERIAL(oplist, item, f); \ + if (ret != M_SERIAL_OK_DONE) { break; } \ + M_F(name, _push_back)(array, item); \ + ret = f->m_interface->read_array_next(local, f); \ + } while (ret == M_SERIAL_OK_CONTINUE); \ + } \ + M_ARRA4_CONTRACT(array); \ + return ret; \ + } \ + , /* no IN_SERIAL & INIT */ ) \ + +/* Definition of the emplace_back function for arrays */ +#define M_ARRA4_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_F(name, _subtype_ct) *data = M_F(name, _push_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ + } \ + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define ARRAY_DEF M_ARRAY_DEF +#define ARRAY_DEF_AS M_ARRAY_DEF_AS +#define ARRAY_OPLIST M_ARRAY_OPLIST +#define ARRAY_INIT_VALUE M_ARRAY_INIT_VALUE +#endif + +#endif diff --git a/components/mlib/m-atomic.h b/components/mlib/m-atomic.h new file mode 100644 index 00000000..54ceaa7c --- /dev/null +++ b/components/mlib/m-atomic.h @@ -0,0 +1,342 @@ +/* + * M*LIB - Thin stdatomic wrapper for C++ compatibility + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_ATOMIC_H +#define MSTARLIB_ATOMIC_H + +/* NOTE: Due to the C++ not having recognized stdatomic.h officialy, + it is hard to use this header directly with a C++ compiler. + See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932 + clang++ has no issue with this header but if someone includes + atomic from C++, there is incompatibility between atomic & stdatomic. + Moreover some compilers lack a working stdatomic header. + GCC 4.9 doesn't have a working implementation of 'atomic'. + APPLE Clang defines __GNUC__ to be only 4 despite having full support + for atomic. +*/ +#if defined(__cplusplus) && __cplusplus >= 201103L \ + && !(defined(__GNUC__) && __GNUC__ < 5 && !defined(__APPLE__)) + +/* NOTE: This is what the stdatomic.h header shall do in C++ mode. */ +#include + +using std::memory_order; + +using std::atomic_bool; +using std::atomic_char; +using std::atomic_short; +using std::atomic_int; +using std::atomic_long; +using std::atomic_llong; +using std::atomic_uchar; +using std::atomic_schar; +using std::atomic_ushort; +using std::atomic_uint; +using std::atomic_ulong; +using std::atomic_ullong; +using std::atomic_intptr_t; +using std::atomic_uintptr_t; +using std::atomic_size_t; +using std::atomic_ptrdiff_t; +using std::atomic_intmax_t; +using std::atomic_uintmax_t; +using std::atomic_flag; + +using std::kill_dependency; +using std::atomic_thread_fence; +using std::atomic_signal_fence; +using std::atomic_is_lock_free; +using std::atomic_store_explicit; +using std::atomic_store; +using std::atomic_load_explicit; +using std::atomic_load; +using std::atomic_exchange_explicit; +using std::atomic_exchange; +using std::atomic_compare_exchange_strong_explicit; +using std::atomic_compare_exchange_strong; +using std::atomic_compare_exchange_weak_explicit; +using std::atomic_compare_exchange_weak; +using std::atomic_fetch_add; +using std::atomic_fetch_add_explicit; +using std::atomic_fetch_sub; +using std::atomic_fetch_sub_explicit; +using std::atomic_fetch_or; +using std::atomic_fetch_or_explicit; +using std::atomic_fetch_xor; +using std::atomic_fetch_xor_explicit; +using std::atomic_fetch_and; +using std::atomic_fetch_and_explicit; +using std::atomic_flag_test_and_set; +using std::atomic_flag_test_and_set_explicit; +using std::atomic_flag_clear; +using std::atomic_flag_clear_explicit; + +using std::memory_order_relaxed; +using std::memory_order_consume; +using std::memory_order_acquire; +using std::memory_order_release; +using std::memory_order_acq_rel; +using std::memory_order_seq_cst; + +/* CLANG provides a warning on defining _Atomic as it sees it + * as a reserved system macro. It is true. However, the goal of this + * header is to provide stdatomic semantic, so it needs to define + * _Atomic macro. + * + * So, this warning has to be ignored. + * + * It cannot use M_BEGIN_PROTECTED_CODE as this header is normally + * independent of m-core.h + */ +#if defined(__clang__) && __clang_major__ >= 4 + _Pragma("clang diagnostic push") + _Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"") +#endif + +#define _Atomic(T) std::atomic< T > + +#if defined(__clang__) && __clang_major__ >= 4 + _Pragma("clang diagnostic pop") +#endif + +/* C11 with working stdatomic + STDATOMIC doesn't work with C++ except for clang but is incompatible with atomic. + GCC < 4.9 doesn't provide a compliant stdatomic.h + CLANG 3.5 has issues with GCC's stdatomic.h and doesn't provide its own + ICC < 18 doesn't provide a compliant stdatomic.h +*/ +#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) ) \ + || (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \ + || (defined(__clang__) && (__clang_major__ * 100 + __clang_minor__) >= 308) \ + || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1800) + +#include + +/* MSYS2 has a conflict between cdefs.h which defines a _Atomic macro (if not C11) + not compatible with the used stdatomic.h (from GCC). + Provide a configurable mechanism to undef it with auto-detection of msys2 / gcc */ +#ifndef M_USE_UNDEF_ATOMIC +# if defined(__MSYS__) && defined(__GNUC__) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L) +# define M_USE_UNDEF_ATOMIC 1 +# endif +#endif +#if defined(M_USE_UNDEF_ATOMIC) && M_USE_UNDEF_ATOMIC == 1 +# undef _Atomic +#endif + +/* Non working C++ atomic header, nor working stdatomic.h found. + Write a compatible layer using mutex as slin as possible. + Supports only up to 64-bits atomic (sizeof long long to be more precise). + The locks are never properly cleared and remain active until + the end of the program. + We also assume that the call to the atomic_* interface is "macro clean". +*/ +#else + +#include "m-thread.h" +#include "m-core.h" + +M_BEGIN_PROTECTED_CODE + +/* _Atomic qualifier for a type (emulation). + The structure is quite large: + _val : value of the atomic type, + _zero : zero value of the atomic type (constant), + _previous: temporary value used within the mutex lock, + _lock : the mutex lock. + Support up to sizeof (long long) type. + */ +#define _Atomic(T) \ + struct { \ + T volatile _val; \ + T _zero; \ + T _previous; \ + m_mutex_t _lock; \ + } + +/* Define the supported memory order. + Even if memory order is defined, only the strongest constraint is used */ +typedef enum { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +} memory_order; + +typedef _Atomic(bool) atomic_bool; +typedef _Atomic(char) atomic_char; +typedef _Atomic(short) atomic_short; +typedef _Atomic(int) atomic_int; +typedef _Atomic(long) atomic_long; +typedef _Atomic(long long) atomic_llong; +typedef _Atomic(unsigned char) atomic_uchar; +typedef _Atomic(signed char) atomic_schar; +typedef _Atomic(unsigned short) atomic_ushort; +typedef _Atomic(unsigned int) atomic_uint; +typedef _Atomic(unsigned long) atomic_ulong; +typedef _Atomic(unsigned long long) atomic_ullong; +typedef _Atomic(intptr_t) atomic_intptr_t; +typedef _Atomic(uintptr_t) atomic_uintptr_t; +typedef _Atomic(size_t) atomic_size_t; +typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t; + +/* Define the minimum size supported by the architecture + for an atomic read or write. + This can help a lot since it avoids locking for atomic_load and + atomic_store. +*/ +#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) +# define ATOMICI_MIN_RW_SIZE 8 +#elif defined(_M_86) || defined (__i386__) +# define ATOMICI_MIN_RW_SIZE 4 +#else +# define ATOMICI_MIN_RW_SIZE 0 +#endif + +/* Detect if stdint.h was included */ +#if (defined (INTMAX_C) && defined (UINTMAX_C) && !defined(__cplusplus)) || \ + defined (_STDINT_H) || defined (_STDINT_H_) || defined (_STDINT) || \ + defined (_SYS_STDINT_H_) +/* Define additional atomic types */ +typedef _Atomic(intmax_t) atomic_intmax_t; +typedef _Atomic(uintmax_t) atomic_uintmax_t; +#endif + +/* (INTERNAL) Unlock the mutex and return the given value */ +M_INLINE long long atomic_fetch_unlock (m_mutex_t *lock, long long val) +{ + m_mutex_unlock (*lock); + return val; +} + +/* (INTERNAL) This is the heart of the wrapper: + lock the atomic value, read it and returns the value. + In order to avoid any compiler extension, we need to transform the + atomic type into 'long long' then convert it back to its value. + This is because _previous can't be read after the lock, and we can't + generate temporary variable within a macro. + The trick is computing _val - _zero within the lock, then + returns retvalue + _zero after the release of the lock. +*/ +#define atomic_fetch_op(ptr, val, op) \ + (m_mutex_lock((ptr)->_lock), \ + (ptr)->_previous = (ptr)->_val, \ + (ptr)->_val op (val), \ + atomic_fetch_unlock(&(ptr)->_lock, (long long)((ptr)->_previous-(ptr)->_zero))+(ptr)->_zero) + +/* Perform an atomic add (EMULATION) */ +#define atomic_fetch_add(ptr, val) atomic_fetch_op(ptr, val, +=) +/* Perform an atomic sub (EMULATION) */ +#define atomic_fetch_sub(ptr, val) atomic_fetch_op(ptr, val, -=) +/* Perform an atomic or (EMULATION) */ +#define atomic_fetch_or(ptr, val) atomic_fetch_op(ptr, val, |=) +/* Perform an atomic xor (EMULATION) */ +#define atomic_fetch_xor(ptr, val) atomic_fetch_op(ptr, val, ^=) +/* Perform an atomic and (EMULATION) */ +#define atomic_fetch_and(ptr, val) atomic_fetch_op(ptr, val, &=) +/* Perform an atomic exchange (EMULATION) */ +#define atomic_exchange(ptr, val) atomic_fetch_op(ptr, val, =) + +/* Initialize an atomic GLOBAL variable */ +#define ATOMIC_VAR_INIT(val) { val, 0, 0, M_MUTEXI_INIT_VALUE } + +/* Initialize an atomic variable */ +#define atomic_init(ptr, val) \ + (m_mutex_init((ptr)->_lock), (ptr)->_val = val, (ptr)->_zero = 0) + +/* (INTERNAL) Load an atomic variable within a lock + (needed for variable greater than CPU atomic size) */ +#define atomic_load_lock(ptr) \ + (m_mutex_lock((ptr)->_lock), \ + (ptr)->_previous = (ptr)->_val, \ + atomic_fetch_unlock(&(ptr)->_lock, (long long) ((ptr)->_previous-(ptr)->_zero))+(ptr)->_zero) + +/* (INTERNAL) Store an atomic variable within a lock + (needed for variable greater than CPU atomic size) */ +#define atomic_store_lock(ptr, val) \ + (m_mutex_lock((ptr)->_lock), \ + (ptr)->_val = (val), \ + m_mutex_unlock((ptr)->_lock)) + +/* Atomic load of a variable (EMULATION) + If the atomic type size is not greater than the CPU atomic size, + we can perform a direct read of the variable (much faster) */ +#define atomic_load(ptr) \ + ( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE \ + ? (ptr)->_val \ + : atomic_load_lock(ptr)) + +/* Atomic store of a variable (EMULATION) + If the atomic type size is not greater than the CPU atomic size, + we can perform a direct write of the variable (much faster) */ +#define atomic_store(ptr, val) do { \ + if ( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE) { \ + (ptr)->_val = (val); \ + } else { \ + long long _offset = (long long) ((val) - (ptr)->_zero); \ + atomic_store_lock(ptr, (ptr)->_zero + _offset); \ + } \ + } while (0) + +/* Perform a CAS (Compare and swap) operation (EMULATION) */ +#define atomic_compare_exchange_strong(ptr, exp, val) \ + (m_mutex_lock((ptr)->_lock), \ + atomic_fetch_unlock(&(ptr)->_lock, \ + (ptr)->_val == *(exp) \ + ? ((ptr)->_val = (val), true) \ + : (*(exp) = (ptr)->_val, false))) + + +#define atomic_fetch_add_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, +=) +#define atomic_fetch_sub_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, -=) +#define atomic_fetch_or_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, |=) +#define atomic_fetch_xor_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, ^=) +#define atomic_fetch_and_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, &=) +#define atomic_exchange_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, =) +#define atomic_load_explicit(ptr, mem) atomic_load(ptr) +#define atomic_store_explicit(ptr, val, mem) atomic_store(ptr, val) +#define kill_dependency(ptr) atomic_load(ptr) +#define atomic_thread_fence(mem) (void) 0 +#define atomic_signal_fence(mem) (void) 0 +#define atomic_is_lock_free(ptr) false +#define atomic_compare_exchange_strong_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val) +#define atomic_compare_exchange_weak_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val) +#define atomic_compare_exchange_weak(ptr, exp, val) atomic_compare_exchange_strong(ptr, exp, val) + +/* TODO: Missing atomic_flag. Problem: it is supposed to be lock free! */ + +M_END_PROTECTED_CODE + +#endif + +// C17 deprecated ATOMIC_VAR_INIT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201710L +# define M_ATOMIC_VAR_INIT(x) (x) +#else +# define M_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x) +#endif + +#endif diff --git a/components/mlib/m-bitset.h b/components/mlib/m-bitset.h new file mode 100644 index 00000000..b1ec85c3 --- /dev/null +++ b/components/mlib/m-bitset.h @@ -0,0 +1,981 @@ +/* + * M*LIB - BITSET module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_BITSET_H +#define MSTARLIB_BITSET_H + +#include +#include "m-core.h" + +/********************************** INTERNAL ************************************/ + +M_BEGIN_PROTECTED_CODE + +// Define the basic limb of a bitset +typedef uint64_t m_b1tset_limb_ct; +// And its size in bits +#define M_B1TSET_LIMB_BIT (sizeof(m_b1tset_limb_ct) * CHAR_BIT) + +// bitset grow policy. n is limb size +#define M_B1TSET_INC_ALLOC_SIZE(n) ((n) < 4 ? 4 : (n) * 2) + +// Compute the number of allocated limbs needed to handle 'n' bits. +#define M_B1TSET_TO_ALLOC(n) (((n) + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT) + +// Compute the number of bits available from the allocated size in limbs +#define M_B1TSET_FROM_ALLOC(n) ((n) * M_B1TSET_LIMB_BIT) + +// Contract of a bitset +#define M_B1TSET_CONTRACT(t) do { \ + M_ASSERT (t != NULL); \ + M_ASSERT (t->size <= M_B1TSET_FROM_ALLOC (t->alloc)); \ + M_ASSERT (t->alloc <= ((size_t)-1) / M_B1TSET_LIMB_BIT); \ + M_ASSERT (t->size < ((size_t)-1) - M_B1TSET_LIMB_BIT); \ + M_ASSERT (t->size == 0 || t->ptr != NULL); \ + M_ASSERT (t->alloc == 0 || t->ptr != NULL); \ + M_ASSERT ((t->size % M_B1TSET_LIMB_BIT) == 0 || (t->ptr[ (t->size-1) / M_B1TSET_LIMB_BIT] & ~(((((m_b1tset_limb_ct)1)<<(t->size % M_B1TSET_LIMB_BIT))<<1)-1)) == 0); \ + } while (0) + + +/********************************** EXTERNAL ************************************/ + +/* Define a type of variable 'bits' or array of packed booleans */ +typedef struct m_bitset_s { + size_t size; // Size is the number of bits + size_t alloc; // Alloc is the number of allocated limbs + m_b1tset_limb_ct *ptr; // Pointer to the allocated limbs +} m_bitset_t[1]; + +/* Pointer to a m_bitset_t */ +typedef struct m_bitset_s *m_bitset_ptr; + +/* Constant Pointer to a m_bitset_t */ +typedef const struct m_bitset_s *m_bitset_srcptr; + +/* Iterator on a bitset */ +typedef struct m_bitset_it_s { + size_t index; // index to the array of bit + bool value; // value used for _ref & _cref to store the value + struct m_bitset_s *set; // the associated bitset +} m_bitset_it_t[1]; + +/* Initialize a bitset (CONSTRUCTOR) */ +M_INLINE void +m_bitset_init(m_bitset_t t) +{ + M_ASSERT (t != NULL); + M_STATIC_ASSERT (M_POWEROF2_P(M_B1TSET_LIMB_BIT), MLIB_INTERNAL, "M*LIB: BITSET LIMB shall be a power of 2."); + t->size = 0; + t->alloc = 0; + t->ptr = NULL; + M_B1TSET_CONTRACT(t); +} + +/* Clean a bitset */ +M_INLINE void +m_bitset_reset(m_bitset_t t) +{ + M_B1TSET_CONTRACT(t); + t->size = 0; +} + +/* Clear a bitset (DESTRUCTOR) */ +M_INLINE void +m_bitset_clear(m_bitset_t t) +{ + m_bitset_reset(t); + M_MEMORY_FREE(t->ptr); + // This is not really needed, but is safer + // This representation is invalid and will be detected by the contract. + // A C compiler should be able to optimize out theses initializations. + t->alloc = 1; + t->ptr = NULL; +} + +/* Set a bitset to another one */ +M_INLINE void +m_bitset_set(m_bitset_t d, const m_bitset_t s) +{ + M_B1TSET_CONTRACT(d); + M_B1TSET_CONTRACT(s); + if (M_UNLIKELY (d == s)) return; + const size_t needAlloc = M_B1TSET_TO_ALLOC (s->size); + if (M_LIKELY (s->size > 0)) { + // Test if enough space in target + if (s->size > M_B1TSET_FROM_ALLOC (d->alloc)) { + m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, d->ptr, needAlloc); + if (M_UNLIKELY_NOMEM (ptr == NULL)) { + M_MEMORY_FULL(needAlloc); + return ; + } + d->ptr = ptr; + d->alloc = needAlloc; + } + M_ASSERT(d->ptr != NULL); + M_ASSERT(s->ptr != NULL); + memcpy (d->ptr, s->ptr, needAlloc * sizeof(m_b1tset_limb_ct) ); + } + d->size = s->size; + M_B1TSET_CONTRACT(d); +} + +/* Initialize & set a bitset to another one (CONSTRUCTOR) */ +M_INLINE void +m_bitset_init_set(m_bitset_t d, const m_bitset_t s) +{ + M_ASSERT (d != s); + m_bitset_init(d); + m_bitset_set(d, s); +} + +/* Initialize & move a bitset (CONSTRUCTOR) from another one (DESTRUCTOR) */ +M_INLINE void +m_bitset_init_move(m_bitset_t d, m_bitset_t s) +{ + M_B1TSET_CONTRACT(s); + d->size = s->size; + d->alloc = s->alloc; + d->ptr = s->ptr; + // Illegal representation of a bitset, to be detectable + s->alloc = 1; + s->ptr = NULL; + M_B1TSET_CONTRACT(d); +} + +/* Move a bitset from another one (DESTRUCTOR) */ +M_INLINE void +m_bitset_move(m_bitset_t d, m_bitset_t s) +{ + m_bitset_clear(d); + m_bitset_init_move (d, s); +} + +/* Set the bit 'i' in the bitset to the value 'x' */ +M_INLINE void +m_bitset_set_at(m_bitset_t v, size_t i, bool x) +{ + M_B1TSET_CONTRACT(v); + M_ASSERT (v->ptr != NULL); + M_ASSERT_INDEX(i, v->size); + const size_t offset = i / M_B1TSET_LIMB_BIT; + const size_t index = i % M_B1TSET_LIMB_BIT; + // This is a branchless version as x can only be 0 or 1 with only one variable shift. + const m_b1tset_limb_ct mask = ((m_b1tset_limb_ct)1)<ptr[offset] = (v->ptr[offset] & ~mask) | (mask & (0-(m_b1tset_limb_ct)x)); + M_B1TSET_CONTRACT (v); +} + +/* Flip the bit 'i' in the bitset */ +M_INLINE void +m_bitset_flip_at(m_bitset_t v, size_t i) +{ + M_B1TSET_CONTRACT(v); + M_ASSERT (v->ptr != NULL); + M_ASSERT_INDEX(i, v->size); + size_t offset = i / M_B1TSET_LIMB_BIT; + size_t index = i % M_B1TSET_LIMB_BIT; + v->ptr[offset] ^= ((m_b1tset_limb_ct)1)<size >= M_B1TSET_FROM_ALLOC (v->alloc))) { + // Compute the needed allocation. + const size_t needAlloc = M_B1TSET_INC_ALLOC_SIZE(v->alloc); + // Check for integer overflow + if (M_UNLIKELY_NOMEM (needAlloc <= v->alloc)) { + M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct)); + return; + } + // Alloc memory + m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, needAlloc); + // Check if success + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { + M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct)); + return; + } + v->ptr = ptr; + v->alloc = needAlloc; + } + M_ASSERT(v->ptr != NULL); + + const size_t i = v->size; + const size_t offset = i / M_B1TSET_LIMB_BIT; + const size_t index = i % M_B1TSET_LIMB_BIT; + if (M_UNLIKELY(index == 0)) { + // A new limb if used. Clear it before using it. + v->ptr[offset] = 0; + } + // This is a branchless version as x can only be 0 or 1 with only one variable shift. + const m_b1tset_limb_ct mask = ((m_b1tset_limb_ct)1)<ptr[offset] = (v->ptr[offset] & ~mask) | (mask & (0-(m_b1tset_limb_ct)x)); + v->size ++; + M_B1TSET_CONTRACT (v); +} + +/* Resize the bitset to have exactly 'size' bits */ +M_INLINE void +m_bitset_resize (m_bitset_t v, size_t size) +{ + M_B1TSET_CONTRACT (v); + // Check for overflow + if (M_UNLIKELY_NOMEM (size >= ((size_t)-1) - M_B1TSET_LIMB_BIT)) { + M_MEMORY_FULL((size_t) -1); + return; + } + // Compute the needed allocation. + size_t newAlloc = M_B1TSET_TO_ALLOC (size); + if (newAlloc > v->alloc) { + // Allocate more limbs to store the bitset. + m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc); + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { + M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct)); + return; + } + v->ptr = ptr; + v->alloc = newAlloc; + } + // Resize the bitsets + const size_t old_size = v->size; + const size_t offset = size / M_B1TSET_LIMB_BIT; + const size_t index = size % M_B1TSET_LIMB_BIT; + const m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1)<ptr[offset] &= mask; + } + } else if (size > old_size) { + // Resize up the bitset: set to 0 new bits. + const size_t old_offset = (old_size + M_B1TSET_LIMB_BIT - 1)/ M_B1TSET_LIMB_BIT; + for(size_t i = old_offset ; i < offset; i++) { + v->ptr[i] = 0; + } + if (M_LIKELY(index != 0)) { + // Mask the last limb to clear the last bits + v->ptr[offset] = 0; + } + } + v->size = size; + M_B1TSET_CONTRACT (v); +} + +/* Reserve allocation in the bitset to accomodate at least 'size' bits without reallocation */ +M_INLINE void +m_bitset_reserve (m_bitset_t v, size_t alloc) +{ + M_B1TSET_CONTRACT (v); + size_t oldAlloc = M_B1TSET_TO_ALLOC (v->size); + size_t newAlloc = M_B1TSET_TO_ALLOC (alloc); + // We refuse to reduce allocation below current size + if (oldAlloc > newAlloc) { + newAlloc = oldAlloc; + } + if (M_UNLIKELY (newAlloc == 0)) { + // Free all memory used by the bitsets + M_MEMORY_FREE (v->ptr); + v->size = v->alloc = 0; + v->ptr = NULL; + } else { + // Allocate more memory or reduce memory usage + m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc); + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { + M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct)); + return; + } + v->ptr = ptr; + v->alloc = newAlloc; + } + M_B1TSET_CONTRACT (v); +} + +/* Return the value of the boolean at index 'i'. + * NOTE: Interface is a little bit different: + * It doesn't return a pointer to the data, but the data itself. + */ +M_INLINE bool +m_bitset_get(const m_bitset_t v, size_t i) +{ + M_B1TSET_CONTRACT(v); + M_ASSERT (v->ptr != NULL); + M_ASSERT_INDEX(i, v->size); + size_t offset = i / M_B1TSET_LIMB_BIT; + size_t index = i % M_B1TSET_LIMB_BIT; + return ( v->ptr[offset] & (((m_b1tset_limb_ct)1) << index) ) != 0; +} + +/* m_bitset_cget is the exact same service than m_bitset_get */ +#define m_bitset_cget m_bitset_get + +/* Pop back the last bit in the bitset */ +M_INLINE void +m_bitset_pop_back(bool *dest, m_bitset_t v) +{ + M_B1TSET_CONTRACT (v); + M_ASSERT_INDEX (0, v->size); + // Remove one item from the bitset + v->size--; + // Prepare clearing popped bit + const size_t offset = v->size / M_B1TSET_LIMB_BIT; + const size_t index = v->size % M_B1TSET_LIMB_BIT; + const m_b1tset_limb_ct mask = ((m_b1tset_limb_ct)1)<ptr[offset] & mask) != 0; + } + v->ptr[offset] &= mask-1; + M_B1TSET_CONTRACT (v); +} + +/* Return the front bit value in the bitset */ +M_INLINE bool +m_bitset_front(m_bitset_t v) +{ + M_B1TSET_CONTRACT (v); + M_ASSERT_INDEX (0, v->size); + return m_bitset_get(v, 0); +} + +/* Return the back bit value in the bitset */ +M_INLINE bool +m_bitset_back(m_bitset_t v) +{ + M_B1TSET_CONTRACT (v); + M_ASSERT_INDEX (0, v->size); + return m_bitset_get(v, v->size-1); +} + +/* Test if the bitset is empty (no bits stored)*/ +M_INLINE bool +m_bitset_empty_p(m_bitset_t v) +{ + M_B1TSET_CONTRACT (v); + return v->size == 0; +} + +/* Return the number of bits of the bitset */ +M_INLINE size_t +m_bitset_size(m_bitset_t v) +{ + M_B1TSET_CONTRACT (v); + return v->size; +} + +/* Return the capacity in limbs of the bitset */ +M_INLINE size_t +m_bitset_capacity(m_bitset_t v) +{ + M_B1TSET_CONTRACT (v); + return M_B1TSET_FROM_ALLOC (v->alloc); +} + +/* Swap the bit at index i and j of the bitset */ +M_INLINE void +m_bitset_swap_at (m_bitset_t v, size_t i, size_t j) +{ + M_ASSERT_INDEX(i, v->size); + M_ASSERT_INDEX(j, v->size); + + bool i_val = m_bitset_get(v, i); + bool j_val = m_bitset_get(v, j); + m_bitset_set_at (v, i, j_val); + m_bitset_set_at (v, j, i_val); +} + +/* Swap the bitsets */ +M_INLINE void +m_bitset_swap (m_bitset_t v1, m_bitset_t v2) +{ + M_B1TSET_CONTRACT (v1); + M_B1TSET_CONTRACT (v2); + M_SWAP (size_t, v1->size, v2->size); + M_SWAP (size_t, v1->alloc, v2->alloc); + M_SWAP (m_b1tset_limb_ct *, v1->ptr, v2->ptr); + M_B1TSET_CONTRACT (v1); + M_B1TSET_CONTRACT (v2); +} + +/* (INTERNAL) Left shift of the bitset (ptr+size) by 1 bit, + * integrating the carry in the lowest position. + * Return the new carry. + */ +M_INLINE m_b1tset_limb_ct +m_b1tset_lshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry) +{ + for(size_t i = 0; i < n; i++) { + m_b1tset_limb_ct v = ptr[i]; + ptr[i] = (v << 1) | carry; + carry = (v >> (M_B1TSET_LIMB_BIT-1) ); + } + return carry; +} + +/* (INTERNAL) Right shift of the bitset (ptr+size) by 1 bit, + * integrating the carry in the lowest position. + * Return the new carry. + */ +M_INLINE m_b1tset_limb_ct +m_b1tset_rshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry) +{ + for(size_t i = n - 1; i < n; i--) { + m_b1tset_limb_ct v = ptr[i]; + ptr[i] = (v >> 1) | (carry << (M_B1TSET_LIMB_BIT-1) ); + carry = v & 1; + } + return carry; +} + +/* Insert a new bit at position 'key' of value 'value' in the bitset 'set' + shifting the set accordingly */ +M_INLINE void +m_bitset_push_at(m_bitset_t set, size_t key, bool value) +{ + M_B1TSET_CONTRACT (set); + // First push another value to extend the array to the right size + m_bitset_push_back(set, false); + M_ASSERT (set->ptr != NULL); + M_ASSERT_INDEX(key, set->size); + + // Then shift it + size_t offset = key / M_B1TSET_LIMB_BIT; + size_t index = key % M_B1TSET_LIMB_BIT; + m_b1tset_limb_ct v = set->ptr[offset]; + m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1)<> (M_B1TSET_LIMB_BIT-1) ); + v = (v & mask) | ((unsigned int) value << index) | ((v & ~mask) << 1); + set->ptr[offset] = v; + size_t size = (set->size + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT; + M_ASSERT (size >= offset + 1); + v = m_b1tset_lshift(&set->ptr[offset+1], size - offset - 1, carry); + // v is unused as it should be zero. + M_ASSERT(v == 0); + (void) v; + M_B1TSET_CONTRACT (set); +} + +/* Pop a new bit at position 'key' in the bitset + * and return in *dest its value if *dest exists */ +M_INLINE void +m_bitset_pop_at(bool *dest, m_bitset_t set, size_t key) +{ + M_B1TSET_CONTRACT (set); + M_ASSERT (set->ptr != NULL); + M_ASSERT_INDEX(key, set->size); + + if (dest) { + *dest = m_bitset_get (set, key); + } + // Shift it + size_t offset = key / M_B1TSET_LIMB_BIT; + size_t index = key % M_B1TSET_LIMB_BIT; + size_t size = (set->size + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT; + m_b1tset_limb_ct v, mask, carry; + carry = m_b1tset_rshift(&set->ptr[offset+1], size - offset - 1, false); + v = set->ptr[offset]; + mask = (((m_b1tset_limb_ct)1)<>1) & ~mask) | (carry << (M_B1TSET_LIMB_BIT-1)) ; + set->ptr[offset] = v; + // Decrease size + set->size --; + M_B1TSET_CONTRACT (set); +} + +/* Test if two bitsets are equal */ +M_INLINE bool +m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2) +{ + M_B1TSET_CONTRACT (set1); + M_B1TSET_CONTRACT (set2); + if (set1->size != set2->size) + return false; + /* We won't compare each bit individualy, + but instead compare them per limb */ + const size_t limbSize = (set1->size + M_B1TSET_LIMB_BIT -1) / M_B1TSET_LIMB_BIT; + for(size_t i = 0 ; i < limbSize;i++) + if (set1->ptr[i] != set2->ptr[i]) + return false; + return true; +} + +/* Initialize an iterator to the first bit of the biset */ +M_INLINE void +m_bitset_it(m_bitset_it_t it, m_bitset_t set) +{ + M_B1TSET_CONTRACT (set); + it->index = 0; + it->set = set; +} + +/* Initialize an iterator to reference the same bit as the given one*/ +M_INLINE void +m_bitset_it_set(m_bitset_it_t it, const m_bitset_it_t itorg) +{ + M_ASSERT (it != NULL && itorg != NULL); + it->index = itorg->index; + it->set = itorg->set; +} + +/* Initialize an iterator to reference the last bit of the bitset*/ +M_INLINE void +m_bitset_it_last(m_bitset_it_t it, m_bitset_t set) +{ + M_B1TSET_CONTRACT (set); + it->index = set->size-1; + it->set = set; +} + +/* Initialize an iterator to reference no valid bit of the bitset*/ +M_INLINE void +m_bitset_it_end(m_bitset_it_t it, m_bitset_t set) +{ + M_B1TSET_CONTRACT (set); + it->index = set->size; + it->set = set; +} + +/* Test if an iterator references no valid bit of the bitset anymore */ +M_INLINE bool +m_bitset_end_p(const m_bitset_it_t it) +{ + M_ASSERT (it != NULL && it->set != NULL); + return (it->index) >= (it->set->size); +} + +/* Test if an iterator references the last (or end) bit of the bitset anymore */ +M_INLINE bool +m_bitset_last_p(const m_bitset_it_t it) +{ + M_ASSERT (it != NULL && it->set != NULL); + /* NOTE: Can not compute 'size-1' due to potential overflow + if size is 0 */ + return (it->index+1) >= (it->set->size); +} + +/* Test if both iterators reference the same bit */ +M_INLINE bool +m_bitset_it_equal_p(const m_bitset_it_t it1, const m_bitset_it_t it2) +{ + M_ASSERT (it1 != NULL && it2 != NULL); + return it1->index == it2->index && it1->set == it2->set; +} + +/* Move the iterator to the next bit */ +M_INLINE void +m_bitset_next(m_bitset_it_t it) +{ + M_ASSERT (it != NULL && it->set != NULL); + it->index++; +} + +/* Move the iterator to the previous bit */ +M_INLINE void +m_bitset_previous(m_bitset_it_t it) +{ + M_ASSERT (it != NULL && it->set != NULL); + it->index--; +} + +// There is no _ref as it is not possible to modify the value using the IT interface + +/* Return a pointer to the bit referenced by the iterator + * Only one reference is possible at a time per iterator */ +M_INLINE const bool * +m_bitset_cref(m_bitset_it_t it) +{ + M_ASSERT (it != NULL && it->set != NULL); + it->value = m_bitset_get(it->set, it->index); + return &it->value; +} + +/* Output the bitset as a formatted text in a FILE */ +M_INLINE void +m_bitset_out_str(FILE *file, const m_bitset_t set) +{ + M_B1TSET_CONTRACT (set); + M_ASSERT(file != NULL); + fputc ('[', file); + for(size_t i = 0; i < set->size; i++) { + const bool b = m_bitset_get (set, i); + const char c = b ? '1' : '0'; + fputc (c, file); + } + fputc (']', file); +} + +/* Input the bitset from a formatted text in a FILE */ +M_INLINE bool +m_bitset_in_str(m_bitset_t set, FILE *file) +{ + M_B1TSET_CONTRACT (set); + M_ASSERT(file != NULL); + m_bitset_reset(set); + int c = fgetc(file); + if (M_UNLIKELY (c != '[')) return false; + c = fgetc(file); + while (c == '0' || c == '1') { + const bool b = (c == '1'); + m_bitset_push_back (set, b); + c = fgetc(file); + } + M_B1TSET_CONTRACT (set); + return c == ']'; +} + +/* Parse the bitset from a formatted text in a C string */ +M_INLINE bool +m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr) +{ + M_B1TSET_CONTRACT (set); + M_ASSERT(str != NULL); + bool success = false; + m_bitset_reset(set); + char c = *str++; + if (M_UNLIKELY(c != '[')) goto exit; + c = *str++; + do { + if (M_UNLIKELY(c != '0' && c != '1')) goto exit; + const bool b = (c == '1'); + m_bitset_push_back (set, b); + c = *str++; + } while (c != ']' && c != 0); + M_B1TSET_CONTRACT (set); + success = (c == ']'); + exit: + if (endptr) *endptr = str; + return success; +} + +/* Set the bitset from a formatted text in a C string */ +M_INLINE bool +m_bitset_set_str(m_bitset_t dest, const char str[]) +{ + return m_bitset_parse_str(dest, str, NULL); +} + +/* Perform an AND operation between the bitsets, + * up to the minimum size of both bitsets */ +M_INLINE void +m_bitset_and(m_bitset_t dest, const m_bitset_t src) +{ + M_B1TSET_CONTRACT(dest); + M_B1TSET_CONTRACT(src); + size_t s = M_MIN(dest->size, src->size); + size_t n = (s + M_B1TSET_LIMB_BIT -1) / M_B1TSET_LIMB_BIT; + for(size_t i = 0 ; i < n; i++) + dest->ptr[i] &= src->ptr[i]; + // Reduce the dest size to the minimum size between both + dest->size = s; + M_B1TSET_CONTRACT(dest); +} + +/* Perform an OR operation between the bitsets, + * up to the minimum size of both bitsets */ +M_INLINE void +m_bitset_or(m_bitset_t dest, const m_bitset_t src) +{ + M_B1TSET_CONTRACT(dest); + M_B1TSET_CONTRACT(src); + size_t s = M_MIN(dest->size, src->size); + size_t n = (s + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT; + for(size_t i = 0 ; i < n; i++) + dest->ptr[i] |= src->ptr[i]; + // Reduce the dest size to the minimum size between both + dest->size = s; + M_B1TSET_CONTRACT(dest); +} + +/* Perform an XOR operation between the bitsets, + * up to the minimum size of both bitsets */ +M_INLINE void +m_bitset_xor(m_bitset_t dest, const m_bitset_t src) +{ + M_B1TSET_CONTRACT(dest); + M_B1TSET_CONTRACT(src); + size_t s = M_MIN(dest->size, src->size); + size_t n = s / M_B1TSET_LIMB_BIT; + size_t m = s % M_B1TSET_LIMB_BIT; + for(size_t i = 0 ; i < n; i++) + dest->ptr[i] ^= src->ptr[i]; + if (M_LIKELY(m)) { + // Last limb needs to be masked too + m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1) << m) - 1; + dest->ptr[n] = (dest->ptr[n] ^ src->ptr[n]) & mask; + } + // Reduce the dest size to the minimum size between both + dest->size = s; + M_B1TSET_CONTRACT(dest); +} + +/* Perform a NOT operation of the bitset */ +M_INLINE void +m_bitset_not(m_bitset_t dest) +{ + M_B1TSET_CONTRACT(dest); + size_t s = dest->size; + size_t n = s / M_B1TSET_LIMB_BIT; + size_t m = s % M_B1TSET_LIMB_BIT; + for(size_t i = 0 ; i < n; i++) + dest->ptr[i] = ~ (dest->ptr[i]); + if (M_LIKELY(m)) { + // Last limb needs to be masked too + m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1) << m) - 1; + dest->ptr[n] = (~ dest->ptr[n]) & mask; + } + M_B1TSET_CONTRACT(dest); +} + +/* Copute a hash of the bitset */ +M_INLINE size_t +m_bitset_hash(const m_bitset_t set) +{ + M_B1TSET_CONTRACT(set); + size_t s = set->size; + size_t n = (s + M_B1TSET_LIMB_BIT-1) / M_B1TSET_LIMB_BIT; + M_HASH_DECL(hash); + for(size_t i = 0 ; i < n; i++) + M_HASH_UP(hash, set->ptr[i]); + return M_HASH_FINAL (hash); +} + +/* Count the number of leading zero */ +M_INLINE size_t +m_bitset_clz(const m_bitset_t set) +{ + M_B1TSET_CONTRACT(set); + size_t s = set->size; + if (M_UNLIKELY (s == 0)) { + return 0; + } + size_t n = (s -1) / M_B1TSET_LIMB_BIT; + size_t m = s % M_B1TSET_LIMB_BIT; + m_b1tset_limb_ct limb = set->ptr[n]; + if (m) { + m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1) << m) - 1; + limb &= mask; + } else { + m = M_B1TSET_LIMB_BIT; + } + s = 0; + while (limb == 0 && n > 0) { + s += m; + limb = set->ptr[--n]; + m = M_B1TSET_LIMB_BIT; + } + s += m_core_clz64(limb) - (M_B1TSET_LIMB_BIT - m); + return s; +} + +/* Count the number of trailing zero */ +M_INLINE size_t +m_bitset_ctz(const m_bitset_t set) +{ + M_B1TSET_CONTRACT(set); + size_t s = set->size; + if (M_UNLIKELY (s == 0)) { + return 0; + } + size_t i = 0, n = (s -1) / M_B1TSET_LIMB_BIT; + size_t m = s % M_B1TSET_LIMB_BIT; + m_b1tset_limb_ct limb = set->ptr[0]; + s = 0; + while (limb == 0 && i < n) { + s += M_B1TSET_LIMB_BIT; + limb = set->ptr[++i]; + } + if (i == n && m != 0) { + m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1) << m) - 1; + limb &= mask; + } + unsigned ctz = m_core_ctz64(limb); + s += (ctz == 64) ? m : ctz; + return s; +} + +// For GCC or CLANG or ICC +#if defined(__GNUC__) +M_INLINE size_t m_b1tset_popcount64(m_b1tset_limb_ct limb) +{ + return (size_t) __builtin_popcountll(limb); +} +#else +// MSVC __popcnt64 may not exist on the target architecture (no emulation layer) +// Use emulation layer: https://en.wikipedia.org/wiki/Hamming_weight +M_INLINE size_t m_b1tset_popcount64(m_b1tset_limb_ct limb) +{ + limb = limb - ((limb >> 1) & 0x5555555555555555ULL); + limb = (limb & 0x3333333333333333ULL) + ((limb >> 2) & 0x3333333333333333ULL); + limb = (limb + (limb >> 4)) & 0x0f0f0f0f0f0f0f0fULL; + return (limb * 0x0101010101010101ULL) >> 56; +} +#endif + +/* Count the number of 1 */ +M_INLINE size_t +m_bitset_popcount(const m_bitset_t set) +{ + M_B1TSET_CONTRACT(set); + size_t s = 0; + size_t n = (set->size + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT; + for(size_t i = 0 ; i < n; i++) + s += m_b1tset_popcount64(set->ptr[i]); + return s; +} + +/* Oplist for a bitset */ +#define M_BITSET_OPLIST \ + (INIT(m_bitset_init) \ + ,INIT_SET(m_bitset_init_set) \ + ,INIT_WITH(API_1(M_INIT_VAI)) \ + ,SET(m_bitset_set) \ + ,CLEAR(m_bitset_clear) \ + ,INIT_MOVE(m_bitset_init_move) \ + ,MOVE(m_bitset_move) \ + ,SWAP(m_bitset_swap) \ + ,TYPE(m_bitset_t) \ + ,SUBTYPE(bool) \ + ,EMPTY_P(m_bitset_empty_p), \ + ,GET_SIZE(m_bitset_size) \ + ,IT_TYPE(m_bitset_it_t) \ + ,IT_FIRST(m_bitset_it) \ + ,IT_SET(m_bitset_it_set) \ + ,IT_LAST(m_bitset_it_last) \ + ,IT_END(m_bitset_it_end) \ + ,IT_END_P(m_bitset_end_p) \ + ,IT_LAST_P(m_bitset_last_p) \ + ,IT_EQUAL_P(m_bitset_it_equal_p) \ + ,IT_NEXT(m_bitset_next) \ + ,IT_PREVIOUS(m_bitset_previous) \ + ,IT_CREF(m_bitset_cref) \ + ,RESET(m_bitset_reset) \ + ,PUSH(m_bitset_push_back) \ + ,POP(m_bitset_pop_back) \ + ,HASH(m_bitset_hash) \ + ,GET_STR(m_bitset_get_str) \ + ,OUT_STR(m_bitset_out_str) \ + ,PARSE_STR(m_bitset_parse_str) \ + ,IN_STR(m_bitset_in_str) \ + ,EQUAL(m_bitset_equal_p) \ + ) + +/* Register the OPLIST as a global one */ +#define M_OPL_m_bitset_t() M_BITSET_OPLIST + +// TODO: set_at2, insert_v, remove_v + +#if M_USE_SMALL_NAME + +#define bitset_s m_bitset_s +#define bitset_t m_bitset_t +#define bitset_ptr m_bitset_ptr +#define bitset_srcptr m_bitset_srcptr +#define bitset_it_s m_bitset_it_s +#define bitset_it_t m_bitset_it_t + +#define bitset_init m_bitset_init +#define bitset_reset m_bitset_reset +#define bitset_clear m_bitset_clear +#define bitset_set m_bitset_set +#define bitset_init_set m_bitset_init_set +#define bitset_init_move m_bitset_init_move +#define bitset_move m_bitset_move +#define bitset_set_at m_bitset_set_at +#define bitset_flip_at m_bitset_flip_at +#define bitset_push_back m_bitset_push_back +#define bitset_resize m_bitset_resize +#define bitset_reserve m_bitset_reserve +#define bitset_get m_bitset_get +#define bitset_pop_back m_bitset_pop_back +#define bitset_front m_bitset_front +#define bitset_back m_bitset_back +#define bitset_empty_p m_bitset_empty_p +#define bitset_size m_bitset_size +#define bitset_capacity m_bitset_capacity +#define bitset_swap_at m_bitset_swap_at +#define bitset_swap m_bitset_swap +#define bitset_push_at m_bitset_push_at +#define bitset_pop_at m_bitset_pop_at +#define bitset_equal_p m_bitset_equal_p +#define bitset_it m_bitset_it +#define bitset_it_set m_bitset_it_set +#define bitset_it_last m_bitset_it_last +#define bitset_it_end m_bitset_it_end +#define bitset_end_p m_bitset_end_p +#define bitset_last_p m_bitset_last_p +#define bitset_it_equal_p m_bitset_it_equal_p +#define bitset_next m_bitset_next +#define bitset_previous m_bitset_previous +#define bitset_cref m_bitset_cref +#define bitset_out_str m_bitset_out_str +#define bitset_in_str m_bitset_in_str +#define bitset_parse_str m_bitset_parse_str +#define bitset_set_str m_bitset_set_str +#define bitset_and m_bitset_and +#define bitset_or m_bitset_or +#define bitset_xor m_bitset_xor +#define bitset_not m_bitset_not +#define bitset_hash m_bitset_hash +#define bitset_clz m_bitset_clz +#define bitset_ctz m_bitset_ctz +#define bitset_popcount m_bitset_popcount +#define bitset_get_str m_bitset_get_str + +#define BITSET_OPLIST M_BITSET_OPLIST +#define M_OPL_bitset_t M_OPL_m_bitset_t + +#endif + +M_END_PROTECTED_CODE + +#endif + +// NOTE: Define this function only if m-string has been included +#if !defined(MSTARLIB_BITSET_STRING_H) && defined(MSTARLIB_STRING_H) +#define MSTARLIB_BITSET_STRING_H + +M_BEGIN_PROTECTED_CODE + +/* Output to a m_string_t 'str' the formatted text representation of the bitset 'set' + or append it to the strinf (append=true) */ +M_INLINE void +m_bitset_get_str(m_string_t str, const m_bitset_t set, bool append) +{ + M_B1TSET_CONTRACT (set); + M_ASSERT(str != NULL); + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); + for(size_t i = 0; i < set->size; i++) { + const bool b = m_bitset_get (set, i); + const char c = b ? '1' : '0'; + m_string_push_back (str, c); + } + m_string_push_back (str, ']'); +} + +M_END_PROTECTED_CODE + +#endif diff --git a/components/mlib/m-bptree.h b/components/mlib/m-bptree.h new file mode 100644 index 00000000..bd823311 --- /dev/null +++ b/components/mlib/m-bptree.h @@ -0,0 +1,1499 @@ +/* + * M*LIB - B+TREE module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_BPTREE_H +#define MSTARLIB_BPTREE_H + +#include "m-core.h" + +/* Define a B+tree of size 'N' that maps a 'key' to a 'value' + with its associated functions. + USAGE: + BPTREE_DEF2(name, N, key_t, key_oplist, value_t, value_oplist) + OR + BPTREE_DEF2(name, N, key_t, value_t) +*/ +#define M_BPTREE_DEF2(name, N, key_type, ...) \ + M_BPTREE_DEF2_AS(name, M_F(name,_t), M_F(name,_it_t), M_F(name, _itref_t), N, key_type, __VA_ARGS__) + + +/* Define a B+tree of size 'N' that maps a 'key' to a 'value' + as the given name name_t with its associated functions. + USAGE: + BPTREE_DEF2_AS(name, name_t, it_t, itref_t, N, key_t, key_oplist, value_t, value_oplist) + OR + BPTREE_DEF2_AS(name, name_t, it_t, itref_t, N, key_t, value_t) +*/ +#define M_BPTREE_DEF2_AS(name, name_t, it_t, itref_t, N, key_type, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_BPTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 0, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, itref_t ), \ + (name, N, key_type, __VA_ARGS__, 1, 0, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, itref_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a B+tree of a given type, of size N. + with its associated functions + USAGE: BPTREE_DEF(name, N, type, [, oplist_of_the_type]) */ +#define M_BPTREE_DEF(name, N, ...) \ + M_BPTREE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), N, __VA_ARGS__) + + +/* Define a B+tree of a given type, of size N. + as the given name name_t with its associated functions + USAGE: BPTREE_DEF_AS(name, name_t, it_t, N, type, [, oplist_of_the_type]) */ +#define M_BPTREE_DEF_AS(name, name_t, it_t, N, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_BPTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 0, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, M_F(name, _itref_ct) ), \ + (name, N, __VA_ARGS__, __VA_ARGS__, 0, 0, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, M_F(name, _itref_ct) ))) \ + M_END_PROTECTED_CODE + + +/* Define a B+tree of size 'N' that maps a 'key' to a 'value', + allowing multiple equal keys to exist, + with its associated functions. + USAGE: + BPTREE_MULTI_DEF2(name, N, key_t, key_oplist, value_t, value_oplist) + OR + BPTREE_MULTI_DEF2(name, N, key_t, value_t) +*/ +#define M_BPTREE_MULTI_DEF2(name, N, key_type, ...) \ + M_BPTREE_MULTI_DEF2_AS(name, M_F(name,_t), M_F(name,_it_t), M_F(name,_itref_t), N, key_type, __VA_ARGS__) + + +/* Define a B+tree of size 'N' that maps a 'key' to a 'value', + allowing multiple equal keys to exist, + as the given name name_t with its associated functions. + USAGE: + BPTREE_MULTI_DEF2_AS(name, name_t, it_t, itref_t, N, key_t, key_oplist, value_t, value_oplist) + OR + BPTREE_MULTI_DEF2_AS(name, name_t, it_t, itref_t, N, key_t, value_t) +*/ +#define M_BPTREE_MULTI_DEF2_AS(name, name_t, it_t, itref_t, N, key_type, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_BPTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 1, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, itref_t ), \ + (name, N, key_type, __VA_ARGS__, 1, 1, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, itref_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a B+tree of a given type, of size N. + allowing multiple equal keys to exist, + with its associated functions + USAGE: BPTREE_MULTI_DEF(name, N, type, [, oplist_of_the_type]) */ +#define M_BPTREE_MULTI_DEF(name, N, ...) \ + M_BPTREE_MULTI_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), N, __VA_ARGS__) + + +/* Define a B+tree of a given type, of size N. + allowing multiple equal keys to exist, + as the given name name_t with its associated functions + USAGE: BPTREE_MULTI_DEF_AS(name, name_t, it_t, N, type, [, oplist_of_the_type]) */ +#define M_BPTREE_MULTI_DEF_AS(name, name_t, it_t, N, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_BPTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 1, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, M_F(name, _itref_ct) ), \ + (name, N, __VA_ARGS__, __VA_ARGS__, 0, 1, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, M_F(name, _itref_ct) ))) \ + M_BEGIN_PROTECTED_CODE + + +/* Define the oplist of a B+TREE used as a set of type (from BPTREE_DEF). + USAGE: BPTREE_OPLIST(name [, oplist_of_the_type]) + NOTE: IT_REF is not exported so that the container appears as not modifiable + by algorithm. */ +#define M_BPTREE_OPLIST(...) \ + M_BPTR33_KEY_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST ), \ + (__VA_ARGS__ ))) + + +/* Define the oplist of a B+TREE used as a map of a key type to a value type (from BPTREE_DEF2). + USAGE: BPTREE_OPLIST2(name[, key_oplist, value_oplist]) + NOTE: IT_REF is not exported so that the container appears as not modifiable + by algorithm. */ +#define M_BPTREE_OPLIST2(...) \ + M_BPTR33_OPLIST2_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST, M_BASIC_OPLIST ), \ + (__VA_ARGS__ ))) + + +/*****************************************************************************/ +/******************************** INTERNAL ***********************************/ +/*****************************************************************************/ + +/* Deferred evaluation for the oplist definition, + so that all arguments are evaluated before further expansion */ +#define M_BPTR33_KEY_OPLIST_P1(arg) M_BPTR33_KEY_OPLIST_P2 arg + +/* Validation of the given oplists */ +#define M_BPTR33_KEY_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_BPTR33_KEY_OPLIST_P3, M_BPTR33_KEY_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_BPTR33_KEY_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_BPTREE_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* OPLIST definition of a b+tree (global tree) */ +#define M_BPTR33_KEY_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)), \ + INIT_SET(M_F(name, _init_set)), \ + INIT_WITH(API_1(M_INIT_VAI)), \ + SET(M_F(name, _set)), \ + CLEAR(M_F(name, _clear)), \ + INIT_MOVE(M_F(name, _init_move)), \ + MOVE(M_F(name, _move)), \ + SWAP(M_F(name, _swap)), \ + NAME(name), \ + TYPE(M_F(name,_ct)), \ + SUBTYPE(M_F(name, _subtype_ct)), \ + IT_TYPE(M_F(name, _it_ct)), \ + IT_FIRST(M_F(name,_it)), \ + IT_SET(M_F(name,_it_set)), \ + IT_END(M_F(name,_it_end)), \ + IT_END_P(M_F(name,_end_p)), \ + IT_EQUAL_P(M_F(name,_it_equal_p)), \ + IT_NEXT(M_F(name,_next)), \ + IT_CREF(M_F(name,_cref)), \ + RESET(M_F(name,_reset)), \ + PUSH(M_F(name,_push)), \ + GET_MIN(M_F(name,_min)), \ + GET_MAX(M_F(name,_max)), \ + M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),), \ + M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),), \ + M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),), \ + M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),), \ + M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),), \ + M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),), \ + M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \ + M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \ + ) + + +/* Deferred evaluation */ +#define M_BPTR33_OPLIST2_P1(arg) M_BPTR33_OPLIST2_P2 arg + +/* Validation of the given oplists (first the key oplist, then the value oplist) */ +#define M_BPTR33_OPLIST2_P2(name, key_oplist, value_oplist) \ + M_IF_OPLIST(key_oplist)(M_BPTR33_OPLIST2_P3, M_BPTR33_OPLIST2_FAILURE)(name, key_oplist, value_oplist) +#define M_BPTR33_OPLIST2_P3(name, key_oplist, value_oplist) \ + M_IF_OPLIST(value_oplist)(M_BPTR33_OPLIST2_P4, M_BPTR33_OPLIST2_FAILURE)(name, key_oplist, value_oplist) + +/* Prepare a clean compilation failure */ +#define M_BPTR33_OPLIST2_FAILURE(name, key_oplist, value_oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_BPTREE_OPLIST_IS_NOT_AN_OPLIST, name, key_oplist, value_oplist))) + +/* Final definition of the oplist (associative array) */ +#define M_BPTR33_OPLIST2_P4(name, key_oplist, value_oplist) \ + (INIT(M_F(name, _init)), \ + INIT_SET(M_F(name, _init_set)), \ + INIT_WITH(API_1(M_INIT_KEY_VAI)), \ + SET(M_F(name, _set)), \ + CLEAR(M_F(name, _clear)), \ + INIT_MOVE(M_F(name, _init_move)), \ + MOVE(M_F(name, _move)), \ + SWAP(M_F(name, _swap)), \ + NAME(name), \ + TYPE(M_F(name,_ct)), \ + SUBTYPE(M_F(name, _subtype_ct)), \ + EMPTY_P(M_F(name,_empty_p)), \ + IT_TYPE(M_F(name, _it_ct)), \ + IT_FIRST(M_F(name,_it)), \ + IT_SET(M_F(name,_it_set)), \ + IT_END(M_F(name,_it_end)), \ + IT_END_P(M_F(name,_end_p)), \ + IT_EQUAL_P(M_F(name,_it_equal_p)), \ + IT_NEXT(M_F(name,_next)), \ + IT_CREF(M_F(name,_cref)), \ + RESET(M_F(name,_reset)), \ + GET_MIN(M_F(name,_min)), \ + GET_MAX(M_F(name,_max)), \ + KEY_TYPE(M_F(name, _key_ct)), \ + VALUE_TYPE(M_F(name, _value_ct)), \ + SET_KEY(M_F(name, _set_at)), \ + GET_KEY(M_F(name, _get)), \ + SAFE_GET_KEY(M_F(name, _safe_get)) \ + ERASE_KEY(M_F(name, _erase)), \ + KEY_OPLIST(key_oplist), \ + VALUE_OPLIST(value_oplist), \ + M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(M_F(name, _parse_str)),), \ + M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_F(name, _out_str)),), \ + M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(M_F(name, _in_str)),), \ + M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(M_F(name, _out_serial)),), \ + M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(M_F(name, _in_serial)),), \ + M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)(EQUAL(M_F(name, _equal_p)),), \ + M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)(HASH(M_F(name, _hash)),) \ + ) + + +/******************************** INTERNAL ***********************************/ + +/* Internal contract of a B+TREE of size 'N' for a node 'node' or root 'root' */ +#ifdef NDEBUG +# define M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, node, root) do { } while (0) +#else +# define M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, node, root) do { \ + M_ASSERT ((node) != NULL); \ + M_ASSERT ((root) != NULL); \ + int num2 = (node)->num; \ + bool is_leaf2 = num2 <= 0; \ + num2 = num2 < 0 ? -num2 : num2; \ + if ((node) == (root)) { \ + /* Contract of the root node. num can be 0 */ \ + M_ASSERT( 0 <= num2 && num2 <= N); \ + if (num2 == 0) M_ASSERT (is_leaf2); \ + } else { \ + /* Contract of a non-root node. num cannot be 0 */ \ + int c2 = N / 2; \ + M_ASSERT (c2 > 0); \ + M_ASSERT (c2 <= num2 && num2 <= N); \ + } \ + /* The node is sorted */ \ + for(int i2 = 1; i2 < num2 ; i2++) { \ + M_ASSERT (M_CALL_CMP(key_oplist, (node)->key[i2-1], (node)->key[i2]) M_IF(isMulti)(<=, <) 0); \ + } \ + /* The chain node is also sorted */ \ + if ((node)->next != NULL) { \ + M_ASSERT (num2 >= 1); \ + M_ASSERT (M_CALL_CMP(key_oplist, (node)->key[num2-1], (node)->next->key[0]) M_IF(isMulti)(<=, <) 0); \ + } \ + } while (0) +#endif + +/* Contract for a B+TREE of size N named 'b' */ +#define M_BPTR33_CONTRACT(N, isMulti, key_oplist, b) do { \ + M_ASSERT (N >= 3); /* TBC: 2 instead ? */ \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, (b)->root, (b)->root); \ + M_ASSERT ((b)->root->next == NULL); \ + if ((b)->root->num <= 0) M_ASSERT (-(b)->root->num == (int) (b)->size); \ + } while (0) + +/* Max depth of any B+tree + Worst case is when all nodes are only half full. + Worst case is with the mininum size of a node (2) + Maximum number of elements: SIZE_MAX = 2 ^ (CHAR_BIT*sizeof (size_t)) - 1 + Height of such a tree if inferior to: + Ceil(Log2(2 ^ (CHAR_BIT*sizeof (size_t)))) + 1 + "+ 1" due to final line composed of nodes. + */ +#define M_BPTR33_MAX_STACK ((int)(1 + CHAR_BIT*sizeof (size_t))) + + +/* Deferred evaluation for the b+tree definition, + so that all arguments are evaluated before further expansion */ +#define M_BPTR33_DEF_P1(arg) M_ID( M_BPTR33_DEF_P2 arg ) + +/* Validate the key oplist before going further */ +#define M_BPTR33_DEF_P2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_IF_OPLIST(key_oplist)(M_BPTR33_DEF_P3, M_BPTR33_DEF_FAILURE) \ + (name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) + +/* Validate the value oplist before going further */ +#define M_BPTR33_DEF_P3(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_IF_OPLIST(value_oplist)(M_BPTR33_DEF_P4, M_BPTR33_DEF_FAILURE) \ + (name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) + +/* Stop processing with a compilation failure */ +#define M_BPTR33_DEF_FAILURE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, \ + "(BPTREE*_DEF): one of the given argument is not a valid oplist: " \ + M_AS_STR(key_oplist) " / " M_AS_STR(value_oplist)) + +/* Internal b+tree definition + - name: prefix to be used + - N: size of the node + - key_t: key type of the elements of the container + - key_oplist: oplist of the key type of the elements of the container + - value_t: value type of the elements of the container + - value_oplist: oplist of the value type of the elements of the container + - isMap: true if map, false if set + - isMulti: true if multimap/multiset, false otherwise + - tree_t: alias for the type of the container + - it_t: alias for the iterator of the container + - node_t: alias for internal node + - pit_t: alias for internal parent iterator + - subtype_t: alias for the type referenced by the iterator + */ +#define M_BPTR33_DEF_P4(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_BPTR33_DEF_TYPE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, key_t, key_oplist) \ + M_CHECK_COMPATIBLE_OPLIST(name, 2, value_t, value_oplist) \ + M_BPTR33_DEF_CORE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_BPTR33_DEF_IT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_BPTR33_DEF_EXT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF(M_INV(isMap), name, tree_t, key_oplist, value_oplist) + +/* Define the types of a B+Tree */ +#define M_BPTR33_DEF_TYPE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + M_IF(isMap)( \ + /* Type returned by the iterator. Due to having key and value \ + separated in their own array in the node, it is pointers to \ + the objects, not a global pointer to both objects. */ \ + typedef struct M_F(name, _pair_s) { \ + key_t *key_ptr; \ + value_t *value_ptr; \ + } subtype_t; \ + , \ + typedef key_t subtype_t; \ + ) \ + \ + /* Define a Node of a B+TREE \ + * For a reason of simplicity, it allocates one more element than \ + * needed so that the code can push one more element in the node and \ + * then split the nodes (simplify the code) \ + */ \ + typedef struct M_F(name, _node_s) { \ + int num; /* Abs=Number of keys. Sign <0 is leaf */ \ + key_t key[N+1]; /* We can temporary push one more key */ \ + struct M_F(name, _node_s) *next; /* next node reference */ \ + union M_F(name, _kind_s) { /* either value or pointer to other nodes */ \ + M_IF(isMap)(value_t value[N+1];,) \ + struct M_F(name, _node_s) *node[N+2]; \ + } kind; \ + } *node_t; \ + \ + /* A B+TREE is just a pointer to the root node */ \ + typedef struct M_F(name, _s) { \ + node_t root; \ + size_t size; \ + } tree_t[1]; \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* Definition of the alias used by the oplists */ \ + typedef subtype_t M_F(name, _subtype_ct); \ + typedef key_t M_F(name, _key_ct); \ + typedef value_t M_F(name, _value_ct); \ + typedef tree_t M_F(name, _ct); \ + \ + /* Define the Parent Tree Iterator */ \ + typedef struct M_F(name, _parent_it_s) { \ + int num; \ + node_t parent[M_BPTR33_MAX_STACK]; \ + } pit_t[1]; \ + \ + /* Define the Iterator */ \ + typedef struct M_F(name, _it_s) { \ + M_IF(isMap)(struct M_F(name, _pair_s) pair;,) \ + node_t node; \ + int idx; \ + } it_t[1]; \ + typedef it_t M_F(name, _it_ct); \ + +/* Define the core functions of a B+ Tree */ +#define M_BPTR33_DEF_CORE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + \ + /* Allocate a new node */ \ + /* TODO: Can be specialized to alloc for leaf or for non leaf */ \ + M_INLINE node_t M_F(name, _new_node)(void) \ + { \ + M_STATIC_ASSERT(N >= 2, M_LIB_ILLEGAL_PARAM, \ + "Number of items per node shall be >= 2."); \ + node_t n = M_CALL_NEW(key_oplist, struct M_F(name, _node_s)); \ + if (M_UNLIKELY_NOMEM (n == NULL)) { \ + M_MEMORY_FULL(sizeof (node_t)); \ + M_ASSERT (0); \ + } \ + n->next = NULL; \ + n->num = 0; \ + return n; \ + } \ + \ + M_INLINE void M_F(name, _init)(tree_t b) \ + { \ + b->root = M_F(name, _new_node)(); \ + b->size = 0; \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + } \ + \ + M_INLINE bool M_F(name, _is_leaf)(const node_t n) \ + { \ + /* We consider the empty node as a leaf */ \ + /* Only the root node can be empty */ \ + return n->num <= 0; \ + } \ + \ + /* Return the number of keys of the node */ \ + M_INLINE int M_F(name, _get_num)(const node_t n) \ + { \ + int num = n->num; \ + num = num < 0 ? -num : num; \ + M_ASSERT (num <= N); \ + return num; \ + } \ + \ + M_INLINE void M_F(name, _reset)(tree_t b) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + node_t next, n = b->root; \ + pit_t pit; \ + /* np is the heigh of the tree */ \ + int np = 0; \ + /* Scan down the nodes to the left down node */ \ + while (!M_F(name, _is_leaf)(n)) { \ + pit->parent[np++] = n; \ + M_ASSERT (np <= M_BPTR33_MAX_STACK); \ + n = n->kind.node[0]; \ + } \ + pit->parent[np++] = n; \ + M_ASSERT (np <= M_BPTR33_MAX_STACK); \ + /* Clean & free non root */ \ + for(int i = 0; i < np; i++) { \ + n = pit->parent[i]; \ + while (n != NULL) { \ + /* Clear key (& value for leaf) */ \ + int num = M_F(name, _get_num)(n); \ + M_IF(isMap)(bool is_leaf = M_F(name, _is_leaf)(n);,) \ + for(int j = 0; j < num; j++) { \ + M_CALL_CLEAR(key_oplist, n->key[j]); \ + M_IF(isMap)(if (is_leaf) { \ + M_CALL_CLEAR(value_oplist, n->kind.value[j]); \ + },) \ + } \ + /* Next node of the same height */ \ + next = n->next; \ + if (i != 0) { \ + /* Free the node if non root */ \ + M_CALL_DEL(key_oplist, n); \ + } \ + n = next; \ + } \ + } \ + /* Clean root */ \ + b->root->num = 0; \ + b->size = 0; \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + } \ + \ + M_INLINE void M_F(name, _clear)(tree_t b) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + M_F(name, _reset)(b); \ + /* Once the tree is clean, only the root remains */ \ + M_CALL_DEL(key_oplist, b->root); \ + b->root = NULL; \ + } \ + \ + /* Copy recursively the node 'o' of root node 'root' */ \ + M_INLINE node_t M_F(name, _copy_node)(const node_t o, const node_t root) \ + { \ + node_t n = M_F(name, _new_node)(); \ + /* Set default number of keys and type to copy */ \ + n->num = o->num; \ + /* By default it is not linked to its brother. \ + Only the parent of this node can do it. It is fixed by it */ \ + n->next = NULL; \ + /* Get number of keys in the node and copy them */ \ + int num = M_F(name, _get_num)(o); \ + for(int i = 0; i < num; i++) { \ + M_CALL_INIT_SET(key_oplist, n->key[i], o->key[i]); \ + } \ + if (M_F(name, _is_leaf)(o)) { \ + /* Copy the associated values if it is a leaf and a MAP */ \ + M_IF(isMap)( \ + for(int i = 0; i < num; i++) { \ + M_CALL_INIT_SET(value_oplist, n->kind.value[i], o->kind.value[i]); \ + } \ + , /* End of isMap */) \ + } else { \ + /* Copy recursively the associated nodes if it is not a leaf */ \ + for(int i = 0; i <= num; i++) { \ + M_ASSERT(o->kind.node[i] != root); \ + n->kind.node[i] = M_F(name, _copy_node)(o->kind.node[i], root); \ + } \ + /* The copied nodes don't have their next field correct */ \ + /* Fix the next field for the copied nodes */ \ + for(int i = 0; i < num; i++) { \ + node_t current = n->kind.node[i]; \ + node_t next = n->kind.node[i+1]; \ + current->next = next; \ + /* Go down the tree up to the leaf \ + and fix the final 'next' link with the copied node */ \ + while (!M_F(name, _is_leaf)(current)) { \ + M_ASSERT(!M_F(name, _is_leaf)(next)); \ + current = current->kind.node[current->num]; \ + next = next->kind.node[0]; \ + current->next = next; \ + } \ + } \ + } \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, (o==root) ? n : root); \ + return n; \ + } \ + \ + M_INLINE void M_F(name, _init_set)(tree_t b, const tree_t o) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, o); \ + M_ASSERT (b != NULL); \ + /* Just copy recursively the root node */ \ + b->root = M_F(name, _copy_node)(o->root, o->root); \ + b->size = o->size; \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + } \ + \ + M_INLINE void M_F(name, _set)(tree_t b, const tree_t o) \ + { \ + /* NOTE: We could reuse the already allocated nodes of 'b'. \ + Not sure if it worth the effort */ \ + M_F(name, _clear)(b); \ + M_F(name, _init_set)(b, o); \ + } \ + \ + M_INLINE bool M_F(name, _empty_p)(const tree_t b) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + /* root shall be an empty leaf */ \ + return b->size == 0; \ + } \ + \ + M_INLINE size_t M_F(name, _size)(const tree_t b) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + return b->size; \ + } \ + \ + M_INLINE node_t M_F(name, _search_for_leaf)(pit_t pit, const tree_t b, key_t const key) \ + { \ + node_t n = b->root; \ + int np = 0; \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \ + /* Go down the tree while searching for key */ \ + while (!M_F(name, _is_leaf)(n)) { \ + M_ASSERT (np <= M_BPTR33_MAX_STACK); \ + int i, hi = n->num; \ + M_ASSERT (hi > 0); \ + /* Linear search is usually faster than binary search for \ + B+TREE (due to cache effect). If a binary tree is faster for \ + the choosen type and size , it probably means that the \ + size of B+TREE is too big and should be reduced. */ \ + for(i = 0; i < hi; i++) { \ + if (M_CALL_CMP(key_oplist, key, n->key[i]) <= 0) \ + break; \ + } \ + /* Update the Parent iterator */ \ + pit->parent[np++] = n; \ + /* Select the new node to go down to */ \ + n = n->kind.node[i]; \ + M_ASSERT (n != NULL); \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \ + } \ + pit->num = np; \ + return n; \ + } \ + \ + M_INLINE value_t *M_F(name, _get)(const tree_t b, key_t const key) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + pit_t pit; \ + /* Get the leaf node where the key can be */ \ + node_t n = M_F(name, _search_for_leaf)(pit, b, key); \ + int cmp = 0; \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \ + /* Search in the leaf for key */ \ + for(int i = 0; cmp >= 0 && i < -n->num; i++) { \ + cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \ + if (cmp == 0) { \ + /* Return the value if MAP mode or the key if SET mode */ \ + return M_IF(isMap)(&n->kind.value[i],&n->key[i]); \ + } \ + } \ + /* Key not found */ \ + return NULL; \ + } \ + \ + M_INLINE value_t const *M_F(name, _cget)(const tree_t b, key_t const key) \ + { \ + return M_CONST_CAST(value_t, M_F(name, _get)(b, key)); \ + } \ + \ + M_INLINE int \ + M_F(name, _search_and_insert_in_leaf)(node_t n, key_t const key \ + M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \ + { \ + M_ASSERT (M_F(name, _is_leaf)(n)); \ + int i, num = M_F(name, _get_num)(n); \ + M_ASSERT (num <= N); \ + /* Search for the key in the node n (a leaf) for insertion */ \ + for(i = 0; i < num; i++) { \ + int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \ + if (cmp <= 0) { \ + M_IF(isMulti)( /* Nothing to do : fallthrough */, \ + /* Update value if keys are equal */ \ + if (M_UNLIKELY (cmp == 0)) { \ + M_IF(isMap)(M_CALL_SET(value_oplist, n->kind.value[i], value);,) \ + return -1; \ + } \ + ) \ + /* Move tables to make space for insertion */ \ + memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(unsigned int)(num-i)); \ + M_IF(isMap)(memmove(&n->kind.value[i+1], &n->kind.value[i], sizeof(value_t)*(unsigned int)(num-i));,) \ + break; \ + } \ + } \ + /* Insert key & value if MAP mode */ \ + M_CALL_INIT_SET(key_oplist, n->key[i], key); \ + M_IF(isMap)(M_CALL_INIT_SET(value_oplist, n->kind.value[i], value);,) \ + /* Increase the number of key in the node */ \ + n->num += -1; /* Increase num as num<0 for leaf */ \ + return i; \ + } \ + \ + M_INLINE int \ + M_F(name, _search_and_insert_in_node)(node_t n, node_t l, key_t key) \ + { \ + M_ASSERT (!M_F(name, _is_leaf)(n)); \ + int i, num = M_F(name, _get_num)(n); \ + M_ASSERT (num <= N); \ + /* Search for the key in the node n (not a leaf) for insertion */ \ + for(i = 0; i < num; i++) { \ + if (n->kind.node[i] == l) { \ + /* Move tables to make space for insertion */ \ + memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(unsigned int)(num-i)); \ + memmove(&n->kind.node[i+1], &n->kind.node[i], sizeof(node_t)*(unsigned int)(num-i+1)); \ + break; \ + } \ + } \ + /* Insert key in node */ \ + /* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \ + M_CALL_INIT_SET(key_oplist, n->key[i], key); \ + /* Increase the number of key in the node */ \ + n->num += 1; \ + return i; \ + } \ + \ + M_INLINE void \ + M_IF(isMap)(M_F(name, _set_at),M_F(name,_push))(tree_t b, key_t const key \ + M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \ + { \ + pit_t pit; \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + node_t leaf = M_F(name, _search_for_leaf)(pit, b, key); \ + /* Insert key into the leaf.*/ \ + /* NOTE: Even if there is N elements, we can still add one more.*/ \ + int i = M_F(name, _search_and_insert_in_leaf)(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \ + if (i < 0) { \ + /* Nothing to do anymore. key already exists in the tree. \ + value has been updated if needed */ \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + return; \ + } \ + b->size ++; \ + /* Most likely case: leaf can accept key */ \ + int num = -leaf->num; \ + M_ASSERT (num > 0); \ + if (M_LIKELY (num <= N)) { \ + /* nothing more to do */ \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + return; \ + } \ + M_ASSERT (num == N+1); \ + \ + /* Needs to rebalance the B+TREE */ \ + /* leaf is full: need to slip the leaf in two */ \ + int nnum = (N + 1) / 2; \ + num = N + 1 - nnum; \ + node_t nleaf = M_F(name, _new_node)(); \ + /* Move half objects to the new node */ \ + memmove(&nleaf->key[0], &leaf->key[num], sizeof(key_t)*(unsigned int)nnum); \ + M_IF(isMap)(memmove(&nleaf->kind.value[0], &leaf->kind.value[num], sizeof(value_t)*(unsigned int)nnum);,) \ + leaf->num = -num; \ + nleaf->num = -nnum; \ + nleaf->next = leaf->next; \ + leaf->next = nleaf; \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, leaf, b->root); \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, nleaf, b->root); \ + /* Update parent to inject *key_ptr that splits between (leaf, nleaf) */ \ + key_t *key_ptr = &leaf->key[num-1]; \ + while (true) { \ + if (pit->num == 0) { \ + /* We reach root ==> Need to increase the height of the tree.*/ \ + node_t parent = M_F(name, _new_node)(); \ + parent->num = 1; \ + /* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \ + M_CALL_INIT_SET(key_oplist, parent->key[0], *key_ptr); \ + parent->kind.node[0] = leaf; \ + parent->kind.node[1] = nleaf; \ + b->root = parent; \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + return; \ + } \ + /* Non root node. Get the parent node */ \ + node_t parent = pit->parent[--pit->num]; \ + /* Insert into parent (It is big enough to receive temporary one more) */ \ + i = M_F(name, _search_and_insert_in_node)(parent, leaf, *key_ptr); \ + parent->kind.node[i] = leaf; \ + parent->kind.node[i+1] = nleaf; \ + /* Test if parent node is full? */ \ + if (M_LIKELY (parent->num <= N)) { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + return; /* No need to split parent.*/ \ + } \ + M_ASSERT (parent->num == N+1); \ + /* Need to split parent in {np} {med} {nnp} */ \ + int nnp = N / 2; \ + int np = N - nnp; \ + M_ASSERT (nnp > 0 && np > 0 && nnp+np+1 == N+1); \ + node_t nparent = M_F(name, _new_node)(); \ + /* Move half items to new node (Like a classic B-TREE) \ + and the median key to the grand-parent*/ \ + memmove(&nparent->key[0], &parent->key[np+1], sizeof(key_t)*(unsigned int)nnp); \ + memmove(&nparent->kind.node[0], &parent->kind.node[np+1], sizeof(node_t)*(unsigned int)(nnp+1)); \ + parent->num = np; \ + nparent->num = nnp; \ + nparent->next = parent->next; \ + parent->next = nparent; \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, parent, b->root); \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, nparent, b->root); \ + /* Prepare for the next step */ \ + key_ptr = &parent->key[np]; \ + leaf = parent; \ + nleaf = nparent; \ + } \ + } \ + \ + M_INLINE value_t *M_F(name, _safe_get)(tree_t b, key_t const key) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + /* Not optimized implementation */ \ + value_t *ret = M_F(name, _get)(b, key); \ + if (ret == NULL) { \ + M_IF(isMap)( \ + value_t v; \ + M_CALL_INIT(value_oplist, v); \ + M_F(name, _set_at)(b, key, v); \ + M_CALL_CLEAR(value_oplist, v); \ + , \ + M_F(name, _push)(b, key); \ + ) \ + ret = M_F(name, _get)(b, key); \ + } \ + return ret; \ + } \ + \ + M_INLINE int \ + M_F(name, _search_and_remove_in_leaf)(node_t n, key_t const key) \ + { \ + M_ASSERT(M_F(name, _is_leaf)(n)); \ + const int num = M_F(name, _get_num)(n); \ + for(int i = 0; i < num; i++) { \ + const int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \ + if (cmp == 0) { \ + /* found key ==> delete it */ \ + M_CALL_CLEAR(key_oplist, n->key[i]); \ + M_IF(isMap)(M_CALL_CLEAR(value_oplist, n->kind.value[i]);,) \ + memmove(&n->key[i], &n->key[i+1], sizeof(key_t)*(unsigned int)(num-1-i)); \ + M_IF(isMap)(memmove(&n->kind.value[i], &n->kind.value[i+1], sizeof(value_t)*(unsigned int)(num-1-i));,) \ + n->num -= -1; /* decrease number as num is < 0 */ \ + return i; \ + } \ + } \ + return -1; /* Not found */ \ + } \ + \ + M_INLINE void M_F(name, _left_shift)(node_t parent, int k) \ + { \ + M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \ + M_ASSERT (0 <= k && k < M_F(name, _get_num)(parent)); \ + node_t left = parent->kind.node[k]; \ + node_t right = parent->kind.node[k+1]; \ + M_ASSERT (left != NULL && right != NULL); \ + int num_left = M_F(name, _get_num)(left); \ + int num_right = M_F(name, _get_num)(right); \ + M_ASSERT (num_left > N/2); \ + M_ASSERT (num_right < N/2); \ + \ + /* Move one item from the left node to the right node */ \ + memmove(&right->key[1], &right->key[0], sizeof(key_t)*(unsigned int)num_right); \ + if (M_F(name, _is_leaf)(left)) { \ + M_IF(isMap)(memmove (&right->kind.value[1], &right->kind.value[0], sizeof(value_t)*(unsigned int)num_right);,) \ + memmove (&right->key[0], &left->key[num_left-1], sizeof (key_t)); \ + M_IF(isMap)(memmove (&right->kind.value[0], &left->kind.value[num_left-1], sizeof (value_t));,) \ + right->num = -num_right - 1; \ + left->num = -num_left + 1; \ + M_CALL_SET(key_oplist, parent->key[k], left->key[num_left-2]); \ + } else { \ + memmove(&right->kind.node[1], &right->kind.node[0], sizeof(node_t)*(unsigned int)(num_right+1)); \ + /* parent[k] is moved to right[0] (clear). parent[k] is therefore clear */ \ + memmove(&right->key[0], &parent->key[k], sizeof(key_t)); \ + right->kind.node[0] = left->kind.node[num_left]; \ + right->num = num_right + 1; \ + left->num = num_left - 1; \ + /* left[n-1] is move to parent[k] (clear). left[n-1] is therefore clear */ \ + memmove(&parent->key[k], &left->key[num_left-1], sizeof (key_t)); \ + } \ + M_ASSERT (right->num != 0); \ + M_ASSERT (left->num != 0); \ + } \ + \ + M_INLINE void M_F(name, _right_shift)(node_t parent, int k) \ + { \ + M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \ + M_ASSERT (0 <= k && k < M_F(name, _get_num)(parent)); \ + node_t left = parent->kind.node[k]; \ + node_t right = parent->kind.node[k+1]; \ + M_ASSERT (left != NULL && right != NULL); \ + int num_left = M_F(name, _get_num)(left); \ + int num_right = M_F(name, _get_num)(right); \ + M_ASSERT (num_left < N/2); \ + M_ASSERT (num_right > N/2); \ + \ + /* Move one item from the right node to the left node. */ \ + if (M_F(name, _is_leaf)(right)) { \ + memmove (&left->key[num_left], &right->key[0], sizeof(key_t)); \ + memmove (&right->key[0], &right->key[1], sizeof(key_t)*(unsigned int)(num_right-1)); \ + M_IF(isMap)(memmove (&left->kind.value[num_left], &right->kind.value[0], sizeof (value_t));,) \ + M_IF(isMap)(memmove (&right->kind.value[0], &right->kind.value[1], sizeof(value_t)*(unsigned int)(num_right-1));,) \ + right->num = -num_right + 1; \ + left->num = -num_left - 1; \ + M_CALL_SET(key_oplist, parent->key[k], left->key[num_left]); \ + } else { \ + memmove (&left->key[num_left], &parent->key[k], sizeof (key_t)); \ + memmove (&parent->key[k], &right->key[0], sizeof (key_t)); \ + memmove (&right->key[0], &right->key[1], sizeof(key_t)*(unsigned int)(num_right-1)); \ + left->kind.node[num_left+1] = right->kind.node[0]; \ + memmove (&right->kind.node[0], &right->kind.node[1], sizeof(node_t)*(unsigned int)num_right); \ + right->num = num_right - 1; \ + left->num = num_left + 1; \ + } \ + M_ASSERT (right->num != 0); \ + M_ASSERT (left->num != 0); \ + } \ + \ + M_INLINE void M_F(name, _merge_node)(node_t parent, int k, bool leaf) \ + { \ + M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \ + M_ASSERT (0 <= k && k < M_F(name, _get_num(parent))); \ + node_t left = parent->kind.node[k]; \ + node_t right = parent->kind.node[k+1]; \ + M_ASSERT (left != NULL && right != NULL); \ + int num_parent = M_F(name, _get_num)(parent); \ + int num_left = M_F(name, _get_num)(left); \ + int num_right = M_F(name, _get_num)(right); \ + \ + /* Merge node 'k' and 'k+1' into a single one */ \ + if (leaf) { \ + M_ASSERT (num_left + num_right <= N); \ + memmove(&left->key[num_left], &right->key[0], sizeof(key_t)*(unsigned int)num_right); \ + M_IF(isMap)(memmove(&left->kind.value[num_left], &right->kind.value[0], sizeof(value_t)*(unsigned int)num_right);,) \ + left->num = -num_left - num_right; \ + } else { \ + M_ASSERT (num_left + num_right <= N -1); \ + memmove(&left->key[num_left+1], &right->key[0], sizeof(key_t)*(unsigned int)num_right); \ + memmove(&left->kind.node[num_left+1], &right->kind.node[0], sizeof(node_t)*(unsigned int)(num_right+1)); \ + M_CALL_INIT_SET(key_oplist, left->key[num_left], parent->key[k]); \ + left->num = num_left + 1 + num_right; \ + } \ + left->next = right->next; \ + M_CALL_DEL(key_oplist, right); \ + /* remove k'th key from the parent */ \ + M_CALL_CLEAR(key_oplist, parent->key[k]); \ + memmove(&parent->key[k], &parent->key[k+1], sizeof(key_t)*(unsigned int)(num_parent - k - 1)); \ + memmove(&parent->kind.node[k+1], &parent->kind.node[k+2], sizeof(node_t)*(unsigned int)(num_parent - k -1)); \ + parent->num --; \ + } \ + \ + /* We can also cache the index when we descend the tree. \ + TODO: Bench if this is worth the effort.*/ \ + M_INLINE int \ + M_F(name, _search_for_node)(node_t parent, node_t child) \ + { \ + M_ASSERT (!M_F(name, _is_leaf)(parent)); \ + int i = 0; \ + while (true) { \ + M_ASSERT(i <= M_F(name, _get_num)(parent)); \ + if (parent->kind.node[i] == child) \ + return i; \ + i++; \ + } \ + } \ + \ + M_INLINE bool M_F(name, _erase)(tree_t b, key_t const key) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + pit_t pit; \ + node_t leaf = M_F(name, _search_for_leaf)(pit, b, key); \ + int k = M_F(name, _search_and_remove_in_leaf)(leaf, key); \ + /* If key is not found ==> erase failed */ \ + if (k < 0) return false; \ + /* Remove one item from the B+TREE */ \ + b->size --; \ + /* If number of keys greater than N>2 or root ==> Nothing more to do */ \ + if (M_LIKELY (M_F(name, _get_num)(leaf) >= N/2) || pit->num == 0) \ + return true; \ + /* Leaf is too small. Needs rebalancing */ \ + M_ASSERT (M_F(name, _get_num)(leaf) == N/2-1); \ + bool pass1 = true; \ + while (true) { \ + M_ASSERT (pit->num > 0); \ + /* Search for node 'leaf' in parent */ \ + node_t parent = pit->parent[--pit->num]; \ + M_ASSERT (parent != NULL); \ + k = M_F(name, _search_for_node)(parent, leaf); \ + /* Look for the neighboor of the removed key. */ \ + /* if we can steal one key from them to keep our node balanced */ \ + if (k > 0 && M_F(name, _get_num)(parent->kind.node[k-1]) > N/2) { \ + M_F(name, _left_shift)(parent, k-1); \ + return true; \ + } else if (k < M_F(name, _get_num)(parent) \ + && M_F(name, _get_num)(parent->kind.node[k+1]) > N/2) { \ + M_F(name, _right_shift)(parent, k); \ + return true; \ + } \ + /* Merge both nodes, removing 'k' from parent */ \ + if (k == M_F(name, _get_num)(parent)) \ + k--; \ + M_ASSERT(k >= 0 && k < M_F(name, _get_num)(parent)); \ + /* Merge 'k' & 'k+1' & remove 'k' from parent */ \ + M_F(name, _merge_node)(parent, k, pass1); \ + /* Check if we need to continue */ \ + if (M_F(name, _get_num)(parent) >= N/2) \ + return true; \ + if (pit->num == 0) { \ + /* We reach the root */ \ + if (M_F(name, _get_num)(parent) == 0) { \ + /* Update root (deleted) */ \ + b->root = parent->kind.node[0]; \ + M_CALL_DEL(key_oplist, parent); \ + } \ + return true; \ + } \ + /* Next iteration */ \ + leaf = parent; \ + pass1 = false; \ + } \ + } \ + \ + M_INLINE bool M_F(name, _pop_at)(value_t *ptr, tree_t b, key_t const key) \ + { \ + if (ptr != NULL) { \ + value_t *ref = M_F(name, _get)(b, key); \ + if (ref == NULL) { \ + return false; \ + } \ + M_CALL_SET(value_oplist, *ptr, *ref); \ + } \ + return M_F(name, _erase)(b, key); \ + } \ + \ + M_INLINE value_t * \ + M_F(name, _min)(const tree_t b) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + if (M_UNLIKELY (b->size == 0)) return NULL; \ + node_t n = b->root; \ + /* Scan down the nodes */ \ + while (!M_F(name, _is_leaf)(n)) { \ + n = n->kind.node[0]; \ + } \ + return &n->M_IF(isMap)(kind.value, key)[0]; \ + } \ + \ + M_INLINE value_t * \ + M_F(name, _max)(const tree_t b) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + if (M_UNLIKELY (b->size == 0)) return NULL; \ + node_t n = b->root; \ + /* Scan down the nodes */ \ + while (!M_F(name, _is_leaf)(n)) { \ + n = n->kind.node[n->num]; \ + } \ + return &n->M_IF(isMap)(kind.value, key)[-n->num-1]; \ + } \ + \ + M_INLINE value_t const * \ + M_F(name, _cmin)(const tree_t tree) \ + { \ + return M_CONST_CAST(value_t, M_F(name, _min)(tree)); \ + } \ + \ + M_INLINE value_t const * \ + M_F(name, _cmax)(const tree_t tree) \ + { \ + return M_CONST_CAST(value_t, M_F(name, _max)(tree)); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(tree_t b, tree_t ref) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, ref); \ + M_ASSERT (b != NULL && b != ref); \ + b->size = ref->size; \ + b->root = ref->root; \ + ref->root = NULL; \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(tree_t b, tree_t ref) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, ref); \ + M_ASSERT (b != ref); \ + M_F(name,_clear)(b); \ + M_F(name,_init_move)(b, ref); \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(tree_t tree1, tree_t tree2) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree1); \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree2); \ + M_SWAP(size_t, tree1->size, tree2->size); \ + M_SWAP(node_t, tree1->root, tree2->root); \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree1); \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree2); \ + } \ + +/* Define iterator functions. */ +#define M_BPTR33_DEF_IT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + \ + M_INLINE void \ + M_F(name, _it)(it_t it, const tree_t b) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + M_ASSERT (it != NULL); \ + node_t n = b->root; \ + /* Scan down the nodes */ \ + while (!M_F(name, _is_leaf)(n)) { \ + n = n->kind.node[0]; \ + } \ + it->node = n; \ + it->idx = 0; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(it_t it, const tree_t b) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + M_ASSERT (it != NULL); \ + node_t n = b->root; \ + /* Scan down the nodes */ \ + while (!M_F(name, _is_leaf)(n)) { \ + n = n->kind.node[n->num]; \ + } \ + it->node = n; \ + it->idx = -n->num; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t itd, const it_t its) \ + { \ + M_ASSERT (itd != NULL && its != NULL); \ + itd->node = its->node; \ + itd->idx = its->idx; \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(it_t it) \ + { \ + M_ASSERT (it != NULL && it->node != NULL); \ + M_ASSERT (M_F(name, _is_leaf)(it->node)); \ + return it->node->next ==NULL && it->idx >= -it->node->num; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const it_t it1, const it_t it2) \ + { \ + return it1->node == it2->node && it1->idx == it2->idx; \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(it_t it) \ + { \ + M_ASSERT (it != NULL && it->node != NULL); \ + M_ASSERT (M_F(name, _is_leaf)(it->node)); \ + it->idx ++; \ + if (it->idx >= -it->node->num && it->node->next != NULL) { \ + it->node = it->node->next; \ + it->idx = 0; \ + } \ + } \ + \ + M_INLINE subtype_t * \ + M_F(name, _ref)(it_t it) \ + { \ + M_ASSERT (it != NULL && it->node != NULL); \ + M_ASSERT (M_F(name, _is_leaf)(it->node)); \ + M_ASSERT (it->idx <= -it->node->num); \ + M_IF(isMap)( \ + it->pair.key_ptr = &it->node->key[it->idx]; \ + it->pair.value_ptr = &it->node->kind.value[it->idx]; \ + return &it->pair \ + , \ + return &it->node->key[it->idx] \ + ); \ + } \ + \ + M_INLINE subtype_t const * \ + M_F(name, _cref)(it_t it) \ + { \ + return M_CONST_CAST(subtype_t, M_F(name, _ref)(it)); \ + } \ + \ + \ + M_INLINE void \ + M_F(name, _it_from)(it_t it, const tree_t b, key_t const key) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \ + M_ASSERT (it != NULL); \ + pit_t pit; \ + node_t n = M_F(name, _search_for_leaf)(pit, b, key); \ + it->node = n; \ + int i; \ + M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \ + for(i = 0; i < -n->num; i++) { \ + if (M_CALL_CMP(key_oplist, key, n->key[i]) <= 0) \ + break; \ + } \ + if (i == -n->num && n->next != NULL) { \ + it->node = n->next; \ + i = 0; \ + } \ + it->idx = i; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_until_p)(it_t it, key_t const key) \ + { \ + M_ASSERT (it != NULL); \ + node_t n = it->node; \ + if (it->idx >= -n->num) return true; \ + int cmp = M_CALL_CMP(key_oplist, n->key[it->idx], key); \ + return (cmp >= 0); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_while_p)(it_t it, key_t const key) \ + { \ + M_ASSERT (it != NULL); \ + node_t n = it->node; \ + if (it->idx >= -n->num) return false; \ + int cmp = M_CALL_CMP(key_oplist, n->key[it->idx], key); \ + return (cmp <= 0); \ + } \ + +/* Define additional functions. + Do not used any fields but the already defined methods */ +#define M_BPTR33_DEF_EXT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \ + \ + M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)( \ + M_INLINE bool M_F(name,_equal_p)(const tree_t t1, const tree_t t2) { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t2); \ + if (t1->size != t2->size) return false; \ + if (t1->size == 0) return true; \ + /* Slow comparaison */ \ + it_t it1; \ + it_t it2; \ + /* NOTE: We can't compare two trees directly as they can be \ + structuraly different but functionnaly equal (you get this by \ + constructing the tree in a different way). We have to \ + compare the ordered value within the tree. */ \ + M_F(name, _it)(it1, t1); \ + M_F(name, _it)(it2, t2); \ + while (!M_F(name, _end_p)(it1) \ + && !M_F(name, _end_p)(it2)) { \ + const subtype_t *ref1 = M_F(name, _cref)(it1); \ + const subtype_t *ref2 = M_F(name, _cref)(it2); \ + M_IF(isMap)( \ + if (!M_CALL_EQUAL(key_oplist, *ref1->key_ptr, *ref2->key_ptr)) \ + return false; \ + if (!M_CALL_EQUAL(value_oplist, *ref1->value_ptr, *ref2->value_ptr)) \ + return false; \ + , \ + if (!M_CALL_EQUAL(key_oplist, *ref1, *ref2)) \ + return false; \ + ) \ + M_F(name, _next)(it1); \ + M_F(name, _next)(it2); \ + } \ + return M_F(name, _end_p)(it1) \ + && M_F(name, _end_p)(it2); \ + } \ + , /* NO EQUAL METHOD */ ) \ + \ + M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)( \ + M_INLINE size_t M_F(name,_hash)(const tree_t t1) { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \ + M_HASH_DECL(hash); \ + /* NOTE: We can't compute the hash directly for the same reason \ + than for EQUAL operator. */ \ + it_t it1; \ + M_F(name, _it)(it1, t1); \ + while (!M_F(name, _end_p)(it1)) { \ + subtype_t const *ref1 = M_F(name, _cref)(it1); \ + M_IF(isMap)( \ + M_HASH_UP(hash, M_CALL_HASH(key_oplist, *ref1->key_ptr)); \ + M_HASH_UP(hash, M_CALL_HASH(value_oplist, *ref1->value_ptr)); \ + , \ + M_HASH_UP(hash, M_CALL_HASH(key_oplist, *ref1)); \ + ) \ + M_F(name, _next)(it1); \ + } \ + return M_HASH_FINAL (hash); \ + } \ + , /* NO HASH METHOD */ ) \ + \ + M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \ + M_INLINE void M_F(name, _get_str)(m_string_t str, \ + const tree_t t1, bool append) { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \ + M_ASSERT(str != NULL); \ + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \ + /* NOTE: The print is really naive, and not really efficient */ \ + bool commaToPrint = false; \ + it_t it; \ + for (M_F(name, _it)(it, t1) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)) { \ + if (commaToPrint) \ + m_string_push_back (str, M_GET_SEPARATOR key_oplist); \ + commaToPrint = true; \ + subtype_t const *ref1 = M_F(name, _cref)(it); \ + M_IF(isMap)( \ + M_CALL_GET_STR(key_oplist, str, *ref1->key_ptr, true); \ + m_string_cat_cstr(str, ":"); \ + M_CALL_GET_STR(value_oplist,str, *ref1->value_ptr, true) \ + , \ + M_CALL_GET_STR(key_oplist, str, *ref1, true); \ + ); \ + } \ + m_string_push_back (str, ']'); \ + } \ + , /* NO GET_STR */ ) \ + \ + M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)( \ + M_INLINE void \ + M_F(name, _out_str)(FILE *file, tree_t const t1) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \ + M_ASSERT (file != NULL); \ + fputc ('[', file); \ + bool commaToPrint = false; \ + it_t it; \ + for (M_F(name, _it)(it, t1) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + if (commaToPrint) \ + fputc (M_GET_SEPARATOR key_oplist, file); \ + commaToPrint = true; \ + subtype_t const *ref1 = M_F(name, _cref)(it); \ + M_IF(isMap)( \ + M_CALL_OUT_STR(key_oplist, file, *ref1->key_ptr); \ + fputc (':', file); \ + M_CALL_OUT_STR(value_oplist, file, *ref1->value_ptr) \ + , \ + M_CALL_OUT_STR(key_oplist, file, *ref1); \ + ); \ + } \ + fputc (']', file); \ + } \ + , /* no out_str */ ) \ + \ + M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)( \ + M_INLINE bool \ + M_F(name, _parse_str)(tree_t t1, const char str[], const char **endp) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \ + M_ASSERT (str != NULL); \ + M_F(name,_reset)(t1); \ + bool success = false; \ + int c = *str++; \ + if (M_UNLIKELY (c != '[')) goto exit; \ + c = *str++; \ + if (M_UNLIKELY (c == ']')) { success = true; goto exit;} \ + if (M_UNLIKELY (c == 0)) goto exit; \ + str--; \ + key_t key; \ + M_CALL_INIT(key_oplist, key); \ + M_IF(isMap)(value_t value; \ + M_CALL_INIT(value_oplist, value); \ + , /* No isMap */) \ + do { \ + bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \ + do { c = *str++; } while (isspace(c)); \ + if (b == false) goto exit_clear; \ + M_IF(isMap)(if (c != ':') goto exit_clear; \ + b = M_CALL_PARSE_STR(value_oplist, value, str, &str); \ + do { c = *str++; } while (isspace(c)); \ + if (b == false || c == 0) goto exit_clear; \ + M_F(name, _set_at)(t1, key, value); \ + , \ + M_F(name, _push)(t1, key); \ + ) \ + } while (c == M_GET_SEPARATOR key_oplist); \ + success = (c == ']'); \ + exit_clear: \ + M_CALL_CLEAR(key_oplist, key); \ + M_IF(isMap)(M_CALL_CLEAR(value_oplist, value); , /* No isMap */ ) \ + exit: \ + if (endp) *endp = str; \ + return success; \ + } \ + , /* no parse_str */ ) \ + \ + M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)( \ + M_INLINE bool \ + M_F(name, _in_str)(tree_t t1, FILE *file) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \ + M_ASSERT (file != NULL); \ + M_F(name,_reset)(t1); \ + int c = fgetc(file); \ + if (M_UNLIKELY (c != '[')) return false; \ + c = fgetc(file); \ + if (M_UNLIKELY (c == ']')) return true; \ + if (M_UNLIKELY (c == EOF)) return false; \ + ungetc(c, file); \ + key_t key; \ + M_CALL_INIT (key_oplist, key); \ + M_IF(isMap)(value_t value; \ + M_CALL_INIT (value_oplist, value); \ + ,) \ + do { \ + bool b = M_CALL_IN_STR(key_oplist, key, file); \ + do { c = fgetc(file); } while (isspace(c)); \ + if (b == false) break; \ + M_IF(isMap)(if (c!=':') break; \ + b = M_CALL_IN_STR(value_oplist,value, file); \ + do { c = fgetc(file); } while (isspace(c)); \ + if (b == false || c == EOF) break; \ + M_F(name, _set_at)(t1, key, value) \ + , \ + M_F(name, _push)(t1, key) \ + ); \ + } while (c == M_GET_SEPARATOR key_oplist); \ + M_CALL_CLEAR(key_oplist, key); \ + M_IF(isMap)(M_CALL_CLEAR(value_oplist, value); \ + ,) \ + return c == ']'; \ + } \ + , /* no in_str */ ) \ + \ + M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, tree_t const t1) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_return_code_t ret; \ + m_serial_local_t local; \ + subtype_t const *item; \ + bool first_done = false; \ + it_t it; \ + /* Format is different between associative container \ + & set container */ \ + M_IF(isMap)( \ + ret = f->m_interface->write_map_start(local, f, t1->size); \ + for (M_F(name, _it)(it, t1) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + item = M_F(name, _cref)(it); \ + if (first_done) \ + ret |= f->m_interface->write_map_next(local, f); \ + ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item->key_ptr); \ + ret |= f->m_interface->write_map_value(local, f); \ + ret |= M_CALL_OUT_SERIAL(value_oplist, f, *item->value_ptr); \ + first_done = true; \ + } \ + ret |= f->m_interface->write_map_end(local, f); \ + , \ + ret = f->m_interface->write_array_start(local, f, t1->size); \ + for (M_F(name, _it)(it, t1) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + item = M_F(name, _cref)(it); \ + if (first_done) \ + ret |= f->m_interface->write_array_next(local, f); \ + ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item); \ + first_done = true; \ + } \ + ret |= f->m_interface->write_array_end(local, f); \ + ) \ + return ret & M_SERIAL_FAIL; \ + } \ + , /* no OUT_SERIAL */ ) \ + \ + M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(tree_t t1, m_serial_read_t f) \ + { \ + M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + size_t estimated_size = 0; \ + key_t key; \ + M_F(name,_reset)(t1); \ + M_IF(isMap)( \ + value_t value; \ + ret = f->m_interface->read_map_start(local, f, &estimated_size); \ + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ + M_CALL_INIT(key_oplist, key); \ + M_CALL_INIT (value_oplist, value); \ + do { \ + ret = M_CALL_IN_SERIAL(key_oplist, key, f); \ + if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \ + ret = f->m_interface->read_map_value(local, f); \ + if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \ + ret = M_CALL_IN_SERIAL(value_oplist, value, f); \ + if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \ + M_F(name, _set_at)(t1, key, value); \ + } while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \ + M_CALL_CLEAR(key_oplist, key); \ + M_CALL_CLEAR(value_oplist, value); \ + , \ + ret = f->m_interface->read_array_start(local, f, &estimated_size); \ + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ + M_CALL_INIT(key_oplist, key); \ + do { \ + ret = M_CALL_IN_SERIAL(key_oplist, key, f); \ + if (ret != M_SERIAL_OK_DONE) { break; } \ + M_F(name, _push)(t1, key); \ + } while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \ + M_CALL_CLEAR(key_oplist, key); \ + ) /* End of IF isMap */ \ + return ret; \ + } \ + , /* no in_serial */ ) \ + + +/******************************** INTERNAL ***********************************/ + +#if M_USE_SMALL_NAME +#define BPTREE_DEF2 M_BPTREE_DEF2 +#define BPTREE_DEF2_AS M_BPTREE_DEF2_AS +#define BPTREE_DEF M_BPTREE_DEF +#define BPTREE_DEF_AS M_BPTREE_DEF_AS +#define BPTREE_MULTI_DEF2 M_BPTREE_MULTI_DEF2 +#define BPTREE_MULTI_DEF2_AS M_BPTREE_MULTI_DEF2_AS +#define BPTREE_MULTI_DEF M_BPTREE_MULTI_DEF +#define BPTREE_MULTI_DEF_AS M_BPTREE_MULTI_DEF_AS +#define BPTREE_OPLIST M_BPTREE_OPLIST +#define BPTREE_OPLIST2 M_BPTREE_OPLIST2 +#endif + +#endif diff --git a/components/mlib/m-buffer.h b/components/mlib/m-buffer.h new file mode 100644 index 00000000..a98630fa --- /dev/null +++ b/components/mlib/m-buffer.h @@ -0,0 +1,1350 @@ +/* + * M*LIB - Fixed size (Bounded) QUEUE & STACK interface + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_BUFFER_H +#define MSTARLIB_BUFFER_H + +#include "m-core.h" +#include "m-thread.h" +#include "m-atomic.h" + +/* Define the different kind of policy a lock-based buffer can have: + * - the buffer can be either a queue (policy is FIFO) or a stack (policy is FILO), + * - if the push method is by default blocking (waiting for the buffer to has some space) or not, *** deprecated *** + * - if the pop method is by default blocking (waiting for the buffer to has some data) or not, *** deprecated *** + * - if both methods are blocking, *** deprecated *** + * - if it shall be thread safe or not (i.e. remove the mutex lock and atomic costs), + * - if the buffer has to be init with empty elements, or if it shall init an element when it is pushed (and moved when popped), + * - if the buffer has to overwrite the last element if the buffer is full, + * - if the pop of an element is not complete until the call to pop_release (preventing push until this call). + */ +typedef enum { + M_BUFFER_QUEUE = 0, M_BUFFER_STACK = 1, + M_BUFFER_BLOCKING_PUSH = 0, M_BUFFER_UNBLOCKING_PUSH = 2, + M_BUFFER_BLOCKING_POP = 0, M_BUFFER_UNBLOCKING_POP = 4, + M_BUFFER_BLOCKING = 0, M_BUFFER_UNBLOCKING = 6, + M_BUFFER_THREAD_SAFE = 0, M_BUFFER_THREAD_UNSAFE = 8, + M_BUFFER_PUSH_INIT_POP_MOVE = 16, + M_BUFFER_PUSH_OVERWRITE = 32, + M_BUFFER_DEFERRED_POP = 64 +} m_buffer_policy_e; + + +/* Define a lock based buffer. + If size is 0, then the size will only be defined at run-time when initializing the buffer, + otherwise the size will be a compile time constant. + USAGE: BUFFER_DEF(name, type, size_of_buffer_or_0, policy[, oplist]) */ +#define M_BUFFER_DEF(name, type, m_size, ... ) \ + M_BUFFER_DEF_AS(name, M_F(name, _t), type, m_size, __VA_ARGS__) + + +/* Define a lock based buffer + as the provided type name_t. + USAGE: BUFFER_DEF_AS(name, name_t, type, size_of_buffer_or_0, policy[, oplist of type]) */ +#define M_BUFFER_DEF_AS(name, name_t, type, m_size, ... ) \ + M_BEGIN_PROTECTED_CODE \ + M_BUFF3R_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, type, m_size,__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), name_t ), \ + (name, type, m_size,__VA_ARGS__, name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a lock based buffer given its name and its oplist. + USAGE: BUFFER_OPLIST(name[, oplist of the type]) */ +#define M_BUFFER_OPLIST(...) \ + M_BUFF3R_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST), \ + (__VA_ARGS__ ))) + + +/* Define a nearly lock-free queue for Many Producer Many Consummer. + Much faster than queue of BUFFER_DEF in heavy communication scenario + but without any blocking features (this is let to the user). + Size of created queue shall be a power of 2 and is defined at run-time. + USAGE: QUEUE_MPMC_DEF(name, type, policy, [oplist of type]) +*/ +#define M_QUEUE_MPMC_DEF(name, type, ...) \ + M_QUEUE_MPMC_DEF_AS(name, M_F(name,_t), type, __VA_ARGS__) + + +/* Define a nearly lock-free queue for Many Producer Many Consummer + as the provided type name_t. + Much faster than queue of BUFFER_DEF in heavy communication scenario + but without any blocking features (this is let to the user). + Size of created queue shall be a power of 2 and is defined at run-time. + USAGE: QUEUE_MPMC_DEF_AS(name, name_t, type, policy, [oplist of type]) +*/ +#define M_QUEUE_MPMC_DEF_AS(name, name_t, type, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_QU3UE_MPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), name_t ), \ + (name, type, __VA_ARGS__, name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a wait-free queue for Single Producer Single Consummer + Much faster than queue of BUFFER_DEF or QUEUE_MPMC in heavy communication scenario + but without any blocking features (this is let to the user). + Size of created queue shall be a power of 2 and is defined at run-time. + USAGE: QUEUE_SPSC_DEF(name, type, policy, [oplist of type]) +*/ +#define M_QUEUE_SPSC_DEF(name, type, ...) \ + M_QUEUE_SPSC_DEF_AS(name, M_F(name, _t), type, __VA_ARGS__) + + +/* Define a wait-free queue for Single Producer Single Consummer + as the provided type name_t. + Much faster than queue of BUFFER_DEF in heavy communication scenario + but without any blocking features (this is let to the user). + Size of created queue shall be a power of 2 and is defined at run-time. + USAGE: QUEUE_SPSC_DEF_AS(name, name_t, type, policy, [oplist of type]) +*/ +#define M_QUEUE_SPSC_DEF_AS(name, name_t, type, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_QU3UE_SPSC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), name_t ), \ + (name, type, __VA_ARGS__, name_t ))) \ + M_END_PROTECTED_CODE + + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* Test if the given policy is true or not. + WARNING: The policy shall be a non zero value (i.e. not a default). */ +#define M_BUFF3R_POLICY_P(policy, val) \ + (((policy) & (val)) != 0) + +/* Handle either atomic integer or normal integer in function of the policy + parameter of the buffer BUFFER_THREAD_UNSAFE (BUFFER_THREAD_SAFE is the + default). This enables avoiding to pay the cost of atomic operations if not + applicable. + */ +typedef union m_buff3r_number_s { + unsigned int u; + atomic_uint a; +#ifdef __cplusplus + // Not sure why, but C++ needs an explicit default constructor for this union. + m_buff3r_number_s() : u(0) {}; +#endif +} m_buff3r_number_ct[1]; + +M_INLINE void +m_buff3r_number_init(m_buff3r_number_ct n, unsigned int policy) +{ + if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE)) + atomic_init(&n->a, 0U); + else + n->u = 0UL; +} + +M_INLINE unsigned int +m_buff3r_number_load(m_buff3r_number_ct n, unsigned int policy) +{ + if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE)) + // Perform a memory acquire so that further usage of the buffer + // is synchronized. + return atomic_load_explicit(&n->a, memory_order_acquire); + else + return n->u; +} + +M_INLINE void +m_buff3r_number_store(m_buff3r_number_ct n, unsigned int v, unsigned int policy) +{ + if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE)) + // This function is used in context where a relaxed access is sufficient. + atomic_store_explicit(&n->a, v, memory_order_relaxed); + else + n->u = v; +} + +M_INLINE void +m_buff3r_number_set(m_buff3r_number_ct n, m_buff3r_number_ct v, unsigned int policy) +{ + if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE)) + // This function is used in context where a relaxed access is sufficient. + atomic_store_explicit(&n->a, atomic_load_explicit(&v->a, memory_order_relaxed), memory_order_relaxed); + else + n->u = v->u; +} + +M_INLINE unsigned int +m_buff3r_number_inc(m_buff3r_number_ct n, unsigned int policy) +{ + if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE)) + return atomic_fetch_add(&n->a, 1U); + else + return n->u ++; +} + +M_INLINE unsigned int +m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy) +{ + if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE)) + return atomic_fetch_sub(&n->a, 1U); + else + return n->u --; +} + +/********************************** INTERNAL *********************************/ + +/* Test if the size is only run-time or build time */ +#define M_BUFF3R_IF_CTE_SIZE(m_size) M_IF(M_BOOL(m_size)) + +/* Return the size (run time or build time). + NOTE: It assumed that the buffer variable name is 'v' */ +#define M_BUFF3R_SIZE(m_size) \ + M_BUFF3R_IF_CTE_SIZE(m_size) (m_size, v->capacity) + +/* Contract of a buffer. + Nothing particular since we cannot test much without locking it. +*/ +#define M_BUFF3R_CONTRACT(buffer, size) do { \ + M_ASSERT (buffer != NULL); \ + M_ASSERT (buffer->data != NULL); \ + }while (0) + +/* Contract of a buffer within a protected section */ +#define M_BUFF3R_PROTECTED_CONTRACT(policy, buffer, size) do { \ + M_ASSERT (m_buff3r_number_load(buffer->number[0], policy) <= M_BUFF3R_SIZE(size)); \ + } while (0) + + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_BUFF3R_DEF_P1(arg) M_ID( M_BUFF3R_DEF_P2 arg ) + +/* Validate the value oplist before going further */ +#define M_BUFF3R_DEF_P2(name, type, m_size, policy, oplist, buffer_t) \ + M_IF_OPLIST(oplist)(M_BUFF3R_DEF_P3, M_BUFF3R_DEF_FAILURE)(name, type, m_size, policy, oplist, buffer_t) + +/* Stop processing with a compilation failure */ +#define M_BUFF3R_DEF_FAILURE(name, type, m_size, policy, oplist, buffer_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(M_BUFFER_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist)) + +/* Define the buffer type using mutex lock and its functions. + - name: main prefix of the container + - type: type of an element of the buffer + - m_size: constant to 0 if variable runtime size, or else the fixed size of the buffer + - policy: the policy of the buffer + - oplist: the oplist of the type of an element of the buffer + - buffer_t: name of the buffer + */ +#define M_BUFF3R_DEF_P3(name, type, m_size, policy, oplist, buffer_t) \ + M_BUFF3R_DEF_TYPE(name, type, m_size, policy, oplist, buffer_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_BUFF3R_DEF_CORE(name, type, m_size, policy, oplist, buffer_t) \ + M_EMPLACE_QUEUE_DEF(name, buffer_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE) + +/* Define the type of a buffer */ +#define M_BUFF3R_DEF_TYPE(name, type, m_size, policy, oplist, buffer_t) \ + \ + /* Put each data in a separate cache line to avoid false sharing \ + by multiple writing threads. No need to align if there is no thread */ \ + typedef union M_F(name, _el_s) { \ + type x; \ + char align[M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE) ? 1 : M_ALIGN_FOR_CACHELINE_EXCLUSION]; \ + } M_F(name, _el_ct); \ + \ + typedef struct M_F(name, _s) { \ + /* Data for a producer */ \ + m_mutex_t mutexPush; /* MUTEX used for pushing elements */ \ + size_t idx_prod; /* Index of the production threads */ \ + size_t overwrite; /* Number of overwritten values */ \ + m_cond_t there_is_data; /* condition raised when there is data */ \ + /* Read only Data */ \ + M_BUFF3R_IF_CTE_SIZE(m_size)( ,size_t capacity;) /* Capacity of the buffer */ \ + /* Data for a consummer */ \ + m_cond_t there_is_room_for_data; /* Cond. raised when there is room */ \ + m_mutex_t mutexPop; /* MUTEX used for popping elements */ \ + size_t idx_cons; /* Index of the consumption threads */ \ + /* number[0] := Number of elements in the buffer */ \ + /* number[1] := [OPTION] Number of elements being deferred in the buffer */ \ + m_buff3r_number_ct number[1 + M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)]; \ + /* If fixed size, array of elements, otherwise pointer to element */ \ + M_F(name, _el_ct) M_BUFF3R_IF_CTE_SIZE(m_size)(data[m_size], *data); \ + } buffer_t[1]; \ + \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + /* Internal type used to unconst the buffer */ \ + typedef union { M_F(name, _srcptr) cptr; M_F(name, _ptr) ptr; } M_F(name, _uptr_ct); \ + /* Internal types used by the oplist */ \ + typedef type M_F(name, _subtype_ct); \ + typedef buffer_t M_F(name, _ct); \ + +/* Define the core functionnalities of a buffer */ +#define M_BUFF3R_DEF_CORE(name, type, m_size, policy, oplist, buffer_t) \ + \ +M_INLINE void \ +M_F(name, _init)(buffer_t v, size_t size) \ +{ \ + M_ASSERT(size <= UINT_MAX); \ + M_BUFF3R_IF_CTE_SIZE(m_size)(M_ASSERT(size == m_size), v->capacity = size); \ + v->idx_prod = v->idx_cons = v->overwrite = 0; \ + m_buff3r_number_init (v->number[0], policy); \ + if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \ + m_buff3r_number_init (v->number[1], policy); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_init(v->mutexPush); \ + m_mutex_init(v->mutexPop); \ + m_cond_init(v->there_is_data); \ + m_cond_init(v->there_is_room_for_data); \ + } else { \ + M_ASSERT(M_BUFF3R_POLICY_P((policy), M_BUFFER_UNBLOCKING)); \ + } \ + \ + M_BUFF3R_IF_CTE_SIZE(m_size)( /* Statically allocated */ , \ + v->data = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, M_BUFF3R_SIZE(m_size)); \ + if (M_UNLIKELY_NOMEM (v->data == NULL)) { \ + M_MEMORY_FULL (M_BUFF3R_SIZE(m_size)*sizeof(M_F(name, _el_ct))); \ + return; \ + } \ + ) \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + for(size_t i = 0; i < size; i++) { \ + M_CALL_INIT(oplist, v->data[i].x); \ + } \ + } \ + M_BUFF3R_CONTRACT(v,m_size); \ +} \ + \ + M_BUFF3R_IF_CTE_SIZE(m_size)( \ + M_INLINE void \ + M_C3(m_buff3r_,name,_init)(buffer_t v) \ + { \ + M_F(name, _init)(v, m_size); \ + } \ + , ) \ + \ + M_INLINE void \ + M_C3(m_buff3r_,name,_clear_obj)(buffer_t v) \ + { \ + M_BUFF3R_CONTRACT(v,m_size); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + for(size_t i = 0; i < M_BUFF3R_SIZE(m_size); i++) { \ + M_CALL_CLEAR(oplist, v->data[i].x); \ + } \ + } else { \ + size_t i = M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK) ? 0 : v->idx_cons; \ + while (i != v->idx_prod) { \ + M_CALL_CLEAR(oplist, v->data[i].x); \ + i++; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK) && i >= M_BUFF3R_SIZE(m_size)) \ + i = 0; \ + } \ + } \ + v->idx_prod = v->idx_cons = 0; \ + m_buff3r_number_store (v->number[0], 0U, policy); \ + if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \ + m_buff3r_number_store(v->number[1], 0U, policy); \ + M_BUFF3R_CONTRACT(v,m_size); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(buffer_t v) \ + { \ + M_BUFF3R_CONTRACT(v,m_size); \ + M_C3(m_buff3r_,name,_clear_obj)(v); \ + M_BUFF3R_IF_CTE_SIZE(m_size)( , \ + M_CALL_FREE(oplist, v->data); \ + v->data = NULL; \ + ) \ + v->overwrite = 0; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_clear(v->mutexPush); \ + m_mutex_clear(v->mutexPop); \ + m_cond_clear(v->there_is_data); \ + m_cond_clear(v->there_is_room_for_data); \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(buffer_t v) \ + { \ + M_BUFF3R_CONTRACT(v,m_size); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_lock(v->mutexPush); \ + m_mutex_lock(v->mutexPop); \ + } \ + M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \ + if (M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) \ + M_C3(m_buff3r_,name,_clear_obj)(v); \ + v->idx_prod = v->idx_cons = 0; \ + m_buff3r_number_store (v->number[0], 0U, policy); \ + if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \ + m_buff3r_number_store(v->number[1], 0U, policy); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_cond_broadcast(v->there_is_room_for_data); \ + m_mutex_unlock(v->mutexPop); \ + m_mutex_unlock(v->mutexPush); \ + } \ + M_BUFF3R_CONTRACT(v,m_size); \ + } \ + \ + M_INLINE void M_ATTR_DEPRECATED \ + M_F(name, _clean)(buffer_t v) \ + { \ + M_F(name,_reset)(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(buffer_t dest, const buffer_t src) \ + { \ + /* unconst 'src', so that we can lock it (semantically it is const) */ \ + M_F(name, _uptr_ct) vu; \ + vu.cptr = src; \ + M_F(name, _ptr) v = vu.ptr; \ + M_ASSERT (dest != v); \ + M_BUFF3R_CONTRACT(v,m_size); \ + M_F(name, _init)(dest, M_BUFF3R_SIZE(m_size)); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_lock(v->mutexPush); \ + m_mutex_lock(v->mutexPop); \ + } \ + \ + M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + for(size_t i = 0; i < M_BUFF3R_SIZE(m_size); i++) { \ + M_CALL_INIT_SET(oplist, dest->data[i].x, v->data[i].x); \ + } \ + } else { \ + size_t i = M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK) ? 0 : v->idx_cons; \ + while (i != v->idx_prod) { \ + M_CALL_INIT_SET(oplist, dest->data[i].x, v->data[i].x); \ + i++; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK) && i >= M_BUFF3R_SIZE(m_size)) \ + i = 0; \ + } \ + } \ + \ + dest->idx_prod = v->idx_prod; \ + dest->idx_cons = v->idx_cons; \ + m_buff3r_number_set (dest->number[0], v->number[0], policy); \ + if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \ + m_buff3r_number_set(dest->number[1], v->number[1], policy); \ + \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_unlock(v->mutexPop); \ + m_mutex_unlock(v->mutexPush); \ + } \ + M_BUFF3R_CONTRACT(v,m_size); \ + M_BUFF3R_CONTRACT(dest, m_size); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(buffer_t dest, const buffer_t src) \ + { \ + /* unconst 'src', so that we can lock it (semantically it is const) */ \ + M_F(name, _uptr_ct) vu; \ + vu.cptr = src; \ + M_F(name, _ptr) v = vu.ptr; \ + M_BUFF3R_CONTRACT(dest,m_size); \ + M_BUFF3R_CONTRACT(v,m_size); \ + \ + if (dest == v) return; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + /* Case of deadlock: A := B, B:=C, C:=A (all in //) \ + Solution: order the lock by increasing memory */ \ + if (dest < v) { \ + m_mutex_lock(dest->mutexPush); \ + m_mutex_lock(dest->mutexPop); \ + m_mutex_lock(v->mutexPush); \ + m_mutex_lock(v->mutexPop); \ + } else { \ + m_mutex_lock(v->mutexPush); \ + m_mutex_lock(v->mutexPop); \ + m_mutex_lock(dest->mutexPush); \ + m_mutex_lock(dest->mutexPop); \ + } \ + } \ + \ + M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \ + M_C3(m_buff3r_,name,_clear_obj)(dest); \ + \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + for(size_t i = 0; i < M_BUFF3R_SIZE(m_size); i++) { \ + M_CALL_INIT_SET(oplist, dest->data[i].x, v->data[i].x); \ + } \ + } else { \ + size_t i = M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK) ? 0 : v->idx_cons; \ + while (i != v->idx_prod) { \ + M_CALL_INIT_SET(oplist, dest->data[i].x, v->data[i].x); \ + i++; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK) && i >= M_BUFF3R_SIZE(m_size)) \ + i = 0; \ + } \ + } \ + \ + dest->idx_prod = v->idx_prod; \ + dest->idx_cons = v->idx_cons; \ + m_buff3r_number_set (dest->number[0], v->number[0], policy); \ + if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \ + m_buff3r_number_set(dest->number[1], v->number[1], policy); \ + \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + /* It may be false, but it is not wrong! */ \ + m_cond_broadcast(v->there_is_room_for_data); \ + m_cond_broadcast(v->there_is_data); \ + if (dest < v) { \ + m_mutex_unlock(v->mutexPop); \ + m_mutex_unlock(v->mutexPush); \ + m_mutex_unlock(dest->mutexPop); \ + m_mutex_unlock(dest->mutexPush); \ + } else { \ + m_mutex_unlock(dest->mutexPop); \ + m_mutex_unlock(dest->mutexPush); \ + m_mutex_unlock(v->mutexPop); \ + m_mutex_unlock(v->mutexPush); \ + } \ + } \ + M_BUFF3R_CONTRACT(v,m_size); \ + M_BUFF3R_CONTRACT(dest, m_size); \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(buffer_t v) \ + { \ + M_BUFF3R_CONTRACT(v,m_size); \ + /* If the buffer has been configured with deferred pop \ + we considered the queue as empty when the number of \ + deferred pop has reached 0, not the number of items in the \ + buffer is 0. */ \ + if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \ + return m_buff3r_number_load (v->number[1], policy) == 0; \ + else \ + return m_buff3r_number_load (v->number[0], policy) == 0; \ + } \ + \ + M_INLINE bool \ + M_F(name, _full_p)(buffer_t v) \ + { \ + M_BUFF3R_CONTRACT(v,m_size); \ + return m_buff3r_number_load (v->number[0], policy) \ + == M_BUFF3R_SIZE(m_size); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(buffer_t v) \ + { \ + M_BUFF3R_CONTRACT(v,m_size); \ + return m_buff3r_number_load (v->number[0], policy); \ + } \ + \ + M_INLINE bool \ + M_F(name, _push_blocking)(buffer_t v, type const data, bool blocking) \ + { \ + M_BUFF3R_CONTRACT(v,m_size); \ + \ + /* Producer Mutex lock (mutex lock performs an acquire memory barrier) */ \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_lock(v->mutexPush); \ + while (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_OVERWRITE) \ + && M_F(name, _full_p)(v)) { \ + if (!blocking) { \ + m_mutex_unlock(v->mutexPush); \ + return false; \ + } \ + m_cond_wait(v->there_is_room_for_data, v->mutexPush); \ + } \ + } else if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_OVERWRITE) \ + && M_F(name, _full_p)(v)) \ + return false; \ + M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \ + \ + size_t previousSize, idx = v->idx_prod; \ + /* INDEX computation if we have to overwrite the last element */ \ + if (M_UNLIKELY (M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_OVERWRITE) \ + && M_F(name, _full_p)(v))) { \ + v->overwrite++; \ + /* Let's overwrite the last element */ \ + /* Compute the index of the last push element */ \ + idx--; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK)) { \ + idx = idx >= M_BUFF3R_SIZE(m_size) ? M_BUFF3R_SIZE(m_size)-1 : idx; \ + } \ + /* Update data in the buffer */ \ + M_CALL_SET(oplist, v->data[idx].x, data); \ + previousSize = M_BUFF3R_SIZE(m_size); \ + } else { \ + /* Add a new item in the buffer */ \ + /* PUSH data in the buffer */ \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, v->data[idx].x, data); \ + } else { \ + M_CALL_INIT_SET(oplist, v->data[idx].x, data); \ + } \ + \ + /* Increment production INDEX of the buffer */ \ + idx++; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK)) { \ + idx = (idx == M_BUFF3R_SIZE(m_size)) ? 0 : idx; \ + } \ + v->idx_prod = idx; \ + \ + /* number[] is the only variable which can be modified by both \ + the consummer thread which has the pop lock and the producer \ + thread which has the push lock. As such, it is an atomic variable \ + that performs a release memory barrier. */ \ + /* Increment number of elements of the buffer */ \ + previousSize = m_buff3r_number_inc (v->number[0], policy); \ + if (M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) { \ + previousSize = m_buff3r_number_inc (v->number[1], policy); \ + } \ + /* From this point, consummer may read the data in the table */ \ + } \ + \ + /* Producer unlock (mutex unlock performs a release memory barrier) */ \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_unlock(v->mutexPush); \ + /* If the number of items in the buffer was 0, some consummer \ + may be waiting. Signal to them the availibility of the data \ + We cannot only signal one thread. */ \ + if (previousSize == 0) { \ + m_mutex_lock(v->mutexPop); \ + m_cond_broadcast(v->there_is_data); \ + m_mutex_unlock(v->mutexPop); \ + } \ + } \ + \ + M_BUFF3R_CONTRACT(v,m_size); \ + return true; \ + } \ + \ + M_INLINE bool \ + M_F(name, _pop_blocking)(type *data, buffer_t v, bool blocking) \ + { \ + M_BUFF3R_CONTRACT(v,m_size); \ + M_ASSERT (data != NULL); \ + \ + /* Consummer lock (mutex lock performs an acquire memory barrier) */ \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_lock(v->mutexPop); \ + while (M_F(name, _empty_p)(v)) { \ + if (!blocking) { \ + m_mutex_unlock(v->mutexPop); \ + return false; \ + } \ + m_cond_wait(v->there_is_data, v->mutexPop); \ + } \ + } else if (M_F(name, _empty_p)(v)) \ + return false; \ + M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \ + \ + /* POP data from the buffer and update INDEX */ \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_STACK)) { \ + /* FIFO queue */ \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, *data, v->data[v->idx_cons].x); \ + } else { \ + M_DO_INIT_MOVE (oplist, *data, v->data[v->idx_cons].x); \ + } \ + v->idx_cons = (v->idx_cons == M_BUFF3R_SIZE(m_size)-1) ? 0 : (v->idx_cons + 1); \ + } else { \ + /* STACK queue */ \ + v->idx_prod --; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, *data, v->data[v->idx_prod].x); \ + } else { \ + M_DO_INIT_MOVE (oplist, *data, v->data[v->idx_prod].x); \ + } \ + } \ + \ + /* number[] is the only variable which can be modified by both \ + the consummer thread which has the pop lock and the producer \ + thread which has the push lock. As such, it is an atomic variable \ + that performs a release memory barrier. */ \ + /* Decrement number of elements in the buffer */ \ + size_t previousSize; \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) { \ + previousSize = m_buff3r_number_dec (v->number[0], policy); \ + } else { \ + m_buff3r_number_dec (v->number[1], policy); \ + } \ + /* Space may be reused by a producer thread from this point */ \ + \ + /* Consummer unlock (mutex unlock perfoms a release memory barrier) */ \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \ + m_mutex_unlock(v->mutexPop); \ + /* If the number of items in the buffer was the max, some producer \ + may be waiting. Signal to them the availibility of the free room \ + We cannot only signal one thread. */ \ + if ((!M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) \ + && previousSize == M_BUFF3R_SIZE(m_size)) { \ + m_mutex_lock(v->mutexPush); \ + m_cond_broadcast(v->there_is_room_for_data); \ + m_mutex_unlock(v->mutexPush); \ + } \ + } \ + \ + M_BUFF3R_CONTRACT(v,m_size); \ + return true; \ + } \ + \ + \ + M_INLINE bool \ + M_F(name, _push)(buffer_t v, type const data) \ + { \ + return M_F(name, _push_blocking)(v, data, \ + !M_BUFF3R_POLICY_P((policy), M_BUFFER_UNBLOCKING_PUSH)); \ + } \ + \ + M_INLINE bool \ + M_F(name, _pop)(type *data, buffer_t v) \ + { \ + return M_F(name, _pop_blocking)(data, v, \ + !M_BUFF3R_POLICY_P((policy), M_BUFFER_UNBLOCKING_POP)); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _overwrite)(const buffer_t v) \ + { \ + return v->overwrite; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity)(const buffer_t v) \ + { \ + (void) v; /* may be unused */ \ + return M_BUFF3R_SIZE(m_size); \ + } \ + \ + M_INLINE void \ + M_F(name, _pop_release)(buffer_t v) \ + { \ + /* Decrement the effective number of elements in the buffer */ \ + if (M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) { \ + size_t previousSize = m_buff3r_number_dec (v->number[0], policy); \ + if (previousSize == M_BUFF3R_SIZE(m_size)) { \ + m_mutex_lock(v->mutexPush); \ + m_cond_broadcast(v->there_is_room_for_data); \ + m_mutex_unlock(v->mutexPush); \ + } \ + } \ + } \ + + +/********************************** INTERNAL *********************************/ + +/* Definition of a a QUEUE for Many Produccer / Many Consummer + for high bandwidth scenario: + * nearly lock-free, + * quite fast + * no blocking calls. + * only queue (no stack) + * size of queue is always a power of 2 + * no overwriting. + */ + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_QU3UE_MPMC_DEF_P1(arg) M_ID( M_QU3UE_MPMC_DEF_P2 arg ) + +/* Validate the value oplist before going further */ +#define M_QU3UE_MPMC_DEF_P2(name, type, policy, oplist, buffer_t) \ + M_IF_OPLIST(oplist)(M_QU3UE_MPMC_DEF_P3, M_QU3UE_MPMC_DEF_FAILURE)(name, type, policy, oplist, buffer_t) + +/* Stop processing with a compilation failure */ +#define M_QU3UE_MPMC_DEF_FAILURE(name, type, policy, oplist, buffer_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(QUEUE_MPMC_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist)) + +#ifdef NDEBUG +# define M_QU3UE_MPMC_CONTRACT(v) /* nothing */ +#else +# define M_QU3UE_MPMC_CONTRACT(v) do { \ + M_ASSERT (v != 0); \ + M_ASSERT (v->Tab != NULL); \ + unsigned int _r = atomic_load(&v->ConsoIdx); \ + unsigned int _w = atomic_load(&v->ProdIdx); \ + _r = atomic_load(&v->ConsoIdx); \ + M_ASSERT (_r > _w || _w-_r <= v->size); \ + M_ASSERT (M_POWEROF2_P(v->size)); \ + } while (0) +#endif + + +/* Define the buffer type MPMC using atomics and its functions. + - name: main prefix of the container + - type: type of an element of the buffer + - policy: the policy of the buffer + - oplist: the oplist of the type of an element of the buffer + - buffer_t: name of the buffer + */ +#define M_QU3UE_MPMC_DEF_P3(name, type, policy, oplist, buffer_t) \ + M_QU3UE_MPMC_DEF_TYPE(name, type, policy, oplist, buffer_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_QU3UE_MPMC_DEF_CORE(name, type, policy, oplist, buffer_t) \ + M_EMPLACE_QUEUE_DEF(name, buffer_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE) + +/* Define the type of a MPMC queue */ +#define M_QU3UE_MPMC_DEF_TYPE(name, type, policy, oplist, buffer_t) \ + \ + /* The sequence number of an element will be equal to either \ + - 2* the index of the production which creates it, \ + - 1 + 2* the index of the consumption which consummes it \ + In case of wrapping, as there is no order comparison but only \ + equal comparison, there is no special issue. \ + Each element is put in a separate cache line to avoid false \ + sharing by multiple writing threads. \ + */ \ + typedef struct M_F(name, _el_s) { \ + atomic_uint seq; /* Can only increase until wrapping */ \ + type x; \ + M_CACHELINE_ALIGN(align, atomic_uint, type); \ + } M_F(name, _el_ct); \ + \ + /* If there is only one producer and one consummer, then they won't \ + typically use the same cache line, increasing performance. */ \ + typedef struct M_F(name, _s) { \ + atomic_uint ProdIdx; /* Can only increase until wrapping */ \ + M_CACHELINE_ALIGN(align1, atomic_uint); \ + atomic_uint ConsoIdx; /* Can only increase until wrapping */ \ + M_CACHELINE_ALIGN(align2, atomic_uint); \ + M_F(name, _el_ct) *Tab; \ + unsigned int size; \ + } buffer_t[1]; \ + \ + typedef type M_F(name, _subtype_ct); \ + typedef buffer_t M_F(name, _ct); \ + +/* Define the core functionnalities of a MPMC queue */ +#define M_QU3UE_MPMC_DEF_CORE(name, type, policy, oplist, buffer_t) \ + M_INLINE bool \ + M_F(name, _push)(buffer_t table, type const x) \ + { \ + M_QU3UE_MPMC_CONTRACT(table); \ + unsigned int idx = atomic_load_explicit(&table->ProdIdx, \ + memory_order_relaxed); \ + const unsigned int i = idx & (table->size -1); \ + const unsigned int seq = atomic_load_explicit(&table->Tab[i].seq, \ + memory_order_acquire); \ + if (M_UNLIKELY (2*(idx - table->size) + 1 != seq)) { \ + /* Buffer full (or unlikely preemption). Can not push */ \ + return false; \ + } \ + if (M_UNLIKELY (!atomic_compare_exchange_strong_explicit(&table->ProdIdx, \ + &idx, idx+1, memory_order_relaxed, memory_order_relaxed))) { \ + /* Thread has been preempted by another one. */ \ + return false; \ + } \ + /* If it is interrupted here, it may block all pop methods (not push) \ + even if there is other threads that have pushed data later in the \ + queue as all pop threads will try to enqueue this particular element \ + but always fail. The won't try to enqueue other elements. \ + As such, this queue is not strictly lock-free.*/ \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, table->Tab[i].x, x); \ + } else { \ + M_CALL_INIT_SET(oplist, table->Tab[i].x, x); \ + } \ + /* Finish transaction */ \ + atomic_store_explicit(&table->Tab[i].seq, 2*idx, memory_order_release); \ + M_QU3UE_MPMC_CONTRACT(table); \ + return true; \ + } \ + \ + M_INLINE bool \ + M_F(name, _pop)(type *ptr, buffer_t table) \ + { \ + M_QU3UE_MPMC_CONTRACT(table); \ + M_ASSERT (ptr != NULL); \ + unsigned int iC = atomic_load_explicit(&table->ConsoIdx, \ + memory_order_relaxed); \ + const unsigned int i = (iC & (table->size -1)); \ + const unsigned int seq = atomic_load_explicit(&table->Tab[i].seq, \ + memory_order_acquire); \ + if (seq != 2 * iC) { \ + /* Nothing in buffer to consumme (or unlikely preemption) */ \ + return false; \ + } \ + if (M_UNLIKELY (!atomic_compare_exchange_strong_explicit(&table->ConsoIdx, \ + &iC, iC+1, memory_order_relaxed, memory_order_relaxed))) { \ + /* Thread has been preempted by another one */ \ + return false; \ + } \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, *ptr, table->Tab[i].x); \ + } else { \ + M_DO_INIT_MOVE (oplist, *ptr, table->Tab[i].x); \ + } \ + atomic_store_explicit(&table->Tab[i].seq, 2*iC + 1, memory_order_release); \ + M_QU3UE_MPMC_CONTRACT(table); \ + return true; \ + } \ + \ + M_INLINE void \ + M_F(name, _init)(buffer_t buffer, size_t size) \ + { \ + M_ASSERT (buffer != NULL); \ + M_ASSERT( M_POWEROF2_P(size)); \ + M_ASSERT (0 < size && size <= UINT_MAX); \ + M_ASSERT(((policy) & (M_BUFFER_STACK|M_BUFFER_THREAD_UNSAFE|M_BUFFER_PUSH_OVERWRITE)) == 0); \ + atomic_init(&buffer->ProdIdx, (unsigned int) size); \ + atomic_init(&buffer->ConsoIdx, (unsigned int) size); \ + buffer->size = (unsigned int) size; \ + buffer->Tab = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, size); \ + if (M_UNLIKELY_NOMEM (buffer->Tab == NULL)) { \ + M_MEMORY_FULL (size*sizeof(M_F(name, _el_ct) )); \ + return; \ + } \ + for(unsigned int j = 0; j < (unsigned int) size; j++) { \ + atomic_init(&buffer->Tab[j].seq, 2*j+1U); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_INIT(oplist, buffer->Tab[j].x); \ + } \ + } \ + M_QU3UE_MPMC_CONTRACT(buffer); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(buffer_t buffer) \ + { \ + M_QU3UE_MPMC_CONTRACT(buffer); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + for(unsigned int j = 0; j < buffer->size; j++) { \ + M_CALL_CLEAR(oplist, buffer->Tab[j].x); \ + } \ + } else { \ + unsigned int iP = atomic_load_explicit(&buffer->ProdIdx, memory_order_relaxed); \ + unsigned int i = iP & (buffer->size -1); \ + unsigned int iC = atomic_load_explicit(&buffer->ConsoIdx, memory_order_relaxed); \ + unsigned int j = iC & (buffer->size -1); \ + while (i != j) { \ + M_CALL_CLEAR(oplist, buffer->Tab[j].x); \ + j++; \ + if (j >= buffer->size) \ + j = 0; \ + } \ + } \ + M_CALL_FREE(oplist, buffer->Tab); \ + buffer->Tab = NULL; /* safer */ \ + buffer->size = 3; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(buffer_t table) \ + { \ + M_QU3UE_MPMC_CONTRACT(table); \ + const unsigned int iC = atomic_load_explicit(&table->ConsoIdx, memory_order_relaxed); \ + const unsigned int iP = atomic_load_explicit(&table->ProdIdx, memory_order_acquire); \ + /* We return an approximation as we can't read both iC & iP atomically \ + As we read producer index after consummer index, \ + and they are atomic variables without reordering \ + producer index is always greater or equal than consumer index \ + (or on overflow occurs, in which case as we compute with modulo \ + arithmetic, the right result is computed). \ + We may return a result which is greater than the size of the queue \ + if the function is interrupted a long time between reading iC & \ + iP. the function is not protected against it. \ + */ \ + return iP-iC; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity)(buffer_t v) \ + { \ + M_QU3UE_MPMC_CONTRACT(v); \ + return v->size; \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(buffer_t v) \ + { \ + return M_F(name, _size) (v) == 0; \ + } \ + \ + M_INLINE bool \ + M_F(name, _full_p)(buffer_t v) \ + { \ + return M_F(name, _size)(v) >= v->size; \ + } \ + + +/********************************** INTERNAL *********************************/ + +/* Definition of a a QUEUE for Single Producer / Single Consummer + for high bandwidth scenario: + * wait-free, + * quite fast + * no blocking calls. + * only queue (no stack) + * size of queue is always a power of 2 + * no overwriting. + */ + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_QU3UE_SPSC_DEF_P1(arg) M_ID( M_QU3UE_SPSC_DEF_P2 arg ) + +/* Validate the value oplist before going further */ +#define M_QU3UE_SPSC_DEF_P2(name, type, policy, oplist, buffer_t) \ + M_IF_OPLIST(oplist)(M_QU3UE_SPSC_DEF_P3, M_QU3UE_SPSC_DEF_FAILURE)(name, type, policy, oplist, buffer_t) + +/* Stop processing with a compilation failure */ +#define M_QU3UE_SPSC_DEF_FAILURE(name, type, policy, oplist, buffer_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(QUEUE_SPSC_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist)) + +#ifdef NDEBUG +#define M_QU3UE_SPSC_CONTRACT(table) do { } while (0) +#else +#define M_QU3UE_SPSC_CONTRACT(table) do { \ + M_ASSERT (table != NULL); \ + unsigned int _r = atomic_load(&table->consoIdx); \ + unsigned int _w = atomic_load(&table->prodIdx); \ + /* Due to overflow we don't have M_ASSERT (_r <= _w); */ \ + _r = atomic_load(&table->consoIdx); \ + M_ASSERT (_r > _w || _w-_r <= table->size); \ + M_ASSERT (M_POWEROF2_P(table->size)); \ + } while (0) +#endif + +/* Define the buffer type SPSC using atomics and its functions. + - name: main prefix of the container + - type: type of an element of the buffer + - policy: the policy of the buffer + - oplist: the oplist of the type of an element of the buffer + - buffer_t: name of the buffer + */ +#define M_QU3UE_SPSC_DEF_P3(name, type, policy, oplist, buffer_t) \ + M_QU3UE_SPSC_DEF_TYPE(name, type, policy, oplist, buffer_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_QU3UE_SPSC_DEF_CORE(name, type, policy, oplist, buffer_t) \ + M_EMPLACE_QUEUE_DEF(name, buffer_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE) + +/* Define the type of a SPSC queue */ +#define M_QU3UE_SPSC_DEF_TYPE(name, type, policy, oplist, buffer_t) \ + \ + /* Single producer / Single consummer \ + So, only one thread will write in this table. The other thread \ + will only read. As such, there is no concurrent write, and no \ + need to align the structure for best performance. */ \ + typedef struct M_F(name, _el_s) { \ + type x; \ + } M_F(name, _el_ct); \ + \ + typedef struct M_F(name, _s) { \ + atomic_uint consoIdx; /* Can only increase until overflow */ \ + unsigned int size; \ + M_F(name, _el_ct) *Tab; \ + M_CACHELINE_ALIGN(align, atomic_uint, size_t, M_F(name, _el_ct) *); \ + atomic_uint prodIdx; /* Can only increase until overflow */ \ + } buffer_t[1]; \ + \ + typedef type M_F(name, _subtype_ct); \ + typedef buffer_t M_F(name, _ct); \ + +/* Define the core functionnalities of a SPSC queue */ +#define M_QU3UE_SPSC_DEF_CORE(name, type, policy, oplist, buffer_t) \ + \ + M_INLINE bool \ + M_F(name, _push)(buffer_t table, type const x) \ + { \ + M_QU3UE_SPSC_CONTRACT(table); \ + unsigned int r = atomic_load_explicit(&table->consoIdx, \ + memory_order_relaxed); \ + unsigned int w = atomic_load_explicit(&table->prodIdx, \ + memory_order_acquire); \ + if (w-r >= table->size) \ + return false; \ + unsigned int i = w & (table->size -1); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, table->Tab[i].x, x); \ + } else { \ + M_CALL_INIT_SET(oplist, table->Tab[i].x, x); \ + } \ + atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \ + M_QU3UE_SPSC_CONTRACT(table); \ + return true; \ + } \ + \ + M_INLINE bool \ + M_F(name, _push_move)(buffer_t table, type *x) \ + { \ + M_QU3UE_SPSC_CONTRACT(table); \ + unsigned int r = atomic_load_explicit(&table->consoIdx, \ + memory_order_relaxed); \ + unsigned int w = atomic_load_explicit(&table->prodIdx, \ + memory_order_acquire); \ + if (w-r >= table->size) \ + return false; \ + unsigned int i = w & (table->size -1); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_DO_MOVE(oplist, table->Tab[i].x, *x); \ + } else { \ + M_DO_INIT_MOVE(oplist, table->Tab[i].x, *x); \ + } \ + atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \ + M_QU3UE_SPSC_CONTRACT(table); \ + return true; \ + } \ + \ + M_INLINE bool \ + M_F(name, _pop)(type *ptr, buffer_t table) \ + { \ + M_QU3UE_SPSC_CONTRACT(table); \ + M_ASSERT (ptr != NULL); \ + unsigned int w = atomic_load_explicit(&table->prodIdx, \ + memory_order_relaxed); \ + unsigned int r = atomic_load_explicit(&table->consoIdx, \ + memory_order_acquire); \ + if (w-r == 0) \ + return false; \ + unsigned int i = r & (table->size -1); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, *ptr , table->Tab[i].x); \ + } else { \ + M_DO_INIT_MOVE (oplist, *ptr, table->Tab[i].x); \ + } \ + atomic_store_explicit(&table->consoIdx, r+1, memory_order_release); \ + M_QU3UE_SPSC_CONTRACT(table); \ + return true; \ + } \ + \ + M_INLINE unsigned \ + M_F(name, _push_bulk)(buffer_t table, unsigned n, type const x[]) \ + { \ + M_QU3UE_SPSC_CONTRACT(table); \ + M_ASSERT (x != NULL); \ + M_ASSERT (n <= table->size); \ + unsigned int r = atomic_load_explicit(&table->consoIdx, \ + memory_order_relaxed); \ + unsigned int w = atomic_load_explicit(&table->prodIdx, \ + memory_order_acquire); \ + unsigned int max = M_MIN(n, table->size - (w-r) ); \ + if (max == 0) \ + return 0; \ + for(unsigned int k = 0; k < max; k++) { \ + unsigned int i = (w+k) & (table->size -1); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, table->Tab[i].x, x[k]); \ + } else { \ + M_CALL_INIT_SET(oplist, table->Tab[i].x, x[k]); \ + } \ + } \ + atomic_store_explicit(&table->prodIdx, w+max, memory_order_release); \ + M_QU3UE_SPSC_CONTRACT(table); \ + return max; \ + } \ + \ + M_INLINE unsigned \ + M_F(name, _pop_bulk)(unsigned int n, type ptr[], buffer_t table) \ + { \ + M_QU3UE_SPSC_CONTRACT(table); \ + M_ASSERT (ptr != NULL); \ + M_ASSERT (n <= table->size); \ + unsigned int w = atomic_load_explicit(&table->prodIdx, \ + memory_order_relaxed); \ + unsigned int r = atomic_load_explicit(&table->consoIdx, \ + memory_order_acquire); \ + if (w-r == 0) \ + return 0; \ + unsigned int max = M_MIN(w-r, n); \ + for(unsigned int k = 0; k < max; k++) { \ + unsigned int i = (r+k) & (table->size -1); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, ptr[k], table->Tab[i].x); \ + } else { \ + M_DO_INIT_MOVE (oplist, ptr[k], table->Tab[i].x); \ + } \ + } \ + atomic_store_explicit(&table->consoIdx, r+max, memory_order_release); \ + M_QU3UE_SPSC_CONTRACT(table); \ + return max; \ + } \ + \ + M_INLINE void \ + M_F(name, _push_force)(buffer_t table, type const x) \ + { \ + M_QU3UE_SPSC_CONTRACT(table); \ + unsigned int r = atomic_load_explicit(&table->consoIdx, \ + memory_order_relaxed); \ + unsigned int w = atomic_load_explicit(&table->prodIdx, \ + memory_order_acquire); \ + /* If no place in queue, try to skip the last one */ \ + while (w-r >= table->size) { \ + bool b = atomic_compare_exchange_strong(&table->consoIdx, &r, r+1); \ + r += b; \ + } \ + unsigned int i = w & (table->size -1); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + M_CALL_SET(oplist, table->Tab[i].x, x); \ + } else { \ + M_CALL_INIT_SET(oplist, table->Tab[i].x, x); \ + } \ + atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \ + M_QU3UE_SPSC_CONTRACT(table); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(buffer_t table) \ + { \ + M_QU3UE_SPSC_CONTRACT(table); \ + unsigned int r = atomic_load_explicit(&table->consoIdx, \ + memory_order_relaxed); \ + unsigned int w = atomic_load_explicit(&table->prodIdx, \ + memory_order_acquire); \ + /* We return an approximation as we can't read both r & w atomically \ + As we read producer index after consummer index, \ + and they are atomic variables without reordering \ + producer index is always greater or equal than consumer index \ + (or on overflow occurs, in which case as we compute with modulo \ + arithmetic, the right result is computed). \ + We may return a result which is greater than the size of the queue \ + if the function is interrupted a long time between reading the \ + indexs. The function is not protected against it. \ + */ \ + return w-r; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity)(buffer_t v) \ + { \ + return v->size; \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(buffer_t v) \ + { \ + return M_F(name, _size) (v) == 0; \ + } \ + \ + M_INLINE bool \ + M_F(name, _full_p)(buffer_t v) \ + { \ + return M_F(name, _size)(v) >= v->size; \ + } \ + \ + M_INLINE void \ + M_F(name, _init)(buffer_t buffer, size_t size) \ + { \ + M_ASSERT (buffer != NULL); \ + M_ASSERT( M_POWEROF2_P(size)); \ + M_ASSERT (0 < size && size <= UINT_MAX); \ + M_ASSERT(((policy) & (M_BUFFER_STACK|M_BUFFER_THREAD_UNSAFE|M_BUFFER_PUSH_OVERWRITE)) == 0); \ + atomic_init(&buffer->prodIdx, (unsigned int) size); \ + atomic_init(&buffer->consoIdx, (unsigned int) size); \ + buffer->size = (unsigned int) size; \ + buffer->Tab = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, size); \ + if (M_UNLIKELY_NOMEM (buffer->Tab == NULL)) { \ + M_MEMORY_FULL (size*sizeof(M_F(name, _el_ct) )); \ + return; \ + } \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + for(unsigned int j = 0; j < (unsigned int) size; j++) { \ + M_CALL_INIT(oplist, buffer->Tab[j].x); \ + } \ + } \ + M_QU3UE_SPSC_CONTRACT(buffer); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(buffer_t buffer) \ + { \ + M_QU3UE_SPSC_CONTRACT(buffer); \ + if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \ + for(unsigned int j = 0; j < buffer->size; j++) { \ + M_CALL_CLEAR(oplist, buffer->Tab[j].x); \ + } \ + } else { \ + unsigned int iP = atomic_load_explicit(&buffer->prodIdx, memory_order_relaxed); \ + unsigned int i = iP & (buffer->size -1); \ + unsigned int iC = atomic_load_explicit(&buffer->consoIdx, memory_order_relaxed); \ + unsigned int j = iC & (buffer->size -1); \ + while (i != j) { \ + M_CALL_CLEAR(oplist, buffer->Tab[j].x); \ + j++; \ + if (j >= buffer->size) \ + j = 0; \ + } \ + } \ + M_CALL_FREE(oplist, buffer->Tab); \ + buffer->Tab = NULL; /* safer */ \ + buffer->size = 3; \ + } \ + + +/********************************** INTERNAL *********************************/ + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_BUFF3R_OPLIST_P1(arg) M_BUFF3R_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_BUFF3R_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_BUFF3R_OPLIST_P3, M_BUFF3R_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_BUFF3R_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_BUFFER_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* OPLIST definition of a buffer */ +#define M_BUFF3R_OPLIST_P3(name, oplist) \ + (INIT(M_C3(m_buff3r_,name, _init)) \ + ,INIT_SET(M_F(name, _init_set)) \ + ,SET(M_F(name, _set)) \ + ,CLEAR(M_F(name, _clear)) \ + ,NAME(name) \ + ,TYPE(M_F(name,_ct)) \ + ,SUBTYPE(M_F(name, _subtype_ct)) \ + ,RESET(M_F(name,_reset)) \ + ,PUSH(M_F(name,_push)) \ + ,POP(M_F(name,_pop)) \ + ,OPLIST(oplist) \ + ,EMPTY_P(M_F(name, _empty_p)), \ + ,GET_SIZE(M_F(name, _size)) \ + ) + + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define BUFFER_DEF M_BUFFER_DEF +#define BUFFER_DEF_AS M_BUFFER_DEF_AS +#define BUFFER_OPLIST M_BUFFER_OPLIST +#define QUEUE_MPMC_DEF M_QUEUE_MPMC_DEF +#define QUEUE_MPMC_DEF_AS M_QUEUE_MPMC_DEF_AS +#define QUEUE_SPSC_DEF M_QUEUE_SPSC_DEF +#define QUEUE_SPSC_DEF_AS M_QUEUE_SPSC_DEF_AS + +#define buffer_policy_e m_buffer_policy_e +#define BUFFER_QUEUE M_BUFFER_QUEUE +#define BUFFER_STACK M_BUFFER_STACK +#define BUFFER_BLOCKING_PUSH M_BUFFER_BLOCKING_PUSH +#define BUFFER_UNBLOCKING_PUSH M_BUFFER_UNBLOCKING_PUSH +#define BUFFER_BLOCKING_POP M_BUFFER_BLOCKING_POP +#define BUFFER_UNBLOCKING_POP M_BUFFER_UNBLOCKING_POP +#define BUFFER_BLOCKING M_BUFFER_BLOCKING +#define BUFFER_UNBLOCKING M_BUFFER_UNBLOCKING +#define BUFFER_THREAD_SAFE M_BUFFER_THREAD_SAFE +#define BUFFER_THREAD_UNSAFE M_BUFFER_THREAD_UNSAFE +#define BUFFER_PUSH_INIT_POP_MOVE M_BUFFER_PUSH_INIT_POP_MOVE +#define BUFFER_PUSH_OVERWRITE M_BUFFER_PUSH_OVERWRITE +#define BUFFER_DEFERRED_POP M_BUFFER_DEFERRED_POP + +#endif + +#endif diff --git a/components/mlib/m-c-mempool.h b/components/mlib/m-c-mempool.h new file mode 100644 index 00000000..6e496294 --- /dev/null +++ b/components/mlib/m-c-mempool.h @@ -0,0 +1,835 @@ +/* + * M*LIB - Concurrent memory pool allocator + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_CONCURRENT_MEMPOOL_H +#define MSTARLIB_CONCURRENT_MEMPOOL_H + +#include "m-core.h" +#include "m-atomic.h" +#include "m-genint.h" + +M_BEGIN_PROTECTED_CODE + +/* Minimum number of nodes per group of nodes */ +#define M_CMEMP00L_MIN_NODE_PER_GROUP 16 + +#define M_C_MEMPOOL_DEF(name, type_t) \ + M_BEGIN_PROTECTED_CODE \ + M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \ + M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \ + M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \ + M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \ + M_CMEMP00L_DEF_LF_MEMPOOL(name, type_t) \ + M_END_PROTECTED_CODE + +/* Classic internal Singly List without allocation */ +#define M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \ + \ + typedef struct M_F(name, _slist_node_s) { \ + struct M_F(name, _slist_node_s) *next; \ + type_t data; \ + } M_F(name, _slist_node_ct); \ + \ + typedef struct M_F(name, _slist_node_s) *M_F(name, _slist_ct)[1]; \ + \ + M_INLINE void \ + M_F(name, _slist_init)(M_F(name, _slist_ct) list) \ + { \ + *list = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _slist_push)(M_F(name, _slist_ct) list, \ + M_F(name, _slist_node_ct) *node) \ + { \ + node->next = *list; \ + *list = node; \ + } \ + \ + M_INLINE M_F(name, _slist_node_ct) * \ + M_F(name, _slist_pop)(M_F(name, _slist_ct) list) \ + { \ + M_ASSERT (*list != NULL); \ + M_F(name, _slist_node_ct) *node = *list; \ + *list = node->next; \ + M_IF_DEBUG(node->next = NULL;) \ + return node; \ + } \ + \ + M_INLINE bool \ + M_F(name, _slist_empty_p)(M_F(name, _slist_ct) list) \ + { \ + return *list == NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _slist_move)(M_F(name, _slist_ct) list, \ + M_F(name, _slist_ct) src) \ + { \ + *list = *src; \ + *src = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _slist_clear)(M_F(name, _slist_ct) list) \ + { \ + M_F(name, _slist_node_ct) *it = *list, *next; \ + while (it) { \ + next = it->next; \ + M_MEMORY_DEL(it); \ + it = next; \ + } \ + *list = NULL; \ + } \ + + +/* Lock Free free queue list (not generic one) of lists without allocation + Based on Michael & Scott Lock Free Queue List algorithm. + Each list is considered empty if there is only one node within. + This LF Queue List doesn't try to prevent the ABA problem. It is up to the + caller to avoid recycling the nodes too fast. + Each list has its own unique NIL ptr in order to avoid issues when + migrating a node from a Q to another: in the following scenario, + - Thread 1 performs a PUSH of N in Q1 with Q1 empty (only node is NA) + NA.next is NIL. + - Thread 1 is interrupted just before the CAS on NA.next + - Thread 2 performs a sucessfull push of NB in Q1. NA.next is set to NB. + - Thread 2 performs a sucessfull pop of NA in Q1 + - Thread 2 performs a sucessfull push of NA in Q2. NA.next is set to NIL. + - Thread 1 is restored and will succeed as NA.next is once again NIL. + In order to prevent the last CAS to succeed, each queue uses its own NIL pointer. + It is a derived problem of the ABA problem. + */ +/* TODO: Optimize alignement to reduce memory consumption. NIL object can use [] + to reduce memory consumption too (non compatible with C++ ...) */ +#define M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \ + \ + typedef struct M_F(name, _lf_node_s) { \ + M_ATTR_EXTENSION _Atomic(struct M_F(name, _lf_node_s) *) next; \ + m_gc_atomic_ticket_ct cpt; \ + M_F(name, _slist_ct) list; \ + } M_F(name, _lf_node_t); \ + \ + typedef struct M_F(name, _lflist_s) { \ + M_ATTR_EXTENSION _Atomic(M_F(name, _lf_node_t) *) head; \ + char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \ + M_ATTR_EXTENSION _Atomic(M_F(name, _lf_node_t) *) tail; \ + char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \ + M_F(name, _lf_node_t) nil; \ + } M_F(name, _lflist_ct)[1]; \ + \ + M_INLINE void \ + M_F(name, _lflist_init)(M_F(name, _lflist_ct) list, \ + M_F(name, _lf_node_t) *node) \ + { \ + atomic_init(&list->head, node); \ + atomic_init(&list->tail, node); \ + atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \ + } \ + \ + M_INLINE bool \ + M_F(name, _lflist_empty_p)(M_F(name, _lflist_ct) list) \ + { \ + return atomic_load(&list->tail) == atomic_load(&list->head); \ + } \ + \ + M_INLINE void \ + M_F(name, _lflist_push)(M_F(name, _lflist_ct) list, \ + M_F(name, _lf_node_t) *node, m_core_backoff_ct bkoff) \ + { \ + M_F(name, _lf_node_t) *tail; \ + M_F(name, _lf_node_t) *next; \ + \ + atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \ + m_core_backoff_reset(bkoff); \ + while (true) { \ + tail = atomic_load(&list->tail); \ + next = atomic_load_explicit(&tail->next, memory_order_acquire); \ + if (M_UNLIKELY(next != &list->nil)) { \ + /* Tail was not pointing to the last node \ + Try to swing Tail to the next node */ \ + atomic_compare_exchange_weak_explicit(&list->tail, \ + &tail, next, \ + memory_order_release, \ + memory_order_relaxed); \ + } else { \ + /* Try to link node at the end of the linked list */ \ + if (atomic_compare_exchange_strong_explicit(&tail->next, \ + &next, node, \ + memory_order_release, \ + memory_order_relaxed)) \ + break; \ + m_core_backoff_wait(bkoff); \ + } \ + } \ + /* Enqueue is done. Try to swing Tail to the inserted node \ + If it fails, someone else will do it or has already did it. */ \ + atomic_compare_exchange_strong_explicit(&list->tail, &tail, node, \ + memory_order_acq_rel, \ + memory_order_relaxed); \ + } \ + \ + M_INLINE M_F(name, _lf_node_t) * \ + M_F(name, _lflist_pop)(M_F(name, _lflist_ct) list, m_core_backoff_ct bkoff) \ + { \ + M_F(name, _lf_node_t) *head; \ + M_F(name, _lf_node_t) *tail; \ + M_F(name, _lf_node_t) *next; \ + \ + /* Reinitialize backoff */ \ + m_core_backoff_reset(bkoff); \ + while (true) { \ + head = atomic_load(&list->head); \ + tail = atomic_load(&list->tail); \ + next = atomic_load(&head->next); \ + /* Are head, tail, and next consistent?*/ \ + if (M_LIKELY(head == \ + atomic_load_explicit(&list->head, memory_order_relaxed))) \ + { \ + /* Is queue empty or Tail falling behind? */ \ + if (head == tail) { \ + /* Is queue empty? */ \ + if (next == &list->nil) \ + return NULL; \ + /* Tail is falling behind. Try to advance it */ \ + atomic_compare_exchange_strong_explicit(&list->tail, &tail, \ + next, \ + memory_order_release, \ + memory_order_relaxed); \ + } else { \ + /* Try to swing Head to the next node */ \ + if (atomic_compare_exchange_strong_explicit(&list->head, \ + &head, next, \ + memory_order_release, \ + memory_order_relaxed)) { \ + break; \ + } \ + /* Failure: perform a random exponential backoff */ \ + m_core_backoff_wait(bkoff); \ + } \ + } \ + } \ + /* dequeue returns an element that becomes the new dummy element (the new head), \ + and the former dummy element (the former head) is removed: \ + Since we want a link of free list, and we don't care about the content itsef, \ + provided that the node we return is older than the one we should return, \ + Therefore, we return the previous dummy head. \ + As such, it is not the original MSqueue algorithm. */ \ + M_IF_DEBUG(atomic_store(&head->next, (M_F(name, _lf_node_t) *) 0);) \ + return head; \ + } \ + \ + /* Dequeue a node if the node is old enough */ \ + M_INLINE M_F(name, _lf_node_t) * \ + M_F(name, _lflist_pop_if)(M_F(name, _lflist_ct) list, \ + m_gc_ticket_ct age, m_core_backoff_ct bkoff) \ + { \ + M_F(name, _lf_node_t) *head; \ + M_F(name, _lf_node_t) *tail; \ + M_F(name, _lf_node_t) *next; \ + \ + m_core_backoff_reset(bkoff); \ + while (true) { \ + head = atomic_load(&list->head); \ + tail = atomic_load(&list->tail); \ + next = atomic_load(&head->next); \ + if (M_LIKELY(head == atomic_load_explicit(&list->head, memory_order_relaxed))) \ + { \ + if (head == tail) { \ + if (next == &list->nil) \ + return NULL; \ + atomic_compare_exchange_strong_explicit(&list->tail, &tail, next, \ + memory_order_release, \ + memory_order_relaxed); \ + } else { \ + /* Test if the node is old enought to be popped */ \ + if (atomic_load_explicit(&next->cpt, memory_order_relaxed) >= age) \ + return NULL; \ + /* Try to swing Head to the next node */ \ + if (atomic_compare_exchange_strong_explicit(&list->head, \ + &head, next, \ + memory_order_release, \ + memory_order_relaxed)) { \ + break; \ + } \ + m_core_backoff_wait(bkoff); \ + } \ + } \ + } \ + M_IF_DEBUG(atomic_store(&head->next, (M_F(name, _lf_node_t) *) 0);) \ + return head; \ + } \ + \ + M_INLINE void \ + M_F(name, _lflist_clear)(M_F(name, _lflist_ct) list) \ + { \ + m_core_backoff_ct bkoff; \ + m_core_backoff_init(bkoff); \ + while (true) { \ + M_F(name, _lf_node_t) *node = M_F(name, _lflist_pop)(list, bkoff); \ + if (node == NULL) break; \ + M_F(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \ + memory_order_relaxed); \ + M_F(name, _slist_clear)(node->list); \ + M_MEMORY_DEL(node); \ + node = next; \ + } \ + /* Dummy node to free too */ \ + M_F(name, _lf_node_t) *dummy; \ + dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \ + M_F(name, _slist_clear)(dummy->list); \ + M_MEMORY_DEL(dummy); \ + } \ + +/* System node allocator: request memory to the system. + As such it is a non Lock-Free path. */ +#define M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \ + \ + M_INLINE M_F(name, _lf_node_t) * \ + M_F(name, _alloc_node)(unsigned int initial) \ + { \ + M_F(name, _lf_node_t) * node; \ + node = M_MEMORY_ALLOC(M_F(name, _lf_node_t)); \ + if (M_UNLIKELY_NOMEM (node == NULL)) { \ + M_MEMORY_FULL(sizeof(M_F(name, _lf_node_t))); \ + return NULL; \ + } \ + atomic_init(&node->next, (M_F(name, _lf_node_t) *) 0); \ + atomic_init(&node->cpt, 0UL); \ + M_F(name, _slist_init)(node->list); \ + for(unsigned i = 0; i < initial; i++) { \ + M_F(name, _slist_node_ct) *n; \ + n = M_MEMORY_ALLOC(M_F(name, _slist_node_ct)); \ + if (M_UNLIKELY_NOMEM (n == NULL)) { \ + M_MEMORY_FULL(sizeof(M_F(name, _lf_node_t))); \ + return NULL; \ + } \ + M_F(name, _slist_push)(node->list, n); \ + } \ + return node; \ + } \ + +/* Concurrent Memory pool + The data structure is the following. + Each thread has its own pool of nodes (local) that only it can + access (it is a singly list). If there is no longer any node in this + pool, it requests a new pool to the lock free queue of pool (group of + nodes). If it fails, it requests a new pool to the system allocator + (and from there it is no longer lock free). + This memory pool can only be lock free if the initial state is + sufficiently dimensionned to avoid calling the system allocator during + the normal processing. + Then each thread pushs its deleted node into another pool of nodes, + where the node is logically deleted (no contain of the node is destroyed + at this point and the node can be freely accessed by other threads). + Once the thread mempool is put to sleep, the age of the pool of logical + deleted nodes is computed and this pool is move to the Lock Free Queue + List of pools to be reclaimed. Then A Garbage Collector is performed + on this Lock Free Queue list to reclaim all pools thare are sufficiently + aged (taking into account the grace period of the pool) to be moved back + to the Lock Free Queue of the free pools. + + Each pool of nodes can be in the following state: + * FREE state if it is present in the Lock Free Queue of free pools. + * EMPTY state if it is present in the Lock Free Queue of empty pools + which means that the nodes present in it has been USED directly by a thread, + * TO_BE_RECLAIMED state if it is present in the Lock Free Queue of TBR pools + + A pool of nodes will go to the following state: + FREE --> EMPTY --> TO_BE_RECLAIMED + ^ | + +----------------------+ + + The ABA problem is taken into account as a node cannot be reused in the + same queue without performing a full cycle of its state. Moreover + it can only move from TO_BE_RECLAIMED to FREE if and only if a grace + period is finished (and then we are sure that no thread references any + older node). + + Each thread has its own backoff structure (with local pseudo-random + generator). + + The grace period is detected through a global age counter (ticket) + that is incremented each time a thread is awaken / sleep. + Each thread has its own age that is set to the global ticket on sleep/awaken. + The age of the pool to be reclaimed is also set to this global age counter. + + To ensure that the grace period is finished, it tests if all threads + are younger than the age of the pool to be reclaimed. + + From a performance point of view, this puts a bottleneck on the global + age counter that is shared and incremented by all threads. However, + the sleep/awaken operations are much less frequent than other operations. + Thus, it shall not have a huge impact on the performance if the user + code is intelligent with the sleep/awaken operations. + + As such it won't support more than ULONG_MAX sleep for all threads. +*/ +#define M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \ + \ + typedef struct M_F(name, _lfmp_thread_s) { \ + M_F(name, _slist_ct) free; \ + M_F(name, _slist_ct) to_be_reclaimed; \ + M_CACHELINE_ALIGN(align1, M_F(name, _slist_ct), M_F(name, _slist_ct)); \ + } M_F(name, _lfmp_thread_ct); \ + \ + M_INLINE void \ + M_F(name, _lfmp_thread_init)(M_F(name, _lfmp_thread_ct) *t) \ + { \ + M_F(name, _slist_init)(t->free); \ + M_F(name, _slist_init)(t->to_be_reclaimed); \ + } \ + \ + M_INLINE void \ + M_F(name, _lfmp_thread_clear)(M_F(name, _lfmp_thread_ct) *t) \ + { \ + M_ASSERT(M_F(name, _slist_empty_p)(t->to_be_reclaimed)); \ + M_F(name, _slist_clear)(t->free); \ + M_F(name, _slist_clear)(t->to_be_reclaimed); \ + } \ + +/* NOTE: once a node is deleted, its data are kept readable until the future GC */ +#define M_CMEMP00L_DEF_LF_MEMPOOL(name, type_t) \ + \ + typedef struct M_F(name, _s) { \ + unsigned initial; \ + M_F(name, _lfmp_thread_ct) *thread_data; \ + M_F(name, _lflist_ct) free; \ + M_F(name, _lflist_ct) to_be_reclaimed; \ + M_F(name, _lflist_ct) empty; \ + m_cmemp00l_list_ct mempool_node; \ + struct m_gc_s *gc_mem; \ + } M_F(name, _t)[1]; \ + \ + /* Garbage collect of the nodes of the mempool on sleep */ \ + M_INLINE void \ + M_C3(m_cmemp00l_,name,_gc_on_sleep)(m_gc_t gc_mem, m_cmemp00l_list_ct *data, \ + m_gc_tid_t id, m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket) \ + { \ + /* Get back the mempool from the node */ \ + struct M_F(name, _s) *mempool = \ + M_TYPE_FROM_FIELD(struct M_F(name, _s), data, m_cmemp00l_list_ct, mempool_node); \ + \ + /* Move the local nodes of the mempool to be reclaimed to the thread into the global pool */ \ + if (!M_F(name, _slist_empty_p)(mempool->thread_data[id].to_be_reclaimed)) { \ + M_F(name, _lf_node_t) *node; \ + /* Get a new empty group of nodes */ \ + node = M_F(name, _lflist_pop)(mempool->empty, gc_mem->thread_data[id].bkoff); \ + if (M_UNLIKELY (node == NULL)) { \ + /* Fail to get an empty group of node. \ + Alloc a new one from the system */ \ + node = M_F(name, _alloc_node)(0); \ + M_ASSERT(node != NULL); \ + } \ + M_ASSERT(M_F(name, _slist_empty_p)(node->list)); \ + M_F(name, _slist_move)(node->list, mempool->thread_data[id].to_be_reclaimed); \ + atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed); \ + M_F(name, _lflist_push)(mempool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff); \ + } \ + \ + /* Perform a GC of the freelist of nodes */ \ + while (true) { \ + M_F(name, _lf_node_t) *node; \ + node = M_F(name, _lflist_pop_if)(mempool->to_be_reclaimed, \ + min_ticket, gc_mem->thread_data[id].bkoff); \ + if (node == NULL) break; \ + M_F(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _init)(M_F(name, _t) mem, m_gc_t gc_mem, \ + unsigned init_node_count, unsigned init_group_count) \ + { \ + const size_t max_thread = gc_mem->max_thread; \ + /* Initialize the thread data of the mempool */ \ + mem->thread_data = M_MEMORY_REALLOC(M_F(name, _lfmp_thread_ct), NULL, max_thread); \ + if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) { \ + M_MEMORY_FULL(max_thread * sizeof(M_F(name, _lfmp_thread_ct))); \ + return; \ + } \ + for(unsigned i = 0; i < max_thread;i++) { \ + M_F(name, _lfmp_thread_init)(&mem->thread_data[i]); \ + } \ + /* Preallocate some group of nodes for the mempool */ \ + mem->initial = M_MAX(M_CMEMP00L_MIN_NODE_PER_GROUP, init_node_count); \ + M_F(name, _lflist_init)(mem->free, M_F(name, _alloc_node)(init_node_count)); \ + M_F(name, _lflist_init)(mem->to_be_reclaimed, M_F(name, _alloc_node)(init_node_count)); \ + M_F(name, _lflist_init)(mem->empty, M_F(name, _alloc_node)(0)); \ + for(unsigned i = 1; i < init_group_count; i++) { \ + M_F(name, _lflist_push)(mem->free, M_F(name, _alloc_node)(init_node_count), \ + gc_mem->thread_data[0].bkoff); \ + M_F(name, _lflist_push)(mem->empty, M_F(name, _alloc_node)(0), \ + gc_mem->thread_data[0].bkoff); \ + } \ + /* Register the mempool in the GC */ \ + mem->mempool_node.gc_on_sleep = M_C3(m_cmemp00l_,name,_gc_on_sleep); \ + mem->mempool_node.next = gc_mem->mempool_list; \ + gc_mem->mempool_list = &mem->mempool_node; \ + mem->gc_mem = gc_mem; \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(M_F(name, _t) mem) \ + { \ + const unsigned max_thread = mem->gc_mem->max_thread; \ + for(unsigned i = 0; i < max_thread;i++) { \ + M_F(name, _lfmp_thread_clear)(&mem->thread_data[i]); \ + } \ + M_MEMORY_FREE(mem->thread_data); \ + mem->thread_data = NULL; \ + M_F(name, _lflist_clear)(mem->empty); \ + M_F(name, _lflist_clear)(mem->free); \ + M_ASSERT(M_F(name, _lflist_empty_p)(mem->to_be_reclaimed)); \ + M_F(name, _lflist_clear)(mem->to_be_reclaimed); \ + /* TODO: Unregister from the GC? */ \ + } \ + \ + M_INLINE type_t * \ + M_F(name, _new)(M_F(name, _t) mem, m_gc_tid_t id) \ + { \ + M_F(name, _slist_node_ct) *snode; \ + M_F(name, _lf_node_t) *node; \ + while (true) { \ + /* Fast & likely path where we access the thread pool of nodes */ \ + if (M_LIKELY(!M_F(name, _slist_empty_p)(mem->thread_data[id].free))) { \ + snode = M_F(name, _slist_pop)(mem->thread_data[id].free); \ + return &snode->data; \ + } \ + /* Request a group node to the freelist of groups */ \ + node = M_F(name, _lflist_pop)(mem->free, mem->gc_mem->thread_data[id].bkoff); \ + if (M_UNLIKELY (node == NULL)) { \ + /* Request a new group to the system. Non Lock Free path */ \ + M_ASSERT(mem->initial > 0); \ + node = M_F(name, _alloc_node)(mem->initial); \ + M_ASSERT(node != NULL); \ + M_ASSERT(!M_F(name, _slist_empty_p)(node->list)); \ + } \ + M_F(name, _slist_move)(mem->thread_data[id].free, node->list); \ + /* Push back the empty group */ \ + M_ASSERT (M_F(name, _slist_empty_p)(node->list)); \ + M_F(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _del)(M_F(name, _t) mem, type_t *d, m_gc_tid_t id) \ + { \ + M_F(name, _slist_node_ct) *snode; \ + M_ASSERT( d != NULL); \ + snode = M_TYPE_FROM_FIELD(M_F(name, _slist_node_ct), d, type_t, data); \ + M_F(name, _slist_push)(mem->thread_data[id].to_be_reclaimed, snode); \ + } \ + + +/***********************************************************************/ + +/* Define the ID of a thread */ +typedef unsigned int m_gc_tid_t; + +/* Define the age of a node */ +/* TODO: Compute if sufficient (worst cast ULONG_MAX is 32 bits) */ +typedef unsigned long m_gc_ticket_ct; +typedef atomic_ulong m_gc_atomic_ticket_ct; + +/* Define the Linked List of mempools that are registered in the GC */ +struct m_gc_s; +typedef struct m_cmemp00l_list_s { + struct m_cmemp00l_list_s *next; + void (*gc_on_sleep)(struct m_gc_s *gc_mem, + struct m_cmemp00l_list_s *data, m_gc_tid_t id, + m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket); + void *data; +} m_cmemp00l_list_ct; + +/* Define the Garbage collector thread data */ +typedef struct m_gc_lfmp_thread_s { + m_gc_atomic_ticket_ct ticket; + m_core_backoff_ct bkoff; + M_CACHELINE_ALIGN(align1, atomic_ulong, m_core_backoff_ct); +} m_gc_lfmp_thread_ct; + +/* Define the Garbage collector coordinator */ +typedef struct m_gc_s { + m_gc_atomic_ticket_ct ticket; + m_gc_tid_t max_thread; + m_genint_t thread_alloc; + m_gc_lfmp_thread_ct *thread_data; + m_cmemp00l_list_ct *mempool_list; +} m_gc_t[1]; + +M_INLINE void +m_gc_init(m_gc_t gc_mem, size_t max_thread) +{ + M_ASSERT(gc_mem != NULL); + M_ASSERT(max_thread > 0 && max_thread < INT_MAX); + + atomic_init(&gc_mem->ticket, 0UL); + m_genint_init(gc_mem->thread_alloc, (unsigned int) max_thread); + gc_mem->thread_data = M_MEMORY_REALLOC(m_gc_lfmp_thread_ct, NULL, max_thread); + if (M_UNLIKELY_NOMEM (gc_mem->thread_data == NULL)) { + M_MEMORY_FULL(max_thread * sizeof(m_gc_lfmp_thread_ct)); + return; + } + for(unsigned i = 0; i < max_thread;i++) { + atomic_init(&gc_mem->thread_data[i].ticket, ULONG_MAX); + m_core_backoff_init(gc_mem->thread_data[i].bkoff); + } + gc_mem->max_thread = (unsigned int) max_thread; + gc_mem->mempool_list = NULL; +} + +M_INLINE void +m_gc_clear(m_gc_t gc_mem) +{ + M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0); + + for(m_gc_tid_t i = 0; i < gc_mem->max_thread;i++) { + m_core_backoff_clear(gc_mem->thread_data[i].bkoff); + } + M_MEMORY_FREE(gc_mem->thread_data); + gc_mem->thread_data = NULL; + m_genint_clear(gc_mem->thread_alloc); +} + +M_INLINE m_gc_tid_t +m_gc_attach_thread(m_gc_t gc_mem) +{ + M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0); + + unsigned id = m_genint_pop(gc_mem->thread_alloc); + return M_ASSIGN_CAST(m_gc_tid_t, id); +} + +M_INLINE void +m_gc_detach_thread(m_gc_t gc_mem, m_gc_tid_t id) +{ + M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0); + M_ASSERT(id < gc_mem->max_thread); + M_ASSERT(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX); + + m_genint_push(gc_mem->thread_alloc, id); +} + +M_INLINE void +m_gc_awake(m_gc_t gc_mem, m_gc_tid_t id) +{ + M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0); + M_ASSERT(id < gc_mem->max_thread); + M_ASSERT(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX); + + m_gc_ticket_ct t = atomic_fetch_add(&gc_mem->ticket, 1UL) + 1; + atomic_store(&gc_mem->thread_data[id].ticket, t); +} + +M_INLINE m_gc_ticket_ct +m_cmemp00l_gc_min_ticket(m_gc_t gc_mem) +{ + m_gc_ticket_ct min = atomic_load(&gc_mem->thread_data[0].ticket); + for(m_gc_tid_t i = 1; i < gc_mem->max_thread; i++) { + m_gc_ticket_ct t = atomic_load(&gc_mem->thread_data[i].ticket); + min = M_MIN(t, min); + } + return min; +} + +M_INLINE void +m_gc_sleep(m_gc_t gc_mem, m_gc_tid_t id) +{ + /* Increase life time of the thread */ + m_gc_ticket_ct t = atomic_fetch_add(&gc_mem->ticket, 1UL); + atomic_store(&gc_mem->thread_data[id].ticket, t+1); + const m_gc_ticket_ct min_ticket = m_cmemp00l_gc_min_ticket(gc_mem); + /* Iterate over all registered mempools */ + m_cmemp00l_list_ct *it = gc_mem->mempool_list; + + while (it) { + /* Perform a garbage collect of the mempool */ + it->gc_on_sleep(gc_mem, it, id, t, min_ticket); + /* Next mempool to scan for GC */ + it = it->next; + } + /* Sleep the thread */ + atomic_store(&gc_mem->thread_data[id].ticket, ULONG_MAX); +} + + +/***********************************************************************/ +/* */ +/* Variable Length Array MEMPOOL */ +/* */ +/***********************************************************************/ + +M_CMEMP00L_DEF_SINGLY_LIST(m_vlapool, char) +M_CMEMP00L_DEF_LF_QUEUE(m_vlapool, char) +M_CMEMP00L_DEF_SYSTEM_ALLOC(m_vlapool, char) + +typedef struct m_vlapool_lfmp_thread_s { + m_vlapool_slist_ct to_be_reclaimed; + M_CACHELINE_ALIGN(align1, m_vlapool_slist_ct); +} m_vlapool_lfmp_thread_ct; + +M_INLINE void +m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_ct *t) +{ + m_vlapool_slist_init(t->to_be_reclaimed); +} + +M_INLINE void +m_vlapool_lfmp_thread_clear(m_vlapool_lfmp_thread_ct *t) +{ + M_ASSERT(m_vlapool_slist_empty_p(t->to_be_reclaimed)); + m_vlapool_slist_clear(t->to_be_reclaimed); +} + +typedef struct m_vlapool_s { + m_vlapool_lflist_ct to_be_reclaimed; + m_vlapool_lflist_ct empty; + m_vlapool_lfmp_thread_ct *thread_data; + m_cmemp00l_list_ct mvla_node; + struct m_gc_s *gc_mem; +} m_vlapool_t[1]; + +/* Garbage collect of the nodes of the vla mempool on sleep */ +M_INLINE void +m_cmemp00l_vlapool_on_sleep(m_gc_t gc_mem, m_cmemp00l_list_ct *data, + m_gc_tid_t id, m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket) +{ + /* Get back the mempool from the node */ + struct m_vlapool_s *vlapool = + M_TYPE_FROM_FIELD(struct m_vlapool_s, data, m_cmemp00l_list_ct, mvla_node); + + /* Move the local nodes of the vlapool to be reclaimed to the thread into the global pool */ + if (!m_vlapool_slist_empty_p(vlapool->thread_data[id].to_be_reclaimed)) { + m_vlapool_lf_node_t *node; + /* Get a new empty group of nodes */ + node = m_vlapool_lflist_pop(vlapool->empty, gc_mem->thread_data[id].bkoff); + if (M_UNLIKELY (node == NULL)) { + /* Fail to get an empty group of node. + Alloc a new one from the system */ + node = m_vlapool_alloc_node(0); + M_ASSERT(node != NULL); + } + M_ASSERT(m_vlapool_slist_empty_p(node->list)); + m_vlapool_slist_move(node->list, vlapool->thread_data[id].to_be_reclaimed); + atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed); + m_vlapool_lflist_push(vlapool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff); + } + + /* Perform a GC of the freelist of nodes */ + while (true) { + m_vlapool_lf_node_t *node; + node = m_vlapool_lflist_pop_if(vlapool->to_be_reclaimed, + min_ticket, gc_mem->thread_data[id].bkoff); + if (node == NULL) break; + // No reuse of VLA nodes. Free physically the node back to the system + m_vlapool_slist_clear(node->list); + // Add back the empty group of nodes + m_vlapool_slist_init(node->list); + m_vlapool_lflist_push(vlapool->empty, node, gc_mem->thread_data[id].bkoff); + } +} + +M_INLINE void +m_vlapool_init(m_vlapool_t mem, m_gc_t gc_mem) +{ + const size_t max_thread = gc_mem->max_thread; + + /* Initialize the thread data of the vlapool */ + mem->thread_data = M_MEMORY_REALLOC(m_vlapool_lfmp_thread_ct, NULL, max_thread); + if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) { + M_MEMORY_FULL(max_thread * sizeof(m_vlapool_lfmp_thread_ct)); + return; + } + for(unsigned i = 0; i < max_thread;i++) { + m_vlapool_lfmp_thread_init(&mem->thread_data[i]); + } + + /* Initialize the lists */ + m_vlapool_lflist_init(mem->to_be_reclaimed, m_vlapool_alloc_node(0)); + m_vlapool_lflist_init(mem->empty, m_vlapool_alloc_node(0)); + + /* Register the mempool in the GC */ + mem->mvla_node.gc_on_sleep = m_cmemp00l_vlapool_on_sleep; + mem->mvla_node.next = gc_mem->mempool_list; + gc_mem->mempool_list = &mem->mvla_node; + mem->gc_mem = gc_mem; +} + +M_INLINE void +m_vlapool_clear(m_vlapool_t mem) +{ + const unsigned max_thread = mem->gc_mem->max_thread; + for(unsigned i = 0; i < max_thread;i++) { + m_vlapool_lfmp_thread_clear(&mem->thread_data[i]); + } + M_MEMORY_FREE(mem->thread_data); + mem->thread_data = NULL; + m_vlapool_lflist_clear(mem->empty); + M_ASSERT(m_vlapool_lflist_empty_p(mem->to_be_reclaimed)); + m_vlapool_lflist_clear(mem->to_be_reclaimed); + /* TODO: Unregister from the GC? */ +} + +M_INLINE void * +m_vlapool_new(m_vlapool_t mem, m_gc_tid_t id, size_t size) +{ + M_ASSERT(mem != NULL && mem->gc_mem != NULL); + M_ASSERT(id < mem->gc_mem->max_thread); + M_ASSERT( atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX); + + // Nothing to do with theses parameters yet + (void) mem; + (void) id; + + // Ensure the size is big enough to also represent a node + size += offsetof(struct m_vlapool_slist_node_s, data); + + // Simply wrap around a system call to get the memory + char *ptr = M_MEMORY_REALLOC(char, NULL, size); + return (ptr == NULL) ? NULL : M_ASSIGN_CAST(void *, ptr + offsetof(struct m_vlapool_slist_node_s, data)); +} + +M_INLINE void +m_vlapool_del(m_vlapool_t mem, void *d, m_gc_tid_t id) +{ + M_ASSERT(mem != NULL && mem->gc_mem != NULL); + M_ASSERT(id < mem->gc_mem->max_thread); + M_ASSERT(atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX); + M_ASSERT(d != NULL); + + // Get back the pointer to a struct m_vlapool_slist_node_s. + d = M_ASSIGN_CAST(void *, M_ASSIGN_CAST(char *, d) - offsetof(struct m_vlapool_slist_node_s, data)); + m_vlapool_slist_node_ct *snode = M_ASSIGN_CAST(m_vlapool_slist_node_ct *, d); + // Push the logicaly free memory into the list of the nodes to be reclaimed. + m_vlapool_slist_push(mem->thread_data[id].to_be_reclaimed, snode); +} + +M_END_PROTECTED_CODE + +#if M_USE_SMALL_NAME +#define C_MEMPOOL_DEF M_C_MEMPOOL_DEF +#endif + +#endif diff --git a/components/mlib/m-concurrent.h b/components/mlib/m-concurrent.h new file mode 100644 index 00000000..0360c39f --- /dev/null +++ b/components/mlib/m-concurrent.h @@ -0,0 +1,925 @@ +/* + * M*LIB - Basic Protected Concurrent module over container. + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_CONCURRENT_H +#define MSTARLIB_CONCURRENT_H + +#include "m-core.h" +#include "m-thread.h" +#include "m-atomic.h" + +/* Define a protected concurrent container and its associated functions + based on the given container. + USAGE: CONCURRENT_DEF(name, type [, oplist_of_the_type]) */ +#define M_CONCURRENT_DEF(name, ...) \ + M_CONCURRENT_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define a protected concurrent container and its associated functions + based on the given container as the given name name_t + USAGE: CONCURRENT_DEF_AS(name, name_t, type [, oplist_of_the_type]) */ +#define M_CONCURRENT_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_C0NCURRENT_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \ + (name, __VA_ARGS__, name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a protected concurrent container and its associated functions + based on its given container. Operations that perform only read of the container + can be done in parallel. + USAGE: CONCURRENT_RP_DEF(name, type [, oplist_of_the_type]) */ +#define M_CONCURRENT_RP_DEF(name, ...) \ + M_CONCURRENT_RP_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define a protected concurrent container and its associated functions + as the given name name_t + based on its given container. Operations that perform only read of the container + can be done in parallel. + USAGE: CONCURRENT_RP_DEF_AS(name, name_t, type [, oplist_of_the_type]) */ +#define M_CONCURRENT_RP_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_C0NCURRENT_RP_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \ + (name, __VA_ARGS__, name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a protected concurrent container given its name and its oplist. + USAGE: CONCURRENT_OPLIST(name[, oplist of the type]) */ +#define M_CONCURRENT_OPLIST(...) \ + M_C0NCURRENT_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST), \ + (__VA_ARGS__ ))) + + +/*****************************************************************************/ +/******************************** INTERNAL ***********************************/ +/*****************************************************************************/ + +/* Deferred evaluation for the oplist definition, + so that all arguments are evaluated before further expansion */ +#define M_C0NCURRENT_OPLIST_P1(arg) M_C0NCURRENT_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_C0NCURRENT_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_C0NCURRENT_OPLIST_P3, M_C0NCURRENT_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_C0NCURRENT_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_CONCURRENT_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* OPLIST definition + GET_KEY is not present as its interface is not compatible with a concurrent + container (_get returns a pointer to an internal data, data that may be + destroyed by another thread). +*/ +#define M_C0NCURRENT_OPLIST_P3(name, oplist) \ + (M_IF_METHOD(INIT, oplist)(INIT(M_F(name, _init)),) \ + ,M_IF_METHOD(INIT_SET, oplist)(INIT_SET(M_F(name, _init_set)),) \ + ,M_IF_METHOD(SET, oplist)(SET(M_F(name, _set)),) \ + ,M_IF_METHOD(CLEAR, oplist)(CLEAR(M_F(name, _clear)),) \ + ,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_F(name, _init_move)),) \ + ,M_IF_METHOD(MOVE, oplist)(MOVE(M_F(name, _move)),) \ + ,M_IF_METHOD(SWAP,oplist)(SWAP(M_F(name, _swap)),) \ + ,NAME(name) \ + ,TYPE(M_F(name,_ct)) \ + ,SUBTYPE(M_F(name, _subtype_ct)) \ + ,OPLIST(oplist) \ + ,M_IF_METHOD(EMPTY_P, oplist)(EMPTY_P(M_F(name,_empty_p)),) \ + ,M_IF_METHOD(GET_SIZE, oplist)(GET_SIZE(M_F(name,_size)),) \ + ,M_IF_METHOD(RESET, oplist)(RESET(M_F(name,_reset)),) \ + ,M_IF_METHOD(KEY_TYPE, oplist)(KEY_TYPE(M_GET_KEY_TYPE oplist),) \ + ,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_TYPE(M_GET_VALUE_TYPE oplist),) \ + ,M_IF_METHOD(KEY_TYPE, oplist)(KEY_OPLIST(M_GET_KEY_OPLIST oplist),) \ + ,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_OPLIST(M_GET_VALUE_OPLIST oplist), ) \ + ,M_IF_METHOD(SET_KEY, oplist)(SET_KEY(M_F(name, _set_at)),) \ + ,M_IF_METHOD(ERASE_KEY, oplist)(ERASE_KEY(M_F(name, _erase)),) \ + ,M_IF_METHOD(PUSH, oplist)(PUSH(M_F(name,_push)),) \ + ,M_IF_METHOD(POP, oplist)(POP(M_F(name,_pop)),) \ + ,M_IF_METHOD(PUSH_MOVE, oplist)(PUSH_MOVE(M_F(name,_push_move)),) \ + ,M_IF_METHOD(POP_MOVE, oplist)(POP_MOVE(M_F(name,_pop_move)),) \ + ,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \ + ,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \ + ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \ + ,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \ + ,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \ + ,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \ + ,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \ + ,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \ + ) + + +/******************************** INTERNAL ***********************************/ + +/* Internal contract + NOTE: Can't check too much without locking the container itself +*/ +#define M_C0NCURRENT_CONTRACT(c) do { \ + M_ASSERT ((c) != NULL); \ + M_ASSERT ((c)->self == (c)); \ + } while (0) + +/* Deferred evaluation for the concurrent definition, + so that all arguments are evaluated before further expansion */ +#define M_C0NCURRENT_DEF_P1(arg) M_ID( M_C0NCURRENT_DEF_P2 arg ) + +/* Validate the value oplist before going further */ +#define M_C0NCURRENT_DEF_P2(name, type, oplist, concurrent_t) \ + M_IF_OPLIST(oplist)(M_C0NCURRENT_DEF_P3, M_C0NCURRENT_DEF_FAILURE)(name, type, oplist, concurrent_t) + +/* Stop processing with a compilation failure */ +#define M_C0NCURRENT_DEF_FAILURE(name, type, oplist, concurrent_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist)) + +/* Internal concurrent definition + - name: prefix to be used + - type: type of the sub container + - oplist: oplist of the type of the sub container + - concurrent_t: alias for M_F(name, _t) [ type of the container ] + */ +#define M_C0NCURRENT_DEF_P3(name, type, oplist, concurrent_t) \ + M_C0NCURRENT_DEF_TYPE(name, type, oplist, concurrent_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_C0NCURRENT_DEF_CORE(name, type, oplist, concurrent_t) \ + M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t) + +/* Define the type of a concurrent container */ +#define M_C0NCURRENT_DEF_TYPE(name, type, oplist, concurrent_t) \ + \ + /* Define a concurrent container using a lock */ \ + typedef struct M_F(name, _s) { \ + struct M_F(name, _s) *self; \ + m_mutex_t lock; \ + m_cond_t there_is_data; /* condition raised when there is data */ \ + type data; \ + } concurrent_t[1]; \ + \ + /* Define alias for pointer types */ \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* Internal types for oplist */ \ + typedef concurrent_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + \ + /* Cannot define iterator as it cannot be reliable in a concurrent type */ \ + + /* Define the internal services used for the lock strategy */ +#define M_C0NCURRENT_DEF_CORE(name, type, oplist, concurrent_t) \ + \ + /* Initial the fields of the concurrent object not associated to the \ + sub-container. */ \ + M_INLINE void \ + M_F(name, _internal_init)(concurrent_t out) \ + { \ + m_mutex_init(out->lock); \ + m_cond_init(out->there_is_data); \ + out->self = out; \ + M_C0NCURRENT_CONTRACT(out); \ + } \ + \ + /* Clear the fields of the concurrent object not associated to the \ + sub-container. */ \ + M_INLINE void \ + M_F(name, _internal_clear)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_clear(out->lock); \ + m_cond_clear(out->there_is_data); \ + out->self = NULL; \ + } \ + \ + /* Get the read lock. Multiple threads can get it, but only for reading. \ + write lock is exclusive. \ + NOTE: This instance doesn't implement the read/write strategy, \ + and only get the lock */ \ + M_INLINE void \ + M_F(name, _read_lock)(const concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_lock (out->self->lock); \ + } \ + \ + /* Free the read lock. See above. \ + NOTE: This instance doesn't implement the read/write strategy, \ + and only get the lock */ \ + M_INLINE void \ + M_F(name, _read_unlock)(const concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_unlock (out->self->lock); \ + } \ + \ + /* Wait for a thread pushing some data in the container. \ + CONSTRAINT: the read lock shall be get before calling this service */ \ + M_INLINE void \ + M_F(name, _read_wait)(const concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_cond_wait(out->self->there_is_data, out->self->lock); \ + } \ + \ + /* Get the write lock. Only one threads can get it, and no other threads \ + can get the read lock too. \ + NOTE: This instance doesn't implement the read/write strategy, \ + and only get the lock */ \ + M_INLINE void \ + M_F(name, _write_lock)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_lock (out->lock); \ + } \ + \ + /* Free the write lock. \ + NOTE: This instance doesn't implement the read/write strategy, \ + and only get the lock */ \ + M_INLINE void \ + M_F(name, _write_unlock)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_unlock (out->lock); \ + } \ + \ + /* Wait for a thread pushing some data in the container. \ + CONSTRAINT: the write lock shall be get before calling this service */ \ + M_INLINE void \ + M_F(name, _write_wait)(const concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_cond_wait(out->self->there_is_data, out->self->lock); \ + } \ + \ + /* Wait to all threads that some data are available in the container. \ + CONSTRAINT: the write lock shall be get before calling this service */ \ + M_INLINE void \ + M_F(name, _write_signal)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + /* We need to signal this to ALL waiting threads as multiple threads \ + may wait on a some data of this container. */ \ + m_cond_broadcast(out->there_is_data); \ + } \ + +/* Internal definition of the functions commons to concurrent and rp-concurrent + - name: prefix to be used + - type: type of the sub container + - oplist: oplist of the type of the sub container + - concurrent_t: alias for M_F(name, _t) [ type of the container ] + A function is defined only if the underlying container exports the needed + services. It is usually one service declared per service exported. +*/ +#define M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t) \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE void \ + M_F(name, _init)(concurrent_t out) \ + { \ + M_F(name, _internal_init)(out); \ + M_CALL_INIT(oplist, out->data); \ + M_C0NCURRENT_CONTRACT(out); \ + } \ + ,) \ + \ + M_IF_METHOD(INIT_SET, oplist)( \ + M_INLINE void \ + M_F(name, _init_set)(concurrent_t out, concurrent_t const src) \ + { \ + M_C0NCURRENT_CONTRACT(src); \ + M_ASSERT (out != src); \ + M_F(name, _internal_init)(out); \ + M_F(name, _read_lock)(src); \ + M_CALL_INIT_SET(oplist, out->data, src->data); \ + M_F(name, _read_unlock)(src); \ + M_C0NCURRENT_CONTRACT(out); \ + } \ + ,) \ + \ + M_IF_METHOD(SET, oplist)( \ + M_INLINE void \ + M_F(name, _set)(concurrent_t out, concurrent_t const src) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + if (M_UNLIKELY (out == src)) return; \ + /* Need to order the locks in a total way to avoid lock deadlock. \ + Indeed, two call to _set can be done in two threads with : \ + T1: A := B \ + T2: B := A \ + If we lock first the mutex of out, then the src, it could be possible \ + in the previous scenario that both mutexs are locked: T1 has locked A \ + and T2 has locked B, and T1 is waiting for locking B, and T2 is waiting \ + for locking A, resulting in a deadlock. \ + To avoid this problem, we **always** lock the mutex which address is \ + the lowest. */ \ + if (out < src) { \ + M_F(name, _write_lock)(out); \ + M_F(name, _read_lock)(src); \ + } else { \ + M_F(name, _read_lock)(src); \ + M_F(name, _write_lock)(out); \ + } \ + M_CALL_SET(oplist, out->data, src->data); \ + if (out < src) { \ + M_F(name, _read_lock)(src); \ + M_F(name, _write_unlock)(out); \ + } else { \ + M_F(name, _write_unlock)(out); \ + M_F(name, _read_unlock)(src); \ + } \ + M_C0NCURRENT_CONTRACT(out); \ + } \ + ,) \ + \ + M_IF_METHOD(CLEAR, oplist)( \ + M_INLINE void \ + M_F(name, _clear)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + /* No need to lock. A clear is supposed to be called when all operations \ + of the container in other threads are terminated */ \ + M_CALL_CLEAR(oplist, out->data); \ + M_F(name, _internal_clear)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(INIT_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _init_move)(concurrent_t out, concurrent_t src) \ + { \ + M_C0NCURRENT_CONTRACT(src); \ + M_ASSERT (out != src); \ + /* No need to lock 'src' ? */ \ + M_F(name, _internal_init)(out); \ + M_CALL_INIT_MOVE(oplist, out->data, src->data); \ + M_F(name, _internal_clear)(src); \ + M_C0NCURRENT_CONTRACT(out); \ + } \ + ,) \ + \ + M_IF_METHOD(MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _move)(concurrent_t out, concurrent_t src) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_C0NCURRENT_CONTRACT(src); \ + /* No need to lock 'src' ? */ \ + M_F(name, _write_lock)(out); \ + M_CALL_MOVE(oplist, out->data, src->data); \ + M_F(name, _write_unlock)(out); \ + M_F(name, _internal_clear)(src); \ + M_C0NCURRENT_CONTRACT(out); \ + } \ + ,) \ + \ + M_IF_METHOD(SWAP, oplist)( \ + M_INLINE void \ + M_F(name, _swap)(concurrent_t out, concurrent_t src) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_C0NCURRENT_CONTRACT(src); \ + if (M_UNLIKELY (out == src)) return; \ + /* See comment above */ \ + if (out < src) { \ + M_F(name, _write_lock)(out); \ + M_F(name, _write_lock)(src); \ + } else { \ + M_F(name, _write_lock)(src); \ + M_F(name, _write_lock)(out); \ + } \ + M_CALL_SWAP(oplist, out->data, src->data); \ + if (out < src) { \ + M_F(name, _write_unlock)(src); \ + M_F(name, _write_unlock)(out); \ + } else { \ + M_F(name, _write_unlock)(out); \ + M_F(name, _write_unlock)(src); \ + } \ + } \ + ,) \ + \ + M_IF_METHOD(RESET, oplist)( \ + M_INLINE void \ + M_F(name, _reset)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + M_CALL_RESET(oplist, out->data); \ + M_F(name, _write_unlock)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(EMPTY_P, oplist)( \ + M_INLINE bool \ + M_F(name, _empty_p)(concurrent_t const out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _read_lock)(out); \ + bool b = M_CALL_EMPTY_P(oplist, out->data); \ + M_F(name, _read_unlock)(out); \ + return b; \ + } \ + ,) \ + \ + M_IF_METHOD(GET_SIZE, oplist)( \ + M_INLINE size_t \ + M_F(name, _size)(concurrent_t const out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _read_lock)(out); \ + size_t r = M_CALL_GET_SIZE(oplist, out->data); \ + M_F(name, _read_unlock)(out); \ + return r; \ + } \ + ,) \ + \ + M_IF_METHOD(SET_KEY, oplist)( \ + M_INLINE void \ + M_F(name, _set_at)(concurrent_t out, M_GET_KEY_TYPE oplist const key, M_GET_VALUE_TYPE oplist const data) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + M_CALL_SET_KEY(oplist, out->data, key, data); \ + M_F(name, _write_signal)(out); \ + M_F(name, _write_unlock)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(GET_KEY, oplist)( \ + M_INLINE bool \ + M_F(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_ASSERT (out_data != NULL); \ + M_F(name, _read_lock)(out); \ + M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \ + if (p != NULL) { \ + M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \ + } \ + M_F(name, _read_unlock)(out); \ + return p != NULL; \ + } \ + ,) \ + \ + M_IF_METHOD(SAFE_GET_KEY, oplist)( \ + M_INLINE void \ + M_F(name, _safe_get_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_ASSERT (out_data != NULL); \ + M_F(name, _write_lock)(out); \ + M_GET_VALUE_TYPE oplist *p = M_CALL_SAFE_GET_KEY(oplist, out->data, key); \ + M_ASSERT (p != NULL); \ + M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \ + M_F(name, _write_unlock)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(ERASE_KEY, oplist)( \ + M_INLINE bool \ + M_F(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \ + /* We suppose that the container has 'infinite' capacity, so \ + we won't signal that a free space has been created */ \ + M_F(name, _write_unlock)(out); \ + return b; \ + } \ + ,) \ + \ + M_IF_METHOD(PUSH, oplist)( \ + M_INLINE void \ + M_F(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + M_CALL_PUSH(oplist, out->data, data); \ + M_F(name, _write_signal)(out); \ + M_F(name, _write_unlock)(out); \ + } \ + \ + M_EMPLACE_QUEUE_DEF(name, concurrent_t, M_F(name, _emplace), M_GET_OPLIST oplist, M_EMPLACE_QUEUE_GENE) \ + ,) \ + \ + M_IF_METHOD(POP, oplist)( \ + M_INLINE void \ + M_F(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + M_CALL_POP(oplist, p, out->data); \ + /* See comment above */ \ + M_F(name, _write_unlock)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(PUSH_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + M_CALL_PUSH_MOVE(oplist, out->data, data); \ + M_F(name, _write_signal)(out); \ + M_F(name, _write_unlock)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(POP_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + M_CALL_POP_MOVE(oplist, p, out->data); \ + /* See comment above */ \ + M_F(name, _write_unlock)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(GET_STR, oplist)( \ + M_INLINE void \ + M_F(name, _get_str)(m_string_t str, concurrent_t const out, bool a) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _read_lock)(out); \ + M_CALL_GET_STR(oplist, str, out->data, a); \ + M_F(name, _read_unlock)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(OUT_STR, oplist)( \ + M_INLINE void \ + M_F(name, _out_str)(FILE *f, concurrent_t const out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _read_lock)(out); \ + M_CALL_OUT_STR(oplist, f, out->data); \ + M_F(name, _read_unlock)(out); \ + } \ + ,) \ + \ + M_IF_METHOD(PARSE_STR, oplist)( \ + M_INLINE bool \ + M_F(name, _parse_str)(concurrent_t out, const char str[], const char **e) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \ + M_F(name, _write_signal)(out); \ + M_F(name, _write_unlock)(out); \ + return b; \ + } \ + ,) \ + \ + M_IF_METHOD(IN_STR, oplist)( \ + M_INLINE bool \ + M_F(name, _in_str)(concurrent_t out, FILE *f) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + bool b = M_CALL_IN_STR(oplist, out->data, f); \ + M_F(name, _write_signal)(out); \ + M_F(name, _write_unlock)(out); \ + return b; \ + } \ + ,) \ + \ + M_IF_METHOD(OUT_SERIAL, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, concurrent_t const out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _read_lock)(out); \ + m_serial_return_code_t r = M_CALL_OUT_SERIAL(oplist, f, out->data); \ + M_F(name, _read_unlock)(out); \ + return r; \ + } \ + ,) \ + \ + M_IF_METHOD(IN_SERIAL, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(concurrent_t out, m_serial_read_t f) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _write_lock)(out); \ + m_serial_return_code_t r = M_CALL_IN_SERIAL(oplist, out->data, f); \ + M_F(name, _write_signal)(out); \ + M_F(name, _write_unlock)(out); \ + return r; \ + } \ + ,) \ + \ + M_IF_METHOD(EQUAL, oplist)( \ + M_INLINE bool \ + M_F(name, _equal_p)(concurrent_t const out1, concurrent_t const out2) \ + { \ + M_C0NCURRENT_CONTRACT(out1); \ + M_C0NCURRENT_CONTRACT(out2); \ + if (M_UNLIKELY (out1 == out2)) return true; \ + /* See comment above on mutal mutexs */ \ + if (out1 < out2) { \ + M_F(name, _read_lock)(out1); \ + M_F(name, _read_lock)(out2); \ + } else { \ + M_F(name, _read_lock)(out2); \ + M_F(name, _read_lock)(out1); \ + } \ + bool b = M_CALL_EQUAL(oplist, out1->data, out2->data); \ + if (out1 < out2) { \ + M_F(name, _read_unlock)(out2); \ + M_F(name, _read_unlock)(out1); \ + } else { \ + M_F(name, _read_unlock)(out1); \ + M_F(name, _read_unlock)(out2); \ + } \ + return b; \ + } \ + ,) \ + \ + M_IF_METHOD(GET_KEY, oplist)( \ + M_INLINE bool \ + M_F(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_ASSERT (out_data != NULL); \ + bool ret = false; \ + M_F(name, _read_lock)(out); \ + while (true) { \ + M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \ + if (p != NULL) { \ + M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \ + ret = true; \ + break; \ + } \ + if (blocking == false) break; \ + /* No data: wait for a write to signal some data */ \ + M_F(name, _read_wait)(out); \ + } \ + M_F(name, _read_unlock)(out); \ + return ret; \ + } \ + ,) \ + \ + M_IF_METHOD2(POP, EMPTY_P, oplist)( \ + M_INLINE bool \ + M_F(name, _pop_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_ASSERT (p != NULL); \ + bool ret = false; \ + M_F(name, _write_lock)(out); \ + while (true) { \ + if (!M_CALL_EMPTY_P(oplist, out->data)) { \ + M_CALL_POP(oplist, p, out->data); \ + ret = true; \ + break; \ + } \ + if (blocking == false) break; \ + /* No data: wait for a write to signal some data */ \ + M_F(name, _write_wait)(out); \ + } \ + M_F(name, _write_unlock)(out); \ + return ret; \ + } \ + ,) \ + \ + M_IF_METHOD2(POP_MOVE, EMPTY_P, oplist)( \ + M_INLINE bool \ + M_F(name, _pop_move_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_ASSERT (p != NULL); \ + bool ret = false; \ + M_F(name, _write_lock)(out); \ + while (true) { \ + if (!M_CALL_EMPTY_P(oplist, out->data)) { \ + M_CALL_POP_MOVE(oplist, p, out->data); \ + ret = true; \ + break; \ + } \ + if (blocking == false) break; \ + /* No data: wait for a write to signal some data */ \ + M_F(name, _write_wait)(out); \ + } \ + M_F(name, _write_unlock)(out); \ + return ret; \ + } \ + ,) \ + \ + M_IF_METHOD(HASH, oplist)( \ + M_INLINE size_t \ + M_F(name, _hash)(concurrent_t const out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + M_F(name, _read_lock)(out); \ + size_t h = M_CALL_HASH(oplist, out->data); \ + M_F(name, _read_unlock)(out); \ + /* The hash is unchanged by the concurrent container */ \ + return h; \ + } \ + ,) \ + + +/******************************** INTERNAL ***********************************/ + +/* Deferred evaluation for the RP concurrent definition, + so that all arguments are evaluated before further expansion */ +#define M_C0NCURRENT_RP_DEF_P1(arg) M_ID( M_C0NCURRENT_RP_DEF_P2 arg ) + +/* Validate the value oplist before going further */ +#define M_C0NCURRENT_RP_DEF_P2(name, type, oplist, concurrent_t) \ + M_IF_OPLIST(oplist)(M_C0NCURRENT_RP_DEF_P3, M_C0NCURRENT_RP_DEF_FAILURE)(name, type, oplist, concurrent_t) + +/* Stop processing with a compilation failure */ +#define M_C0NCURRENT_RP_DEF_FAILURE(name, type, oplist, concurrent_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_RP_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist)) + +/* Internal RP concurrent definition + - name: prefix to be used + - type: type of the sub container + - oplist: oplist of the type of the sub container + - concurrent_t: alias for M_F(name, _t) [ type of the container ] + */ +#define M_C0NCURRENT_RP_DEF_P3(name, type, oplist, concurrent_t) \ + M_C0NCURRENT_RP_DEF_TYPE(name, type, oplist, concurrent_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_C0NCURRENT_RP_DEF_CORE(name, type, oplist, concurrent_t) \ + M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t) + +/* Define the type of a RP concurrent container */ +#define M_C0NCURRENT_RP_DEF_TYPE(name, type, oplist, concurrent_t) \ + \ + typedef struct M_F(name, _s) { \ + struct M_F(name, _s) *self; \ + m_mutex_t lock; \ + m_cond_t rw_done; \ + size_t read_count; \ + bool writer_waiting; \ + m_cond_t there_is_data; /* condition raised when there is data */ \ + type data; \ + } concurrent_t[1]; \ + \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + typedef type M_F(name, _subtype_ct); \ + +/* Define the internal services for the lock strategy of a RP container */ +#define M_C0NCURRENT_RP_DEF_CORE(name, type, oplist, concurrent_t) \ + \ + M_INLINE void \ + M_F(name, _internal_init)(concurrent_t out) \ + { \ + m_mutex_init(out->lock); \ + m_cond_init(out->rw_done); \ + m_cond_init(out->there_is_data); \ + out->self = out; \ + out->read_count = 0; \ + out->writer_waiting = false; \ + M_C0NCURRENT_CONTRACT(out); \ + } \ + \ + M_INLINE void \ + M_F(name, _internal_clear)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_clear(out->lock); \ + m_cond_clear(out->rw_done); \ + m_cond_clear(out->there_is_data); \ + out->self = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _read_lock)(const concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + struct M_F(name, _s) *self = out->self; \ + m_mutex_lock (self->lock); \ + while (self->writer_waiting == true) { \ + m_cond_wait(self->rw_done, self->lock); \ + } \ + self->read_count ++; \ + m_mutex_unlock (self->lock); \ + } \ + \ + M_INLINE void \ + M_F(name, _read_unlock)(const concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + struct M_F(name, _s) *self = out->self; \ + m_mutex_lock (self->lock); \ + self->read_count --; \ + if (self->read_count == 0) { \ + m_cond_broadcast (self->rw_done); \ + } \ + m_mutex_unlock (self->lock); \ + } \ + \ + M_INLINE void \ + M_F(name, _write_lock)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_lock (out->lock); \ + while (out->writer_waiting == true) { \ + m_cond_wait(out->rw_done, out->lock); \ + } \ + out->writer_waiting = true; \ + while (out->read_count > 0) { \ + m_cond_wait(out->rw_done, out->lock); \ + } \ + m_mutex_unlock (out->lock); \ + } \ + \ + M_INLINE void \ + M_F(name, _write_unlock)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_lock (out->lock); \ + out->writer_waiting = false; \ + m_cond_broadcast (out->rw_done); \ + m_mutex_unlock (out->lock); \ + } \ + \ + M_INLINE void \ + M_F(name, _read_wait)(const concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + struct M_F(name, _s) *self = out->self; \ + M_ASSERT (self == out); \ + m_mutex_lock (out->self->lock); \ + self->read_count --; \ + if (self->read_count == 0) { \ + m_cond_broadcast (self->rw_done); \ + } \ + m_cond_wait(self->there_is_data, self->lock); \ + while (self->writer_waiting == true) { \ + m_cond_wait(self->rw_done, self->lock); \ + } \ + self->read_count ++; \ + m_mutex_unlock (out->self->lock); \ + } \ + \ + M_INLINE void \ + M_F(name, _write_wait)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_lock (out->lock); \ + out->writer_waiting = false; \ + m_cond_broadcast (out->rw_done); \ + m_cond_wait(out->there_is_data, out->lock); \ + while (out->writer_waiting == true) { \ + m_cond_wait(out->rw_done, out->lock); \ + } \ + out->writer_waiting = true; \ + while (out->read_count > 0) { \ + m_cond_wait(out->rw_done, out->lock); \ + } \ + m_mutex_unlock (out->lock); \ + } \ + \ + M_INLINE void \ + M_F(name, _write_signal)(concurrent_t out) \ + { \ + M_C0NCURRENT_CONTRACT(out); \ + m_mutex_lock (out->lock); \ + m_cond_broadcast(out->there_is_data); \ + m_mutex_unlock (out->lock); \ + } \ + + +/******************************** INTERNAL ***********************************/ + +#if M_USE_SMALL_NAME +#define CONCURRENT_DEF M_CONCURRENT_DEF +#define CONCURRENT_DEF_AS M_CONCURRENT_DEF_AS +#define CONCURRENT_RP_DEF M_CONCURRENT_RP_DEF +#define CONCURRENT_RP_DEF_AS M_CONCURRENT_RP_DEF_AS +#define CONCURRENT_OPLIST M_CONCURRENT_OPLIST +#endif + +#endif diff --git a/components/mlib/m-core.h b/components/mlib/m-core.h new file mode 100644 index 00000000..2b3b0423 --- /dev/null +++ b/components/mlib/m-core.h @@ -0,0 +1,5297 @@ +/* + * M*LIB - Extended Pre-processing macros module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_MACRO_H +#define MSTARLIB_MACRO_H + +#include +#include +#include +#include +#include +#include /* For toupper, tolower, isprint, isspace */ +#include +#include /* For abort, malloc, realloc, free, strtol, strtoul, strtoll, strtoull, strtof, strtod, strtold, rand */ + +/* By default, always use stdio. Can be turned off in specific environment if needed + by defining M_USE_STDIO to 0 */ +#ifndef M_USE_STDIO +# define M_USE_STDIO 1 +#endif +#if M_USE_STDIO +# include +#endif + +/* By default, always use stdarg. Can be turned off in specific environment if needed + by defining M_USE_STDARG to 0 */ +#ifndef M_USE_STDARG +# define M_USE_STDARG 1 +#endif +#if M_USE_STDARG +# include +#endif + +/* By default, define also the small name API (without the m_ prefix) */ +#ifndef M_USE_SMALL_NAME +# define M_USE_SMALL_NAME 1 +#endif + +/* Used functions of the libc which are locale dependent: + toupper, tolower, isprint, isspace + strtol, strtoul, strtoll, strtoull, strtof, strtod, strtold + printf, fprintf, fscanf + Theses functions may behave differently in function of the locale. +*/ + +/***************************************************************/ +/************************ Compiler Macro ***********************/ +/***************************************************************/ + +/* Define M*LIB version */ +#define M_CORE_VERSION_MAJOR 0 +#define M_CORE_VERSION_MINOR 7 +#define M_CORE_VERSION_PATCHLEVEL 1 + +/* M_ASSUME is equivalent to M_ASSERT, but gives hints to compiler + about how to optimize the code if NDEBUG is defined. + It is worth in very specific places usually. */ +#if !defined(NDEBUG) +# define M_ASSUME(x) M_ASSERT(x) +#elif defined(__GNUC__) \ + && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 +# define M_ASSUME(x) \ + ( (x) ? (void) 0 : __builtin_unreachable()) +#elif defined(_MSC_VER) +# define M_ASSUME(x) __assume(x) +#else +# define M_ASSUME(x) M_ASSERT(x) +#endif + +/* M_LIKELY / M_UNLIKELY gives hints on the compiler of the likehood + of the given condition */ +#ifdef __GNUC__ +# define M_LIKELY(cond) __builtin_expect(!!(cond), 1) +# define M_UNLIKELY(cond) __builtin_expect(!!(cond), 0) +#else +# define M_LIKELY(cond) (cond) +# define M_UNLIKELY(cond) (cond) +#endif + +/* Define the exclusion size so that 2 atomic variables are in + separate cache lines. This prevents false sharing to occur within the + CPU. */ +#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) +# define M_ALIGN_FOR_CACHELINE_EXCLUSION 128 +#else +# define M_ALIGN_FOR_CACHELINE_EXCLUSION 64 +#endif + +/* Deprecated attribute for a function */ +#if defined(__GNUC__) && __GNUC__ >= 4 +# define M_ATTR_DEPRECATED __attribute__((deprecated)) +#else +# define M_ATTR_DEPRECATED +#endif + +/* Extension attribute to silent warnings on extensions */ +#if defined(__GNUC__) +# define M_ATTR_EXTENSION __extension__ +#else +# define M_ATTR_EXTENSION +#endif + +/* Extension attribute for no return function */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +# define M_ATTR_NO_RETURN _Noreturn +#elif defined(__GNUC__) +# define M_ATTR_NO_RETURN __attribute__ ((noreturn)) +#elif defined(_MSC_VER) +# define M_ATTR_NO_RETURN __declspec(noreturn) +#else +# define M_ATTR_NO_RETURN +#endif + +/* The cold attribute on functions is used to inform the compiler + that the function is unlikely to be executed. */ +#if defined(__GNUC__) +# define M_ATTR_COLD_FUNCTION __attribute__ ((cold)) +#else +# define M_ATTR_COLD_FUNCTION +#endif + + +/* Ignore some warnings detected by some compilers in the library. + * Whatever we do, there is some warnings that cannot be fixed. + * So they are ignored in order to avoid polluting the user with + * theses warnings. They are: + * + * * If we build in C++ mode, they are warnings about using the C + * dialect. It is expected as M*LIB is a C library. + * + * * For clang, the generated functions may not be always used, + * and CLANG failed to realize it. + * See https://bugs.llvm.org//show_bug.cgi?id=22712 + * + * * A manualy created buffer is given to fscanf. It is needed + * to give the size of the array of char to fscanf (this is the safe way). + */ +#if defined(__clang__) && defined(__cplusplus) + +/* Warnings disabled for CLANG in C++ mode */ +#if __clang_major__ >= 6 +#define M_BEGIN_PROTECTED_CODE \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + _Pragma("clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"") \ + _Pragma("clang diagnostic ignored \"-Wunused-function\"") \ + _Pragma("clang diagnostic ignored \"-Wformat-nonliteral\"") +#else +#define M_BEGIN_PROTECTED_CODE \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + _Pragma("clang diagnostic ignored \"-Wunused-function\"") \ + _Pragma("clang diagnostic ignored \"-Wformat-nonliteral\"") +#endif + +#define M_END_PROTECTED_CODE \ + _Pragma("clang diagnostic pop") + +#elif defined(__GNUC__) && defined(__cplusplus) + +/* Warnings disabled for GNU C in C++ mode + * However, G++ doesn't support well disabling temporary warnings. + * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431 + */ +#define M_BEGIN_PROTECTED_CODE \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wold-style-cast\"") \ + _Pragma("GCC diagnostic ignored \"-Wzero-as-null-pointer-constant\"") \ + _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") + +#define M_END_PROTECTED_CODE \ + _Pragma("GCC diagnostic pop") + +#elif defined(__clang__) + +/* Warnings disabled for CLANG in C mode */ +#define M_BEGIN_PROTECTED_CODE \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunused-function\"") \ + _Pragma("clang diagnostic ignored \"-Wformat-nonliteral\"") + +#define M_END_PROTECTED_CODE \ + _Pragma("clang diagnostic pop") + +#elif defined(__GNUC__) + +#if __GNUC__ >= 12 +/* Warnings disabled for GNU C in C mode (Wstringop-overflow produces false warnings) */ +#define M_BEGIN_PROTECTED_CODE \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") \ + _Pragma("GCC diagnostic ignored \"-Wstringop-overflow\"") +#else +/* Warnings disabled for GNU C in C mode */ +#define M_BEGIN_PROTECTED_CODE \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") +#endif + +#define M_END_PROTECTED_CODE \ + _Pragma("GCC diagnostic pop") + +#else + +/* No warnings disabled */ +#define M_BEGIN_PROTECTED_CODE +#define M_END_PROTECTED_CODE + +#endif + + +/* Autodetect if Address sanitizer is run */ +#if defined(__has_feature) +# if __has_feature(address_sanitizer) +# define M_ADDRESS_SANITIZER 1 +# endif +#endif +#if defined(__SANITIZE_ADDRESS__) && !defined(M_ADDRESS_SANITIZER) +# define M_ADDRESS_SANITIZER 1 +#endif + +/* For visual C++, we need a standard compliant C99/C++11 */ +#if defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL +# error Miss option '/Zc:preprocessor' to enable a standard-compliant C99/C++11 preprocessor. +#endif + +/* + * Do not create the following symbols that are defined in GLIBC malloc.h + * M_MXFAST + * M_NLBLKS + * M_GRAIN + * M_KEEP + * M_TRIM_THRESHOLD + * M_TOP_PAD + * M_MMAP_THRESHOLD + * M_MMAP_MAX + * M_CHECK_ACTION + * M_PERTURB + * M_ARENA_TEST + * M_ARENA_MAX + */ + +M_BEGIN_PROTECTED_CODE + + +/************************************************************/ +/************************* LINKAGE **************************/ +/************************************************************/ + +/* The following semantics apply to inline in C99: + - inline: No generation of an extern visible function. May inline or may call the external function. + - extern inline: externally visible code is emitted, so at most one translation unit can use this. + - static inline: No generation of an extern visible function. May inline or may call one static function. +*/ +/* If the user requests to use only declaration for all M*LIB functions globally (M_USE_DECL), + it requests no inlining to the compiler and emits code as weak symbol. + Then at most one translation unit should request the definition of all functions (M_USE_DEF). + This is only supported by GCC and CLANG. You should also use the options -ffunction-sections -fdata-sections -Wl,--gc-sections + Otherwise it would increase the size of the executable. + If M_USE_FINE_GRAINED_LINKAGE, then the mechanism can be turn on / off dynamically in compilation time + to decl or def the functions in function of M_USE_DECL / M_USE_DEF + Otherwise uses the classic "M_INLINE" (inline for C++ or static inline for C) + inline in C++ as a weak definition by default, which may reduce code size in executable. +*/ +#if !defined(__cplusplus) && defined(__GNUC__) +# define M_INLINE_PRAGMA _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") +#else +# define M_INLINE_PRAGMA +#endif +#if defined(__GNUC__) && defined(M_USE_FINE_GRAINED_LINKAGE) +#define M_INLINE \ + M_IF_EMPTY(M_USE_DECL)( \ + M_IF_EMPTY(M_USE_DEF)(M_INLINE_PRAGMA __attribute__((noinline)) extern, \ + M_INLINE_PRAGMA __attribute__((weak, noinline)) extern), \ + static inline) +#elif defined(__GNUC__) && defined(M_USE_DECL) +# ifdef M_USE_DEF +# define M_INLINE M_INLINE_PRAGMA __attribute__((noinline)) extern +# else +# define M_INLINE M_INLINE_PRAGMA __attribute__((weak, noinline)) extern +#endif +#elif defined(__cplusplus) +#define M_INLINE inline +#else +#define M_INLINE static inline +#endif + + + +/************************************************************/ +/********************* MEMORY handling **********************/ +/************************************************************/ + +/* Default MEMORY handling macros. + Can be overloaded by user code. +*/ + +/* Define a C and C++ version for default memory allocators. + Note: For C build, we explicitly don't cast the return value of + malloc, realloc as it is safer (compilers shall warn in case + of invalid implicit cast, whereas they won't if there is an + explicit cast) */ + +/* Define allocators for object: + * void *M_MEMORY_ALLOC(type): Return a pointer to a new object of type 'type' + * It returns NULL in case of memory allocation failure. + * void M_MEMORY_DEL(ptr): Free the object associated to the pointer. + */ +#ifndef M_MEMORY_ALLOC +#ifdef __cplusplus +# include +# define M_MEMORY_ALLOC(type) ((type*)std::malloc (sizeof (type))) +# define M_MEMORY_DEL(ptr) std::free(ptr) +#else +# define M_MEMORY_ALLOC(type) malloc (sizeof (type)) +# define M_MEMORY_DEL(ptr) free(ptr) +#endif +#endif + +/* Define allocators for array + * void *M_MEMORY_REALLOC(type, ptr, n): Return a pointer to a new array of 'n' object of type 'type' + * If ptr is NULL, it creates a new array. + * If ptr is not null, it reallocates the given array to the new size. + * It returns NULL in case of memory allocation failure. + * void M_MEMORY_FREE(ptr): Free the object associated to the array. + */ +#ifndef M_MEMORY_REALLOC +#ifdef __cplusplus +# include +# define M_MEMORY_REALLOC(type, ptr, n) \ + ((type*) (M_UNLIKELY ((n) > SIZE_MAX / sizeof(type)) ? NULL : std::realloc ((ptr), (n)*sizeof (type)))) +# define M_MEMORY_FREE(ptr) std::free(ptr) +#else +# define M_MEMORY_REALLOC(type, ptr, n) (M_UNLIKELY ((n) > SIZE_MAX / sizeof(type)) ? NULL : realloc ((ptr), (n)*sizeof (type))) +# define M_MEMORY_FREE(ptr) free(ptr) +#endif +#endif + +/* This macro is called on memory allocation failure. + * By default, it raises a fatal error. + * NOTE: Can be overloaded by user code. +*/ +#ifndef M_MEMORY_FULL +#define M_MEMORY_FULL(size) \ + M_RAISE_FATAL("Cannot allocate %zu bytes of memory at (%s:%s:%d).\n", \ + (size_t) (size), __FILE__, __func__, __LINE__) +#endif + + +/************************************************************/ +/********************* ERROR handling **********************/ +/************************************************************/ + +/* Raise a fatal error and terminate the current execution flow. + Default behavior performs the following actions: + * format and print the error message on stderr + * terminate the program. + Can be overloaded by the user code. + Usage: + M_RAISE_FATAL(message, ...) + with + message, a printf formatted message associated to the error +*/ +#ifndef M_RAISE_FATAL +#if M_USE_STDIO == 1 +#define M_RAISE_FATAL(...) do { \ + fprintf(stderr, "ERROR(M*LIB): " __VA_ARGS__); \ + abort(); \ + } while (0) +#else +# error Without stdio.h, definitions for macro M_RAISE_FATAL is mandatory. +#endif +#endif + + +/* Define the default assertion macro used by M*LIB. + * By default, it is an encapsulation of CLIB assert. + * NOTE: Can be overiden by user if it needs to keep finer access + * on the assertions. + */ +#ifndef M_ASSERT +#define M_ASSERT(expr) assert(expr) +#endif + + +/* If within the M*LIB tests, perform additional (potentialy slow) checks + * By default, it is an encapsulation of CLIB assert for M*LIB own tests. + * NOTE: Can be overiden by user if it needs to keep finer access + * on the assertions. + */ +#ifndef M_ASSERT_SLOW +# if defined(M_USE_ADDITIONAL_CHECKS) && M_USE_ADDITIONAL_CHECKS +# define M_ASSERT_SLOW(n) assert(n) +# else +# define M_ASSERT_SLOW(n) (void) 0 +# endif +#endif + + +/* Always perform a runtime check of the given condition + * NOTE: Can be overiden by user if it needs to keep finer access + * on the assertions or display message on another device. + */ +#ifndef M_ASSERT_INIT +#define M_ASSERT_INIT(expr, object) { \ + if (!(expr)) { \ + M_RAISE_FATAL("Cannot initialize %s at (%s:%s:%d): %s\n", \ + (object), __FILE__, __func__, __LINE__, #expr); \ + } } while (0) +#endif + + +/* Define an assertion check on an index, compared to its maximum. + * The index is supposed to be unsigned. + * It is only used to valid user input, not an intermediary calculus. + * NOTE: Can be overiden by user if it needs to keep access under control + * even on release mode + * NOTE: (index)-(index) is used to represent 0, but to avoid spurious + * warning by the compiler on "comparaison is always true" for unsigned + * numbers (It is properly optimized in 0). + * */ +#ifndef M_ASSERT_INDEX +#define M_ASSERT_INDEX(index, max) do { \ + M_ASSERT((index) >= ((index)-(index)) && (index) < (max)); \ + } while (0) +#endif + + +/* Terminate the compilation of the current unit with an error message. + The error parameter is a C token which classifies the error + with an optional message detailling the error. + It is not an expression and shall not be used in an expression. + It shall be used outside of a function, therefore it is not an expression. + In C99, it uses a bitfield to be compatible with most compilers + (so that it properly displays 'error' on the command line + Quite usefull to terminate with a proper error message rather than + a garbage of error due to incorrect code generation in the methods + expansion. + */ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && defined(static_assert)) || defined(__cplusplus) +# define M_STATIC_FAILURE(error, msg) static_assert(false, #error ": " msg); +#else +# define M_STATIC_FAILURE(error, msg) struct error { int error : 0;}; +#endif + + +/* Test at compile time if the given condition is true. + The error parameter is a C token which classifies the error + with an optional message detailling the error. + In C99, it uses a bitfield to be compatible with most compilers + (so that it properly displays 'error' on the command line + C11 static Assert is not usable in expression, + but is usable in struct declaration. Therefore we create + a dummy struct, define the static assert within it + compute its size and cast it to void. + NOTE: Some implementation claims to be C11 (default mode) but fails + to deliver a working assert.h with static_assert so we test for the + explicity precense of the macro static_assert. +*/ +#ifdef __cplusplus +# define M_STATIC_ASSERT(cond, error, msg) \ + ([] { static_assert(cond, #error ": " msg); } ()) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && defined(static_assert) +# define M_STATIC_ASSERT(cond, error, msg) \ + ((void) sizeof(struct { int error; static_assert(cond, #error ": " msg);})) +#else +# define M_STATIC_ASSERT(cond, error, msg) \ + ((void) sizeof(struct { int error : !!(cond);})) +#endif + + +/* M_UNLIKELY used to test conditions that will trigger memory errors. + It is needed so that it can be disabled in coverage mode. */ +#define M_UNLIKELY_NOMEM(cond) M_UNLIKELY(cond) + + +/***************************************************************/ +/****************** Preprocessing Times Macro ******************/ +/***************************************************************/ + +/* Maximum number of argument which can be at least handled + by the macros of this header. + Can be increased for future release of this header. +*/ +#define M_MAX_NB_ARGUMENT 52 + + +/* Basic handling of concatenation of symbols: + * M_C, M_C3, M_C4 + */ +#define M_C2I(a, ...) a ## __VA_ARGS__ +#define M_C(a, ...) M_C2I(a, __VA_ARGS__) +#define M_C3I(a, b, ...) a ## b ## __VA_ARGS__ +#define M_C3(a, b, ...) M_C3I(a ,b, __VA_ARGS__) +#define M_C4I(a, b, c, ...) a ## b ## c ## __VA_ARGS__ +#define M_C4(a, b, c, ...) M_C4I(a ,b, c, __VA_ARGS__) + + +/* Generation of function name by concatenation + Can be ovverrided by user +*/ +#ifndef M_F +#define M_F(a, b) M_C2I(a, b) +#endif + +/* Concatenation of function name that enable developer to use customizaton + developer shall provide its own name customization by forcing M_F to it + then provide its own suffix in the namespace M_OVERRIDE_ ## suffix (): + Each generated suffix shall start with a comma. +#define M_F(a,b) M_OVERRIDE_F(a,b) +#define M_OVERRIDE__clear() , _cleanup , +#include "m-core.h" +*/ +#define M_OVERRIDE_F(a, b) M_C(a, M_RET_ARG2( M_C(M_OVERRIDE_, b)() , b)) + +/* Inverse 0 into 1 and 1 into 0 */ +#define M_INVI_0 1 +#define M_INVI_1 0 +#define M_INV(x) M_C(M_INVI_, x) + +/* Perform a AND between the two boolean inputs */ +#define M_ANDI_00 0 +#define M_ANDI_01 0 +#define M_ANDI_10 0 +#define M_ANDI_11 1 +#define M_AND(x,y) M_C3(M_ANDI_, x, y) + +/* Perform a AND between the three boolean inputs */ +#define M_ANDI_000 0 +#define M_ANDI_001 0 +#define M_ANDI_010 0 +#define M_ANDI_011 0 +#define M_ANDI_100 0 +#define M_ANDI_101 0 +#define M_ANDI_110 0 +#define M_ANDI_111 1 +#define M_AND3(x,y,z) M_C4(M_ANDI_, x, y, z) + +/* Perform a OR between the boolean inputs */ +#define M_ORI_00 0 +#define M_ORI_01 1 +#define M_ORI_10 1 +#define M_ORI_11 1 +#define M_OR(x,y) M_C3(M_ORI_, x, y) + +/* Perform a OR between the three boolean inputs */ +#define M_ORI_000 0 +#define M_ORI_001 1 +#define M_ORI_010 1 +#define M_ORI_011 1 +#define M_ORI_100 1 +#define M_ORI_101 1 +#define M_ORI_110 1 +#define M_ORI_111 1 +#define M_OR3(x,y,z) M_C4(M_ORI_, x, y, z) + + +/* Increment the number given as argument (from [0..52[) + Generated by: + for i in $(seq 0 52) ; do printf "#define M_INC_%d %d\n" $i $(($i + 1)) ; done + */ +#define M_INC(x) M_C(M_INC_, x) +#define M_INC_0 1 +#define M_INC_1 2 +#define M_INC_2 3 +#define M_INC_3 4 +#define M_INC_4 5 +#define M_INC_5 6 +#define M_INC_6 7 +#define M_INC_7 8 +#define M_INC_8 9 +#define M_INC_9 10 +#define M_INC_10 11 +#define M_INC_11 12 +#define M_INC_12 13 +#define M_INC_13 14 +#define M_INC_14 15 +#define M_INC_15 16 +#define M_INC_16 17 +#define M_INC_17 18 +#define M_INC_18 19 +#define M_INC_19 20 +#define M_INC_20 21 +#define M_INC_21 22 +#define M_INC_22 23 +#define M_INC_23 24 +#define M_INC_24 25 +#define M_INC_25 26 +#define M_INC_26 27 +#define M_INC_27 28 +#define M_INC_28 29 +#define M_INC_29 30 +#define M_INC_30 31 +#define M_INC_31 32 +#define M_INC_32 33 +#define M_INC_33 34 +#define M_INC_34 35 +#define M_INC_35 36 +#define M_INC_36 37 +#define M_INC_37 38 +#define M_INC_38 39 +#define M_INC_39 40 +#define M_INC_40 41 +#define M_INC_41 42 +#define M_INC_42 43 +#define M_INC_43 44 +#define M_INC_44 45 +#define M_INC_45 46 +#define M_INC_46 47 +#define M_INC_47 48 +#define M_INC_48 49 +#define M_INC_49 50 +#define M_INC_50 51 +#define M_INC_51 52 +#define M_INC_52 53 +#define M_INC_53 M_OVERFLOW +#define M_INC_M_OVERFLOW M_OVERFLOW +#define M_INC_M_UNDERFLOW M_UNDERFLOW + + +/* Decrement the number given in argument (from [0..52[) + Generated by: + for i in $(seq 1 52) ; do printf "#define M_DEC_%d %d\n" $i $(($i - 1)) ; done +*/ +#define M_DEC(x) M_C(M_DEC_, x) +#define M_DEC_M_UNDERFLOW M_UNDERFLOW +#define M_DEC_M_OVERFLOW M_OVERFLOW +#define M_DEC_0 M_UNDERFLOW +#define M_DEC_1 0 +#define M_DEC_2 1 +#define M_DEC_3 2 +#define M_DEC_4 3 +#define M_DEC_5 4 +#define M_DEC_6 5 +#define M_DEC_7 6 +#define M_DEC_8 7 +#define M_DEC_9 8 +#define M_DEC_10 9 +#define M_DEC_11 10 +#define M_DEC_12 11 +#define M_DEC_13 12 +#define M_DEC_14 13 +#define M_DEC_15 14 +#define M_DEC_16 15 +#define M_DEC_17 16 +#define M_DEC_18 17 +#define M_DEC_19 18 +#define M_DEC_20 19 +#define M_DEC_21 20 +#define M_DEC_22 21 +#define M_DEC_23 22 +#define M_DEC_24 23 +#define M_DEC_25 24 +#define M_DEC_26 25 +#define M_DEC_27 26 +#define M_DEC_28 27 +#define M_DEC_29 28 +#define M_DEC_30 29 +#define M_DEC_31 30 +#define M_DEC_32 31 +#define M_DEC_33 32 +#define M_DEC_34 33 +#define M_DEC_35 34 +#define M_DEC_36 35 +#define M_DEC_37 36 +#define M_DEC_38 37 +#define M_DEC_39 38 +#define M_DEC_40 39 +#define M_DEC_41 40 +#define M_DEC_42 41 +#define M_DEC_43 42 +#define M_DEC_44 43 +#define M_DEC_45 44 +#define M_DEC_46 45 +#define M_DEC_47 46 +#define M_DEC_48 47 +#define M_DEC_49 48 +#define M_DEC_50 49 +#define M_DEC_51 50 +#define M_DEC_52 51 + + +/* Add two integers with both integers from [0 to M_MAX_NB_ARGUMENT[ + Generated by: + for i in $(seq 0 52) ; do printf "#define M_ADDI_%d(n) " $i ; for j in $(seq 1 $i) ; do printf "M_INC(";done ; printf "n" ; for j in $(seq 1 $i) ; do printf ")" ;done ; printf "\n" ; done */ +#define M_ADD(x,y) M_C(M_ADDI_, y)(x) +#define M_ADDI_0(n) n +#define M_ADDI_1(n) M_INC(n) +#define M_ADDI_2(n) M_INC(M_INC(n)) +#define M_ADDI_3(n) M_INC(M_INC(M_INC(n))) +#define M_ADDI_4(n) M_INC(M_INC(M_INC(M_INC(n)))) +#define M_ADDI_5(n) M_INC(M_INC(M_INC(M_INC(M_INC(n))))) +#define M_ADDI_6(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))) +#define M_ADDI_7(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))) +#define M_ADDI_8(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))) +#define M_ADDI_9(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))) +#define M_ADDI_10(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))) +#define M_ADDI_11(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))) +#define M_ADDI_12(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))) +#define M_ADDI_13(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))) +#define M_ADDI_14(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))) +#define M_ADDI_15(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))) +#define M_ADDI_16(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))) +#define M_ADDI_17(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))) +#define M_ADDI_18(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))) +#define M_ADDI_19(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))) +#define M_ADDI_20(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))) +#define M_ADDI_21(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))) +#define M_ADDI_22(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))) +#define M_ADDI_23(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))) +#define M_ADDI_24(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))) +#define M_ADDI_25(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))) +#define M_ADDI_26(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))) +#define M_ADDI_27(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))) +#define M_ADDI_28(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))) +#define M_ADDI_29(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))) +#define M_ADDI_30(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))) +#define M_ADDI_31(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))) +#define M_ADDI_32(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))) +#define M_ADDI_33(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))) +#define M_ADDI_34(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))) +#define M_ADDI_35(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))) +#define M_ADDI_36(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))) +#define M_ADDI_37(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))))) +#define M_ADDI_38(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))))) +#define M_ADDI_39(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_40(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_41(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_42(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_43(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_44(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_45(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_46(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_47(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_48(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_49(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_50(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_51(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n))))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_ADDI_52(n) M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(M_INC(n)))))))))))))))))))))))))))))))))))))))))))))))))))) + + +/* Substract two integers with both integers from [0 to M_MAX_NB_ARGUMENT[ + Generated by: + for i in $(seq 0 52) ; do printf "#define M_SUBI_%d(n) " $i ; for j in $(seq 1 $i) ; do printf "M_DEC(";done ; printf "n" ; for j in $(seq 1 $i) ; do printf ")" ;done ; printf "\n" ; done */ +#define M_SUB(x,y) M_C(M_SUBI_, y)(x) +#define M_SUBI_0(n) n +#define M_SUBI_1(n) M_DEC(n) +#define M_SUBI_2(n) M_DEC(M_DEC(n)) +#define M_SUBI_3(n) M_DEC(M_DEC(M_DEC(n))) +#define M_SUBI_4(n) M_DEC(M_DEC(M_DEC(M_DEC(n)))) +#define M_SUBI_5(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))) +#define M_SUBI_6(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))) +#define M_SUBI_7(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))) +#define M_SUBI_8(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))) +#define M_SUBI_9(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))) +#define M_SUBI_10(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))) +#define M_SUBI_11(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))) +#define M_SUBI_12(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))) +#define M_SUBI_13(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))) +#define M_SUBI_14(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))) +#define M_SUBI_15(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))) +#define M_SUBI_16(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))) +#define M_SUBI_17(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))) +#define M_SUBI_18(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))) +#define M_SUBI_19(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))) +#define M_SUBI_20(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))) +#define M_SUBI_21(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))) +#define M_SUBI_22(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))) +#define M_SUBI_23(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))) +#define M_SUBI_24(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))) +#define M_SUBI_25(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))) +#define M_SUBI_26(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))) +#define M_SUBI_27(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))) +#define M_SUBI_28(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))) +#define M_SUBI_29(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))) +#define M_SUBI_30(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))) +#define M_SUBI_31(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))) +#define M_SUBI_32(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))) +#define M_SUBI_33(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))) +#define M_SUBI_34(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))) +#define M_SUBI_35(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))) +#define M_SUBI_36(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))) +#define M_SUBI_37(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))))) +#define M_SUBI_38(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))))) +#define M_SUBI_39(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_40(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_41(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_42(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_43(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_44(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_45(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_46(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_47(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_48(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_49(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_50(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_51(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n))))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_SUBI_52(n) M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(M_DEC(n)))))))))))))))))))))))))))))))))))))))))))))))))))) + + +/* NOTEQUAL(val1,val2) with val from [0 to M_MAX_NB_ARGUMENT[ + Return 1 or 0 if val1=val2 + */ +#define M_NOTEQUAL_0_0 0 +#define M_NOTEQUAL_1_1 0 +#define M_NOTEQUAL_2_2 0 +#define M_NOTEQUAL_3_3 0 +#define M_NOTEQUAL_4_4 0 +#define M_NOTEQUAL_5_5 0 +#define M_NOTEQUAL_6_6 0 +#define M_NOTEQUAL_7_7 0 +#define M_NOTEQUAL_8_8 0 +#define M_NOTEQUAL_9_9 0 +#define M_NOTEQUAL_10_10 0 +#define M_NOTEQUAL_11_11 0 +#define M_NOTEQUAL_12_12 0 +#define M_NOTEQUAL_13_13 0 +#define M_NOTEQUAL_14_14 0 +#define M_NOTEQUAL_15_15 0 +#define M_NOTEQUAL_16_16 0 +#define M_NOTEQUAL_17_17 0 +#define M_NOTEQUAL_18_18 0 +#define M_NOTEQUAL_19_19 0 +#define M_NOTEQUAL_20_20 0 +#define M_NOTEQUAL_21_21 0 +#define M_NOTEQUAL_22_22 0 +#define M_NOTEQUAL_23_23 0 +#define M_NOTEQUAL_24_24 0 +#define M_NOTEQUAL_25_25 0 +#define M_NOTEQUAL_26_26 0 +#define M_NOTEQUAL_27_27 0 +#define M_NOTEQUAL_28_28 0 +#define M_NOTEQUAL_29_29 0 +#define M_NOTEQUAL_30_30 0 +#define M_NOTEQUAL_31_31 0 +#define M_NOTEQUAL_32_32 0 +#define M_NOTEQUAL_33_33 0 +#define M_NOTEQUAL_34_34 0 +#define M_NOTEQUAL_35_35 0 +#define M_NOTEQUAL_36_36 0 +#define M_NOTEQUAL_37_37 0 +#define M_NOTEQUAL_38_38 0 +#define M_NOTEQUAL_39_39 0 +#define M_NOTEQUAL_40_40 0 +#define M_NOTEQUAL_41_41 0 +#define M_NOTEQUAL_42_42 0 +#define M_NOTEQUAL_43_43 0 +#define M_NOTEQUAL_44_44 0 +#define M_NOTEQUAL_45_45 0 +#define M_NOTEQUAL_46_46 0 +#define M_NOTEQUAL_47_47 0 +#define M_NOTEQUAL_48_48 0 +#define M_NOTEQUAL_49_49 0 +#define M_NOTEQUAL_50_50 0 +#define M_NOTEQUAL_51_51 0 +#define M_NOTEQUAL_52_52 0 +#define M_NOTEQUAL(x,y) M_BOOL(M_C4(M_NOTEQUAL_, x, _, y)) + +/* EQUAL(val1,val2) with val from [0 to M_MAX_NB_ARGUMENT[ + Return 1 if val1=val2 or 0 + Example: M_EQUAL(1,2) --> 0 + */ +#define M_EQUAL(x,y) M_INV(M_NOTEQUAL(x,y)) + +/* Return 1 if a < b, 0 otherwise + a and b shall be in [0..M_MAX_NB_ARGUMENT] */ +#define M_LESS_THAN_P(a, b) M_KEYWORD_P(M_UNDERFLOW, M_SUB(a, b)) + +/* Return 1 if a >= b, 0 otherwise + a and b shall be in [0..M_MAX_NB_ARGUMENT] */ +#define M_GREATER_OR_EQUAL_P(a, b) M_INV(M_LESS_THAN_P(a, b)) + +/* Return 1 if a <= b, 0 otherwise + a and b shall be in [0..M_MAX_NB_ARGUMENT] */ +#define M_LESS_OR_EQUAL_P(a, b) M_GREATER_OR_EQUAL_P(b, a) + +/* Return 1 if a > b, 0 otherwise + a and b shall be in [0..M_MAX_NB_ARGUMENT] */ +#define M_GREATER_THAN_P(a, b) M_LESS_THAN_P(b,a) + + +/* Return the nth argument of a VA_ARGS. + - M_RET_ARG lets the VA_ARGS been evaluated, and returns the argument. + - M_RETI_ARG returns the argument. + - M_RET_ARG takes n as argument, evaluate the arguments, and returns the 'n' argument. + NOTE: A comma is added at the end in M_RET_ARG in order to avoid error if the number of + argument is exactly (in which case, otherwise the __VA_ARGS__ will be non existent). + Generated by: + for i in $(seq 1 53 ) ; do printf "#define M_RETI_ARG%d(" $i; for j in $(seq 1 $i) ; do printf "_%d, " $j ; done ; printf "...) _%d\n#define M_RET_ARG%d(...) M_RETI_ARG%d(__VA_ARGS__,)\n\n" $i $i $i ; done + */ +#define M_RET_ARG(n, ...) M_C(M_RET_ARG, n)(__VA_ARGS__, ) + +#define M_RETI_ARG1(_1, ...) _1 +#define M_RET_ARG1(...) M_RETI_ARG1(__VA_ARGS__,) + +#define M_RETI_ARG2(_1, _2, ...) _2 +#define M_RET_ARG2(...) M_RETI_ARG2(__VA_ARGS__,) + +#define M_RETI_ARG3(_1, _2, _3, ...) _3 +#define M_RET_ARG3(...) M_RETI_ARG3(__VA_ARGS__,) + +#define M_RETI_ARG4(_1, _2, _3, _4, ...) _4 +#define M_RET_ARG4(...) M_RETI_ARG4(__VA_ARGS__,) + +#define M_RETI_ARG5(_1, _2, _3, _4, _5, ...) _5 +#define M_RET_ARG5(...) M_RETI_ARG5(__VA_ARGS__,) + +#define M_RETI_ARG6(_1, _2, _3, _4, _5, _6, ...) _6 +#define M_RET_ARG6(...) M_RETI_ARG6(__VA_ARGS__,) + +#define M_RETI_ARG7(_1, _2, _3, _4, _5, _6, _7, ...) _7 +#define M_RET_ARG7(...) M_RETI_ARG7(__VA_ARGS__,) + +#define M_RETI_ARG8(_1, _2, _3, _4, _5, _6, _7, _8, ...) _8 +#define M_RET_ARG8(...) M_RETI_ARG8(__VA_ARGS__,) + +#define M_RETI_ARG9(_1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9 +#define M_RET_ARG9(...) M_RETI_ARG9(__VA_ARGS__,) + +#define M_RETI_ARG10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) _10 +#define M_RET_ARG10(...) M_RETI_ARG10(__VA_ARGS__,) + +#define M_RETI_ARG11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) _11 +#define M_RET_ARG11(...) M_RETI_ARG11(__VA_ARGS__,) + +#define M_RETI_ARG12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) _12 +#define M_RET_ARG12(...) M_RETI_ARG12(__VA_ARGS__,) + +#define M_RETI_ARG13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) _13 +#define M_RET_ARG13(...) M_RETI_ARG13(__VA_ARGS__,) + +#define M_RETI_ARG14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) _14 +#define M_RET_ARG14(...) M_RETI_ARG14(__VA_ARGS__,) + +#define M_RETI_ARG15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 +#define M_RET_ARG15(...) M_RETI_ARG15(__VA_ARGS__,) + +#define M_RETI_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16 +#define M_RET_ARG16(...) M_RETI_ARG16(__VA_ARGS__,) + +#define M_RETI_ARG17(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) _17 +#define M_RET_ARG17(...) M_RETI_ARG17(__VA_ARGS__,) + +#define M_RETI_ARG18(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) _18 +#define M_RET_ARG18(...) M_RETI_ARG18(__VA_ARGS__,) + +#define M_RETI_ARG19(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) _19 +#define M_RET_ARG19(...) M_RETI_ARG19(__VA_ARGS__,) + +#define M_RETI_ARG20(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, ...) _20 +#define M_RET_ARG20(...) M_RETI_ARG20(__VA_ARGS__,) + +#define M_RETI_ARG21(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) _21 +#define M_RET_ARG21(...) M_RETI_ARG21(__VA_ARGS__,) + +#define M_RETI_ARG22(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, ...) _22 +#define M_RET_ARG22(...) M_RETI_ARG22(__VA_ARGS__,) + +#define M_RETI_ARG23(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, ...) _23 +#define M_RET_ARG23(...) M_RETI_ARG23(__VA_ARGS__,) + +#define M_RETI_ARG24(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, ...) _24 +#define M_RET_ARG24(...) M_RETI_ARG24(__VA_ARGS__,) + +#define M_RETI_ARG25(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, ...) _25 +#define M_RET_ARG25(...) M_RETI_ARG25(__VA_ARGS__,) + +#define M_RETI_ARG26(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, ...) _26 +#define M_RET_ARG26(...) M_RETI_ARG26(__VA_ARGS__,) + +#define M_RETI_ARG27(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, ...) _27 +#define M_RET_ARG27(...) M_RETI_ARG27(__VA_ARGS__,) + +#define M_RETI_ARG28(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, ...) _28 +#define M_RET_ARG28(...) M_RETI_ARG28(__VA_ARGS__,) + +#define M_RETI_ARG29(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, ...) _29 +#define M_RET_ARG29(...) M_RETI_ARG29(__VA_ARGS__,) + +#define M_RETI_ARG30(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, ...) _30 +#define M_RET_ARG30(...) M_RETI_ARG30(__VA_ARGS__,) + +#define M_RETI_ARG31(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, ...) _31 +#define M_RET_ARG31(...) M_RETI_ARG31(__VA_ARGS__,) + +#define M_RETI_ARG32(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, ...) _32 +#define M_RET_ARG32(...) M_RETI_ARG32(__VA_ARGS__,) + +#define M_RETI_ARG33(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, ...) _33 +#define M_RET_ARG33(...) M_RETI_ARG33(__VA_ARGS__,) + +#define M_RETI_ARG34(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, ...) _34 +#define M_RET_ARG34(...) M_RETI_ARG34(__VA_ARGS__,) + +#define M_RETI_ARG35(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, ...) _35 +#define M_RET_ARG35(...) M_RETI_ARG35(__VA_ARGS__,) + +#define M_RETI_ARG36(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, ...) _36 +#define M_RET_ARG36(...) M_RETI_ARG36(__VA_ARGS__,) + +#define M_RETI_ARG37(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, ...) _37 +#define M_RET_ARG37(...) M_RETI_ARG37(__VA_ARGS__,) + +#define M_RETI_ARG38(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, ...) _38 +#define M_RET_ARG38(...) M_RETI_ARG38(__VA_ARGS__,) + +#define M_RETI_ARG39(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, ...) _39 +#define M_RET_ARG39(...) M_RETI_ARG39(__VA_ARGS__,) + +#define M_RETI_ARG40(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, ...) _40 +#define M_RET_ARG40(...) M_RETI_ARG40(__VA_ARGS__,) + +#define M_RETI_ARG41(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, ...) _41 +#define M_RET_ARG41(...) M_RETI_ARG41(__VA_ARGS__,) + +#define M_RETI_ARG42(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, ...) _42 +#define M_RET_ARG42(...) M_RETI_ARG42(__VA_ARGS__,) + +#define M_RETI_ARG43(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, ...) _43 +#define M_RET_ARG43(...) M_RETI_ARG43(__VA_ARGS__,) + +#define M_RETI_ARG44(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, ...) _44 +#define M_RET_ARG44(...) M_RETI_ARG44(__VA_ARGS__,) + +#define M_RETI_ARG45(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, ...) _45 +#define M_RET_ARG45(...) M_RETI_ARG45(__VA_ARGS__,) + +#define M_RETI_ARG46(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, ...) _46 +#define M_RET_ARG46(...) M_RETI_ARG46(__VA_ARGS__,) + +#define M_RETI_ARG47(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, ...) _47 +#define M_RET_ARG47(...) M_RETI_ARG47(__VA_ARGS__,) + +#define M_RETI_ARG48(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, ...) _48 +#define M_RET_ARG48(...) M_RETI_ARG48(__VA_ARGS__,) + +#define M_RETI_ARG49(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, ...) _49 +#define M_RET_ARG49(...) M_RETI_ARG49(__VA_ARGS__,) + +#define M_RETI_ARG50(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, ...) _50 +#define M_RET_ARG50(...) M_RETI_ARG50(__VA_ARGS__,) + +#define M_RETI_ARG51(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, ...) _51 +#define M_RET_ARG51(...) M_RETI_ARG51(__VA_ARGS__,) + +#define M_RETI_ARG52(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, ...) _52 +#define M_RET_ARG52(...) M_RETI_ARG52(__VA_ARGS__,) + +#define M_RETI_ARG53(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, ...) _53 +#define M_RET_ARG53(...) M_RETI_ARG53(__VA_ARGS__,) + +#define M_RETI_ARG54(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, ...) _54 +#define M_RET_ARG54(...) M_RETI_ARG54(__VA_ARGS__,) + +#define M_RETI_ARG55(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, ...) _55 +#define M_RET_ARG55(...) M_RETI_ARG55(__VA_ARGS__,) + +#define M_RETI_ARG56(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, ...) _56 +#define M_RET_ARG56(...) M_RETI_ARG56(__VA_ARGS__,) + +#define M_RETI_ARG57(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, ...) _57 +#define M_RET_ARG57(...) M_RETI_ARG57(__VA_ARGS__,) + +#define M_RETI_ARG58(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, ...) _58 +#define M_RET_ARG58(...) M_RETI_ARG58(__VA_ARGS__,) + +#define M_RETI_ARG59(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, ...) _59 +#define M_RET_ARG59(...) M_RETI_ARG59(__VA_ARGS__,) + +#define M_RETI_ARG60(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, ...) _60 +#define M_RET_ARG60(...) M_RETI_ARG60(__VA_ARGS__,) + +#define M_RETI_ARG61(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, ...) _61 +#define M_RET_ARG61(...) M_RETI_ARG61(__VA_ARGS__,) + +#define M_RETI_ARG62(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, ...) _62 +#define M_RET_ARG62(...) M_RETI_ARG62(__VA_ARGS__,) + +#define M_RETI_ARG63(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, ...) _63 +#define M_RET_ARG63(...) M_RETI_ARG63(__VA_ARGS__,) + +#define M_RETI_ARG64(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, ...) _64 +#define M_RET_ARG64(...) M_RETI_ARG64(__VA_ARGS__,) + +#define M_RETI_ARG65(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, ...) _65 +#define M_RET_ARG65(...) M_RETI_ARG65(__VA_ARGS__,) + +#define M_RETI_ARG66(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, ...) _66 +#define M_RET_ARG66(...) M_RETI_ARG66(__VA_ARGS__,) + +#define M_RETI_ARG67(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, ...) _67 +#define M_RET_ARG67(...) M_RETI_ARG67(__VA_ARGS__,) + +#define M_RETI_ARG68(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, ...) _68 +#define M_RET_ARG68(...) M_RETI_ARG68(__VA_ARGS__,) + +#define M_RETI_ARG69(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, ...) _69 +#define M_RET_ARG69(...) M_RETI_ARG69(__VA_ARGS__,) + +#define M_RETI_ARG70(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, ...) _70 +#define M_RET_ARG70(...) M_RETI_ARG70(__VA_ARGS__,) + +#define M_RETI_ARG71(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, ...) _71 +#define M_RET_ARG71(...) M_RETI_ARG71(__VA_ARGS__,) + +#define M_RETI_ARG72(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, ...) _72 +#define M_RET_ARG72(...) M_RETI_ARG72(__VA_ARGS__,) + +#define M_RETI_ARG73(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, ...) _73 +#define M_RET_ARG73(...) M_RETI_ARG73(__VA_ARGS__,) + +#define M_RETI_ARG74(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, ...) _74 +#define M_RET_ARG74(...) M_RETI_ARG74(__VA_ARGS__,) + +#define M_RETI_ARG75(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, ...) _75 +#define M_RET_ARG75(...) M_RETI_ARG75(__VA_ARGS__,) + +#define M_RETI_ARG76(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, ...) _76 +#define M_RET_ARG76(...) M_RETI_ARG76(__VA_ARGS__,) + + +/* Return the value of the "arglist" associated to the given index (zero based). + An "arglist" is a VA_ARGS between parenthesis. + EXAMPLE: M_GET_AT((f_0,f_1,f_2),1) returns f_1 + */ +#define M_GET_AT(arglist, index) M_RET_ARG(M_INC(index), M_GETI_AT_ID arglist) +#define M_GETI_AT_ID(...) __VA_ARGS__ + + +/* For a pair of argument (arg0, arg1), return either the first or the second. + NOTE: Needed in case where M_RET_ARG cannot be used */ +#define M_PAIR_1(a,b) a +#define M_PAIR_2(a,b) b + +/* Same for triple */ +#define M_TRIPLE_1(a,b,c) a +#define M_TRIPLE_2(a,b,c) b +#define M_TRIPLE_3(a,b,c) c + +/* Skip the Nth first arguments of a VA_ARGS + Generated by: + for i in $(seq 0 52) ; do printf "#define M_SKIPI_%d(" $i ; for j in $(seq 1 $i) ; do printf "_%d, " $j ; done ; printf "...) __VA_ARGS__\n"; done + */ +#define M_SKIP_ARGS(n, ...) M_C(M_SKIPI_, n)(__VA_ARGS__) +#define M_SKIPI_0(...) __VA_ARGS__ +#define M_SKIPI_1(_1, ...) __VA_ARGS__ +#define M_SKIPI_2(_1, _2, ...) __VA_ARGS__ +#define M_SKIPI_3(_1, _2, _3, ...) __VA_ARGS__ +#define M_SKIPI_4(_1, _2, _3, _4, ...) __VA_ARGS__ +#define M_SKIPI_5(_1, _2, _3, _4, _5, ...) __VA_ARGS__ +#define M_SKIPI_6(_1, _2, _3, _4, _5, _6, ...) __VA_ARGS__ +#define M_SKIPI_7(_1, _2, _3, _4, _5, _6, _7, ...) __VA_ARGS__ +#define M_SKIPI_8(_1, _2, _3, _4, _5, _6, _7, _8, ...) __VA_ARGS__ +#define M_SKIPI_9(_1, _2, _3, _4, _5, _6, _7, _8, _9, ...) __VA_ARGS__ +#define M_SKIPI_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) __VA_ARGS__ +#define M_SKIPI_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) __VA_ARGS__ +#define M_SKIPI_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) __VA_ARGS__ +#define M_SKIPI_13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) __VA_ARGS__ +#define M_SKIPI_14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) __VA_ARGS__ +#define M_SKIPI_15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) __VA_ARGS__ +#define M_SKIPI_16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) __VA_ARGS__ +#define M_SKIPI_17(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) __VA_ARGS__ +#define M_SKIPI_18(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) __VA_ARGS__ +#define M_SKIPI_19(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) __VA_ARGS__ +#define M_SKIPI_20(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, ...) __VA_ARGS__ +#define M_SKIPI_21(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) __VA_ARGS__ +#define M_SKIPI_22(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, ...) __VA_ARGS__ +#define M_SKIPI_23(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, ...) __VA_ARGS__ +#define M_SKIPI_24(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, ...) __VA_ARGS__ +#define M_SKIPI_25(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, ...) __VA_ARGS__ +#define M_SKIPI_26(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, ...) __VA_ARGS__ +#define M_SKIPI_27(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, ...) __VA_ARGS__ +#define M_SKIPI_28(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, ...) __VA_ARGS__ +#define M_SKIPI_29(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, ...) __VA_ARGS__ +#define M_SKIPI_30(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, ...) __VA_ARGS__ +#define M_SKIPI_31(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, ...) __VA_ARGS__ +#define M_SKIPI_32(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, ...) __VA_ARGS__ +#define M_SKIPI_33(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, ...) __VA_ARGS__ +#define M_SKIPI_34(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, ...) __VA_ARGS__ +#define M_SKIPI_35(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, ...) __VA_ARGS__ +#define M_SKIPI_36(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, ...) __VA_ARGS__ +#define M_SKIPI_37(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, ...) __VA_ARGS__ +#define M_SKIPI_38(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, ...) __VA_ARGS__ +#define M_SKIPI_39(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, ...) __VA_ARGS__ +#define M_SKIPI_40(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, ...) __VA_ARGS__ +#define M_SKIPI_41(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, ...) __VA_ARGS__ +#define M_SKIPI_42(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, ...) __VA_ARGS__ +#define M_SKIPI_43(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, ...) __VA_ARGS__ +#define M_SKIPI_44(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, ...) __VA_ARGS__ +#define M_SKIPI_45(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, ...) __VA_ARGS__ +#define M_SKIPI_46(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, ...) __VA_ARGS__ +#define M_SKIPI_47(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, ...) __VA_ARGS__ +#define M_SKIPI_48(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, ...) __VA_ARGS__ +#define M_SKIPI_49(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, ...) __VA_ARGS__ +#define M_SKIPI_50(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, ...) __VA_ARGS__ +#define M_SKIPI_51(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, ...) __VA_ARGS__ +#define M_SKIPI_52(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, ...) __VA_ARGS__ + + +/* Keep the nth first arguments of a VA_ARGS + Generated by: + for i in $(seq 0 52) ; do printf "#define M_KEEPI_%d(" $i ; for j in $(seq 1 $i) ; do printf "_%d, " $j ; done ; printf "...) " ; for j in $(seq 1 $(($i - 1))) ; do printf "_%d, " $j; done ; if test "$i" -ne 0 ; then printf "_$i" ; fi ; printf "\n" ; done */ +#define M_KEEP_ARGS(n, ...) M_C(M_KEEPI_, n)(__VA_ARGS__) +#define M_KEEPI_0(...) +#define M_KEEPI_1(_1, ...) _1 +#define M_KEEPI_2(_1, _2, ...) _1, _2 +#define M_KEEPI_3(_1, _2, _3, ...) _1, _2, _3 +#define M_KEEPI_4(_1, _2, _3, _4, ...) _1, _2, _3, _4 +#define M_KEEPI_5(_1, _2, _3, _4, _5, ...) _1, _2, _3, _4, _5 +#define M_KEEPI_6(_1, _2, _3, _4, _5, _6, ...) _1, _2, _3, _4, _5, _6 +#define M_KEEPI_7(_1, _2, _3, _4, _5, _6, _7, ...) _1, _2, _3, _4, _5, _6, _7 +#define M_KEEPI_8(_1, _2, _3, _4, _5, _6, _7, _8, ...) _1, _2, _3, _4, _5, _6, _7, _8 +#define M_KEEPI_9(_1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9 +#define M_KEEPI_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10 +#define M_KEEPI_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11 +#define M_KEEPI_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12 +#define M_KEEPI_13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13 +#define M_KEEPI_14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14 +#define M_KEEPI_15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15 +#define M_KEEPI_16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16 +#define M_KEEPI_17(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17 +#define M_KEEPI_18(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18 +#define M_KEEPI_19(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19 +#define M_KEEPI_20(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 +#define M_KEEPI_21(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21 +#define M_KEEPI_22(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22 +#define M_KEEPI_23(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23 +#define M_KEEPI_24(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24 +#define M_KEEPI_25(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25 +#define M_KEEPI_26(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26 +#define M_KEEPI_27(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27 +#define M_KEEPI_28(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28 +#define M_KEEPI_29(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29 +#define M_KEEPI_30(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30 +#define M_KEEPI_31(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31 +#define M_KEEPI_32(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32 +#define M_KEEPI_33(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33 +#define M_KEEPI_34(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34 +#define M_KEEPI_35(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35 +#define M_KEEPI_36(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36 +#define M_KEEPI_37(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37 +#define M_KEEPI_38(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38 +#define M_KEEPI_39(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39 +#define M_KEEPI_40(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40 +#define M_KEEPI_41(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41 +#define M_KEEPI_42(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42 +#define M_KEEPI_43(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43 +#define M_KEEPI_44(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44 +#define M_KEEPI_45(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45 +#define M_KEEPI_46(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46 +#define M_KEEPI_47(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47 +#define M_KEEPI_48(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48 +#define M_KEEPI_49(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49 +#define M_KEEPI_50(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50 +#define M_KEEPI_51(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51 +#define M_KEEPI_52(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, ...) _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52 + + +/* Keep the medium characters of a sequence */ +#define M_MID_ARGS(first, len, ...) M_KEEP_ARGS(len, M_SKIP_ARGS(first, __VA_ARGS__)) + + +/* Revert the VA_ARGS given as parameter + Example: M_REVERSE(a,b,c) --> c,b,a */ +/* Generated by: + for i in $(seq 1 52) ; do printf "#define M_REVERSE_%d(" $i ; for j in $(seq 1 $i) ; do printf "_%d" $j ; if test $i -ne $j ; then printf "," ; fi ; done; printf ") " ; for j in $(seq $i -1 1) ; do printf "_%d" $j ; if test 1 -ne $j ; then printf "," ; fi ; done ; printf "\n" ; done */ +#define M_REVERSE(...) M_C(M_REVERSE_, M_NARGS(__VA_ARGS__))(__VA_ARGS__) +#define M_REVERSE_0() () +#define M_REVERSE_1(_1) _1 +#define M_REVERSE_2(_1,_2) _2,_1 +#define M_REVERSE_3(_1,_2,_3) _3,_2,_1 +#define M_REVERSE_4(_1,_2,_3,_4) _4,_3,_2,_1 +#define M_REVERSE_5(_1,_2,_3,_4,_5) _5,_4,_3,_2,_1 +#define M_REVERSE_6(_1,_2,_3,_4,_5,_6) _6,_5,_4,_3,_2,_1 +#define M_REVERSE_7(_1,_2,_3,_4,_5,_6,_7) _7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_8(_1,_2,_3,_4,_5,_6,_7,_8) _8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_9(_1,_2,_3,_4,_5,_6,_7,_8,_9) _9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_10(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10) _10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_11(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11) _11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_12(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12) _12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_13(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13) _13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_14(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14) _14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_15(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15) _15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_16(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16) _16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_17(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17) _17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_18(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18) _18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_19(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19) _19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_20(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20) _20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_21(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21) _21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_22(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22) _22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_23(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23) _23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_24(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24) _24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_25(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25) _25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_26(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26) _26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_27(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27) _27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_28(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28) _28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_29(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29) _29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_30(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30) _30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_31(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31) _31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_32(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32) _32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_33(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33) _33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_34(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34) _34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_35(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35) _35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_36(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36) _36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_37(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37) _37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_38(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38) _38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_39(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39) _39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_40(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40) _40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_41(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41) _41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_42(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42) _42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_43(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43) _43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_44(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44) _44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_45(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45) _45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_46(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46) _46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_47(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47) _47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_48(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48) _48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_49(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49) _49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_50(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50) _50,_49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_51(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51) _51,_50,_49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 +#define M_REVERSE_52(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52) _52,_51,_50,_49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1 + + +/* Convert an integer or a symbol into 0 (if 0) or 1 (if not 0). + NOTE: It shall exactly the C-preprocessing token '0'. + 1 if symbol unknown */ +#define M_TOBOOLI_0 1, 0, +#define M_BOOL(x) M_RET_ARG2(M_C(M_TOBOOLI_, x), 1, useless) + + +/* M_IF Macro : Perform an IF test at preprocessing time. + The condition is assumed to be true if unknown. + USAGE: + M_IF(condition)(Block if true, Block if false) + Example: + M_IF(0)(true_action, false_action) --> false_action + NOTE: true_action and false_action can recursively contain M_IF + but not 'condition'. + */ +#define M_IFI_0(true_c, ...) __VA_ARGS__ +#define M_IFI_1(true_c, ...) true_c +#define M_IF(c) M_C(M_IFI_, M_BOOL(c)) + + +/* Return 1 if there is a comma inside the VA_ARGS, 0 otherwise. + NOTE: this is one of the lowest level check performed + (all other high level checks depend on it). +*/ +#if defined(__clang__) && defined(_MSC_VER) +// CLANG on windows has a non compliant preprocessor (but not the one for Linux / Mac). Workaround for it (with minor issue) +#define M_COMMA_P(...) M_RET_ARG76(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, useless) +#define M_COMMA_P_WORKAROUND 1 +#else +#define M_COMMA_P(...) M_RETI_ARG76(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, useless) +#endif + + +/* Return 1 if the argument is empty (aka ''), 0 otherwise. + This is quite hard to detect properly. + Handle: EMPTY_P(), EMPTY_P(x), EMPTY_P(()) and EMPTY_P(,) and EMPTY_P(f) with #define f() 2,3 */ +#define M_EMPTYI_DETECT(...) , +#define M_EMPTYI_P_C1(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__ () ) +#define M_EMPTYI_P_C2(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__) +#define M_EMPTYI_P_C3(...) M_COMMA_P(__VA_ARGS__ () ) +#define M_EMPTY_P(...) M_AND(M_EMPTYI_P_C1(__VA_ARGS__), M_INV(M_OR3(M_EMPTYI_P_C2(__VA_ARGS__), M_COMMA_P(__VA_ARGS__),M_EMPTYI_P_C3(__VA_ARGS__)))) + +/* Generate a comma later in the next evaluation pass. */ +#define M_DEFERRED_COMMA , + +/* M_IF macro for empty arguments: + M_IF_EMPTY(arguments)(action if empty, action if not empty) */ +#define M_IF_EMPTY(...) M_IF(M_EMPTY_P(__VA_ARGS__)) + + +/* Concatene the two preprocessing token. + * Handle the case where the second argument is empty, in which case + * it returns the first token without concatenation. + */ +#define M_C_EMPTY(a, b) M_CI_EMPTY(a, b) +#define M_CI_EMPTY(a, b) M_IF_EMPTY(b)(M_CI_EMPTY_B, M_CI_EMPTY_CAT)(a, b) +#define M_CI_EMPTY_B(a, b) a +#define M_CI_EMPTY_CAT(a, b) a ## b + +#define M_C3_EMPTY(a, b, c) M_C3I_EMPTY(a, b, c) +#define M_C3I_EMPTY(a, b, c) M_CI_EMPTY( M_CI_EMPTY(a, b), c) + +#define M_C5_EMPTY(a, b, c, d, e) M_C5I_EMPTY(a, b, c, d, e) +#define M_C5I_EMPTY(a, b, c, d, e) M_CI_EMPTY( M_C3I_EMPTY(a, b, c), M_CI_EMPTY(d, e) ) + +/* Return 1 if argument is "()" or "(x)" + * Test if () or (x) can be transformed into a comma (primary check for parenthesis), + * and that there is no (toplevel) comma (remove false positive) + * and that there is nothing beyond the parenthesis (remove false positive) + * A simple check could only contain M_PARENTHESISI_DETECT1, but there will be false positives. + */ +#define M_PARENTHESISI_DETECT1(...) , +#define M_PARENTHESIS_P(...) \ + M_AND3(M_COMMA_P(M_PARENTHESISI_DETECT1 __VA_ARGS__), \ + M_INV(M_COMMA_P(__VA_ARGS__)), \ + M_EMPTY_P(M_EAT __VA_ARGS__)) + + +/* Return 1 if argument type or variable or function is the keyword. + - refFunc is the reference keyword. + It shall be either 'and', 'or', 'sum' (equivalent to 'add'), 'bool' + - testedFunc is the tested keyword. + It cannot start with a special character */ +#define M_KEYWORD_P(refFunc, testedFunc) \ + M_COMMA_P(M_C4(M_PATTERN_, refFunc, _, testedFunc)) + +/* The different special patterns recognized by M_KEYWORD_P + * Each recognized keyword shall define a macro like this: + * M_PATTERN_ ## keyword ## _ ## keyword + * And this macro shall expands to the comma separator. + */ +#define M_PATTERN_and_and , +#define M_PATTERN_or_or , +#define M_PATTERN_sum_sum , +#define M_PATTERN_sum_add , +#define M_PATTERN_add_add , +#define M_PATTERN_add_sum , +#define M_PATTERN_product_product , +#define M_PATTERN_product_mul , +#define M_PATTERN_bool_bool , +#define M_PATTERN_char_char , +#define M_PATTERN_short_short , +#define M_PATTERN_int_int , +#define M_PATTERN_long_long , +#define M_PATTERN_float_float , +#define M_PATTERN_double_double , +#define M_PATTERN_void_void , +#define M_PATTERN_type_type , +#define M_PATTERN_TYPE_TYPE , +#define M_PATTERN_SUBTYPE_SUBTYPE , +#define M_PATTERN_IT_TYPE_IT_TYPE , +#define M_PATTERN_M_UNDERFLOW_M_UNDERFLOW , +#define M_PATTERN_M_OVERFLOW_M_OVERFLOW , +#define M_PATTERN_M_OVERFLOW_M_OVERFLOW , +#define M_PATTERN_SEQUENCE_SEQUENCE , +#define M_PATTERN_MAP_MAP , +#define M_PATTERN_KEYVAL_KEYVAL , +#define M_PATTERN_KEYVAL_PTR_KEYVAL_PTR , +#define M_PATTERN_priority_priority , +#define M_PATTERN_list_list , +#define M_PATTERN_LIST_LIST , +#define M_PATTERN_queue_queue , +#define M_PATTERN_QUEUE_QUEUE , +#define M_PATTERN_INIT_WITH_INIT_WITH , +#define M_PATTERN____ , + + +/* Extract the VA ARGS of a keyword function like. + Transform 'LIST( a, b, c)' into 'a, b, c' for keyword=LIST + It shall start with KEYWORD (M_KEYWORD_P shall return 1). +*/ +#define M_KEYWORD_TO_VA_ARGS(keyword, list) M_C4(M_EAT_KEYWORD_, keyword, _, list) + +/* The different special patterns recognized by M_KEYWORD_TO_VA_ARGS + * Each recognized keyword shall define a macro function like this: + * M_EAT_KEYWORD_ ## keyword ## _ ## keyword (...) + * And this macro shall expands to __VA_ARGS__ + * CONSTRAINT: Theses keywords shall be a sub-list of the keywords supported by M_KEYWORD_P + */ +#define M_EAT_KEYWORD_MAP_MAP(...) __VA_ARGS__ +#define M_EAT_KEYWORD_LIST_LIST(...) __VA_ARGS__ +#define M_EAT_KEYWORD_SEQ_SEQ(...) __VA_ARGS__ +#define M_EAT_KEYWORD_QUEUE_QUEUE(...) __VA_ARGS__ +#define M_EAT_KEYWORD____(...) __VA_ARGS__ + + +/* Necessary macros to handle recursivity, + delaying the evaluation by one (or more) level of macro expansion. + The argument is a macro-function which has to be deferred */ +#define M_DELAY0() +#define M_DELAY1(...) __VA_ARGS__ M_DELAY0 () +#define M_DELAY2(...) __VA_ARGS__ M_DELAY1 (M_DELAY0) () +#define M_DELAY3(...) __VA_ARGS__ M_DELAY2 (M_DELAY0) () +#define M_DELAY4(...) __VA_ARGS__ M_DELAY3 (M_DELAY0) () +#define M_DELAY5(...) __VA_ARGS__ M_DELAY4 (M_DELAY0) () +#define M_DELAY6(...) __VA_ARGS__ M_DELAY5 (M_DELAY0) () +#define M_DELAY7(...) __VA_ARGS__ M_DELAY6 (M_DELAY0) () +#define M_DELAY8(...) __VA_ARGS__ M_DELAY7 (M_DELAY0) () +#define M_DELAY9(...) __VA_ARGS__ M_DELAY8 (M_DELAY0) () + +/* Perform 3^5 evaluation, + forcing the pre-processor to expand the given macro a lot of times. + NOTE: There can be only one EVAL macro per complete macro-evaluation pass. + As such the given macro cannot contain M_EVAL itself, + NOTE: Using recursivity impacts compilation time performance. +*/ +#define M_EVAL(...) M_EVAL1(M_EVAL1(M_EVAL1(__VA_ARGS__))) +#define M_EVAL1(...) M_EVAL2(M_EVAL2(M_EVAL2(__VA_ARGS__))) +#define M_EVAL2(...) M_EVAL3(M_EVAL3(M_EVAL3(__VA_ARGS__))) +#define M_EVAL3(...) M_EVAL4(M_EVAL4(M_EVAL4(__VA_ARGS__))) +#define M_EVAL4(...) M_EVAL0(M_EVAL0(M_EVAL0(__VA_ARGS__))) +#define M_EVAL0(...) __VA_ARGS__ + + +/* Apply Macro : + * It delays the expansion of the macro function 'a' until the VA_ARGS itself is evaluated. + * It is needed when the evaluation process changes the numbers of arguments. + * For example, when merging 2 arglists using "M_OPFLAT M_MERGE_ARGLIST" + */ +#define M_APPLY(a, ...) a (__VA_ARGS__) + + +/* MAP: apply the given macro to all arguments. + NOTE: Perform a first step evalution so that the given VA_ARGS is fullu evaluated + and expanded before further preprocessing. This helps if the argument given to M_MAP + perform a construction of the VA_ARGS as its argument. + NOTE: It is a non recursive version that is much faster than the recursive one. + Example: + M_MAP(f,a, b, c) ==> f(a) f(b) f(c) + Generated by: + for i in $(seq 1 52) ; do printf "#define M_MAPI_%d(f" $i ; for j in $(seq 1 $i) ; do printf ", _%d" $j ; done ; printf ") " ; for j in $(seq 1 $i) ; do printf "f(_%d) " $j ; done ; printf "\n"; done +*/ +#define M_MAP(...) M_MAP_0(__VA_ARGS__) +#define M_MAP_0(f, ...) M_C(M_MAP_, M_NARGS(__VA_ARGS__))(f, __VA_ARGS__) +#define M_MAP_1(f, _1) f(_1) +#define M_MAP_2(f, _1, _2) f(_1) f(_2) +#define M_MAP_3(f, _1, _2, _3) f(_1) f(_2) f(_3) +#define M_MAP_4(f, _1, _2, _3, _4) f(_1) f(_2) f(_3) f(_4) +#define M_MAP_5(f, _1, _2, _3, _4, _5) f(_1) f(_2) f(_3) f(_4) f(_5) +#define M_MAP_6(f, _1, _2, _3, _4, _5, _6) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) +#define M_MAP_7(f, _1, _2, _3, _4, _5, _6, _7) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) +#define M_MAP_8(f, _1, _2, _3, _4, _5, _6, _7, _8) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) +#define M_MAP_9(f, _1, _2, _3, _4, _5, _6, _7, _8, _9) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) +#define M_MAP_10(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) +#define M_MAP_11(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) +#define M_MAP_12(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) +#define M_MAP_13(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) +#define M_MAP_14(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) +#define M_MAP_15(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) +#define M_MAP_16(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) +#define M_MAP_17(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) +#define M_MAP_18(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) +#define M_MAP_19(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) +#define M_MAP_20(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) +#define M_MAP_21(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) +#define M_MAP_22(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) +#define M_MAP_23(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) +#define M_MAP_24(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) +#define M_MAP_25(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) +#define M_MAP_26(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) +#define M_MAP_27(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) +#define M_MAP_28(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) +#define M_MAP_29(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) +#define M_MAP_30(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) +#define M_MAP_31(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) +#define M_MAP_32(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) +#define M_MAP_33(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) +#define M_MAP_34(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) +#define M_MAP_35(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) +#define M_MAP_36(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) +#define M_MAP_37(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) +#define M_MAP_38(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) +#define M_MAP_39(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) +#define M_MAP_40(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) +#define M_MAP_41(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) +#define M_MAP_42(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) +#define M_MAP_43(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) +#define M_MAP_44(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) +#define M_MAP_45(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) f(_45) +#define M_MAP_46(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) +#define M_MAP_47(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) +#define M_MAP_48(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) +#define M_MAP_49(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) +#define M_MAP_50(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) +#define M_MAP_51(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) +#define M_MAP_52(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) + + +/* Map a macro to all given arguments with one additional fixed data (non recursive version) */ +/* Example: M_MAP2(f, data, a, b, c) ==> f(data,a) f(data,b) f(data,c) */ +#define M_MAP2(...) M_MAP2I_0(__VA_ARGS__) +#define M_MAP2I_0(f, d, ...) M_C(M_MAP2I_, M_NARGS(__VA_ARGS__))(f, d, __VA_ARGS__) +#define M_MAP2I_1(f, d, _1) f(d, _1) +#define M_MAP2I_2(f, d, _1, _2) f(d, _1) f(d, _2) +#define M_MAP2I_3(f, d, _1, _2, _3) f(d, _1) f(d, _2) f(d, _3) +#define M_MAP2I_4(f, d, _1, _2, _3, _4) f(d, _1) f(d, _2) f(d, _3) f(d, _4) +#define M_MAP2I_5(f, d, _1, _2, _3, _4, _5) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) +#define M_MAP2I_6(f, d, _1, _2, _3, _4, _5, _6) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) +#define M_MAP2I_7(f, d, _1, _2, _3, _4, _5, _6, _7) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) +#define M_MAP2I_8(f, d, _1, _2, _3, _4, _5, _6, _7, _8) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) +#define M_MAP2I_9(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) +#define M_MAP2I_10(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) +#define M_MAP2I_11(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) +#define M_MAP2I_12(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) +#define M_MAP2I_13(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) +#define M_MAP2I_14(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) +#define M_MAP2I_15(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) +#define M_MAP2I_16(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) +#define M_MAP2I_17(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) +#define M_MAP2I_18(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) +#define M_MAP2I_19(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) +#define M_MAP2I_20(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) +#define M_MAP2I_21(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) +#define M_MAP2I_22(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) +#define M_MAP2I_23(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) +#define M_MAP2I_24(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) +#define M_MAP2I_25(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) +#define M_MAP2I_26(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) +#define M_MAP2I_27(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) +#define M_MAP2I_28(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) +#define M_MAP2I_29(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) +#define M_MAP2I_30(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) +#define M_MAP2I_31(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) +#define M_MAP2I_32(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) +#define M_MAP2I_33(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) +#define M_MAP2I_34(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) +#define M_MAP2I_35(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) +#define M_MAP2I_36(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) +#define M_MAP2I_37(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) +#define M_MAP2I_38(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) +#define M_MAP2I_39(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) +#define M_MAP2I_40(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) +#define M_MAP2I_41(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) +#define M_MAP2I_42(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) +#define M_MAP2I_43(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) +#define M_MAP2I_44(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) +#define M_MAP2I_45(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) +#define M_MAP2I_46(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) +#define M_MAP2I_47(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) +#define M_MAP2I_48(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) +#define M_MAP2I_49(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) +#define M_MAP2I_50(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) +#define M_MAP2I_51(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) +#define M_MAP2I_52(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) + +/* Duplicate of M_MAP2 for GET_METHOD as it may be called in context where a M_MAP2 is in progress (for oplist). + NOTE: Increase number of arguments to 76 due to the number of available methods. + NOTE: Don't perform another step of evaluation (not needed in the usage context) + NOTE: Rewrite with another approach much more verbose but also much faster for the compiler. + This kind of approach seems ugly, but is in fact the fast one. + Generated by the following command: + for i in $(seq 1 52) ; do printf "#define M_MAP2B_%d(f, d" $i ; for j in $(seq 1 $i) ; do printf ", _%d" $j ; done ; printf ") " ; for j in $(seq 1 $i) ; do printf "f(d, _%d) " $j ; done ; printf "\n"; done +*/ +#define M_MAP2B(f, d, ...) M_C(M_MAP2B_, M_NARGS(__VA_ARGS__))(f, d, __VA_ARGS__) +#define M_MAP2B_1(f, d, _1) f(d, _1) +#define M_MAP2B_2(f, d, _1, _2) f(d, _1) f(d, _2) +#define M_MAP2B_3(f, d, _1, _2, _3) f(d, _1) f(d, _2) f(d, _3) +#define M_MAP2B_4(f, d, _1, _2, _3, _4) f(d, _1) f(d, _2) f(d, _3) f(d, _4) +#define M_MAP2B_5(f, d, _1, _2, _3, _4, _5) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) +#define M_MAP2B_6(f, d, _1, _2, _3, _4, _5, _6) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) +#define M_MAP2B_7(f, d, _1, _2, _3, _4, _5, _6, _7) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) +#define M_MAP2B_8(f, d, _1, _2, _3, _4, _5, _6, _7, _8) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) +#define M_MAP2B_9(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) +#define M_MAP2B_10(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) +#define M_MAP2B_11(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) +#define M_MAP2B_12(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) +#define M_MAP2B_13(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) +#define M_MAP2B_14(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) +#define M_MAP2B_15(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) +#define M_MAP2B_16(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) +#define M_MAP2B_17(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) +#define M_MAP2B_18(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) +#define M_MAP2B_19(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) +#define M_MAP2B_20(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) +#define M_MAP2B_21(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) +#define M_MAP2B_22(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) +#define M_MAP2B_23(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) +#define M_MAP2B_24(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) +#define M_MAP2B_25(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) +#define M_MAP2B_26(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) +#define M_MAP2B_27(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) +#define M_MAP2B_28(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) +#define M_MAP2B_29(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) +#define M_MAP2B_30(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) +#define M_MAP2B_31(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) +#define M_MAP2B_32(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) +#define M_MAP2B_33(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) +#define M_MAP2B_34(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) +#define M_MAP2B_35(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) +#define M_MAP2B_36(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) +#define M_MAP2B_37(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) +#define M_MAP2B_38(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) +#define M_MAP2B_39(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) +#define M_MAP2B_40(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) +#define M_MAP2B_41(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) +#define M_MAP2B_42(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) +#define M_MAP2B_43(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) +#define M_MAP2B_44(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) +#define M_MAP2B_45(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) +#define M_MAP2B_46(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) +#define M_MAP2B_47(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) +#define M_MAP2B_48(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) +#define M_MAP2B_49(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) +#define M_MAP2B_50(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) +#define M_MAP2B_51(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) +#define M_MAP2B_52(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) +#define M_MAP2B_53(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) +#define M_MAP2B_54(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) +#define M_MAP2B_55(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) +#define M_MAP2B_56(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) +#define M_MAP2B_57(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) +#define M_MAP2B_58(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) +#define M_MAP2B_59(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) +#define M_MAP2B_60(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) +#define M_MAP2B_61(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) +#define M_MAP2B_62(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) +#define M_MAP2B_63(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) +#define M_MAP2B_64(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) +#define M_MAP2B_65(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) +#define M_MAP2B_66(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) +#define M_MAP2B_67(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) +#define M_MAP2B_68(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) f(d, _68) +#define M_MAP2B_69(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) f(d, _68) f(d, _69) +#define M_MAP2B_70(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) f(d, _68) f(d, _69) f(d, _70) +#define M_MAP2B_71(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) f(d, _68) f(d, _69) f(d, _70) f(d, _71) +#define M_MAP2B_72(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) f(d, _68) f(d, _69) f(d, _70) f(d, _71) f(d, _72) +#define M_MAP2B_73(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) f(d, _68) f(d, _69) f(d, _70) f(d, _71) f(d, _72) f(d, _73) +#define M_MAP2B_74(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) f(d, _68) f(d, _69) f(d, _70) f(d, _71) f(d, _72) f(d, _73) f(d, _74) +#define M_MAP2B_75(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75) f(d, _1) f(d, _2) f(d, _3) f(d, _4) f(d, _5) f(d, _6) f(d, _7) f(d, _8) f(d, _9) f(d, _10) f(d, _11) f(d, _12) f(d, _13) f(d, _14) f(d, _15) f(d, _16) f(d, _17) f(d, _18) f(d, _19) f(d, _20) f(d, _21) f(d, _22) f(d, _23) f(d, _24) f(d, _25) f(d, _26) f(d, _27) f(d, _28) f(d, _29) f(d, _30) f(d, _31) f(d, _32) f(d, _33) f(d, _34) f(d, _35) f(d, _36) f(d, _37) f(d, _38) f(d, _39) f(d, _40) f(d, _41) f(d, _42) f(d, _43) f(d, _44) f(d, _45) f(d, _46) f(d, _47) f(d, _48) f(d, _49) f(d, _50) f(d, _51) f(d, _52) f(d, _53) f(d, _54) f(d, _55) f(d, _56) f(d, _57) f(d, _58) f(d, _59) f(d, _60) f(d, _61) f(d, _62) f(d, _63) f(d, _64) f(d, _65) f(d, _66) f(d, _67) f(d, _68) f(d, _69) f(d, _70) f(d, _71) f(d, _72) f(d, _73) f(d, _74) f(d, _75) + +/* Map a macro to all given arguments with two additional fixed data (non recursive version): + one of the parameter is given and one numerical which is the argument number. + Example: + M_MAP3(f, data, a, b, c) ==> f(data,1,a) f(data,2,b) f(data,3,c) + Generated by the following command: + for i in $(seq 1 52) ; do printf "#define M_MAP3I_%d(f, d" $i ; for j in $(seq 1 $i) ; do printf ", _%d" $j ; done ; printf ") " ; for j in $(seq 1 $i) ; do printf "f(d, %d, _%d) " $j $j ; done ; printf "\n"; done +*/ +#define M_MAP3(...) M_MAP3I_0(__VA_ARGS__) +#define M_MAP3I_0(f, d, ...) M_C(M_MAP3I_, M_NARGS(__VA_ARGS__))(f, d, __VA_ARGS__) +#define M_MAP3I_1(f, d, _1) f(d, 1, _1) +#define M_MAP3I_2(f, d, _1, _2) f(d, 1, _1) f(d, 2, _2) +#define M_MAP3I_3(f, d, _1, _2, _3) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) +#define M_MAP3I_4(f, d, _1, _2, _3, _4) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) +#define M_MAP3I_5(f, d, _1, _2, _3, _4, _5) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) +#define M_MAP3I_6(f, d, _1, _2, _3, _4, _5, _6) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) +#define M_MAP3I_7(f, d, _1, _2, _3, _4, _5, _6, _7) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) +#define M_MAP3I_8(f, d, _1, _2, _3, _4, _5, _6, _7, _8) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) +#define M_MAP3I_9(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) +#define M_MAP3I_10(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) +#define M_MAP3I_11(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) +#define M_MAP3I_12(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) +#define M_MAP3I_13(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) +#define M_MAP3I_14(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) +#define M_MAP3I_15(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) +#define M_MAP3I_16(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) +#define M_MAP3I_17(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) +#define M_MAP3I_18(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) +#define M_MAP3I_19(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) +#define M_MAP3I_20(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) +#define M_MAP3I_21(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) +#define M_MAP3I_22(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) +#define M_MAP3I_23(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) +#define M_MAP3I_24(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) +#define M_MAP3I_25(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) +#define M_MAP3I_26(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) +#define M_MAP3I_27(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) +#define M_MAP3I_28(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) +#define M_MAP3I_29(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) +#define M_MAP3I_30(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) +#define M_MAP3I_31(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) +#define M_MAP3I_32(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) +#define M_MAP3I_33(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) +#define M_MAP3I_34(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) +#define M_MAP3I_35(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) +#define M_MAP3I_36(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) +#define M_MAP3I_37(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) +#define M_MAP3I_38(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) +#define M_MAP3I_39(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) +#define M_MAP3I_40(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) +#define M_MAP3I_41(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) +#define M_MAP3I_42(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) +#define M_MAP3I_43(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) +#define M_MAP3I_44(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) +#define M_MAP3I_45(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) f(d, 45, _45) +#define M_MAP3I_46(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) f(d, 45, _45) f(d, 46, _46) +#define M_MAP3I_47(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) f(d, 45, _45) f(d, 46, _46) f(d, 47, _47) +#define M_MAP3I_48(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) f(d, 45, _45) f(d, 46, _46) f(d, 47, _47) f(d, 48, _48) +#define M_MAP3I_49(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) f(d, 45, _45) f(d, 46, _46) f(d, 47, _47) f(d, 48, _48) f(d, 49, _49) +#define M_MAP3I_50(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) f(d, 45, _45) f(d, 46, _46) f(d, 47, _47) f(d, 48, _48) f(d, 49, _49) f(d, 50, _50) +#define M_MAP3I_51(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) f(d, 45, _45) f(d, 46, _46) f(d, 47, _47) f(d, 48, _48) f(d, 49, _49) f(d, 50, _50) f(d, 51, _51) +#define M_MAP3I_52(f, d, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) f(d, 1, _1) f(d, 2, _2) f(d, 3, _3) f(d, 4, _4) f(d, 5, _5) f(d, 6, _6) f(d, 7, _7) f(d, 8, _8) f(d, 9, _9) f(d, 10, _10) f(d, 11, _11) f(d, 12, _12) f(d, 13, _13) f(d, 14, _14) f(d, 15, _15) f(d, 16, _16) f(d, 17, _17) f(d, 18, _18) f(d, 19, _19) f(d, 20, _20) f(d, 21, _21) f(d, 22, _22) f(d, 23, _23) f(d, 24, _24) f(d, 25, _25) f(d, 26, _26) f(d, 27, _27) f(d, 28, _28) f(d, 29, _29) f(d, 30, _30) f(d, 31, _31) f(d, 32, _32) f(d, 33, _33) f(d, 34, _34) f(d, 35, _35) f(d, 36, _36) f(d, 37, _37) f(d, 38, _38) f(d, 39, _39) f(d, 40, _40) f(d, 41, _41) f(d, 42, _42) f(d, 43, _43) f(d, 44, _44) f(d, 45, _45) f(d, 46, _46) f(d, 47, _47) f(d, 48, _48) f(d, 49, _49) f(d, 50, _50) f(d, 51, _51) f(d, 52, _52) + + + +/* Map a macro to all given pair of arguments (Using recursivity) (OBSOLETE) */ +/* Example: M_MAP_PAIR(f, a, b, c, d) ==> f(a,b) f(c,d) */ +#define M_MAP_PAIR_L0_INDIRECT() M_MAP_PAIR_L0 +#define M_MAP_PAIR_L0(f, ...) M_IF_NARGS_EQ2(__VA_ARGS__)( f(__VA_ARGS__) , M_MAP_PAIR_L1(f, __VA_ARGS__)) +#define M_MAP_PAIR_L1(f, a, b, ...) f(a,b) M_DELAY3(M_MAP_PAIR_L0_INDIRECT) () (f, __VA_ARGS__) +#define M_MAP_PAIR(f, ...) M_IF_EMPTY(__VA_ARGS__)( /* end */, M_EVAL(M_MAP_PAIR_L0(f, __VA_ARGS__))) + + +/* Map a macro to all given arguments and reduce all theses computation + with another reduce macro. + Example: + M_REDUCE(f, g, a, b, c) ==> g( f(a), g( f(b), f(c)) + Generated by: + for i in $(seq 1 52) ; do printf "#define M_REDUCEI1_%d(f, g" $i ; for j in $(seq 1 $i) ; do printf ", _%d" $j ; done ; printf ") "; for j in $(seq 1 $(( $i - 1 )) ) ; do printf "g(f(_%d), " $j; done ; printf "f(_%d" $i; for j in $(seq 1 $i); do printf ")"; done ; printf "\n"; done +*/ +#define M_REDUCE(...) M_REDUCEI1_0(__VA_ARGS__) +#define M_REDUCEI1_0(f, g, ...) M_C(M_REDUCEI1_, M_NARGS(__VA_ARGS__))(f, g, __VA_ARGS__) +#define M_REDUCEI1_1(f, g, _1) f(_1) +#define M_REDUCEI1_2(f, g, _1, _2) g(f(_1), f(_2)) +#define M_REDUCEI1_3(f, g, _1, _2, _3) g(f(_1), g(f(_2), f(_3))) +#define M_REDUCEI1_4(f, g, _1, _2, _3, _4) g(f(_1), g(f(_2), g(f(_3), f(_4)))) +#define M_REDUCEI1_5(f, g, _1, _2, _3, _4, _5) g(f(_1), g(f(_2), g(f(_3), g(f(_4), f(_5))))) +#define M_REDUCEI1_6(f, g, _1, _2, _3, _4, _5, _6) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), f(_6)))))) +#define M_REDUCEI1_7(f, g, _1, _2, _3, _4, _5, _6, _7) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), f(_7))))))) +#define M_REDUCEI1_8(f, g, _1, _2, _3, _4, _5, _6, _7, _8) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), f(_8)))))))) +#define M_REDUCEI1_9(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), f(_9))))))))) +#define M_REDUCEI1_10(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), f(_10)))))))))) +#define M_REDUCEI1_11(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), f(_11))))))))))) +#define M_REDUCEI1_12(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), f(_12)))))))))))) +#define M_REDUCEI1_13(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), f(_13))))))))))))) +#define M_REDUCEI1_14(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), f(_14)))))))))))))) +#define M_REDUCEI1_15(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), f(_15))))))))))))))) +#define M_REDUCEI1_16(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), f(_16)))))))))))))))) +#define M_REDUCEI1_17(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), f(_17))))))))))))))))) +#define M_REDUCEI1_18(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), f(_18)))))))))))))))))) +#define M_REDUCEI1_19(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), f(_19))))))))))))))))))) +#define M_REDUCEI1_20(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), f(_20)))))))))))))))))))) +#define M_REDUCEI1_21(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), f(_21))))))))))))))))))))) +#define M_REDUCEI1_22(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), f(_22)))))))))))))))))))))) +#define M_REDUCEI1_23(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), f(_23))))))))))))))))))))))) +#define M_REDUCEI1_24(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), f(_24)))))))))))))))))))))))) +#define M_REDUCEI1_25(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), f(_25))))))))))))))))))))))))) +#define M_REDUCEI1_26(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), f(_26)))))))))))))))))))))))))) +#define M_REDUCEI1_27(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), f(_27))))))))))))))))))))))))))) +#define M_REDUCEI1_28(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), f(_28)))))))))))))))))))))))))))) +#define M_REDUCEI1_29(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), f(_29))))))))))))))))))))))))))))) +#define M_REDUCEI1_30(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), f(_30)))))))))))))))))))))))))))))) +#define M_REDUCEI1_31(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), f(_31))))))))))))))))))))))))))))))) +#define M_REDUCEI1_32(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), f(_32)))))))))))))))))))))))))))))))) +#define M_REDUCEI1_33(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), f(_33))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_34(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), f(_34)))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_35(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), f(_35))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_36(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), f(_36)))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_37(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), f(_37))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_38(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), f(_38)))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_39(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), f(_39))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_40(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), f(_40)))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_41(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), f(_41))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_42(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), f(_42)))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_43(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), f(_43))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_44(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), f(_44)))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_45(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), g(f(_44), f(_45))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_46(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), g(f(_44), g(f(_45), f(_46)))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_47(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), g(f(_44), g(f(_45), g(f(_46), f(_47))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_48(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), g(f(_44), g(f(_45), g(f(_46), g(f(_47), f(_48)))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_49(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), g(f(_44), g(f(_45), g(f(_46), g(f(_47), g(f(_48), f(_49))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_50(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), g(f(_44), g(f(_45), g(f(_46), g(f(_47), g(f(_48), g(f(_49), f(_50)))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_51(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), g(f(_44), g(f(_45), g(f(_46), g(f(_47), g(f(_48), g(f(_49), g(f(_50), f(_51))))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI1_52(f, g, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) g(f(_1), g(f(_2), g(f(_3), g(f(_4), g(f(_5), g(f(_6), g(f(_7), g(f(_8), g(f(_9), g(f(_10), g(f(_11), g(f(_12), g(f(_13), g(f(_14), g(f(_15), g(f(_16), g(f(_17), g(f(_18), g(f(_19), g(f(_20), g(f(_21), g(f(_22), g(f(_23), g(f(_24), g(f(_25), g(f(_26), g(f(_27), g(f(_28), g(f(_29), g(f(_30), g(f(_31), g(f(_32), g(f(_33), g(f(_34), g(f(_35), g(f(_36), g(f(_37), g(f(_38), g(f(_39), g(f(_40), g(f(_41), g(f(_42), g(f(_43), g(f(_44), g(f(_45), g(f(_46), g(f(_47), g(f(_48), g(f(_49), g(f(_50), g(f(_51), f(_52)))))))))))))))))))))))))))))))))))))))))))))))))))) + + +/* Map a macro to all given arguments and reduce all theses computation + with another reduce macro with an argument: + Example: + M_REDUCE2(f, g, data, a, b, c) ==> g( f(data,a), g( f(data, b), f(data, c))) + Generated by: + for i in $(seq 1 52) ; do printf "#define M_REDUCEI2_%d(f,g,d," $i ; for j in $(seq 1 $(($i - 1))) ; do printf "_%d, " $j ; done ; printf "_%d) " $i ; for j in $(seq 1 $(($i - 1))) ; do printf "g(f(d, _%d), " $j ; done ; printf "f(d, _%d)" $i ; for j in $(seq 1 $(( $i - 1 ))); do printf ")" ; done ; printf "\n" ; done + */ +#define M_REDUCE2(...) M_REDUCEI2_0(__VA_ARGS__) +#define M_REDUCEI2_0(f, g, d, ...) M_C(M_REDUCEI2_, M_NARGS(__VA_ARGS__))(f, g, d, __VA_ARGS__) +#define M_REDUCEI2_1(f,g,d,_1) f(d, _1) +#define M_REDUCEI2_2(f,g,d,_1, _2) g(f(d, _1), f(d, _2)) +#define M_REDUCEI2_3(f,g,d,_1, _2, _3) g(f(d, _1), g(f(d, _2), f(d, _3))) +#define M_REDUCEI2_4(f,g,d,_1, _2, _3, _4) g(f(d, _1), g(f(d, _2), g(f(d, _3), f(d, _4)))) +#define M_REDUCEI2_5(f,g,d,_1, _2, _3, _4, _5) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), f(d, _5))))) +#define M_REDUCEI2_6(f,g,d,_1, _2, _3, _4, _5, _6) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), f(d, _6)))))) +#define M_REDUCEI2_7(f,g,d,_1, _2, _3, _4, _5, _6, _7) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), f(d, _7))))))) +#define M_REDUCEI2_8(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), f(d, _8)))))))) +#define M_REDUCEI2_9(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), f(d, _9))))))))) +#define M_REDUCEI2_10(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), f(d, _10)))))))))) +#define M_REDUCEI2_11(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), f(d, _11))))))))))) +#define M_REDUCEI2_12(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), f(d, _12)))))))))))) +#define M_REDUCEI2_13(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), f(d, _13))))))))))))) +#define M_REDUCEI2_14(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), f(d, _14)))))))))))))) +#define M_REDUCEI2_15(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), f(d, _15))))))))))))))) +#define M_REDUCEI2_16(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), f(d, _16)))))))))))))))) +#define M_REDUCEI2_17(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), f(d, _17))))))))))))))))) +#define M_REDUCEI2_18(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), f(d, _18)))))))))))))))))) +#define M_REDUCEI2_19(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), f(d, _19))))))))))))))))))) +#define M_REDUCEI2_20(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), f(d, _20)))))))))))))))))))) +#define M_REDUCEI2_21(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), f(d, _21))))))))))))))))))))) +#define M_REDUCEI2_22(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), f(d, _22)))))))))))))))))))))) +#define M_REDUCEI2_23(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), f(d, _23))))))))))))))))))))))) +#define M_REDUCEI2_24(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), f(d, _24)))))))))))))))))))))))) +#define M_REDUCEI2_25(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), f(d, _25))))))))))))))))))))))))) +#define M_REDUCEI2_26(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), f(d, _26)))))))))))))))))))))))))) +#define M_REDUCEI2_27(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), f(d, _27))))))))))))))))))))))))))) +#define M_REDUCEI2_28(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), f(d, _28)))))))))))))))))))))))))))) +#define M_REDUCEI2_29(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), f(d, _29))))))))))))))))))))))))))))) +#define M_REDUCEI2_30(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), f(d, _30)))))))))))))))))))))))))))))) +#define M_REDUCEI2_31(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), f(d, _31))))))))))))))))))))))))))))))) +#define M_REDUCEI2_32(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), f(d, _32)))))))))))))))))))))))))))))))) +#define M_REDUCEI2_33(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), f(d, _33))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_34(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), f(d, _34)))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_35(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), f(d, _35))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_36(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), f(d, _36)))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_37(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), f(d, _37))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_38(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), f(d, _38)))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_39(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), f(d, _39))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_40(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), f(d, _40)))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_41(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), f(d, _41))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_42(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), f(d, _42)))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_43(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), f(d, _43))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_44(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), f(d, _44)))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_45(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), f(d, _45))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_46(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), f(d, _46)))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_47(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), f(d, _47))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_48(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), f(d, _48)))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_49(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), g(f(d, _48), f(d, _49))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_50(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), g(f(d, _48), g(f(d, _49), f(d, _50)))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_51(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), g(f(d, _48), g(f(d, _49), g(f(d, _50), f(d, _51))))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2_52(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), g(f(d, _48), g(f(d, _49), g(f(d, _50), g(f(d, _51), f(d, _52)))))))))))))))))))))))))))))))))))))))))))))))))))) + + +/* Duplicate of M_REDUCE2 for use by API transformation */ +#define M_REDUCE2B(...) M_REDUCEI2B_0(__VA_ARGS__) +#define M_REDUCEI2B_0(f, g, d, ...) M_C(M_REDUCEI2B_, M_NARGS(__VA_ARGS__))(f, g, d, __VA_ARGS__) +#define M_REDUCEI2B_1(f,g,d,_1) f(d, _1) +#define M_REDUCEI2B_2(f,g,d,_1, _2) g(f(d, _1), f(d, _2)) +#define M_REDUCEI2B_3(f,g,d,_1, _2, _3) g(f(d, _1), g(f(d, _2), f(d, _3))) +#define M_REDUCEI2B_4(f,g,d,_1, _2, _3, _4) g(f(d, _1), g(f(d, _2), g(f(d, _3), f(d, _4)))) +#define M_REDUCEI2B_5(f,g,d,_1, _2, _3, _4, _5) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), f(d, _5))))) +#define M_REDUCEI2B_6(f,g,d,_1, _2, _3, _4, _5, _6) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), f(d, _6)))))) +#define M_REDUCEI2B_7(f,g,d,_1, _2, _3, _4, _5, _6, _7) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), f(d, _7))))))) +#define M_REDUCEI2B_8(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), f(d, _8)))))))) +#define M_REDUCEI2B_9(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), f(d, _9))))))))) +#define M_REDUCEI2B_10(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), f(d, _10)))))))))) +#define M_REDUCEI2B_11(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), f(d, _11))))))))))) +#define M_REDUCEI2B_12(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), f(d, _12)))))))))))) +#define M_REDUCEI2B_13(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), f(d, _13))))))))))))) +#define M_REDUCEI2B_14(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), f(d, _14)))))))))))))) +#define M_REDUCEI2B_15(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), f(d, _15))))))))))))))) +#define M_REDUCEI2B_16(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), f(d, _16)))))))))))))))) +#define M_REDUCEI2B_17(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), f(d, _17))))))))))))))))) +#define M_REDUCEI2B_18(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), f(d, _18)))))))))))))))))) +#define M_REDUCEI2B_19(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), f(d, _19))))))))))))))))))) +#define M_REDUCEI2B_20(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), f(d, _20)))))))))))))))))))) +#define M_REDUCEI2B_21(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), f(d, _21))))))))))))))))))))) +#define M_REDUCEI2B_22(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), f(d, _22)))))))))))))))))))))) +#define M_REDUCEI2B_23(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), f(d, _23))))))))))))))))))))))) +#define M_REDUCEI2B_24(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), f(d, _24)))))))))))))))))))))))) +#define M_REDUCEI2B_25(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), f(d, _25))))))))))))))))))))))))) +#define M_REDUCEI2B_26(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), f(d, _26)))))))))))))))))))))))))) +#define M_REDUCEI2B_27(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), f(d, _27))))))))))))))))))))))))))) +#define M_REDUCEI2B_28(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), f(d, _28)))))))))))))))))))))))))))) +#define M_REDUCEI2B_29(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), f(d, _29))))))))))))))))))))))))))))) +#define M_REDUCEI2B_30(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), f(d, _30)))))))))))))))))))))))))))))) +#define M_REDUCEI2B_31(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), f(d, _31))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_32(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), f(d, _32)))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_33(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), f(d, _33))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_34(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), f(d, _34)))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_35(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), f(d, _35))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_36(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), f(d, _36)))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_37(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), f(d, _37))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_38(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), f(d, _38)))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_39(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), f(d, _39))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_40(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), f(d, _40)))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_41(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), f(d, _41))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_42(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), f(d, _42)))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_43(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), f(d, _43))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_44(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), f(d, _44)))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_45(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), f(d, _45))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_46(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), f(d, _46)))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_47(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), f(d, _47))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_48(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), f(d, _48)))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_49(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), g(f(d, _48), f(d, _49))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_50(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), g(f(d, _48), g(f(d, _49), f(d, _50)))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_51(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), g(f(d, _48), g(f(d, _49), g(f(d, _50), f(d, _51))))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI2B_52(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) g(f(d, _1), g(f(d, _2), g(f(d, _3), g(f(d, _4), g(f(d, _5), g(f(d, _6), g(f(d, _7), g(f(d, _8), g(f(d, _9), g(f(d, _10), g(f(d, _11), g(f(d, _12), g(f(d, _13), g(f(d, _14), g(f(d, _15), g(f(d, _16), g(f(d, _17), g(f(d, _18), g(f(d, _19), g(f(d, _20), g(f(d, _21), g(f(d, _22), g(f(d, _23), g(f(d, _24), g(f(d, _25), g(f(d, _26), g(f(d, _27), g(f(d, _28), g(f(d, _29), g(f(d, _30), g(f(d, _31), g(f(d, _32), g(f(d, _33), g(f(d, _34), g(f(d, _35), g(f(d, _36), g(f(d, _37), g(f(d, _38), g(f(d, _39), g(f(d, _40), g(f(d, _41), g(f(d, _42), g(f(d, _43), g(f(d, _44), g(f(d, _45), g(f(d, _46), g(f(d, _47), g(f(d, _48), g(f(d, _49), g(f(d, _50), g(f(d, _51), f(d, _52)))))))))))))))))))))))))))))))))))))))))))))))))))) + + +/* Map a macro to all given arguments and reduce all theses computation + with another reduce macro with an argument and a counter: + Example: + M_REDUCE3(f, g, data, a, b, c) ==> g( f(data,1,a), g( f(data, 2, b), f(data, 3, c))) + Generated by: + for i in $(seq 1 52) ; do printf "#define M_REDUCEI3_%d(f,g,d," $i ; for j in $(seq 1 $(($i - 1))) ; do printf "_%d, " $j ; done ; printf "_%d) " $i ; for j in $(seq 1 $(($i - 1))) ; do printf "g(f(d, %d, _%d), " $j $j ; done ; printf "f(d, %d, _%d)" $i $i ; for j in $(seq 1 $(( $i - 1 ))); do printf ")" ; done ; printf "\n" ; done + */ +#define M_REDUCE3(...) M_REDUCEI3_0(__VA_ARGS__) +#define M_REDUCEI3_0(f, g, d, ...) M_C(M_REDUCEI3_, M_NARGS(__VA_ARGS__))(f, g, d, __VA_ARGS__) +#define M_REDUCEI3_1(f,g,d,_1) f(d, 1, _1) +#define M_REDUCEI3_2(f,g,d,_1, _2) g(f(d, 1, _1), f(d, 2, _2)) +#define M_REDUCEI3_3(f,g,d,_1, _2, _3) g(f(d, 1, _1), g(f(d, 2, _2), f(d, 3, _3))) +#define M_REDUCEI3_4(f,g,d,_1, _2, _3, _4) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), f(d, 4, _4)))) +#define M_REDUCEI3_5(f,g,d,_1, _2, _3, _4, _5) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), f(d, 5, _5))))) +#define M_REDUCEI3_6(f,g,d,_1, _2, _3, _4, _5, _6) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), f(d, 6, _6)))))) +#define M_REDUCEI3_7(f,g,d,_1, _2, _3, _4, _5, _6, _7) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), f(d, 7, _7))))))) +#define M_REDUCEI3_8(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), f(d, 8, _8)))))))) +#define M_REDUCEI3_9(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), f(d, 9, _9))))))))) +#define M_REDUCEI3_10(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), f(d, 10, _10)))))))))) +#define M_REDUCEI3_11(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), f(d, 11, _11))))))))))) +#define M_REDUCEI3_12(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), f(d, 12, _12)))))))))))) +#define M_REDUCEI3_13(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), f(d, 13, _13))))))))))))) +#define M_REDUCEI3_14(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), f(d, 14, _14)))))))))))))) +#define M_REDUCEI3_15(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), f(d, 15, _15))))))))))))))) +#define M_REDUCEI3_16(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), f(d, 16, _16)))))))))))))))) +#define M_REDUCEI3_17(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), f(d, 17, _17))))))))))))))))) +#define M_REDUCEI3_18(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), f(d, 18, _18)))))))))))))))))) +#define M_REDUCEI3_19(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), f(d, 19, _19))))))))))))))))))) +#define M_REDUCEI3_20(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), f(d, 20, _20)))))))))))))))))))) +#define M_REDUCEI3_21(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), f(d, 21, _21))))))))))))))))))))) +#define M_REDUCEI3_22(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), f(d, 22, _22)))))))))))))))))))))) +#define M_REDUCEI3_23(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), f(d, 23, _23))))))))))))))))))))))) +#define M_REDUCEI3_24(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), f(d, 24, _24)))))))))))))))))))))))) +#define M_REDUCEI3_25(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), f(d, 25, _25))))))))))))))))))))))))) +#define M_REDUCEI3_26(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), f(d, 26, _26)))))))))))))))))))))))))) +#define M_REDUCEI3_27(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), f(d, 27, _27))))))))))))))))))))))))))) +#define M_REDUCEI3_28(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), f(d, 28, _28)))))))))))))))))))))))))))) +#define M_REDUCEI3_29(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), f(d, 29, _29))))))))))))))))))))))))))))) +#define M_REDUCEI3_30(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), f(d, 30, _30)))))))))))))))))))))))))))))) +#define M_REDUCEI3_31(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), f(d, 31, _31))))))))))))))))))))))))))))))) +#define M_REDUCEI3_32(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), f(d, 32, _32)))))))))))))))))))))))))))))))) +#define M_REDUCEI3_33(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), f(d, 33, _33))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_34(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), f(d, 34, _34)))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_35(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), f(d, 35, _35))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_36(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), f(d, 36, _36)))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_37(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), f(d, 37, _37))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_38(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), f(d, 38, _38)))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_39(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), f(d, 39, _39))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_40(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), f(d, 40, _40)))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_41(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), f(d, 41, _41))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_42(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), f(d, 42, _42)))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_43(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), f(d, 43, _43))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_44(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), f(d, 44, _44)))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_45(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), g(f(d, 44, _44), f(d, 45, _45))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_46(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), g(f(d, 44, _44), g(f(d, 45, _45), f(d, 46, _46)))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_47(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), g(f(d, 44, _44), g(f(d, 45, _45), g(f(d, 46, _46), f(d, 47, _47))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_48(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), g(f(d, 44, _44), g(f(d, 45, _45), g(f(d, 46, _46), g(f(d, 47, _47), f(d, 48, _48)))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_49(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), g(f(d, 44, _44), g(f(d, 45, _45), g(f(d, 46, _46), g(f(d, 47, _47), g(f(d, 48, _48), f(d, 49, _49))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_50(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), g(f(d, 44, _44), g(f(d, 45, _45), g(f(d, 46, _46), g(f(d, 47, _47), g(f(d, 48, _48), g(f(d, 49, _49), f(d, 50, _50)))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_51(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), g(f(d, 44, _44), g(f(d, 45, _45), g(f(d, 46, _46), g(f(d, 47, _47), g(f(d, 48, _48), g(f(d, 49, _49), g(f(d, 50, _50), f(d, 51, _51))))))))))))))))))))))))))))))))))))))))))))))))))) +#define M_REDUCEI3_52(f,g,d,_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) g(f(d, 1, _1), g(f(d, 2, _2), g(f(d, 3, _3), g(f(d, 4, _4), g(f(d, 5, _5), g(f(d, 6, _6), g(f(d, 7, _7), g(f(d, 8, _8), g(f(d, 9, _9), g(f(d, 10, _10), g(f(d, 11, _11), g(f(d, 12, _12), g(f(d, 13, _13), g(f(d, 14, _14), g(f(d, 15, _15), g(f(d, 16, _16), g(f(d, 17, _17), g(f(d, 18, _18), g(f(d, 19, _19), g(f(d, 20, _20), g(f(d, 21, _21), g(f(d, 22, _22), g(f(d, 23, _23), g(f(d, 24, _24), g(f(d, 25, _25), g(f(d, 26, _26), g(f(d, 27, _27), g(f(d, 28, _28), g(f(d, 29, _29), g(f(d, 30, _30), g(f(d, 31, _31), g(f(d, 32, _32), g(f(d, 33, _33), g(f(d, 34, _34), g(f(d, 35, _35), g(f(d, 36, _36), g(f(d, 37, _37), g(f(d, 38, _38), g(f(d, 39, _39), g(f(d, 40, _40), g(f(d, 41, _41), g(f(d, 42, _42), g(f(d, 43, _43), g(f(d, 44, _44), g(f(d, 45, _45), g(f(d, 46, _46), g(f(d, 47, _47), g(f(d, 48, _48), g(f(d, 49, _49), g(f(d, 50, _50), g(f(d, 51, _51), f(d, 52, _52)))))))))))))))))))))))))))))))))))))))))))))))))))) + + +/* Like M_MAP but with a comma separator. + M_MAP_C(f, a, b, c) ==> f(a), f(b), f(c) */ +#define M_MAP_C(f, ...) M_REDUCE(f, M_MAP_C_ID, __VA_ARGS__) +#define M_MAP_C_ID(...) __VA_ARGS__ + +/* Like M_MAP2 but with a comma separator. + M_MAP2_C(f, d, a, b, c) ==> f(d, a), f(d, b), f(d, c) */ +#define M_MAP2_C(f, d, ...) M_REDUCE2(f, M_MAP2_C_ID, d, __VA_ARGS__) +#define M_MAP2_C_ID(...) __VA_ARGS__ + +/* Like M_MAP2_C but for API transformation only */ +#define M_MAP2B_C(f, d, ...) M_REDUCE2B(f, M_MAP2B_C_ID, d, __VA_ARGS__) +#define M_MAP2B_C_ID(...) __VA_ARGS__ + + +/* Like M_MAP3 but with a comma separator. + M_MAP3_C(f, d, a, b, c) ==> f(d, 1, a), f(d, 2, b), f(d, 3, c) */ +#define M_MAP3_C(f, d, ...) M_REDUCE3(f, M_MAP3_C_ID, d, __VA_ARGS__) +#define M_MAP3_C_ID(...) __VA_ARGS__ + + +/* Map a function f to each pair of arguments from the arglist 'a' and the arglist 'b' + M_CROSS_MAP(f, (a,b), (c,d) ) ==> f(a, c) f(a, d) f(b, c) f(b, d) */ +#define M_CROSS_MAP(f, alist, blist) M_CROSSI_MAP1(f, alist, blist) +#define M_CROSSI_MAP1(f, alist, blist) M_C(M_CROSSI_MAP1_, M_NARGS alist)(f, blist, M_ID alist) +/* Generated by : +for i in $(seq 1 52) ; do + printf "#define M_CROSSI_MAP1_%d(...) M_CROSSI_MAP1_%db(__VA_ARGS__)\n" $i $i ; + printf "#define M_CROSSI_MAP1_%db(f, blist" $i ; for j in $(seq 1 $i) ; do printf ", a%d" $j ; done ; printf ") " ; + for j in $(seq 1 $i) ; do printf "M_MAP2(f, a%d, M_ID blist) " $j; done ; + printf "\n" ; +done +*/ +#define M_CROSSI_MAP1_1(...) M_CROSSI_MAP1_1b(__VA_ARGS__) +#define M_CROSSI_MAP1_1b(f, blist, a1) M_MAP2(f, a1, M_ID blist) +#define M_CROSSI_MAP1_2(...) M_CROSSI_MAP1_2b(__VA_ARGS__) +#define M_CROSSI_MAP1_2b(f, blist, a1, a2) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) +#define M_CROSSI_MAP1_3(...) M_CROSSI_MAP1_3b(__VA_ARGS__) +#define M_CROSSI_MAP1_3b(f, blist, a1, a2, a3) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) +#define M_CROSSI_MAP1_4(...) M_CROSSI_MAP1_4b(__VA_ARGS__) +#define M_CROSSI_MAP1_4b(f, blist, a1, a2, a3, a4) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) +#define M_CROSSI_MAP1_5(...) M_CROSSI_MAP1_5b(__VA_ARGS__) +#define M_CROSSI_MAP1_5b(f, blist, a1, a2, a3, a4, a5) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) +#define M_CROSSI_MAP1_6(...) M_CROSSI_MAP1_6b(__VA_ARGS__) +#define M_CROSSI_MAP1_6b(f, blist, a1, a2, a3, a4, a5, a6) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) +#define M_CROSSI_MAP1_7(...) M_CROSSI_MAP1_7b(__VA_ARGS__) +#define M_CROSSI_MAP1_7b(f, blist, a1, a2, a3, a4, a5, a6, a7) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) +#define M_CROSSI_MAP1_8(...) M_CROSSI_MAP1_8b(__VA_ARGS__) +#define M_CROSSI_MAP1_8b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) +#define M_CROSSI_MAP1_9(...) M_CROSSI_MAP1_9b(__VA_ARGS__) +#define M_CROSSI_MAP1_9b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) +#define M_CROSSI_MAP1_10(...) M_CROSSI_MAP1_10b(__VA_ARGS__) +#define M_CROSSI_MAP1_10b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) +#define M_CROSSI_MAP1_11(...) M_CROSSI_MAP1_11b(__VA_ARGS__) +#define M_CROSSI_MAP1_11b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) +#define M_CROSSI_MAP1_12(...) M_CROSSI_MAP1_12b(__VA_ARGS__) +#define M_CROSSI_MAP1_12b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) +#define M_CROSSI_MAP1_13(...) M_CROSSI_MAP1_13b(__VA_ARGS__) +#define M_CROSSI_MAP1_13b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) +#define M_CROSSI_MAP1_14(...) M_CROSSI_MAP1_14b(__VA_ARGS__) +#define M_CROSSI_MAP1_14b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) +#define M_CROSSI_MAP1_15(...) M_CROSSI_MAP1_15b(__VA_ARGS__) +#define M_CROSSI_MAP1_15b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) +#define M_CROSSI_MAP1_16(...) M_CROSSI_MAP1_16b(__VA_ARGS__) +#define M_CROSSI_MAP1_16b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) +#define M_CROSSI_MAP1_17(...) M_CROSSI_MAP1_17b(__VA_ARGS__) +#define M_CROSSI_MAP1_17b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) +#define M_CROSSI_MAP1_18(...) M_CROSSI_MAP1_18b(__VA_ARGS__) +#define M_CROSSI_MAP1_18b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) +#define M_CROSSI_MAP1_19(...) M_CROSSI_MAP1_19b(__VA_ARGS__) +#define M_CROSSI_MAP1_19b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) +#define M_CROSSI_MAP1_20(...) M_CROSSI_MAP1_20b(__VA_ARGS__) +#define M_CROSSI_MAP1_20b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) +#define M_CROSSI_MAP1_21(...) M_CROSSI_MAP1_21b(__VA_ARGS__) +#define M_CROSSI_MAP1_21b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) +#define M_CROSSI_MAP1_22(...) M_CROSSI_MAP1_22b(__VA_ARGS__) +#define M_CROSSI_MAP1_22b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) +#define M_CROSSI_MAP1_23(...) M_CROSSI_MAP1_23b(__VA_ARGS__) +#define M_CROSSI_MAP1_23b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) +#define M_CROSSI_MAP1_24(...) M_CROSSI_MAP1_24b(__VA_ARGS__) +#define M_CROSSI_MAP1_24b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) +#define M_CROSSI_MAP1_25(...) M_CROSSI_MAP1_25b(__VA_ARGS__) +#define M_CROSSI_MAP1_25b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) +#define M_CROSSI_MAP1_26(...) M_CROSSI_MAP1_26b(__VA_ARGS__) +#define M_CROSSI_MAP1_26b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) +#define M_CROSSI_MAP1_27(...) M_CROSSI_MAP1_27b(__VA_ARGS__) +#define M_CROSSI_MAP1_27b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) +#define M_CROSSI_MAP1_28(...) M_CROSSI_MAP1_28b(__VA_ARGS__) +#define M_CROSSI_MAP1_28b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) +#define M_CROSSI_MAP1_29(...) M_CROSSI_MAP1_29b(__VA_ARGS__) +#define M_CROSSI_MAP1_29b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) +#define M_CROSSI_MAP1_30(...) M_CROSSI_MAP1_30b(__VA_ARGS__) +#define M_CROSSI_MAP1_30b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) +#define M_CROSSI_MAP1_31(...) M_CROSSI_MAP1_31b(__VA_ARGS__) +#define M_CROSSI_MAP1_31b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) +#define M_CROSSI_MAP1_32(...) M_CROSSI_MAP1_32b(__VA_ARGS__) +#define M_CROSSI_MAP1_32b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) +#define M_CROSSI_MAP1_33(...) M_CROSSI_MAP1_33b(__VA_ARGS__) +#define M_CROSSI_MAP1_33b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) +#define M_CROSSI_MAP1_34(...) M_CROSSI_MAP1_34b(__VA_ARGS__) +#define M_CROSSI_MAP1_34b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) +#define M_CROSSI_MAP1_35(...) M_CROSSI_MAP1_35b(__VA_ARGS__) +#define M_CROSSI_MAP1_35b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) +#define M_CROSSI_MAP1_36(...) M_CROSSI_MAP1_36b(__VA_ARGS__) +#define M_CROSSI_MAP1_36b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) +#define M_CROSSI_MAP1_37(...) M_CROSSI_MAP1_37b(__VA_ARGS__) +#define M_CROSSI_MAP1_37b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) +#define M_CROSSI_MAP1_38(...) M_CROSSI_MAP1_38b(__VA_ARGS__) +#define M_CROSSI_MAP1_38b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) +#define M_CROSSI_MAP1_39(...) M_CROSSI_MAP1_39b(__VA_ARGS__) +#define M_CROSSI_MAP1_39b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) +#define M_CROSSI_MAP1_40(...) M_CROSSI_MAP1_40b(__VA_ARGS__) +#define M_CROSSI_MAP1_40b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) +#define M_CROSSI_MAP1_41(...) M_CROSSI_MAP1_41b(__VA_ARGS__) +#define M_CROSSI_MAP1_41b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) +#define M_CROSSI_MAP1_42(...) M_CROSSI_MAP1_42b(__VA_ARGS__) +#define M_CROSSI_MAP1_42b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) +#define M_CROSSI_MAP1_43(...) M_CROSSI_MAP1_43b(__VA_ARGS__) +#define M_CROSSI_MAP1_43b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) +#define M_CROSSI_MAP1_44(...) M_CROSSI_MAP1_44b(__VA_ARGS__) +#define M_CROSSI_MAP1_44b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) +#define M_CROSSI_MAP1_45(...) M_CROSSI_MAP1_45b(__VA_ARGS__) +#define M_CROSSI_MAP1_45b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) M_MAP2(f, a45, M_ID blist) +#define M_CROSSI_MAP1_46(...) M_CROSSI_MAP1_46b(__VA_ARGS__) +#define M_CROSSI_MAP1_46b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) M_MAP2(f, a45, M_ID blist) M_MAP2(f, a46, M_ID blist) +#define M_CROSSI_MAP1_47(...) M_CROSSI_MAP1_47b(__VA_ARGS__) +#define M_CROSSI_MAP1_47b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) M_MAP2(f, a45, M_ID blist) M_MAP2(f, a46, M_ID blist) M_MAP2(f, a47, M_ID blist) +#define M_CROSSI_MAP1_48(...) M_CROSSI_MAP1_48b(__VA_ARGS__) +#define M_CROSSI_MAP1_48b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) M_MAP2(f, a45, M_ID blist) M_MAP2(f, a46, M_ID blist) M_MAP2(f, a47, M_ID blist) M_MAP2(f, a48, M_ID blist) +#define M_CROSSI_MAP1_49(...) M_CROSSI_MAP1_49b(__VA_ARGS__) +#define M_CROSSI_MAP1_49b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) M_MAP2(f, a45, M_ID blist) M_MAP2(f, a46, M_ID blist) M_MAP2(f, a47, M_ID blist) M_MAP2(f, a48, M_ID blist) M_MAP2(f, a49, M_ID blist) +#define M_CROSSI_MAP1_50(...) M_CROSSI_MAP1_50b(__VA_ARGS__) +#define M_CROSSI_MAP1_50b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) M_MAP2(f, a45, M_ID blist) M_MAP2(f, a46, M_ID blist) M_MAP2(f, a47, M_ID blist) M_MAP2(f, a48, M_ID blist) M_MAP2(f, a49, M_ID blist) M_MAP2(f, a50, M_ID blist) +#define M_CROSSI_MAP1_51(...) M_CROSSI_MAP1_51b(__VA_ARGS__) +#define M_CROSSI_MAP1_51b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) M_MAP2(f, a45, M_ID blist) M_MAP2(f, a46, M_ID blist) M_MAP2(f, a47, M_ID blist) M_MAP2(f, a48, M_ID blist) M_MAP2(f, a49, M_ID blist) M_MAP2(f, a50, M_ID blist) M_MAP2(f, a51, M_ID blist) +#define M_CROSSI_MAP1_52(...) M_CROSSI_MAP1_52b(__VA_ARGS__) +#define M_CROSSI_MAP1_52b(f, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52) M_MAP2(f, a1, M_ID blist) M_MAP2(f, a2, M_ID blist) M_MAP2(f, a3, M_ID blist) M_MAP2(f, a4, M_ID blist) M_MAP2(f, a5, M_ID blist) M_MAP2(f, a6, M_ID blist) M_MAP2(f, a7, M_ID blist) M_MAP2(f, a8, M_ID blist) M_MAP2(f, a9, M_ID blist) M_MAP2(f, a10, M_ID blist) M_MAP2(f, a11, M_ID blist) M_MAP2(f, a12, M_ID blist) M_MAP2(f, a13, M_ID blist) M_MAP2(f, a14, M_ID blist) M_MAP2(f, a15, M_ID blist) M_MAP2(f, a16, M_ID blist) M_MAP2(f, a17, M_ID blist) M_MAP2(f, a18, M_ID blist) M_MAP2(f, a19, M_ID blist) M_MAP2(f, a20, M_ID blist) M_MAP2(f, a21, M_ID blist) M_MAP2(f, a22, M_ID blist) M_MAP2(f, a23, M_ID blist) M_MAP2(f, a24, M_ID blist) M_MAP2(f, a25, M_ID blist) M_MAP2(f, a26, M_ID blist) M_MAP2(f, a27, M_ID blist) M_MAP2(f, a28, M_ID blist) M_MAP2(f, a29, M_ID blist) M_MAP2(f, a30, M_ID blist) M_MAP2(f, a31, M_ID blist) M_MAP2(f, a32, M_ID blist) M_MAP2(f, a33, M_ID blist) M_MAP2(f, a34, M_ID blist) M_MAP2(f, a35, M_ID blist) M_MAP2(f, a36, M_ID blist) M_MAP2(f, a37, M_ID blist) M_MAP2(f, a38, M_ID blist) M_MAP2(f, a39, M_ID blist) M_MAP2(f, a40, M_ID blist) M_MAP2(f, a41, M_ID blist) M_MAP2(f, a42, M_ID blist) M_MAP2(f, a43, M_ID blist) M_MAP2(f, a44, M_ID blist) M_MAP2(f, a45, M_ID blist) M_MAP2(f, a46, M_ID blist) M_MAP2(f, a47, M_ID blist) M_MAP2(f, a48, M_ID blist) M_MAP2(f, a49, M_ID blist) M_MAP2(f, a50, M_ID blist) M_MAP2(f, a51, M_ID blist) M_MAP2(f, a52, M_ID blist) + + +/* Map a function f to each pair of arguments from the arglist 'a' and the arglist 'b', with an additional given data. + M_CROSS_MAP2(f, dat, (a,b), (c,d) ) ==> f(dat, a, c) f(dat, a, d) f(dat, b, c) f(dat, b, d) */ +#define M_CROSS_MAP2(f, d, alist, blist) M_CROSSI_MAP2(f, d, alist, blist) +#define M_CROSSI_MAP2(f, d, alist, blist) M_C(M_CROSSI_MAP2_, M_NARGS alist)(f, d, blist, M_ID alist) +/* Generated by : +for i in $(seq 1 52) ; do + printf "#define M_CROSSI_MAP2_%d(...) M_CROSSI_MAP2_%db(__VA_ARGS__)\n" $i $i ; + printf "#define M_CROSSI_MAP2_%db(f, d, blist" $i ; for j in $(seq 1 $i) ; do printf ", a%d" $j ; done ; printf ") " ; + for j in $(seq 1 $i) ; do printf "M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a%d), M_ID blist) " $j; done ; + printf "\n" ; +done +*/ +#define M_CROSSI_MAP2_1(...) M_CROSSI_MAP2_1b(__VA_ARGS__) +#define M_CROSSI_MAP2_1b(f, d, blist, a1) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) +#define M_CROSSI_MAP2_2(...) M_CROSSI_MAP2_2b(__VA_ARGS__) +#define M_CROSSI_MAP2_2b(f, d, blist, a1, a2) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) +#define M_CROSSI_MAP2_3(...) M_CROSSI_MAP2_3b(__VA_ARGS__) +#define M_CROSSI_MAP2_3b(f, d, blist, a1, a2, a3) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) +#define M_CROSSI_MAP2_4(...) M_CROSSI_MAP2_4b(__VA_ARGS__) +#define M_CROSSI_MAP2_4b(f, d, blist, a1, a2, a3, a4) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) +#define M_CROSSI_MAP2_5(...) M_CROSSI_MAP2_5b(__VA_ARGS__) +#define M_CROSSI_MAP2_5b(f, d, blist, a1, a2, a3, a4, a5) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) +#define M_CROSSI_MAP2_6(...) M_CROSSI_MAP2_6b(__VA_ARGS__) +#define M_CROSSI_MAP2_6b(f, d, blist, a1, a2, a3, a4, a5, a6) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) +#define M_CROSSI_MAP2_7(...) M_CROSSI_MAP2_7b(__VA_ARGS__) +#define M_CROSSI_MAP2_7b(f, d, blist, a1, a2, a3, a4, a5, a6, a7) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) +#define M_CROSSI_MAP2_8(...) M_CROSSI_MAP2_8b(__VA_ARGS__) +#define M_CROSSI_MAP2_8b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) +#define M_CROSSI_MAP2_9(...) M_CROSSI_MAP2_9b(__VA_ARGS__) +#define M_CROSSI_MAP2_9b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) +#define M_CROSSI_MAP2_10(...) M_CROSSI_MAP2_10b(__VA_ARGS__) +#define M_CROSSI_MAP2_10b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) +#define M_CROSSI_MAP2_11(...) M_CROSSI_MAP2_11b(__VA_ARGS__) +#define M_CROSSI_MAP2_11b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) +#define M_CROSSI_MAP2_12(...) M_CROSSI_MAP2_12b(__VA_ARGS__) +#define M_CROSSI_MAP2_12b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) +#define M_CROSSI_MAP2_13(...) M_CROSSI_MAP2_13b(__VA_ARGS__) +#define M_CROSSI_MAP2_13b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) +#define M_CROSSI_MAP2_14(...) M_CROSSI_MAP2_14b(__VA_ARGS__) +#define M_CROSSI_MAP2_14b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) +#define M_CROSSI_MAP2_15(...) M_CROSSI_MAP2_15b(__VA_ARGS__) +#define M_CROSSI_MAP2_15b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) +#define M_CROSSI_MAP2_16(...) M_CROSSI_MAP2_16b(__VA_ARGS__) +#define M_CROSSI_MAP2_16b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) +#define M_CROSSI_MAP2_17(...) M_CROSSI_MAP2_17b(__VA_ARGS__) +#define M_CROSSI_MAP2_17b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) +#define M_CROSSI_MAP2_18(...) M_CROSSI_MAP2_18b(__VA_ARGS__) +#define M_CROSSI_MAP2_18b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) +#define M_CROSSI_MAP2_19(...) M_CROSSI_MAP2_19b(__VA_ARGS__) +#define M_CROSSI_MAP2_19b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) +#define M_CROSSI_MAP2_20(...) M_CROSSI_MAP2_20b(__VA_ARGS__) +#define M_CROSSI_MAP2_20b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) +#define M_CROSSI_MAP2_21(...) M_CROSSI_MAP2_21b(__VA_ARGS__) +#define M_CROSSI_MAP2_21b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) +#define M_CROSSI_MAP2_22(...) M_CROSSI_MAP2_22b(__VA_ARGS__) +#define M_CROSSI_MAP2_22b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) +#define M_CROSSI_MAP2_23(...) M_CROSSI_MAP2_23b(__VA_ARGS__) +#define M_CROSSI_MAP2_23b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) +#define M_CROSSI_MAP2_24(...) M_CROSSI_MAP2_24b(__VA_ARGS__) +#define M_CROSSI_MAP2_24b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) +#define M_CROSSI_MAP2_25(...) M_CROSSI_MAP2_25b(__VA_ARGS__) +#define M_CROSSI_MAP2_25b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) +#define M_CROSSI_MAP2_26(...) M_CROSSI_MAP2_26b(__VA_ARGS__) +#define M_CROSSI_MAP2_26b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) +#define M_CROSSI_MAP2_27(...) M_CROSSI_MAP2_27b(__VA_ARGS__) +#define M_CROSSI_MAP2_27b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) +#define M_CROSSI_MAP2_28(...) M_CROSSI_MAP2_28b(__VA_ARGS__) +#define M_CROSSI_MAP2_28b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) +#define M_CROSSI_MAP2_29(...) M_CROSSI_MAP2_29b(__VA_ARGS__) +#define M_CROSSI_MAP2_29b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) +#define M_CROSSI_MAP2_30(...) M_CROSSI_MAP2_30b(__VA_ARGS__) +#define M_CROSSI_MAP2_30b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) +#define M_CROSSI_MAP2_31(...) M_CROSSI_MAP2_31b(__VA_ARGS__) +#define M_CROSSI_MAP2_31b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) +#define M_CROSSI_MAP2_32(...) M_CROSSI_MAP2_32b(__VA_ARGS__) +#define M_CROSSI_MAP2_32b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) +#define M_CROSSI_MAP2_33(...) M_CROSSI_MAP2_33b(__VA_ARGS__) +#define M_CROSSI_MAP2_33b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) +#define M_CROSSI_MAP2_34(...) M_CROSSI_MAP2_34b(__VA_ARGS__) +#define M_CROSSI_MAP2_34b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) +#define M_CROSSI_MAP2_35(...) M_CROSSI_MAP2_35b(__VA_ARGS__) +#define M_CROSSI_MAP2_35b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) +#define M_CROSSI_MAP2_36(...) M_CROSSI_MAP2_36b(__VA_ARGS__) +#define M_CROSSI_MAP2_36b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) +#define M_CROSSI_MAP2_37(...) M_CROSSI_MAP2_37b(__VA_ARGS__) +#define M_CROSSI_MAP2_37b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) +#define M_CROSSI_MAP2_38(...) M_CROSSI_MAP2_38b(__VA_ARGS__) +#define M_CROSSI_MAP2_38b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) +#define M_CROSSI_MAP2_39(...) M_CROSSI_MAP2_39b(__VA_ARGS__) +#define M_CROSSI_MAP2_39b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) +#define M_CROSSI_MAP2_40(...) M_CROSSI_MAP2_40b(__VA_ARGS__) +#define M_CROSSI_MAP2_40b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) +#define M_CROSSI_MAP2_41(...) M_CROSSI_MAP2_41b(__VA_ARGS__) +#define M_CROSSI_MAP2_41b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) +#define M_CROSSI_MAP2_42(...) M_CROSSI_MAP2_42b(__VA_ARGS__) +#define M_CROSSI_MAP2_42b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) +#define M_CROSSI_MAP2_43(...) M_CROSSI_MAP2_43b(__VA_ARGS__) +#define M_CROSSI_MAP2_43b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) +#define M_CROSSI_MAP2_44(...) M_CROSSI_MAP2_44b(__VA_ARGS__) +#define M_CROSSI_MAP2_44b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) +#define M_CROSSI_MAP2_45(...) M_CROSSI_MAP2_45b(__VA_ARGS__) +#define M_CROSSI_MAP2_45b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a45), M_ID blist) +#define M_CROSSI_MAP2_46(...) M_CROSSI_MAP2_46b(__VA_ARGS__) +#define M_CROSSI_MAP2_46b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a45), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a46), M_ID blist) +#define M_CROSSI_MAP2_47(...) M_CROSSI_MAP2_47b(__VA_ARGS__) +#define M_CROSSI_MAP2_47b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a45), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a46), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a47), M_ID blist) +#define M_CROSSI_MAP2_48(...) M_CROSSI_MAP2_48b(__VA_ARGS__) +#define M_CROSSI_MAP2_48b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a45), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a46), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a47), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a48), M_ID blist) +#define M_CROSSI_MAP2_49(...) M_CROSSI_MAP2_49b(__VA_ARGS__) +#define M_CROSSI_MAP2_49b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a45), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a46), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a47), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a48), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a49), M_ID blist) +#define M_CROSSI_MAP2_50(...) M_CROSSI_MAP2_50b(__VA_ARGS__) +#define M_CROSSI_MAP2_50b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a45), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a46), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a47), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a48), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a49), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a50), M_ID blist) +#define M_CROSSI_MAP2_51(...) M_CROSSI_MAP2_51b(__VA_ARGS__) +#define M_CROSSI_MAP2_51b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a45), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a46), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a47), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a48), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a49), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a50), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a51), M_ID blist) +#define M_CROSSI_MAP2_52(...) M_CROSSI_MAP2_52b(__VA_ARGS__) +#define M_CROSSI_MAP2_52b(f, d, blist, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a1), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a2), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a3), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a4), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a5), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a6), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a7), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a8), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a9), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a10), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a11), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a12), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a13), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a14), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a15), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a16), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a17), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a18), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a19), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a20), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a21), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a22), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a23), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a24), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a25), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a26), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a27), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a28), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a29), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a30), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a31), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a32), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a33), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a34), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a35), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a36), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a37), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a38), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a39), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a40), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a41), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a42), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a43), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a44), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a45), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a46), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a47), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a48), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a49), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a50), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a51), M_ID blist) M_MAP2(M_CROSSI_MAP2_F_A, (f, d, a52), M_ID blist) +/* Wrapper function that unpacks the argument to call the real macro with the right arguments */ +#define M_CROSSI_MAP2_F_A(d, b) M_CROSSI_MAP2_F_B(M_ID d, b) +#define M_CROSSI_MAP2_F_B(...) M_CROSSI_MAP2_F_C(__VA_ARGS__) +#define M_CROSSI_MAP2_F_C(f, d, a1, b1) f(d, a1, b1) + + +/* Sequence of numerical + Example: + M_SEQ(init,end)==>init, init+1, ...end + M_MAP(f, M_SEQ(init, end)) ==> f(init) f(init+1) .... f(end) + M_MAP2(f, d, M_SEQ(init, end)) +*/ +#define M_SEQ(init, end) M_MID_ARGS(init, M_INC(M_SUB(end, init)), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52) + +/* Replicate the value 'N' times */ +#define M_REPLICATE(N, value) \ + M_MAP2(M_RET_ARG1, value, M_SEQ(1, N)) + +/* Replicate the value 'N' times, separated by commas */ +#define M_REPLICATE_C(N, value) \ + M_MAP2_C(M_RET_ARG1, value, M_SEQ(1, N)) + +/* Filter the elements of the list so that only the elements + that match the function f are returned. + f takes as argument (extra, item) and shall return 0 or 1. */ +#define M_FILTER(f, extra, ...) \ + M_MAP2( M_FILTER_00, (f, extra), __VA_ARGS__) + +/* Filter the elements of the list so that only the elements (separated by commas) + that match the function f are returned. + f takes as argument (extra, item) and shall return 0 or 1. */ +#define M_FILTER_C(f, extra, ...) \ + M_REMOVE_PARENTHESIS (M_REDUCE2(M_FILTER_00, M_CAT_ARGLIST, (f, extra), __VA_ARGS__) ) + +/* d is (filter_function, extra_value) item is the element ot test */ +#define M_FILTER_00(d, item) \ + M_IF(M_PAIR_1 d(M_PAIR_2 d, item))(item, ) + + +/* Return the HEAD element of a VA_ARGS */ +#define M_HEAD(x, ...) x + +/* Return the second HEAD element of a VA_ARGS */ +#define M_HEAD_2(x, y, ...) y + +/* Return the tail sublist of the VA_ARGS (removing the HEAD) */ +#define M_TAIL(x, ...) __VA_ARGS__ + +/* Return the tail sublist of the sublist of the VA_ARGS (removing the 2 first elements) */ +#define M_TAIL_2(x, y, ...) __VA_ARGS__ + +/* Return the tail sublist of the sublist of the VA_ARGS (removing the 3 first elements) */ +#define M_TAIL_3(x, y, z, ...) __VA_ARGS__ + +/* Concatene two arglists 'a' and 'b', + handling the case of empty arglist + handling the cases where the arguments are not arglist. + and returning an arglist within parenthesis if it concats. + (internal macro) +*/ +#define M_CAT_ARGLIST(a, b) \ + M_IF_EMPTY(a)(b, M_IF_EMPTY(b)(a,( M_REMOVE_PARENTHESIS (a) , M_REMOVE_PARENTHESIS (b)))) + + +/* Merge two arglists 'a' and 'b' + (a1, a2, a3) and (b1, b2, b3) ==> ( (a1, b1) , (b1, b2), (a3, b3) ) + They shall be non empty and of the same size. + (internal macro) + Generated by: + for i in $(seq 52) ; do printf "#define M_MERGE_ARGLIST_%d_%d(" $i $i ; for j in $(seq 1 $i) ; do printf "a%d, " $j ; done ; for j in $(seq 1 $i) ; do printf "b%d" $j ; if test $i != $j ; then printf ", " ; fi ; done ; printf ") " ; for j in $(seq 1 $i) ; do printf "(a%d, b%d) " $j $j ; if (test $i != $j ) ; then printf ", " ; fi ; done ; printf "\n" ; done +*/ +#define M_MERGE_ARGLIST(a, b) ( M_APPLY( M_C4(M_MERGE_ARGLIST_, M_NARGS a, _, M_NARGS b), M_MERGE_ARGLIST_ID a, M_MERGE_ARGLIST_ID b) ) +#define M_MERGE_ARGLIST_ID(...) __VA_ARGS__ +#define M_MERGE_ARGLIST_1_1(a1, b1) (a1, b1) +#define M_MERGE_ARGLIST_2_2(a1, a2, b1, b2) (a1, b1) , (a2, b2) +#define M_MERGE_ARGLIST_3_3(a1, a2, a3, b1, b2, b3) (a1, b1) , (a2, b2) , (a3, b3) +#define M_MERGE_ARGLIST_4_4(a1, a2, a3, a4, b1, b2, b3, b4) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) +#define M_MERGE_ARGLIST_5_5(a1, a2, a3, a4, a5, b1, b2, b3, b4, b5) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) +#define M_MERGE_ARGLIST_6_6(a1, a2, a3, a4, a5, a6, b1, b2, b3, b4, b5, b6) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) +#define M_MERGE_ARGLIST_7_7(a1, a2, a3, a4, a5, a6, a7, b1, b2, b3, b4, b5, b6, b7) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) +#define M_MERGE_ARGLIST_8_8(a1, a2, a3, a4, a5, a6, a7, a8, b1, b2, b3, b4, b5, b6, b7, b8) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) +#define M_MERGE_ARGLIST_9_9(a1, a2, a3, a4, a5, a6, a7, a8, a9, b1, b2, b3, b4, b5, b6, b7, b8, b9) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) +#define M_MERGE_ARGLIST_10_10(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) +#define M_MERGE_ARGLIST_11_11(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) +#define M_MERGE_ARGLIST_12_12(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) +#define M_MERGE_ARGLIST_13_13(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) +#define M_MERGE_ARGLIST_14_14(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) +#define M_MERGE_ARGLIST_15_15(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) +#define M_MERGE_ARGLIST_16_16(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) +#define M_MERGE_ARGLIST_17_17(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) +#define M_MERGE_ARGLIST_18_18(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) +#define M_MERGE_ARGLIST_19_19(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) +#define M_MERGE_ARGLIST_20_20(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) +#define M_MERGE_ARGLIST_21_21(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) +#define M_MERGE_ARGLIST_22_22(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) +#define M_MERGE_ARGLIST_23_23(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) +#define M_MERGE_ARGLIST_24_24(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) +#define M_MERGE_ARGLIST_25_25(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) +#define M_MERGE_ARGLIST_26_26(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) +#define M_MERGE_ARGLIST_27_27(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) +#define M_MERGE_ARGLIST_28_28(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) +#define M_MERGE_ARGLIST_29_29(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) +#define M_MERGE_ARGLIST_30_30(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) +#define M_MERGE_ARGLIST_31_31(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) +#define M_MERGE_ARGLIST_32_32(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) +#define M_MERGE_ARGLIST_33_33(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) +#define M_MERGE_ARGLIST_34_34(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) +#define M_MERGE_ARGLIST_35_35(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) +#define M_MERGE_ARGLIST_36_36(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) +#define M_MERGE_ARGLIST_37_37(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) +#define M_MERGE_ARGLIST_38_38(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) +#define M_MERGE_ARGLIST_39_39(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) +#define M_MERGE_ARGLIST_40_40(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) +#define M_MERGE_ARGLIST_41_41(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) +#define M_MERGE_ARGLIST_42_42(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) +#define M_MERGE_ARGLIST_43_43(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) +#define M_MERGE_ARGLIST_44_44(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) +#define M_MERGE_ARGLIST_45_45(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) , (a45, b45) +#define M_MERGE_ARGLIST_46_46(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) , (a45, b45) , (a46, b46) +#define M_MERGE_ARGLIST_47_47(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) , (a45, b45) , (a46, b46) , (a47, b47) +#define M_MERGE_ARGLIST_48_48(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) , (a45, b45) , (a46, b46) , (a47, b47) , (a48, b48) +#define M_MERGE_ARGLIST_49_49(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) , (a45, b45) , (a46, b46) , (a47, b47) , (a48, b48) , (a49, b49) +#define M_MERGE_ARGLIST_50_50(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49, b50) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) , (a45, b45) , (a46, b46) , (a47, b47) , (a48, b48) , (a49, b49) , (a50, b50) +#define M_MERGE_ARGLIST_51_51(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49, b50, b51) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) , (a45, b45) , (a46, b46) , (a47, b47) , (a48, b48) , (a49, b49) , (a50, b50) , (a51, b51) +#define M_MERGE_ARGLIST_52_52(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49, b50, b51, b52) (a1, b1) , (a2, b2) , (a3, b3) , (a4, b4) , (a5, b5) , (a6, b6) , (a7, b7) , (a8, b8) , (a9, b9) , (a10, b10) , (a11, b11) , (a12, b12) , (a13, b13) , (a14, b14) , (a15, b15) , (a16, b16) , (a17, b17) , (a18, b18) , (a19, b19) , (a20, b20) , (a21, b21) , (a22, b22) , (a23, b23) , (a24, b24) , (a25, b25) , (a26, b26) , (a27, b27) , (a28, b28) , (a29, b29) , (a30, b30) , (a31, b31) , (a32, b32) , (a33, b33) , (a34, b34) , (a35, b35) , (a36, b36) , (a37, b37) , (a38, b38) , (a39, b39) , (a40, b40) , (a41, b41) , (a42, b42) , (a43, b43) , (a44, b44) , (a45, b45) , (a46, b46) , (a47, b47) , (a48, b48) , (a49, b49) , (a50, b50) , (a51, b51) , (a52, b52) + + +/* Remove the parenthesis if there are present. + (internal macro) */ +#define M_REMOVE_PARENTHESIS(...) \ + M_IF(M_PARENTHESIS_P(__VA_ARGS__))(M_REMOVE_PARENTHESIS_2, M_REMOVE_PARENTHESIS_3)(__VA_ARGS__) +#define M_REMOVE_PARENTHESIS_2(...) M_REMOVE_PARENTHESIS_3 __VA_ARGS__ +#define M_REMOVE_PARENTHESIS_3(...) __VA_ARGS__ + + +/* Return the input (delay evaluation). + WARNING: Cannot be used by internal macros as it may appear in such cases + in recursive context, preventing expansion. Only top level macros can use it. + */ +#define M_ID(...) __VA_ARGS__ + + +/* Globber the input */ +#define M_EAT(...) + + +/* M_NARGS: Return number of argument. + (don't work for empty arg) */ +#define M_NARGS(...) \ + M_RET_ARG76(__VA_ARGS__, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, \ + 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, \ + 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, \ + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, \ + 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, \ + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, useless) + +/* If the number of arguments is 1 */ +#define M_IF_NARGS_EQ1(...) M_IF(M_EQUAL(M_NARGS(__VA_ARGS__), 1)) + +/* If the number of arguments is 2 */ +#define M_IF_NARGS_EQ2(...) M_IF(M_EQUAL(M_NARGS(__VA_ARGS__), 2)) + + +/* If NDEBUG macro is defined + M_IF_DEBUG(code if NDEBUG is not defined) + Note: not 100% robust */ +#define M_TEST_NDEBUG_P() M_C3(M_, NDEBUG, _TEST) +#define M_NDEBUG_TEST 0 +#define M_IF_DEBUG(a) M_IF(M_TEST_NDEBUG_P())(,a) + + +/* If the Function Object is included, expands the code, + otherwise do nothing. + M_FUNC0BJ_IS_NOT_DEFINED is defined to 0. + NOTE: M_IF is the variable is not defined assummes yes. +*/ +#define M_IF_FUNCOBJ(a) M_IF(M_FUNC0BJ_IS_NOT_DEFINED)( ,a) + + +/* Helper macro to redefine a function with a default value: + + Give the number of expected arguments, the value list of the + default argument, and the arguments. + It will complete the arguments with the value of the default + argument to complete up to 'expected' arguments. + USAGE: + M_DEFAULT_ARGS(expected_num_of_args, (list_of_default_values), given_arguments...) + Example: + extern int f(int x, int y = 0, int z = 1, const char *p = NULL); + ==> + #define f(...) f(M_DEFAULT_ARGS(4, (0, 1, NULL), __VA_ARGS__)) +*/ +#define M_DEFAULTI_ARGS2(numArgs, numExpected, value, ...) \ + __VA_ARGS__ \ + M_IF(M_NOTEQUAL(numArgs, numExpected))(M_DEFERRED_COMMA, ) \ + M_REVERSE(M_KEEP_ARGS(M_SUB(numExpected, numArgs), M_REVERSE value, M_LIB_ERROR_MISSING_MANDATORY_ARGUMENT)) +#define M_DEFAULTI_ARGS_EVAL(...) __VA_ARGS__ +#define M_DEFAULT_ARGS(expected, value, ...) \ + M_DEFAULTI_ARGS_EVAL(M_DEFAULTI_ARGS2(M_NARGS(__VA_ARGS__), expected, value, __VA_ARGS__)) + + + + +/***************************************************************/ +/******************** Compile Times Macro **********************/ +/***************************************************************/ + +/* Return the string representation of the evaluated expression. */ +#define M_AS_STR(x) M_AS_STRI(x) +#define M_AS_STRI(x) #x + +/* Convert an oplist into a string. It may generate a very long string, + which generates warning (string length greater than 4095). + So the conversion is optional */ +#ifdef M_USE_PRINT_OPLIST +# define M_OPL_AS_STR(x) M_AS_STR(x) +#else +# define M_OPL_AS_STR(x) +#endif + +/* C11 MACROS */ + +/* Maximum number of characters of an internal identifier (field name) + including final null char. Bigger than + 63 significant initial characters of C11 standard (§5.2.4.1) + Can be overloaded by user if needed. + NOTE: Used by variant & serial JSON to translate a field name into + a structure offset. +*/ +#ifndef M_USE_IDENTIFIER_ALLOC +#define M_USE_IDENTIFIER_ALLOC 128 /* (including of final null char) */ +#endif + +/* Return the string format of a variable */ +#define M_PRINTF_FORMAT(x) \ + _Generic(((void)0,(x)), \ + char: "%c", \ + bool: "%d", \ + signed char: "%hhd", \ + unsigned char: "%hhu", \ + signed short: "%hd", \ + unsigned short: "%hu", \ + signed int: "%d", \ + unsigned int: "%u", \ + long int: "%ld", \ + unsigned long int: "%lu", \ + long long int: "%lld", \ + unsigned long long int: "%llu", \ + float: "%f", \ + double: "%f", \ + long double: "%Lf", \ + const char *: "%s", \ + char *: "%s", \ + const void *: "%p", \ + void *: "%p" \ + M_PRINTF_FORMAT_EXTEND() ) + +#define M_PRINTF_FORMAT_EXTEND() /* Nothing in m-core (see m-string.h) */ + +/* IF FILE is supported */ +#if M_USE_STDIO + +/* Define internal wrappers around Annex K functions or classic functions for: + * - fopen + * - fscanf + * - strncpy + * - strncat + * + * There is no real usage outside of MSVC of Annex K, + * so the real standard compliant Annex K is not taken into account + * by this specific wrapper. + * + * If Microsoft Visual Studio C Library + * and the user wants to use the Annex K. + * ==> Use Annex K like functions to avoid warnings. + * + * Only these functions produce warning, + * so we keep the wrapper as simple as possible by including only then. + */ +#if defined(_MSC_VER) && defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ + +/* Wrapper around fopen_s */ +M_INLINE FILE * +m_core_fopen(const char filename[], const char opt[]) +{ + FILE *f; + int err = fopen_s(&f, filename, opt); + if (err) return NULL; + return f; +} + +/* Wrapper around strncpy */ +#define m_core_strncpy(s1, alloc, s2, size) strncpy_s(s1, alloc, s2, size) + +/* Wrapper around strncat */ +#define m_core_strncat(s1, alloc, s2, size) strncat_s(s1, alloc, s2, size) + +/* Wrapper around fscanf_s */ +#define m_core_fscanf(...) fscanf_s(__VA_ARGS__) +/* Macro to be used in m_core_fscanf for argument associated + * to the format %c, %s or %[ + * in order to specify the size of the argument */ +#define m_core_arg_size(arg, size) arg, (unsigned) size + +#else /* Use classic C functions */ + +/* Wrapper around fopen */ +#define m_core_fopen(...) fopen(__VA_ARGS__) +/* Wrapper around strncpy */ +#define m_core_strncpy(s1, alloc, s2, size) strncpy(s1, s2, size) +/* Wrapper around strncat */ +#define m_core_strncat(s1, alloc, s2, size) strncat(s1, s2, size) +/* Wrapper around fscanf */ +#define m_core_fscanf(...) fscanf(__VA_ARGS__) + +/* Macro to be used in m_core_fscanf for argument associated + * to the format %c, %s or %[ + * in order to specify the size of the argument */ +#define m_core_arg_size(arg, size) arg + +#endif + +/* Can be overloaded by m-string to support string output too */ +#define M_CORE_PRINTF_ARG(x) x + +/* Print a C variable if it is a standard type to the given file 'f'. + If a variable is extended (i.e. like (x, type) ) it will use the + method associated to the OUT_STR operator. +*/ +#define M_FPRINT_ARG(f, x) \ + M_IF(M_PARENTHESIS_P(x)) \ + ( M_FPRINT_ARG_OUT_STR(f, M_PAIR_2 x, M_PAIR_1 x), \ + fprintf(f, M_PRINTF_FORMAT(x), M_CORE_PRINTF_ARG(x) ) ) + +/* Internal macro to call the OUT_STR method */ +#define M_FPRINT_ARG_OUT_STR(file, oplist, var) \ + M_CALL_OUT_STR( M_GLOBAL_OPLIST(oplist) , file, var) + +/* Get a C variable if it is a standard type from the given file 'f'.*/ +#define M_FSCAN_ARG(xptr, f) \ + _Generic(((void)0,*(xptr)), \ + bool: m_core_fscan_bool(M_AS_TYPE(bool*,xptr), f), \ + char: m_core_fscan_char(M_AS_TYPE(char*,xptr), f), \ + signed char: m_core_fscan_schar(M_AS_TYPE(signed char*,xptr),f), \ + unsigned char: m_core_fscan_uchar(M_AS_TYPE(unsigned char*,xptr),f), \ + signed short: m_core_fscan_sshort(M_AS_TYPE(signed short*,xptr),f), \ + unsigned short: m_core_fscan_ushort(M_AS_TYPE(unsigned short*,xptr), f), \ + signed int: m_core_fscan_sint(M_AS_TYPE(signed int*,xptr), f), \ + unsigned int: m_core_fscan_uint(M_AS_TYPE(unsigned int*,xptr), f), \ + long int: m_core_fscan_slong(M_AS_TYPE(long*,xptr), f), \ + unsigned long int: m_core_fscan_ulong(M_AS_TYPE(unsigned long*,xptr), f), \ + long long int: m_core_fscan_sllong(M_AS_TYPE(long long*,xptr), f), \ + unsigned long long int: m_core_fscan_ullong(M_AS_TYPE(unsigned long long*,xptr),f), \ + float: m_core_fscan_float(M_AS_TYPE(float*,xptr), f), \ + double: m_core_fscan_double(M_AS_TYPE(double*,xptr), f), \ + long double: m_core_fscan_ldouble(M_AS_TYPE(long double*,xptr),f), \ + const char *: false /* unsupported */, \ + char *: false /* unsupported */, \ + const void *: false /* unsupported */, \ + void *: false /* unsupported */) + +/* Internal wrappers used by M_FSCAN_ARG : */ +M_INLINE bool +m_core_fscan_bool (bool *ptr, FILE *f) +{ + int c = fgetc(f); + *ptr = (c == '1'); + return (c == '0' || c == '1'); +} + +M_INLINE bool +m_core_fscan_char (char *ptr, FILE *f) +{ + int c = fgetc(f); + *ptr = (char) c; + return c != EOF; +} + +#define M_FSCAN_DEFAULT_TYPE_DEF(name, type, format) \ + M_INLINE bool \ + name (type *ptr, FILE *f) \ + { \ + return m_core_fscanf(f, format, ptr) == 1; \ + } +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_schar, signed char, "%hhd") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_uchar, unsigned char, "%hhu") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_sshort, signed short, "%hd") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_ushort, unsigned short, "%hu") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_sint, signed int, "%d") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_uint, unsigned int, "%u") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_slong, signed long, "%ld") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_ulong, unsigned long, "%lu") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_sllong, signed long long, "%lld") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_ullong, unsigned long long, "%llu") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_float, float, "%f") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_double, double, "%lf") +M_FSCAN_DEFAULT_TYPE_DEF(m_core_fscan_ldouble, long double, "%Lf") + +/* Return the next character (like fgetc) which is not a space character + and advance the FILE stream accordingly */ +M_INLINE int m_core_fgetc_nospace(FILE *f) +{ + int c; + do { + c = fgetc(f); + } while (c != EOF && isspace(c)); + return c; +} + +#endif /* End of stdio definitions */ + +/* Return the next character (like fgetc) which is not a space character + and advance the string stream accordingly */ +M_INLINE char m_core_str_nospace(const char **str) +{ + char c; + do { + c = *((*str)++); + } while (isspace((unsigned char) c)); + return c; +} + +/* Transform a C variable into a m_string_t (needs m-string.h) */ +#define M_GET_STRING_ARG(str, x, append) \ + (append ? m_string_cat_printf : m_string_printf) (str, M_PRINTF_FORMAT(x), M_CORE_PRINTF_ARG(x)) + +/* No use of GET_STR if no inclusion of m-string */ +#define M_GET_STR_METHOD_FOR_DEFAULT_TYPE /* */ + +/* Parse string of default type */ +#define M_PARSE_DEFAULT_TYPE(x, str, endptr) \ + _Generic(((void)0,*(x)), \ + char: m_core_parse_char(M_AS_TYPE(char*,x),str,endptr), \ + bool: m_core_parse_bool(M_AS_TYPE(bool*,x),str,endptr), \ + signed char: m_core_parse_schar(M_AS_TYPE(signed char*,x),str,endptr), \ + unsigned char: m_core_parse_uchar(M_AS_TYPE(unsigned char*,x),str,endptr), \ + signed short: m_core_parse_sshort(M_AS_TYPE(signed short*,x),str,endptr), \ + unsigned short: m_core_parse_ushort(M_AS_TYPE(unsigned short*,x),str,endptr), \ + signed int: m_core_parse_sint(M_AS_TYPE(signed int*,x),str,endptr), \ + unsigned int: m_core_parse_uint(M_AS_TYPE(unsigned int*,x),str,endptr), \ + signed long: m_core_parse_slong(M_AS_TYPE(signed long *,x),str,endptr), \ + unsigned long: m_core_parse_ulong(M_AS_TYPE(unsigned long*,x),str,endptr), \ + signed long long: m_core_parse_sllong(M_AS_TYPE(signed long long*,x),str,endptr), \ + unsigned long long: m_core_parse_ullong(M_AS_TYPE(unsigned long long*,x),str,endptr), \ + float: m_core_parse_float(M_AS_TYPE(float*,x),str,endptr), \ + double: m_core_parse_double(M_AS_TYPE(double*,x),str,endptr), \ + long double: m_core_parse_ldouble(M_AS_TYPE(long double*,x),str,endptr), \ + const char *: false /* not supported */, \ + char *: false /* not supported */, \ + const void *: false /* not supported */, \ + void *: false /* not supported */) + +/* Internal wrappers used by M_PARSE_DEFAULT_TYPE : */ +M_INLINE bool +m_core_parse_char (char *ptr, const char str[], const char **endptr) +{ + *ptr = *str++; + if (endptr != NULL) { *endptr = str; } + return true; +} + +M_INLINE bool +m_core_parse_bool (bool *ptr, const char str[], const char **endptr) +{ + char c = *str++; + *ptr = (c == '1'); + if (endptr != NULL) { *endptr = str; } + return (c == '0') || (c == '1'); +} + +#define M_PARSE_DEFAULT_TYPE_DEF(name, type, parse_func, extra_arg) \ + M_INLINE bool \ + name (type *ptr, const char str[], const char **endptr) \ + { \ + char *end; \ + *ptr = (type) parse_func (str, &end extra_arg); \ + if (endptr != NULL) { *endptr = end; } \ + return end != str; \ + } + +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_schar, signed char, strtol, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_uchar, unsigned char, strtoul, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_sshort, signed short, strtol, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ushort, unsigned short, strtoul, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_sint, signed int, strtol, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_uint, unsigned int, strtoul, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_slong, signed long, strtol, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ulong, unsigned long, strtoul, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_sllong, signed long long, strtoll, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ullong, unsigned long long, strtoull, M_DEFERRED_COMMA 10) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_float, float, strtof, ) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_double, double, strtod, ) +M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ldouble, long double, strtold, ) + +/* Internal macro to separate two arguments by a semicolon */ +#define M_SEPARATE_PER_SEMICOLON(a,b) a ; b + +/* Generic PRINT macro: print all its inputs regardless of the type + provided it is a generic "non-struct" type. */ +#define M_PRINT(...) do { M_REDUCE2(M_FPRINT_ARG, M_SEPARATE_PER_SEMICOLON, stdout, __VA_ARGS__); } while (0) + +/* Generic FPRINT macro: print all its inputs regardless of the type + provided it is a generic "non-struct" type into the file 'f'. */ +#define M_FPRINT(f,...) do { M_REDUCE2(M_FPRINT_ARG, M_SEPARATE_PER_SEMICOLON, f, __VA_ARGS__); } while (0) + +/* Within a C11 _Generic statement, all expressions shall be valid C + expression even if the case if always false, and is not executed. + This can seriously limit the _Generic statement. + This macro overcomes this limitation by returning : + * either the input 'x' if it is of type 'type', + * or the value 0 view as a type 'type'. + NOTE: Not compatible with C++. +*/ +#define M_AS_TYPE(type, x) _Generic(((void)0,(x)), type: (x), default: (type) {0}) + +/* Perform a C conditional operator with the following restriction: + * - cond shall be a compile time constant. + * However, true_expr and false_expr can be objects of different types. + * The type of the returned expression will be the same as the + * returned object without any promotion. + * NOTE: The classic conditional operator can return different types + * if and only both objects are pointers. If the selected pointer is + * a null pointer constant, the returned type depends if the **other** + * expression is itself a null pointer constant or not. + * NOTE: Not compatible with C++. + */ +#define M_CONDITIONAL(cond, true_expr, false_expr) \ + _Generic(1 ? (float *) 0 : (void *)(intptr_t) (cond), float *: false_expr, void *: true_expr) + +/* Return the minimum between x and y (computed in compile time) */ +#define M_MIN(x, y) ((x) < (y) ? (x) : (y)) + +/* Return the maximum between x and y (computed in compile time) */ +#define M_MAX(x, y) ((x) > (y) ? (x) : (y)) + +/* Is the number a power of 2? (computed in compile time) */ +#define M_POWEROF2_P(n) (!((n)&((n)-1))) + +/* Swap two objects x & y of the same type */ +#define M_SWAP(type, x, y) do { \ + type _tmp = (x); \ + (x) = (y); \ + (y) = _tmp; \ + } while (0) + + +/* Check if 'n' is assignable to an object of type 'type'. + It is as if we create a temporary of type 'type' and assign 'n' to it. + Return the object 'n' if it is possible. + Two definitions: one with compound-literals for C, the other with static_cast for C++. + NOTE: C definition is safer than the C++ one. +*/ +#ifndef __cplusplus +/* Cast the object n into type only if it is C assignable */ +# define M_ASSIGN_CAST(type, n) ((type) { 0 } = (n)) +#else +/* Cast the object n into type using a static_cast */ +# define M_ASSIGN_CAST(type, n) static_cast(n) +#endif + + +/* Cast 'n' of type 'type*' into 'type const*'. + This is like (type const*)p but safer as the type of 'n' is checked, + and more robust for double arrays type. + NOTE: Compliant with the C standard as in §6.2.5 Types: + "Similarly, pointers to qualified or unqualified versions + of compatible types shall have the same representation and + alignment requirements." +*/ +#ifndef __cplusplus +# define M_CONST_CAST(type, n) \ + (((union { type *ptr; type const *cptr; }){n}).cptr) +#else +# define M_CONST_CAST(type, n) const_cast(n) +#endif + + +/* + * From a pointer to the 'field' field of type 'field_type' of a 'type' structure, + * return the pointer to the structure. + * Used to handle intrusive structure. + * NOTE: Use an ASSIGN_CAST to make the cast a little bit safer. + */ +#define M_TYPE_FROM_FIELD(type, ptr, field_type, field) \ + ((type *)(void*)( (char *)M_ASSIGN_CAST(field_type*, (ptr)) - offsetof(type, field) )) + +#define M_CTYPE_FROM_FIELD(type, ptr, field_type, field) \ + ((type const *)(const void*)( (const char *)M_ASSIGN_CAST(field_type const *, (ptr)) - offsetof(type, field) )) + + +/* Use to generate a dummy alignment field for cache alignment within a structure + Take the name of the dummy field, and a list of the type of the fields that previously fill in the structure. + */ +#define M_ADD_SIZE(a,b) a + b +#define M_CACHELINE_ALIGN(name, ...) \ + char name[M_ALIGN_FOR_CACHELINE_EXCLUSION > M_REDUCE(sizeof, M_ADD_SIZE, __VA_ARGS__) \ + ? M_ALIGN_FOR_CACHELINE_EXCLUSION - M_REDUCE(sizeof, M_ADD_SIZE, __VA_ARGS__) : 1] + + +/* C++ doesn't support flexible array within a structure. + Let's define at least one element for an array. */ +#ifdef __cplusplus +# define M_MIN_FLEX_ARRAY_SIZE 1 +#else +# define M_MIN_FLEX_ARRAY_SIZE +#endif + +#if M_USE_STDARG && M_USE_STDIO + +/* Define the allocation of the temporary string used by M_CSTR + Default is 256 bytes (so 255 characters excluding the final null char). + It can be overriden by users if needed. +*/ +#ifndef M_USE_CSTR_ALLOC +#define M_USE_CSTR_ALLOC 256 +#endif + +/* Define M_CSTR for C++ & C */ +#if defined(__cplusplus) +namespace m_lib { + template + struct m_char_array { + char str[N]; + inline m_char_array(const char *format, ...) + { + va_list args; + va_start (args, format); + int ret = vsnprintf(str, N, format, args); + (void) ret; + va_end (args); + } + inline const char *m_get(void) + { + return str; + } + }; +} + +/* Return a constant string constructed based on the printf-liked formated string + and its arguments. + The string is constructed at run time and uses a temporary space on the stack. + If the constructed string is longer than M_USE_CSTR_ALLOC (default 256), + the string is truncated. + Example: + strlen( M_CSTR("Len=%d", 17) ) == 6 + NOTE: C++ definition using template as C++ doesn't support compound litteral. +*/ +#define M_CSTR(...) \ + (m_lib::m_char_array(__VA_ARGS__)).m_get() + +#elif defined(__GNUC__) && !defined(M_ADDRESS_SANITIZER) && __GNUC__ < 12 + +/* Return a constant string constructed based on the printf-liked formated string + and its arguments. + The string is constructed at run time and uses a temporary space on the stack. + If the constructed string is longer than M_USE_CSTR_ALLOC (default 256), + the string is truncated. + Example: + strlen( M_CSTR("Len=%d", 17) ) == 6 + NOTE: C definition using GNU C extension statement expression to produce + smaller & faster code and enables the compiler to produce better warnings. + In C, the return value is the array, not the pointer to the array + (if I understand https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Statement-Exprs.html + correctly, it is specified that only G++ converts the final result value + if it is an array to a pointer), so the return value is given back + to the parent scope. + However, address sanitizer doesn't see this particular corner case, + and raise an error. + So let's disable this implementation if address sanitizer is enabled. + Encapsulating the array in a struct will generate an additionnal memcpy + to recopy the buffer from the stack to the stack (!) +*/ +#define M_CSTR(...) \ + M_ATTR_EXTENSION ({char m_core_tmp[M_USE_CSTR_ALLOC]; \ + int m_core_r = snprintf(m_core_tmp, M_USE_CSTR_ALLOC, __VA_ARGS__); \ + (void) m_core_r; m_core_tmp; }) + +#else + +// Encapsulate snprintf to return the input buffer as argument (needed for M_CSTR) +M_INLINE const char * +m_core_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + va_start (args, format); + int ret = vsnprintf(str, size, format, args); + (void) ret; + va_end (args); + return str; +} + +/* Return a constant string constructed based on the printf-liked formated string + and its arguments. + The string is constructed at run time and uses a temporary space on the stack. + If the constructed string is longer than M_USE_CSTR_ALLOC (default 256), + the string is truncated. + Example: + strlen( M_CSTR("Len=%d", 17) ) == 6 + NOTE: C definition using compound litteral (init to 0). + This has a bad performance impact since it clears the memory before using it. +*/ +#define M_CSTR(...) \ + m_core_snprintf( (char [M_USE_CSTR_ALLOC]){0}, M_USE_CSTR_ALLOC, __VA_ARGS__) + +#endif + +#endif // Have stdarg + + +/************************************************************/ +/********************* HASH selection ***********************/ +/************************************************************/ + +/* User code shall overwrite this macro by a random seed (of type size_t) + so that the hash are not easily predictible by an attacker. + See https://events.ccc.de/congress/2011/Fahrplan/attachments/2007_28C3_Effective_DoS_on_web_application_platforms.pdf +*/ +#ifndef M_USE_HASH_SEED +# define M_USE_HASH_SEED 0UL +#endif + +#if defined(M_USE_DJB_HASH) +#define M_HASH_INIT 5381UL +#define M_HASH_CALC(h1,h2) (((h1) * 33UL) + (h2)) +#elif defined(M_USE_DJB_XOR_HASH) +#define M_HASH_INIT 5381UL +#define M_HASH_CALC(h1,h2) (((h1) * 33UL) ^ (h2)) +#elif defined(M_USE_JSHASH) +#define M_HASH_INIT 1315423911UL +#define M_HASH_CALC(h1,h2) ((h1) ^ (((h1) << 5) + (h2) + ((h1) >> 2))) +#elif defined(M_USE_BKDRHASH) +#define M_HASH_INIT 0UL +#define M_HASH_CALC(h1,h2) (((h1) * 131) + (h2)) +#elif defined(M_USE_SDBMHASH) +#define M_HASH_INIT 0UL +#define M_HASH_CALC(h1,h2) ((h2) + ((h1) << 6) + ((h1) << 16) - (h1)) +#elif defined(M_USE_DEKHASH) +#define M_HASH_INIT 0UL /* should be len but not possible with interface */ +#define M_HASH_CALC(h1,h2) ( (((h1)<<5) | (h1 >> (CHAR_BIT*sizeof(size_t)-5))) ^(h2)) +#elif defined(M_USE_BPHASH) +#define M_HASH_INIT 0UL +#define M_HASH_CALC(h1,h2) (((h1) << 7) ^ (h2)) +#else +//FNVHASH +#define M_HASH_INIT 0UL +#define M_HASH_CALC(h1,h2) (((h1) * 0x811C9DC5UL) ^ (h2)) +#endif + +#define M_HASH_DECL(hash) size_t hash = M_HASH_INIT ^ M_USE_HASH_SEED +#define M_HASH_UP(hash,h) do { hash = (size_t) M_HASH_CALC(hash, (h)); } while (0) +#define M_HASH_FINAL(hash) ( (hash) >> (sizeof(size_t)*CHAR_BIT/2) ^ (hash) ) + +/* Safe, efficient, and portable Rotate: + It should be recognized by any compiler and generate a single roll instruction. + If support for n==0 is needed, we can write: + return (x<>(-n&31)); + but compilers are less likely to generate a roll instruction. + */ +M_INLINE uint32_t m_core_rotl32a (uint32_t x, uint32_t n) +{ + M_ASSERT (n > 0 && n<32); + return (x<>(32-n)); +} +M_INLINE uint64_t m_core_rotl64a (uint64_t x, uint32_t n) +{ + M_ASSERT (n > 0 && n<64); + return (x<>(64-n)); +} + +/* Round to the next highest power of 2. + See https://web.archive.org/web/20160703165415/https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +*/ +M_INLINE uint64_t m_core_roundpow2(uint64_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +/* Return the count leading zero of the argument */ +#if defined(__GNUC__) && (__GNUC__*100 + __GNUC_MINOR__) >= 304 +M_INLINE unsigned int m_core_clz32(uint32_t limb) +{ + return (unsigned int) (M_UNLIKELY (limb == 0) ? sizeof(uint32_t)*CHAR_BIT : (size_t) __builtin_clzl(limb) - (sizeof(unsigned long) - sizeof(uint32_t)) * CHAR_BIT); +} +M_INLINE unsigned int m_core_clz64(uint64_t limb) +{ + return (unsigned int) (M_UNLIKELY (limb == 0ULL) ? sizeof (uint64_t)*CHAR_BIT : (size_t) __builtin_clzll(limb) - (sizeof (unsigned long long) - sizeof (uint64_t)) * CHAR_BIT); +} + +M_INLINE unsigned int m_core_ctz32(uint32_t limb) +{ + return (unsigned int) (M_UNLIKELY (limb == 0) ? sizeof(uint32_t)*CHAR_BIT : (size_t) __builtin_ctzl(limb)); +} +M_INLINE unsigned int m_core_ctz64(uint64_t limb) +{ + return (unsigned int) (M_UNLIKELY (limb == 0ULL) ? sizeof (uint64_t)*CHAR_BIT : (size_t) __builtin_ctzll(limb)); +} + +#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_ARM64)) +// NOTE: _BitScanReverse64 is 64-bits only (not compatible 32 bits). +#include +M_INLINE unsigned int m_core_clz32(uint32_t limb) +{ + unsigned long bit = 0; + if (_BitScanReverse( &bit, limb ) != 0) { + return 31 - bit; + } else { + return 32; + } +} +M_INLINE unsigned int m_core_clz64(uint64_t limb) +{ + unsigned long bit = 0; + if (_BitScanReverse64( &bit, limb ) != 0) { + return 63 - bit; + } else { + return 64; + } +} + +M_INLINE unsigned int m_core_ctz32(uint32_t limb) +{ + unsigned long bit = 0; + if (_BitScanForward( &bit, limb ) != 0) { + return bit; + } else { + return 32; + } +} +M_INLINE unsigned int m_core_ctz64(uint64_t limb) +{ + unsigned long bit = 0; + if (_BitScanForward64( &bit, limb ) != 0) { + return bit; + } else { + return 64; + } +} + +#else +// Emulation layer +#define M_CORE_CLZ_TAB "\010\07\06\06\05\05\05\05\04\04\04\04\04\04\04\04\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" + +M_INLINE unsigned int m_core_clz32(uint32_t limb) +{ + unsigned int shift = 0; + /* This code should be branchless on most targets */ + /* Compute shift so that, it is + + -3 if the highest 24 bits are clear, + + -2 for the highest 16 bits, + + -1 for the highest 8 bits, + + 0 otherwise */ + shift = -(unsigned) (limb < 0x1000000UL); + shift -= (limb < 0x10000UL); + shift -= (limb < 0x100UL); + shift = shift*8 + 24; + shift = 24 - shift + (unsigned int) M_CORE_CLZ_TAB[limb >> shift ]; + return shift; +} + +M_INLINE unsigned int m_core_clz64(uint64_t limb) +{ + unsigned int shift = 0; + /* This code should be branchless on most targets */ + /* Compute shift so that, it is + + -7 if the highest 56 bits are clear, + + -6 for the highest 48 bits, + + -5 for the highest 40 bits, + + -4 for the highest 32 bits, + + -3 for the highest 24 bits, + + -2 for the highest 16 bits, + + -1 for the highest 8 bits, + + 0 otherwise */ + shift = -(unsigned) (limb < 0x100000000000000UL); + shift -= (limb < 0x1000000000000UL); + shift -= (limb < 0x10000000000UL); + shift -= (limb < 0x100000000UL); + shift -= (limb < 0x1000000UL); + shift -= (limb < 0x10000UL); + shift -= (limb < 0x100UL); + shift = shift*8 + 56; + shift = 56 - shift + (unsigned int) M_CORE_CLZ_TAB[limb >> shift ]; + return shift; +} + +/* See algorithm ctz5 that uses de Bruijn sequences to construct a minimal perfect hash function (32 bits) */ +#define M_CORE_CTZ_TAB "\000\001\034\002\035\016\030\003\036\026\024\017\031\021\004\010\037\033\015\027\025\023\020\007\032\014\022\006\013\005\012\011" + +M_INLINE unsigned int m_core_ctz32(uint32_t limb) +{ + if (limb == 0) { return 32; } + return (unsigned int) M_CORE_CTZ_TAB[ ( (uint32_t) (limb & -limb) * 0x077CB531) >> 27]; +} + +M_INLINE unsigned int m_core_ctz64(uint64_t limb) +{ + unsigned int shift = 0; + if ((limb & 0x0FFFFFFFFull) == 0) { shift = 32; limb >>= 32; } + if (limb == 0) { return 32+shift; } + return shift + (unsigned int) M_CORE_CTZ_TAB[ ( (uint32_t) (limb & -limb) * 0x077CB531) >> 27]; +} + +#endif + +/* Implement a kind of FNV1A Hash. + Inspired by http://www.sanmayce.com/Fastest_Hash/ Jesteress and port to 64 bits. + See https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash + The buffer given as argument shall be aligned: + - to 8 (resp. 4) if size is greater than 8 on 64 bits (resp. 4 on 32 bits), + - to the power of 2 just lower or equal to its size otherwise. + NOTE: A lot of cast. Not really type nor alignement safe. + NOTE: Can be reduced to very few instructions if constant size argument. + FIXME: It is trivial for an attacker to generate collision and HASH_SEED doesn't prevent it. + */ +#if SIZE_MAX <= 4294967295U +/* 32 bits variant with an average measured avalanche effect of 16.056 bits */ +M_INLINE uint32_t +m_core_hash (const void *str, size_t length) +{ + const uint32_t prime = 709607U; + uint32_t hash32 = 2166136261U ^ M_USE_HASH_SEED; + const uint8_t *p = (const uint8_t *)str; + + M_ASSERT (str != NULL || length == 0); + M_ASSERT ( (( (uintptr_t)p & (sizeof(uint32_t)-1) ) == 0) || (length <= sizeof(uint32_t))); + M_ASSERT ( (( (uintptr_t)p & (sizeof(uint16_t)-1) ) == 0) || (length <= sizeof(uint16_t))); + + // Main loop that handles 64 bits at a time. + while (length >= 2*sizeof(uint32_t)) { + const uint32_t *ptr = (const uint32_t *) (uintptr_t) p; + hash32 = (hash32 ^ (m_core_rotl32a(ptr[0], 5) ^ ptr[1])) * prime; + length -= 2*sizeof(uint32_t); + p += 2*sizeof(uint32_t); + } + // Cases: 0,1,2,3,4,5,6,7 + if (length & sizeof(uint32_t)) { + const uint32_t *ptr = (const uint32_t *) (uintptr_t) p; + hash32 = (hash32 ^ ptr[0]) * prime; + p += sizeof(uint32_t); + } + if (length & sizeof(uint16_t)) { + const uint16_t *ptr = (const uint16_t *) (uintptr_t) p; + hash32 = (hash32 ^ ptr[0]) * prime; + p += sizeof(uint16_t); + } + if (length & 1) + hash32 = (hash32 ^ *p) * prime; + return hash32 ^ (hash32 >> 16); +} +#else +/* 64 bits variant with an average measured avalanche effect of 31.862 bits */ +M_INLINE uint64_t +m_core_hash (const void *str, size_t length) +{ + const uint64_t prime = 1099511628211ULL; + uint64_t hash64 = 14695981039346656037ULL ^ M_USE_HASH_SEED; + const uint8_t *p = M_ASSIGN_CAST(const uint8_t *, str); + + M_ASSERT (str != NULL || length == 0); + M_ASSERT ( (( (uintptr_t)p & (sizeof(uint64_t)-1) ) == 0) || (length <= sizeof(uint32_t))); + M_ASSERT ( (( (uintptr_t)p & (sizeof(uint32_t)-1) ) == 0) || (length <= sizeof(uint32_t))); + M_ASSERT ( (( (uintptr_t)p & (sizeof(uint16_t)-1) ) == 0) || (length <= sizeof(uint16_t))); + + // Main loop that handles 128 bits at a time. + while (length >= 2*sizeof(uint64_t)) { + const uint64_t *ptr = (const uint64_t *) (uintptr_t) p; + hash64 = (hash64 ^ (m_core_rotl64a(ptr[0], 5) ^ ptr[1])) * prime; + length -= 2*sizeof(uint64_t); + p += 2*sizeof(uint64_t); + } + //Cases: 0 to 15. + if (length & sizeof(uint64_t)) { + const uint64_t *ptr = (const uint64_t *) (uintptr_t) p; + hash64 = (hash64 ^ ptr[0]) * prime; + p += sizeof(uint64_t); + } + // Cases: 0,1,2,3,4,5,6,7 + if (length & sizeof(uint32_t)) { + const uint32_t *ptr = (const uint32_t *) (uintptr_t) p; + hash64 = (hash64 ^ ptr[0]) * prime; + p += sizeof(uint32_t); + } + if (length & sizeof(uint16_t)) { + const uint16_t *ptr = (const uint16_t *) (uintptr_t) p; + hash64 = (hash64 ^ ptr[0]) * prime; + p += sizeof(uint16_t); + } + if (length & 1) + hash64 = (hash64 ^ *p) * prime; + return hash64 ^ (hash64 >> 32); +} +#endif + +/* HASH function for a C-string (to be used within oplist) + * We cannot use m_core_hash due to the alignment constraint, + * and it avoids computing the size before computing the hash. + */ +M_INLINE size_t m_core_cstr_hash(const char str[]) +{ + M_HASH_DECL(hash); + while (*str) { + unsigned long u = (unsigned char) *str++; + M_HASH_UP(hash, u); + } + return M_HASH_FINAL(hash); +} + + +/* Define default HASH function. + Macro encapsulation for C11: use specialized version of the hash function + if the type is recognized. + NOTE: Default case is not safe if the type is defined with the '[1]' trick. */ +#define M_HASH_POD_DEFAULT(a) m_core_hash((const void*) &(a), sizeof (a)) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define M_HASH_INT32(a) ( (a) ^ ((a) << 11) ^ M_USE_HASH_SEED ) +#define M_HASH_INT64(a) ( ( (a) >> 33 ) ^ (a) ^ ((a) << 11) ^ M_USE_HASH_SEED ) +#define M_HASH_DEFAULT(a) \ + (size_t) _Generic((a)+0, \ + int32_t: M_HASH_INT32((uint32_t) M_AS_TYPE(int32_t, a)), \ + uint32_t: M_HASH_INT32(M_AS_TYPE(uint32_t, a)), \ + int64_t: M_HASH_INT64((uint64_t) M_AS_TYPE(int64_t, a)), \ + uint64_t: M_HASH_INT64(M_AS_TYPE(uint64_t, a)), \ + default: M_HASH_POD_DEFAULT(a) ) +#else +#define M_HASH_DEFAULT(a) M_HASH_POD_DEFAULT(a) +#endif + + + +/************************************************************/ +/******************** METHODS handling **********************/ +/************************************************************/ + + +/* Helper internal macros to make M_GET_METHOD works. + List of supported methods for an oplist */ +#define M_INIT_INIT(a) ,a, +#define M_INIT_SET_INIT_SET(a) ,a, +#define M_INIT_MOVE_INIT_MOVE(a) ,a, +#define M_INIT_WITH_INIT_WITH(a) ,a, +#define M_SWAP_SWAP(a) ,a, +#define M_SET_SET(a) ,a, +#define M_MOVE_MOVE(a) ,a, +#define M_CLEAR_CLEAR(a) ,a, +#define M_HASH_HASH(a) ,a, +#define M_EQUAL_EQUAL(a) ,a, +#define M_CMP_CMP(a) ,a, +#define M_TYPE_TYPE(a) ,a, +#define M_SUBTYPE_SUBTYPE(a) ,a, +#define M_GENTYPE_GENTYPE(a) ,a, +#define M_NAME_NAME(a) ,a, +#define M_OPLIST_OPLIST(a) ,a, +#define M_SORT_SORT(a) ,a, +#define M_SPLICE_BACK_SPLICE_BACK(a) ,a, +#define M_SPLICE_AT_SPLICE_AT(a) ,a, +#define M_IT_TYPE_IT_TYPE(a) ,a, +#define M_IT_FIRST_IT_FIRST(a) ,a, +#define M_IT_LAST_IT_LAST(a) ,a, +#define M_IT_END_IT_END(a) ,a, +#define M_IT_SET_IT_SET(a) ,a, +#define M_IT_END_P_IT_END_P(a) ,a, +#define M_IT_LAST_P_IT_LAST_P(a) ,a, +#define M_IT_EQUAL_P_IT_EQUAL_P(a) ,a, +#define M_IT_NEXT_IT_NEXT(a) ,a, +#define M_IT_PREVIOUS_IT_PREVIOUS(a) ,a, +#define M_IT_REF_IT_REF(a) ,a, +#define M_IT_CREF_IT_CREF(a) ,a, +#define M_IT_REMOVE_IT_REMOVE(a) ,a, +#define M_IT_INSERT_IT_INSERT(a) ,a, +#define M_EMPTY_P_EMPTY_P(a) ,a, +#define M_ADD_ADD(a) ,a, +#define M_SUB_SUB(a) ,a, +#define M_MUL_MUL(a) ,a, +#define M_DIV_DIV(a) ,a, +#define M_RESET_RESET(a) ,a, +#define M_KEY_TYPE_KEY_TYPE(a) ,a, +#define M_VALUE_TYPE_VALUE_TYPE(a) ,a, +#define M_KEY_OPLIST_KEY_OPLIST(a) ,a, +#define M_VALUE_OPLIST_VALUE_OPLIST(a) ,a, +#define M_GET_KEY_GET_KEY(a) ,a, +#define M_SET_KEY_SET_KEY(a) ,a, +#define M_SAFE_GET_KEY_SAFE_GET_KEY(a) ,a, +#define M_ERASE_KEY_ERASE_KEY(a) ,a, +#define M_GET_SIZE_GET_SIZE(a) ,a, +#define M_PUSH_PUSH(a) ,a, +#define M_POP_POP(a) ,a, +#define M_PUSH_MOVE_PUSH_MOVE(a) ,a, +#define M_POP_MOVE_POP_MOVE(a) ,a, +#define M_REVERSE_REVERSE(a) ,a, +#define M_GET_STR_GET_STR(a) ,a, +#define M_PARSE_STR_PARSE_STR(a) ,a, +#define M_OUT_STR_OUT_STR(a) ,a, +#define M_IN_STR_IN_STR(a) ,a, +#define M_OUT_SERIAL_OUT_SERIAL(a) ,a, +#define M_IN_SERIAL_IN_SERIAL(a) ,a, +#define M_SEPARATOR_SEPARATOR(a) ,a, +#define M_EXT_ALGO_EXT_ALGO(a) ,a, +#define M_INC_ALLOC_INC_ALLOC(a) ,a, +#define M_OOR_SET_OOR_SET(a) ,a, +#define M_OOR_EQUAL_OOR_EQUAL(a) ,a, +#define M_LIMITS_LIMITS(a) ,a, +#define M_PROPERTIES_PROPERTIES(a) ,a, +#define M_EMPLACE_TYPE_EMPLACE_TYPE(a) ,a, +// As attribute customization +#define M_NEW_NEW(a) ,a, +#define M_DEL_DEL(a) ,a, +#define M_REALLOC_REALLOC(a) ,a, +#define M_FREE_FREE(a) ,a, +#define M_MEMPOOL_MEMPOOL(a) ,a, +#define M_MEMPOOL_LINKAGE_MEMPOOL_LINKAGE(a) ,a, +#define M_SIZE_SIZE(a) ,a, +#define M_CONTEXT_CONTEXT(a) ,a, +#define M_POLICY_POLICY(a) ,a, +// As properties only +#define M_LET_AS_INIT_WITH_LET_AS_INIT_WITH(a) ,a, +#define M_NOCLEAR_NOCLEAR(a) ,a, + +/* From an oplist - an unorded list of methods : like "INIT(mpz_init),CLEAR(mpz_clear),SET(mpz_set)" - + Return the given method in the oplist or the default method. + Example: + M_GET_METHOD(INIT, my_default, INIT(mpz_init),CLEAR(mpz_clear),SET(mpz_set)) --> mpz_init + M_GET_METHOD(INIT, my_default, CLEAR(mpz_clear),SET(mpz_set)) --> my_default */ +#define M_GET_METHOD(method, method_default, ...) \ + M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_, method, _), __VA_ARGS__), method_default,) + +/* Get the given method */ +#define M_GET_INIT(...) M_GET_METHOD(INIT, M_INIT_DEFAULT, __VA_ARGS__) +#define M_GET_INIT_SET(...) M_GET_METHOD(INIT_SET, M_SET_DEFAULT, __VA_ARGS__) +#define M_GET_INIT_MOVE(...) M_GET_METHOD(INIT_MOVE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_INIT_WITH(...) M_GET_METHOD(INIT_WITH, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SET(...) M_GET_METHOD(SET, M_SET_DEFAULT, __VA_ARGS__) +#define M_GET_MOVE(...) M_GET_METHOD(MOVE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SWAP(...) M_GET_METHOD(SWAP, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_CLEAR(...) M_GET_METHOD(CLEAR, M_NOTHING_DEFAULT, __VA_ARGS__) +#define M_GET_HASH(...) M_GET_METHOD(HASH, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_EQUAL(...) M_GET_METHOD(EQUAL, M_EQUAL_DEFAULT, __VA_ARGS__) +#define M_GET_CMP(...) M_GET_METHOD(CMP, M_CMP_DEFAULT, __VA_ARGS__) +#define M_GET_TYPE(...) M_GET_METHOD(TYPE, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_SUBTYPE(...) M_GET_METHOD(SUBTYPE, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_GENTYPE(...) M_GET_METHOD(GENTYPE, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_NAME(...) M_GET_METHOD(NAME, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_OPLIST(...) M_GET_METHOD(OPLIST, (), __VA_ARGS__) +#define M_GET_SORT(...) M_GET_METHOD(SORT, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SPLICE_BACK(...) M_GET_METHOD(SPLICE_BACK, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SPLICE_AT(...) M_GET_METHOD(SPLICE_AT, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_TYPE(...) M_GET_METHOD(IT_TYPE, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_IT_FIRST(...) M_GET_METHOD(IT_FIRST, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_LAST(...) M_GET_METHOD(IT_LAST, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_END(...) M_GET_METHOD(IT_END, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_SET(...) M_GET_METHOD(IT_SET, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_END_P(...) M_GET_METHOD(IT_END_P, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_LAST_P(...) M_GET_METHOD(IT_LAST_P, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_EQUAL_P(...) M_GET_METHOD(IT_EQUAL_P, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_NEXT(...) M_GET_METHOD(IT_NEXT, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_PREVIOUS(...) M_GET_METHOD(IT_PREVIOUS, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_REF(...) M_GET_METHOD(IT_REF, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_CREF(...) M_GET_METHOD(IT_CREF, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_REMOVE(...) M_GET_METHOD(IT_REMOVE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_INSERT(...) M_GET_METHOD(IT_INSERT, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_EMPTY_P(...) M_GET_METHOD(EMPTY_P, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_ADD(...) M_GET_METHOD(ADD, M_ADD_DEFAULT, __VA_ARGS__) +#define M_GET_SUB(...) M_GET_METHOD(SUB, M_SUB_DEFAULT, __VA_ARGS__) +#define M_GET_MUL(...) M_GET_METHOD(MUL, M_MUL_DEFAULT, __VA_ARGS__) +#define M_GET_DIV(...) M_GET_METHOD(DIV, M_DIV_DEFAULT, __VA_ARGS__) +#define M_GET_RESET(...) M_GET_METHOD(RESET, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_KEY_TYPE(...) M_GET_METHOD(KEY_TYPE, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_VALUE_TYPE(...) M_GET_METHOD(VALUE_TYPE, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_KEY_OPLIST(...) M_GET_METHOD(KEY_OPLIST, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_VALUE_OPLIST(...) M_GET_METHOD(VALUE_OPLIST, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_GET_KEY(...) M_GET_METHOD(GET_KEY, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SET_KEY(...) M_GET_METHOD(SET_KEY, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SAFE_GET_KEY(...) M_GET_METHOD(SAFE_GET_KEY, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_ERASE_KEY(...) M_GET_METHOD(ERASE_KEY, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_GET_SIZE(...) M_GET_METHOD(GET_SIZE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_PUSH(...) M_GET_METHOD(PUSH, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_POP(...) M_GET_METHOD(POP, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_PUSH_MOVE(...) M_GET_METHOD(PUSH_MOVE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_POP_MOVE(...) M_GET_METHOD(POP_MOVE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_REVERSE(...) M_GET_METHOD(REVERSE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_GET_STR(...) M_GET_METHOD(GET_STR, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_PARSE_STR(...) M_GET_METHOD(PARSE_STR, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_OUT_STR(...) M_GET_METHOD(OUT_STR, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IN_STR(...) M_GET_METHOD(IN_STR, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_OUT_SERIAL(...) M_GET_METHOD(OUT_SERIAL, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IN_SERIAL(...) M_GET_METHOD(IN_SERIAL, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SEPARATOR(...) M_GET_METHOD(SEPARATOR, ',', __VA_ARGS__) +#define M_GET_EXT_ALGO(...) M_GET_METHOD(EXT_ALGO, M_NO_EXT_ALGO, __VA_ARGS__) +#define M_GET_INC_ALLOC(...) M_GET_METHOD(INC_ALLOC, M_INC_ALLOC_DEFAULT, __VA_ARGS__) +#define M_GET_OOR_SET(...) M_GET_METHOD(OOR_SET, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_OOR_EQUAL(...) M_GET_METHOD(OOR_EQUAL, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_LIMITS(...) M_GET_METHOD(LIMITS, M_LIMITS_DEFAULT, __VA_ARGS__) +#define M_GET_PROPERTIES(...) M_GET_METHOD(PROPERTIES, (), __VA_ARGS__) +#define M_GET_EMPLACE_TYPE(...) M_GET_METHOD(EMPLACE_TYPE, M_NO_DEFAULT, __VA_ARGS__) +// As sttribute customization +#define M_GET_NEW(...) M_GET_METHOD(NEW, M_NEW_DEFAULT, __VA_ARGS__) +#define M_GET_DEL(...) M_GET_METHOD(DEL, M_DEL_DEFAULT, __VA_ARGS__) +#define M_GET_REALLOC(...) M_GET_METHOD(REALLOC, M_REALLOC_DEFAULT, __VA_ARGS__) +#define M_GET_FREE(...) M_GET_METHOD(FREE, M_FREE_DEFAULT, __VA_ARGS__) +#define M_GET_MEMPOOL(...) M_GET_METHOD(MEMPOOL, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_MEMPOOL_LINKAGE(...) M_GET_METHOD(MEMPOOL_LINKAGE, , __VA_ARGS__) +#define M_GET_SIZE(...) M_GET_METHOD(SIZE, 0, __VA_ARGS__) +#define M_GET_CONTEXT(...) M_GET_METHOD(CONTEXT, 0, __VA_ARGS__) +#define M_GET_POLICY(...) M_GET_METHOD(POLICY, 0, __VA_ARGS__) + +// Calling method with support of defined transformation API +// operators that are not methods are commented +#define M_CALL_INIT(oplist, ...) M_APPLY_API(M_GET_INIT oplist, oplist, __VA_ARGS__) +#define M_CALL_INIT_SET(oplist, ...) M_APPLY_API(M_GET_INIT_SET oplist, oplist, __VA_ARGS__) +#define M_CALL_INIT_MOVE(oplist, ...) M_APPLY_API(M_GET_INIT_MOVE oplist, oplist, __VA_ARGS__) +#define M_CALL_INIT_WITH(oplist, ...) M_APPLY_API(M_GET_INIT_WITH oplist, oplist, __VA_ARGS__) +#define M_CALL_SET(oplist, ...) M_APPLY_API(M_GET_SET oplist, oplist, __VA_ARGS__) +#define M_CALL_MOVE(oplist, ...) M_APPLY_API(M_GET_MOVE oplist, oplist, __VA_ARGS__) +#define M_CALL_SWAP(oplist, ...) M_APPLY_API(M_GET_SWAP oplist, oplist, __VA_ARGS__) +#define M_CALL_CLEAR(oplist, ...) M_APPLY_API(M_GET_CLEAR oplist, oplist, __VA_ARGS__) +#define M_CALL_NEW(oplist, ...) M_APPLY_API(M_GET_NEW oplist, oplist, __VA_ARGS__) +#define M_CALL_HASH(oplist, ...) M_APPLY_API(M_GET_HASH oplist, oplist, __VA_ARGS__) +#define M_CALL_EQUAL(oplist, ...) M_APPLY_API(M_GET_EQUAL oplist, oplist, __VA_ARGS__) +#define M_CALL_CMP(oplist, ...) M_APPLY_API(M_GET_CMP oplist, oplist, __VA_ARGS__) +//#define M_CALL_TYPE(oplist, ...) M_APPLY_API(M_GET_TYPE oplist, oplist, __VA_ARGS__) +//#define M_CALL_SUBTYPE(oplist, ...) M_APPLY_API(M_GET_SUBTYPE oplist, oplist, __VA_ARGS__) +//#define M_CALL_GENTYPE(oplist, ...) M_APPLY_API(M_GET_GENTYPE oplist, oplist, __VA_ARGS__) +//#define M_CALL_NAME(oplist, ...) M_APPLY_API(M_GET_NAME oplist, oplist, __VA_ARGS__) +//#define M_CALL_OPLIST(oplist, ...) M_APPLY_API(M_GET_OPLIST oplist, oplist, __VA_ARGS__) +#define M_CALL_SORT(oplist, ...) M_APPLY_API(M_GET_SORT oplist, oplist, __VA_ARGS__) +#define M_CALL_SPLICE_BACK(oplist, ...) M_APPLY_API(M_GET_SPLICE_BACK oplist, oplist, __VA_ARGS__) +#define M_CALL_SPLICE_AT(oplist, ...) M_APPLY_API(M_GET_SPLICE_AT oplist, oplist, __VA_ARGS__) +//#define M_CALL_IT_TYPE(oplist, ...) M_APPLY_API(M_GET_IT_TYPE oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_FIRST(oplist, ...) M_APPLY_API(M_GET_IT_FIRST oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_LAST(oplist, ...) M_APPLY_API(M_GET_IT_LAST oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_END(oplist, ...) M_APPLY_API(M_GET_IT_END oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_SET(oplist, ...) M_APPLY_API(M_GET_IT_SET oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_END_P(oplist, ...) M_APPLY_API(M_GET_IT_END_P oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_LAST_P(oplist, ...) M_APPLY_API(M_GET_IT_LAST_P oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_EQUAL_P(oplist, ...) M_APPLY_API(M_GET_IT_EQUAL_P oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_NEXT(oplist, ...) M_APPLY_API(M_GET_IT_NEXT oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_PREVIOUS(oplist, ...) M_APPLY_API(M_GET_IT_PREVIOUS oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_REF(oplist, ...) M_APPLY_API(M_GET_IT_REF oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_CREF(oplist, ...) M_APPLY_API(M_GET_IT_CREF oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_REMOVE(oplist, ...) M_APPLY_API(M_GET_IT_REMOVE oplist, oplist, __VA_ARGS__) +#define M_CALL_IT_INSERT(oplist, ...) M_APPLY_API(M_GET_IT_INSERT oplist, oplist, __VA_ARGS__) +#define M_CALL_EMPTY_P(oplist, ...) M_APPLY_API(M_GET_EMPTY_P oplist, oplist, __VA_ARGS__) +#define M_CALL_ADD(oplist, ...) M_APPLY_API(M_GET_ADD oplist, oplist, __VA_ARGS__) +#define M_CALL_SUB(oplist, ...) M_APPLY_API(M_GET_SUB oplist, oplist, __VA_ARGS__) +#define M_CALL_MUL(oplist, ...) M_APPLY_API(M_GET_MUL oplist, oplist, __VA_ARGS__) +#define M_CALL_DIV(oplist, ...) M_APPLY_API(M_GET_DIV oplist, oplist, __VA_ARGS__) +#define M_CALL_RESET(oplist, ...) M_APPLY_API(M_GET_RESET oplist, oplist, __VA_ARGS__) +#define M_CALL_GET_KEY(oplist, ...) M_APPLY_API(M_GET_GET_KEY oplist, oplist, __VA_ARGS__) +#define M_CALL_SET_KEY(oplist, ...) M_APPLY_API(M_GET_SET_KEY oplist, oplist, __VA_ARGS__) +#define M_CALL_SAFE_GET_KEY(oplist, ...) M_APPLY_API(M_GET_SAFE_GET_KEY oplist, oplist, __VA_ARGS__) +#define M_CALL_ERASE_KEY(oplist, ...) M_APPLY_API(M_GET_ERASE_KEY oplist, oplist, __VA_ARGS__) +#define M_CALL_GET_SIZE(oplist, ...) M_APPLY_API(M_GET_GET_SIZE oplist, oplist, __VA_ARGS__) +#define M_CALL_PUSH(oplist, ...) M_APPLY_API(M_GET_PUSH oplist, oplist, __VA_ARGS__) +#define M_CALL_POP(oplist, ...) M_APPLY_API(M_GET_POP oplist, oplist, __VA_ARGS__) +#define M_CALL_PUSH_MOVE(oplist, ...) M_APPLY_API(M_GET_PUSH_MOVE oplist, oplist, __VA_ARGS__) +#define M_CALL_POP_MOVE(oplist, ...) M_APPLY_API(M_GET_POP_MOVE oplist, oplist, __VA_ARGS__) +#define M_CALL_REVERSE(oplist, ...) M_APPLY_API(M_GET_REVERSE oplist, oplist, __VA_ARGS__) +#define M_CALL_GET_STR(oplist, ...) M_APPLY_API(M_GET_GET_STR oplist, oplist, __VA_ARGS__) +#define M_CALL_PARSE_STR(oplist, ...) M_APPLY_API(M_GET_PARSE_STR oplist, oplist, __VA_ARGS__) +#define M_CALL_OUT_STR(oplist, ...) M_APPLY_API(M_GET_OUT_STR oplist, oplist, __VA_ARGS__) +#define M_CALL_IN_STR(oplist, ...) M_APPLY_API(M_GET_IN_STR oplist, oplist, __VA_ARGS__) +#define M_CALL_OUT_SERIAL(oplist, ...) M_APPLY_API(M_GET_OUT_SERIAL oplist, oplist, __VA_ARGS__) +#define M_CALL_IN_SERIAL(oplist, ...) M_APPLY_API(M_GET_IN_SERIAL oplist, oplist, __VA_ARGS__) +//#define M_CALL_SEPARATOR(oplist, ...) M_APPLY_API(M_GET_SEPARATOR oplist, oplist, __VA_ARGS__) +//#define M_CALL_EXT_ALGO(oplist, ...) M_APPLY_API(M_GET_EXT_ALGO oplist, oplist, __VA_ARGS__) +#define M_CALL_INC_ALLOC(oplist, ...) M_APPLY_API(M_GET_INC_ALLOC oplist, oplist, __VA_ARGS__) +#define M_CALL_OOR_SET(oplist, ...) M_APPLY_API(M_GET_OOR_SET oplist, oplist, __VA_ARGS__) +#define M_CALL_OOR_EQUAL(oplist, ...) M_APPLY_API(M_GET_OOR_EQUAL oplist, oplist, __VA_ARGS__) +//#define M_CALL_LIMITS(oplist, ...) M_APPLY_API(M_GET_LIMITS oplist, oplist, __VA_ARGS__) +//#define M_CALL_PROPERTIES(oplist, ...) M_APPLY_API(M_GET_PROPERTIES oplist, oplist, __VA_ARGS__) +//#define M_CALL_EMPLACE_TYPE(oplist, ...) M_APPLY_API(M_GET_EMPLACE_TYPE oplist, oplist, __VA_ARGS__) +// As attribute customization +#define M_CALL_DEL(oplist, ...) M_APPLY_API(M_GET_DEL oplist, oplist, __VA_ARGS__) +#define M_CALL_REALLOC(oplist, ...) M_APPLY_API(M_GET_REALLOC oplist, oplist, __VA_ARGS__) +#define M_CALL_FREE(oplist, ...) M_APPLY_API(M_GET_FREE oplist, oplist, __VA_ARGS__) +#define M_CALL_MEMPOOL(oplist, ...) M_APPLY_API(M_GET_MEMPOOL oplist, oplist, __VA_ARGS__) +#define M_CALL_MEMPOOL_LINKAGE(oplist, ...) M_APPLY_API(M_GET_MEMPOOL_LINKAGE oplist, oplist, __VA_ARGS__) +//#define M_CALL_SIZE(oplist, ...) M_APPLY_API(M_GET_SIZE oplist, oplist, __VA_ARGS__) +//#define M_CALL_CONTEXT(oplist, ...) M_APPLY_API(M_GET_CONTEXT oplist, oplist, __VA_ARGS__) +//#define M_CALL_POLICY(oplist, ...) M_APPLY_API(M_GET_POLICY oplist, oplist, __VA_ARGS__) + + +/* API transformation support: + transform the call to the method into the supported API by the method. + Example of usage in oplist: ( INIT_WITH(API_1(method)) ) + NOTE: It uses M_RET_ARG3 instead of M_RET_ARG2 since M_GET_METHOD + uses M_RET_ARG2: to be able to use M_GET_METHOD within a M_APPLY_API + we need another macro to avoid being marked. +*/ +#define M_APPLY_API(method, oplist, ...) \ + M_RET_ARG3( , M_C(M_OPLAPI_INDIRECT_, method)(M_C(M_OPLAPI_EXTRACT_,method),oplist,__VA_ARGS__), method(__VA_ARGS__),) + +/* Pre-defined API Transformation : + 0: Method has been disable. It shall not be called. Raised an error. + API_0: default, API_1: oplist given, + API_2: by addr for first argument, API_3: oplist given, + API_4 :by affectation for first argument, 5: API_by affectation + oplist + API_6 :by addr for two arguments, 5: API_by address for both + oplist + + NOTE: It starts by a comma, to ack the success of the transformation. +*/ +#define M_OPLAPI_ERROR(method, oplist, ...) , M_STATIC_ASSERT(false, M_LIB_DISABLED_METHOD, "The method has been explictly disabled for the requested operator, yet it has been called by the container") +#define M_OPLAPI_0(method, oplist, ...) , M_DELAY3(method)(__VA_ARGS__) +#define M_OPLAPI_1(method, oplist, ...) , M_DELAY3(method)(oplist, __VA_ARGS__) +#define M_OPLAPI_2(method, oplist, ...) , M_DELAY3(method)(& __VA_ARGS__) +#define M_OPLAPI_3(method, oplist, ...) , M_DELAY3(method)(oplist, &__VA_ARGS__) +#define M_OPLAPI_4(method, oplist, x, ...) , x = M_DELAY3(method)(__VA_ARGS__) +#define M_OPLAPI_5(method, oplist, x, ...) , x = M_DELAY3(method)(oplist, __VA_ARGS__) +#define M_OPLAPI_6(method, oplist, x, ...) , M_DELAY3(method)(&x, &__VA_ARGS__) +#define M_OPLAPI_7(method, oplist, x, ...) , M_DELAY3(method)(oplist, &x, &__VA_ARGS__) +/* API transformation support for indirection */ +#define M_OPLAPI_INDIRECT_0 M_OPLAPI_ERROR +#define M_OPLAPI_INDIRECT_API_0(...) M_OPLAPI_0 +#define M_OPLAPI_EXTRACT_API_0(...) __VA_ARGS__ +#define M_OPLAPI_INDIRECT_API_1(...) M_OPLAPI_1 +#define M_OPLAPI_EXTRACT_API_1(...) __VA_ARGS__ +#define M_OPLAPI_INDIRECT_API_2(...) M_OPLAPI_2 +#define M_OPLAPI_EXTRACT_API_2(...) __VA_ARGS__ +#define M_OPLAPI_INDIRECT_API_3(...) M_OPLAPI_3 +#define M_OPLAPI_EXTRACT_API_3(...) __VA_ARGS__ +#define M_OPLAPI_INDIRECT_API_4(...) M_OPLAPI_4 +#define M_OPLAPI_EXTRACT_API_4(...) __VA_ARGS__ +#define M_OPLAPI_INDIRECT_API_5(...) M_OPLAPI_5 +#define M_OPLAPI_EXTRACT_API_5(...) __VA_ARGS__ +#define M_OPLAPI_INDIRECT_API_6(...) M_OPLAPI_6 +#define M_OPLAPI_EXTRACT_API_6(...) __VA_ARGS__ +#define M_OPLAPI_INDIRECT_API_7(...) M_OPLAPI_7 +#define M_OPLAPI_EXTRACT_API_7(...) __VA_ARGS__ + + +/* Generic API transformation. + * The API itself is described in the operator mapping with the method name. + * + * Usage in oplist: + * operator ( API( method, returncode, args...) ) + * * method is the method name + * * returncode is the transformation to perform of the return code. It can be: + * - NONE: no transformation + * - VOID: cast to void + * - EQ(val)/NEQ(val)/LT(val)/GT(val)/LE(val)/GE(val): compare return code to val + * - ARG[1-9]: set the associated argument number of the operator to the return code + * * args are the list of the arguments of the function. It can be: + * - a constant + * - a variable name (probable global) + * - ARG[1-9] : the associated argument number of the operator + * - ARGPTR[1-9] : the pointer of the associated argument number of the operator + * - OPLIST: the oplist + */ + +/* Needed duplicate of M_RET_ARG2 as we call it within a M_RET_ARG2 + * FIXME: Can M_HEAD_2 be used instead? */ +#define M_RETI_ARG2_BIS(_1, _2, ...) _2 +#define M_RET_ARG2_BIS(...) M_RETI_ARG2_BIS(__VA_ARGS__,) + +/* Reorder the arglist 'arglist' according to the order 'orderlist' + * Inject also '&' if the address of the argument is requested in orderlist + * Inject also default value if requested in orderlist + * Inject also the oplist of requested in orderlist + */ +#define M_REORDER_ARGLIST(arglist, orderlist) \ + M_MAP2B_C( M_REORDER_ARGLIST_FUNC, arglist, M_REORDER_ARGLIST_ID orderlist) +#define M_REORDER_ARGLIST_ID(...) __VA_ARGS__ +#define M_REORDER_ARGLIST_FUNC(arglist, order) \ + M_RET_ARG2_BIS( M_C(M_REORDER_ARGLIST_FUNC_, order) (arglist), order, ) + +/* The first ARG starts from the second index as the first index is the oplist */ +#define M_REORDER_ARGLIST_FUNC_OPLIST(arglist) , M_RET_ARG1 arglist , + +#define M_REORDER_ARGLIST_FUNC_ARG1(arglist) , M_RET_ARG2 arglist , +#define M_REORDER_ARGLIST_FUNC_ARG2(arglist) , M_RET_ARG3 arglist , +#define M_REORDER_ARGLIST_FUNC_ARG3(arglist) , M_RET_ARG4 arglist , +#define M_REORDER_ARGLIST_FUNC_ARG4(arglist) , M_RET_ARG5 arglist , +#define M_REORDER_ARGLIST_FUNC_ARG5(arglist) , M_RET_ARG6 arglist , +#define M_REORDER_ARGLIST_FUNC_ARG6(arglist) , M_RET_ARG7 arglist , +#define M_REORDER_ARGLIST_FUNC_ARG7(arglist) , M_RET_ARG8 arglist , +#define M_REORDER_ARGLIST_FUNC_ARG8(arglist) , M_RET_ARG9 arglist , +#define M_REORDER_ARGLIST_FUNC_ARG9(arglist) , M_RET_ARG10 arglist , + +#define M_REORDER_ARGLIST_FUNC_ARGPTR1(arglist) , & M_RET_ARG2 arglist , +#define M_REORDER_ARGLIST_FUNC_ARGPTR2(arglist) , & M_RET_ARG3 arglist , +#define M_REORDER_ARGLIST_FUNC_ARGPTR3(arglist) , & M_RET_ARG4 arglist , +#define M_REORDER_ARGLIST_FUNC_ARGPTR4(arglist) , & M_RET_ARG5 arglist , +#define M_REORDER_ARGLIST_FUNC_ARGPTR5(arglist) , & M_RET_ARG6 arglist , +#define M_REORDER_ARGLIST_FUNC_ARGPTR6(arglist) , & M_RET_ARG7 arglist , +#define M_REORDER_ARGLIST_FUNC_ARGPTR7(arglist) , & M_RET_ARG8 arglist , +#define M_REORDER_ARGLIST_FUNC_ARGPTR8(arglist) , & M_RET_ARG9 arglist , +#define M_REORDER_ARGLIST_FUNC_ARGPTR9(arglist) , & M_RET_ARG10 arglist , + +#define M_REORDER_ARGLIST_FUNC_ID(arg) , arg , M_EAT + +/* Perform the API translation of the return code depending + on the given keyword (NONE, EQ, NEQ, ...).) */ +#define M_REORDER_RET_NONE(...) /* Nothing */ +#define M_REORDER_RET_VOID(...) (void) +#define M_REORDER_RET_NEG(...) - +#define M_REORDER_RET_EQ(value) value == M_EAT +#define M_REORDER_RET_NEQ(value) value != M_EAT +#define M_REORDER_RET_LT(value) value > M_EAT +#define M_REORDER_RET_LE(value) value >= M_EAT +#define M_REORDER_RET_GT(value) value < M_EAT +#define M_REORDER_RET_GE(value) value <= M_EAT +#define M_REORDER_RET_ARG1(...) M_RET_ARG1(__VA_ARGS__) = +#define M_REORDER_RET_ARG2(...) M_RET_ARG2(__VA_ARGS__) = +#define M_REORDER_RET_ARG3(...) M_RET_ARG3(__VA_ARGS__) = +#define M_REORDER_RET_ARG4(...) M_RET_ARG4(__VA_ARGS__) = +#define M_REORDER_RET_ARG5(...) M_RET_ARG5(__VA_ARGS__) = +#define M_REORDER_RET_ARG6(...) M_RET_ARG6(__VA_ARGS__) = +#define M_REORDER_RET_ARG7(...) M_RET_ARG7(__VA_ARGS__) = +#define M_REORDER_RET_ARG8(...) M_RET_ARG8(__VA_ARGS__) = +#define M_REORDER_RET_ARG9(...) M_RET_ARG9(__VA_ARGS__) = + +/* Integrate the generic API in the API transformation framework */ +#define M_OPLAPI(method, oplist, ...) , ( M_C( M_REORDER_RET_, M_DELAY2(M_HEAD_2 method))(__VA_ARGS__, ) M_DELAY2(M_HEAD method)( M_REORDER_ARGLIST( (oplist, __VA_ARGS__), ( M_TAIL_2 method ) ) ) ) +#define M_OPLAPI_INDIRECT_API(...) M_OPLAPI +#define M_OPLAPI_EXTRACT_API(...) ( __VA_ARGS__ ) + + +/* Define the no default function that generates a compiler error + if the method is expanded. +*/ +#define M_NO_DEFAULT(...) \ + M_STATIC_ASSERT(false, M_LIB_MISSING_METHOD, \ + "The requested operator has no method registered in the given OPLIST. ") + +#define M_NO_DEF_TYPE \ + M_STATIC_ASSERT(false, M_LIB_MISSING_METHOD, \ + "The requested operator has no type/subtype/suboplist registered in the given OPLIST. ") + +/* Test if the given variable is a basic C variable: + int, float, enum, bool or compatible. + NOTE: Not perfect, but catch some errors */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define M_CHECK_BASIC_TYPE(a) \ + M_STATIC_ASSERT(_Generic(a, \ + _Bool: 1, \ + char: 1, unsigned char: 1, signed char: 1, \ + unsigned short: 1, signed short: 1, \ + unsigned int: 1, signed int: 1, \ + unsigned long: 1, signed long: 1, \ + unsigned long long: 1, signed long long:1, \ + float: 1, double:1, long double: 1, \ + char *:1, void*:1, \ + default: 0), \ + M_LIB_NOT_A_BASIC_TYPE, \ + "The variable " M_AS_STR(a) " is not a basic C type (int/float), " \ + "but the given methods use it like this. " \ + "It is likely the given oplist is not right.") +#else +#define M_CHECK_BASIC_TYPE(a) \ + M_STATIC_ASSERT(sizeof (a) <= M_MAX(sizeof(long long), \ + M_MAX(sizeof (long double), \ + sizeof (uintmax_t))), \ + M_LIB_NOT_A_BASIC_TYPE, \ + "The variable " M_AS_STR(a) " is too big to be a basic C type (int/float), " \ + "but the given methods use it like this. " \ + "It is likely the given oplist is not right.") +#endif + +/* Check if both variables are of the same type. + The test compare their size. + NOTE: Not perfect but catch some errors */ +#define M_CHECK_SAME(a, b) \ + M_STATIC_ASSERT(sizeof(a) == sizeof(b), \ + M_LIB_NOT_SAME_TYPE, \ + "The variable " M_AS_STR(a) " and " M_AS_STR(b) \ + " are not of same type.") + +/* Check if the oplist is compatible with the type. + The oplist should define a TYPE method, in which case it is tested. + If it is not exported, do nothing. + Compare the type in C11, the size in C99 or C++. + - name is the base name of the container. + - inst is a number which is incremented for each use of this macro + within the named container. + - type is the type to test + - oplist of the oplist to test to. +*/ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define M_CHECK_COMPATIBLE_OPLIST(name, inst, type, oplist) \ + M_IF_METHOD(TYPE, oplist) \ + ( \ + M_INLINE void M_C3(m_c0re_ctype_, name, inst)(void) \ + { \ + type x; \ + /* Proper check using _Generic */ \ + M_STATIC_ASSERT(_Generic(&x, \ + M_GET_TYPE oplist *: 1 /* Ok, type matches */, \ + default: 0 /* NOK, type doesn't match */ ), \ + M_LIB_TYPE_MISTMACH, \ + "The given type " M_AS_STR(type) \ + " and the type of the oplist does not match: " \ + M_OPL_AS_STR(oplist) ); \ + } \ + , /* End of TYPE */) +#else +#define M_CHECK_COMPATIBLE_OPLIST(name, inst, type, oplist) \ + M_IF_METHOD(TYPE, oplist) \ + ( \ + M_INLINE void M_C3(m_c0re_ctype_, name, inst)(void) \ + { \ + /* Imperfect check using size of type */ \ + M_STATIC_ASSERT(sizeof (type) == sizeof (M_GET_TYPE oplist), \ + M_LIB_TYPE_MISTMACH, \ + "The given type " M_AS_STR(type) \ + " and the type of the oplist does not match: " \ + M_OPL_AS_STR(oplist) ); \ + } \ + , /* End of TYPE */) +#endif + + +/* Define the default method. + NOTE: M_SET_DEFAULT may be called in conditions where a and b + are different but compatible type. + */ +#define M_INIT_DEFAULT(a) ((a) = 0) +#define M_SET_DEFAULT(a,b) ((a) = (b)) +#define M_NOTHING_DEFAULT(...) ((void)(__VA_ARGS__)) +#define M_EMPTY_DEFAULT(...) ((void)1) +#define M_TRUE_DEFAULT(...) true +#define M_NEW_DEFAULT(a) M_MEMORY_ALLOC(a) +#define M_DEL_DEFAULT(a) M_MEMORY_DEL(a) +#define M_REALLOC_DEFAULT(t,p,s) M_MEMORY_REALLOC(t,p,s) +#define M_FREE_DEFAULT(a) M_MEMORY_FREE(a) +#define M_EQUAL_DEFAULT(a,b) ((a) == (b)) +#define M_CMP_DEFAULT(a,b) ((a) < (b) ? -1 : (a) > (b)) +#define M_ADD_DEFAULT(a,b,c) ((a) = (b) + (c)) +#define M_SUB_DEFAULT(a,b,c) ((a) = (b) - (c)) +#define M_MUL_DEFAULT(a,b,c) ((a) = (b) * (c)) +#define M_DIV_DEFAULT(a,b,c) ((a) = (b) / (c)) +#define M_AND_DEFAULT(a,b,c) ((a) = (b) & (c)) +#define M_OR_DEFAULT(a,b,c) ((a) = (b) | (c)) +#define M_NO_EXT_ALGO(n,co,to) +#define M_INC_ALLOC_DEFAULT(n) (M_MAX(8, (n))*2) + +/* Define the method for basic types */ +/* Check that the type matches a C basic type and do the job */ +#define M_INIT_BASIC(a) (M_CHECK_BASIC_TYPE(a), (a) = 0) +#define M_SET_BASIC(a,b) (M_CHECK_BASIC_TYPE(a), (a) = (b)) +#define M_EQUAL_BASIC(a,b) (M_CHECK_BASIC_TYPE(a), (a) == (b)) +#define M_CMP_BASIC(a,b) (M_CHECK_BASIC_TYPE(a), (a) < (b) ? -1 : (a) > (b)) + +/* NOTE: Theses operators are NOT compatible with the '[1]' tricks + if the variable is defined as a parameter of a function + (sizeof (a) is not portable). */ +#define M_MOVE_DEFAULT(a,b) (M_CHECK_SAME(a, b), M_MEMCPY_DEFAULT(a, b), memset(&(b), 0, sizeof (a))) +#define M_MEMCPY_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcpy(&(a), &(b), sizeof (a))) +#define M_MEMSET_DEFAULT(a) (memset(&(a), 0, sizeof (a))) +#define M_MEMCMP1_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcmp(&(a), &(b), sizeof (a)) == 0) +#define M_MEMCMP2_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcmp(&(a), &(b), sizeof (a))) +#define M_SWAP_DEFAULT(el1, el2) do { \ + char _tmp[sizeof (el1)]; \ + M_CHECK_SAME(el1, el2); \ + memcpy(&_tmp, &(el1), sizeof (el1)); \ + memcpy(&(el1), &(el2), sizeof (el1)); \ + memcpy(&(el2), &_tmp, sizeof (el1)); \ + } while (0) + +/* NOTE: Theses operators are to be used with the '[1]' tricks + if the variable is defined as a parameter of a function + (sizeof (a) is not portable). */ +#define M_MOVE_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), M_MEMCPY_A1_DEFAULT(a, b), M_MEMSET_A1_DEFAULT(b)) +#define M_MEMCPY_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), memcpy(&(a[0]), &(b[0]), sizeof (a[0]))) +#define M_MEMSET_A1_DEFAULT(a) (memset(&(a[0]), 0, sizeof (a[0]))) +#define M_MEMCMP1_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0],b[0]), memcmp(&(a[0]), &(b[0]), sizeof (a[0])) == 0) +#define M_MEMCMP2_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), memcmp(&(a[0]), &(b[0]), sizeof (a[0]))) +#define M_HASH_A1_DEFAULT(a) (m_core_hash((const void*) &(a[0]), sizeof (a[0])) ) + +/* Default oplist for plain structure */ +#define M_POD_OPLIST \ + (INIT(M_MEMSET_DEFAULT), INIT_SET(M_MEMCPY_DEFAULT), SET(M_MEMCPY_DEFAULT), \ + CLEAR(M_NOTHING_DEFAULT), EQUAL(M_MEMCMP1_DEFAULT), CMP(M_MEMCMP2_DEFAULT), \ + HASH(M_HASH_POD_DEFAULT), SWAP(M_SWAP_DEFAULT)) + + +/* Default oplist for a structure defined with an array of size 1 */ +#define M_A1_OPLIST \ + (INIT(M_MEMSET_A1_DEFAULT), INIT_SET(M_MEMCPY_A1_DEFAULT), SET(M_MEMCPY_A1_DEFAULT), \ + CLEAR(M_NOTHING_DEFAULT), EQUAL(M_MEMCMP1_A1_DEFAULT), CMP(M_MEMCMP2_A1_DEFAULT), \ + HASH(M_HASH_A1_DEFAULT)) + + +/* Oplist for a type that does nothing and shall not be instanciated */ +#define M_EMPTY_OPLIST \ + (INIT(M_EMPTY_DEFAULT), INIT_SET(M_EMPTY_DEFAULT), \ + SET(M_EMPTY_DEFAULT), CLEAR(M_EMPTY_DEFAULT), \ + INIT_MOVE(M_EMPTY_DEFAULT), MOVE(M_EMPTY_DEFAULT), \ + EQUAL(M_TRUE_DEFAULT), GET_STR(M_EMPTY_DEFAULT), \ + OUT_STR(M_EMPTY_DEFAULT), IN_STR(M_TRUE_DEFAULT), \ + OUT_SERIAL(M_EMPTY_DEFAULT), IN_SERIAL(M_TRUE_DEFAULT), \ + PARSE_STR(M_TRUE_DEFAULT)) + + +/* Default oplist for C standard types (int & float). + Implement generic out_str/in_str/parse_str/get_str function if using C11. + Add FILE I/O if stdio.h has been included +*/ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + +# if M_USE_STDIO +/* C11 + FILE support */ +# define M_BASIC_OPLIST \ + (INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \ + CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_BASIC), CMP(M_CMP_BASIC), \ + INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \ + RESET(M_INIT_BASIC), PROPERTIES( (NOCLEAR(1)) ), \ + ADD(M_ADD_DEFAULT), SUB(M_SUB_DEFAULT), \ + MUL(M_MUL_DEFAULT), DIV(M_DIV_DEFAULT), \ + HASH(M_HASH_DEFAULT), SWAP(M_SWAP_DEFAULT) , \ + IN_STR(M_FSCAN_ARG M_IPTR), OUT_STR(M_FPRINT_ARG), \ + IN_SERIAL(M_IN_SERIAL_DEFAULT_ARG M_IPTR), OUT_SERIAL(M_OUT_SERIAL_DEFAULT_ARG), \ + PARSE_STR(M_PARSE_DEFAULT_TYPE M_IPTR), M_GET_STR_METHOD_FOR_DEFAULT_TYPE) +# else +/* C11 + No FILE support */ +# define M_BASIC_OPLIST \ + (INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \ + CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_BASIC), CMP(M_CMP_BASIC), \ + INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \ + RESET(M_INIT_BASIC), PROPERTIES( (NOCLEAR(1)) ), \ + ADD(M_ADD_DEFAULT), SUB(M_SUB_DEFAULT), \ + MUL(M_MUL_DEFAULT), DIV(M_DIV_DEFAULT), \ + HASH(M_HASH_DEFAULT), SWAP(M_SWAP_DEFAULT) , \ + IN_SERIAL(M_IN_SERIAL_DEFAULT_ARG M_IPTR), OUT_SERIAL(M_OUT_SERIAL_DEFAULT_ARG), \ + PARSE_STR(M_PARSE_DEFAULT_TYPE M_IPTR), M_GET_STR_METHOD_FOR_DEFAULT_TYPE) +# endif +#else +/* C99 */ +# define M_BASIC_OPLIST \ + (INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \ + CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_BASIC), CMP(M_CMP_BASIC), \ + INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \ + RESET(M_INIT_BASIC), PROPERTIES( (NOCLEAR(1)) ), \ + ADD(M_ADD_DEFAULT), SUB(M_SUB_DEFAULT), \ + MUL(M_MUL_DEFAULT), DIV(M_DIV_DEFAULT), \ + HASH(M_HASH_DEFAULT), SWAP(M_SWAP_DEFAULT) ) +#endif + +/* Obsolete name */ +#define M_DEFAULT_OPLIST M_BASIC_OPLIST + +/* Specialized oplist for a boolean. + * M_BASIC_OPLIST is nearly ok, except for ADD/SUB/MUL/DIV + * that generates warnings with boolean. + */ +#define M_BOOL_OPLIST \ + M_OPEXTEND(M_BASIC_OPLIST, ADD(M_OR_DEFAULT), MUL(M_AND_DEFAULT), \ + SUB(0), DIV(0)) + + +/* Specialized oplist for an enumerate. + * M_BASIC_OPLIST is nearly ok, except if build in C++ mode. + * Also I/O are specialized and arithmetics are removed + * OPLIST doesn't store an oplist but an additional parameter + * (the initial value) + */ +#if M_USE_STDIO +/* FILE support */ +#define M_ENUM_OPLIST(type, init) \ + (INIT(API_1(M_ENUM_INIT)), INIT_SET(M_SET_DEFAULT), \ + SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \ + EQUAL(M_EQUAL_DEFAULT), CMP(M_CMP_DEFAULT), \ + INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \ + HASH(M_HASH_POD_DEFAULT), SWAP(M_SWAP_DEFAULT), \ + TYPE(type), OPLIST(init), \ + IN_STR(API_1(M_ENUM_FSCAN)), OUT_STR(M_ENUM_FPRINT), \ + IN_SERIAL(API_1(M_ENUM_IN_SERIAL)), OUT_SERIAL(M_ENUM_OUT_SERIAL), \ + PARSE_STR(API_1(M_ENUM_PARSE)), M_GET_STR_METHOD_FOR_ENUM_TYPE \ + ) +#else +/* No File support */ +#define M_ENUM_OPLIST(type, init) \ + (INIT(API_1(M_ENUM_INIT)), INIT_SET(M_SET_DEFAULT), \ + SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \ + EQUAL(M_EQUAL_DEFAULT), CMP(M_CMP_DEFAULT), \ + INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \ + HASH(M_HASH_POD_DEFAULT), SWAP(M_SWAP_DEFAULT), \ + TYPE(type), OPLIST(init), \ + IN_SERIAL(API_1(M_ENUM_IN_SERIAL)), OUT_SERIAL(M_ENUM_OUT_SERIAL), \ + PARSE_STR(API_1(M_ENUM_PARSE)), M_GET_STR_METHOD_FOR_ENUM_TYPE \ + ) +#endif /* M_USE_STDIO */ + +/* It will be overloaded if m-string.h is included */ +#define M_GET_STR_METHOD_FOR_ENUM_TYPE + +/* Initialize an enum to its init value */ +#define M_ENUM_INIT(oplist, var) \ + ((var) = M_GET_OPLIST oplist ) + +/* Define helper functions for enum oplist */ +/* Input/Output an enumerate. + It is stored as a long long integer for best compatibility. */ +#if M_USE_STDIO +M_INLINE long long +m_core_fscan_enum (FILE *f) +{ + long long ret; + int s = m_core_fscanf(f, "%lld", &ret) == 1; + /* HACK: Push back the return code in FILE stream, + so that it can be popped later with a fgetc */ + ungetc(s, f); + return ret; +} +#define M_ENUM_FPRINT(f, var) fprintf( (f), "%lld", (long long) (var)) +#define M_ENUM_FSCAN(oplist, var, f) \ + ( var = (M_GET_TYPE oplist) (true ? m_core_fscan_enum(f) : 0), fgetc(f)) +#endif /* M_USE_STDIO */ + +/* HACK: Parse two times to convert and then compute + if the conversion succeeds for PARSE */ +M_INLINE long long +m_core_parse1_enum (const char str[]) +{ + return strtoll(str, NULL, 10); +} +M_INLINE bool +m_core_parse2_enum (const char str[], const char **endptr) +{ + char *end; + strtoll(str, &end, 10); + if (endptr != NULL) *endptr = end; + return end != str; +} + +#define M_ENUM_OUT_SERIAL(serial, var) \ + ((serial)->m_interface->write_integer(serial, (long long) (var), sizeof (var))) +#define M_ENUM_IN_SERIAL(oplist, var, serial) \ + ( var = (M_GET_TYPE oplist)(true ? m_core_in_serial_enum(serial) : 0), (serial)->tmp.r) +#define M_ENUM_GET_STR(str, var, append) \ + ((append ? m_string_cat_printf : m_string_printf) (str, "%lld", (long long) (var) )) +#define M_ENUM_PARSE(oplist, var, str, endptr) \ + ( var = (M_GET_TYPE oplist) (true ? m_core_parse1_enum(str) : 0), m_core_parse2_enum(str, endptr)) + + +/* Default oplist for standard types of pointers. + */ +#define M_PTR_OPLIST \ + (INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), \ + CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_DEFAULT), \ + INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \ + SWAP(M_SWAP_DEFAULT) ) + + +/* Default oplist for complex objects with "classic" names for methods. + */ +#define M_CLASSIC_OPLIST(name) ( \ + INIT(M_F(name, _init)), \ + INIT_SET(M_F(name, _init_set)), \ + SET(M_F(name, _set)), \ + CLEAR(M_F(name, _clear)), \ + TYPE(M_F(name, _t)) ) + + +/* OPLIST for 'const char *' string (with NO memory allocation). + */ +#define M_CSTR_EQUAL(a,b) (strcmp((a),(b)) == 0) +#define M_CSTR_OUT_STR(file, str) fprintf(file, "%s", str) +#define M_CSTR_OPLIST (INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), \ + SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \ + HASH(m_core_cstr_hash), EQUAL(M_CSTR_EQUAL), \ + CMP(strcmp), TYPE(const char *), \ + OUT_STR(M_CSTR_OUT_STR) ) + + +/* From an oplist (...) return ... (Like M_ID but for OPLIST) */ +#define M_OPFLAT(...) __VA_ARGS__ + +/* Concat two oplists in one. op1 will have higher priority to op2 */ +#define M_OPCAT(op1,op2) (M_OPFLAT op1, M_OPFLAT op2) + +/* Apply an oplist */ +#define M_OPAPPLY(a, oplist) a oplist + +/* Extend an oplist by adding some methods */ +#define M_OPEXTEND(op, ...) (__VA_ARGS__, M_OPFLAT op) + +/* Return the content of a property named 'propname' + in the PROPERTIES field of oplist + or 0, if it is not defined */ +#define M_GET_PROPERTY(oplist, propname) \ + M_GET_METHOD (propname, 0, M_OPFLAT M_GET_PROPERTIES oplist) + +/* Test if a method is present in an oplist. + Return 0 (method is absent or disabled) or 1 (method is present and not disabled). + NOTE: M_TEST_METHOD_P does not work if method is something within parenthesis (like OPLIST*) + In this case, you shall use the M_TEST_METHOD_ALTER_P macro (safer but slower). + */ +#define M_TEST_METHOD_P(method, oplist) \ + M_BOOL(M_GET_METHOD (method, 0, M_OPFLAT oplist)) + +#define M_TEST_METHOD_ALTER_P(method, oplist) \ + M_INV(M_EMPTY_P(M_GET_METHOD (method, , M_OPFLAT oplist))) + + +/* Test if a method is disabled in an oplist. + To disable an oplist, just put '0' in the method like this: + Example: (INIT(0), CLEAR(clear)) + Here INIT is disabled, whereas CLEAR is not. + Return 1 (method is disabled) or 0 (method is not disabled - absent or present) */ +#define M_TEST_DISABLED_METHOD_P(method, oplist) \ + M_INV(M_BOOL(M_GET_METHOD (method, 1, M_OPFLAT oplist))) + +/* Perfom a preprocessing M_IF, if the method is present in the oplist. + Example: M_IF_METHOD(HASH, oplist)(define function with HASH method, ) */ +#define M_IF_METHOD(method, oplist) M_IF(M_TEST_METHOD_P(method, oplist)) + +/* Perfom a preprocessing M_IF, if the method is disabled in the oplist. + Example: M_IF_DISABLED_METHOD(HASH, oplist)(define function without HASH method, ) */ +#define M_IF_DISABLED_METHOD(method, oplist) M_IF(M_TEST_DISABLED_METHOD_P(method, oplist)) + +/* Perform a preprocessing M_IF if the method exists in both oplist. + Example: M_IF_METHOD_BOTH(HASH, oplist1, oplist2) (define function with HASH method, ) */ +#define M_IF_METHOD_BOTH(method, oplist1, oplist2) \ + M_IF(M_AND(M_TEST_METHOD_P(method, oplist1), M_TEST_METHOD_P(method, oplist2))) + +/* Perform a preprocessing M_IF if both methods exist in the oplist. + Example: M_IF_METHOD2(HASH, CMP, oplist) (define function with HASH & CMP method, ) */ +#define M_IF_METHOD2(method1, method2, oplist) \ + M_IF(M_AND(M_TEST_METHOD_P(method1, oplist), M_TEST_METHOD_P(method2, oplist))) + +/* Perform a preprocessing M_IF if at least one method exists in the oplist. + Example: M_IF_AT_LEAST_METHOD(HASH, CMP, oplist) (define function with HASH or CMP method, ) */ +#define M_IF_AT_LEAST_METHOD(method1, method2, oplist) \ + M_IF(M_OR(M_TEST_METHOD_P(method1, oplist), M_TEST_METHOD_P(method2, oplist))) + +/* Perform a preprocessing M_IF if both methods exists in both oplist. + Example: M_IF_METHOD2_BOTH(HASH, INIT, oplist1, oplist2) (define function with HASH & INIT method, ) */ +#define M_IF_METHOD2_BOTH(method1, method2, oplist1, oplist2) \ + M_IF(M_AND(M_AND(M_TEST_METHOD_P(method1, oplist1), M_TEST_METHOD_P(method1, oplist2)), \ + M_AND(M_TEST_METHOD_P(method2, oplist1), M_TEST_METHOD_P(method2, oplist2)))) + +/* Perform a preprocessing M_IF if the method exists for all oplist. + Example: M_IF_METHOD_ALL(HASH, oplist1, oplist2) (define function with HASH method, ) */ +#define M_IF_METHOD_ALL(method, ...) \ + M_IF(M_REDUCE2(M_TEST_METHOD_P, M_AND, method, __VA_ARGS__)) + +/* Perform a preprocessing M_IF if both methods exist for all oplist. + Example: M_IF_METHOD2_ALL(HASH, INIT, oplist1, oplist2) (define function with HASH & INIT methods, ) */ +#define M_IF_METHOD2_ALL(method1, method2, ...) \ + M_IF(M_REDUCE2(M_TEST_METHOD2_P, M_AND, (method1, method2), __VA_ARGS__)) +#define M_TEST_METHOD2_P(method_pair, oplist) \ + M_AND(M_TEST_METHOD_P(M_PAIR_1 method_pair, oplist), M_TEST_METHOD_P(M_PAIR_2 method_pair, oplist)) + +/* By putting this after a method in an oplist, we transform the argument list + so that the first argument becomes a pointer to the destination (OBSOLETE MACRO) */ +#define M_IPTR(...) ( & __VA_ARGS__ ) + +/* Perform an INIT_MOVE if present, or emulate it using INIT_SET/CLEAR */ +#define M_DO_INIT_MOVE(oplist, dest, src) do { \ + M_IF_METHOD(INIT_MOVE, oplist)(M_CALL_INIT_MOVE(oplist, (dest), (src)), \ + M_CALL_INIT_SET(oplist, (dest), (src)) ; \ + M_CALL_CLEAR(oplist, (src) )); \ + } while (0) + +/* Perform a MOVE if present, or emulate it using CLEAR/INIT_MOVE + if possible, or with SET/CLEAR otherwise */ +#define M_DO_MOVE(oplist, dest, src) do { \ + M_IF_METHOD(MOVE, oplist) (M_CALL_MOVE(oplist, (dest), (src)), \ + M_IF_METHOD(INIT_MOVE, oplist)(M_CALL_CLEAR(oplist, (dest)); \ + M_CALL_INIT_MOVE(oplist, (dest), (src)), \ + M_CALL_SET(oplist, (dest), (src)); \ + M_CALL_CLEAR(oplist, (src)) \ + )); \ + } while (0) + +/* Provide SET semantics through INIT_SET. Needs API_1 */ +#define M_SET_THROUGH_INIT_SET(oplist, dst, src) \ + ( M_CALL_CLEAR(oplist, dst), M_CALL_INIT_SET(oplist, dst, src) ) + +/* Test if the argument is an expression that looks like an oplist: + * - the data are within parenthesis + * - there is only one level of parenthesis + * The detection is imperfect. +*/ +#define M_OPLIST_P(a) \ + M_AND(M_PARENTHESIS_P(a), M_INV(M_PARENTHESIS_P (M_OPFLAT a))) + +#define M_IF_OPLIST(a) M_IF(M_OPLIST_P(a)) + +/* If 'a' seems to be an oplist, it returns a, + else if a symbol composed of M_OPL_##a() exists and is defined as an oplist, it returns it + else it returns a (but it is likely to fail latter). + In short, if a global oplist is defined for the argument, it returns it + otherwise it returns the argument. + Global oplist is limited to typedef types. + We need to delay the concat between M_OPL_ and a until we know it cannot be an oplist + as if a is an oplist the concat of M_OPL_ and an oplist will produce an illegal token. +*/ +#define M_GLOBAL_OPLIST(a) \ + M_IF( M_OPLIST_P(a))(M_GLOBALI_OPLIST_ID, M_GLOBALI_OPLIST_ELSE)(a) +#define M_GLOBALI_OPLIST_ID(a) a +#define M_GLOBALI_OPLIST_ELSE(a) M_GLOBALI_OPLIST_ELSE2(a, M_C(M_OPL_, a)()) +#define M_GLOBALI_OPLIST_ELSE2(a, op) M_IF( M_OPLIST_P (op))(op, a) + +#define M_GLOBAL_TYPE(a) \ + M_IF( M_OPLIST_P(a))(M_GLOBALI_TYPE_GET, M_GLOBALI_OPLIST_ID)(a) +#define M_GLOBALI_TYPE_GET(a) M_GET_TYPE a + +/* If a symbol composed of M_OPL_##a() exists and is defined as an oplist, + it returns it otherwise it returns M_BASIC_OPLIST. + Global oplist is limited to typedef types. + NOTE1: It first tests if the type doesn't start with a parenthesis, + in which case concatenation cannot be used. + NOTE: It doesn't test if M_OPL_##a() is exactly an oplist (M_OPLIST_P) + but rather than if it starts with parenthesis: this is to allow + M_OPL_a() to expand into an invalid oplist ((M_LIB_ERROR())) + NOTE: The result of this macro shall be evaluated like this: + M_GLOBAL_OPLIST_OR_DEF(type)() +*/ +#define M_GLOBAL_OPLIST_OR_DEF(a) \ + M_IF( M_PARENTHESIS_P(a))(M_GLOBALI_OPLIST_DEFAULT1, M_GLOBALI_OPLIST_OR_DEF_ELSE)(a) +#define M_GLOBALI_OPLIST_DEFAULT1(a) M_GLOBALI_OPLIST_DEFAULT2 +#define M_GLOBALI_OPLIST_DEFAULT2() M_BASIC_OPLIST +#define M_GLOBALI_OPLIST_OR_DEF_ELSE(a) M_GLOBALI_OPLIST_OR_DEF_ELSE2(a, M_C(M_OPL_, a)()) +#define M_GLOBALI_OPLIST_OR_DEF_ELSE2(a, op) M_IF( M_PARENTHESIS_P(op))(M_C(M_OPL_, a), M_GLOBALI_OPLIST_DEFAULT2) + + +/* Register simple classic C types (no qualifier) + * We cannot register type with qualifier (for example long long) however. + */ +#define M_OPL_char() M_BASIC_OPLIST +#define M_OPL_short() M_BASIC_OPLIST +#define M_OPL_int() M_BASIC_OPLIST +#define M_OPL_unsigned() M_BASIC_OPLIST +#define M_OPL_long() M_BASIC_OPLIST +#define M_OPL_float() M_BASIC_OPLIST +#define M_OPL_double() M_BASIC_OPLIST +#define M_OPL_bool() M_BOOL_OPLIST +#define M_OPL__Bool() M_BOOL_OPLIST + +/************************************************************/ +/******************** Syntax Enhancing **********************/ +/************************************************************/ + +/* M_EACH enables iterating over a container using a for loop. + First argument will be a created pointer var to the underlying type. + Second argument is the container to iterate. + Third argument can be the oplist of the list or the type of the list if a global + oplist has been recorded. + USAGE: for M_EACH(item, list, LIST_OPLIST) { action; } +*/ +#define M_EACH(item, container, oplist) \ + M_EACHI_OPLIST(item, container, M_GLOBAL_OPLIST(oplist)) + +/* Internal for M_EACH */ +#define M_EACHI_OPLIST(item, container, oplist) \ + M_IF_METHOD(IT_REF, oplist)(M_EACHI, M_EACHI_CONST) \ + (item, container, oplist, M_C(local_iterator_, __LINE__), \ + M_C(local_cont_, __LINE__)) + +/* Internal for M_EACH with M_GET_IT_REF operator */ +#define M_EACHI(item,container,oplist, iterator, cont) \ + (bool cont = true; cont; cont = false) \ + for(M_GET_SUBTYPE oplist *item; cont ; cont = false) \ + for(M_GET_IT_TYPE oplist iterator; cont ; cont = false) \ + for(M_GET_IT_FIRST oplist (iterator, container) ; \ + !M_GET_IT_END_P oplist (iterator) \ + && (item = M_GET_IT_REF oplist (iterator), true) ; \ + M_GET_IT_NEXT oplist (iterator)) + +/* Internal for M_EACH with M_GET_IT_CREF operator */ +#define M_EACHI_CONST(item,container,oplist, iterator, cont) \ + (bool cont = true; cont; cont = false) \ + for(const M_GET_SUBTYPE oplist *item; cont ; cont = false) \ + for(M_GET_IT_TYPE oplist iterator; cont ; cont = false) \ + for(M_GET_IT_FIRST oplist (iterator, container) ; \ + !M_GET_IT_END_P oplist (iterator) \ + && (item = M_GET_IT_CREF oplist (iterator), true) ; \ + M_GET_IT_NEXT oplist (iterator)) + + +/* M_LET defines, initializes & clears an object available within the next bracket. + USAGE: + M_LET(variable_list, variable_oplist|variable_type) { code } + * variable_list can be a comma separated list of variable. + * A variable alone is initialized to its default value. + * A variable with initializer (like '(var, init value...)' + is initialized with this init values. + NOTE: The code within {} can not perform return or goto command. + break is supported and will exit the braket (calling the clear method) + Last argument can be the oplist or the type itself if a global + oplist has been recorded for this type. + */ +#define M_LET(a, ...) \ + M_ID(M_LETI0 ((M_REVERSE(a, __VA_ARGS__, \ + M_IF(M_PARENTHESIS_P(a))(M_LETI_VAR_NAME_A, \ + M_LETI_VAR_NAME_B)(a) )))) + +// 1b. Generate a unique name based on the first variable and the line number +#define M_LETI_VAR_NAME_A(var) M_C3(_local_cont_, M_RET_ARG1 var, __LINE__) +#define M_LETI_VAR_NAME_B(var) M_C3(_local_cont_, var, __LINE__) +// 2. Evaluate with or without and inject oplist +#define M_LETI0(list) \ + M_LETI1 list +#define M_LETI1(cont, oplist, ...) \ + M_LETI2(cont, M_GLOBAL_OPLIST(oplist), __VA_ARGS__) +// 3. Validate oplist before going any further +#define M_LETI2(cont, oplist, ...) \ + M_IF_OPLIST(oplist)(M_LETI3, M_LETI2_FAILURE)(cont, oplist, __VA_ARGS__) +// Stop with a failure (invalid oplist) +#define M_LETI2_FAILURE(cont, oplist, ...) \ + M_STATIC_ASSERT(false, M_LIB_NOT_AN_OPLIST, "(M_LET): the given argument is not a valid oplist: " M_AS_STR(oplist)) +// 4. Map all variables to their own LET +#define M_LETI3(cont, oplist, ...) \ + for(bool cont = true; cont ; /* unused */) \ + M_MAP2(M_LETI_SINGLE, (cont, oplist), __VA_ARGS__) \ + for(;cont;cont = false) +// 5. Dispatch the right LET in function of having or not arguments with data = (cont, oplist) +#define M_LETI_SINGLE(data, name) \ + M_IF(M_PARENTHESIS_P(name))(M_LETI_SINGLE2_SET,M_LETI_SINGLE2) \ + (M_PAIR_1 data, M_PAIR_2 data, name, M_RET_ARG1 name, M_SKIPI_1 name) +// 6a. Define without argument ==> use the INIT operator +#define M_LETI_SINGLE2(cont, oplist, name, ...) \ + M_LET_TRY_INJECT_PRE(cont, oplist, name) \ + for(M_GET_TYPE oplist name; \ + cont && (M_CALL_INIT(oplist, name), true); \ + (M_CALL_CLEAR(oplist, name), cont = false)) \ + M_LET_TRY_INJECT_POST(cont, oplist, name) +// 6b. Define with arguments ==> use the INIT_SET or INIT_WITH operator (if defined). +#define M_LETI_SINGLE2_SET(cont, oplist, params, name, ...) \ + M_LET_TRY_INJECT_PRE(cont, oplist, name) \ + for(M_GET_TYPE oplist name; \ + cont && (M_LETI_SINGLE2_INIT(oplist, name, __VA_ARGS__), true); \ + (M_CALL_CLEAR(oplist, name), cont = false)) \ + M_LET_TRY_INJECT_POST(cont, oplist, name) +// 6c. Use of INIT_SET or INIT_WITH. +#define M_LETI_SINGLE2_INIT(oplist, name, ...) \ + M_IF_METHOD(INIT_WITH,oplist)(M_LETI_SINGLE3_INIT, M_CALL_INIT_SET)(oplist, name, __VA_ARGS__) +#define M_LETI_SINGLE3_INIT(oplist, name, ...) \ + M_IF(M_NOTEQUAL( M_NARGS(__VA_ARGS__), 1))(M_CALL_INIT_WITH, M_LETI_SINGLE4_INIT)(oplist, name, __VA_ARGS__) +#define M_LETI_SINGLE4_INIT(oplist, name, arg) \ + M_IF(M_GET_PROPERTY(oplist, LET_AS_INIT_WITH))(M_CALL_INIT_WITH, M_LETI_SINGLE5_INIT)(oplist, name, arg) +#define M_LETI_SINGLE5_INIT(oplist, name, arg) \ + M_IF(M_PARENTHESIS_P( arg ) )(M_CALL_INIT_WITH, M_CALL_INIT_SET)(oplist, name, M_REMOVE_PARENTHESIS (arg)) + +/* Dummy stubs that do nothing by default (overrided if needed by m-try) */ +#define M_LET_TRY_INJECT_PRE(cont, oplist, name) +#define M_LET_TRY_INJECT_POST(cont, oplist, name) + +/* + Internal M_LET for use for withing container. +*/ +#define M_QLET(x, type, oplist) \ + M_LET(x, M_OPEXTEND(oplist, TYPE(type))) + +/* Transform the va list by adding their number as the first argument of + the list. + Example: M_VA(a,b,c,d,e) ==> 5,a,b,c,d,e + TODO: Use of a structure instead of an integer to ensure limited syntax control. +*/ +#define M_VA(...) M_NARGS(__VA_ARGS__), __VA_ARGS__ + + +/* Defer the evaluation of the given expression until the closing brace. + M_DEFER(code) { } + Example: + M_DEFER(free(p)) { + // code using p + } // Here p is free +*/ +#define M_DEFER(...) \ + M_DEFER_INTERNAL(M_C(m_var_, __LINE__), __VA_ARGS__) + +#define M_DEFER_INTERNAL(cont, ...) \ + for(bool cont = true; cont; cont = false) \ + M_DEFER_TRY_INJECT_PRE(cont, __VA_ARGS__) \ + for( (void) 0; cont ; (__VA_ARGS__), cont = false) \ + M_DEFER_TRY_INJECT_POST(cont, __VA_ARGS__) \ + for( (void) 0; cont; cont = false) + +// Theses macros will be overrided by m-try if needed. +#define M_DEFER_TRY_INJECT_PRE(cont, clear) /* Nothing */ +#define M_DEFER_TRY_INJECT_POST(cont, clear) /* Nothing */ + + +/* If exceptions are activated, M_CHAIN_INIT enables support for chaining + initialization at the begining of a constructor for the fields of the constructed + object so that even if the constructor failed and throw an exception, + the fields of the constructed object are properly cleared. + This is equivalent to C++ construct: void type() : field1(), field2() { rest of constructor } + M_CHAIN_INIT shall be the first instruction of the init function. + Second argument is the initialization code. + Third argument is the clear code. + The clear code is *only* executed on exception. + USAGE: + void init_function(struct_t s) { + M_CHAIN_INIT(name, string_init(s->str), string_clear(s->str)) { + // Rest of initialization code + } + } + */ +#define M_CHAIN_INIT(name, init, clear) \ + M_CHAIN_INIT_INTERNAL(M_C(m_var_, name), init, clear) + +#define M_CHAIN_INIT_INTERNAL(cont, init, clear) \ + for(bool cont = true; cont; cont = false) \ + M_DEFER_TRY_INJECT_PRE(cont, clear) \ + for( init; cont ; cont = false) \ + M_DEFER_TRY_INJECT_POST(cont, clear) \ + for( (void) 0; cont; cont = false) + + +/* If exceptions are activated, M_CHAIN_OBJ enables support for chaining + initialization at the begining of a constructor for the fields of the constructed + object so that even if the constructor failed and throw an exception, + the fields of the constructed object are properly cleared. + This is equivalent to M_CHAIN_INIT except it takes an oplist. + This is equivalent to C++ construct: void type() : field1(), field2() { rest of constructor } + M_CHAIN_OBJ shall be the first instruction of the init function except + it can be freely mixed with M_CHAIN_INIT + First argument is a name identifier. + Second argument is the oplist of the variable to initialize. + Third argument is the variable to initialize. + Optional fourth argument is the value to use for initialization. + If there is no value, it will initialize using the INIT method. + Otherwise, if there is no parenthesis around the value, it will use the INIT_SET method. + Otherwise the INIT_WITH method. + The clear code is *only* executed on exception. + If the property NOCLEAR is defined for the object, + it won't generate an exception handler at all even if exceptions are activated, + and will only initialize the object. + USAGE: + void init_function(struct_t s) { + M_CHAIN_OBJ(name, STRING_OPLIST, s->str ) { + // Rest of initialization code + } + } + */ +#define M_CHAIN_OBJ(name, oplist, ...) \ + M_BY_NARGS( M_IF(M_GET_PROPERTY(oplist, NOCLEAR))(M_CHAIN_OBJ_A, M_CHAIN_OBJ_B), __VA_ARGS__) \ + (name, oplist, __VA_ARGS__) +// Need to define an exception handler. Use of M_CHAIN_INIT +#define M_CHAIN_OBJ_B__1(name, oplist, var) \ + M_CHAIN_INIT( name, M_CALL_INIT(oplist, var), M_CALL_CLEAR(oplist, var) ) +#define M_CHAIN_OBJ_B__2(name, oplist, var, value) \ + M_IF(M_PARENTHESIS_P(value))(M_CHAIN_OBJ_B__2_WITH, M_CHAIN_OBJ_B__2_SET) \ + (name, oplist, var, value) +#define M_CHAIN_OBJ_B__2_WITH(name, oplist, var, value) \ + M_CHAIN_INIT( name, M_CALL_INIT_WITH(oplist, var, value), M_CALL_CLEAR(oplist, var) ) +#define M_CHAIN_OBJ_B__2_SET(name, oplist, var, value) \ + M_CHAIN_INIT( name, M_CALL_INIT_SET(oplist, var, value), M_CALL_CLEAR(oplist, var) ) +// No need to define an exception handler. Just call the INIT function. +#define M_CHAIN_OBJ_A__1(name, oplist, var) \ + M_CHAIN_FOR(name, M_CALL_INIT(oplist, var) ) +#define M_CHAIN_OBJ_A__2(name, oplist, var, value) \ + M_CHAIN_FOR(name, \ + M_IF(M_PARENTHESIS_P(value))(M_CALL_INIT_WITH, M_CALL_INIT_SET) \ + (oplist, var, value) ) + +// Execute the 'init' function in a for loop construct +#define M_CHAIN_FOR(name, init ) \ + M_CHAIN_FOR_B(M_C(m_var_, name), init) +#define M_CHAIN_FOR_B(cont, init) \ + for(bool cont = true; cont; cont = false) \ + for( init; cont ; cont = false) \ + for( (void) 0; cont; cont = false) + + +/* If exceptions are activated, M_ON_EXCEPTION enables support for fixing + the data structure when an exception is throw, + so that the data structure is proper for the CLEAR method. + The fix code is *only* executed on exception. + USAGE: + void init_function(struct_t s) { + M_ON_EXCEPTION(name, OPLIST, s->size = i ) { + // Rest of initialization code + } + } + */ +#define M_ON_EXCEPTION(name, oplist, fix) \ + M_IF(M_GET_PROPERTY(oplist, NOCLEAR))(M_EAT, M_ON_EXCEPTION_B) \ + (M_C(m_var_, name), fix) + +#define M_ON_EXCEPTION_B(cont, fix) \ + for(bool cont = true; cont; cont = false) \ + M_DEFER_TRY_INJECT_PRE(cont, clear) \ + for( ; cont ; cont = false) \ + M_DEFER_TRY_INJECT_POST(cont, clear) \ + for( ; cont; cont = false) + + +/* Declare a variable, initialize it, continue if the initialization succeeds, + and clears the variable afterwards. + Otherwise, stop the execution and execute else_code if defined. + M_LET_IF(init code, test code, clear code[, else_code]) { code using the variable } + Example: + M_LET_IF(void * p = malloc(100), p!=0, free(p)) { + // code using p + } // Here p is free +*/ +#define M_LET_IF(init, test, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + ( \ + M_LET_IF_INTERNAL (init, test, __VA_ARGS__, (void)0, M_C(m_var_, __LINE__)), \ + M_LET_IF_INTERNAL (init, test, __VA_ARGS__, M_C(m_var_, __LINE__)) \ + ) + +#define M_LET_IF_INTERNAL(init, test, clear, else_a, cont) \ + for(bool cont = true; cont; cont = false) \ + for( init ; cont && ( (test) || (else_a, false) ); cont = false) \ + M_DEFER_INTERNAL(M_C(cont, defer), clear ) + + + +/************************************************************/ +/******************* INIT_WITH Enhancing ********************/ +/************************************************************/ + +/* Add as suffix for the given function the number of arguments of the calls + equal to __ # NARGS(...) + Can be used to call different function in function of the number of arguments. + It doesn't call the function with the argument in order to be able to chain the + macro */ +#define M_BY_NARGS(function, ...) M_C3(function, __, M_NARGS(__VA_ARGS__)) + +/* Call different methods in function of the number of arguments of the call, + * to be used in an OPLIST for the INIT_WITH operator. + * Shall be used with API_1 call (example INIT_WITH(API_1(M_INIT_WITH_NVAR)) ) + * Shall define a NAME base method + * All INIT_WITH methods shall be named as name ## _init_with__ ## NARGS + */ +#define M_INIT_WITH_NVAR(oplist, ...) \ + M_BY_NARGS(M_C(M_GET_NAME oplist, _init_with), __VA_ARGS__)(__VA_ARGS__) + + +/* Initialize the container 'dest' as per 'oplist' INIT operator + and fill it with the given VA_ARGS arguments with the PUSH operator. + NOTE: If the REVERSE operator exists, it assumes a list, + so it reverses the final order. +*/ +#define M_INIT_VAI(oplist, dest, ...) \ + (void)(M_GET_INIT oplist (dest) , \ + M_MAP2_C(M_INIT_VAI_FUNC, (dest, M_GET_PUSH oplist) , __VA_ARGS__) \ + M_IF_METHOD(REVERSE, oplist)(M_DEFERRED_COMMA M_GET_REVERSE oplist (dest), ) \ + ) +#define M_INIT_VAI_FUNC(d, a) \ + M_PAIR_2 d (M_PAIR_1 d, a) + + +/* Initialize the container 'dest' as per 'oplist' INIT operator + and fill it with the given VA_ARGS arguments with the _push_raw method + allowing INIT_WITH operator for the pushed argument if the given + argument is between parenthesis, otherwise an INIT_SET is performed. + NOTE: Parenthesis detection is the best we can do to detect a special + argument. The argument may start with '*' or a '+' and we cannot use + M_KEYWORD_P with such arguments (as it cannot be concatened). + NOTE: If the REVERSE operator exists, it assumes a list, + so it reverses the final order. +*/ +#define M_INIT_WITH_VAI(oplist, dest, ...) \ + (void)(M_GET_INIT oplist (dest) , \ + M_MAP2_C(M_INIT_WITH_VAI22_FUNC, (dest, oplist) , __VA_ARGS__) \ + M_IF_METHOD(REVERSE, oplist)(M_DEFERRED_COMMA M_GET_REVERSE oplist(dest), ) \ + ) +#define M_INIT_WITH_VAI22_FUNC(pair, a) \ + M_INIT_WITH_VAI23_FUNC(M_PAIR_1 pair, M_PAIR_2 pair, a) + +/* We first push a raw new item, and then we get back its pointer using _back. + _back (contrary to _push_raw) has no side effect, and so is safe to be used + in a macro */ +#define M_INIT_WITH_VAI23_FUNC(d, op, a) \ + ( \ + (void) M_C(M_GET_NAME op, _push_raw)(d), \ + M_IF(M_PARENTHESIS_P(a)) \ + (M_CALL_INIT_WITH, M_CALL_INIT_SET) \ + (M_GET_OPLIST op, *M_C(M_GET_NAME op, _back)(d), M_REMOVE_PARENTHESIS(a)) \ + ) + + +/* Initialize the container 'dest' as per 'oplist' INIT operator + and fill it with the given VA_ARGS arguments with the _emplace method + if the given argument is between parenthesis, otherwise an _init_set + is performed. + NOTE: Parenthesis detection is the best we can do to detect a special + argument. The argument may start with '*' or a '+' and we cannot use + M_KEYWORD_P with such arguments (as it cannot be concatened). + NOTE: If the REVERSE operator exists, it assumes a list, + so it reverses the final order. +*/ +#define M_INIT_EMPLACE_VAI(oplist, dest, ...) \ + (void)(M_GET_INIT oplist (dest) , \ + M_MAP2_C(M_INIT_EMPLACE_VAI2_FUNC, (dest, oplist) , __VA_ARGS__) \ + M_IF_METHOD(REVERSE, oplist)(M_DEFERRED_COMMA M_GET_REVERSE oplist(dest), ) \ + ) +#define M_INIT_EMPLACE_VAI2_FUNC(pair, a) \ + M_INIT_EMPLACE_VAI3_FUNC(M_PAIR_1 pair, M_PAIR_2 pair, a) +#define M_INIT_EMPLACE_VAI3_FUNC(d, op, a) \ + ( \ + M_IF(M_PARENTHESIS_P(a)) \ + (M_C(M_GET_NAME op, _emplace)(d, M_REMOVE_PARENTHESIS_3 a), \ + M_CALL_PUSH(op, d, a)) \ + ) + + +/* Initialize the container 'dest' as per 'oplist' INIT operator + and fill it with the given VA argument + assumed to be pair (key,value) with the SET_KEY operator. + If key (resp. value) is itself in the form ( ) and the + oplist defined an EMPLACE_TYPE operator, performs an emplace insertion + by construction an emplace call with no suffix. +*/ +#define M_INIT_KEY_VAI(oplist, dest, ...) \ + (void)(M_GET_INIT oplist (dest) , \ + M_MAP2_C(M_INIT_KEY_VAI_FUNC_P1, (dest, oplist) , __VA_ARGS__)) +#define M_INIT_KEY_VAI_FUNC_P1(dst_op, a) \ + M_INIT_KEY_VAI_FUNC_P2(M_PAIR_1 dst_op, M_PAIR_2 dst_op, M_PAIR_1 a, M_PAIR_2 a) +#define M_INIT_KEY_VAI_FUNC_P2(dst, oplist, key, value) \ + M_IF(M_AND(M_PARENTHESIS_P(key), M_TEST_METHOD_P(EMPLACE_TYPE, M_GET_KEY_OPLIST oplist))) \ + (M_INIT_KEY_VAI_FUNC_P2Y, M_INIT_KEY_VAI_FUNC_P2N)(dst, oplist, key, value) +#define M_INIT_KEY_VAI_FUNC_P2Y(dst, oplist, key, value) \ + M_IF(M_AND(M_PARENTHESIS_P(value), M_TEST_METHOD_P(EMPLACE_TYPE, M_GET_VALUE_OPLIST oplist))) \ + (M_INIT_KEY_VAI_FUNC_P2YY, M_INIT_KEY_VAI_FUNC_P2YN)(dst, oplist, key, value) +#define M_INIT_KEY_VAI_FUNC_P2N(dst, oplist, key, value) \ + M_IF(M_AND(M_PARENTHESIS_P(value), M_TEST_METHOD_P(EMPLACE_TYPE, M_GET_VALUE_OPLIST oplist))) \ + (M_INIT_KEY_VAI_FUNC_P2NY, M_INIT_KEY_VAI_FUNC_P2NN)(dst, oplist, key, value) +#define M_INIT_KEY_VAI_FUNC_P2YY(dst, oplist, key, value) \ + M_C(M_GET_NAME oplist, _emplace_key_val) \ + (dst, M_INIT_KEY_VAI_ID key, M_INIT_KEY_VAI_ID value) +#define M_INIT_KEY_VAI_FUNC_P2YN(dst, oplist, key, value) \ + M_C(M_GET_NAME oplist, _emplace_key)(dst, M_INIT_KEY_VAI_ID key, value) +#define M_INIT_KEY_VAI_FUNC_P2NY(dst, oplist, key, value) \ + M_C(M_GET_NAME oplist, _emplace_val)(dst, key, M_INIT_KEY_VAI_ID value) +#define M_INIT_KEY_VAI_FUNC_P2NN(dst, oplist, key, value) \ + M_CALL_SET_KEY(oplist, dst, key, value) +#define M_INIT_KEY_VAI_ID(a) a + + +/* Provide an INIT_WITH method that uses the EMPLACE_TYPE definition + by performing a _Generic selection on the first given argument + for all selections that match the number of arguments given to INIT_WITH, + and uses the provided init_func functions for constructing the variable. + Needs to be defined in the OPLIST with API_1 + Only LIST based emplace_type supported as we need a dedicated init_func + FIXME: M_MAP2 allowed in INIT_WITH? (Seems working) +*/ +#define M_INIT_WITH_THROUGH_EMPLACE_TYPE(oplist, dst, ...) \ + M_IF(M_TEST_METHOD_P(EMPLACE_TYPE, oplist)) \ + (M_INIT_WITH_EMPLACE_TYPE_2, M_INIT_WITH_NOT_AVAILABLE) \ + (oplist, dst, __VA_ARGS__) +#define M_INIT_WITH_NOT_AVAILABLE(oplist, dst, ...) \ + M_STATIC_ASSERT(false, M_LIB_ILLEGAL_EMPLACE_TYPE, \ + "Cannot INIT_WITH through EMPLACE_TYPE, such operator is not provided in the given oplist: " #oplist) +#define M_INIT_WITH_EMPLACE_TYPE_2(oplist, dst, ...) \ + M_INIT_WITH_EMPLACE_TYPE_3(M_GET_EMPLACE_TYPE oplist, oplist, dst, __VA_ARGS__) +#define M_INIT_WITH_EMPLACE_TYPE_3(emplace_type, oplist, dst, ...) \ + M_IF(M_KEYWORD_P(LIST,emplace_type)) \ + (M_INIT_WITH_EMPLACE_TYPE_4, M_INIT_WITH_EMPLACE_TYPE_FAIL) \ + (emplace_type, oplist, dst, __VA_ARGS__) +#define M_INIT_WITH_EMPLACE_TYPE_FAIL(emplace_type, oplist, dst, ...) \ + M_STATIC_ASSERT(false, M_LIB_ILLEGAL_EMPLACE_TYPE, \ + "Only LIST based EMPLACE_TYPE with explicit init_func (No INIT_WITH) are supported") +#define M_INIT_WITH_EMPLACE_TYPE_4(emplace_type, oplist, dst, ...) \ + _Generic( M_RET_ARG1( __VA_ARGS__, ) \ + M_MAP2(M_INIT_WITH_EMPLACE_APPLY, \ + (oplist, dst, M_ADD(2, M_NARGS(__VA_ARGS__)), __VA_ARGS__), \ + M_KEYWORD_TO_VA_ARGS(LIST, emplace_type)) \ + , default: M_STATIC_ASSERT(M_INIT_WITH_EMPLACE_LIST_ASSERT_COND(emplace_type, __VA_ARGS__), M_LIB_ILLEGAL_EMPLACE_TYPE, \ + "Cannot find any matching constructor for the type of " \ + M_AS_STR (M_RET_ARG1( __VA_ARGS__, ) \ + " with " M_AS_STR(M_NARGS(__VA_ARGS__)) " arguments in " M_AS_STR(emplace_type) )) \ + ) +/* arglist is (oplist, dest, NARGS, args...) emplace_type is (suffix, init_func, types...) */ +#define M_INIT_WITH_EMPLACE_APPLY(arglist, emplace_type) \ + M_IF(M_EQUAL(M_RET_ARG3 arglist, M_NARGS emplace_type)) \ + (M_INIT_WITH_EMPLACE_APPLY_2, M_EAT)(arglist, emplace_type) +#define M_INIT_WITH_EMPLACE_APPLY_2(arglist, emplace_type) \ + , M_RET_ARG3 emplace_type : M_APPLY_API(M_RET_ARG2 emplace_type, M_RET_ARG1 arglist, M_RET_ARG2 arglist M_INIT_WITH_EMPLACE_LIST_VAR((M_TAIL_3 arglist), emplace_type) ) +#define M_INIT_WITH_EMPLACE_LIST_VAR(arglist, emplace_type) \ + M_MAP3( M_INIT_WITH_EMPLACE_LIST_VAR_MULTI_FUNC, emplace_type, \ + M_INIT_WITH_EMPLACE_LIST_VAR_ID arglist) +#define M_INIT_WITH_EMPLACE_LIST_VAR_MULTI_FUNC(emplace_type, num, arg) \ + , M_AS_TYPE(M_RET_ARG(M_ADD(num, 2), M_INIT_WITH_EMPLACE_LIST_VAR_ID emplace_type), arg) +#define M_INIT_WITH_EMPLACE_LIST_VAR_ID(...) __VA_ARGS__ /* M_ID macro */ +// Workaround to provide a nice static failure at compile time if we cannot find a matching type. +#define M_INIT_WITH_EMPLACE_LIST_ASSERT_COND(emplace_type, ...) \ + _Generic( M_RET_ARG1( __VA_ARGS__, ) \ + M_MAP2(M_INIT_WITH_EMPLACE_LIST_ASSERT_COND_APPLY, M_ADD(2, M_NARGS(__VA_ARGS__)), \ + M_KEYWORD_TO_VA_ARGS(LIST, emplace_type)) \ + , default: 0 ) +#define M_INIT_WITH_EMPLACE_LIST_ASSERT_COND_APPLY(args, emplace_type) \ + M_IF(M_EQUAL(args, M_NARGS emplace_type)) \ + (M_INIT_WITH_EMPLACE_LIST_ASSERT_COND_APPLY_2, M_EAT)(args, emplace_type) +#define M_INIT_WITH_EMPLACE_LIST_ASSERT_COND_APPLY_2(args, emplace_type) \ + , M_RET_ARG3 emplace_type : 1 + + + +/************************************************************/ +/******************* EMPLACE GENERATION ********************/ +/************************************************************/ + +/* Expand to the list of emplace type with their variable name + to use in the function declaration (start with a ,) */ +#define M_EMPLACE_LIST_TYPE_VAR(prefix, emplace_type) \ + M_IF(M_PARENTHESIS_P( emplace_type ))(M_EMPLACE_LIST_TYPE_VAR_MULTI, M_EMPLACE_LIST_TYPE_VAR_SINGLE)(prefix, emplace_type) +#define M_EMPLACE_LIST_TYPE_VAR_MULTI(prefix, emplace_type) \ + M_MAP3(M_EMPLACE_LIST_TYPE_VAR_MULTI_F, prefix, M_ID emplace_type) +#define M_EMPLACE_LIST_TYPE_VAR_MULTI_F(prefix, num, type) \ + , type const M_C(prefix, num) +#define M_EMPLACE_LIST_TYPE_VAR_SINGLE(prefix, emplace_type) \ + , emplace_type const prefix + + +/* Expand to the list of variable name based on the the declaration with + emplace type to use in the INIT_WITH or specific method call (start with a ,) */ +#define M_EMPLACE_LIST_VAR(prefix, emplace_type) \ + M_IF(M_PARENTHESIS_P( emplace_type ))(M_EMPLACE_LIST_VAR_MULTI, M_EMPLACE_LIST_VAR_SINGLE)(prefix, emplace_type) +#define M_EMPLACE_LIST_VAR_MULTI(prefix, emplace_type) \ + , M_MAP2_C( M_C, prefix, M_SEQ(1, M_NARGS emplace_type) ) +#define M_EMPLACE_LIST_VAR_SINGLE(prefix, emplace_type) \ + , prefix + + +/* Call either INIT_WITH with the prefixed arguments used for emplace + if init_func is the keyword INIT_WITH + or with the user provided init_func (otherwise) */ +#define M_EMPLACE_CALL_FUNC(prefix, init_func, oplist, dest, emplace_type) \ + M_IF(M_KEYWORD_P(INIT_WITH, init_func)) \ + (M_EMPLACE_CALL_FUNC_INIT_WITH, M_EMPLACE_CALL_FUNC_USER_PROVIDED) \ + (prefix, init_func, oplist, dest, emplace_type) +#define M_EMPLACE_CALL_FUNC_INIT_WITH(prefix, init_func, oplist, dest, emplace_type) \ + /* If INIT_FUNC==INIT_WITH, classic use of INIT_WITH operator */ \ + M_CALL_INIT_WITH(oplist, dest M_EMPLACE_LIST_VAR(prefix, emplace_type) ) +#define M_EMPLACE_CALL_FUNC_USER_PROVIDED(prefix, init_func, oplist, dest, emplace_type) \ + /* Use the user provided init_func instead (with API transformation) */ \ + M_APPLY_API(init_func, oplist, dest M_EMPLACE_LIST_VAR(prefix, emplace_type) ) + + +/* Generate one or several definitions for the EMPLACE methods + using the operator EMPLACE_TYPE to get the suitable user types + and the user provided macro to expand the function definition. + The user provided a macro which prototype is + macro(name, name_t, function_name, oplist, init_func, exp_emplace_type) + It is suitable for container with a single type of data within. + NOTE: Use internally, M_MAP2, M_MAP2_C & M_MAP3 + */ +#define M_EMPLACE_QUEUE_DEF(name, name_t, function_name, oplist, macro) \ + M_ID( \ + M_IF(M_TEST_METHOD_ALTER_P(EMPLACE_TYPE, oplist)) \ + (M_EMPLACE_QUEUE_DEF_EXPAND, M_EAT) \ + (name, name_t, function_name, oplist, macro, M_GET_EMPLACE_TYPE oplist) \ + ) +/* One EMPLACE_TYPE operator is defined ==> Perform the emplace function expansion. + If the emplace type starts with parenthesis, it is only one with multiple args */ +#define M_EMPLACE_QUEUE_DEF_EXPAND(name, name_t, function_name, oplist, macro, emplace_type) \ + M_IF(M_PARENTHESIS_P(emplace_type)) \ + (M_EMPLACE_QUEUE_DEF_SINGLE_EXPAND, M_EMPLACE_QUEUE_DEF_EXPAND_P2) \ + (name, name_t, function_name, oplist, macro, emplace_type) +/* Test if it starts with LIST, in which case, multiple emplace methods have to be defined */ +#define M_EMPLACE_QUEUE_DEF_EXPAND_P2(name, name_t, function_name, oplist, macro, emplace_type) \ + M_IF(M_KEYWORD_P(LIST, emplace_type)) \ + (M_EMPLACE_QUEUE_DEF_LIST_EXPAND, M_EMPLACE_QUEUE_DEF_SINGLE_EXPAND) \ + (name, name_t, function_name, oplist, macro, emplace_type) +/* Define & expand multiple emplace methods */ +#define M_EMPLACE_QUEUE_DEF_LIST_EXPAND(name, name_t, function_name, oplist, macro, emplace_type) \ + M_MAP2(M_EMPLACE_QUEUE_DEF_SUFFIX, (name, name_t, function_name, oplist, macro), M_KEYWORD_TO_VA_ARGS(LIST, emplace_type)) +/* Let the arguments be expanded before calling the definition macro */ +#define M_EMPLACE_QUEUE_DEF_SUFFIX(trio, list) M_EMPLACE_QUEUE_DEF_SUFFIX2(M_ID trio, M_ID list) +/* Let the arguments be expanded before calling the definition macro */ +#define M_EMPLACE_QUEUE_DEF_SUFFIX2(...) M_EMPLACE_QUEUE_DEF_SUFFIX3(__VA_ARGS__) +/* Everything is now proprely expanded. Just call the original macro (with a properly expanded emplace type) */ +#define M_EMPLACE_QUEUE_DEF_SUFFIX3(name, name_t, function_name, oplist, macro, sup_suffix, init_func, ...) \ + macro(name, name_t, M_C_EMPTY(function_name, sup_suffix), oplist, init_func, (__VA_ARGS__) ) +/* Define & expand one emplace method by using the definition macro */ +#define M_EMPLACE_QUEUE_DEF_SINGLE_EXPAND(name, name_t, function_name, oplist, macro, emplace_type) \ + macro(name, name_t, function_name, oplist, INIT_WITH, emplace_type) + + +/* Generate one or several definitions for the EMPLACE methods + using the operator EMPLACE_TYPE to get the suitable user types + and the user provided macro to expand the function definition. + The user provided three macros which prototype are + macro(name, name_t, function_name, key_oplist, val_oplist, key_init_func, val_init_func, key_emplace_type, val_emplace_type) + first macro is for generating emplacing with both key & value are emplaced, + second macro is for generating emplacing with only key is emplaced, + third macro is for generating emplacing with only value is emplaced, + It is suitable for associative array containers. + NOTE: Use internally, M_MAP2, M_MAP2_C & M_MAP3 + */ +#define M_EMPLACE_ASS_ARRAY_DEF(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val) \ + M_IF(M_TEST_METHOD_ALTER_P(EMPLACE_TYPE, key_oplist)) \ + (M_EMPLACE_ASS_ARRAY_DEF_EXPAND_KEY_OR_BOTH, M_EMPLACE_ASS_ARRAY_DEF_EXPAND_VALUE_OR_NONE) \ + (name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val) +/* We have a key EMPLACE_TYPE. We can generate key only or both emplace functions */ +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_KEY_OR_BOTH(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val) \ + M_IF(M_TEST_METHOD_ALTER_P(EMPLACE_TYPE, val_oplist)) \ + (M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH, M_EMPLACE_ASS_ARRAY_DEF_EXPAND_KEY) \ + (name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, M_GET_EMPLACE_TYPE key_oplist, M_GET_EMPLACE_TYPE val_oplist) +/* We have a value EMPLACE_TYPE. We can generate value only or no emplace functions */ +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_VALUE_OR_NONE(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val) \ + M_IF(M_TEST_METHOD_ALTER_P(EMPLACE_TYPE, val_oplist)) \ + (M_EMPLACE_ASS_ARRAY_DEF_EXPAND_VAL, M_EAT) \ + (name, name_t, function_name, key_oplist, val_oplist, macro_val, macro_key, macro_val, M_GET_EMPLACE_TYPE key_oplist, M_GET_EMPLACE_TYPE val_oplist) + +/* Generate KEY only emplace functions */ +/* One EMPLACE_TYPE operator is defined ==> Perform the emplace function expansion. + If the emplace type starts with parenthesis, it is only one with multiple args */ +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_KEY(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_IF(M_PARENTHESIS_P(key_emp_type)) \ + (M_EMPLACE_ASS_ARRAY_DEF_SINGLE_EXPAND_KEY, M_EMPLACE_ASS_ARRAY_DEF_EXPAND_P2_KEY) \ + (name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) +/* Test if it starts with LIST, in which case, multiple emplace methods have to be defined */ +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_P2_KEY(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_IF(M_KEYWORD_P(LIST, key_emp_type)) \ + (M_EMPLACE_ASS_ARRAY_DEF_LIST_EXPAND_KEY, M_EMPLACE_ASS_ARRAY_DEF_SINGLE_EXPAND_KEY) \ + (name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) +/* Define & expand multiple emplace methods with the _key prefix */ +#define M_EMPLACE_ASS_ARRAY_DEF_LIST_EXPAND_KEY(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_MAP2(M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_KEY, (name, name_t, function_name, key_oplist, val_oplist, macro_key, key_emp_type, val_emp_type), M_KEYWORD_TO_VA_ARGS(LIST, key_emp_type)) +/* Let the arguments be expanded before calling the definition macro */ +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_KEY(trio, list) M_EMPLACE_ASS_ARRAY_DEF_SUFFIX2_KEY(M_ID trio, M_ID list) +/* Let the arguments be expanded before calling the definition macro */ +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX2_KEY(...) M_EMPLACE_ASS_ARRAY_DEF_SUFFIX3_KEY(__VA_ARGS__) +/* Everything is now proprely expanded. Just call the original macro (with a properly expanded emplace type) */ +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX3_KEY(name, name_t, function_name, key_oplist, val_oplist, macro, key_emp_type, val_emp_type, sup_suffix, init_func, ...) \ + macro(name, name_t, M_C3_EMPTY(function_name, _key, sup_suffix), key_oplist, val_oplist, init_func, val_init_func, (__VA_ARGS__), val_list ) +/* Define & expand one emplace method by using the definition macro */ +#define M_EMPLACE_ASS_ARRAY_DEF_SINGLE_EXPAND_KEY(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + macro_key(name, name_t, M_C(function_name, _key), key_oplist, val_oplist, INIT_WITH, none, key_emp_type, val_emp_type) + +/* Generate VALUE only emplace functions */ +/* One EMPLACE_TYPE operator is defined ==> Perform the emplace function expansion. + If the emplace type starts with parenthesis, it is only one with multiple args */ +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_VAL(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_IF(M_PARENTHESIS_P(val_emp_type)) \ + (M_EMPLACE_ASS_ARRAY_DEF_SINGLE_EXPAND_VAL, M_EMPLACE_ASS_ARRAY_DEF_EXPAND_P2_VAL) \ + (name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) +/* Test if it starts with LIST, in which case, multiple emplace methods have to be defined */ +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_P2_VAL(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_IF(M_KEYWORD_P(LIST, val_emp_type)) \ + (M_EMPLACE_ASS_ARRAY_DEF_LIST_EXPAND_VAL, M_EMPLACE_ASS_ARRAY_DEF_SINGLE_EXPAND_VAL) \ + (name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) +/* Define & expand multiple emplace methods with the _val prefix */ +#define M_EMPLACE_ASS_ARRAY_DEF_LIST_EXPAND_VAL(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_MAP2(M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_VAL, (name, name_t, function_name, key_oplist, val_oplist, macro_val, key_emp_type, val_emp_type), M_KEYWORD_TO_VA_ARGS(LIST, val_emp_type)) +/* Let the arguments be expanded before calling the definition macro */ +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_VAL(trio, list) M_EMPLACE_ASS_ARRAY_DEF_SUFFIX2_VAL(M_ID trio, M_ID list) +/* Let the arguments be expanded before calling the definition macro */ +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX2_VAL(...) M_EMPLACE_ASS_ARRAY_DEF_SUFFIX3_VAL(__VA_ARGS__) +/* Everything is now proprely expanded. Just call the original macro (with a properly expanded emplace type) */ +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX3_VAL(name, name_t, function_name, key_oplist, val_oplist, macro, key_emp_type, val_emp_type, sup_suffix, init_func, ...) \ + macro(name, name_t, M_C3_EMPTY(function_name, _val, sup_suffix), key_oplist, val_oplist, key_init_func, init_func, val_list, (__VA_ARGS__) ) +/* Define & expand one emplace method by using the definition macro */ +#define M_EMPLACE_ASS_ARRAY_DEF_SINGLE_EXPAND_VAL(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + macro_val(name, name_t, M_C(function_name, _val), key_oplist, val_oplist, none, INIT_WITH, key_emp_type, val_emp_type) + +/* Generate KEY, VALUE & BOTH emplace functions */ +/* One EMPLACE_TYPE operator is defined ==> Perform the emplace function expansion. + If the emplace type starts with parenthesis, it is only one with multiple args */ +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_EMPLACE_ASS_ARRAY_DEF_EXPAND_KEY(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_EMPLACE_ASS_ARRAY_DEF_EXPAND_VAL(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_B(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE(key_emp_type), M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE(val_emp_type) ) +/* Transform all supported user forms to the generic LIST form */ +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE(val_emp) \ + M_IF(M_PARENTHESIS_P(val_emp))(M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE_PARENTHESIS, M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE_B)(val_emp) +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE_B(val_emp) \ + M_IF(M_KEYWORD_P(LIST, val_emp))(M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE_LIST, M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE_SINGLE)(val_emp) +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE_PARENTHESIS(val_emp) LIST( ( , INIT_WITH, M_ID val_emp) ) +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE_SINGLE(val_emp) LIST( ( , INIT_WITH, val_emp) ) +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_EMP_TYPE_LIST(val_emp) val_emp +#define M_EMPLACE_ASS_ARRAY_DEF_EXPAND_BOTH_B(name, name_t, function_name, key_oplist, val_oplist, macro_both, macro_key, macro_val, key_emp_type, val_emp_type) \ + M_CROSS_MAP2(M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_BOTH, (name, name_t, function_name, key_oplist, val_oplist, macro_both, key_emp_type, val_emp_type), ( M_KEYWORD_TO_VA_ARGS(LIST, key_emp_type) ), ( M_KEYWORD_TO_VA_ARGS(LIST, val_emp_type) ) ) +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_BOTH(d, a, b) M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_BOTH_B(M_ID d, a, b) +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_BOTH_B(...) M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_BOTH_C(__VA_ARGS__) +#define M_EMPLACE_ASS_ARRAY_DEF_SUFFIX_BOTH_C(name, name_t, function_name, key_oplist, val_oplist, macro_both, key_emp_type, val_emp_type, a, b) \ + macro_both(name, name_t, M_C5_EMPTY(function_name, _key, M_RET_ARG1(M_ID a), _val, M_RET_ARG1(M_ID b) ), key_oplist, val_oplist, M_RET_ARG2(M_ID a), M_RET_ARG2(M_ID b), (M_TAIL_2 a), (M_TAIL_2 b) ) + + +/* Quick call to emplace def generation which transform into an + associative array emplace generation or queue generation + depending on the parameter isSet (= 0 or 1) */ +#define M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF(isSet, name, name_t, key_oplist, val_oplist) \ + M_ID( M_C(M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF, isSet)(name, name_t, key_oplist, val_oplist) ) +#define M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF0(name, name_t, key_oplist, val_oplist) \ + M_EMPLACE_ASS_ARRAY_DEF(name, name_t, M_F(name, _emplace), key_oplist, val_oplist, M_EMPLACE_ASS_ARRA1_BOTH_GENE, M_EMPLACE_ASS_ARRA1_KEY_GENE, M_EMPLACE_ASS_ARRA1_VAL_GENE) \ + M_EMPLACE_QUEUE_DEF(name, name_t, M_F(name, _get_emplace), key_oplist, M_EMPLACE_GET_GENE) +#define M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF1(name, name_t, key_oplist, val_oplist) \ + M_EMPLACE_QUEUE_DEF(name, name_t, M_F(name, _emplace), key_oplist, M_EMPLACE_QUEUE_GENE) \ + M_EMPLACE_QUEUE_DEF(name, name_t, M_F(name, _get_emplace), key_oplist, M_EMPLACE_GET_GENE) + + +/* Definition of the emplace_back function for associative arrays. + It is defined here so that this definition is shared accross different + kind of associative array (RB-Tree, Hashmap, OA-Hashmap, B+Tree, ...) + This definition is far from being efficient but works for the current interface. + 3 macros are needed: one when we emplace both key & value, one for key only + one for value only. +*/ +#define M_EMPLACE_ASS_ARRA1_BOTH_GENE(name, name_t, function_name, key_oplist, val_oplist, key_init_func, val_init_func, key_emplace_type, val_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(akey, key_emplace_type) \ + M_EMPLACE_LIST_TYPE_VAR(aval, val_emplace_type) \ + ){ \ + M_F(name, _key_ct) key; \ + M_EMPLACE_CALL_FUNC(akey, key_init_func, key_oplist, key, key_emplace_type); \ + M_F(name, _value_ct) *val; \ + val = M_F(name, _safe_get)(v, key); \ + M_CALL_CLEAR(val_oplist, *val); \ + M_EMPLACE_CALL_FUNC(aval, val_init_func, val_oplist, *val, val_emplace_type); \ + M_CALL_CLEAR(key_oplist, key); \ + } \ + +#define M_EMPLACE_ASS_ARRA1_KEY_GENE(name, name_t, function_name, key_oplist, val_oplist, key_init_func, val_init_func, key_emplace_type, val_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(akey, key_emplace_type) \ + , M_F(name, _value_ct) const val \ + ){ \ + M_F(name, _key_ct) key; \ + M_EMPLACE_CALL_FUNC(akey, key_init_func, key_oplist, key, key_emplace_type); \ + M_F(name, _set_at)(v, key, val); \ + M_CALL_CLEAR(key_oplist, key); \ + } \ + +#define M_EMPLACE_ASS_ARRA1_VAL_GENE(name, name_t, function_name, key_oplist, val_oplist, key_init_func, val_init_func, key_emplace_type, val_emplace_type) \ + M_INLINE void \ + function_name(name_t v, M_F(name, _key_ct) const key \ + M_EMPLACE_LIST_TYPE_VAR(aval, val_emplace_type) \ + ){ \ + M_F(name, _value_ct) *val; \ + val = M_F(name, _safe_get)(v, key); \ + M_CALL_CLEAR(val_oplist, *val); \ + M_EMPLACE_CALL_FUNC(aval, val_init_func, val_oplist, *val, val_emplace_type); \ + } \ + +/* Definition of the emplace_back function for queue. + It is defined here so that this definition is shared accross different + kind of kind of queue. + This definition is far from being efficient but works for the current interface. +*/ +#define M_EMPLACE_QUEUE_GENE(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_GET_TYPE oplist data; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, data, exp_emplace_type); \ + M_F(name, _push)(v, data); \ + M_CALL_CLEAR(oplist, data); \ + } + +/* Definition of the get_emplace function for set / map. + It is defined here so that this definition is shared accross different + kind of kind of queue. + This definition is far from being efficient but works for the current interface. +*/ +#define M_EMPLACE_GET_GENE(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE M_F(name, _value_ct) * \ + function_name(name_t const v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_GET_TYPE oplist data; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, data, exp_emplace_type); \ + M_F(name, _value_ct) *ret = M_F(name, _get)(v, data); \ + M_CALL_CLEAR(oplist, data); \ + return ret; \ + } + + +/************************************************************/ +/******************* Exponential Backoff ********************/ +/************************************************************/ + +/* Can be increased / decreased by user code if needed + to increase / decrease backoff of code */ +#ifndef M_USE_BACKOFF_MAX_COUNT +#define M_USE_BACKOFF_MAX_COUNT 6 +#endif + +/* Exponential backoff object. + * An exponential backoff object will increase its wait time + * each time it is called with a pseudo random wait. + * It is used to avoid too many threads trying to grab some atomic + * variables at the same times (it makes the threads waits randomly). + */ +typedef struct m_core_backoff_s { + unsigned int count; // Number of times it has been run + unsigned int seed; // Initial seed +} m_core_backoff_ct[1]; + +/* Initialize a backoff object. + * Use the C function rand to initialize its internal seed. + * It should be good enough for the purpose of the backoff */ +M_INLINE void +m_core_backoff_init(m_core_backoff_ct backoff) +{ + backoff->count = 0; + backoff->seed = (unsigned int) rand(); +} + +/* Reset the count of the backoff object */ +M_INLINE void +m_core_backoff_reset(m_core_backoff_ct backoff) +{ + backoff->count = 0; +} + +/* Wait a few nanoseconds using an active loop, + * generating a random number of nanosecond to wait, + * and increment the number of times wait has been called */ +M_INLINE void +m_core_backoff_wait(m_core_backoff_ct backoff) +{ + /* x is qualified as volatile to avoid being optimized away + by the compiler in the active sleep loop */ + volatile int x = 0; + /* Cheap but fast pseudo random. */ + backoff->seed = backoff->seed * 34721 + 17449; + const unsigned int mask = (1U << backoff->count) -1; + const unsigned int count = mask & (backoff->seed >> 8); + /* Active sleep for 'count' times */ + for(unsigned int i = 0; i <= count; i++) + x = 0; + (void) x; + /* Increment count for next step if needed */ + backoff->count += (backoff->count < M_USE_BACKOFF_MAX_COUNT); +} + +/* Clear the backoff object */ +M_INLINE void +m_core_backoff_clear(m_core_backoff_ct backoff) +{ + // Nothing to do + (void) backoff; +} + + + +/************************************************************/ +/********************** Serialization ***********************/ +/************************************************************/ + +/* Forward declaration of m_string_t defined in m-string.h */ +struct m_string_s; + +/* Serialization Return code: + * - OK & done (object is fully parsed), + * - OK & continue parsing (object is partially parsed) + * - Fail parsing + * - Fail parsing with given arguments (give explicit size) + */ +typedef enum m_serial_return_code_e { + M_SERIAL_OK_DONE = 0, M_SERIAL_OK_CONTINUE = 1, M_SERIAL_FAIL = 2, M_SERIAL_FAIL_RETRY = 4 +} m_serial_return_code_t; + +#if defined(__cplusplus) +// C++ doesn't allow mixing enum and integer, so we provided overloaded operators. +inline void operator|(m_serial_return_code_t &a, m_serial_return_code_t b) +{ + a = static_cast(static_cast(a) | static_cast(b)); +} +inline m_serial_return_code_t operator|=(m_serial_return_code_t a, m_serial_return_code_t b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} +inline m_serial_return_code_t operator&(m_serial_return_code_t a, m_serial_return_code_t b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} +#endif + +/* Maximum data size of a serializator structure + * Can be overloaded by user */ +#ifndef M_USE_SERIAL_MAX_DATA_SIZE +#define M_USE_SERIAL_MAX_DATA_SIZE 4 +#endif + +/* Different types of types that can be stored in a serial object to represent it: + * a boolean + * different kind of integers + * different kind of floats + * a size + * a pointer to something. + * a serial return code + */ +typedef union m_serial_ll_u { + bool b; + char c; + int i; + long l; + long long ll; + float f; + double d; + long double e; + size_t s; + uintptr_t u; + void *p; + const char *cstr; + m_serial_return_code_t r; +} m_serial_ll_ct; + +/* Object to handle the construction of a serial write/read of an object + that needs multiple calls (array, map, ...) + It is common to all calls to the same object. + It shall be used as a local state of the object being parsed */ +typedef struct m_serial_local_s { + m_serial_ll_ct data[M_USE_SERIAL_MAX_DATA_SIZE]; +} m_serial_local_t[1]; + +/* Object to handle the generic serial read of an object: + * - m_interface is the pointer to the constant interface object that has all callbacks + * - tmp is temporary variable used localy by the non recursive serializer (not to be used by serializer) + * - data is user defined data to use by the serialization object as it wants + * NOTE: 'interface' word cannot be used as a field name as some system headers + * define it as a macro. + * */ +typedef struct m_serial_read_s { + const struct m_serial_read_interface_s *m_interface; + m_serial_ll_ct tmp; + m_serial_ll_ct data[M_USE_SERIAL_MAX_DATA_SIZE]; +} m_serial_read_t[1]; + +/* Interface that has to be exported by the serial read object. + * All function pointers shall be not null. + */ +typedef struct m_serial_read_interface_s { + m_serial_return_code_t (*read_boolean)(m_serial_read_t,bool *); + m_serial_return_code_t (*read_integer)(m_serial_read_t, long long *, const size_t size_of_type); + m_serial_return_code_t (*read_float)(m_serial_read_t, long double *, const size_t size_of_type); + m_serial_return_code_t (*read_string)(m_serial_read_t, struct m_string_s *); + m_serial_return_code_t (*read_array_start)(m_serial_local_t, m_serial_read_t, size_t *); + m_serial_return_code_t (*read_array_next)(m_serial_local_t, m_serial_read_t); + m_serial_return_code_t (*read_map_start)(m_serial_local_t, m_serial_read_t, size_t *); + m_serial_return_code_t (*read_map_value)(m_serial_local_t, m_serial_read_t); + m_serial_return_code_t (*read_map_next)(m_serial_local_t, m_serial_read_t); + m_serial_return_code_t (*read_tuple_start)(m_serial_local_t, m_serial_read_t); + m_serial_return_code_t (*read_tuple_id)(m_serial_local_t, m_serial_read_t, const char *const field_name [], const int max, int *); + m_serial_return_code_t (*read_variant_start)(m_serial_local_t, m_serial_read_t, const char *const field_name[], const int max, int*); + m_serial_return_code_t (*read_variant_end)(m_serial_local_t, m_serial_read_t); +} m_serial_read_interface_t; + + +/* Object to handle the generic serial write of an object: + * - m_interface is the pointer to the constant interface object that has all callbacks + * - tmp is temporary variable used localy by the non recursive serializer + * - data is user defined data to use by the serialization object as it wants + * NOTE: 'interface' word cannot be used as a field name as some system headers + * define it as a macro. + * */ +typedef struct m_serial_write_s { + const struct m_serial_write_interface_s *m_interface; + m_serial_ll_ct tmp; + m_serial_ll_ct data[M_USE_SERIAL_MAX_DATA_SIZE]; +} m_serial_write_t[1]; + +/* Interface that has to be exported by the serial write object. + * All function pointers shall be not null. + */ +typedef struct m_serial_write_interface_s { + m_serial_return_code_t (*write_boolean)(m_serial_write_t,const bool data); + m_serial_return_code_t (*write_integer)(m_serial_write_t,const long long data, const size_t size_of_type); + m_serial_return_code_t (*write_float)(m_serial_write_t, const long double data, const size_t size_of_type); + m_serial_return_code_t (*write_string)(m_serial_write_t,const char data[], size_t len); + m_serial_return_code_t (*write_array_start)(m_serial_local_t, m_serial_write_t, const size_t number_of_elements); + m_serial_return_code_t (*write_array_next)(m_serial_local_t, m_serial_write_t); + m_serial_return_code_t (*write_array_end)(m_serial_local_t, m_serial_write_t); + m_serial_return_code_t (*write_map_start)(m_serial_local_t, m_serial_write_t, const size_t number_of_elements); + m_serial_return_code_t (*write_map_value)(m_serial_local_t, m_serial_write_t); + m_serial_return_code_t (*write_map_next)(m_serial_local_t, m_serial_write_t); + m_serial_return_code_t (*write_map_end)(m_serial_local_t, m_serial_write_t); + m_serial_return_code_t (*write_tuple_start)(m_serial_local_t, m_serial_write_t); + m_serial_return_code_t (*write_tuple_id)(m_serial_local_t, m_serial_write_t, const char * const field_name[], const int max, const int index); + m_serial_return_code_t (*write_tuple_end)(m_serial_local_t, m_serial_write_t); + m_serial_return_code_t (*write_variant_start)(m_serial_local_t, m_serial_write_t, const char * const field_name[], const int max, const int index); + m_serial_return_code_t (*write_variant_end)(m_serial_local_t, m_serial_write_t); +} m_serial_write_interface_t; + + +/* Convert a C default variale (bool, integer, float) to a Serialized data + * by calling the serializer interface associated to the type of the object. + * NOTE: Supports only C11. +*/ +#define M_OUT_SERIAL_DEFAULT_ARG(serial, x) \ + _Generic(((void)0,(x)), \ + bool: (serial)->m_interface->write_boolean(serial, M_AS_TYPE(bool, (x))), \ + char: (serial)->m_interface->write_integer(serial, M_AS_TYPE(char,(x)), sizeof (x)), \ + signed char: (serial)->m_interface->write_integer(serial, M_AS_TYPE(signed char,(x)), sizeof (x)), \ + unsigned char: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned char,(x)), sizeof (x)), \ + signed short: (serial)->m_interface->write_integer(serial, M_AS_TYPE(signed short,(x)), sizeof (x)), \ + unsigned short: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned short,(x)), sizeof (x)), \ + signed int: (serial)->m_interface->write_integer(serial, M_AS_TYPE(signed int,(x)), sizeof (x)), \ + unsigned int: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned int,(x)), sizeof (x)), \ + long int: (serial)->m_interface->write_integer(serial, M_AS_TYPE(long,(x)), sizeof (x)), \ + unsigned long int: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned long,(x)), sizeof (x)), \ + long long int: (serial)->m_interface->write_integer(serial, M_AS_TYPE(long long,(x)), sizeof (x)), \ + unsigned long long int: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned long long,(x)), sizeof (x)), \ + float: (serial)->m_interface->write_float(serial, M_AS_TYPE(float,(x)), sizeof (x)), \ + double: (serial)->m_interface->write_float(serial, M_AS_TYPE(double,(x)), sizeof (x)), \ + long double: (serial)->m_interface->write_float(serial, M_AS_TYPE(long double,(x)), sizeof (x)), \ + const char *: (serial)->m_interface->write_string(serial, M_AS_TYPE(const char *,(x)), m_core_out_serial_strlen(M_AS_TYPE(const char *,(x))) ), \ + char *: (serial)->m_interface->write_string(serial, M_AS_TYPE(char *,(x)), m_core_out_serial_strlen(M_AS_TYPE(const char *,(x))) ), \ + const void *: M_SERIAL_FAIL /* unsupported */, \ + void *: M_SERIAL_FAIL /* unsupported */) + +/* Convert a Serialized data to a C default variale (bool, integer, float) + * by calling the serializer interface associated to the type of the object. + * NOTE: Supports only C11. +*/ +#define M_IN_SERIAL_DEFAULT_ARG(xptr, serial) \ + _Generic(((void)0,*(xptr)), \ + bool: (serial)->m_interface->read_boolean(serial, M_AS_TYPE(bool *, xptr)), \ + char: m_core_in_serial_char(serial, M_AS_TYPE(char*,xptr)), \ + signed char: m_core_in_serial_schar(serial, M_AS_TYPE(signed char*,xptr)), \ + unsigned char: m_core_in_serial_uchar(serial, M_AS_TYPE(unsigned char*,xptr)), \ + signed short: m_core_in_serial_sshort(serial, M_AS_TYPE(signed short*,xptr)), \ + unsigned short: m_core_in_serial_ushort(serial, M_AS_TYPE(unsigned short*,xptr)), \ + signed int: m_core_in_serial_sint(serial, M_AS_TYPE(signed int*,xptr)), \ + unsigned int: m_core_in_serial_uint(serial, M_AS_TYPE(unsigned int*,xptr)), \ + long int: m_core_in_serial_slong(serial, M_AS_TYPE(long*,xptr)), \ + unsigned long int: m_core_in_serial_ulong(serial, M_AS_TYPE(unsigned long*,xptr)), \ + long long int: m_core_in_serial_sllong(serial, M_AS_TYPE(long long*,xptr)), \ + unsigned long long int: m_core_in_serial_ullong(serial, M_AS_TYPE(unsigned long long*,xptr)), \ + float: m_core_in_serial_float(serial, M_AS_TYPE(float*,xptr)), \ + double: m_core_in_serial_double(serial, M_AS_TYPE(double*,xptr)), \ + long double: m_core_in_serial_ldouble(serial, M_AS_TYPE(long double*,xptr)), \ + const char *: M_SERIAL_FAIL /* unsupported (size unknown) */, \ + char *: M_SERIAL_FAIL /* unsupported (size unknown) */, \ + const void *: M_SERIAL_FAIL /* unsupported */, \ + void *: M_SERIAL_FAIL /* unsupported */) + +/* Helper functions for M_IN_SERIAL_DEFAULT_ARG + as we need to define a function per supported type in the generic expression */ +#define M_IN_SERIAL_DEFAULT_TYPE_DEF(name, type, func, promoted_type) \ + M_INLINE m_serial_return_code_t \ + name (m_serial_read_t serial, type *ptr) \ + { \ + promoted_type i; \ + m_serial_return_code_t r; \ + r = serial->m_interface->func(serial, &i, sizeof (type)); \ + *ptr = (type) i; \ + return r; \ + } + +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_char, char, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_schar, signed char, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_uchar, unsigned char, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_sshort, signed short, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_ushort, unsigned short, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_sint, signed int, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_uint, unsigned int, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_slong, signed long, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_ulong, unsigned long, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_sllong, signed long long, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_ullong, unsigned long long, read_integer, long long) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_float, float, read_float, long double) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_double, double, read_float, long double) +M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_ldouble, long double, read_float, long double) + +/* Helper function for M_ENUM_IN_SERIAL */ +M_INLINE long long +m_core_in_serial_enum(m_serial_read_t serial) +{ + long long i; + /* Store the return code temporary in the serialize object */ + serial->tmp.r = serial->m_interface->read_integer(serial, &i, sizeof (long long)); + return i; +} + +/* Encapsulation of strlen to avoid warnings in M_OUT_SERIAL_DEFAULT_ARG + * because of expanded code will call strlen with NULL (which is illegal) + * However, the branch where it is called is unreachable, so the warning + * is not justified. */ +M_INLINE size_t +m_core_out_serial_strlen(const char s[]) +{ + M_ASSERT(s != NULL); + return strlen(s); +} + +/* Encapsulation of returning error, + * A serializer should return 'm_core_serial_fail()' instead of directly + * M_SERIAL_FAIL + * so that a breakpoint can be put on this function for debugging purpose. + */ +M_INLINE m_serial_return_code_t +m_core_serial_fail(void) +{ + return M_SERIAL_FAIL; +} + +M_END_PROTECTED_CODE + +#endif diff --git a/components/mlib/m-deque.h b/components/mlib/m-deque.h new file mode 100644 index 00000000..f8742e71 --- /dev/null +++ b/components/mlib/m-deque.h @@ -0,0 +1,1147 @@ +/* + * M*LIB - DEQUE module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_DEQUE_H +#define MSTARLIB_DEQUE_H + +#include "m-i-list.h" + +/* Define a deque of a given type and its associated functions. + USAGE: DEQUE_DEF(name, type [, oplist_of_the_type]) */ +#define M_DEQUE_DEF(name, ...) \ + M_DEQUE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) + + +/* Define a deque of a given type and its associated functions. + as the provided type name_t with the iterator named it_t. + USAGE: DEQUE_DEF(name, name_t, it_t, type [, oplist_of_the_type]) */ +#define M_DEQUE_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_D3QU3_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t, M_F(name, _node_ct) ), \ + (name, __VA_ARGS__, name_t, it_t, M_F(name, _node_ct)))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a deque of a type. + USAGE: DEQUE_OPLIST(name[, oplist of the type]) */ +#define M_DEQUE_OPLIST(...) \ + M_D3QU3_OPLIST_P1 (M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST), \ + (__VA_ARGS__ ))) + +/* Default initial size of a bucket of items */ +#ifndef M_USE_DEQUE_DEFAULT_SIZE +#define M_USE_DEQUE_DEFAULT_SIZE 8 +#endif + + +/*****************************************************************************/ +/********************************* INTERNAL **********************************/ +/*****************************************************************************/ + +/* Define the internal contract of a deque */ +#define M_D3QU3_CONTRACT(d) do { \ + M_ASSERT ((d) != NULL); \ + M_ASSERT ((d)->default_size >= M_USE_DEQUE_DEFAULT_SIZE); \ + M_ASSERT ((d)->front->node != NULL); \ + M_ASSERT ((d)->front->index <= (d)->front->node->size); \ + M_ASSERT ((d)->back->node != NULL); \ + M_ASSERT ((d)->back->index <= (d)->back->node->size); \ + M_ASSERT ((d)->front->node != (d)->back->node || \ + (d)->front->index <= (d)->back->index); \ + M_ASSERT ((d)->front->node != (d)->back->node || \ + (d)->back->index - (d)->front->index == (d)->count); \ + } while (0) + +/* Deferred evaluation for the deque definition, + so that all arguments are evaluated before further expansion */ +#define M_D3QU3_DEF_P1(arg) M_ID( M_D3QU3_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_D3QU3_DEF_P2(name, type, oplist, deque_t, it_t, node_t) \ + M_IF_OPLIST(oplist)(M_D3QU3_DEF_P3, M_D3QU3_DEF_FAILURE)(name, type, oplist, deque_t, it_t, node_t) + +/* Stop processing with a compilation failure */ +#define M_D3QU3_DEF_FAILURE(name, type, oplist, deque_t, it_t, node_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(DEQUE_DEF): the given argument is not a valid oplist: " #oplist) + +/* Internal deque definition + - name: prefix to be used + - type: type of the elements of the deque + - oplist: oplist of the type of the elements of the container + - deque_t: alias for M_F(name, _t) [ type of the container ] + - it_t: alias for M_F(name, _it_t) [ iterator of the container ] + - node_t: alias for node_t [ node ] + */ +#define M_D3QU3_DEF_P3(name, type, oplist, deque_t, it_t, node_t) \ + M_D3QU3_DEF_TYPE(name, type, oplist, deque_t, it_t, node_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_D3QU3_DEF_CORE(name, type, oplist, deque_t, it_t, node_t) \ + M_EMPLACE_QUEUE_DEF(name, deque_t, M_F(name, _emplace_back), oplist, M_D3QUE_EMPLACE_BACK_DEF) \ + M_EMPLACE_QUEUE_DEF(name, deque_t, M_F(name, _emplace_front), oplist, M_D3QUE_EMPLACE_FRONT_DEF) + +/* Define the types. + It is a linked list of buckets of the types, + each new created bucket size grows compared to the previous one + resulting in: + strict O(1) for push/pop + O(ln(n)) for random access. + No insert / delete operations are planned. + [Could be done in O(n) complexity if needed] + Define the bucket (aka node) structure. +*/ +#define M_D3QU3_DEF_TYPE(name, type, oplist, deque_t, it_t, node_t) \ + \ + typedef struct M_F(name, _node_s) { \ + ILIST_INTERFACE(M_F(name, _node_list), struct M_F(name, _node_s)); \ + size_t size; \ + type data[M_MIN_FLEX_ARRAY_SIZE]; \ + } node_t; \ + \ + /* Each node is allocated with a variable size bucket (so we use \ + M_GET_REALLOC for the allocation). But we want to delete the nodes \ + automatically with the intrusive list used for storing the nodes: \ + so we register as a DEL operator the FREE operator of the oplist. \ + The interfaces are compatible. \ + */ \ + /* FIXME: How can I separate public types and private implementation? */ \ + ILIST_DEF(M_F(name, _node_list), node_t, (DEL(M_GET_FREE oplist)) ) \ + \ + /* Define an internal iterator */ \ + typedef struct M_F(name, _it2_s) { \ + node_t *node; \ + size_t index; \ + } M_F(name, _it2_ct)[1]; \ + \ + /* Define the deque type: \ + - 'list' if the list of buckets containing the objects. \ + - 'front' is a pseudo-iterator to the first \ + - 'back' is a pseudo-iterator to the one after last element \ + - 'default_size' is the size used for the creation of a new bucket \ + - 'count' is the number of elements in the container. \ + */ \ + typedef struct M_F(name, _s) { \ + M_F(name, _node_list_t) list; \ + M_F(name, _it2_ct) front; \ + M_F(name, _it2_ct) back; \ + size_t default_size; \ + size_t count; \ + } deque_t[1]; \ + \ + /* Define pointer alias */ \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* Define the iterator object */ \ + typedef struct M_F(name, _it_s) { \ + node_t *node; \ + size_t index; \ + const struct M_F(name, _s) *deque; \ + } it_t[1]; \ + \ + /* Define internal types for oplist */ \ + typedef deque_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + typedef it_t M_F(name, _it_ct); \ + +/* Define the core functions */ +#define M_D3QU3_DEF_CORE(name, type, oplist, deque_t, it_t, node_t) \ + \ + /* Allocate a new node for a deque */ \ + M_INLINE node_t* \ + M_C3(m_d3qu3_,name,_new_node)(deque_t d) \ + { \ + size_t def = d->default_size; \ + /* Test for overflow of the size computation */ \ + if (M_UNLIKELY_NOMEM (def > SIZE_MAX / sizeof (type) - sizeof(node_t))) { \ + M_MEMORY_FULL(sizeof(node_t)+def * sizeof(type)); \ + return NULL; \ + } \ + /* Alloc a new node with dynamic size */ \ + node_t*n = (node_t*) (void*) \ + M_CALL_REALLOC(oplist, char, NULL, \ + sizeof(node_t) + def * sizeof(type) ); \ + if (M_UNLIKELY_NOMEM (n==NULL)) { \ + M_MEMORY_FULL(sizeof(node_t)+def * sizeof(type)); \ + return NULL; \ + } \ + /* Initialize the node */ \ + n->size = def; \ + M_F(name, _node_list_init_field)(n); \ + /* Increase the next bucket allocation */ \ + /* Do not increase it too much if there are few items */ \ + def = M_MIN(def, d->count); \ + d->default_size = M_CALL_INC_ALLOC(oplist, def); \ + /* FIXME: Check for overflow? */ \ + return n; \ + } \ + \ + M_INLINE void \ + M_F(name, _init)(deque_t d) \ + { \ + M_F(name, _node_list_init)(d->list); \ + d->default_size = M_USE_DEQUE_DEFAULT_SIZE; \ + d->count = 0; \ + node_t *n = M_C3(m_d3qu3_,name,_new_node)(d); \ + if (n == NULL) return; \ + M_F(name, _node_list_push_back)(d->list, n); \ + d->front->node = n; \ + d->front->index = M_USE_DEQUE_DEFAULT_SIZE/2; \ + d->back->node = n; \ + d->back->index = M_USE_DEQUE_DEFAULT_SIZE/2; \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + node_t *min_node = NULL; \ + for(node_t *n = d->front->node; \ + n != NULL ; \ + n = (n == d->back->node) ? NULL : \ + M_F(name, _node_list_next_obj)(d->list, n) ){ \ + size_t min = n == d->front->node ? d->front->index : 0; \ + size_t max = n == d->back->node ? d->back->index : n->size; \ + for(size_t i = min; i < max; i++) { \ + M_CALL_CLEAR(oplist, n->data[i]); \ + } \ + min_node = (min_node == NULL || min_node->size > n->size) ? n : min_node; \ + } \ + M_ASSERT (min_node != NULL); \ + d->front->node = min_node; \ + d->front->index = min_node->size / 2; \ + d->back->node = min_node; \ + d->back->index = min_node->size / 2; \ + d->count = 0; \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_F(name, _reset)(d); \ + /* We have registered the delete operator to clear all objects */ \ + M_F(name, _node_list_clear)(d->list); \ + /* It is safer to clean some variables */ \ + d->front->node = NULL; \ + d->back->node = NULL; \ + d->count = 0; \ + } \ + \ + M_INLINE type * \ + M_F(name, _push_back_raw)(deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + node_t *n = d->back->node; \ + size_t index = d->back->index; \ + if (M_UNLIKELY (n->size <= index)) { \ + /* try to get an already allocated node */ \ + n = M_F(name, _node_list_next_obj)(d->list, n); \ + if (n == NULL) { \ + /* No node exists, allocate a new one */ \ + n = M_C3(m_d3qu3_,name,_new_node)(d); \ + if (M_UNLIKELY (n == NULL)) return NULL; \ + M_F(name, _node_list_push_back)(d->list, n); \ + } \ + d->back->node = n; \ + index = 0; \ + } \ + type *ret = &n->data[index]; \ + index++; \ + d->count ++; \ + d->back->index = index; \ + M_D3QU3_CONTRACT(d); \ + return ret; \ + } \ + \ + /* Internal, for INIT_WITH */ \ + M_INLINE type * \ + M_F(name, _push_raw)(deque_t d) \ + { \ + return M_F(name, _push_back_raw)(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _push_back)(deque_t d, type const x) \ + { \ + type *p = M_F(name, _push_back_raw)(d); \ + if (M_LIKELY(p != NULL)) { \ + M_CALL_INIT_SET(oplist, *p, x); \ + } \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE type * \ + M_F(name, _push_back_new)(deque_t d) \ + { \ + type *p = M_F(name, _push_back_raw)(d); \ + if (M_LIKELY(p != NULL)) { \ + M_CALL_INIT(oplist, *p); \ + } \ + return p; \ + } \ + , ) \ + \ + M_INLINE void \ + M_F(name, _push_back_move)(deque_t d, type *x) \ + { \ + M_ASSERT (x != NULL); \ + type *p = M_F(name, _push_back_raw)(d); \ + if (M_UNLIKELY(p == NULL)) \ + return; \ + M_DO_INIT_MOVE (oplist, *p, *x); \ + } \ + \ + M_INLINE type* \ + M_F(name, _push_front_raw)(deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + node_t *n = d->front->node; \ + size_t index = d->front->index; \ + index --; \ + /* If overflow */ \ + if (M_UNLIKELY (n->size <= index)) { \ + n = M_F(name, _node_list_previous_obj)(d->list, n); \ + if (n == NULL) { \ + n = M_C3(m_d3qu3_,name,_new_node)(d); \ + if (M_UNLIKELY (n == NULL)) return NULL; \ + M_F(name, _node_list_push_front)(d->list, n); \ + } \ + d->front->node = n; \ + index = n->size -1; \ + } \ + type *ret = &n->data[index]; \ + d->count ++; \ + d->front->index = index; \ + M_D3QU3_CONTRACT(d); \ + return ret; \ + } \ + \ + M_INLINE void \ + M_F(name, _push_front)(deque_t d, type const x) \ + { \ + type *p = M_F(name, _push_front_raw)(d); \ + if (M_LIKELY(p != NULL)) { \ + M_CALL_INIT_SET(oplist, *p, x); \ + } \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE type * \ + M_F(name, _push_front_new)(deque_t d) \ + { \ + type *p = M_F(name, _push_front_raw)(d); \ + if (M_LIKELY(p != NULL)) { \ + M_CALL_INIT(oplist, *p); \ + } \ + return p; \ + } \ + ,) \ + \ + M_INLINE void \ + M_F(name, _push_front_move)(deque_t d, type *x) \ + { \ + M_ASSERT (x != NULL); \ + type *p = M_F(name, _push_front_raw)(d); \ + if (M_UNLIKELY(p == NULL)) \ + return; \ + M_DO_INIT_MOVE (oplist, *p, *x); \ + } \ + \ + M_INLINE void \ + M_F(name, _pop_back)(type *ptr, deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT(d->count > 0); \ + node_t *n = d->back->node; \ + size_t index = d->back->index; \ + index --; \ + if (M_UNLIKELY (n->size <= index)) { \ + /* If there is a next node, \ + pop the back node and push it back to the front. This \ + reduce the used memory if the deque is used as a FIFO queue.*/ \ + node_t *next = M_F(name, _node_list_next_obj)(d->list, n); \ + if (next != NULL) { \ + next = M_F(name, _node_list_pop_back)(d->list); \ + M_ASSERT (next != n); \ + M_F(name, _node_list_push_front)(d->list, next); \ + } \ + n = M_F(name, _node_list_previous_obj)(d->list, n); \ + M_ASSERT (n != NULL && n->size > 1); \ + d->back->node = n; \ + index = n->size-1; \ + } \ + if (ptr != NULL) \ + M_IF_METHOD(MOVE, oplist) ( \ + M_CALL_MOVE(oplist, *ptr, n->data[index]); else \ + , \ + M_CALL_SET(oplist, *ptr, n->data[index]); \ + ) \ + M_CALL_CLEAR(oplist, n->data[index]); \ + d->count --; \ + d->back->index = index; \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE void \ + M_F(name, _pop_back_move)(type *ptr, deque_t d) \ + { \ + M_ASSERT(ptr != NULL); \ + /* Note: Lazy implementation. Can be improved if needed */ \ + M_CALL_INIT(oplist, *ptr); \ + M_F(name, _pop_back)(ptr, d); \ + } \ + , ) \ + \ + M_INLINE void \ + M_F(name, _pop_front)(type *ptr, deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT(d->count > 0); \ + node_t *n = d->front->node; \ + size_t index = d->front->index; \ + if (ptr != NULL) \ + M_IF_METHOD(MOVE, oplist) ( \ + M_CALL_MOVE(oplist, *ptr, n->data[index]); else \ + , \ + M_CALL_SET(oplist, *ptr, n->data[index]); \ + ) \ + M_CALL_CLEAR(oplist, n->data[index]); \ + index++; \ + if (M_UNLIKELY (n->size <= index)) { \ + /* If there is a previous node, \ + pop the front node and push it back to the back. This \ + recycles the used memory if the deque is used as a FIFO queue.*/ \ + node_t *prev = M_F(name, _node_list_previous_obj)(d->list, n); \ + if (prev != NULL) { \ + prev = M_F(name, _node_list_pop_front)(d->list); \ + M_ASSERT (prev != n); \ + M_F(name, _node_list_push_back)(d->list, prev); \ + } \ + /* Update front iterator to point to the next object */ \ + n = M_F(name, _node_list_next_obj)(d->list, n); \ + if (M_UNLIKELY(n == NULL)) { \ + /* No next obj. \ + It is only possible if there was only 1 element */ \ + M_ASSERT(d->count == 1); \ + /* Reset the deque to the midle of the current node */ \ + d->back->node = d->front->node; \ + index = d->front->node->size/2; \ + d->back->index = d->front->node->size/2; \ + } else { \ + d->front->node = n; \ + index = 0; \ + } \ + } \ + d->count --; \ + d->front->index = index; \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE void \ + M_F(name, _pop_front_move)(type *ptr, deque_t d) \ + { \ + M_ASSERT(ptr != NULL); \ + /* Note: Lazy implementation */ \ + M_CALL_INIT(oplist, *ptr); \ + M_F(name, _pop_front)(ptr, d); \ + } \ + ,) \ + \ + M_INLINE type * \ + M_F(name, _back)(const deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT (d->count > 0); \ + size_t i = d->back->index; \ + node_t *n = d->back->node; \ + if (M_UNLIKELY (i == 0)) { \ + n = M_F(name, _node_list_previous_obj)(d->list, n); \ + M_ASSERT (n != NULL); \ + i = n->size; \ + } \ + return &n->data[i-1]; \ + } \ + \ + M_INLINE type * \ + M_F(name, _front)(const deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT (d->count > 0); \ + size_t i = d->front->index; \ + node_t *n = d->front->node; \ + return &n->data[i]; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(const deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + return d->count; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity_back)(const deque_t v) \ + { \ + M_D3QU3_CONTRACT(v); \ + size_t s = 0; \ + for(node_t *n = M_F(name, _node_list_back)(v->list); \ + n != NULL ; \ + n = (n == v->back->node) ? NULL : \ + M_F(name, _node_list_previous_obj)(v->list, n) ){ \ + s += (n == v->back->node ? v->back->index : n->size); \ + } \ + return s; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity_front)(const deque_t v) \ + { \ + M_D3QU3_CONTRACT(v); \ + size_t s = 0; \ + for(node_t *n = M_F(name, _node_list_front)(v->list); \ + n != NULL ; \ + n = (n == v->front->node) ? NULL : \ + M_F(name, _node_list_next_obj)(v->list, n) ){ \ + s += n->size - (n == v->front->node ? v->front->index : 0); \ + } \ + return s; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity)(const deque_t v) \ + { \ + return M_F(name, _capacity_back)(v)+M_F(name, _capacity_front)(v); \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(const deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + return d->count == 0; \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(it_t it, const deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT (it != NULL); \ + it->node = d->front->node; \ + it->index = d->front->index; \ + it->deque = d; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_last)(it_t it, const deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT (it != NULL); \ + it->node = d->back->node; \ + it->index = d->back->index - 1; \ + it->deque = d; \ + if (M_UNLIKELY (it->index >= it->node->size)) { \ + it->node = M_F(name, _node_list_previous_obj)(d->list, it->node); \ + M_ASSERT (it->node != NULL && it->node->size > 1); \ + it->index = it->node->size-1; \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(it_t it, const deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT (it != NULL); \ + it->node = d->back->node; \ + it->index = d->back->index; \ + it->deque = d; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t it1, const it_t it2) \ + { \ + M_ASSERT (it1 != NULL); \ + M_ASSERT (it2 != NULL); \ + it1->node = it2->node; \ + it1->index = it2->index; \ + it1->deque = it2->deque; \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + return (it->node == it->deque->back->node \ + && it->index >= it->deque->back->index) \ + || (it->node == it->deque->front->node \ + && it->index < it->deque->front->index); \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + node_t *n = it->node; \ + it->index ++; \ + if (M_UNLIKELY (it->index >= n->size)) { \ + n = M_F(name, _node_list_next_obj)(it->deque->list, n); \ + if (M_UNLIKELY (n == NULL || it->node == it->deque->back->node)) { \ + /* Point to 'end' (can't undo it) */ \ + it->node = it->deque->back->node; \ + it->index = it->deque->back->node->size; \ + return; \ + } \ + it->node = n; \ + it->index = 0; \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _previous)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + node_t *n = it->node; \ + it->index --; \ + if (M_UNLIKELY (it->index >= n->size)) { \ + n = M_F(name, _node_list_previous_obj)(it->deque->list, n); \ + if (M_UNLIKELY (n == NULL || it->node == it->deque->front->node)) { \ + /* Point to 'end' (can't undo it) */ \ + it->node = it->deque->back->node; \ + it->index = it->deque->back->node->size; \ + return; \ + } \ + it->node = n; \ + it->index = n->size - 1; \ + } \ + } \ + \ + M_INLINE bool \ + M_F(name, _last_p)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + it_t it2; \ + M_F(name, _it_set)(it2, it); \ + M_F(name, _next)(it2); \ + return M_F(name, _end_p)(it2); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(it_t it1, const it_t it2) \ + { \ + M_ASSERT (it1 != NULL); \ + M_ASSERT (it2 != NULL); \ + return it1->deque == it2->deque \ + && it1->node == it2->node \ + && it1->index == it2->index; \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + M_ASSERT (it->index < it->node->size); \ + return &it->node->data[it->index]; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + M_ASSERT (it->index < it->node->size); \ + return M_CONST_CAST(type, &it->node->data[it->index]); \ + } \ + \ + M_INLINE void \ + M_F(name, _remove)(deque_t d, it_t it) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT (it != NULL && it->node != NULL); \ + M_ASSERT (d->count >= 1); \ + M_ASSERT_INDEX(it->index, it->node->size); \ + \ + node_t *n = it->node; \ + M_CALL_CLEAR(oplist, n->data[it->index]); \ + if (n == d->back->node) { \ + /* back index points to the element after the end */ \ + M_ASSERT (d->back->index != 0); \ + M_ASSERT (it->index < d->back->index); \ + /* We cannot have a node deletion in this case */ \ + memmove(&n->data[it->index], &n->data[it->index+1], \ + sizeof(type) * (d->back->index - it->index - 1)); \ + d->back->index --; \ + /* The iterator points to the next element */ \ + } else if (n == d->front->node) { \ + /* front index points to the first element */ \ + if (M_UNLIKELY (d->front->index == n->size -1)) { \ + /* Node 'smart' deletion (single element) */ \ + /* Update front iterator to point to the next object */ \ + n = M_F(name, _node_list_next_obj)(d->list, n); \ + /* We must have a next element, as we have a different back node \ + than the front node. */ \ + M_ASSERT (n != NULL); \ + d->front->node = n; \ + d->front->index = 0; \ + /* The iterator references this element */ \ + it->node = n; \ + it->index = 0; \ + } else { \ + memmove(&n->data[d->front->index+1], &n->data[d->front->index], \ + sizeof(type) * (it->index - d->front->index)); \ + d->front->index ++; \ + /* The iterator shall reference the next element */ \ + M_F(name, _next)(it); \ + } \ + } else { \ + /* Nether front or end node */ \ + if (M_UNLIKELY(n->size == 1)) { \ + /* The iterator shall reference the next element */ \ + M_F(name, _next)(it); \ + /* Node deletion */ \ + M_ASSERT(d->count > 1); \ + M_F(name, _node_list_unlink)(n); \ + M_CALL_FREE(oplist, n); \ + } else { \ + memmove(&n->data[it->index], &n->data[it->index+1], \ + sizeof(type) * (it->node->size - it->index - 1)); \ + /* We lose capacity of the node... */ \ + n->size --; \ + /* The iterator points to the next element \ + except if it was the last one*/ \ + if (M_UNLIKELY(it->index >= n->size)) M_F(name, _next)(it); \ + } \ + } \ + d->count--; \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(deque_t d, const deque_t src) \ + { \ + M_D3QU3_CONTRACT(src); \ + M_ASSERT (d != NULL); \ + M_F(name, _node_list_init)(d->list); \ + d->default_size = M_USE_DEQUE_DEFAULT_SIZE + src->count; \ + d->count = src->count; \ + node_t *n = M_C3(m_d3qu3_,name,_new_node)(d); \ + if (n == NULL) return; \ + d->default_size /= 2; \ + M_F(name, _node_list_push_back)(d->list, n); \ + d->front->node = n; \ + d->front->index = M_USE_DEQUE_DEFAULT_SIZE/2; \ + d->back->node = n; \ + d->back->index = M_USE_DEQUE_DEFAULT_SIZE/2 + src->count; \ + it_t it; \ + size_t i = M_USE_DEQUE_DEFAULT_SIZE/2; \ + for(M_F(name, _it)(it, src); !M_F(name, _end_p)(it) ; M_F(name, _next)(it)) { \ + type const *obj = M_F(name, _cref)(it); \ + M_CALL_INIT_SET(oplist, n->data[i], *obj); \ + i++; \ + M_ASSERT (i <= d->back->index); \ + } \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(deque_t d, deque_t const src) \ + { \ + if (M_UNLIKELY (src == d)) \ + return; \ + /* TODO: Reuse memory of d! */ \ + M_F(name, _clear)(d); \ + M_F(name, _init_set)(d, src); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(deque_t d, deque_t src) \ + { \ + M_D3QU3_CONTRACT(src); \ + M_ASSERT (d!= NULL); \ + M_F(name,_node_list_init_move)(d->list, src->list); \ + d->front->node = src->front->node; \ + d->front->index = src->front->index; \ + d->back->node = src->back->node; \ + d->back->index = src->back->index; \ + d->default_size = src->default_size; \ + d->count = src->count; \ + memset(src, 0, sizeof(deque_t)); \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(deque_t d, deque_t src) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_D3QU3_CONTRACT(src); \ + M_F(name, _clear)(d); \ + M_F(name, _init_move)(d, src); \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(deque_t d, deque_t e) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_D3QU3_CONTRACT(e); \ + M_F(name, _node_list_swap) (d->list, e->list); \ + M_SWAP(node_t *, d->front->node, e->front->node); \ + M_SWAP(node_t *, d->back->node, e->back->node); \ + M_SWAP(size_t, d->front->index, e->front->index); \ + M_SWAP(size_t, d->back->index, e->back->index); \ + M_SWAP(size_t, d->default_size, e->default_size); \ + M_SWAP(size_t, d->count, e->count); \ + M_D3QU3_CONTRACT(d); \ + M_D3QU3_CONTRACT(e); \ + } \ + \ + M_INLINE type* \ + M_F(name, _get)(deque_t d, size_t key) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT_INDEX (key, d->count); \ + const size_t index0 = d->front->index; \ + size_t count = 0; \ + /* This loop is in log(N) since the size increase exponentially.*/ \ + for(node_t *n = d->front->node; true ; \ + n = (n == d->back->node) ? NULL : \ + M_F(name, _node_list_next_obj)(d->list, n) ){ \ + M_ASSERT(n != NULL); \ + if (index0 + key < count + n->size) { \ + return &n->data[index0 + key - count]; \ + } \ + count += n->size; \ + } \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cget)(deque_t d, size_t key) \ + { \ + return M_CONST_CAST(type, M_F(name, _get)(d, key)); \ + } \ + \ + M_INLINE void \ + M_F(name, _set_at)(deque_t d, size_t key, type const x) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT_INDEX (key, d->count); \ + type *p = M_F(name, _get)(d, key); \ + M_CALL_SET(oplist, *p, x); \ + M_D3QU3_CONTRACT(d); \ + } \ + \ + M_IF_METHOD(EQUAL, oplist)( \ + M_INLINE bool \ + M_F(name, _equal_p)(const deque_t d1, const deque_t d2) \ + { \ + M_D3QU3_CONTRACT(d1); \ + M_D3QU3_CONTRACT(d2); \ + if (d1->count != d2->count) \ + return false; \ + it_t it1; \ + it_t it2; \ + for(M_F(name, _it)(it1, d1), M_F(name,_it)(it2, d2); \ + !M_F(name, _end_p)(it1) ; \ + M_F(name, _next)(it1), M_F(name, _next)(it2)) { \ + type const *obj1 = M_F(name, _cref)(it1); \ + type const *obj2 = M_F(name, _cref)(it2); \ + if (M_CALL_EQUAL(oplist, *obj1, *obj2) == false) \ + return false; \ + } \ + M_ASSERT (M_F(name, _end_p)(it2)); \ + return true; \ + } \ + , /* NO EQUAL */) \ + \ + M_IF_METHOD(HASH, oplist)( \ + M_INLINE size_t \ + M_F(name, _hash)(const deque_t d) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_HASH_DECL(hash); \ + it_t it; \ + for(M_F(name, _it)(it, d); !M_F(name, _end_p)(it); M_F(name, _next)(it)) { \ + type const *obj = M_F(name, _cref)(it); \ + M_HASH_UP (hash, M_CALL_HASH(oplist, *obj)); \ + } \ + return M_HASH_FINAL(hash); \ + } \ + , /* NO HASH */) \ + \ + M_IF_METHOD(SWAP, oplist)( \ + M_INLINE void \ + M_F(name, _swap_at)(deque_t d, size_t i, size_t j) \ + { \ + M_D3QU3_CONTRACT(d); \ + M_ASSERT_INDEX (i, d->count); \ + M_ASSERT_INDEX (j, d->count); \ + type *obj1 = M_F(name, _get)(d, i); \ + type *obj2 = M_F(name, _get)(d, j); \ + M_CALL_SWAP(oplist, *obj1, *obj2); \ + M_D3QU3_CONTRACT(d); \ + } \ + , /* NO SWAP */) \ + \ + M_IF_METHOD(GET_STR, oplist)( \ + M_INLINE void \ + M_F(name, _get_str)(m_string_t str, deque_t const deque, bool append) \ + { \ + M_D3QU3_CONTRACT(deque); \ + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \ + it_t it; \ + for (M_F(name, _it)(it, deque) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + type const *item = M_F(name, _cref)(it); \ + M_CALL_GET_STR(oplist, str, *item, true); \ + if (!M_F(name, _last_p)(it)) \ + m_string_push_back (str, M_GET_SEPARATOR oplist); \ + } \ + m_string_push_back (str, ']'); \ + } \ + , /* no GET_STR */ ) \ + \ + M_IF_METHOD(OUT_STR, oplist)( \ + M_INLINE void \ + M_F(name, _out_str)(FILE *file, const deque_t deque) \ + { \ + M_D3QU3_CONTRACT(deque); \ + M_ASSERT (file != NULL); \ + fputc ('[', file); \ + it_t it; \ + for (M_F(name, _it)(it, deque) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)) { \ + type const *item = M_F(name, _cref)(it); \ + M_CALL_OUT_STR(oplist, file, *item); \ + if (!M_F(name, _last_p)(it)) \ + fputc (M_GET_SEPARATOR oplist, file); \ + } \ + fputc (']', file); \ + } \ + , /* no OUT_STR */ ) \ + \ + M_IF_METHOD2(PARSE_STR, INIT, oplist)( \ + M_INLINE bool \ + M_F(name, _parse_str)(deque_t deque, const char str[], const char **endp) \ + { \ + M_D3QU3_CONTRACT(deque); \ + M_ASSERT (str != NULL); \ + M_F(name,_reset)(deque); \ + bool success = false; \ + int c = *str++; \ + if (M_UNLIKELY (c != '[')) goto exit; \ + c = *str++; \ + if (M_UNLIKELY (c == ']')) {success = true; goto exit;} \ + if (M_UNLIKELY (c == 0)) goto exit; \ + str--; \ + type item; \ + M_CALL_INIT(oplist, item); \ + do { \ + bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \ + do { c = *str++; } while (isspace(c)); \ + if (b == false || c == 0) { goto exit_clear; } \ + M_F(name, _push_back)(deque, item); \ + } while (c == M_GET_SEPARATOR oplist); \ + M_D3QU3_CONTRACT(deque); \ + success = (c == ']'); \ + exit_clear: \ + M_CALL_CLEAR(oplist, item); \ + exit: \ + if (endp) *endp = str; \ + return success; \ + } \ + , /* no PARSE_STR */ ) \ + \ + M_IF_METHOD2(IN_STR, INIT, oplist)( \ + M_INLINE bool \ + M_F(name, _in_str)(deque_t deque, FILE *file) \ + { \ + M_D3QU3_CONTRACT(deque); \ + M_ASSERT (file != NULL); \ + M_F(name,_reset)(deque); \ + int c = fgetc(file); \ + if (M_UNLIKELY (c != '[')) return false; \ + c = fgetc(file); \ + if (M_UNLIKELY (c == ']')) return true; \ + if (M_UNLIKELY (c == EOF)) return false; \ + ungetc(c, file); \ + type item; \ + M_CALL_INIT(oplist, item); \ + do { \ + bool b = M_CALL_IN_STR(oplist, item, file); \ + do { c = fgetc(file); } while (isspace(c)); \ + if (b == false || c == EOF) { break; } \ + M_F(name, _push_back)(deque, item); \ + } while (c == M_GET_SEPARATOR oplist); \ + M_CALL_CLEAR(oplist, item); \ + M_D3QU3_CONTRACT(deque); \ + return c == ']'; \ + } \ + , /* no IN_STR */ ) \ + \ + M_IF_METHOD(OUT_SERIAL, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, const deque_t deque) \ + { \ + M_D3QU3_CONTRACT(deque); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + bool first_done = false; \ + ret = f->m_interface->write_array_start(local, f, deque->count); \ + M_F(name, _it_ct) it; \ + for (M_F(name, _it)(it, deque) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + type const *item = M_F(name, _cref)(it); \ + if (first_done) \ + ret |= f->m_interface->write_array_next(local, f); \ + ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \ + first_done = true; \ + } \ + ret |= f->m_interface->write_array_end(local, f); \ + return ret & M_SERIAL_FAIL; \ + } \ + , /* no OUT_SERIAL */ ) \ + \ + M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(deque_t deque, m_serial_read_t f) \ + { \ + M_D3QU3_CONTRACT(deque); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + size_t estimated_size = 0; \ + M_F(name,_reset)(deque); \ + ret = f->m_interface->read_array_start(local, f, &estimated_size); \ + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ + type item; \ + M_CALL_INIT(oplist, item); \ + do { \ + ret = M_CALL_IN_SERIAL(oplist, item, f); \ + if (ret != M_SERIAL_OK_DONE) { break; } \ + M_F(name, _push_back)(deque, item); \ + ret = f->m_interface->read_array_next(local, f); \ + } while (ret == M_SERIAL_OK_CONTINUE); \ + M_CALL_CLEAR(oplist, item); \ + return ret; \ + } \ + , /* no IN_SERIAL & INIT */ ) \ + +/* Definition of the emplace_back function for deque */ +#define M_D3QUE_EMPLACE_BACK_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ + } + + +/* Definition of the emplace_front function for deque */ +#define M_D3QUE_EMPLACE_FRONT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ + } + + +/********************************* INTERNAL **********************************/ + +/* Deferred evaluation for the oplist definition, + so that all arguments are evaluated before further expansion */ +#define M_D3QU3_OPLIST_P1(arg) M_D3QU3_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_D3QU3_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_D3QU3_OPLIST_P3, M_D3QU3_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_D3QU3_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_DEQUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* OPLIST definition of a deque */ +#define M_D3QU3_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)) \ + ,INIT_SET(M_F(name, _init_set)) \ + ,INIT_WITH(API_1(M_INIT_WITH_VAI)) \ + ,SET(M_F(name, _set)) \ + ,CLEAR(M_F(name, _clear)) \ + ,INIT_MOVE(M_F(name, _init_move)) \ + ,MOVE(M_F(name, _move)) \ + ,SWAP(M_F(name, _swap)) \ + ,NAME(name) \ + ,TYPE(M_F(name,_ct)) \ + ,SUBTYPE(M_F(name, _subtype_ct)) \ + ,EMPTY_P(M_F(name,_empty_p)) \ + ,IT_TYPE(M_F(name,_it_ct)) \ + ,IT_FIRST(M_F(name,_it)) \ + ,IT_LAST(M_F(name,_it_last)) \ + ,IT_END(M_F(name,_it_end)) \ + ,IT_SET(M_F(name,_it_set)) \ + ,IT_END_P(M_F(name,_end_p)) \ + ,IT_LAST_P(M_F(name,_last_p)) \ + ,IT_EQUAL_P(M_F(name,_it_equal_p)) \ + ,IT_NEXT(M_F(name,_next)) \ + ,IT_PREVIOUS(M_F(name,_previous)) \ + ,IT_REF(M_F(name,_ref)) \ + ,IT_CREF(M_F(name,_cref)) \ + ,IT_REMOVE(M_F(name,_remove)) \ + ,RESET(M_F(name,_reset)) \ + ,GET_SIZE(M_F(name, _size)) \ + ,PUSH(M_F(name,_push_back)) \ + ,POP(M_F(name,_pop_back)) \ + ,OPLIST(oplist) \ + ,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \ + ,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \ + ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \ + ,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \ + ,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \ + ,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \ + ,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \ + ,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \ + ) + + +/********************************* INTERNAL **********************************/ + +#if M_USE_SMALL_NAME +#define DEQUE_DEF M_DEQUE_DEF +#define DEQUE_DEF_AS M_DEQUE_DEF_AS +#define DEQUE_OPLIST M_DEQUE_OPLIST +#endif + +#endif diff --git a/components/mlib/m-dict.h b/components/mlib/m-dict.h new file mode 100644 index 00000000..61f086b8 --- /dev/null +++ b/components/mlib/m-dict.h @@ -0,0 +1,1988 @@ +/* + * M*LIB - DICTIONARY Module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_DICT_H +#define MSTARLIB_DICT_H + +#include "m-list.h" +#include "m-array.h" +#include "m-tuple.h" + + +/* Define a dictionary associating the key key_type to the value value_type and its associated functions. + USAGE: + DICT_DEF2(name, key_type, key_oplist, value_type, value_oplist) + OR + DICT_DEF2(name, key_type, value_type) +*/ +#define M_DICT_DEF2(name, key_type, ...) \ + M_DICT_DEF2_AS(name, M_F(name,_t), M_F(name,_it_t), M_F(name,_itref_t), key_type, __VA_ARGS__) + + +/* Define a dictionary associating the key key_type to the value value_type and its associated functions. + as the given name name_t with its associated functions. + USAGE: + DICT_DEF2_AS(name, name_t, it_t, itref_t, key_type, key_oplist, value_type, value_oplist) + OR + DICT_DEF2_AS(name, name_t, it_t, itref_t, key_type, value_type) +*/ +#define M_DICT_DEF2_AS(name, name_t, it_t, itref_t, key_type, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_D1CT_DEF2_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t, itref_t ), \ + (name, key_type, __VA_ARGS__, name_t, it_t, itref_t ) )) \ + M_END_PROTECTED_CODE + + +/* Define a dictionary asssociating the key key_type to the value value_type and its associated functions. + It stores the computed hash value, avoiding the need of recomputing it but increasing memory + consumption. + USAGE: + DICT_STOREHASH_DEF2(name, key_type[, key_oplist], value_type[, value_oplist]) + OR + DICT_STOREHASH_DEF2(name, key_type[, key_oplist], value_type[, value_oplist]) +*/ +#define M_DICT_STOREHASH_DEF2(name, key_type, ...) \ + M_DICT_STOREHASH_DEF2_AS(name, M_F(name,_t), M_F(name,_it_t), M_F(name,_itref_t), key_type, __VA_ARGS__) + + +/* Define a dictionary asssociating the key key_type to the value value_type and its associated functions. + as the given name name_t with its associated functions. + It stores the computed hash value, avoiding the need of recomputing it but increasing memory + consumption. + USAGE: + DICT_STOREHASH_DEF2_AS(name, name_t, it_t, itref_t, key_type[, key_oplist], value_type[, value_oplist]) + OR + DICT_STOREHASH_DEF2_AS(name, name_t, it_t, itref_t, key_type[, key_oplist], value_type[, value_oplist]) +*/ +#define M_DICT_STOREHASH_DEF2_AS(name, name_t, it_t, itref_t, key_type, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_D1CT_SHASH_DEF2_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t, itref_t ), \ + (name, key_type, __VA_ARGS__, name_t, it_t, itref_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a dictionary associating the key key_type to the value value_type + with an Open Addressing implementation and its associated functions. + KEY_OPLIST needs the operators OOR_EQUAL & OOR_SET. + USAGE: + DICT_OA_DEF2(name, key_type, key_oplist, value_type, value_oplist) + OR + DICT_OA_DEF2(name, key_type, value_type) +*/ +#define M_DICT_OA_DEF2(name, key_type, ...) \ + M_DICT_OA_DEF2_AS(name, M_F(name,_t), M_F(name,_it_t), M_F(name,_itref_t), key_type, __VA_ARGS__) + + +/* Define a dictionary associating the key key_type to the value value_type + with an Open Addressing implementation and its associated functions. + as the given name name_t with its associated functions. + KEY_OPLIST needs the operators OOR_EQUAL & OOR_SET. + USAGE: + DICT_OA_DEF2_AS(name, name_t, it_t, itref_t, key_type, key_oplist, value_type, value_oplist) + OR + DICT_OA_DEF2_AS(name, name_t, it_t, itref_t, key_type, value_type) +*/ +#define M_DICT_OA_DEF2_AS(name, name_t, it_t, itref_t, key_type, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_D1CT_OA_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t, itref_t ), \ + (name, key_type, __VA_ARGS__, name_t, it_t, itref_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a set of the key key_type and its associated functions. + The set is unordered. + USAGE: DICT_SET_DEF(name, key_type[, key_oplist]) +*/ +#define M_DICT_SET_DEF(name, ...) \ + M_DICT_SET_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) + + +/* Define a set of the key key_type and its associated functions. + as the given name name_t with its associated functions. + The set is unordered. + USAGE: DICT_SET_DEF_AS(name, name_t, it_t, key_type[, key_oplist]) +*/ +#define M_DICT_SET_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_D1CT_SET_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t, M_F(name, _itref_ct) ), \ + (name, __VA_ARGS__, name_t, it_t, M_F(name, _itref_ct) ))) \ + M_END_PROTECTED_CODE + + +/* Define a set of the key key_type + with an Open Addressing implementation and its associated functions. + The set is unordered. + USAGE: DICT_OASET_DEF(name, key_type[, key_oplist]) +*/ +#define M_DICT_OASET_DEF(name, ...) \ + M_DICT_OASET_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) + + +/* Define a set of the key key_type + with an Open Addressing implementation and its associated functions. + as the given name name_t with its associated functions. + The set is unordered. + USAGE: DICT_OASET_DEF_AS(name, name_t, it_t, key_type[, key_oplist]) +*/ +#define M_DICT_OASET_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_D1CT_OASET_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t, M_F(name, _itref_ct) ), \ + (name, __VA_ARGS__, name_t, it_t, M_F(name, _itref_ct) ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a dictionnary (DICT_DEF2, DICT_STOREHASH_DEF2 or DICT_OA_DEF2). + USAGE: + DICT_OPLIST(name, oplist of the key type, oplist of the value type) + OR + DICT_OPLIST(name) +*/ +#define M_DICT_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (M_D1CT_OPLIST_P1((__VA_ARGS__, M_BASIC_OPLIST, M_BASIC_OPLIST )), \ + M_D1CT_OPLIST_P1((__VA_ARGS__ ))) + + +/* Define the oplist of a dictionnary (DICT_SET_DEF). + USAGE: DICT_SET_OPLIST(name[, oplist of the key type]) */ +#define M_DICT_SET_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (M_D1CT_SET_OPLIST_P1((__VA_ARGS__, M_BASIC_OPLIST)), \ + M_D1CT_SET_OPLIST_P1((__VA_ARGS__ ))) + + +/*****************************************************************************/ +/******************************** INTERNAL ***********************************/ +/*****************************************************************************/ + +/* Define a dictionary from the key key_type to the value value_type. + It is defined as an array of singly linked list (each list + representing a bucket of items with the same hash value modulo the + current array size). +*/ +/* Deferred evaluation for arg */ +#define M_D1CT_DEF2_P1(arg) M_ID( M_D1CT_DEF2_P2 arg ) + +/* Validate the key oplist before going further */ +#define M_D1CT_DEF2_P2(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_IF_OPLIST(key_oplist)(M_D1CT_DEF2_P3, M_D1CT_DEF2_FAILURE)(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) + +/* Validate the value oplist before going further */ +#define M_D1CT_DEF2_P3(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_IF_OPLIST(value_oplist)(M_D1CT_DEF2_P4, M_D1CT_DEF2_FAILURE)(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) + +/* Stop processing with a compilation failure */ +#define M_D1CT_DEF2_FAILURE(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(DICT_DEF2): at least one of the given argument is not a valid oplist: " M_AS_STR(key_oplist) " / " M_AS_STR(value_oplist) ) + +#define M_D1CT_DEF2_P4(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, key_type, key_oplist) \ + M_CHECK_COMPATIBLE_OPLIST(name, 2, value_type, value_oplist) \ + \ + TUPLE_DEF2(M_F(name, _pair), (key, key_type, key_oplist), (value, value_type, value_oplist)) \ + \ + M_D1CT_FUNC_DEF2_P5(name, key_type, key_oplist, value_type, value_oplist, \ + M_F(name, _pair_ct), TUPLE_OPLIST(M_F(name, _pair), key_oplist, value_oplist), 0, 0, \ + dict_t, dict_it_t, it_deref_t ) + + +/* Define a dictionary with the key key_type to the value value_type. + which stores the computed hash value (avoiding the need of recomputing it). + It is defined as an array of singly linked list (each list + representing a bucket). +*/ +/* Defered evaluation for arg */ +#define M_D1CT_SHASH_DEF2_P1(arg) M_ID( M_D1CT_SHASH_DEF2_P2 arg ) + +/* Validate the key oplist before going further */ +#define M_D1CT_SHASH_DEF2_P2(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_IF_OPLIST(key_oplist)(M_D1CT_SHASH_DEF2_P3, M_D1CT_SHASH_DEF2_FAILURE)(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) + +/* Validate the value oplist before going further */ +#define M_D1CT_SHASH_DEF2_P3(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_IF_OPLIST(value_oplist)(M_D1CT_SHASH_DEF2_P4, M_D1CT_SHASH_DEF2_FAILURE)(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) + +/* Stop processing with a compilation failure */ +#define M_D1CT_SHASH_DEF2_FAILURE(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(DICT_STOREHASH_DEF2): at least one of the given argument is not a valid oplist: " M_AS_STR(key_oplist) " / " M_AS_STR(value_oplist) ) + +#define M_D1CT_SHASH_DEF2_P4(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, key_type, key_oplist) \ + M_CHECK_COMPATIBLE_OPLIST(name, 2, value_type, value_oplist) \ + \ + TUPLE_DEF2(M_F(name, _pair), (hash, size_t, M_BASIC_OPLIST), (key, key_type, key_oplist), (value, value_type, value_oplist)) \ + \ + M_D1CT_FUNC_DEF2_P5(name, key_type, key_oplist, value_type, value_oplist, \ + M_F(name, _pair_ct), TUPLE_OPLIST(M_F(name, _pair), M_BASIC_OPLIST, key_oplist, value_oplist), 0, 1, \ + dict_t, dict_it_t, it_deref_t ) + + +/* Define a set with the key key_type + It is defined as an array of singly linked list (each list + representing a bucket). +*/ +#define M_D1CT_SET_DEF_P1(arg) M_ID( M_D1CT_SET_DEF_P2 arg ) + +/* Validate the key oplist before going further */ +#define M_D1CT_SET_DEF_P2(name, key_type, key_oplist, dict_t, dict_it_t, it_deref_t) \ + M_IF_OPLIST(key_oplist)(M_D1CT_SET_DEF_P4, M_D1CT_SET_DEF_FAILURE)(name, key_type, key_oplist, dict_t, dict_it_t, it_deref_t) + +/* Stop processing with a compilation failure */ +#define M_D1CT_SET_DEF_FAILURE(name, key_type, key_oplist, dict_t, dict_it_t, it_deref_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(DICT_SET_DEF): the given argument is not a valid oplist: " M_AS_STR(key_oplist) ) + +#define M_D1CT_SET_DEF_P4(name, key_type, key_oplist, dict_t, dict_it_t, it_deref_t) \ + \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, key_type, key_oplist) \ + \ + TUPLE_DEF2(M_F(name, _pair), (key, key_type, key_oplist)) \ + \ + M_D1CT_FUNC_DEF2_P5(name, key_type, key_oplist, key_type, M_EMPTY_OPLIST, \ + M_F(name, _pair_ct), TUPLE_OPLIST(M_F(name, _pair), key_oplist), 1, 0, \ + dict_t, dict_it_t, it_deref_t) + + + +/* Define the structure of a chained dictionnary for all kind of dictionaries + * name: prefix of the container, + * key_type: type of the key + * key_oplist: oplist of the key + * value_type: type of the value (if not a SET) + * value_oplist: oplist of the value (if not a SET) + * pair_type: type of the pair (key, value) + * pair_oplist: oplist of the pair (key, value) + * isSet: is the container a SET (=1) or a MAP (=0) + * isStoreHash: is the computed hash stored in the bucker (=1) or not (=0) + * dict_t: name of the type to construct + * dict_it_t: name of the iterator within the dictionnary. + * it_deref_t: name of the type returned by an iterator +*/ +#define M_D1CT_FUNC_DEF2_P5(name, key_type, key_oplist, value_type, value_oplist, pair_type, pair_oplist, isSet, isStoreHash, dict_t, dict_it_t, it_deref_t) \ + \ + /* NOTE: \ + if isSet is true, all methods of value_oplist are NOP methods */ \ + \ + /* Define the list of buckets */ \ + /* Use memory allocator for bucket if needed */ \ + M_IF_METHOD(MEMPOOL, key_oplist) \ + ( \ + LIST_DEF(M_F(name, _list_pair), pair_type, \ + M_OPEXTEND(pair_oplist, MEMPOOL(M_GET_MEMPOOL key_oplist), MEMPOOL_LINKAGE(M_GET_MEMPOOL_LINKAGE key_oplist))) \ + , \ + LIST_DEF(M_F(name, _list_pair), pair_type, pair_oplist) \ + ) \ + \ + /* Define the array of list of buckets */ \ + ARRAY_DEF(M_F(name, _array_list_pair), M_F(name, _list_pair_ct), \ + LIST_OPLIST(M_F(name, _list_pair), pair_oplist)) \ + \ + /* Define chained dict type */ \ + typedef struct M_F(name, _s) { \ + size_t count, lower_limit, upper_limit; \ + M_F(name, _array_list_pair_ct) table; \ + } dict_t[1]; \ + \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* Define iterator type */ \ + typedef struct M_F(name, _it_s) { \ + M_F(name, _array_list_pair_it_ct) array_it; \ + M_F(name, _list_pair_it_ct) list_it; \ + } dict_it_t[1]; \ + \ + /* Define type returned by the _ref method of an iterator */ \ + M_IF(isSet)( \ + typedef key_type it_deref_t; \ + , \ + typedef struct M_F(name, _pair_s) it_deref_t; \ + ) \ + \ + /* Define internal types for oplist */ \ + typedef dict_t M_F(name, _ct); \ + typedef it_deref_t M_F(name, _subtype_ct); \ + typedef key_type M_F(name, _key_ct); \ + typedef value_type M_F(name, _value_ct); \ + typedef dict_it_t M_F(name, _it_ct); \ + \ + M_INLINE void \ + M_F(name, _init)(dict_t map) \ + { \ + M_ASSERT (map != NULL); \ + map->count = 0; \ + M_F(name, _array_list_pair_init)(map->table); \ + M_F(name, _array_list_pair_resize)(map->table, M_D1CT_INITIAL_SIZE); \ + map->lower_limit = M_D1CT_LOWER_BOUND(M_D1CT_INITIAL_SIZE); \ + map->upper_limit = M_D1CT_UPPER_BOUND(M_D1CT_INITIAL_SIZE); \ + M_D1CT_CONTRACT(name, map); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(dict_t map, const dict_t org) \ + { \ + M_D1CT_CONTRACT(name, org); \ + M_ASSERT (map != org); \ + map->count = org->count; \ + map->lower_limit = org->lower_limit; \ + map->upper_limit = org->upper_limit; \ + M_F(name, _array_list_pair_init_set)(map->table, org->table); \ + M_D1CT_CONTRACT(name, map); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(dict_t map, const dict_t org) \ + { \ + M_D1CT_CONTRACT(name, map); \ + M_D1CT_CONTRACT(name, org); \ + map->count = org->count; \ + map->lower_limit = org->lower_limit; \ + map->upper_limit = org->upper_limit; \ + M_F(name, _array_list_pair_set)(map->table, org->table); \ + M_D1CT_CONTRACT(name, map); \ + } \ + \ + M_INLINE void \ + M_F(name,_clear)(dict_t map) \ + { \ + M_D1CT_CONTRACT(name, map); \ + M_F(name, _array_list_pair_clear)(map->table); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(dict_t map, dict_t org) \ + { \ + M_D1CT_CONTRACT(name, org); \ + map->count = org->count; \ + map->lower_limit = org->lower_limit; \ + map->upper_limit = org->upper_limit; \ + M_F(name, _array_list_pair_init_move)(map->table, org->table); \ + M_D1CT_CONTRACT(name, map); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(dict_t d1, dict_t d2) \ + { \ + M_D1CT_CONTRACT(name, d1); \ + M_D1CT_CONTRACT(name, d2); \ + M_SWAP (size_t, d1->count, d2->count); \ + M_SWAP (size_t, d1->lower_limit, d2->lower_limit); \ + M_SWAP (size_t, d1->upper_limit, d2->upper_limit); \ + M_F(name, _array_list_pair_swap)(d1->table, d2->table); \ + M_D1CT_CONTRACT(name, d1); \ + M_D1CT_CONTRACT(name, d2); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(dict_t map, dict_t org) \ + { \ + M_D1CT_CONTRACT(name, map); \ + M_D1CT_CONTRACT(name, org); \ + M_ASSERT (map != org); \ + M_F(name,_clear)(map); \ + M_F(name,_init_move)(map, org); \ + M_D1CT_CONTRACT(name, map); \ + } \ + \ + M_INLINE void \ + M_F(name,_reset)(dict_t map) \ + { \ + M_F(name, _array_list_pair_reset)(map->table); \ + M_F(name, _array_list_pair_resize)(map->table, M_D1CT_INITIAL_SIZE); \ + map->lower_limit = M_D1CT_LOWER_BOUND(M_D1CT_INITIAL_SIZE); \ + map->upper_limit = M_D1CT_UPPER_BOUND(M_D1CT_INITIAL_SIZE); \ + map->count = 0; \ + M_D1CT_CONTRACT(name, map); \ + } \ + \ + M_INLINE value_type * \ + M_F(name, _get)(const dict_t map, key_type const key) \ + { \ + M_D1CT_CONTRACT(name, map); \ + size_t hash = M_CALL_HASH(key_oplist, key); \ + size_t i = hash & (M_F(name, _array_list_pair_size)(map->table) - 1); \ + const M_F(name, _list_pair_ct) *list_ptr = \ + M_F(name, _array_list_pair_cget)(map->table, i); \ + M_F(name, _list_pair_it_ct) it; \ + for(M_F(name, _list_pair_it)(it, *list_ptr); \ + !M_F(name, _list_pair_end_p)(it); \ + M_F(name, _list_pair_next)(it)) { \ + pair_type *ref = M_F(name, _list_pair_ref)(it); \ + M_IF(isStoreHash)(if ((*ref)->hash != hash) { continue; }, ) \ + if (M_CALL_EQUAL(key_oplist, (*ref)->key, key)) \ + return &(*ref)->M_IF(isSet)(key, value); \ + } \ + return NULL; \ + } \ + \ + M_INLINE value_type const * \ + M_F(name, _cget)(const dict_t map, key_type const key) \ + { \ + return M_CONST_CAST(value_type, M_F(name,_get)(map,key)); \ + } \ + \ + M_INLINE void \ + M_C3(m_d1ct_,name,_resize_up)(dict_t map) \ + { \ + /* NOTE: Contract may not be fulfilled here */ \ + size_t old_size = M_F(name, _array_list_pair_size)(map->table); \ + size_t new_size = old_size * 2; \ + if (M_UNLIKELY_NOMEM (new_size <= old_size)) { \ + M_MEMORY_FULL((size_t)-1); \ + } \ + M_ASSERT (old_size > 1 && new_size > 1); \ + /* Resize the table of the dictionnary */ \ + M_F(name, _array_list_pair_resize)(map->table, new_size); \ + /* Move the items to the new upper part */ \ + for(size_t i = 0; i < old_size; i++) { \ + M_F(name, _list_pair_ct) *list = \ + M_F(name, _array_list_pair_get)(map->table, i); \ + if (M_F(name, _list_pair_empty_p)(*list)) \ + continue; \ + /* We need to scan each item and recompute its hash to know \ + if it remains inplace or shall be moved to the upper part.*/ \ + M_F(name, _list_pair_it_ct) it; \ + M_F(name, _list_pair_it)(it, *list); \ + while (!M_F(name, _list_pair_end_p)(it)) { \ + M_F(name, _pair_ptr) pair = *M_F(name, _list_pair_ref)(it); \ + size_t hash = M_IF(isStoreHash)(pair->hash, M_CALL_HASH(key_oplist, pair->key)); \ + if ((hash & (new_size-1)) >= old_size) { \ + M_ASSERT( (hash & (new_size-1)) == (i + old_size)); \ + M_F(name, _list_pair_ct) *new_list = \ + M_F(name, _array_list_pair_get)(map->table, i + old_size); \ + M_F(name, _list_pair_splice_back)(*new_list, *list, it); \ + /* Splice_back has updated the iterator to the next one */ \ + } else { \ + M_F(name, _list_pair_next)(it); \ + } \ + } \ + } \ + map->upper_limit = M_D1CT_UPPER_BOUND(new_size); \ + map->lower_limit = M_D1CT_LOWER_BOUND(new_size); \ + } \ + \ + M_INLINE void \ + M_C3(m_d1ct_,name,_resize_down)(dict_t map) \ + { \ + /* NOTE: Contract may not be fulfilled here */ \ + size_t old_size = M_F(name, _array_list_pair_size)(map->table); \ + M_ASSERT ((old_size % 2) == 0); \ + size_t new_size = old_size / 2; \ + M_ASSERT (new_size >= M_D1CT_INITIAL_SIZE); \ + /* Move all items from the upper part to the lower part of the table */ \ + /* NOTE: We don't need to recompute the hash to move them! */ \ + for(size_t i = new_size; i < old_size; i++) { \ + M_F(name, _list_pair_ct) *list = \ + M_F(name, _array_list_pair_get)(map->table, i); \ + if (M_F(name, _list_pair_empty_p)(*list)) \ + continue; \ + M_F(name, _list_pair_ct) *new_list = \ + M_F(name, _array_list_pair_get)(map->table, i - new_size); \ + M_F(name, _list_pair_splice)(*new_list, *list); \ + } \ + /* Resize the table of the dictionary */ \ + M_F(name, _array_list_pair_resize)(map->table, new_size); \ + map->upper_limit = M_D1CT_UPPER_BOUND(new_size); \ + map->lower_limit = M_D1CT_LOWER_BOUND(new_size); \ + } \ + \ + M_INLINE void \ + M_IF(isSet)(M_F(name, _push), M_F(name, _set_at)) \ + (dict_t map, key_type const key \ + M_IF(isSet)(, M_DEFERRED_COMMA value_type const value)) \ + { \ + M_D1CT_CONTRACT(name, map); \ + \ + size_t hash = M_CALL_HASH(key_oplist, key); \ + size_t i = hash & (M_F(name, _array_list_pair_size)(map->table) - 1); \ + M_F(name, _list_pair_ct) *list_ptr = \ + M_F(name, _array_list_pair_get)(map->table, i); \ + M_F(name, _list_pair_it_ct) it; \ + for(M_F(name, _list_pair_it)(it, *list_ptr); \ + !M_F(name, _list_pair_end_p)(it); \ + M_F(name, _list_pair_next)(it)) { \ + M_F(name, _pair_ptr) ref = *M_F(name, _list_pair_ref)(it); \ + M_IF(isStoreHash)(if (ref->hash != hash) continue;, ) \ + if (M_CALL_EQUAL(key_oplist,ref->key, key)) { \ + M_CALL_SET(value_oplist, ref->value, value); \ + return; \ + } \ + } \ + M_F(name, _pair_init_emplace)(*M_F(name, _list_pair_push_raw)(*list_ptr), \ + M_IF(isStoreHash)(hash M_DEFERRED_COMMA,) \ + key \ + M_IF(isSet)(, M_DEFERRED_COMMA value)); \ + map->count ++; \ + if (M_UNLIKELY (map->count > map->upper_limit) ) \ + M_C3(m_d1ct_,name,_resize_up)(map); \ + M_D1CT_CONTRACT(name, map); \ + } \ + \ + M_INLINE value_type * \ + M_F(name, _safe_get)(dict_t map, key_type const key) \ + { \ + M_D1CT_CONTRACT(name, map); \ + \ + size_t hash = M_CALL_HASH(key_oplist, key); \ + size_t i = hash & (M_F(name, _array_list_pair_size)(map->table) - 1); \ + M_F(name, _list_pair_ct) *list_ptr = \ + M_F(name, _array_list_pair_get)(map->table, i); \ + M_F(name, _list_pair_it_ct) it; \ + for(M_F(name, _list_pair_it)(it, *list_ptr); \ + !M_F(name, _list_pair_end_p)(it); \ + M_F(name, _list_pair_next)(it)) { \ + M_F(name, _pair_ptr) ref = *M_F(name, _list_pair_ref)(it); \ + M_IF(isStoreHash)(if (ref->hash != hash) continue;, ) \ + if (M_CALL_EQUAL(key_oplist, ref->key, key)) { \ + return &ref->M_IF(isSet)(key, value); \ + } \ + } \ + pair_type *ref = M_F(name, _list_pair_push_new)(*list_ptr); \ + M_IF(isStoreHash)(M_F(name, _pair_set_hash)(*ref, hash);,) \ + M_F(name, _pair_set_key)(*ref, key); \ + map->count ++; \ + if (M_UNLIKELY (map->count > map->upper_limit) ) { \ + M_C3(m_d1ct_,name,_resize_up)(map); \ + /* Even if the array is being resized, the pointer 'ref' \ + shall still point to the same item in the bucket (it may still \ + be in a different bucket) */ \ + } \ + M_D1CT_CONTRACT(name, map); \ + return &(*ref)->M_IF(isSet)(key, value); \ + } \ + \ + M_INLINE bool \ + M_F(name, _erase)(dict_t map, key_type const key) \ + { \ + M_D1CT_CONTRACT(name, map); \ + \ + bool ret = false; \ + size_t hash = M_CALL_HASH(key_oplist, key); \ + size_t i = hash & (M_F(name, _array_list_pair_size)(map->table) - 1); \ + M_F(name, _list_pair_ct) *list_ptr = \ + M_F(name, _array_list_pair_get)(map->table, i); \ + M_F(name, _list_pair_it_ct) it; \ + for(M_F(name, _list_pair_it)(it, *list_ptr); \ + !M_F(name, _list_pair_end_p)(it); \ + M_F(name, _list_pair_next)(it)) { \ + M_F(name, _pair_ptr) ref = *M_F(name, _list_pair_ref)(it); \ + M_IF(isStoreHash)(if (ref->hash != hash) continue;, ) \ + if (M_CALL_EQUAL(key_oplist, ref->key, key)) { \ + M_F(name, _list_pair_remove)(*list_ptr, it); \ + map->count --; \ + ret = true; \ + break; \ + } \ + } \ + if (M_UNLIKELY (map->count < map->lower_limit) ) \ + M_C3(m_d1ct_,name,_resize_down)(map); \ + return ret; \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(dict_it_t it, const dict_t d) \ + { \ + M_D1CT_CONTRACT(name, d); \ + M_F(name, _array_list_pair_it)(it->array_it, d->table); \ + M_F(name, _list_pair_ct) *ref = \ + M_F(name, _array_list_pair_ref)(it->array_it); \ + M_F(name, _list_pair_it)(it->list_it, *ref); \ + while (M_F(name, _list_pair_end_p)(it->list_it)) { \ + M_F(name, _array_list_pair_next)(it->array_it); \ + if (M_UNLIKELY (M_F(name, _array_list_pair_end_p)(it->array_it))) \ + break; \ + ref = M_F(name, _array_list_pair_ref)(it->array_it); \ + M_F(name, _list_pair_it)(it->list_it, *ref); \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(dict_it_t it, const dict_it_t ref) \ + { \ + M_ASSERT (it != NULL && ref != NULL); \ + M_F(name, _array_list_pair_it_set)(it->array_it, \ + ref->array_it); \ + M_F(name, _list_pair_it_set)(it->list_it, ref->list_it); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(dict_it_t it, const dict_t d) \ + { \ + M_D1CT_CONTRACT(name, d); \ + M_F(name, _array_list_pair_it_end)(it->array_it, d->table); \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(const dict_it_t it) \ + { \ + M_ASSERT (it != NULL); \ + return M_F(name, _list_pair_end_p)(it->list_it); \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(dict_it_t it) \ + { \ + M_ASSERT(it != NULL); \ + M_F(name, _list_pair_next)(it->list_it); \ + M_F(name, _list_pair_ct) *ref; \ + while (M_F(name, _list_pair_end_p)(it->list_it)) { \ + M_F(name, _array_list_pair_next)(it->array_it); \ + if (M_F(name, _array_list_pair_end_p)(it->array_it)) \ + break; \ + ref = M_F(name, _array_list_pair_ref)(it->array_it); \ + M_F(name, _list_pair_it)(it->list_it, *ref); \ + } \ + } \ + \ + M_INLINE bool \ + M_F(name, _last_p)(const dict_it_t it) \ + { \ + M_ASSERT (it != NULL); \ + dict_it_t it2; \ + M_F(name,_it_set)(it2, it); \ + M_F(name, _next)(it2); \ + return M_F(name, _end_p)(it2); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const dict_it_t it1, const dict_it_t it2) \ + { \ + M_ASSERT (it1 != NULL && it2 != NULL); \ + return M_F(name, _list_pair_it_equal_p)(it1->list_it, \ + it2->list_it); \ + } \ + \ + M_INLINE it_deref_t * \ + M_F(name, _ref)(const dict_it_t it) \ + { \ + M_ASSERT(it != NULL); \ + /* NOTE: partially unsafe if the user modify the 'key' \ + in a non equivalent way */ \ + M_IF(isSet)( \ + return &(*M_F(name, _list_pair_ref)(it->list_it))->key; \ + , \ + return *M_F(name, _list_pair_ref)(it->list_it); \ + ) \ + } \ + \ + M_INLINE const it_deref_t * \ + M_F(name, _cref)(const dict_it_t it) \ + { \ + M_ASSERT(it != NULL); \ + M_IF(isSet)( \ + return &(*M_F(name, _list_pair_cref)(it->list_it))->key; \ + , \ + return *M_F(name, _list_pair_cref)(it->list_it); \ + ) \ + } \ + \ + M_D1CT_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t, it_deref_t) + + +/* Define additional functions for dictionnary (Common for all kinds of dictionnary). + Do not used any specific fields of the dictionnary but the public API + + It is not possible to define a method for IT_REMOVE: we could easily define it + by performing an _erase of the key get by the _cref method. However, + computing the next element is way harder. We could easily compute the next + element of the iteration (using _next). However with the _erase method, the + dict may perform a resize down operation, reducing the size of the array, + base of the dict, by two. This operation renders the computation of the + 'next' element impossible as the order of the elements in the dict + has fundamentaly changed in this case. We could detect this and restart + the iteration from the first element, but it wouldn't fit the contract + of the IT_REMOVE operator. + + HASH method for dictionnary itself seems hard to implement: + we have to handle the case where two dictionaries are structuraly + different, but functionnaly identical (seems they have the same + members, but put in a different order). + We cannot iterator over the dictionary to compute a hash, as the + order of the items in the dictionnary is not specified: they more + or less follow the hash of the keys, but if the low bits of the + hash of the key is equal, they order may be different. + Or if the table of the dictionnary has different values (this may + be avoided). + */ +#define M_D1CT_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t, it_deref_t) \ + \ + M_INLINE bool \ + M_F(name,_empty_p)(const dict_t map) \ + { \ + M_ASSERT(map != NULL); \ + return map->count == 0; \ + } \ + \ + M_INLINE size_t \ + M_F(name,_size)(const dict_t map) \ + { \ + M_ASSERT(map != NULL); \ + return map->count; \ + } \ + \ + M_IF_METHOD(EQUAL, value_oplist)( \ + M_INLINE bool \ + M_F(name, _equal_p)(const dict_t dict1, const dict_t dict2) \ + { \ + M_ASSERT (dict1 != NULL && dict2 != NULL); \ + /* NOTE: Key type has mandatory equal operator */ \ + /* First the easy cases */ \ + if (M_LIKELY (dict1->count != dict2->count)) \ + return false; \ + if (M_UNLIKELY (dict1->count == 0)) \ + return true; \ + /* Otherwise this is the slow path : \ + both dictionary may not have arrays with the same size, but \ + still the dictionaries shall be equal as they contain the same \ + items. */ \ + dict_it_t it; \ + for(M_F(name, _it)(it, dict1) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)) { \ + const it_deref_t *item = M_F(name, _cref)(it); \ + value_type *ptr = M_F(name, _get)(dict2, M_IF(isSet)(*item, item->key)); \ + if (ptr == NULL) \ + return false; \ + if (M_CALL_EQUAL(value_oplist, item->value, *ptr) == false) \ + return false; \ + } \ + return true; \ + } \ + , /* no value equal */ ) \ + \ + M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \ + M_INLINE void \ + M_F(name, _get_str)(m_string_t str, const dict_t dict, const bool append) \ + { \ + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "{"); \ + dict_it_t it; \ + bool print_comma = false; \ + for (M_F(name, _it)(it, dict) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + if (print_comma) \ + m_string_push_back (str, ','); \ + const it_deref_t *item = M_F(name, _cref)(it); \ + M_IF(isSet)( \ + M_CALL_GET_STR(key_oplist, str, *item, true); \ + , \ + M_CALL_GET_STR(key_oplist, str, item->key, true); \ + m_string_push_back (str, ':'); \ + M_CALL_GET_STR(value_oplist, str, item->value, true); \ + ) \ + print_comma = true; \ + } \ + m_string_push_back (str, '}'); \ + } \ + , /* no GET_STR */ ) \ + \ + M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)( \ + M_INLINE void \ + M_F(name, _out_str)(FILE *file, const dict_t dict) \ + { \ + M_ASSERT (file != NULL); \ + fputc ('{', file); \ + dict_it_t it; \ + bool print_comma = false; \ + for (M_F(name, _it)(it, dict) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + if (print_comma) \ + fputc (',', file); \ + const it_deref_t *item = M_F(name, _cref)(it); \ + M_IF(isSet)( \ + M_CALL_OUT_STR(key_oplist, file, *item); \ + , \ + M_CALL_OUT_STR(key_oplist, file, item->key); \ + fputc (':', file); \ + M_CALL_OUT_STR(value_oplist, file, item->value); \ + ) \ + print_comma = true; \ + } \ + fputc ('}', file); \ + } \ + , /* no OUT_STR */ ) \ + \ + M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)( \ + M_INLINE bool \ + M_F(name, _parse_str)(dict_t dict, const char str[], const char **endp) \ + { \ + M_ASSERT (str != NULL); \ + M_F(name, _reset)(dict); \ + bool success = false; \ + int c = m_core_str_nospace(&str); \ + if (M_UNLIKELY (c != '{')) { goto exit; } \ + c = m_core_str_nospace(&str); \ + if (M_UNLIKELY (c == '}')) { success = true; goto exit;} \ + if (M_UNLIKELY (c == 0)) { goto exit; } \ + str--; \ + key_type key; \ + M_IF(isSet)( ,value_type value); \ + M_CALL_INIT(key_oplist, key); \ + M_IF(isSet)( , M_CALL_INIT(value_oplist, value) ); \ + do { \ + c = m_core_str_nospace(&str); \ + str--; \ + bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \ + M_IF(isSet)( \ + if (b == false) { goto exit_clear; } \ + M_F(name, _push)(dict, key); \ + , \ + c = m_core_str_nospace(&str); \ + if (b == false || c != ':') { goto exit_clear; } \ + c = m_core_str_nospace(&str); \ + str--; \ + b = M_CALL_PARSE_STR(value_oplist, value, str, &str); \ + if (b == false) { goto exit_clear; } \ + M_F(name, _set_at)(dict, key, value); \ + ) \ + c = m_core_str_nospace(&str); \ + } while (c == ','); \ + success = (c == '}'); \ + exit_clear: \ + M_CALL_CLEAR(key_oplist, key); \ + M_CALL_CLEAR(value_oplist, value); \ + exit: \ + if (endp) *endp = str; \ + return success; \ + } \ + , /* no PARSE_STR */ ) \ + \ + M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)( \ + M_INLINE bool \ + M_F(name, _in_str)(dict_t dict, FILE *file) \ + { \ + M_ASSERT (file != NULL); \ + M_F(name, _reset)(dict); \ + int c = m_core_fgetc_nospace(file); \ + if (M_UNLIKELY (c != '{')) return false; \ + c = m_core_fgetc_nospace(file); \ + if (M_UNLIKELY(c == '}')) return true; \ + if (M_UNLIKELY (c == EOF)) return false; \ + ungetc(c, file); \ + key_type key; \ + M_IF(isSet)( ,value_type value); \ + M_CALL_INIT(key_oplist, key); \ + M_IF(isSet)( , M_CALL_INIT(value_oplist, value) ); \ + do { \ + c = m_core_fgetc_nospace(file); \ + if (M_UNLIKELY (c == EOF)) { break; } \ + ungetc(c, file); \ + bool b = M_CALL_IN_STR(key_oplist, key, file); \ + M_IF(isSet)( \ + if (M_UNLIKELY (b == false)) { break; } \ + M_F(name, _push)(dict, key); \ + , \ + c = m_core_fgetc_nospace(file); \ + if (M_UNLIKELY (b == false || c != ':')) { c = 0; break; } \ + c = m_core_fgetc_nospace(file); \ + if (M_UNLIKELY (c == EOF)) { break; } \ + ungetc(c, file); \ + b = M_CALL_IN_STR(value_oplist, value, file); \ + if (M_UNLIKELY (b == false)) { c = 0; break; } \ + M_F(name, _set_at)(dict, key, value); \ + ) \ + c = m_core_fgetc_nospace(file); \ + } while (c == ','); \ + M_CALL_CLEAR(key_oplist, key); \ + M_IF(isSet)(, M_CALL_CLEAR(value_oplist, value); ) \ + return c == '}'; \ + } \ + , /* no IN_STR */ ) \ + \ + M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, dict_t const t1) \ + { \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + const it_deref_t *item; \ + bool first_done = false; \ + dict_it_t it; \ + /* Format is different between associative container \ + & set container */ \ + M_IF(isSet)( \ + ret = f->m_interface->write_array_start(local, f, M_F(name, _size)(t1)); \ + for (M_F(name, _it)(it, t1) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + item = M_F(name, _cref)(it); \ + if (first_done) \ + ret |= f->m_interface->write_array_next(local, f); \ + ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item); \ + first_done = true; \ + } \ + ret |= f->m_interface->write_array_end(local, f); \ + , \ + ret = f->m_interface->write_map_start(local, f, M_F(name, _size)(t1)); \ + for (M_F(name, _it)(it, t1) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + item = M_F(name, _cref)(it); \ + if (first_done) \ + ret |= f->m_interface->write_map_next(local, f); \ + ret |= M_CALL_OUT_SERIAL(key_oplist, f, item->key); \ + ret |= f->m_interface->write_map_value(local, f); \ + ret |= M_CALL_OUT_SERIAL(value_oplist, f, item->value); \ + first_done = true; \ + } \ + ret |= f->m_interface->write_map_end(local, f); \ + ) \ + return ret & M_SERIAL_FAIL; \ + } \ + , /* no OUT_SERIAL */ ) \ + \ + M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(dict_t t1, m_serial_read_t f) \ + { \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + size_t estimated_size = 0; \ + key_type key; \ + M_F(name,_reset)(t1); \ + M_IF(isSet)( \ + ret = f->m_interface->read_array_start(local, f, &estimated_size); \ + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ + M_CALL_INIT(key_oplist, key); \ + do { \ + ret = M_CALL_IN_SERIAL(key_oplist, key, f); \ + if (ret != M_SERIAL_OK_DONE) { break; } \ + M_F(name, _push)(t1, key); \ + } while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \ + M_CALL_CLEAR(key_oplist, key); \ + , \ + value_type value; \ + ret = f->m_interface->read_map_start(local, f, &estimated_size); \ + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ + M_CALL_INIT(key_oplist, key); \ + M_CALL_INIT (value_oplist, value); \ + do { \ + ret = M_CALL_IN_SERIAL(key_oplist, key, f); \ + if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \ + ret = f->m_interface->read_map_value(local, f); \ + if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \ + ret = M_CALL_IN_SERIAL(value_oplist, value, f); \ + if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \ + M_F(name, _set_at)(t1, key, value); \ + } while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \ + M_CALL_CLEAR(key_oplist, key); \ + M_CALL_CLEAR(value_oplist, value); \ + ) /* End of IF isSet */ \ + return ret; \ + } \ + , /* no in_serial */ ) \ + \ + M_IF(isSet)( \ + M_INLINE void \ + M_F(name, _splice)(dict_t d1, dict_t d2) \ + { \ + dict_it_t it; \ + /* NOTE: Despite using set_at, the accessing of the item in d1 \ + is not as random as other uses of the HASH table as d2 \ + uses the same order than d1 */ \ + for (M_F(name, _it)(it, d2); !M_F(name, _end_p)(it); M_F(name, _next)(it)){ \ + const it_deref_t *item = M_F(name, _cref)(it); \ + M_F(name, _push)(d1, *item); \ + } \ + M_F(name, _reset)(d2); \ + } \ + , \ + M_IF_METHOD(ADD, value_oplist)( \ + M_INLINE void \ + M_F(name, _splice)(dict_t d1, dict_t d2) \ + { \ + dict_it_t it; \ + /* NOTE: Despite using set_at, the accessing of the item in d1 \ + is not as random as other uses of the HASH table as d2 \ + uses the same order than d1 */ \ + for (M_F(name, _it)(it, d2); !M_F(name, _end_p)(it); M_F(name, _next)(it)){ \ + const struct M_F(name, _pair_s) *item = M_F(name, _cref)(it); \ + value_type *ptr = M_F(name, _get)(d1, item->key); \ + if (ptr == NULL) { \ + M_F(name, _set_at)(d1, item->key, item->value); \ + } else { \ + M_CALL_ADD(value_oplist, *ptr, *ptr, item->value); \ + } \ + } \ + M_F(name, _reset)(d2); \ + } \ + , /* NO UPDATE */) ) \ + \ + M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF(isSet, name, dict_t, key_oplist, value_oplist) + + +/******************************** INTERNAL ***********************************/ + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_D1CT_OPLIST_P1(arg) M_D1CT_OPLIST_P2 arg + +/* Validation of the given oplists */ +#define M_D1CT_OPLIST_P2(name, key_oplist, value_oplist) \ + M_IF_OPLIST(key_oplist)(M_D1CT_OPLIST_P3, M_D1CT_OPLIST_FAILURE)(name, key_oplist, value_oplist) +#define M_D1CT_OPLIST_P3(name, key_oplist, value_oplist) \ + M_IF_OPLIST(value_oplist)(M_D1CT_OPLIST_P4, M_D1CT_OPLIST_FAILURE)(name, key_oplist, value_oplist) + +/* Prepare a clean compilation failure */ +#define M_D1CT_OPLIST_FAILURE(name, key_oplist, value_oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_DICT_OPLIST_IS_NOT_AN_OPLIST, name, key_oplist, value_oplist))) + +/* Define the oplist of a dictionnary + NOTE: IT_REF is not exported so that the contained appears as not modifiable + by algorithm.*/ +#define M_D1CT_OPLIST_P4(name, key_oplist, value_oplist) \ + (INIT(M_F(name, _init)), \ + INIT_SET(M_F(name, _init_set)), \ + INIT_WITH(API_1(M_INIT_KEY_VAI)), \ + SET(M_F(name, _set)), \ + CLEAR(M_F(name, _clear)), \ + INIT_MOVE(M_F(name, _init_move)), \ + MOVE(M_F(name, _move)), \ + SWAP(M_F(name, _swap)), \ + RESET(M_F(name, _reset)), \ + NAME(name), \ + TYPE(M_F(name, _ct)), \ + SUBTYPE(M_F(name, _subtype_ct)), \ + EMPTY_P(M_F(name,_empty_p)), \ + IT_TYPE(M_F(name, _it_ct)), \ + IT_FIRST(M_F(name,_it)), \ + IT_SET(M_F(name, _it_set)), \ + IT_END(M_F(name,_it_end)), \ + IT_END_P(M_F(name,_end_p)), \ + IT_LAST_P(M_F(name,_last_p)), \ + IT_NEXT(M_F(name,_next)), \ + IT_CREF(M_F(name,_cref)) \ + ,KEY_TYPE(M_F(name, _key_ct)) \ + ,VALUE_TYPE(M_F(name, _value_ct)) \ + ,SET_KEY(M_F(name, _set_at)) \ + ,GET_KEY(M_F(name, _get)) \ + ,SAFE_GET_KEY(M_F(name, _safe_get)) \ + ,ERASE_KEY(M_F(name, _erase)) \ + ,KEY_OPLIST(key_oplist) \ + ,VALUE_OPLIST(value_oplist) \ + ,GET_SIZE(M_F(name, _size)) \ + ,M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)(GET_STR(M_F(name, _get_str)),) \ + ,M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(M_F(name, _parse_str)),) \ + ,M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_F(name, _out_str)),) \ + ,M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(M_F(name, _in_str)),) \ + ,M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \ + ,M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(M_F(name, _in_serial)),) \ + ,M_IF_METHOD(EQUAL, value_oplist)(EQUAL(M_F(name, _equal_p)),) \ + ) + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_D1CT_SET_OPLIST_P1(arg) M_D1CT_SET_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_D1CT_SET_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_D1CT_SET_OPLIST_P3, M_D1CT_SET_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_D1CT_SET_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_DICT_SET_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* Define the oplist of a set + Note: IT_REF is not exported so that the contained appears as not modifiable +*/ +#define M_D1CT_SET_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)), \ + INIT_SET(M_F(name, _init_set)), \ + INIT_WITH(API_1(M_INIT_VAI)), \ + SET(M_F(name, _set)), \ + CLEAR(M_F(name, _clear)), \ + INIT_MOVE(M_F(name, _init_move)), \ + MOVE(M_F(name, _move)), \ + SWAP(M_F(name, _swap)), \ + RESET(M_F(name, _reset)), \ + NAME(name), \ + TYPE(M_F(name, _ct)), \ + SUBTYPE(M_F(name, _subtype_ct)), \ + EMPTY_P(M_F(name,_empty_p)), \ + PUSH(M_F(name,_push)), \ + KEY_TYPE(M_F(name, _key_ct)), \ + VALUE_TYPE(M_F(name, _key_ct)), \ + GET_KEY(M_F(name, _get)), \ + SAFE_GET_KEY(M_F(name, _safe_get)), \ + ERASE_KEY(M_F(name, _erase)), \ + KEY_OPLIST(oplist), \ + VALUE_OPLIST(oplist), \ + GET_SIZE(M_F(name, _size)), \ + IT_TYPE(M_F(name, _it_ct)), \ + IT_FIRST(M_F(name,_it)), \ + IT_SET(M_F(name, _it_set)), \ + IT_END(M_F(name,_it_end)), \ + IT_END_P(M_F(name,_end_p)), \ + IT_LAST_P(M_F(name,_last_p)), \ + IT_NEXT(M_F(name,_next)), \ + IT_CREF(M_F(name,_cref)) \ + ,OPLIST(oplist) \ + ,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \ + ,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \ + ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \ + ,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \ + ,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \ + ,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \ + ,EQUAL(M_F(name, _equal_p)), \ + ) + +/* Define Lower Bound for hash table (TODO: Common macro for both implementation) */ +#ifndef M_D1CT_LOWER_BOUND +#define M_D1CT_LOWER_BOUND(s) ((s) <= M_D1CT_INITIAL_SIZE ? 0 : (s) * 1 / 4) +#endif + +/* Define Lower Bound for hash table (TODO: Common macro for both implementation) */ +#ifndef M_D1CT_UPPER_BOUND +#define M_D1CT_UPPER_BOUND(s) ((s) * 2 / 3) +#endif + +/* Define initial size of the hash table */ +#ifndef M_D1CT_INITIAL_SIZE +#define M_D1CT_INITIAL_SIZE 16 +#endif + +#define M_D1CT_CONTRACT(name, map) do { \ + M_ASSERT(map != NULL); \ + M_ASSERT(map->count <= map->upper_limit); \ + M_ASSERT(map->upper_limit >= M_D1CT_UPPER_BOUND(M_D1CT_INITIAL_SIZE)); \ + M_ASSERT(map->count >= map->lower_limit); \ + M_ASSERT(M_POWEROF2_P(M_F(name, _array_list_pair_size)(map->table))); \ + } while (0) + + +/******************************** INTERNAL ***********************************/ + +enum m_d1ct_oa_element_e { + M_D1CT_OA_EMPTY = 0, M_D1CT_OA_DELETED = 1 +}; + +/* Performing Quadratic probing + Replace it by '1' to perform linear probing */ +#ifdef M_USE_DICT_OA_PROBING +# define M_D1CT_OA_PROBING M_USE_DICT_OA_PROBING +#else +# define M_D1CT_OA_PROBING(s) ((s)++) +#endif + +/* Lower Bound of the hash table (TODO: Common macro for both dictionnary) */ +#ifndef M_D1CT_OA_LOWER_BOUND +#define M_D1CT_OA_LOWER_BOUND 0.2 +#endif +/* Upper Bound of the hash table (TODO: Common macro for both dictionnary) */ +#ifndef M_D1CT_OA_UPPER_BOUND +#define M_D1CT_OA_UPPER_BOUND 0.7 +#endif + +#define M_D1CT_OA_CONTRACT(dict) do { \ + M_ASSERT ( (dict) != NULL); \ + M_ASSERT( (dict)->lower_limit <= (dict)->count); \ + M_ASSERT( (dict)->count <= (dict)->upper_limit ); \ + M_ASSERT( (dict)->data != NULL); \ + M_ASSERT( M_POWEROF2_P((dict)->mask+1)); \ + M_ASSERT( (dict)->mask+1 >= M_D1CT_INITIAL_SIZE); \ + M_ASSERT( (dict)->upper_limit <= (dict)->mask+1); \ + } while (0) + +#define M_D1CT_OA_DEF_P1(args) M_ID( M_D1CT_OA_DEF_P2 args ) + +/* Validate the key oplist before going further */ +#define M_D1CT_OA_DEF_P2(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_IF_OPLIST(key_oplist)(M_D1CT_OA_DEF_P3, M_D1CT_OA_DEF_FAILURE)(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) + +/* Validate the value oplist before going further */ +#define M_D1CT_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_IF_OPLIST(value_oplist)(M_D1CT_OA_DEF_P4, M_D1CT_OA_DEF_FAILURE)(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) + +/* Stop processing with a compilation failure */ +#define M_D1CT_OA_DEF_FAILURE(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(DICT_OA_DEF2): at least one of the given argument is not a valid oplist: " M_AS_STR(key_oplist) " / " M_AS_STR(value_oplist) ) + +#define M_D1CT_OA_DEF_P4(name, key_type, key_oplist, value_type, value_oplist, dict_t, dict_it_t, it_deref_t) \ + M_D1CT_OA_DEF_P5(name, key_type, key_oplist, value_type, value_oplist, 0, \ + M_D1CT_OA_LOWER_BOUND, M_D1CT_OA_UPPER_BOUND, \ + dict_t, dict_it_t, it_deref_t) + +#define M_D1CT_OASET_DEF_P1(args) M_ID( M_D1CT_OASET_DEF_P2 args ) + +/* Validate the value oplist before going further */ +#define M_D1CT_OASET_DEF_P2(name, key_type, key_oplist, dict_t, dict_it_t, it_deref_t) \ + M_IF_OPLIST(key_oplist)(M_D1CT_OASET_DEF_P4, M_D1CT_OASET_DEF_FAILURE)(name, key_type, key_oplist, dict_t, dict_it_t, it_deref_t) + +/* Stop processing with a compilation failure */ +#define M_D1CT_OASET_DEF_FAILURE(name, key_type, key_oplist, dict_t, dict_it_t, it_deref_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(DICT_OASET_DEF): the given argument is not a valid oplist: " M_AS_STR(key_oplist) ) + +#define M_D1CT_OASET_DEF_P4(name, key_type, key_oplist, dict_t, dict_it_t, it_deref_t) \ + M_D1CT_OA_DEF_P5(name, key_type, key_oplist, key_type, M_EMPTY_OPLIST, 1, \ + M_D1CT_OA_LOWER_BOUND, M_D1CT_OA_UPPER_BOUND, dict_t, dict_it_t, it_deref_t ) + +#define M_D1CT_OA_DEF_P5(name, key_type, key_oplist, value_type, value_oplist, isSet, coeff_down, coeff_up, dict_t, dict_it_t, it_deref_t) \ + \ + /* NOTE: \ + if isSet is true, all methods of value_oplist are NOP methods */ \ + \ + typedef struct M_F(name, _pair_s) { \ + key_type key; \ + M_IF(isSet)( , value_type value;) \ + } M_F(name, _pair_ct); \ + \ + /* Define type returned by the _ref method of an iterator */ \ + M_IF(isSet)( \ + typedef key_type it_deref_t; \ + , \ + typedef struct M_F(name, _pair_s) it_deref_t; \ + ) \ + \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, key_type, key_oplist) \ + M_CHECK_COMPATIBLE_OPLIST(name, 2, value_type, value_oplist) \ + \ + /* NOTE: We don't want a real oplist for this sub type */ \ + ARRAY_DEF(M_F(name, _array_pair), M_F(name, _pair_ct), \ + (INIT(M_NOTHING_DEFAULT), SET(M_MEMCPY_DEFAULT), \ + INIT_SET(M_MEMCPY_DEFAULT), CLEAR(M_NOTHING_DEFAULT))) \ + \ + typedef struct M_F(name,_s) { \ + size_t mask, count, count_delete; \ + size_t upper_limit, lower_limit; \ + struct M_F(name, _pair_s) *data; \ + } dict_t[1]; \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + typedef struct M_F(name, _it_s) { \ + const struct M_F(name,_s) *dict; \ + size_t index; \ + } dict_it_t[1]; \ + \ + /* Define internal types for oplist */ \ + typedef dict_t M_F(name, _ct); \ + typedef it_deref_t M_F(name, _subtype_ct); \ + typedef key_type M_F(name, _key_ct); \ + typedef value_type M_F(name, _value_ct); \ + typedef dict_it_t M_F(name, _it_ct); \ + \ + M_INLINE void \ + M_C3(m_d1ct_,name,_update_limit)(dict_t dict, size_t size) \ + { \ + /* FIXME: Overflow not handled. What to do in case of it? */ \ + dict->upper_limit = (size_t) ((double) size * coeff_up) - 1; \ + dict->lower_limit = (size <= M_D1CT_INITIAL_SIZE) ? 0 : (size_t) ((double) size * coeff_down) ; \ + } \ + \ + M_INLINE void \ + M_F(name, _init)(dict_t dict) \ + { \ + M_ASSERT(0 <= (coeff_down) && (coeff_down)*2 < (coeff_up) && (coeff_up) < 1); \ + dict->mask = M_D1CT_INITIAL_SIZE-1; \ + dict->count = 0; \ + dict->count_delete = 0; \ + M_C3(m_d1ct_,name,_update_limit)(dict, M_D1CT_INITIAL_SIZE); \ + dict->data = M_CALL_REALLOC(key_oplist, M_F(name, _pair_ct), NULL, M_D1CT_INITIAL_SIZE); \ + if (M_UNLIKELY_NOMEM (dict->data == NULL)) { \ + M_MEMORY_FULL(sizeof (M_F(name, _pair_ct)) * M_D1CT_INITIAL_SIZE); \ + return ; \ + } \ + /* Populate the initial table with the 'empty' representation */ \ + for(size_t i = 0; i < M_D1CT_INITIAL_SIZE; i++) { \ + M_CALL_OOR_SET(key_oplist, dict->data[i].key, M_D1CT_OA_EMPTY); \ + M_ASSERT(M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, M_D1CT_OA_EMPTY)); \ + } \ + M_D1CT_OA_CONTRACT(dict); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(dict_t dict) \ + { \ + M_D1CT_OA_CONTRACT(dict); \ + for(size_t i = 0; i <= dict->mask; i++) { \ + if (!M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, M_D1CT_OA_EMPTY) \ + && !M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, M_D1CT_OA_DELETED)) { \ + M_CALL_CLEAR(key_oplist, dict->data[i].key); \ + M_CALL_CLEAR(value_oplist, dict->data[i].value); \ + } \ + } \ + M_CALL_FREE(key_oplist, dict->data); \ + /* Not really needed, but safer */ \ + dict->mask = 0; \ + dict->data = NULL; \ + } \ + \ + M_INLINE value_type * \ + M_F(name, _get)(const dict_t dict, key_type const key) \ + { \ + M_D1CT_OA_CONTRACT(dict); \ + /* NOTE: Key can not be the representation of empty or deleted */ \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, key, M_D1CT_OA_EMPTY)); \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, key, M_D1CT_OA_DELETED)); \ + \ + M_F(name, _pair_ct) *const data = dict->data; \ + const size_t mask = dict->mask; \ + size_t p = M_CALL_HASH(key_oplist, key) & mask; \ + \ + /* Random access, and probably cache miss */ \ + if (M_LIKELY (M_CALL_EQUAL(key_oplist, data[p].key, key)) ) \ + return &data[p].M_IF(isSet)(key, value); \ + else if (M_LIKELY (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY)) ) \ + return NULL; \ + \ + /* Unlikely case */ \ + size_t s = 1; \ + do { \ + p = (p + M_D1CT_OA_PROBING(s)) & mask; \ + /* data[p].key may be OA_DELETED or OA_EMPTY */ \ + if (M_CALL_EQUAL(key_oplist, data[p].key, key)) \ + return &data[p].M_IF(isSet)(key, value); \ + M_ASSERT (s <= dict->mask); \ + } while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ); \ + \ + return NULL; \ + } \ + \ + M_INLINE value_type const * \ + M_F(name, _cget)(const dict_t map, key_type const key) \ + { \ + return M_CONST_CAST(value_type, M_F(name,_get)(map,key)); \ + } \ + \ + M_IF_DEBUG( \ + M_INLINE bool \ + M_C3(m_d1ct_,name,_control_after_resize)(const dict_t h) \ + { \ + /* This function checks if the reshashing of the dict is ok */ \ + M_F(name, _pair_ct) *data = h->data; \ + size_t empty = 0; \ + size_t del = 0; \ + /* Count the number of empty elements and the number of deleted */ \ + for(size_t i = 0 ; i <= h->mask ; i++) { \ + empty += M_CALL_OOR_EQUAL(key_oplist, data[i].key, M_D1CT_OA_EMPTY); \ + del += M_CALL_OOR_EQUAL(key_oplist, data[i].key, M_D1CT_OA_DELETED); \ + } \ + M_ASSERT(del == 0); \ + M_ASSERT(empty + h->count == h->mask + 1); \ + return true; \ + } \ + ) \ + \ + M_INLINE void \ + M_C3(m_d1ct_,name,_resize_up)(dict_t h, size_t newSize, bool updateLimit) \ + { \ + size_t oldSize = h->mask+1; \ + M_ASSERT (newSize >= oldSize); \ + M_ASSERT (M_POWEROF2_P(newSize)); \ + M_F(name, _pair_ct) *data = h->data; \ + /* resize can be called just to delete the items */ \ + if (newSize > oldSize) { \ + data = M_CALL_REALLOC(key_oplist, M_F(name, _pair_ct), data, newSize); \ + if (M_UNLIKELY_NOMEM (data == NULL) ) { \ + M_MEMORY_FULL(sizeof (M_F(name, _pair_ct)) * newSize); \ + return ; \ + } \ + \ + /* First mark the extended space as empty */ \ + for(size_t i = oldSize ; i < newSize; i++) \ + M_CALL_OOR_SET(key_oplist, data[i].key, M_D1CT_OA_EMPTY); \ + } \ + \ + /* Then let's rehash all the entries in their **exact** position. \ + If we can't, let's put them in the 'tmp' array. \ + It has been measured that the size of this 'tmp' array is \ + around 6% of the size of updated dictionnary. \ + NOTE: This should be much cache friendly than typical hash code */ \ + M_F(name, _array_pair_ct) tmp; \ + M_F(name, _array_pair_init)(tmp); \ + const size_t mask = (newSize -1); \ + \ + for(size_t i = 0 ; i < oldSize; i++) { \ + if (!M_CALL_OOR_EQUAL(key_oplist, data[i].key, M_D1CT_OA_DELETED) \ + && !M_CALL_OOR_EQUAL(key_oplist, data[i].key, M_D1CT_OA_EMPTY)) { \ + size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \ + if (p != i) { \ + if (M_LIKELY (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) \ + || M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_DELETED))) { \ + M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \ + M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \ + } else { \ + M_F(name, _pair_ct) *ptr = M_F(name, _array_pair_push_raw) (tmp); \ + M_DO_INIT_MOVE(key_oplist, ptr->key, data[i].key); \ + M_DO_INIT_MOVE(value_oplist, ptr->value, data[i].value); \ + } \ + M_CALL_OOR_SET(key_oplist, data[i].key, M_D1CT_OA_EMPTY); \ + } \ + } else { \ + M_CALL_OOR_SET(key_oplist, data[i].key, M_D1CT_OA_EMPTY); \ + } \ + } \ + \ + /* Let's put back the entries in the tmp array in their right place */ \ + /* NOTE: There should be very few entries in this array \ + which contains what we weren't be able to fit in the first pass */ \ + while (M_F(name, _array_pair_size)(tmp) > 0) { \ + M_F(name, _pair_ct) const *item = M_F(name, _array_pair_back)(tmp); \ + size_t p = M_CALL_HASH(key_oplist, item->key) & mask; \ + /* NOTE: since the first pass, the bucket might be free now */ \ + if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY)) { \ + size_t s = 1; \ + do { \ + p = (p + M_D1CT_OA_PROBING(s)) & mask; \ + M_ASSERT (s <= h->mask); \ + } while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ); \ + } \ + M_F(name, _array_pair_pop_move)(&data[p], tmp); \ + } \ + \ + M_F(name, _array_pair_clear) (tmp); \ + h->mask = newSize-1; \ + h->count_delete = h->count; \ + if (updateLimit == true) { \ + M_C3(m_d1ct_,name,_update_limit)(h, newSize); \ + } \ + h->data = data; \ + M_IF_DEBUG (M_ASSERT (M_C3(m_d1ct_,name,_control_after_resize)(h));) \ + M_D1CT_OA_CONTRACT(h); \ + } \ + \ + M_INLINE void \ + M_IF(isSet)(M_F(name, _push), M_F(name,_set_at)) \ + (dict_t dict, key_type const key \ + M_IF(isSet)(, M_DEFERRED_COMMA value_type const value) ) \ + { \ + M_D1CT_OA_CONTRACT(dict); \ + /* NOTE: key can not be the representation of empty or deleted */ \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, key, M_D1CT_OA_EMPTY)); \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, key, M_D1CT_OA_DELETED)); \ + \ + M_F(name, _pair_ct) *const data = dict->data; \ + const size_t mask = dict->mask; \ + size_t p = M_CALL_HASH(key_oplist, key) & mask; \ + \ + /* NOTE: Likely cache miss */ \ + if (M_UNLIKELY (M_CALL_EQUAL(key_oplist, data[p].key, key)) ) { \ + M_CALL_SET(value_oplist, data[p].value, value); \ + return; \ + } \ + if (M_UNLIKELY (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ) ) { \ + /* Find the insertion point as the bucket[] is not empty */ \ + size_t delPos = SIZE_MAX; \ + if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_DELETED)) delPos = p; \ + size_t s = 1U; \ + do { \ + p = (p + M_D1CT_OA_PROBING(s)) & mask; \ + if (M_CALL_EQUAL(key_oplist, data[p].key, key)) { \ + M_CALL_SET(value_oplist, data[p].value, value); \ + return; \ + } \ + M_ASSERT (s <= dict->mask); \ + if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_DELETED) \ + && (delPos == (size_t)-1)) { \ + delPos = p; \ + } \ + } while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ); \ + if (delPos != SIZE_MAX) { \ + p = delPos; \ + dict->count_delete --; \ + } \ + } \ + \ + M_CALL_INIT_SET(key_oplist, data[p].key, key); \ + M_CALL_INIT_SET(value_oplist, data[p].value, value); \ + dict->count++; \ + dict->count_delete ++; \ + \ + if (M_UNLIKELY (dict->count_delete >= dict->upper_limit)) { \ + size_t newSize = dict->mask+1; \ + if (dict->count > (dict->mask / 2)) { \ + newSize += newSize; \ + if (M_UNLIKELY_NOMEM (newSize <= dict->mask+1)) { \ + M_MEMORY_FULL((size_t)-1); \ + } \ + } \ + M_C3(m_d1ct_,name,_resize_up)(dict, newSize, true); \ + } \ + M_D1CT_OA_CONTRACT(dict); \ + } \ + \ + M_INLINE value_type * \ + M_F(name,_safe_get)(dict_t dict, key_type const key) \ + { \ + M_D1CT_OA_CONTRACT(dict); \ + /* NOTE: key can not be the representation of empty or deleted */ \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, key, M_D1CT_OA_EMPTY)); \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, key, M_D1CT_OA_DELETED)); \ + \ + M_F(name, _pair_ct) *const data = dict->data; \ + const size_t mask = dict->mask; \ + size_t p = M_CALL_HASH(key_oplist, key) & mask; \ + \ + if (M_CALL_EQUAL(key_oplist, data[p].key, key)) { \ + return &data[p].M_IF(isSet)(key, value); \ + } \ + if (M_UNLIKELY (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ) ) { \ + size_t delPos = SIZE_MAX; \ + if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_DELETED)) delPos = p; \ + size_t s = 1U; \ + do { \ + p = (p + M_D1CT_OA_PROBING(s)) & mask; \ + if (M_CALL_EQUAL(key_oplist, data[p].key, key)) { \ + return &data[p].M_IF(isSet)(key, value); \ + } \ + M_ASSERT (s <= dict->mask); \ + if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_DELETED) \ + && (delPos == (size_t)-1)) { \ + delPos = p; \ + } \ + } while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ); \ + if (delPos != SIZE_MAX) { \ + p = delPos; \ + dict->count_delete --; \ + } \ + } \ + \ + M_CALL_INIT_SET(key_oplist, data[p].key, key); \ + M_CALL_INIT(value_oplist, data[p].value); \ + dict->count++; \ + dict->count_delete ++; \ + \ + if (M_UNLIKELY (dict->count_delete >= dict->upper_limit)) { \ + size_t newSize = dict->mask+1; \ + if (dict->count > (dict->mask / 2)) { \ + newSize += newSize; \ + if (M_UNLIKELY_NOMEM (newSize <= dict->mask+1)) { \ + M_MEMORY_FULL((size_t)-1); \ + } \ + } \ + M_C3(m_d1ct_,name,_resize_up)(dict, newSize, true); \ + /* data is now invalid */ \ + return M_F(name, _get)(dict, key); \ + } \ + M_D1CT_OA_CONTRACT(dict); \ + return &data[p].M_IF(isSet)(key, value); \ + } \ + M_INLINE M_ATTR_DEPRECATED value_type * \ + M_F(name,_get_at)(dict_t dict, key_type const key) \ + { \ + return M_F(name,_safe_get)(dict, key); \ + } \ + \ + M_INLINE void \ + M_C3(m_d1ct_,name,_resize_down)(dict_t h, size_t newSize) \ + { \ + size_t oldSize = h->mask+1; \ + M_ASSERT (newSize <= oldSize && M_POWEROF2_P(newSize)); \ + if (M_UNLIKELY (newSize < M_D1CT_INITIAL_SIZE)) \ + newSize = M_D1CT_INITIAL_SIZE; \ + const size_t mask = newSize -1; \ + M_F(name, _pair_ct) *data = h->data; \ + M_F(name, _array_pair_ct) tmp; \ + M_F(name, _array_pair_init)(tmp); \ + \ + /* Pass 1: scan lower entries, and move them if needed */ \ + for(size_t i = 0; i < newSize; i++) { \ + if (M_CALL_OOR_EQUAL(key_oplist, data[i].key, M_D1CT_OA_EMPTY)) \ + continue; \ + if (M_CALL_OOR_EQUAL(key_oplist, data[i].key, M_D1CT_OA_DELETED)) { \ + M_CALL_OOR_SET(key_oplist, data[i].key, M_D1CT_OA_EMPTY); \ + continue; \ + } \ + size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \ + if (p != i) { \ + if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) \ + || M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_DELETED)) { \ + M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \ + M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \ + } else { \ + M_F(name, _pair_ct) *ptr = M_F(name, _array_pair_push_raw) (tmp); \ + M_DO_INIT_MOVE(key_oplist, ptr->key, data[i].key); \ + M_DO_INIT_MOVE(value_oplist, ptr->value, data[i].value); \ + } \ + M_CALL_OOR_SET(key_oplist, data[i].key, M_D1CT_OA_EMPTY); \ + } \ + } \ + /* Pass 2: scan upper entries and move them back */ \ + for(size_t i = newSize; i < oldSize; i++) { \ + if (!M_CALL_OOR_EQUAL(key_oplist, data[i].key, M_D1CT_OA_DELETED) \ + && !M_CALL_OOR_EQUAL(key_oplist, data[i].key, M_D1CT_OA_EMPTY)) { \ + size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \ + M_ASSERT (p < i); \ + if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY)) { \ + size_t s = 1; \ + do { \ + p = (p + M_D1CT_OA_PROBING(s)) & mask; \ + M_ASSERT (s <= h->mask); \ + } while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ); \ + } \ + M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \ + M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \ + } \ + } \ + /* Pass 3: scan moved entries and move them back */ \ + while (M_F(name, _array_pair_size)(tmp) > 0) { \ + M_F(name, _pair_ct) const *item = M_F(name, _array_pair_back)(tmp); \ + size_t p = M_CALL_HASH(key_oplist, item->key) & mask; \ + if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY)) { \ + size_t s = 1; \ + do { \ + p = (p + M_D1CT_OA_PROBING(s)) & mask; \ + M_ASSERT (s <= h->mask); \ + } while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ); \ + } \ + M_F(name, _array_pair_pop_move)(&data[p], tmp); \ + } \ + \ + M_F(name, _array_pair_clear) (tmp); \ + h->count_delete = h->count; \ + if (newSize != oldSize) { \ + h->mask = newSize-1; \ + M_C3(m_d1ct_,name,_update_limit)(h, newSize); \ + h->data = M_CALL_REALLOC(key_oplist, M_F(name, _pair_ct), data, newSize); \ + M_ASSERT (h->data != NULL); \ + } \ + M_IF_DEBUG (M_ASSERT (M_C3(m_d1ct_,name,_control_after_resize)(h));) \ + M_ASSERT (h->lower_limit < h->count && h->count < h->upper_limit); \ + M_D1CT_OA_CONTRACT(h); \ + } \ + \ + M_INLINE bool \ + M_F(name,_erase)(dict_t dict, key_type const key) \ + { \ + M_D1CT_OA_CONTRACT(dict); \ + /* NOTE: key can't be the representation of empty or deleted */ \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, key, M_D1CT_OA_EMPTY)); \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, key, M_D1CT_OA_DELETED)); \ + \ + M_F(name, _pair_ct) *const data = dict->data; \ + const size_t mask = dict->mask; \ + size_t p = M_CALL_HASH(key_oplist, key) & mask; \ + \ + /* Random access, and probably cache miss */ \ + if (M_UNLIKELY (!M_CALL_EQUAL(key_oplist, data[p].key, key)) ) { \ + if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY)) \ + return false; \ + size_t s = 1; \ + do { \ + p = (p + M_D1CT_OA_PROBING(s)) & mask; \ + if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, M_D1CT_OA_EMPTY) ) \ + return false; \ + M_ASSERT (s <= dict->mask); \ + } while (!M_CALL_EQUAL(key_oplist, data[p].key, key)); \ + } \ + M_CALL_CLEAR(key_oplist, data[p].key); \ + M_CALL_CLEAR(value_oplist, data[p].value); \ + M_CALL_OOR_SET(key_oplist, data[p].key, M_D1CT_OA_DELETED); \ + M_ASSERT (dict->count >= 1); \ + dict->count--; \ + if (M_UNLIKELY (dict->count < dict->lower_limit)) { \ + M_C3(m_d1ct_,name,_resize_down)(dict, (dict->mask+1) >> 1); \ + } \ + M_D1CT_OA_CONTRACT(dict); \ + return true; \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(dict_t map, const dict_t org) \ + { \ + M_D1CT_OA_CONTRACT(org); \ + M_ASSERT (map != org); \ + map->mask = org->mask; \ + map->count = org->count; \ + map->count_delete = org->count_delete; \ + map->upper_limit = org->upper_limit; \ + map->lower_limit = org->lower_limit; \ + map->data = M_CALL_REALLOC(key_oplist, M_F(name, _pair_ct), NULL, map->mask+1); \ + if (M_UNLIKELY_NOMEM (map->data == NULL)) { \ + M_MEMORY_FULL(sizeof (M_F(name, _pair_ct)) * (map->mask+1)); \ + return ; \ + } \ + for(size_t i = 0; i <= org->mask; i++) { \ + if (M_CALL_OOR_EQUAL(key_oplist, org->data[i].key, M_D1CT_OA_EMPTY)) { \ + M_CALL_OOR_SET(key_oplist, map->data[i].key, M_D1CT_OA_EMPTY); \ + } else if (M_CALL_OOR_EQUAL(key_oplist, org->data[i].key, M_D1CT_OA_DELETED)) { \ + M_CALL_OOR_SET(key_oplist, map->data[i].key, M_D1CT_OA_DELETED); \ + } else { \ + M_CALL_INIT_SET(key_oplist, map->data[i].key, org->data[i].key); \ + M_CALL_INIT_SET(value_oplist, map->data[i].value, org->data[i].value); \ + } \ + } \ + M_D1CT_OA_CONTRACT(map); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(dict_t map, const dict_t org) \ + { \ + M_D1CT_OA_CONTRACT(map); \ + M_D1CT_OA_CONTRACT(org); \ + if (M_LIKELY (map != org)) { \ + M_F(name, _clear)(map); \ + M_F(name, _init_set)(map, org); \ + } \ + M_D1CT_OA_CONTRACT(map); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(dict_t map, dict_t org) \ + { \ + M_D1CT_OA_CONTRACT(org); \ + M_ASSERT (map != org); \ + map->mask = org->mask; \ + map->count = org->count; \ + map->count_delete = org->count_delete; \ + map->upper_limit = org->upper_limit; \ + map->lower_limit = org->lower_limit; \ + map->data = org->data; \ + /* Mark org as cleared (safety) */ \ + org->mask = 0; \ + org->data = NULL; \ + M_D1CT_OA_CONTRACT(map); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(dict_t map, dict_t org) \ + { \ + M_D1CT_OA_CONTRACT(map); \ + M_D1CT_OA_CONTRACT(org); \ + if (M_LIKELY (map != org)) { \ + M_F(name, _clear)(map); \ + M_F(name, _init_move)(map, org); \ + } \ + M_D1CT_OA_CONTRACT(map); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(dict_t d1, dict_t d2) \ + { \ + M_D1CT_OA_CONTRACT(d1); \ + M_D1CT_OA_CONTRACT(d2); \ + M_SWAP (size_t, d1->mask, d2->mask); \ + M_SWAP (size_t, d1->count, d2->count); \ + M_SWAP (size_t, d1->count_delete, d2->count_delete); \ + M_SWAP (size_t, d1->upper_limit, d2->upper_limit); \ + M_SWAP (size_t, d1->lower_limit, d2->lower_limit); \ + M_SWAP (M_F(name, _pair_ct) *, d1->data, d2->data); \ + M_D1CT_OA_CONTRACT(d1); \ + M_D1CT_OA_CONTRACT(d2); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(dict_t d) \ + { \ + M_D1CT_OA_CONTRACT(d); \ + for(size_t i = 0; i <= d->mask; i++) { \ + if (!M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, M_D1CT_OA_EMPTY) \ + && !M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, M_D1CT_OA_DELETED)) { \ + M_CALL_CLEAR(key_oplist, d->data[i].key); \ + M_CALL_CLEAR(value_oplist, d->data[i].value); \ + } \ + } \ + d->count = 0; \ + d->count_delete = 0; \ + d->mask = M_D1CT_INITIAL_SIZE-1; \ + M_C3(m_d1ct_,name,_update_limit)(d, M_D1CT_INITIAL_SIZE); \ + d->data = M_CALL_REALLOC(key_oplist, M_F(name, _pair_ct), \ + d->data, M_D1CT_INITIAL_SIZE); \ + M_ASSERT(d->data != NULL); \ + for(size_t i = 0; i <= d->mask; i++) { \ + M_CALL_OOR_SET(key_oplist, d->data[i].key, M_D1CT_OA_EMPTY); \ + } \ + M_D1CT_OA_CONTRACT(d); \ + } \ + \ + M_INLINE void M_ATTR_DEPRECATED \ + M_F(name, _clean)(dict_t d) \ + { \ + M_F(name, _reset)(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(dict_it_t it, const dict_t d) \ + { \ + M_D1CT_OA_CONTRACT(d); \ + M_ASSERT (it != NULL); \ + it->dict = d; \ + size_t i = 0; \ + while (i <= d->mask \ + && (M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, M_D1CT_OA_EMPTY) \ + || M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, M_D1CT_OA_DELETED))) { \ + i++; \ + } \ + it->index = i; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(dict_it_t it, const dict_it_t ref) \ + { \ + M_ASSERT (it != NULL); \ + M_ASSERT (ref != NULL); \ + it->dict = ref->dict; \ + it->index = ref->index; \ + M_D1CT_OA_CONTRACT (it->dict); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_last)(dict_it_t it, const dict_t d) \ + { \ + M_D1CT_OA_CONTRACT(d); \ + M_ASSERT (it != NULL); \ + it->dict = d; \ + size_t i = d->mask; \ + while (i <= d->mask \ + && (M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, M_D1CT_OA_EMPTY) \ + || M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, M_D1CT_OA_DELETED))) { \ + i--; \ + } \ + it->index = i; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(dict_it_t it, const dict_t d) \ + { \ + M_D1CT_OA_CONTRACT(d); \ + M_ASSERT (it != NULL); \ + it->dict = d; \ + it->index = d->mask+1; \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(const dict_it_t it) \ + { \ + M_ASSERT (it != NULL); \ + M_D1CT_OA_CONTRACT (it->dict); \ + return it->index > it->dict->mask; \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(dict_it_t it) \ + { \ + M_ASSERT (it != NULL); \ + M_D1CT_OA_CONTRACT (it->dict); \ + size_t i = it->index + 1; \ + while (i <= it->dict->mask && \ + (M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, M_D1CT_OA_EMPTY) \ + || M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, M_D1CT_OA_DELETED))) { \ + i++; \ + } \ + it->index = i; \ + } \ + \ + M_INLINE void \ + M_F(name, _previous)(dict_it_t it) \ + { \ + M_ASSERT (it != NULL); \ + M_D1CT_OA_CONTRACT (it->dict); \ + /* if index was 0, the operation will overflow, and stops the loop */ \ + size_t i = it->index - 1; \ + while (i <= it->dict->mask && \ + (M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, M_D1CT_OA_EMPTY) \ + || M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, M_D1CT_OA_DELETED))) { \ + i--; \ + } \ + it->index = i; \ + } \ + \ + M_INLINE bool \ + M_F(name, _last_p)(const dict_it_t it) \ + { \ + M_ASSERT (it != NULL); \ + dict_it_t it2; \ + M_F(name,_it_set)(it2, it); \ + M_F(name, _next)(it2); \ + return M_F(name, _end_p)(it2); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const dict_it_t it1,const dict_it_t it2) \ + { \ + M_ASSERT (it1 != NULL && it2 != NULL); \ + M_D1CT_OA_CONTRACT (it1->dict); \ + M_D1CT_OA_CONTRACT (it2->dict); \ + return it1->dict == it2->dict && it1->index == it2->index; \ + } \ + \ + M_INLINE it_deref_t * \ + M_F(name, _ref)(const dict_it_t it) \ + { \ + M_ASSERT (it != NULL); \ + M_D1CT_OA_CONTRACT (it -> dict); \ + const size_t i = it->index; \ + M_ASSERT (i <= it->dict->mask); \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, M_D1CT_OA_EMPTY)); \ + M_ASSERT (!M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, M_D1CT_OA_DELETED)); \ + return &it->dict->data[i] M_IF(isSet)(.key, ); \ + } \ + \ + M_INLINE const it_deref_t * \ + M_F(name, _cref)(const dict_it_t it) \ + { \ + return M_CONST_CAST(it_deref_t, M_F(name, _ref)(it)); \ + } \ + \ + M_INLINE void \ + M_F(name,_reserve)(dict_t dict, size_t capacity) \ + { \ + M_D1CT_OA_CONTRACT(dict); \ + size_t size; \ + /* Get the size which will allow to fit this capacity \ + NOTE: Stricly speaking we need to perform a round up to ensure \ + that no reallocation of the hash map occurs up to capacity */ \ + size = (size_t) m_core_roundpow2 ((uint64_t) ((double) capacity * (1.0 / coeff_up))); \ + /* Test for overflow of the computation */ \ + if (M_UNLIKELY_NOMEM (size < capacity)) { \ + M_MEMORY_FULL((size_t)-1); \ + } \ + M_ASSERT (M_POWEROF2_P(size)); \ + if (size > dict->mask+1) { \ + dict->upper_limit = (size_t) ((double) size * coeff_up) - 1; \ + M_C3(m_d1ct_,name,_resize_up)(dict, size, false); \ + } \ + M_D1CT_OA_CONTRACT(dict); \ + } \ + \ + M_D1CT_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t, it_deref_t) + + +/******************************** INTERNAL ***********************************/ + +#if M_USE_SMALL_NAME +#define DICT_DEF2 M_DICT_DEF2 +#define DICT_DEF2_AS M_DICT_DEF2_AS +#define DICT_STOREHASH_DEF2 M_DICT_STOREHASH_DEF2 +#define DICT_STOREHASH_DEF2_AS M_DICT_STOREHASH_DEF2_AS +#define DICT_OA_DEF2 M_DICT_OA_DEF2 +#define DICT_OA_DEF2_AS M_DICT_OA_DEF2_AS +#define DICT_SET_DEF M_DICT_SET_DEF +#define DICT_SET_DEF_AS M_DICT_SET_DEF_AS +#define DICT_OASET_DEF M_DICT_OASET_DEF +#define DICT_OASET_DEF_AS M_DICT_OASET_DEF_AS +#define DICT_OPLIST M_DICT_OPLIST +#define DICT_SET_OPLIST M_DICT_SET_OPLIST +#endif + +#endif diff --git a/components/mlib/m-funcobj.h b/components/mlib/m-funcobj.h new file mode 100644 index 00000000..7b5401e8 --- /dev/null +++ b/components/mlib/m-funcobj.h @@ -0,0 +1,415 @@ +/* + * M*LIB - Function Object module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_FUNCOBJ_H +#define MSTARLIB_FUNCOBJ_H + +#include "m-core.h" + +/* Define a function object interface of name 'name' + * with a function like retcode, type of param1, type of param 2, ... + * USAGE: + * FUNC_OBJ_ITF_DEF(name, retcode type, type of param1, type of param 2, ...) + */ +#define M_FUNC_OBJ_ITF_DEF(name, ...) \ + M_FUNC_OBJ_ITF_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define a function object interface of name 'name' + * as the given name name_t + * USAGE: + * FUNC_OBJ_ITF_DEF_AS(name, name_t, retcode type, type of param1, type of param 2, ...) + */ +#define M_FUNC_OBJ_ITF_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_IF_NARGS_EQ1(__VA_ARGS__)(M_FUNC0BJ_ITF_NO_PARAM_DEF, M_FUNC0BJ_ITF_PARAM_DEF)(name, name_t, __VA_ARGS__) \ + M_END_PROTECTED_CODE + + +/* Define a function object instance of name 'name' based on the interface 'base_name' + * The function is defined using: + * - the prototype of the inherited interface + * - the parameters of the function are named as per the list param_list + * - the core of the function given in 'callback_core' + * - optionals member attributes of the function object can be defined after the core + * (just like for tuple & variant: (name, type [, oplist]) + * + * In the core of the function, parameters are accessible just like a normal function. + * A special variable named 'self' that refers to the function object itself + * can be used to access member attributes using the syntax self->param1, ... + * + * There shall be **exactly** the same number of parameters in 'param_list' than + * the number of parameters of the interface 'base_name' + * + * USAGE/EXAMPLE: + * FUNC_OBJ_INS_DEF(name, base_name, (param1, ...), { return param1 * self->member1 }, (member1, int), ...) + */ +#define M_FUNC_OBJ_INS_DEF(name, base_name, param_list, ...) \ + M_FUNC_OBJ_INS_DEF_AS(name, M_F(name,_t), base_name, param_list, __VA_ARGS__) + + +/* Define a function object instance of name 'name' based on the interface 'base_name' + * as the given name name_t. + * See FUNC_OBJ_INS_DEF for additional details. + * + * USAGE/EXAMPLE: + * FUNC_OBJ_INS_DEF_AS(name, name_t, base_name, (param1, ...), { return param1 * self->member1 }, (member1, int), ...) + */ +#define M_FUNC_OBJ_INS_DEF_AS(name, name_t, base_name, param_list, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_IF_NARGS_EQ1(__VA_ARGS__)(M_FUNC0BJ_INS_NO_ATTR_DEF, M_FUNC0BJ_INS_ATTR_DEF)(name, name_t, base_name, param_list, __VA_ARGS__) \ + M_END_PROTECTED_CODE + + +/* OPLIST of the instanced function object + * USAGE: + * FUNC_OBJ_INS_OPLIST(name, oplist of the attr1, ...) + */ +#define M_FUNC_OBJ_INS_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__)(M_FUNC0BJ_INS_NO_ATTR_OPLIST, M_FUNC0BJ_INS_ATTR_OPLIST_P1)( __VA_ARGS__) + + + +/*****************************************************************************/ +/******************************** INTERNAL ***********************************/ +/*****************************************************************************/ + +/* To be used by M_IF_FUNCOBJ macro defined in m-core. + NOTE: It is reversed (0 instead of 1) so that it can be used in M_IF reliabely. +*/ +#define M_FUNC0BJ_IS_NOT_DEFINED 0 + +/* Design Constraints: + * callback SHALL be the first member of the structures in all the definitions. + * + * Structure definitions are specialized in function of the presence or not + * of parameters and/or attributes + * FIXME: How to factorize reasonnably well between the definitions? + */ + +/* Specialization of the OPLIST in function if there is at least one member or not */ +#define M_FUNC0BJ_INS_NO_ATTR_OPLIST(name) ( \ + NAME(name), \ + TYPE(M_F(name, _ct)), \ + CLEAR(M_F(name, _clear)), \ + INIT(M_F(name,_init)) \ + ) + +/* Validate the oplist before going further */ +#define M_FUNC0BJ_INS_ATTR_OPLIST_P1(name, ...) \ + M_IF(M_REDUCE(M_OPLIST_P, M_AND, __VA_ARGS__))(M_FUNC0BJ_INS_ATTR_OPLIST_P3, M_FUNC0BJ_INS_ATTR_OPLIST_FAILURE)(name, __VA_ARGS__) + +/* Prepare a clean compilation failure */ +#define M_FUNC0BJ_INS_ATTR_OPLIST_FAILURE(name, ...) \ + ((M_LIB_ERROR(ONE_ARGUMENT_OF_FUNC_OBJ_INS_OPLIST_IS_NOT_AN_OPLIST, name, __VA_ARGS__))) + +/* Define the oplist of the instance */ +#define M_FUNC0BJ_INS_ATTR_OPLIST_P3(name, ...) ( \ + NAME(name), \ + TYPE(M_F(name, _ct)), \ + INIT_WITH(M_F(name, _init_with)), \ + CLEAR(M_F(name, _clear)), \ + M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_F(name,_init)),), \ + PROPERTIES(( LET_AS_INIT_WITH(1) )) \ + ) + + +/******************************** INTERNAL ***********************************/ + +/* Specialization of the definition a function object interface of name 'name' + * with a function like "retcode (void)" that doesn't have any input parameters. + * Define the following types to be used by instance: + * - M_F(name, _retcode_ct): internal type of the return code + * - M_F(name, _callback_ct): internal type of the callback. + * - M_F(name, _ct): synonym of main type used by oplist. + */ +#define M_FUNC0BJ_ITF_NO_PARAM_DEF(name, interface_t, retcode) \ + \ + /* Forward declaration */ \ + struct M_F(name, _s); \ + \ + /* Internal type for instance */ \ + typedef retcode M_F(name, _retcode_ct); \ + /* No parameters to the callback */ \ + typedef retcode(*M_F(name, _callback_ct))(struct M_F(name, _s) *); \ + \ + typedef struct M_F(name, _s) { \ + M_F(name, _callback_ct) callback; \ + } *interface_t; \ + \ + /* Internal type for oplist & instance */ \ + typedef interface_t M_F(name, _ct); \ + \ + M_INLINE retcode \ + M_F(name, _call)(interface_t funcobj) \ + { \ + M_IF(M_KEYWORD_P(void, retcode)) ( /* nothing */,return) \ + funcobj->callback(funcobj); \ + } + + +/* Specialization of the definition a function object interface of name 'name' + * with a function like retcode, type of param1, type of param 2, ... + * with mandatory input parameters. + * Define the following types to be used by instance: + * - M_F(name, _retcode_ct): internal type of the return code + * - M_F(name, _callback_ct): internal type of the callback. + * - M_C4(name, _param_, num, _ct) for each parameter defined + * - M_F(name, _ct): synonym of main type used by oplist. + */ +#define M_FUNC0BJ_ITF_PARAM_DEF(name, interface_t, retcode, ...) \ + \ + /* Forward declaration */ \ + struct M_F(name, _s); \ + \ + /* Internal types for instance */ \ + typedef retcode M_F(name, _retcode_ct); \ + /* Define types for all parameters */ \ + M_MAP3(M_FUNC0BJ_BASE_TYPE, name, __VA_ARGS__) \ + /* Define callback type with all parameters */ \ + typedef retcode(*M_F(name, _callback_ct))(struct M_F(name, _s) *, __VA_ARGS__); \ + \ + typedef struct M_F(name, _s) { \ + M_F(name, _callback_ct) callback; \ + } *interface_t; \ + \ + /* Internal type for oplist & instance */ \ + typedef interface_t M_F(name, _ct); \ + \ + M_INLINE retcode \ + M_F(name, _call)(interface_t funcobj \ + M_MAP3(M_FUNC0BJ_BASE_ARGLIST, name, __VA_ARGS__) ) \ + { \ + /* If the retcode is 'void', don't return the value of the callback */ \ + M_IF(M_KEYWORD_P(void, retcode)) ( /* nothing */,return) \ + funcobj->callback(funcobj M_MAP3(M_FUNC0BJ_BASE_ARGCALL, name, __VA_ARGS__) ); \ + } + + +/******************************** INTERNAL ***********************************/ + +/* Specialization of the definition a function object instance of name 'name' + * with no member attribute. + */ +#define M_FUNC0BJ_INS_NO_ATTR_DEF(name, instance_t, base_name, param_list, callback_core) \ + typedef struct M_F(name, _s) { \ + M_C(base_name, _callback_ct) callback; \ + } instance_t[1]; \ + \ + /* Internal type for oplist */ \ + typedef instance_t M_F(name, _ct); \ + \ + M_INLINE M_C(base_name, _retcode_ct) \ + M_F(name, _callback)(M_C(base_name, _ct) _self \ + M_IF_EMPTY(M_OPFLAT param_list)( \ + /* No param */, \ + M_MAP3(M_FUNC0BJ_INS_ARGLIST, base_name, M_OPFLAT param_list) \ + ) \ + ) \ + { \ + struct M_F(name, _s) *self = (struct M_F(name, _s) *)_self; \ + (void) self; /* maybe unused */ \ + callback_core; \ + } \ + \ + M_INLINE void \ + M_F(name, _init_with)(instance_t obj) \ + { \ + obj->callback = M_F(name, _callback); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(instance_t obj) \ + { \ + (void) obj; /* nothing to do */ \ + } \ + \ + M_INLINE struct M_C(base_name, _s) * \ + M_F(name, _as_interface)(instance_t obj) \ + { \ + return (struct M_C(base_name, _s) *) obj; \ + } \ + \ + M_INLINE void \ + M_F(name, _init)(instance_t obj) \ + { \ + obj->callback = M_F(name, _callback); \ + } \ + + +/* Specialization of the definition a function object instance of name 'name' + * with mandatory member attribute. + * First inject oplist in member attributes. + */ +#define M_FUNC0BJ_INS_ATTR_DEF(name, instance_t, base_name, param_list, callback_core, ...) \ + M_FUNC0BJ_INS_ATTR_DEF_P2(name, instance_t, base_name, param_list, callback_core, M_FUNC0BJ_INJECT_GLOBAL(__VA_ARGS__) ) + +/* Inject the oplist within the list of arguments */ +#define M_FUNC0BJ_INJECT_GLOBAL(...) \ + M_MAP_C(M_FUNC0BJ_INJECT_OPLIST_A, __VA_ARGS__) + +/* Transform (x, type) into (x, type, oplist) if there is global registered oplist + or (x, type, M_BASIC_OPLIST) if there is no global one, + or keep (x, type, oplist) if oplist was already present */ +#define M_FUNC0BJ_INJECT_OPLIST_A( duo_or_trio ) \ + M_FUNC0BJ_INJECT_OPLIST_B duo_or_trio +#define M_FUNC0BJ_INJECT_OPLIST_B( f, ... ) \ + M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) ) + +// Test if all third argument of all arguments is an oplist +#define M_FUNC0BJ_IF_ALL_OPLIST(...) \ + M_IF(M_REDUCE(M_FUNC0BJ_IS_OPLIST_P, M_AND, __VA_ARGS__)) +// Test if the third argument is an oplist. a is a trio (name, type, oplist) +#define M_FUNC0BJ_IS_OPLIST_P(a) \ + M_OPLIST_P(M_RET_ARG3 a) + +/* Validate the oplist before going further */ +#define M_FUNC0BJ_INS_ATTR_DEF_P2(name, instance_t, base_name, param_list, callback_core, ...) \ + M_FUNC0BJ_IF_ALL_OPLIST(__VA_ARGS__)(M_FUNC0BJ_INS_ATTR_DEF_P3, M_FUNC0BJ_INS_ATTR_DEF_FAILURE)(name, instance_t, base_name, param_list, callback_core, __VA_ARGS__) + +/* Stop processing with a compilation failure */ +#define M_FUNC0BJ_INS_ATTR_DEF_FAILURE(name, instance_t, base_name, param_list, callback_core, ...) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(FUNC_OBJ_INS_DEF): at least one of the given argument is not a valid oplist: " #__VA_ARGS__) + +/* Expand the Function Object with members */ +#define M_FUNC0BJ_INS_ATTR_DEF_P3(name, instance_t, base_name, param_list, callback_core, ...) \ + typedef struct M_F(name, _s) { \ + /* Callback is the mandatory first argument */ \ + M_C(base_name, _callback_ct) callback; \ + /* All the member attribute of the Function Object */ \ + M_MAP(M_FUNC0BJ_INS_ATTR_STRUCT, __VA_ARGS__) \ + } instance_t[1]; \ + \ + /* Internal type for oplist */ \ + typedef instance_t M_F(name, _ct); \ + \ + M_FUNC0BJ_CONTROL_ALL_OPLIST(name, __VA_ARGS__) \ + \ + M_INLINE M_C(base_name, _retcode_ct) \ + M_F(name, _callback)(M_C(base_name, _ct) _self \ + M_IF_EMPTY(M_OPFLAT param_list)( \ + /* No param */, \ + M_MAP3(M_FUNC0BJ_INS_ARGLIST, base_name, M_OPFLAT param_list) \ + ) \ + ) \ + { \ + /* Let's go through an uintptr_t to avoid [broken] aliasing detection by compiler */ \ + uintptr_t __self = (uintptr_t) _self; \ + struct M_F(name, _s) *self = (struct M_F(name, _s) *)(void*)__self; \ + (void) self; /* maybe unused */ \ + callback_core; \ + } \ + \ + M_INLINE void \ + M_F(name, _init_with)(instance_t obj M_MAP(M_FUNC0BJ_INS_ATTR_LIST, __VA_ARGS__)) \ + { \ + obj->callback = M_F(name, _callback); \ + M_MAP(M_FUNC0BJ_INS_ATTR_INIT_SET, __VA_ARGS__); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(instance_t obj) \ + { \ + M_MAP(M_FUNC0BJ_INS_ATTR_CLEAR, __VA_ARGS__); \ + } \ + \ + M_INLINE struct M_C(base_name, _s) * \ + M_F(name, _as_interface)(instance_t obj) \ + { \ + return (struct M_C(base_name, _s) *) obj; \ + } \ + \ + M_IF(M_FUNC0BJ_TEST_METHOD_P(INIT, __VA_ARGS)) \ + ( \ + M_INLINE void \ + M_F(name, _init)(instance_t obj) \ + { \ + obj->callback = M_F(name, _callback); \ + M_MAP(M_FUNC0BJ_INS_ATTR_INIT, __VA_ARGS__); \ + } \ + , /* END OF INIT METHOD */ ) \ + + + +/* Define a numbered type of a parameter of the callback*/ +#define M_FUNC0BJ_BASE_TYPE(name, num, type) \ + typedef type M_C4(name, _param_, num, _ct); + +/* Define a list of the type of arguments for a function definition */ +#define M_FUNC0BJ_BASE_ARGLIST(name, num, type) \ + M_DEFERRED_COMMA type M_C(param_, num) + +/* Define a list of arguments for a function call */ +#define M_FUNC0BJ_BASE_ARGCALL(name, num, type) \ + M_DEFERRED_COMMA M_C(param_, num) + + +/* Helper macros */ +/* arg = (name, type [, oplist]) */ +#define M_FUNC0BJ_INS_ATTR_STRUCT(arg) \ + M_RET_ARG2 arg M_RET_ARG1 arg; + +#define M_FUNC0BJ_INS_ATTR_LIST(arg) \ + M_DEFERRED_COMMA M_RET_ARG2 arg const M_RET_ARG1 arg + +#define M_FUNC0BJ_INS_ATTR_INIT(arg) \ + M_CALL_INIT(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg); + +#define M_FUNC0BJ_INS_ATTR_INIT_SET(arg) \ + M_CALL_INIT_SET(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg, M_RET_ARG1 arg); + +#define M_FUNC0BJ_INS_ATTR_CLEAR(arg) \ + M_CALL_CLEAR(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg); + +/* Define the list of arguments of the instance of the callback */ +#define M_FUNC0BJ_INS_ARGLIST(name, num, param) \ + M_DEFERRED_COMMA M_C4(name, _param_, num, _ct) param + + +/* Macros for testing for a method presence in all the attributes */ +#define M_FUNC0BJ_TEST_METHOD2_P(method, op) \ + M_TEST_METHOD_P(method, op) +#define M_FUNC0BJ_TEST_METHOD1_P(method, arg) \ + M_APPLY(M_FUNC0BJ_TEST_METHOD2_P, method, M_RET_ARG3 arg) +#define M_FUNC0BJ_TEST_METHOD_P(method, ...) \ + M_IF(M_REDUCE2(M_FUNC0BJ_TEST_METHOD1_P, M_AND, method, __VA_ARGS__)) + +/* Macro for checking compatible type and oplist for all the attributes */ +#define M_FUNC0BJ_CONTROL_ALL_OPLIST(name, ...) \ + M_MAP2(M_FUNC0BJ_CONTROL_OPLIST, name, __VA_ARGS__) +#define M_FUNC0BJ_CONTROL_OPLIST(name, a) \ + M_CHECK_COMPATIBLE_OPLIST(name, M_RET_ARG1 a, M_RET_ARG2 a, M_RET_ARG3 a) + + +/******************************** INTERNAL ***********************************/ + +#if M_USE_SMALL_NAME +#define FUNC_OBJ_ITF_DEF M_FUNC_OBJ_ITF_DEF +#define FUNC_OBJ_ITF_DEF_AS M_FUNC_OBJ_ITF_DEF_AS +#define FUNC_OBJ_INS_DEF M_FUNC_OBJ_INS_DEF +#define FUNC_OBJ_INS_DEF_AS M_FUNC_OBJ_INS_DEF_AS +#define FUNC_OBJ_INS_OPLIST M_FUNC_OBJ_INS_OPLIST +#endif + +#endif diff --git a/components/mlib/m-genint.h b/components/mlib/m-genint.h new file mode 100644 index 00000000..b688f241 --- /dev/null +++ b/components/mlib/m-genint.h @@ -0,0 +1,247 @@ +/* + * M*LIB - Integer Generator (GENINT) module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_GENINT_H +#define MSTARLIB_GENINT_H + +#include "m-core.h" +#include "m-atomic.h" + +M_BEGIN_PROTECTED_CODE + +/* GENINT is an internal container providing unique integers. + It has the following properties: + - it stores integer from [0..N) (N is fixed). + - an integer can have only one occurrence in the container. + - you can atomically push in / pop out integer from this container + provided that it is not already in the container. + - there are no order (like FIFO or stack) + + This can be used to map integers to index of resources in a table. + At most we can support N = 32*64 = 2048 with the master limb usage. + For the typical usage of this container + (mapping hardware or software limited resources), this should be + enough. +*/ + +// Define the limb size used by genint +typedef unsigned long long m_genint_limb_ct; + +/* Define a generator of unique integer (Lock Free) */ +typedef struct m_genint_s { + unsigned int n; // size of the container + unsigned int max; // number of allocated limb - 1 + m_genint_limb_ct mask0; // mask of the last limb (constant) + m_genint_limb_ct mask_master; // mask of the master limb that controls others (constant) + atomic_ullong master; // master bitfield (which informs if a limb is full or not) + atomic_ullong *data; // the bitfield which informs if an integer is used or not +} m_genint_t[1]; + +// Define the max absolute supported value. It should be 2048 on most implementations. +#define M_GENINT_MAX_ALLOC (M_GEN1NT_LIMBSIZE * (M_GEN1NT_LIMBSIZE - M_GEN1NT_ABA_CPT)) + +// Define the size of a limb in bits. +#define M_GEN1NT_LIMBSIZE ((unsigned)(sizeof(m_genint_limb_ct) * CHAR_BIT)) + +// Define the contract of a genint +#define M_GEN1NT_CONTRACT(s) do { \ + M_ASSERT (s != NULL); \ + M_ASSERT (s->n > 0 && s->n <= M_GENINT_MAX_ALLOC); \ + M_ASSERT ((s->max+1) * M_GEN1NT_LIMBSIZE >= s->n); \ + M_ASSERT (s->data != NULL); \ + } while (0) + +// Define the limb one +#define M_GEN1NT_ONE ((m_genint_limb_ct)1) + +#define M_GEN1NT_FULL_MASK ULLONG_MAX + +// Value returned in case of error (not integer available). +#define M_GENINT_ERROR (UINT_MAX) + +/* 32 bits of the master mask are kept for handling the ABA problem. + * NOTE: May be too much. 16 bits should be more than enough. TBC + */ +#define M_GEN1NT_ABA_CPT 32 +#define M_GEN1NT_ABA_CPT_T uint32_t + +// Set the bit 'i' of the master limb, and increase ABA counter. +#define M_GEN1NT_MASTER_SET(master, i) \ + ((((master)& (~((M_GEN1NT_ONE<< M_GEN1NT_ABA_CPT)-1))) | (M_GEN1NT_ONE << (M_GEN1NT_LIMBSIZE - 1 - i))) \ + |((M_GEN1NT_ABA_CPT_T)((master) + 1))) + +// Reset the bit i of the master limb, and increase ABA counter. +#define M_GEN1NT_MASTER_RESET(master, i) \ + (((master) & (~((M_GEN1NT_ONE<< M_GEN1NT_ABA_CPT)-1)) & ~(M_GEN1NT_ONE << (M_GEN1NT_LIMBSIZE - 1 - i))) \ + |((M_GEN1NT_ABA_CPT_T)((master) + 1))) + +/* Initialize an integer generator (CONSTRUCTOR). + * Initialy, the container is full of all the integers up to 'n-1' + * The typical sequence is to initialize the container, and pop + * the integer from it. Each pop integer is **unique** for all threads, + * meaning it can be used to index global unique resources shared + * for all threads. + */ +M_INLINE void +m_genint_init(m_genint_t s, unsigned int n) +{ + M_ASSERT (s != NULL && n > 0 && n <= M_GENINT_MAX_ALLOC); + const size_t alloc = (n + M_GEN1NT_LIMBSIZE - 1) / M_GEN1NT_LIMBSIZE; + const unsigned int index = n % M_GEN1NT_LIMBSIZE; + atomic_ullong *ptr = M_MEMORY_REALLOC (atomic_ullong, NULL, alloc); + if (M_UNLIKELY_NOMEM (ptr == NULL)) { + M_MEMORY_FULL(alloc); + return; + } + s->n = n; + s->data = ptr; + s->max = (unsigned int) (alloc-1); + s->mask0 = (index == 0) ? M_GEN1NT_FULL_MASK : ~((M_GEN1NT_ONE<<(M_GEN1NT_LIMBSIZE-index))-1); + s->mask_master = (((M_GEN1NT_ONE << alloc) - 1) << (M_GEN1NT_LIMBSIZE-alloc)) >> M_GEN1NT_ABA_CPT; + atomic_init (&s->master, (m_genint_limb_ct)0); + for(unsigned int i = 0; i < alloc; i++) + atomic_init(&s->data[i], (m_genint_limb_ct)0); + M_GEN1NT_CONTRACT(s); +} + +/* Clear an integer generator (Destructor) */ +M_INLINE void +m_genint_clear(m_genint_t s) +{ + M_GEN1NT_CONTRACT(s); + M_MEMORY_FREE(s->data); + s->data = NULL; +} + +/* Return the maximum integer that the generator will provide */ +M_INLINE size_t +m_genint_size(m_genint_t s) +{ + M_GEN1NT_CONTRACT(s); + return s->n; +} + +/* Get an unique integer from the integer generator. + * NOTE: For a typical case, the amortized cost is one CAS per pop. */ +M_INLINE unsigned int +m_genint_pop(m_genint_t s) +{ + M_GEN1NT_CONTRACT(s); + // First read master to see which limb is not full. + m_genint_limb_ct master = atomic_load(&s->master); + // While master is not full + while ((master >> M_GEN1NT_ABA_CPT) != s->mask_master) { + // Let's get the index i of the first not full limb according to master. + unsigned int i = m_core_clz64(~master); + M_ASSERT (i < M_GEN1NT_LIMBSIZE); + // Let's compute the mask of this limb representing the limb as being full + m_genint_limb_ct mask = s->mask0; + mask = (i == s->max) ? mask : M_GEN1NT_FULL_MASK; + unsigned int bit; + // Let's load this limb, + m_genint_limb_ct next, org = atomic_load(&s->data[i]); + do { + // If it is now full, we have been preempted by another. + if (M_UNLIKELY (org == mask)) + goto next_element; + M_ASSERT (org != M_GEN1NT_FULL_MASK); + // At least one bit is free in the limb. Find one. + bit = M_GEN1NT_LIMBSIZE - 1 - m_core_clz64(~org); + M_ASSERT (bit < M_GEN1NT_LIMBSIZE); + M_ASSERT ((org & (M_GEN1NT_ONE<n); + // Set the integer as being used. + next = org | (M_GEN1NT_ONE << bit); + // Try to reserve the integer + } while (!atomic_compare_exchange_weak (&s->data[i], &org, next)); + // We have reserved the integer. + // If the limb is now full, try to update master + if (M_UNLIKELY(next == mask)) { + while (true) { + m_genint_limb_ct newMaster; + if (next == mask) { + newMaster = M_GEN1NT_MASTER_SET(master, i); + } else { + newMaster = M_GEN1NT_MASTER_RESET(master, i); + } + if (atomic_compare_exchange_weak (&s->master, &master, newMaster)) + break; + // Fail to update. Reload limb to check if it is still full. + next = atomic_load(&s->data[i]); + } + } + // Return the new number + M_GEN1NT_CONTRACT(s); + return i * M_GEN1NT_LIMBSIZE + M_GEN1NT_LIMBSIZE - 1 - bit; + next_element: + // Reload master + master = atomic_load(&s->master); + } + M_GEN1NT_CONTRACT(s); + return M_GENINT_ERROR; // No more resource available +} + +/* Restore a used integer in the integer generator. + * NOTE: For a typical case, the amortized cost is one CAS per pop */ +M_INLINE void +m_genint_push(m_genint_t s, unsigned int n) +{ + M_GEN1NT_CONTRACT(s); + M_ASSERT (n < s->n); + const unsigned int i = n / M_GEN1NT_LIMBSIZE; + const unsigned int bit = M_GEN1NT_LIMBSIZE - 1 - (n % M_GEN1NT_LIMBSIZE); + m_genint_limb_ct master = atomic_load(&s->master); + // Load the limb + m_genint_limb_ct next, org = atomic_load(&s->data[i]); + do { + M_ASSERT ((org & (M_GEN1NT_ONE << bit)) != 0); + // Reset it + next = org & (~(M_GEN1NT_ONE << bit)); + // Try to unreserve it. + } while (!atomic_compare_exchange_weak (&s->data[i], &org, next)); + // if the limb was marked as full by master + m_genint_limb_ct mask = s->mask0; + mask = (i == s->max) ? mask : M_GEN1NT_FULL_MASK; + if (M_UNLIKELY (next != mask)) { + // Let's compute the mask of this limb representing the limb as being full + // Let's try to update master to say that this limb is not full + while (true) { + m_genint_limb_ct newMaster; + if (next == mask) { + newMaster = M_GEN1NT_MASTER_SET(master, i); + } else { + newMaster = M_GEN1NT_MASTER_RESET(master, i); + } + if (atomic_compare_exchange_weak (&s->master, &master, newMaster)) + break; + // Fail to update. Reload limb to check if it is still full. + next = atomic_load(&s->data[i]); + } + } + M_GEN1NT_CONTRACT(s); +} + +M_END_PROTECTED_CODE + +#endif diff --git a/components/mlib/m-i-list.h b/components/mlib/m-i-list.h new file mode 100644 index 00000000..9ce955d6 --- /dev/null +++ b/components/mlib/m-i-list.h @@ -0,0 +1,679 @@ +/* + * M*LIB - Intrusive List module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_I_LIST_H +#define MSTARLIB_I_LIST_H + +#include "m-core.h" +#include "m-list.h" // For M_L1ST_ITBASE_DEF + +/* Interface to add to a structure to enable intrusive doubly-linked support. + name: name of the intrusive list. + type: name of the type of the structure (aka. struct test_s) - not used currently + USAGE: + typedef struct tmp_str_s { + ... + ILIST_INTERFACE(tmpstr, struct tmp_str_s); + ... + } tmp_str_t; +*/ +#define M_ILIST_INTERFACE(name, type) \ + struct m_il1st_head_s name + + +/* Define a doubly-linked intrusive list of a given type. + The type needs to have ILIST_INTERFACE(). + USAGE: + ILIST_DEF(name, type [, oplist_of_the_type]) */ +#define M_ILIST_DEF(name, ...) \ + M_ILIST_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) + + +/* Define a doubly-linked intrusive list of a given type + as the provided type name_t with the iterator named it_t. + The type needs to have ILIST_INTERFACE(). + USAGE: + ILIST_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */ +#define M_ILIST_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_IL1ST_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \ + (name, __VA_ARGS__, name_t, it_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a doubly-linked instrusive list of type. + USAGE: + ILIST_OPLIST(name [, oplist_of_the_type]) */ +#define M_ILIST_OPLIST(...) \ + M_IL1ST_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST), \ + (__VA_ARGS__ ))) + + +/*****************************************************************************/ +/******************************** INTERNAL ***********************************/ +/*****************************************************************************/ + +/* Define the basic structure to be added in all objects. */ +typedef struct m_il1st_head_s { + struct m_il1st_head_s *next; + struct m_il1st_head_s *prev; +} m_il1st_head_ct; + +/* Indirection call to allow expanding all arguments */ +#define M_IL1ST_OPLIST_P1(arg) M_IL1ST_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_IL1ST_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_IL1ST_OPLIST_P3, M_IL1ST_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_IL1ST_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_ILIST_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* Define the oplist of an ilist of type */ +#define M_IL1ST_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)), \ + CLEAR(M_F(name, _clear)), \ + INIT_MOVE(M_F(name, _init_move)), \ + MOVE(M_F(name, _move)), \ + NAME(name), \ + TYPE(M_F(name,_ct)), \ + RESET(M_F(name,_reset)), \ + SUBTYPE(M_F(name,_subtype_ct)), \ + EMPTY_P(M_F(name,_empty_p)), \ + IT_TYPE(M_F(name,_it_ct)), \ + IT_FIRST(M_F(name,_it)), \ + IT_SET(M_F(name,_it_set)), \ + IT_LAST(M_F(name,_it_last)), \ + IT_END(M_F(name,_it_end)), \ + IT_END_P(M_F(name,_end_p)), \ + IT_LAST_P(M_F(name,_last_p)), \ + IT_EQUAL_P(M_F(name,_it_equal_p)), \ + IT_NEXT(M_F(name,_next)), \ + IT_PREVIOUS(M_F(name,_previous)), \ + IT_REF(M_F(name,_ref)), \ + IT_CREF(M_F(name,_cref)), \ + IT_REMOVE(M_F(name,_remove)), \ + M_IF_METHOD(NEW, oplist)(IT_INSERT(M_F(name,_insert)),), \ + OPLIST(oplist), \ + SPLICE_BACK(M_F(name,_splice_back)) \ + ) + + +/******************************** INTERNAL ***********************************/ + +/* Contract respected by all intrusive lists */ +#define M_IL1ST_CONTRACT(name, list) do { \ + M_ASSERT(list != NULL); \ + M_ASSERT(list->name.prev != NULL); \ + M_ASSERT(list->name.next != NULL); \ + M_ASSERT(list->name.next->prev == &list->name); \ + M_ASSERT(list->name.prev->next == &list->name); \ + M_ASSERT(!(list->name.prev == &list->name) || list->name.prev == list->name.next); \ + } while (0) + +#define M_IL1ST_NODE_CONTRACT(node) do { \ + M_ASSERT((node) != NULL); \ + M_ASSERT((node)->prev != NULL); \ + M_ASSERT((node)->next != NULL); \ + M_ASSERT((node)->next->prev == node); \ + M_ASSERT((node)->prev->next == node); \ + } while (0) + +/* Indirection call to allow expanding all arguments */ +#define M_IL1ST_DEF_P1(arg) M_ID( M_IL1ST_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_IL1ST_DEF_P2(name, type, oplist, list_t, it_t) \ + M_IF_OPLIST(oplist)(M_IL1ST_DEF_P3, M_IL1ST_DEF_FAILURE)(name, type, oplist, list_t, it_t) + +/* Stop processing with a compilation failure */ +#define M_IL1ST_DEF_FAILURE(name, type, oplist, list_t, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ILIST_DEF): the given argument is not a valid oplist: " #oplist) + +/* Definition of the type and function for an intrusive doubly-linked list. + USAGE: + name: name of the intrusive list + type: type of the object + oplist: oplist of the type + list_t: type of the intrusive list (name##_t) + it_t: iterator of the intrusive list (name##_it_t) +*/ +#define M_IL1ST_DEF_P3(name, type, oplist, list_t, it_t) \ + M_IL1ST_DEF_TYPE(name, type, oplist, list_t, it_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_IL1ST_DEF_CORE(name, type, oplist, list_t, it_t) \ + /* Used of internal macro from m-list */ \ + M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t) + +/* Define the type of an intrusive list */ +#define M_IL1ST_DEF_TYPE(name, type, oplist, list_t, it_t) \ + \ + /* Define the list as a structure containing pointers \ + * to the front & back nodes */ \ + typedef struct M_F(name, _s) { \ + struct m_il1st_head_s name; \ + } list_t[1]; \ + \ + /* Define internal types pointers to such a list */ \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* Define iterator of such a list */ \ + typedef struct M_F(name, _it_s) { \ + struct m_il1st_head_s *head; \ + struct m_il1st_head_s *previous; \ + struct m_il1st_head_s *current; \ + struct m_il1st_head_s *next; \ + } it_t[1]; \ + \ + /* Define types used by oplist */ \ + typedef type M_F(name, _subtype_ct); \ + typedef list_t M_F(name, _ct); \ + typedef it_t M_F(name, _it_ct); \ + +/* Define core functions for intrusive lists */ +#define M_IL1ST_DEF_CORE(name, type, oplist, list_t, it_t) \ + \ + M_INLINE void \ + M_F(name, _init)(list_t list) \ + { \ + M_ASSERT (list != NULL); \ + list->name.next = &list->name; \ + list->name.prev = &list->name; \ + M_IL1ST_CONTRACT(name, list); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + for(struct m_il1st_head_s *it = list->name.next, *next ; \ + it != &list->name; it = next) { \ + /* Cannot check node contract as previous node may be deleted */ \ + type *obj = M_TYPE_FROM_FIELD(type, it, \ + struct m_il1st_head_s, name); \ + /* Read next now before the object is destroyed */ \ + next = it->next; \ + M_ASSERT (next != NULL); \ + M_CALL_CLEAR(oplist, *obj); \ + /* Delete also the object if a DELETE operand is registered */ \ + M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \ + } \ + /* Nothing remains in the list anymore */ \ + list->name.next = &list->name; \ + list->name.prev = &list->name; \ + M_IL1ST_CONTRACT(name, list); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(list_t list) \ + { \ + /* Nothing to do more than clean the list itself */ \ + M_F(name, _reset)(list); \ + /* For safety purpose (create invalid represenation of object) */ \ + list->name.next = NULL; \ + list->name.prev = NULL; \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(const list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + return list->name.next == &list->name; \ + } \ + \ + \ + M_INLINE void \ + M_F(name, _init_move)(list_t list, list_t ref) \ + { \ + M_IL1ST_CONTRACT(name, ref); \ + M_ASSERT (list != ref); \ + M_F(name,_init)(list); \ + if (!M_F(name,_empty_p)(ref)) { \ + list->name.next = ref->name.next; \ + list->name.prev = ref->name.prev; \ + list->name.next->prev = &list->name; \ + list->name.prev->next = &list->name; \ + } \ + ref->name.next = NULL; \ + ref->name.prev = NULL; \ + M_IL1ST_CONTRACT(name, list); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(list_t list, list_t ref) \ + { \ + M_F(name, _clear)(list); \ + M_F(name, _init_move)(list, ref); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(const list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + size_t s = 0; \ + /* Scan the full list to count the number of elements */ \ + for(const struct m_il1st_head_s *it = list->name.next ; \ + it != &list->name; it = it->next) { \ + M_IL1ST_NODE_CONTRACT(it); \ + s++; \ + } \ + return s; \ + } \ + \ + M_INLINE void \ + M_F(name, _push_back)(list_t list, type *obj) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT (obj != NULL); \ + struct m_il1st_head_s *prev = list->name.prev; \ + list->name.prev = &obj->name; \ + obj->name.prev = prev; \ + obj->name.next = &list->name; \ + prev->next = &obj->name; \ + M_IL1ST_CONTRACT(name, list); \ + } \ + \ + M_INLINE void \ + M_F(name, _push_front)(list_t list, type *obj) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT (obj != NULL); \ + struct m_il1st_head_s *next = list->name.next; \ + list->name.next = &obj->name; \ + obj->name.next = next; \ + obj->name.prev = &list->name; \ + next->prev = &obj->name; \ + M_IL1ST_CONTRACT(name, list); \ + } \ + \ + M_INLINE void \ + M_F(name, _push_after)(type *obj_pos, type *obj) \ + { \ + M_ASSERT (obj_pos != NULL && obj != NULL); \ + /* We don't have the list, so we have no contract at list level */ \ + M_IL1ST_NODE_CONTRACT(&obj_pos->name); \ + struct m_il1st_head_s *next = obj_pos->name.next; \ + obj_pos->name.next = &obj->name; \ + obj->name.next = next; \ + obj->name.prev = &obj_pos->name; \ + next->prev = &obj->name; \ + } \ + \ + M_INLINE void \ + M_F(name, _init_field)(type *obj) \ + { \ + M_ASSERT (obj != NULL); \ + /* Init the fields of the node. To be used in object constructor */ \ + obj->name.next = NULL; \ + obj->name.prev = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _unlink)(type *obj) \ + { \ + M_ASSERT (obj != NULL); \ + /* We don't have the list, so we have no contract at list level */ \ + M_IL1ST_NODE_CONTRACT(&obj->name); \ + struct m_il1st_head_s *next = obj->name.next; \ + struct m_il1st_head_s *prev = obj->name.prev; \ + next->prev = prev; \ + prev->next = next; \ + /* Note: not really needed, but safer */ \ + obj->name.next = NULL; \ + obj->name.prev = NULL; \ + } \ + \ + M_INLINE type * \ + M_F(name, _back)(const list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT(!M_F(name, _empty_p)(list)); \ + return M_TYPE_FROM_FIELD(type, list->name.prev, \ + struct m_il1st_head_s, name); \ + } \ + \ + M_INLINE type * \ + M_F(name, _front)(const list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT(!M_F(name, _empty_p)(list)); \ + return M_TYPE_FROM_FIELD(type, list->name.next, \ + struct m_il1st_head_s, name); \ + } \ + \ + M_INLINE type * \ + M_F(name, _next_obj)(const list_t list, type const *obj) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT (obj != NULL); \ + M_IL1ST_NODE_CONTRACT(&obj->name); \ + return obj->name.next == &list->name ? NULL : \ + M_TYPE_FROM_FIELD(type, obj->name.next, \ + struct m_il1st_head_s, name); \ + } \ + \ + M_INLINE type * \ + M_F(name, _previous_obj)(const list_t list, type const *obj) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT (obj != NULL); \ + M_IL1ST_NODE_CONTRACT(&obj->name); \ + return obj->name.prev == &list->name ? NULL : \ + M_TYPE_FROM_FIELD(type, obj->name.prev, \ + struct m_il1st_head_s, name); \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(it_t it, const list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT (it != NULL); \ + it->head = list->name.next->prev; \ + it->current = list->name.next; \ + it->next = list->name.next->next; \ + it->previous = it->head; \ + M_IL1ST_NODE_CONTRACT(it->current); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t it, const it_t cit) \ + { \ + M_ASSERT (it != NULL && cit != NULL); \ + it->head = cit->head; \ + it->current = cit->current; \ + it->next = cit->next; \ + it->previous = cit->previous; \ + M_IL1ST_NODE_CONTRACT(it->current); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_last)(it_t it, list_t const list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT (it != NULL); \ + it->head = list->name.next->prev; \ + it->current = list->name.prev; \ + it->next = it->head; \ + it->previous = list->name.prev->prev; \ + M_IL1ST_NODE_CONTRACT(it->current); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(it_t it, list_t const list) \ + { \ + M_ASSERT (it != NULL && list != NULL); \ + it->head = list->name.next->prev; \ + it->current = it->head; \ + it->next = list->name.next; \ + it->previous = list->name.prev; \ + M_IL1ST_NODE_CONTRACT(it->current); \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(const it_t it) \ + { \ + M_ASSERT (it != NULL); \ + M_IL1ST_NODE_CONTRACT(it->current); \ + return it->current == it->head; \ + } \ + \ + M_INLINE bool \ + M_F(name, _last_p)(const it_t it) \ + { \ + M_ASSERT (it != NULL); \ + M_IL1ST_NODE_CONTRACT(it->current); \ + return it->next == it->head || it->current == it->head; \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + /* Cannot check node for it->current: it may have been deleted! */ \ + /* Note: Can't set it->previous to it->current. \ + it->current may have been unlinked from the list */ \ + it->current = it->next; \ + M_ASSERT (it->current != NULL); \ + it->next = it->current->next; \ + it->previous = it->current->prev; \ + M_ASSERT (it->next != NULL && it->previous != NULL); \ + M_IL1ST_NODE_CONTRACT(it->current); \ + } \ + \ + M_INLINE void \ + M_F(name, _previous)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + /* Cannot check node for it->current: it may have been deleted! */ \ + /* Note: Can't set it->next to it->current. \ + it->current may have been unlinked from the list */ \ + it->current = it->previous; \ + M_ASSERT (it->current != NULL); \ + it->next = it->current->next; \ + it->previous = it->current->prev; \ + M_ASSERT (it->next != NULL && it->previous != NULL); \ + M_IL1ST_NODE_CONTRACT(it->current); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const it_t it1, const it_t it2 ) \ + { \ + M_ASSERT (it1 != NULL && it2 != NULL); \ + /* No need to check for next & previous */ \ + return it1->head == it2->head && it1->current == it2->current; \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(const it_t it) \ + { \ + M_ASSERT (it != NULL && it->current != NULL); \ + M_IL1ST_NODE_CONTRACT(it->current); \ + /* check if 'it' was not deleted */ \ + M_ASSERT (it->current->next == it->next); \ + M_ASSERT (it->current->prev == it->previous); \ + M_ASSERT (!M_F(name, _end_p)(it)); \ + return M_TYPE_FROM_FIELD(type, it->current, \ + struct m_il1st_head_s, name); \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(const it_t it) \ + { \ + type *ptr = M_F(name, _ref)(it); \ + return M_CONST_CAST(type, ptr); \ + } \ + \ + M_INLINE void \ + M_F(name, _remove)(list_t list, it_t it) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_IL1ST_NODE_CONTRACT(it->current); \ + (void)list; /* list param is not used */ \ + type *obj = M_TYPE_FROM_FIELD(type, it->current, \ + struct m_il1st_head_s, name); \ + M_F(name, _unlink)(obj); \ + M_CALL_CLEAR(oplist, obj); \ + M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \ + M_F(name, _next)(it); \ + } \ + \ + M_IF_METHOD2(NEW, INIT_SET, oplist)( \ + M_INLINE void \ + M_F(name, _insert)(list_t list, it_t it, type x) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_IL1ST_NODE_CONTRACT(it->current); \ + type *p = M_CALL_NEW(oplist, type); \ + if (M_UNLIKELY_NOMEM (p == NULL)) { \ + M_MEMORY_FULL (sizeof (type)); \ + return ; \ + } \ + M_CALL_INIT_SET(oplist, *p, x); \ + type *obj = M_F(name, _ref)(it); \ + M_F(name, _push_after)(obj, p); \ + it->current = p; \ + (void) list; \ + M_IL1ST_CONTRACT(name, list); \ + } \ + , /* NEW & INIT_SET not defined */) \ + \ + M_INLINE type * \ + M_F(name, _pop_back)(list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT (!M_F(name, _empty_p)(list)); \ + type *obj = M_F(name, _back)(list); \ + list->name.prev = list->name.prev->prev; \ + list->name.prev->next = &list->name; \ + return obj; \ + } \ + \ + M_INLINE type * \ + M_F(name, _pop_front)(list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + M_ASSERT (!M_F(name, _empty_p)(list)); \ + type *obj = M_F(name, _front)(list); \ + list->name.next = list->name.next->next; \ + list->name.next->prev = &list->name; \ + return obj; \ + } \ + \ + M_INLINE void \ + M_F(name, _splice)(list_t list1, list_t list2) \ + { \ + M_IL1ST_CONTRACT(name, list1); \ + M_IL1ST_CONTRACT(name, list2); \ + struct m_il1st_head_s *midle1 = list1->name.prev; \ + struct m_il1st_head_s *midle2 = list2->name.next; \ + midle1->next = midle2; \ + midle2->prev = midle1; \ + list1->name.prev = list2->name.prev; \ + list2->name.prev->next = &list1->name; \ + list2->name.next = &list2->name; \ + list2->name.prev = &list2->name; \ + M_IL1ST_CONTRACT(name, list1); \ + M_IL1ST_CONTRACT(name, list2); \ + } \ + \ + M_INLINE void \ + M_F(name, _splice_back)(list_t nv, list_t ov, it_t it) \ + { \ + M_IL1ST_CONTRACT(name, nv); \ + M_IL1ST_CONTRACT(name, ov); \ + M_IL1ST_NODE_CONTRACT(it->current); \ + M_ASSERT (it != NULL); \ + (void) ov; \ + type *obj = M_F(name, _ref)(it); \ + M_F(name, _unlink)(obj); \ + M_F(name, _push_back)(nv, obj); \ + M_F(name, _next)(it); \ + M_IL1ST_CONTRACT(name, nv); \ + M_IL1ST_CONTRACT(name, ov); \ + } \ + \ + M_INLINE void \ + M_F(name, _splice_at)(list_t nlist, it_t npos, \ + list_t olist, it_t opos) \ + { \ + M_IL1ST_CONTRACT(name, nlist); \ + M_IL1ST_CONTRACT(name, olist); \ + M_ASSERT (npos != NULL && opos != NULL); \ + M_ASSERT (!M_F(name, _end_p)(opos)); \ + /* npos may be end */ \ + (void) olist, (void) nlist; \ + type *obj = M_F(name, _ref)(opos); \ + struct m_il1st_head_s *ref = npos->current; \ + /* Remove object */ \ + M_F(name, _unlink)(obj); \ + /* Push 'obj' after 'ref' */ \ + struct m_il1st_head_s *next = ref->next; \ + ref->next = &obj->name; \ + obj->name.next = next; \ + obj->name.prev = ref; \ + next->prev = &obj->name; \ + /* Move iterator in old list */ \ + M_F(name, _next)(opos); \ + /* Set npos iterator to new position of object */ \ + npos->previous = ref; \ + npos->current = &obj->name; \ + npos->next = next; \ + M_IL1ST_CONTRACT(name, nlist); \ + M_IL1ST_CONTRACT(name, olist); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(list_t d, list_t e) \ + { \ + M_IL1ST_CONTRACT(name, d); \ + M_IL1ST_CONTRACT(name, e); \ + struct m_il1st_head_s *d_item = d->name.next; \ + struct m_il1st_head_s *e_item = e->name.next; \ + /* it is more complicated than other swap functions since \ + we need to detect "cyclic" loop */ \ + d->name.next = e_item == &e->name ? &d->name : e_item; \ + e->name.next = d_item == &d->name ? &e->name : d_item; \ + d_item = d->name.prev; \ + e_item = e->name.prev; \ + d->name.prev = e_item == &e->name ? &d->name : e_item; \ + e->name.prev = d_item == &d->name ? &e->name : d_item; \ + d->name.next->prev = &d->name; \ + d->name.prev->next = &d->name; \ + e->name.next->prev = &e->name; \ + e->name.prev->next = &e->name; \ + M_IL1ST_CONTRACT(name, d); \ + M_IL1ST_CONTRACT(name, e); \ + } \ + \ + M_INLINE void \ + M_F(name, _reverse)(list_t list) \ + { \ + M_IL1ST_CONTRACT(name, list); \ + struct m_il1st_head_s *next, *it; \ + for(it = list->name.next ; it != &list->name; it = next) { \ + next = it->next; \ + it->next = it->prev; \ + it->prev = next; \ + } \ + next = it->next; \ + it->next = it->prev; \ + it->prev = next; \ + M_IL1ST_CONTRACT(name, list); \ + } \ + + +/******************************** INTERNAL ***********************************/ + +#if M_USE_SMALL_NAME +#define ILIST_INTERFACE M_ILIST_INTERFACE +#define ILIST_DEF M_ILIST_DEF +#define ILIST_DEF_AS M_ILIST_DEF_AS +#define ILIST_OPLIST M_ILIST_OPLIST +#endif + +#endif diff --git a/components/mlib/m-i-shared.h b/components/mlib/m-i-shared.h new file mode 100644 index 00000000..f81fa555 --- /dev/null +++ b/components/mlib/m-i-shared.h @@ -0,0 +1,255 @@ +/* + * M*LIB - INTRUSIVE SHARED PTR Module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_I_SHARED_PTR_H +#define MSTARLIB_I_SHARED_PTR_H + +#include "m-core.h" +#include "m-atomic.h" + +M_BEGIN_PROTECTED_CODE + +/* Define the oplist of a intrusive shared pointer. + USAGE: ISHARED_OPLIST(name [, oplist_of_the_type]) */ +#define M_ISHARED_PTR_OPLIST(...) \ + M_ISHAR3D_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST), \ + (__VA_ARGS__ ))) + + +/* Interface to add to a structure to allow intrusive support. + name: name of the intrusive shared pointer. + type: name of the type of the structure (aka. struct test_s) - not used currently. + NOTE: There can be only one interface of this kind in a type! */ +#define M_ISHARED_PTR_INTERFACE(name, type) \ + atomic_int M_F(name, _cpt) + + +/* Value of the interface field for static intialization (Uses C99 designated element). */ +#define M_ISHARED_PTR_STATIC_DESIGNATED_INIT(name, type) \ + .M_F(name, _cpt) = M_ATOMIC_VAR_INIT(0) + +/* Value of the interface field for static intialization (Uses C89 designated element). */ +#define M_ISHARED_PTR_STATIC_INIT(name, type) \ + M_ATOMIC_VAR_INIT(0) + + +/* Define the intrusive shared pointer type and its M_INLINE functions. + USAGE: ISHARED_PTR_DEF(name, type, [, oplist]) */ +#define M_ISHARED_PTR_DEF(name, ...) \ + M_ISHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define the intrusive shared pointer type and its M_INLINE functions + as the name name_t + USAGE: ISHARED_PTR_DEF_AS(name, name_t, type, [, oplist]) */ +#define M_ISHARED_PTR_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_ISHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \ + (name, __VA_ARGS__ , name_t ))) \ + M_END_PROTECTED_CODE + + +/*****************************************************************************/ +/******************************** INTERNAL ***********************************/ +/*****************************************************************************/ + +// Deferred evaluation +#define M_ISHAR3D_PTR_OPLIST_P1(arg) M_ISHAR3D_PTR_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_ISHAR3D_PTR_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_ISHAR3D_PTR_OPLIST_P3, M_ISHAR3D_PTR_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_ISHAR3D_PTR_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_ISHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +// Define the oplist +#define M_ISHAR3D_PTR_OPLIST_P3(name, oplist) ( \ + INIT(M_INIT_DEFAULT), \ + INIT_SET(API_4(M_F(name, _init_set))), \ + SET(M_F(name, _set) M_IPTR), \ + CLEAR(M_F(name, _clear)), \ + RESET(M_F(name, _reset) M_IPTR), \ + NAME(name), \ + TYPE(M_F(name, _ct)), \ + OPLIST(oplist), \ + SUBTYPE(M_F(name, _subtype_ct)) \ + ) + + +/******************************** INTERNAL ***********************************/ + +// Deferred evaluatioin +#define M_ISHAR3D_PTR_DEF_P1(arg) M_ID( M_ISHAR3D_PTR_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_ISHAR3D_PTR_DEF_P2(name, type, oplist, shared_t) \ + M_IF_OPLIST(oplist)(M_ISHAR3D_PTR_DEF_P3, M_ISHAR3D_PTR_DEF_FAILURE)(name, type, oplist, shared_t) + +/* Stop processing with a compilation failure */ +#define M_ISHAR3D_PTR_DEF_FAILURE(name, type, oplist, shared_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ISHARED_PTR_DEF): the given argument is not a valid oplist: " #oplist) + +#define M_ISHAR3D_PTR_DEF_P3(name, type, oplist, shared_t) \ + M_ISHAR3D_PTR_DEF_TYPE(name, type, oplist, shared_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_ISHAR3D_PTR_DEF_CORE(name, type, oplist, shared_t) \ + +/* Define the types */ +#define M_ISHAR3D_PTR_DEF_TYPE(name, type, oplist, shared_t) \ + \ + /* The shared pointer is only a pointer to the type */ \ + typedef type *shared_t; \ + \ + /* Define internal types for oplist */ \ + typedef shared_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + +/* Define the core functions */ +#define M_ISHAR3D_PTR_DEF_CORE(name, type, oplist, shared_t) \ + \ + M_INLINE shared_t \ + M_F(name, _init)(type *ptr) \ + { \ + /* Initialize the type referenced by the pointer */ \ + if (M_LIKELY (ptr != NULL)) { \ + atomic_init(&ptr->M_F(name, _cpt), 2); \ + } \ + return ptr; \ + } \ + \ + M_INLINE shared_t \ + M_F(name, _init_set)(shared_t shared) \ + { \ + if (M_LIKELY (shared != NULL)) { \ + int n = atomic_fetch_add(&(shared->M_F(name, _cpt)), 2); \ + (void) n; \ + } \ + return shared; \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_IF_DISABLED_METHOD(NEW, oplist) \ + ( \ + /* This function is only for static object */ \ + M_INLINE shared_t \ + M_F(name, _init_once)(type *shared) \ + { \ + if (M_LIKELY (shared != NULL)) { \ + /* Pretty much like atomic_add, except the first one increment by 1, others by 2 */ \ + int o = atomic_load(&(shared->M_F(name, _cpt))); \ + int n; \ + do { \ + n = o + 1 + (o != 0); \ + } while (!atomic_compare_exchange_strong(&(shared->M_F(name, _cpt)), &o, n)); \ + if (o == 0) { \ + /* Partial initialization: _cpt is odd */ \ + /* Call the INIT function once */ \ + M_CALL_INIT(oplist, *shared); \ + /* Finish initialization: _cpt is even */ \ + atomic_fetch_add(&(shared->M_F(name, _cpt)), 1); \ + } else if ( (o&1) != 0) { \ + /* Not fully initialized yet: wait for initialization */ \ + m_core_backoff_ct bkoff; \ + m_core_backoff_init(bkoff); \ + /* Wait for _cpt to be _even */ \ + while ((atomic_load(&(shared->M_F(name, _cpt)))&1) != 0 ) { \ + m_core_backoff_wait(bkoff); \ + } \ + } \ + M_ASSERT( (atomic_load(&(shared->M_F(name, _cpt)))&1) == 0); \ + } \ + return shared; \ + } \ + , \ + /* This function is only for dynamic object */ \ + M_INLINE shared_t \ + M_F(name, _init_new)(void) \ + { \ + type *ptr = M_CALL_NEW(oplist, type); \ + if (M_UNLIKELY_NOMEM (ptr == NULL)) { \ + M_MEMORY_FULL(sizeof(type)); \ + return NULL; \ + } \ + M_CALL_INIT(oplist, *ptr); \ + atomic_init (&ptr->M_F(name, _cpt), 2); \ + return ptr; \ + } \ + /* End of NEW */) \ + , /* End of INIT */) \ + \ + M_INLINE void \ + M_F(name, _clear)(shared_t shared) \ + { \ + if (shared != NULL) { \ + if (atomic_fetch_sub(&(shared->M_F(name, _cpt)), 2) == 2) { \ + M_CALL_CLEAR(oplist, *shared); \ + M_IF_DISABLED_METHOD(DEL, oplist)(, M_CALL_DEL(oplist, shared);) \ + } \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _clear_ptr)(shared_t *shared) \ + { \ + M_ASSERT(shared != NULL); \ + M_F(name, _clear)(*shared); \ + *shared = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(shared_t *shared) \ + { \ + M_F(name, _clear)(*shared); \ + *shared = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(shared_t *ptr, shared_t shared) \ + { \ + M_ASSERT (ptr != NULL); \ + if (M_LIKELY (*ptr != shared)) { \ + M_F(name, _clear)(*ptr); \ + *ptr = M_F(name, _init_set)(shared); \ + } \ + } \ + \ + +M_END_PROTECTED_CODE + +/******************************** INTERNAL ***********************************/ + +#if M_USE_SMALL_NAME +#define ISHARED_PTR_OPLIST M_ISHARED_PTR_OPLIST +#define ISHARED_PTR_INTERFACE M_ISHARED_PTR_INTERFACE +#define ISHARED_PTR_STATIC_DESIGNATED_INIT M_ISHARED_PTR_STATIC_DESIGNATED_INIT +#define ISHARED_PTR_STATIC_INIT M_ISHARED_PTR_STATIC_INIT +#define ISHARED_PTR_DEF M_ISHARED_PTR_DEF +#define ISHARED_PTR_DEF_AS M_ISHARED_PTR_DEF_AS +#endif + +#endif diff --git a/components/mlib/m-list.h b/components/mlib/m-list.h new file mode 100644 index 00000000..7a79b9a9 --- /dev/null +++ b/components/mlib/m-list.h @@ -0,0 +1,1527 @@ +/* + * M*LIB - LIST module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_LIST_H +#define MSTARLIB_LIST_H + +#include "m-core.h" + +/* Define a singly linked list of a given type. + USAGE: LIST_DEF(name, type [, oplist_of_the_type]) */ +#define M_LIST_DEF(name, ...) \ + M_LIST_DEF_AS(name, M_F(name, _t), M_F(name, _it_t), __VA_ARGS__) + + +/* Define a singly linked list of a given type + as the provided type name_t with the iterator named it_t + USAGE: LIST_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */ +#define M_LIST_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_L1ST_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \ + (name, __VA_ARGS__, name_t, it_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a singly linked list of a given type allowing both push. + USAGE: LIST_DUAL_PUSH_DEF_AS(name, name_t, type [, oplist_of_the_type]) */ +#define M_LIST_DUAL_PUSH_DEF(name, ...) \ + M_LIST_DUAL_PUSH_DEF_AS(name, M_F(name,_t), M_F(name, _it_t), __VA_ARGS__) + + +/* Define a singly linked list of a given type allowing both push. + as the provided type name_t with the iterator named it_t + USAGE: LIST_DUAL_PUSH_DEF(name, type [, oplist_of_the_type]) */ +#define M_LIST_DUAL_PUSH_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_L1ST_DUAL_PUSH_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \ + (name, __VA_ARGS__, name_t, it_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a list of the given type. + USAGE: LIST_OPLIST(name [, oplist_of_the_type]) */ +#define M_LIST_OPLIST(...) \ + M_L1ST_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST ), \ + (__VA_ARGS__ ))) + +/* Define an init value to init global variables of type list. + USAGE: + list_t global_variable = LIST_INIT_VALUE(); + */ +#define M_LIST_INIT_VALUE() \ + { NULL } + + +/* Define an init value to init global variables of type dual push list. + USAGE: + list_t global_variable = LIST_DUAL_PUSH_INIT_VALUE(); + */ +#define M_LIST_DUAL_PUSH_INIT_VALUE() \ + { { NULL, NULL } } + + +/********************************** INTERNAL ************************************/ + +/* Deferred evaluation for the oplist definition, + so that all arguments are evaluated before further expansion */ +#define M_L1ST_OPLIST_P1(arg) M_L1ST_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_L1ST_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_L1ST_OPLIST_P3, M_L1ST_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_L1ST_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_LIST_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* OPLIST definition of a list and list_dual_push */ +#define M_L1ST_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)), \ + INIT_SET(M_F(name, _init_set)), \ + INIT_WITH(API_1(M_INIT_WITH_VAI)), \ + SET(M_F(name, _set)), \ + CLEAR(M_F(name, _clear)), \ + MOVE(M_F(name, _move)), \ + INIT_MOVE(M_F(name, _init_move)), \ + SWAP(M_F(name, _swap)), \ + NAME(name), \ + TYPE(M_F(name,_ct)), \ + SUBTYPE(M_F(name,_subtype_ct)), \ + EMPTY_P(M_F(name,_empty_p)), \ + IT_TYPE(M_F(name, _it_ct)), \ + IT_FIRST(M_F(name,_it)), \ + IT_END(M_F(name,_it_end)), \ + IT_SET(M_F(name,_it_set)), \ + IT_END_P(M_F(name,_end_p)), \ + IT_EQUAL_P(M_F(name,_it_equal_p)), \ + IT_LAST_P(M_F(name,_last_p)), \ + IT_NEXT(M_F(name,_next)), \ + IT_REF(M_F(name,_ref)), \ + IT_CREF(M_F(name,_cref)), \ + IT_INSERT(M_F(name, _insert)), \ + IT_REMOVE(M_F(name,_remove)), \ + RESET(M_F(name,_reset)), \ + PUSH(M_F(name,_push_back)), \ + POP(M_F(name,_pop_back)), \ + PUSH_MOVE(M_F(name,_push_move)), \ + POP_MOVE(M_F(name,_pop_move)) \ + ,SPLICE_BACK(M_F(name,_splice_back)) \ + ,SPLICE_AT(M_F(name,_splice_at)) \ + ,REVERSE(M_F(name,_reverse)) \ + ,OPLIST(oplist) \ + ,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \ + ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \ + ,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \ + ,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \ + ,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \ + ,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \ + ,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \ + ,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \ + ) + +/* Deferred evaluation for the list definition, + so that all arguments are evaluated before further expansion */ +#define M_L1ST_DEF_P1(arg) M_ID( M_L1ST_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_L1ST_DEF_P2(name, type, oplist, list_t, it_t) \ + M_IF_OPLIST(oplist)(M_L1ST_DEF_P3, M_L1ST_DEF_FAILURE)(name, type, oplist, list_t, it_t) + +/* Stop processing with a compilation failure */ +#define M_L1ST_DEF_FAILURE(name, type, oplist, list_t, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(LIST_DEF): the given argument is not a valid oplist: " #oplist) + +/* Define allocation functions. If MEMPOOL, we need to define it */ +#define M_L1ST_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \ + M_IF_METHOD(MEMPOOL, oplist)( \ + \ + MEMPOOL_DEF(M_F(name, _mempool), struct M_F(name, _s)) \ + M_GET_MEMPOOL_LINKAGE oplist M_F(name, _mempool_t) M_GET_MEMPOOL oplist; \ + M_INLINE struct M_F(name, _s) *M_C3(m_l1st_,name,_new)(void) { \ + return M_F(name, _mempool_alloc)(M_GET_MEMPOOL oplist); \ + } \ + M_INLINE void M_C3(m_l1st_,name,_del)(struct M_F(name, _s) *ptr) { \ + M_F(name, _mempool_free)(M_GET_MEMPOOL oplist, ptr); \ + } \ + \ + , /* No mempool allocation */ \ + \ + M_INLINE struct M_F(name, _s) *M_C3(m_l1st_,name,_new)(void) { \ + return M_CALL_NEW(oplist, struct M_F(name, _s)); \ + } \ + M_INLINE void M_C3(m_l1st_,name,_del)(struct M_F(name, _s) *ptr) { \ + M_CALL_DEL(oplist, ptr); \ + } \ + ) \ + + +/* Internal list definition + - name: prefix to be used + - type: type of the elements of the list + - oplist: oplist of the type of the elements of the container + - list_t: alias for M_F(name, _t) [ type of the container ] + - it_t: alias for M_F(name, _it_t) [ iterator of the container ] + - node_t: alias for M_F(name, _node_t) [ node ] + */ +#define M_L1ST_DEF_P3(name, type, oplist, list_t, it_t) \ + \ + /* Define the node of a list, and the list as a pointer to a node */ \ + typedef struct M_F(name, _s) { \ + struct M_F(name, _s) *next; /* Next node or NULL if final node */ \ + type data; /* The data itself */ \ + } *list_t[1]; \ + \ + /* Define an iterator of a list */ \ + typedef struct M_F(name, _it_s) { \ + struct M_F(name, _s) *previous; /* Previous node or NULL */ \ + struct M_F(name, _s) *current; /* Current node or NULL */ \ + } it_t[1]; \ + \ + /* Definition of the synonyms of the type */ \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + typedef list_t M_F(name, _ct); \ + typedef it_t M_F(name, _it_ct); \ + typedef type M_F(name, _subtype_ct); \ + \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + \ + M_L1ST_MEMPOOL_DEF(name, type, oplist, list_t, it_t) \ + M_L1ST_DEF_P4(name, type, oplist, list_t, it_t) \ + M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t) + + +/* Define the internal contract of a list + (there is nothing worthy to be checked) */ +#define M_L1ST_CONTRACT(v) do { \ + M_ASSERT (v != NULL); \ + } while (0) + + +/* Internal list function definition + - name: prefix to be used + - type: type of the elements of the list + - oplist: oplist of the type of the elements of the container + - list_t: alias for type of the container + - it_t: alias for iterator of the container + */ +#define M_L1ST_DEF_P4(name, type, oplist, list_t, it_t) \ + \ + M_INLINE void \ + M_F(name, _init)(list_t v) \ + { \ + M_ASSERT (v != NULL); \ + *v = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(list_t v) \ + { \ + M_L1ST_CONTRACT(v); \ + struct M_F(name, _s) *it = *v; \ + *v = NULL; \ + while (it != NULL) { \ + struct M_F(name, _s) *next = it->next; \ + M_CALL_CLEAR(oplist, it->data); \ + M_C3(m_l1st_,name,_del)(it); \ + it = next; \ + } \ + M_L1ST_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(list_t v) \ + { \ + M_F(name, _reset)(v); \ + } \ + \ + M_INLINE type * \ + M_F(name, _back)(const list_t v) \ + { \ + M_L1ST_CONTRACT(v); \ + M_ASSERT(*v != NULL); \ + return &((*v)->data); \ + } \ + \ + M_INLINE type * \ + M_F(name, _push_raw)(list_t v) \ + { \ + M_L1ST_CONTRACT(v); \ + struct M_F(name, _s) *next; \ + next = M_C3(m_l1st_,name,_new)(); \ + if (M_UNLIKELY_NOMEM (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \ + return NULL; \ + } \ + type *ret = &next->data; \ + next->next = *v; \ + *v = next; \ + M_L1ST_CONTRACT(v); \ + return ret; \ + } \ + \ + M_INLINE void \ + M_F(name, _push_back)(list_t v, type const x) \ + { \ + type *data = M_F(name, _push_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return; \ + M_CALL_INIT_SET(oplist, *data, x); \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE type * \ + M_F(name, _push_new)(list_t v) \ + { \ + type *data = M_F(name, _push_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return NULL; \ + M_CALL_INIT(oplist, *data); \ + return data; \ + } \ + , /* No INIT */) \ + \ + M_INLINE void \ + M_F(name, _pop_back)(type *data, list_t v) \ + { \ + M_L1ST_CONTRACT(v); \ + M_ASSERT(*v != NULL); \ + if (data != NULL) { \ + M_DO_MOVE (oplist, *data, (*v)->data); \ + } else { \ + M_CALL_CLEAR(oplist, (*v)->data); \ + } \ + struct M_F(name, _s) *tofree = *v; \ + *v = (*v)->next; \ + M_C3(m_l1st_,name,_del)(tofree); \ + M_L1ST_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _push_move)(list_t v, type *x) \ + { \ + M_ASSERT (x != NULL); \ + type *data = M_F(name, _push_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return; \ + M_DO_INIT_MOVE (oplist, *data, *x); \ + } \ + \ + M_INLINE void \ + M_F(name, _pop_move)(type *data, list_t v) \ + { \ + M_L1ST_CONTRACT(v); \ + M_ASSERT(*v != NULL && data != NULL); \ + M_DO_INIT_MOVE (oplist, *data, (*v)->data); \ + struct M_F(name, _s) *tofree = *v; \ + *v = (*v)->next; \ + M_C3(m_l1st_,name,_del)(tofree); \ + M_L1ST_CONTRACT(v); \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(const list_t v) \ + { \ + M_L1ST_CONTRACT(v); \ + return *v == NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(list_t l, list_t v) \ + { \ + M_L1ST_CONTRACT(l); \ + M_L1ST_CONTRACT(v); \ + M_SWAP(struct M_F(name, _s) *, *l, *v); \ + M_L1ST_CONTRACT(l); \ + M_L1ST_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(it_t it, const list_t v) \ + { \ + M_L1ST_CONTRACT(v); \ + M_ASSERT (it != NULL); \ + it->current = *v; \ + it->previous = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t it1, const it_t it2) \ + { \ + M_ASSERT (it1 != NULL && it2 != NULL); \ + it1->current = it2->current; \ + it1->previous = it2->previous; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(it_t it1, const list_t v) \ + { \ + M_L1ST_CONTRACT(v); \ + M_ASSERT (it1 != NULL); \ + (void)v; /* unused */ \ + it1->current = NULL; \ + it1->previous = NULL; \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(const it_t it) \ + { \ + M_ASSERT (it != NULL); \ + return it->current == NULL; \ + } \ + \ + M_INLINE bool \ + M_F(name, _last_p)(const it_t it) \ + { \ + M_ASSERT (it != NULL); \ + return it->current == NULL || it->current->next == NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(it_t it) \ + { \ + M_ASSERT(it != NULL && it->current != NULL); \ + it->previous = it->current; \ + it->current = it->current->next; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const it_t it1, const it_t it2) \ + { \ + M_ASSERT(it1 != NULL && it2 != NULL); \ + return it1->current == it2->current; \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(const it_t it) \ + { \ + M_ASSERT(it != NULL && it->current != NULL); \ + return &(it->current->data); \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(const it_t it) \ + { \ + M_ASSERT(it != NULL && it->current != NULL); \ + return M_CONST_CAST(type, &(it->current->data)); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(const list_t list) \ + { \ + M_L1ST_CONTRACT(list); \ + size_t size = 0; \ + struct M_F(name, _s) *it = *list; \ + while (it != NULL) { \ + size ++; \ + it = it->next; \ + } \ + return size; \ + } \ + \ + M_INLINE bool \ + M_F(name, _sublist_p)(const list_t list, const it_t itsub) \ + { \ + M_L1ST_CONTRACT(list); \ + M_ASSERT (itsub != NULL); \ + struct M_F(name, _s) *it = *list; \ + while (it != NULL) { \ + if (it == itsub->current) return true; \ + it = it->next; \ + } \ + /* Not found. Check if search item is NULL */ \ + return (itsub->current == NULL); \ + } \ + \ + M_INLINE type * \ + M_F(name, _get)(const list_t list, size_t i) \ + { \ + M_L1ST_CONTRACT(list); \ + struct M_F(name, _s) *it = *list; \ + /* FIXME: How to avoid the double iteration over the list? */ \ + size_t len = M_F(name,_size)(list); \ + M_ASSERT_INDEX (i, len); \ + size_t j = len-1; \ + while (true) { \ + M_ASSERT (it != NULL); \ + if (i == j) return &it->data; \ + it = it->next; \ + j--; \ + } \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cget)(const list_t l, size_t i) \ + { \ + return M_CONST_CAST(type, M_F(name, _get)(l,i)); \ + } \ + \ + M_INLINE void \ + M_F(name, _insert)(list_t list, it_t insertion_point, \ + type const x) \ + { \ + M_L1ST_CONTRACT(list); \ + M_ASSERT (insertion_point != NULL); \ + M_ASSERT(M_F(name, _sublist_p)(list, insertion_point)); \ + struct M_F(name, _s) *next = M_C3(m_l1st_,name,_new)(); \ + if (M_UNLIKELY_NOMEM (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \ + return; \ + } \ + M_CALL_INIT_SET(oplist, next->data, x); \ + struct M_F(name, _s) *current = insertion_point->current; \ + if (M_UNLIKELY (current == NULL)) { \ + next->next = *list; \ + *list = next; \ + } else { \ + next->next = current->next; \ + current->next = next; \ + } \ + /* Update insertion_point to this element */ \ + insertion_point->current = next; \ + insertion_point->previous = current; \ + M_L1ST_CONTRACT(list); \ + } \ + \ + M_INLINE void \ + M_F(name, _remove)(list_t list, it_t removing_point) \ + { \ + M_L1ST_CONTRACT(list); \ + M_ASSERT (removing_point != NULL); \ + M_ASSERT (removing_point->current != NULL); \ + M_ASSERT(M_F(name, _sublist_p)(list, removing_point)); \ + struct M_F(name, _s) *next = removing_point->current->next; \ + if (M_UNLIKELY (removing_point->previous == NULL)) { \ + *list = next; \ + } else { \ + removing_point->previous->next = next; \ + } \ + M_CALL_CLEAR(oplist, removing_point->current->data); \ + M_C3(m_l1st_,name,_del) (removing_point->current); \ + removing_point->current = next; \ + M_L1ST_CONTRACT(list); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(list_t list, const list_t org) \ + { \ + M_L1ST_CONTRACT(org); \ + struct M_F(name, _s) *next, *it_org; \ + struct M_F(name, _s) **update_list; \ + update_list = list; \ + it_org = *org; \ + while (it_org != NULL) { \ + next = M_C3(m_l1st_,name,_new)(); \ + *update_list = next; \ + if (M_UNLIKELY_NOMEM (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \ + /* FIXME: Partialy initialized list. What to do? */ \ + return; \ + } \ + update_list = &next->next; \ + M_CALL_INIT_SET(oplist, next->data, it_org->data); \ + it_org = it_org->next; \ + } \ + *update_list = NULL; \ + M_L1ST_CONTRACT(list); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(list_t list, const list_t org) \ + { \ + if (M_UNLIKELY (list == org)) return; \ + M_F(name, _clear)(list); \ + M_F(name, _init_set)(list, org); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(list_t list, list_t org) \ + { \ + M_L1ST_CONTRACT(org); \ + M_ASSERT (list != NULL && list != org); \ + *list = *org; \ + *org = NULL; /* safer */ \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(list_t list, list_t org) \ + { \ + M_ASSERT (list != org); \ + M_F(name, _clear)(list); \ + M_F(name, _init_move)(list, org); \ + } \ + \ + M_INLINE void \ + M_F(name, _splice_back)(list_t nv, list_t ov, it_t it) \ + { \ + M_L1ST_CONTRACT(nv); \ + M_L1ST_CONTRACT(ov); \ + M_ASSERT (it != NULL); \ + M_ASSERT (it->current != NULL); \ + M_ASSERT (M_F(name, _sublist_p)(ov, it)); \ + /* Remove the item 'it' from the list 'ov' */ \ + struct M_F(name, _s) *current = it->current; \ + struct M_F(name, _s) *next = current->next; \ + if (it->previous == NULL) { \ + *ov = next; \ + } else { \ + it->previous->next = next; \ + } \ + /* Update the item 'it' to point to the next element */ \ + /* it->previous doesn't need to be updated */ \ + it->current = next; \ + /* Push back extracted 'current' in the list 'nv' */ \ + current->next = *nv; \ + *nv = current; \ + } \ + \ + M_INLINE void \ + M_F(name, _splice_at)(list_t nlist, it_t npos, \ + list_t olist, it_t opos) \ + { \ + M_L1ST_CONTRACT(nlist); \ + M_L1ST_CONTRACT(olist); \ + M_ASSERT (npos != NULL); \ + M_ASSERT (opos != NULL); \ + M_ASSERT (M_F(name, _sublist_p)(nlist, npos)); \ + M_ASSERT (M_F(name, _sublist_p)(olist, opos)); \ + /* Remove the item 'opos' from the list 'olist' */ \ + struct M_F(name, _s) *current = opos->current; \ + /* current shall reference a valid element of the list */ \ + M_ASSERT (current != NULL); \ + struct M_F(name, _s) *next = current->next; \ + if (opos->previous == NULL) { \ + *olist = next; \ + } else { \ + opos->previous->next = next; \ + } \ + /* Update 'opos' to point to the next element */ \ + opos->current = next; \ + /* Insert 'current' into 'nlist' just after 'npos' */ \ + struct M_F(name, _s) *previous = npos->current; \ + if (M_UNLIKELY (previous == NULL)) { \ + current->next = *nlist; \ + *nlist = current; \ + } else { \ + current->next = previous->next; \ + previous->next = current; \ + } \ + /* Update 'npos' to point to the new current element */ \ + npos->previous = npos->current; \ + npos->current = current; \ + M_L1ST_CONTRACT(nlist); \ + M_L1ST_CONTRACT(olist); \ + } \ + \ + M_INLINE void \ + M_F(name, _splice)(list_t list1, list_t list2) \ + { \ + M_L1ST_CONTRACT(list1); \ + M_L1ST_CONTRACT(list2); \ + M_ASSERT (list1 != list2); \ + struct M_F(name, _s) **update_list = list1; \ + struct M_F(name, _s) *it = *list1; \ + while (it != NULL) { \ + update_list = &it->next; \ + it = it->next; \ + } \ + *update_list = *list2; \ + *list2 = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _reverse)(list_t list) \ + { \ + M_L1ST_CONTRACT(list); \ + struct M_F(name, _s) *previous = NULL, *it = *list, *next; \ + while (it != NULL) { \ + next = it->next; \ + it->next = previous; \ + previous = it; \ + it = next; \ + } \ + *list = previous; \ + } \ + \ + M_EMPLACE_QUEUE_DEF(name, list_t, M_F(name, _emplace_back), oplist, M_L1ST_EMPLACE_DEF) + + +/* Internal list function definition using only iterator functions + which is common for all kind of lists. + It shall therefore only used the public interface of a list + and no contract can be checked at this level. + - name: prefix to be used + - type: type of the elements of the list + - oplist: oplist of the type of the elements of the container + - list_t: alias for M_F(name, _t) [ type of the container ] + - it_t: alias for M_F(name, _it_t) [ iterator of the container ] + */ +#define M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t) \ + \ + M_IF_METHOD(GET_STR, oplist)( \ + M_INLINE void \ + M_F(name, _get_str)(m_string_t str, const list_t list, \ + bool append) \ + { \ + M_ASSERT (str != NULL && list != NULL); \ + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \ + it_t it; \ + for (M_F(name, _it)(it, list) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + type const *item = M_F(name, _cref)(it); \ + M_CALL_GET_STR(oplist, str, *item, true); \ + if (!M_F(name, _last_p)(it)) \ + m_string_push_back (str, M_GET_SEPARATOR oplist); \ + } \ + m_string_push_back (str, ']'); \ + } \ + , /* no str */ ) \ + \ + M_IF_METHOD(OUT_STR, oplist)( \ + M_INLINE void \ + M_F(name, _out_str)(FILE *file, const list_t list) \ + { \ + M_ASSERT (file != NULL && list != NULL); \ + fputc ('[', file); \ + it_t it; \ + for (M_F(name, _it)(it, list) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + type const *item = M_F(name, _cref)(it); \ + M_CALL_OUT_STR(oplist, file, *item); \ + if (!M_F(name, _last_p)(it)) \ + fputc (M_GET_SEPARATOR oplist, file); \ + } \ + fputc (']', file); \ + } \ + , /* no out_str */ ) \ + \ + M_IF_METHOD2(PARSE_STR, INIT, oplist)( \ + M_INLINE bool \ + M_F(name, _parse_str)(list_t list, const char str[], const char **endp) \ + { \ + M_ASSERT (str != NULL && list != NULL); \ + M_F(name,_reset)(list); \ + bool success = false; \ + int c = *str++; \ + if (M_UNLIKELY (c != '[')) goto exit; \ + c = *str++; \ + if (M_UNLIKELY (c == ']')) { success = true; goto exit;} \ + if (M_UNLIKELY (c == 0)) goto exit; \ + str--; \ + type item; \ + M_CALL_INIT(oplist, item); \ + do { \ + bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \ + do { c = *str++; } while (isspace(c)); \ + if (b == false || c == 0) { goto exit_clear; } \ + M_F(name, _push_back)(list, item); \ + } while (c == M_GET_SEPARATOR oplist); \ + M_F(name, _reverse)(list); \ + success = (c == ']'); \ + exit_clear: \ + M_CALL_CLEAR(oplist, item); \ + exit: \ + if (endp) *endp = str; \ + return success; \ + } \ + , /* no PARSE_STR & INIT */ ) \ + \ + M_IF_METHOD2(IN_STR, INIT, oplist)( \ + M_INLINE bool \ + M_F(name, _in_str)(list_t list, FILE *file) \ + { \ + M_ASSERT (file != NULL && list != NULL); \ + M_F(name,_reset)(list); \ + int c = fgetc(file); \ + if (M_UNLIKELY (c != '[')) return false; \ + c = fgetc(file); \ + if (M_UNLIKELY (c == ']')) return true; \ + if (M_UNLIKELY (c == EOF)) return false; \ + ungetc(c, file); \ + type item; \ + M_CALL_INIT(oplist, item); \ + do { \ + bool b = M_CALL_IN_STR(oplist, item, file); \ + do { c = fgetc(file); } while (isspace(c)); \ + if (b == false || c == EOF) { break; } \ + M_F(name, _push_back)(list, item); \ + } while (c == M_GET_SEPARATOR oplist); \ + M_CALL_CLEAR(oplist, item); \ + M_F(name, _reverse)(list); \ + return c == ']'; \ + } \ + , /* no IN_STR & INIT */ ) \ + \ + M_IF_METHOD(OUT_SERIAL, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, const list_t list) \ + { \ + M_ASSERT (list != NULL); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_return_code_t ret; \ + m_serial_local_t local; \ + bool first_done = false; \ + ret = f->m_interface->write_array_start(local, f, (size_t)-1); \ + if (ret == M_SERIAL_FAIL_RETRY) { \ + size_t n = M_F(name, _size)(list); \ + ret = f->m_interface->write_array_start(local, f, n); \ + } \ + it_t it; \ + for (M_F(name, _it)(it, list) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + type const *item = M_F(name, _cref)(it); \ + if (first_done) \ + ret |= f->m_interface->write_array_next(local, f); \ + ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \ + first_done = true; \ + } \ + ret |= f->m_interface->write_array_end(local, f); \ + return ret & M_SERIAL_FAIL; \ + } \ + , /* no OUT_SERIAL */ ) \ + \ + M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(list_t list, m_serial_read_t f) \ + { \ + M_ASSERT (list != NULL); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_return_code_t ret; \ + m_serial_local_t local; \ + size_t estimated_size = 0; \ + M_F(name,_reset)(list); \ + ret = f->m_interface->read_array_start(local, f, &estimated_size); \ + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ + type item; \ + M_CALL_INIT(oplist, item); \ + do { \ + ret = M_CALL_IN_SERIAL(oplist, item, f); \ + if (ret != M_SERIAL_OK_DONE) { break; } \ + M_F(name, _push_back)(list, item); \ + ret = f->m_interface->read_array_next(local, f); \ + } while (ret == M_SERIAL_OK_CONTINUE); \ + M_CALL_CLEAR(oplist, item); \ + M_F(name, _reverse)(list); \ + return ret; \ + } \ + , /* no IN_SERIAL & INIT */ ) \ + \ + M_IF_METHOD(EQUAL, oplist)( \ + M_INLINE bool \ + M_F(name, _equal_p)(const list_t list1, const list_t list2) \ + { \ + M_ASSERT (list1 != NULL && list2 != NULL); \ + it_t it1; \ + it_t it2; \ + if (list1 == list2) return true; \ + M_F(name, _it)(it1, list1); \ + M_F(name, _it)(it2, list2); \ + while (!M_F(name, _end_p)(it1) \ + &&!M_F(name, _end_p)(it2)) { \ + type const *item1 = M_F(name, _cref)(it1); \ + type const *item2 = M_F(name, _cref)(it2); \ + bool b = M_CALL_EQUAL(oplist, *item1, *item2); \ + if (!b) return false; \ + M_F(name, _next)(it1); \ + M_F(name, _next)(it2); \ + } \ + return M_F(name, _end_p)(it1) \ + && M_F(name, _end_p)(it2); \ + } \ + , /* no equal */ ) \ + \ + M_IF_METHOD(HASH, oplist)( \ + M_INLINE size_t \ + M_F(name, _hash)(const list_t list) \ + { \ + M_ASSERT (list != NULL); \ + M_HASH_DECL(hash); \ + it_t it; \ + for(M_F(name, _it)(it, list) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)) { \ + type const *item = M_F(name, _cref)(it); \ + size_t hi = M_CALL_HASH(oplist, *item); \ + M_HASH_UP(hash, hi); \ + } \ + return M_HASH_FINAL (hash); \ + } \ + , /* no hash */ ) \ + + +/* Definition of the emplace_back function for single list */ +#define M_L1ST_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_F(name, _subtype_ct) *data = M_F(name, _push_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ + } + + +/* Definition of the emplace_back function for dual push list */ +#define M_L1ST_EMPLACE_BACK_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ + } + + +/* Definition of the emplace_front function for dual push list */ +#define M_L1ST_EMPLACE_FRONT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)(v); \ + if (M_UNLIKELY (data == NULL) ) \ + return; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ + } + + +/* Deferred evaluation for the dual-push list definition, + so that all arguments are evaluated before further expansion */ +#define M_L1ST_DUAL_PUSH_DEF_P1(arg) M_ID( M_L1ST_DUAL_PUSH_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_L1ST_DUAL_PUSH_DEF_P2(name, type, oplist, list_t, it_t) \ + M_IF_OPLIST(oplist)(M_L1ST_DUAL_PUSH_DEF_P3, M_L1ST_DUAL_PUSH_DEF_FAILURE)(name, type, oplist, list_t, it_t) + +/* Stop processing with a compilation failure */ +#define M_L1ST_DUAL_PUSH_DEF_FAILURE(name, type, oplist, list_t, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(LIST_DUAL_PUSH_DEF): the given argument is not a valid oplist: " #oplist) + +/* Internal dual-push list definition + - name: prefix to be used + - type: type of the elements of the array + - oplist: oplist of the type of the elements of the container + - list_t: alias for M_F(name, _t) [ type of the container ] + - it_t: alias for M_F(name, _it_t) [ iterator of the container ] + */ +#define M_L1ST_DUAL_PUSH_DEF_P3(name, type, oplist, list_t, it_t) \ + \ + /* Node of a list (it is liked the singly linked list) */ \ + struct M_F(name, _s) { \ + struct M_F(name, _s) *next; \ + type data; \ + }; \ + \ + /* Dual Push singly linked list. \ + Support Push Back / Push Front / Pop back in O(1). \ + Doesn't support Pop front. \ + This is done by keeping a pointer to both back & front \ + */ \ + typedef struct M_F(name, _head_s) { \ + struct M_F(name,_s) *front; /* Pointer to the front node or NULL */ \ + struct M_F(name,_s) *back; /* Pointer to the back node or NULL */ \ + } list_t[1]; \ + \ + /* Define the iterator over a dual push singly linked list */ \ + typedef struct M_F(name, _it_s) { \ + struct M_F(name, _s) *previous; \ + struct M_F(name, _s) *current; \ + } it_t[1]; \ + \ + /* Definition of the synonyms of the type */ \ + typedef struct M_F(name, _head_s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _head_s) *M_F(name, _srcptr); \ + typedef list_t M_F(name, _ct); \ + typedef it_t M_F(name, _it_ct); \ + typedef type M_F(name, _subtype_ct); \ + \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + \ + M_L1ST_MEMPOOL_DEF(name, type, oplist, list_t, it_t) \ + M_L1ST_DUAL_PUSH_DEF_P4(name, type, oplist, list_t, it_t) \ + M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t) + + +/* Define the internal contract of an dual-push list */ +#define M_L1ST_DUAL_PUSH_CONTRACT(l) do { \ + M_ASSERT (l != NULL); \ + M_ASSERT ( (l->back == NULL && l->front == NULL) \ + || (l->back != NULL && l->front != NULL)); \ + } while (0) + +/* Internal dual-push list definition + - name: prefix to be used + - type: type of the elements of the array + - oplist: oplist of the type of the elements of the container + - list_t: alias for type of the container + - it_t: alias for iterator of the container + */ +#define M_L1ST_DUAL_PUSH_DEF_P4(name, type, oplist, list_t, it_t) \ + \ + M_INLINE void \ + M_F(name, _init)(list_t v) \ + { \ + M_ASSERT( v != NULL); \ + v->front = NULL; \ + v->back = NULL; \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + struct M_F(name, _s) *it = v->back; \ + while (it != NULL) { \ + struct M_F(name, _s) *next = it->next; \ + M_CALL_CLEAR(oplist, it->data); \ + M_C3(m_l1st_,name,_del)(it); \ + it = next; \ + } \ + v->front = NULL; \ + v->back = NULL; \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(list_t v) \ + { \ + M_F(name, _reset)(v); \ + } \ + \ + M_INLINE type * \ + M_F(name, _back)(const list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + M_ASSERT (v->back != NULL); \ + return &(v->back->data); \ + } \ + \ + M_INLINE type * \ + M_F(name, _push_back_raw)(list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + struct M_F(name, _s) *next = M_C3(m_l1st_,name,_new)(); \ + if (M_UNLIKELY_NOMEM (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \ + return NULL; \ + } \ + type *ret = &next->data; \ + next->next = v->back; \ + v->back = next; \ + /* Update front too if the list was empty */ \ + /* This C code shall generate branchless code */ \ + struct M_F(name, _s) *front = v->front; \ + front = (front == NULL) ? next : front; \ + v->front = front; \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + return ret; \ + } \ + \ + /* Internal, for INIT_WITH */ \ + M_INLINE type * \ + M_F(name, _push_raw)(list_t d) \ + { \ + return M_F(name, _push_back_raw)(d); \ + } \ + \ + M_INLINE void \ + M_F(name, _push_back)(list_t v, type const x) \ + { \ + type *data = M_F(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return; \ + M_CALL_INIT_SET(oplist, *data, x); \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE type * \ + M_F(name, _push_back_new)(list_t v) \ + { \ + type *data = M_F(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return NULL; \ + M_CALL_INIT(oplist, *data); \ + return data; \ + } \ + , /* No INIT */ ) \ + \ + M_INLINE void \ + M_F(name, _push_back_move)(list_t v, type *x) \ + { \ + M_ASSERT (x != NULL); \ + type *data = M_F(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return; \ + M_DO_INIT_MOVE (oplist, *data, *x); \ + } \ + \ + M_INLINE void \ + M_F(name, _push_move)(list_t v, type *x) \ + { \ + M_F(name, _push_back_move)(v, x); \ + } \ + \ + M_INLINE void \ + M_F(name, _pop_back)(type *data, list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + M_ASSERT (v->back != NULL); \ + struct M_F(name, _s) *tofree = v->back; \ + if (data != NULL) { \ + M_DO_MOVE(oplist, *data, tofree->data); \ + } else { \ + M_CALL_CLEAR(oplist, tofree->data); \ + } \ + v->back = tofree->next; \ + M_C3(m_l1st_,name,_del)(tofree); \ + /* Update front too if the list became empty */ \ + /* This C code shall generate branchless code */ \ + struct M_F(name, _s) *front = v->front; \ + front = (front == tofree) ? NULL : front; \ + v->front = front; \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + } \ + \ + M_INLINE void \ + M_F(name, _pop_move)(type *data, list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + M_ASSERT (v->back != NULL); \ + M_ASSERT (data != NULL); \ + struct M_F(name, _s) *tofree = v->back; \ + M_DO_INIT_MOVE (oplist, *data, tofree->data); \ + v->back = tofree->next; \ + M_C3(m_l1st_,name,_del)(tofree); \ + /* Update front too if the list became empty */ \ + /* This C code shall generate branchless code */ \ + struct M_F(name, _s) *front = v->front; \ + front = (front == tofree) ? NULL : front; \ + v->front = front; \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + } \ + \ + M_INLINE type * \ + M_F(name, _front)(list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + M_ASSERT (v->front != NULL); \ + return &(v->front->data); \ + } \ + \ + M_INLINE type * \ + M_F(name, _push_front_raw)(list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + struct M_F(name, _s) *next = M_C3(m_l1st_,name,_new)(); \ + if (M_UNLIKELY_NOMEM (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \ + return NULL; \ + } \ + type *ret = &next->data; \ + next->next = NULL; \ + if (M_LIKELY(v->front != NULL)) { \ + v->front->next = next; \ + } else { \ + /* Update back too as the list was empty */ \ + v->back = next; \ + } \ + v->front = next; \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + return ret; \ + } \ + \ + M_INLINE void \ + M_F(name, _push_front)(list_t v, type const x) \ + { \ + type *data = M_F(name, _push_front_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return; \ + M_CALL_INIT_SET(oplist, *data, x); \ + } \ + \ + M_INLINE void \ + M_F(name, _push_front_move)(list_t v, type *x) \ + { \ + M_ASSERT (x != NULL); \ + type *data = M_F(name, _push_front_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return; \ + M_DO_INIT_MOVE (oplist, *data, *x); \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE type * \ + M_F(name, _push_front_new)(list_t v) \ + { \ + type *data = M_F(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return NULL; \ + M_CALL_INIT(oplist, *data); \ + return data; \ + } \ + , /* No INIT */) \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(const list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + return v->back == NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(list_t l, list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(l); \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + M_SWAP(struct M_F(name, _s) *, l->front, v->front); \ + M_SWAP(struct M_F(name, _s) *, l->back, v->back); \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(it_t it, const list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + M_ASSERT (it != NULL); \ + it->current = v->back; \ + it->previous = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t it1, const it_t it2) \ + { \ + M_ASSERT (it1 != NULL && it2 != NULL); \ + it1->current = it2->current; \ + it1->previous = it2->previous; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(it_t it1, const list_t v) \ + { \ + M_ASSERT (it1 != NULL); \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + (void)v; /* unused */ \ + it1->current = NULL; \ + it1->previous = NULL; \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(const it_t it) \ + { \ + M_ASSERT (it != NULL); \ + return it->current == NULL; \ + } \ + \ + M_INLINE bool \ + M_F(name, _last_p)(const it_t it) \ + { \ + M_ASSERT (it != NULL); \ + return it->current == NULL || it->current->next == NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(it_t it) \ + { \ + M_ASSERT(it != NULL && it->current != NULL); \ + it->previous = it->current; \ + it->current = it->current->next; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const it_t it1, const it_t it2) \ + { \ + M_ASSERT(it1 != NULL && it2 != NULL); \ + return it1->current == it2->current; \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(const it_t it) \ + { \ + M_ASSERT(it != NULL && it->current != NULL); \ + return &(it->current->data); \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(const it_t it) \ + { \ + M_ASSERT(it != NULL && it->current != NULL); \ + return M_CONST_CAST(type, &(it->current->data)); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(const list_t v) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(v); \ + size_t size = 0; \ + struct M_F(name, _s) *it = v->back; \ + while (it != NULL) { \ + size ++; \ + it = it->next; \ + } \ + return size; \ + } \ + \ + M_INLINE void \ + M_F(name, _insert)(list_t list, it_t insertion_point, \ + type const x) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(list); \ + M_ASSERT (insertion_point != NULL); \ + struct M_F(name, _s) *next = M_C3(m_l1st_,name,_new)(); \ + if (M_UNLIKELY_NOMEM (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \ + return; \ + } \ + M_CALL_INIT_SET(oplist, next->data, x); \ + if (M_UNLIKELY (insertion_point->current == NULL)) { \ + next->next = list->back; \ + list->back = next; \ + /* update front if list is empty */ \ + struct M_F(name, _s) *front = list->front; \ + front = (front == NULL) ? next : front; \ + list->front = front; \ + } else { \ + next->next = insertion_point->current->next; \ + insertion_point->current->next = next; \ + /* update front if current == front */ \ + struct M_F(name, _s) *front = list->front; \ + front = (front == insertion_point->current) ? next : front; \ + list->front = front; \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _remove)(list_t list, it_t removing_point) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(list); \ + M_ASSERT (removing_point != NULL); \ + M_ASSERT(removing_point->current != NULL); \ + struct M_F(name, _s) *next = removing_point->current->next; \ + struct M_F(name, _s) *previous = removing_point->previous; \ + if (M_UNLIKELY (previous == NULL)) { \ + list->back = next; \ + } else { \ + previous->next = next; \ + } \ + /* Update front */ \ + struct M_F(name, _s) *front = list->front; \ + front = (next == NULL) ? previous : front; \ + list->front = front; \ + /* Remove node */ \ + M_CALL_CLEAR(oplist, removing_point->current->data); \ + M_C3(m_l1st_,name,_del) (removing_point->current); \ + removing_point->current = next; \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(list_t list, const list_t org) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(list); \ + M_L1ST_DUAL_PUSH_CONTRACT(org); \ + struct M_F(name, _s) *next = NULL; \ + struct M_F(name, _s) *it_org; \ + struct M_F(name, _s) **update_list; \ + if (M_UNLIKELY (list == org)) return; \ + M_F(name, _reset)(list); \ + update_list = &list->back; \ + it_org = org->back; \ + while (it_org != NULL) { \ + next = M_C3(m_l1st_,name,_new)(); \ + *update_list = next; \ + if (M_UNLIKELY_NOMEM (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \ + return; \ + } \ + update_list = &next->next; \ + M_CALL_INIT_SET(oplist, next->data, it_org->data); \ + it_org = it_org->next; \ + } \ + list->front = next; \ + *update_list = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(list_t list, const list_t org) \ + { \ + M_ASSERT (list != org); \ + M_F(name, _init)(list); \ + M_F(name, _set)(list, org); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(list_t list, list_t org) \ + { \ + M_ASSERT (list != org); \ + list->back = org->back; \ + list->front = org->front; \ + org->back = NULL; \ + org->front = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(list_t list, list_t org) \ + { \ + M_F(name, _clear)(list); \ + M_F(name, _init_move)(list, org); \ + } \ + \ + M_INLINE void \ + M_F(name, _splice_back)(list_t list1, list_t list2, it_t it) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(list1); \ + M_L1ST_DUAL_PUSH_CONTRACT(list2); \ + M_ASSERT (it->current != NULL); \ + /* First remove the item 'it' from the list 'list2' */ \ + struct M_F(name, _s) *current = it->current; \ + struct M_F(name, _s) *next = current->next; \ + if (it->previous == NULL) { \ + list2->back = next; \ + } else { \ + it->previous->next = next; \ + } \ + /* Update the front of 'list2' if it was the last element */ \ + struct M_F(name, _s) *front = list2->front; \ + front = (next == NULL) ? it->previous : front; \ + list2->front = front; \ + /* Update 'it' to point to the next element */ \ + it->current = next; \ + /* Move the extracted 'current' into the list 'nv' */ \ + current->next = list1->back; \ + list1->back = current; \ + /* Update the front field if the list 'nv' was empty */ \ + /* This C code shall generate branchless code */ \ + front = list1->front; \ + front = (front == NULL) ? current : front; \ + list1->front = front; \ + M_L1ST_DUAL_PUSH_CONTRACT(list1); \ + M_L1ST_DUAL_PUSH_CONTRACT(list2); \ + } \ + \ + M_INLINE void \ + M_F(name, _splice_at)(list_t nlist, it_t npos, \ + list_t olist, it_t opos) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(nlist); \ + M_L1ST_DUAL_PUSH_CONTRACT(olist); \ + M_ASSERT (npos != NULL && opos != NULL); \ + /* First remove the item 'opos' from the list 'olist' */ \ + struct M_F(name, _s) *current = opos->current; \ + /* It shall refer a valid argument in the list */ \ + M_ASSERT(current != NULL); \ + struct M_F(name, _s) *next = current->next; \ + if (opos->previous == NULL) { \ + olist->back = next; \ + } else { \ + opos->previous->next = next; \ + } \ + /* Update the front of 'olist' if it was the last element */ \ + struct M_F(name, _s) *front = olist->front; \ + front = (next == NULL) ? opos->previous : front; \ + olist->front = front; \ + /* Update 'opos' to point to the next element */ \ + opos->current = next; \ + /* opos->previous is still valid & doesn't need to be updated */ \ + /* Insert into 'nlist' */ \ + struct M_F(name, _s) *npos_current = npos->current; \ + if (M_UNLIKELY (npos_current == NULL)) { \ + current->next = nlist->back; \ + nlist->back = current; \ + /* update 'front' if the list was empty (branchless) */ \ + front = nlist->front; \ + front = (front == NULL) ? current : front; \ + nlist->front = front; \ + } else { \ + current->next = npos_current->next; \ + npos_current->next = current; \ + /* update front if current == front (branchless) */ \ + front = nlist->front; \ + front = (front == npos_current) ? current : front; \ + nlist->front = front; \ + } \ + /* Update 'npos' to point to 'current'. */ \ + npos->previous = npos_current; \ + npos->current = current; \ + M_L1ST_DUAL_PUSH_CONTRACT(nlist); \ + M_L1ST_DUAL_PUSH_CONTRACT(olist); \ + } \ + \ + M_INLINE void \ + M_F(name, _splice)(list_t list1, list_t list2) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(list1); \ + M_L1ST_DUAL_PUSH_CONTRACT(list2); \ + M_ASSERT (list1 != list2); \ + if (M_LIKELY (list1->front != NULL)) { \ + list1->front->next = list2->back; \ + list1->front = list2->front; \ + } else { \ + /* list1 is empty */ \ + list1->back = list2->back; \ + list1->front = list2->front; \ + } \ + list2->back = NULL; \ + list2->front = NULL; \ + M_L1ST_DUAL_PUSH_CONTRACT(list1); \ + M_L1ST_DUAL_PUSH_CONTRACT(list2); \ + } \ + \ + M_INLINE void \ + M_F(name, _reverse)(list_t list) \ + { \ + M_L1ST_DUAL_PUSH_CONTRACT(list); \ + list->front = list->back; \ + struct M_F(name, _s) *previous = NULL, *it = list->back, *next; \ + while (it != NULL) { \ + next = it->next; \ + it->next = previous; \ + previous = it; \ + it = next; \ + } \ + list->back = previous; \ + M_L1ST_DUAL_PUSH_CONTRACT(list); \ + } \ + \ + M_EMPLACE_QUEUE_DEF(name, list_t, M_F(name, _emplace_back), oplist, M_L1ST_EMPLACE_BACK_DEF) \ + M_EMPLACE_QUEUE_DEF(name, list_t, M_F(name, _emplace_front), oplist, M_L1ST_EMPLACE_FRONT_DEF) + +#if M_USE_SMALL_NAME +#define LIST_DEF M_LIST_DEF +#define LIST_DEF_AS M_LIST_DEF_AS +#define LIST_DUAL_PUSH_DEF M_LIST_DUAL_PUSH_DEF +#define LIST_DUAL_PUSH_DEF_AS M_LIST_DUAL_PUSH_DEF_AS +#define LIST_OPLIST M_LIST_OPLIST +#define LIST_INIT_VALUE M_LIST_INIT_VALUE +#define LIST_DUAL_PUSH_INIT_VALUE M_LIST_DUAL_PUSH_INIT_VALUE +#endif + +#endif diff --git a/components/mlib/m-mempool.h b/components/mlib/m-mempool.h new file mode 100644 index 00000000..f36a152d --- /dev/null +++ b/components/mlib/m-mempool.h @@ -0,0 +1,206 @@ +/* + * M*LIB - MEMPOOL module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_MEMPOOL_H +#define MSTARLIB_MEMPOOL_H + +#include "m-core.h" + +/* Fast, fixed size, thread unsafe allocator based on memory regions. + No oplist is needed. + USAGE: + MEMPOOL_DEF(name, type) + Example: + MEMPOOL_DEF(mempool_uint, unsigned int) + ... + mempool_uint_t m; + mempool_uint_init(m); + unsigned int *ptr = mempool_uint_alloc(m); + *ptr = 17; + mempool_uint_free(m, ptr); + mempool_uint_clear(m); // Give back memory to system +*/ +#define M_MEMPOOL_DEF(name, type) \ + M_MEMPOOL_DEF_AS(name, M_F(name,_t), type) + + +/* Fast, fixed Size, thread unsafe allocator based on memory region. + USAGE: + MEMPOOL_DEF_AS(name, name_t, type) +*/ +#define M_MEMPOOL_DEF_AS(name, name_t, type) \ + M_BEGIN_PROTECTED_CODE \ + M_M3MPOOL_DEF_P2(name, type, name_t ) \ + M_END_PROTECTED_CODE + + +/* User shall be able to cutomize the size of the region segment and/or + the minimun number of elements. + The default is the number of elements that fits in 16KB, or 256 + is the size of the type is too big. +*/ +#ifndef M_USE_MEMPOOL_MAX_PER_SEGMENT +#define M_USE_MEMPOOL_MAX_PER_SEGMENT(type) \ + M_MAX((16*1024-sizeof(unsigned int) - 2*sizeof(void*)) / sizeof (type), 256U) +#endif + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* + Technically, it uses a list of memory regions, where multiple + allocations are performed in each region. However, it + can not use m-list since it may be expanded from LIST_DEF + (recursive dependency problem). */ +#define M_M3MPOOL_DEF_P2(name, type, name_t) \ + M_M3MPOOL_DEF_TYPE(name, type, name_t) \ + M_M3MPOOL_DEF_CORE(name, type, name_t) + +/* Define the types of the mempool */ +#define M_M3MPOOL_DEF_TYPE(name, type, name_t) \ + \ + /* Define the type of element in a segment of the mempool. \ + Either it is the basic type or a pointer to another one. */ \ + typedef union M_F(name,_union_s) { \ + type t; \ + union M_F(name,_union_s) *next; \ + } M_F(name,_union_ct); \ + \ + /* Define a segment of a mempool. \ + It is an array of basic type, each segment is in a linked list */ \ + typedef struct M_F(name,_segment_s) { \ + unsigned int count; \ + struct M_F(name,_segment_s) *next; \ + M_F(name,_union_ct) tab[M_USE_MEMPOOL_MAX_PER_SEGMENT(type)]; \ + } M_F(name,_segment_ct); \ + \ + /* Define a mempool. \ + It is a pointer to the first free object within the segments \ + and the segments themselves */ \ + typedef struct M_F(name, _s) { \ + M_F(name,_union_ct) *free_list; \ + M_F(name,_segment_ct) *current_segment; \ + } name_t[1]; \ + + +/* Define the core functions of the mempool */ +#define M_M3MPOOL_DEF_CORE(name, type, name_t) \ + \ + M_INLINE void \ + M_F(name,_init)(name_t mem) \ + { \ + mem->free_list = NULL; \ + mem->current_segment = M_MEMORY_ALLOC(M_F(name,_segment_ct)); \ + if (M_UNLIKELY_NOMEM(mem->current_segment == NULL)) { \ + M_MEMORY_FULL(sizeof (M_F(name,_segment_ct))); \ + return; \ + } \ + mem->current_segment->next = NULL; \ + mem->current_segment->count = 0; \ + M_M3MPOOL_CONTRACT(mem, type); \ + } \ + \ + M_INLINE void \ + M_F(name,_clear)(name_t mem) \ + { \ + M_M3MPOOL_CONTRACT(mem, type); \ + M_F(name,_segment_ct) *segment = mem->current_segment; \ + while (segment != NULL) { \ + M_F(name,_segment_ct) *next = segment->next; \ + M_MEMORY_DEL (segment); \ + segment = next; \ + } \ + /* Clean pointers to be safer */ \ + mem->free_list = NULL; \ + mem->current_segment = NULL; \ + } \ + \ + M_INLINE type * \ + M_F(name,_alloc)(name_t mem) \ + { \ + M_M3MPOOL_CONTRACT(mem, type); \ + /* Test if one object is in the free list */ \ + M_F(name,_union_ct) *ret = mem->free_list; \ + if (ret != NULL) { \ + /* Yes, so return it, and pop it from the free list */ \ + mem->free_list = ret->next; \ + return &ret->t; \ + } \ + /* No cheap free object exist. Test within a segment */ \ + M_F(name,_segment_ct) *segment = mem->current_segment; \ + M_ASSERT(segment != NULL); \ + unsigned int count = segment->count; \ + /* If segment is full, allocate a new one from the system */ \ + if (M_UNLIKELY (count >= M_USE_MEMPOOL_MAX_PER_SEGMENT(type))) { \ + M_F(name,_segment_ct) *new_segment = M_MEMORY_ALLOC (M_F(name,_segment_ct)); \ + if (M_UNLIKELY_NOMEM (new_segment == NULL)) { \ + M_MEMORY_FULL(sizeof (M_F(name,_segment_ct))); \ + return NULL; \ + } \ + new_segment->next = segment; \ + new_segment->count = 0; \ + mem->current_segment = new_segment; \ + segment = new_segment; \ + count = 0; \ + } \ + /* Return the object as the last element of the current segment */ \ + ret = &segment->tab[count]; \ + segment->count = count + 1; \ + M_M3MPOOL_CONTRACT(mem, type); \ + return &ret->t; \ + } \ + \ + M_INLINE void \ + M_F(name,_free)(name_t mem, type *ptr) \ + { \ + M_M3MPOOL_CONTRACT(mem, type); \ + /* NOTE: Unsafe cast: suppose that the given pointer \ + was allocated by the previous alloc function. */ \ + M_F(name,_union_ct) *ret = (M_F(name,_union_ct) *)(uintptr_t)ptr; \ + /* Add the object back in the free list */ \ + ret->next = mem->free_list; \ + mem->free_list = ret; \ + /* NOTE: the objects are NOT given back to the system until the mempool \ + is fully cleared */ \ + M_M3MPOOL_CONTRACT(mem, type); \ + } \ + +/* MEMPOOL contract. We only control the current segment. */ +#define M_M3MPOOL_CONTRACT(mempool, type) do { \ + M_ASSERT((mempool) != NULL); \ + M_ASSERT((mempool)->current_segment != NULL); \ + M_ASSERT((mempool)->current_segment->count <= M_USE_MEMPOOL_MAX_PER_SEGMENT(type)); \ + } while (0) + + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define MEMPOOL_DEF M_MEMPOOL_DEF +#define MEMPOOL_DEF_AS M_MEMPOOL_DEF_AS +#endif + +#endif diff --git a/components/mlib/m-mutex.h b/components/mlib/m-mutex.h new file mode 100644 index 00000000..6fc34eba --- /dev/null +++ b/components/mlib/m-mutex.h @@ -0,0 +1,28 @@ +/* + * M*LIB - Thin Mutex & Thread wrapper (compatibility layer) + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#if defined(__GNUC__) && __GNUC__ >= 4 +#warning "m-mutex.h is an obsolete header. Use m-thread.h instead." +#endif +#include "m-thread.h" diff --git a/components/mlib/m-prioqueue.h b/components/mlib/m-prioqueue.h new file mode 100644 index 00000000..4c79c0a6 --- /dev/null +++ b/components/mlib/m-prioqueue.h @@ -0,0 +1,520 @@ +/* + * M*LIB - dynamic priority queue module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_PRIOQUEUE_H +#define MSTARLIB_PRIOQUEUE_H + +#include "m-core.h" +#include "m-array.h" /* Priority queue are built upon array */ + +/* Priority queue based on binary heap implementation */ + +/* Define a prioqueue of a given type and its associated functions. + USAGE: PRIOQUEUE_DEF(name, type [, oplist_of_the_type]) */ +#define M_PRIOQUEUE_DEF(name, ...) \ + M_PRIOQUEUE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) + + +/* Define a prioqueue of a given type and its associated functions. + as the name name_t with an iterator named it_t + USAGE: PRIOQUEUE_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */ +#define M_PRIOQUEUE_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_PR1OQUEUE_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \ + (name, __VA_ARGS__, name_t, it_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a prioqueue of type. + USAGE: PRIOQUEUE_OPLIST(name[, oplist of the type]) */ +#define M_PRIOQUEUE_OPLIST(...) \ + M_PR1OQUEUE_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST), \ + (__VA_ARGS__ ))) + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_PR1OQUEUE_OPLIST_P1(arg) M_PR1OQUEUE_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_PR1OQUEUE_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_PR1OQUEUE_OPLIST_P3, M_PR1OQUEUE_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_PR1OQUEUE_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_PRIOQUEUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* Define oplist of a priority queue */ +#define M_PR1OQUEUE_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)) \ + ,INIT_SET(M_F(name, _init_set)) \ + ,INIT_WITH(API_1(M_INIT_VAI)) \ + ,SET(M_F(name, _set)) \ + ,CLEAR(M_F(name, _clear)) \ + ,INIT_MOVE(M_F(name, _init_move)) \ + ,MOVE(M_F(name, _move)) \ + ,SWAP(M_F(name, _swap)) \ + ,NAME(name) \ + ,TYPE(M_F(name,_ct)) \ + ,SUBTYPE(M_F(name, _subtype_ct)) \ + ,RESET(M_F(name,_reset)) \ + ,PUSH(M_F(name,_push)) \ + ,POP(M_F(name,_pop)) \ + ,OPLIST(oplist) \ + ,EMPTY_P(M_F(name, _empty_p)) \ + ,GET_SIZE(M_F(name, _size)) \ + ,IT_TYPE(M_F(name, _it_ct)) \ + ,IT_FIRST(M_F(name,_it)) \ + ,IT_END(M_F(name,_it_end)) \ + ,IT_SET(M_F(name,_it_set)) \ + ,IT_END_P(M_F(name,_end_p)) \ + ,IT_EQUAL_P(M_F(name,_it_equal_p)) \ + ,IT_LAST_P(M_F(name,_last_p)) \ + ,IT_NEXT(M_F(name,_next)) \ + ,IT_CREF(M_F(name,_cref)) \ + ,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \ + ,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \ + ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \ + ,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \ + ,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \ + ,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \ + ) + + +/********************************** INTERNAL *********************************/ + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_PR1OQUEUE_DEF_P1(arg) M_ID( M_PR1OQUEUE_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_PR1OQUEUE_DEF_P2(name, type, oplist, prioqueue_t, it_t) \ + M_IF_OPLIST(oplist)(M_PR1OQUEUE_DEF_P3, M_PR1OQUEUE_DEF_FAILURE)(name, type, oplist, prioqueue_t, it_t) + +/* Stop processing with a compilation failure */ +#define M_PR1OQUEUE_DEF_FAILURE(name, type, oplist, prioqueue_t, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(PRIOQUEUE_DEF): the given argument is not a valid oplist: " #oplist) + +/* Define the priority queue: + - name: prefix to use, + - type: type of the contained objects, + - oplist: oplist of the contained objects, + - prioqueue_t: type of the container, + - it_t: iterator of the container +*/ +#define M_PR1OQUEUE_DEF_P3(name, type, oplist, prioqueue_t, it_t) \ + /* Definition of the internal array used to construct the priority queue */ \ + ARRAY_DEF(M_F(name, _array), type, oplist) \ + M_PR1OQUEUE_DEF_TYPE(name, type, oplist, prioqueue_t, it_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_PR1OQUEUE_DEF_CORE(name, type, oplist, prioqueue_t, it_t) \ + M_PR1OQUEUE_DEF_IT(name, type, oplist, prioqueue_t, it_t) \ + M_PR1OQUEUE_DEF_IO(name, type, oplist, prioqueue_t, it_t) \ + M_EMPLACE_QUEUE_DEF(name, prioqueue_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE) + +/* Define the types */ +#define M_PR1OQUEUE_DEF_TYPE(name, type, oplist, prioqueue_t, it_t) \ + \ + /* Define the priority queue over the defined array */ \ + typedef struct M_F(name, _s) { \ + M_F(name, _array_t) array; \ + } prioqueue_t[1]; \ + /* Define the pointer references to the priority queue */ \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* The iterator is the same one as the one of the internal array */ \ + typedef M_F(name, _array_it_t) it_t; \ + \ + /* Definition of the internal types used by the oplist */ \ + typedef prioqueue_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + typedef it_t M_F(name, _it_ct); \ + +/* Define the core functions */ +#define M_PR1OQUEUE_DEF_CORE(name, type, oplist, prioqueue_t, it_t) \ + \ + M_INLINE void \ + M_F(name, _init)(prioqueue_t p) \ + { \ + M_F(name, _array_init)(p->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(prioqueue_t p, prioqueue_t const o) \ + { \ + M_F(name, _array_init_set)(p->array, o->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(prioqueue_t p, prioqueue_t const o) \ + { \ + M_F(name, _array_set)(p->array, o->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(prioqueue_t p) \ + { \ + M_F(name, _array_clear)(p->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(prioqueue_t p, prioqueue_t o) \ + { \ + M_F(name, _array_init_move)(p->array, o->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(prioqueue_t p, prioqueue_t o) \ + { \ + M_F(name, _array_move)(p->array, o->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(prioqueue_t p, prioqueue_t o) \ + { \ + M_F(name, _array_swap)(p->array, o->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(prioqueue_t p) \ + { \ + M_F(name, _array_reset)(p->array); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _i_parent)(size_t i) \ + { \ + M_ASSERT (i > 0); \ + return (i - 1) / 2; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _i_lchild)(size_t i) \ + { \ + M_ASSERT(i <= ((SIZE_MAX)-2)/2); \ + return 2*i + 1; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _i_rchild)(size_t i) \ + { \ + M_ASSERT(i <= ((SIZE_MAX)-2)/2); \ + return 2*i + 2; \ + } \ + \ + M_INLINE int \ + M_F(name, _i_cmp)(const prioqueue_t p, size_t i, size_t j) \ + { \ + return M_CALL_CMP(oplist, *M_F(name, _array_cget)(p->array, i), \ + *M_F(name, _array_cget)(p->array, j)); \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(prioqueue_t const p) \ + { \ + return M_F(name, _array_empty_p)(p->array); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(prioqueue_t const p) \ + { \ + return M_F(name, _array_size)(p->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _push)(prioqueue_t p, type const x) \ + { \ + /* Push back the new element at the end of the array */ \ + M_F(name, _array_push_back)(p->array, x); \ + \ + /* Reorder the array by swapping with its parent \ + * until it reaches the right position */ \ + size_t i = M_F(name, _array_size)(p->array)-1; \ + while (i > 0) { \ + size_t j = M_F(name, _i_parent)(i); \ + if (M_F(name, _i_cmp)(p, j, i) <= 0) \ + break; \ + M_F(name, _array_swap_at) (p->array, i, j); \ + i = j; \ + } \ + } \ + \ + M_INLINE type const * \ + M_F(name, _front)(prioqueue_t const p) \ + { \ + return M_F(name, _array_cget)(p->array, 0); \ + } \ + \ + M_INLINE void \ + M_F(name, _pop)(type *x, prioqueue_t p) \ + { \ + /* Swap the front element with the last element */ \ + size_t size = M_F(name, _array_size)(p->array)-1; \ + M_F(name, _array_swap_at) (p->array, 0, size); \ + /* Swap the new last element */ \ + M_F(name, _array_pop_back)(x, p->array); \ + \ + /* Reorder the heap */ \ + size_t i = 0; \ + while (true) { \ + size_t child = M_F(name, _i_lchild)(i); \ + if (child >= size) \ + break; \ + size_t otherChild = M_F(name, _i_rchild)(i); \ + if (otherChild < size \ + && M_F(name, _i_cmp)(p, otherChild, child) < 0 ) { \ + child = otherChild; \ + } \ + if (M_F(name, _i_cmp)(p, i, child) <= 0) \ + break; \ + M_F(name, _array_swap_at) (p->array, i, child); \ + i = child; \ + } \ + } \ + \ + M_IF_METHOD(EQUAL, oplist) \ + ( \ + /* EQUAL & CMP may be uncorrelated */ \ + M_INLINE bool \ + M_F(name, _equal_p)(prioqueue_t const p, prioqueue_t const q) \ + { \ + return M_F(name, _array_equal_p)(p->array, q->array); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _i_find)(prioqueue_t p, type const x) \ + { \ + size_t size = M_F(name, _array_size)(p->array); \ + size_t i = 0; \ + for(i = 0; i < size; i++) { \ + /* We cannot use CMP and the partial order to go faster \ + EQUAL & CMP may be uncorrelated */ \ + if (M_CALL_EQUAL(oplist, *M_F(name, _array_cget)(p->array, i), x)) \ + break; \ + } \ + return i; \ + } \ + \ + M_INLINE bool \ + M_F(name, _erase)(prioqueue_t p, type const x) \ + { \ + /* First pass: search for an item EQUAL to x */ \ + size_t size = M_F(name, _array_size)(p->array); \ + size_t i = M_F(name, _i_find)(p, x); \ + /* If x is not found, then stop */ \ + if (i >= size) \ + return false; \ + /* Swap the found item and the last element */ \ + size--; \ + M_F(name, _array_swap_at) (p->array, i, size); \ + M_F(name, _array_pop_back)(NULL, p->array); \ + /* Move back the last swapped element to its right position in the heap */ \ + while (true) { \ + size_t child = M_F(name, _i_lchild)(i); \ + if (child >= size) break; \ + size_t otherChild = M_F(name, _i_rchild)(i); \ + if (otherChild < size \ + && M_F(name, _i_cmp)(p, otherChild, child) < 0 ) { \ + child = otherChild; \ + } \ + if (M_F(name, _i_cmp)(p, i, child) <= 0) break; \ + M_F(name, _array_swap_at) (p->array, i, child); \ + i = child; \ + } \ + return true; \ + } \ + \ + M_INLINE void \ + M_F(name, _update)(prioqueue_t p, type const xold, type const xnew) \ + { \ + /* NOTE: xold can be the same pointer than xnew */ \ + /* First pass: search for an item EQUAL to x */ \ + size_t size = M_F(name, _array_size)(p->array); \ + size_t i = M_F(name, _i_find)(p, xold); \ + /* We shall have found the item */ \ + M_ASSERT (i < size); \ + /* Test if the position of the old data is further or nearer than the new */ \ + int cmp = M_CALL_CMP(oplist, *M_F(name, _array_cget)(p->array, i), xnew); \ + /* Set the found item to the new element */ \ + M_F(name, _array_set_at) (p->array, i, xnew); \ + if (cmp < 0) { \ + /* Move back the updated element to its new position, further in the heap */ \ + while (true) { \ + size_t child = M_F(name, _i_lchild)(i); \ + if (child >= size) break; \ + size_t otherChild = M_F(name, _i_rchild)(i); \ + if (otherChild < size \ + && M_F(name, _i_cmp)(p, otherChild, child) < 0 ) { \ + child = otherChild; \ + } \ + if (M_F(name, _i_cmp)(p, i, child) <= 0) break; \ + M_F(name, _array_swap_at) (p->array, i, child); \ + i = child; \ + } \ + } else { \ + /* Move back the updated element to its new position, nearest in the heap */ \ + while (i > 0) { \ + size_t parent = M_F(name, _i_parent)(i); \ + if (M_F(name, _i_cmp)(p, parent, i) <= 0) break; \ + M_F(name, _array_swap_at) (p->array, i, parent); \ + i = parent; \ + } \ + } \ + } \ + , /* No EQUAL */ ) \ + +/* Define the IT based functions */ +#define M_PR1OQUEUE_DEF_IT(name, type, oplist, prioqueue_t, it_t) \ + \ + /* Define iterators over the array iterator */ \ + M_INLINE void \ + M_F(name, _it)(it_t it, prioqueue_t const v) \ + { \ + M_F(name, _array_it)(it, v->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_last)(it_t it, prioqueue_t const v) \ + { \ + M_F(name, _array_it_last)(it, v->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(it_t it, prioqueue_t const v) \ + { \ + M_F(name, _array_it_end)(it, v->array); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t it, const it_t org) \ + { \ + M_F(name, _array_it_set)(it, org); \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(const it_t it) \ + { \ + return M_F(name, _array_end_p)(it); \ + } \ + \ + M_INLINE bool \ + M_F(name, _last_p)(const it_t it) \ + { \ + return M_F(name, _array_last_p)(it); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const it_t it1, \ + const it_t it2) \ + { \ + return M_F(name, _array_it_equal_p)(it1, it2); \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(it_t it) \ + { \ + M_F(name, _array_next)(it); \ + } \ + \ + M_INLINE void \ + M_F(name, _previous)(it_t it) \ + { \ + M_F(name, _array_previous)(it); \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(const it_t it) \ + { \ + return M_F(name, _array_cref)(it); \ + } \ + +/* Define the IO functions */ +#define M_PR1OQUEUE_DEF_IO(name, type, oplist, prioqueue_t, it_t) \ + M_IF_METHOD(OUT_STR, oplist)( \ + M_INLINE void \ + M_F(name, _out_str)(FILE *file, const prioqueue_t p) \ + { \ + M_F(name, _array_out_str)(file, p->array); \ + } \ + ,/* No OUT_STR */) \ + \ + M_IF_METHOD(IN_STR, oplist)( \ + M_INLINE bool \ + M_F(name, _in_str)(prioqueue_t p, FILE *file) \ + { \ + return M_F(name, _array_in_str)(p->array, file); \ + } \ + ,/* No IN_STR */) \ + \ + M_IF_METHOD(GET_STR, oplist)( \ + M_INLINE void \ + M_F(name, _get_str)(string_t str, const prioqueue_t p, bool append) \ + { \ + M_F(name, _array_get_str)(str, p->array, append); \ + } \ + ,/* No GET_STR */) \ + \ + M_IF_METHOD(PARSE_STR, oplist)( \ + M_INLINE bool \ + M_F(name, _parse_str)(prioqueue_t p, const char str[], const char **endp) \ + { \ + return M_F(name, _array_parse_str)(p->array, str, endp); \ + } \ + ,/* No PARSE_STR */) \ + \ + M_IF_METHOD(OUT_SERIAL, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, const prioqueue_t p) \ + { \ + return M_F(name, _array_out_serial)(f, p->array); \ + } \ + ,/* No OUT_SERIAL */) \ + \ + M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(prioqueue_t p, m_serial_read_t f) \ + { \ + return M_F(name, _array_in_serial)(p->array, f); \ + } \ + ,/* No in_SERIAL */) \ + + +// TODO: set all & remove all function + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define PRIOQUEUE_DEF M_PRIOQUEUE_DEF +#define PRIOQUEUE_DEF_AS M_PRIOQUEUE_DEF_AS +#define PRIOQUEUE_OPLIST M_PRIOQUEUE_OPLIST +#endif + +#endif diff --git a/components/mlib/m-rbtree.h b/components/mlib/m-rbtree.h new file mode 100644 index 00000000..f5880f20 --- /dev/null +++ b/components/mlib/m-rbtree.h @@ -0,0 +1,1186 @@ +/* + * M*LIB - RED BLACK TREE module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_RBTREE_H +#define MSTARLIB_RBTREE_H + +#include "m-core.h" + +/* Define a Red/Black binary tree of a given type. + USAGE: RBTREE_DEF(name, type [, oplist_of_the_type]) */ +#define M_RBTREE_DEF(name, ...) \ + M_RBTREE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) + + +/* Define a Red/Black binary tree of a given type + as the name name_t and the iterator it_t. + USAGE: RBTREE_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */ +#define M_RBTREE_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_RBTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, M_F(name, _node_ct), it_t ), \ + (name, __VA_ARGS__, name_t, M_F(name, _node_ct), it_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a rbtree of type. + USAGE: RBTREE_OPLIST(name [, oplist_of_the_type]) */ +#define M_RBTREE_OPLIST(...) \ + M_RBTR33_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST), \ + (__VA_ARGS__ ))) + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* Deferred evaluation for the oplist definition, + so that all arguments are evaluated before further expansion */ +#define M_RBTR33_OPLIST_P1(arg) M_RBTR33_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_RBTR33_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_RBTR33_OPLIST_P3, M_RBTR33_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_RBTR33_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_RBTREE_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* OPLIST definition of a rbtree + NOTE: IT_REF is not exported so that the container appears as not modifiable + by algorithm.*/ +#define M_RBTR33_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)), \ + INIT_SET(M_F(name, _init_set)), \ + INIT_WITH(API_1(M_INIT_EMPLACE_VAI)), \ + SET(M_F(name, _set)), \ + CLEAR(M_F(name, _clear)), \ + INIT_MOVE(M_F(name, _init_move)), \ + MOVE(M_F(name, _move)), \ + SWAP(M_F(name, _swap)), \ + NAME(name), \ + TYPE(M_F(name,_ct)), \ + SUBTYPE(M_F(name, _subtype_ct)), \ + EMPTY_P(M_F(name,_empty_p)), \ + GET_SIZE(M_F(name, _size)), \ + IT_TYPE(M_F(name, _it_ct)), \ + IT_FIRST(M_F(name,_it)), \ + IT_SET(M_F(name,_it_set)), \ + IT_LAST(M_F(name,_it_last)), \ + IT_END(M_F(name,_it_end)), \ + IT_END_P(M_F(name,_end_p)), \ + IT_LAST_P(M_F(name,_last_p)), \ + IT_EQUAL_P(M_F(name,_it_equal_p)), \ + IT_NEXT(M_F(name,_next)), \ + IT_PREVIOUS(M_F(name,_previous)), \ + IT_CREF(M_F(name,_cref)), \ + IT_REMOVE(M_F(name,_remove)), \ + RESET(M_F(name,_reset)), \ + PUSH(M_F(name,_push)), \ + GET_MIN(M_F(name,_min)), \ + GET_MAX(M_F(name,_max)), \ + M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),), \ + M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),), \ + M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),), \ + M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),), \ + M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),), \ + M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),), \ + M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \ + M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \ + ) + + +/********************************** INTERNAL *********************************/ + +/* Max depth of the binary tree + It is at worst twice the depth of a perfectly even tree with maximum elements. + The maximum number of elements is the max of size_t. + A perfectly even tree is of depth log2(max(size_t))=CHAR_BIT*sizeof(size_t) + */ +#define M_RBTR33_MAX_STACK (2*CHAR_BIT*sizeof (size_t)) + +/* Encapsulation of the color of the nodes. */ +#define M_RBTR33_SET_RED(x) ((x)->color = M_RBTR33_RED) +#define M_RBTR33_SET_BLACK(x) ((x)->color = M_RBTR33_BLACK) +#define M_RBTR33_IS_RED(x) ((x)->color == M_RBTR33_RED) +#define M_RBTR33_IS_BLACK(x) ((x)->color == M_RBTR33_BLACK) +#define M_RBTR33_COPY_COLOR(x,y) ((x)->color = (y)->color) +#define M_RBTR33_GET_COLOR(x) (true ? (x)->color : (x)->color) +#define M_RBTR33_SET_COLOR(x, c) ((x)->color = (c)) +#define M_RBTR33_GET_CHILD(x, n) ((x)->child[n]) +#define M_RBTR33_SET_CHILD(x, n, y) ((x)->child[n] = (y)) + +// Color of a node of a Red/Black tree +typedef enum { + M_RBTR33_BLACK = 0, M_RBTR33_RED +} m_rbtr33_color_e; + +// General contact of a Read/Black tree +#define M_RBTR33_CONTRACT(tree) do { \ + M_ASSERT ((tree) != NULL); \ + M_ASSERT ((tree)->node == NULL || M_RBTR33_IS_BLACK((tree)->node)); \ + M_ASSERT ((tree)->size != 0 || (tree)->node == NULL); \ + } while (0) + +// Contract of a node (doesn't check for equal depth in black) +#define M_RBTR33_CONTRACT_NODE(node) do { \ + M_ASSERT((node) != NULL); \ + M_ASSERT(M_RBTR33_IS_BLACK(node) || M_RBTR33_IS_RED(node)); \ + M_ASSERT(M_RBTR33_IS_BLACK(node) \ + || (((node)->child[0] == NULL || M_RBTR33_IS_BLACK(node->child[0])) \ + && ((node)->child[1] == NULL || M_RBTR33_IS_BLACK(node->child[1])))); \ + } while (0) + + +/* Deferred evaluation for the rbtree definition, + so that all arguments are evaluated before further expansion */ +#define M_RBTR33_DEF_P1(arg) M_ID( M_RBTR33_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_RBTR33_DEF_P2(name, type, oplist, tree_t, node_t, it_t) \ + M_IF_OPLIST(oplist)(M_RBTR33_DEF_P3, M_RBTR33_DEF_FAILURE)(name, type, oplist, tree_t, node_t, it_t) + +/* Stop processing with a compilation failure */ +#define M_RBTR33_DEF_FAILURE(name, type, oplist, tree_t, note_t, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(RBTREE_DEF): the given argument is not a valid oplist: " #oplist) + +/* Internal rbtree definition + - name: prefix to be used + - type: type of the elements of the rbtree + - oplist: oplist of the type of the elements of the container + - tree_t: alias for the type of the container + - it_t: alias for the iterator of the container + - node_t: alias for the node of an element of the container + */ +#define M_RBTR33_DEF_P3(name, type, oplist, tree_t, node_t, it_t) \ + M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \ + M_RBTR33_DEF_MEMPOOL(name, type, oplist, tree_t, node_t, it_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \ + M_RBTR33_DEF_IO(name, type, oplist, tree_t, node_t, it_t) \ + M_EMPLACE_QUEUE_DEF(name, tree_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE) + +/* Define the types associated to a R/B Tree */ +#define M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \ + \ + /* Node of Red/Black tree. \ + Each node has up to two child, a color (Red or black) \ + and the data stored in it */ \ + typedef struct M_F(name, _node_s) { \ + struct M_F(name, _node_s) *child[2]; \ + type data; \ + m_rbtr33_color_e color; \ + } node_t; \ + \ + /* Define the Red/Black tree */ \ + typedef struct M_F(name, _s) { \ + size_t size; /* Number of elements in the tree */ \ + node_t *node; /* Root node of the tree */ \ + } tree_t[1]; \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* Iterator on a tree. The iterator stores the full path to the \ + current node through all its parents and its depth */ \ + typedef struct M_F(name, _it_s) { \ + node_t *stack[M_RBTR33_MAX_STACK]; \ + int8_t which[M_RBTR33_MAX_STACK]; \ + unsigned int cpt; \ + } it_t[1]; \ + \ + /* Definition of the alias used by the oplists */ \ + typedef type M_F(name, _subtype_ct); \ + typedef tree_t M_F(name, _ct); \ + typedef it_t M_F(name, _it_ct); \ + +/* Define the mempool encapsulation */ +#define M_RBTR33_DEF_MEMPOOL(name, type, oplist, tree_t, node_t, it_t) \ + /* Link with fast memory allocator if requested */ \ + M_IF_METHOD(MEMPOOL, oplist)( \ + /* Definition of the memory pool of this kind of node */ \ + MEMPOOL_DEF(M_F(name, _mempool), node_t) \ + /* Definition of the global variable used to reference this pool */ \ + M_GET_MEMPOOL_LINKAGE oplist M_F(name, _mempool_t) M_GET_MEMPOOL oplist; \ + /* Allocator function */ \ + M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \ + return M_F(name, _mempool_alloc)(M_GET_MEMPOOL oplist); \ + } \ + /* Deallocator function */ \ + M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \ + M_F(name, _mempool_free)(M_GET_MEMPOOL oplist, ptr); \ + } \ + \ + , /* No mempool allocation */ \ + /* Classic Allocator function (common case) */ \ + M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \ + return M_CALL_NEW(oplist, node_t); \ + } \ + /* Classic deallocator function (common case) */ \ + M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \ + M_CALL_DEL(oplist, ptr); \ + } ) \ + +/* Define the core functions */ +#define M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \ + \ + M_INLINE void \ + M_F(name, _init)(tree_t tree) \ + { \ + M_ASSERT (tree != NULL); \ + tree->size = 0; \ + tree->node = NULL; \ + M_RBTR33_CONTRACT(tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(tree_t tree) \ + { \ + M_RBTR33_CONTRACT(tree); \ + node_t *stack[M_RBTR33_MAX_STACK]; \ + unsigned int cpt = 0; \ + /* If nothing (no node) nothing to clean: return */ \ + if (tree->node == NULL) return; \ + /* Parse all the tree */ \ + stack[cpt++] = tree->node; \ + while (cpt > 0) { \ + node_t *n = stack[cpt-1]; \ + /* Go down to the bottom left node that exists */ \ + while (true) { \ + M_RBTR33_CONTRACT_NODE(n); \ + /* If there is a left child, get it */ \ + if (n->child[0] != NULL) { \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + stack[cpt++] = n->child[0]; \ + n = n->child[0]; \ + stack[cpt-2]->child[0] = NULL; \ + /* If there is a right child, get it */ \ + } else if (n->child[1] != NULL) { \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + stack[cpt++] = n->child[1]; \ + n = n->child[1]; \ + stack[cpt-2]->child[1] = NULL; \ + /* No left nor right child, node can be deleted */ \ + } else { \ + break; \ + } \ + } \ + M_ASSERT (n == stack[cpt - 1]); \ + /* Clear the bottom left node */ \ + M_CALL_CLEAR(oplist, n->data); \ + M_C3(m_rbtr33_,name,_del) (n); \ + M_ASSERT((stack[cpt-1] = NULL) == NULL); \ + /* Go up to the parent */ \ + cpt--; \ + } \ + /* Mark the root node as empty */ \ + tree->node = NULL; \ + tree->size = 0; \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(tree_t tree) \ + { \ + /* Nothing more than clean the tree as everything is cleared */ \ + M_F(name, _reset)(tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _push)(tree_t tree, type const data) \ + { \ + M_RBTR33_CONTRACT(tree); \ + node_t *tab[M_RBTR33_MAX_STACK]; \ + int8_t which[M_RBTR33_MAX_STACK]; \ + unsigned int cpt = 0; \ + node_t *n = tree->node; \ + /* If there is no root node, create a new node */ \ + if (n == NULL) { \ + n = M_C3(m_rbtr33_,name,_new)(); \ + if (M_UNLIKELY_NOMEM (n == NULL)) { \ + M_MEMORY_FULL(sizeof (node_t)); \ + return; \ + } \ + /* Copy the data in the root node */ \ + M_CALL_INIT_SET(oplist, n->data, data); \ + /* Mark the root node as black */ \ + n->child[0] = n->child[1] = NULL; \ + M_RBTR33_SET_BLACK (n); \ + tree->node = n; \ + M_ASSERT(tree->size == 0); \ + tree->size = 1; \ + M_RBTR33_CONTRACT(tree); \ + return; \ + } \ + /* Search for insertion point in the tree */ \ + tab[cpt] = n; \ + while (n != NULL) { \ + M_RBTR33_CONTRACT_NODE(n); \ + int cmp = M_CALL_CMP(oplist, n->data, data); \ + if (cmp == 0) { \ + /* key found ==> stop analysis */ \ + break; \ + } else { \ + /* go left (if cmp > 0) or right (if cmp < 0) */ \ + int s = (cmp < 0); \ + which[cpt++] = (int8_t) s; \ + n = n->child[s]; \ + } \ + /* We cannot overflow the max depth of a tree */ \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + tab[cpt] = n; \ + } \ + /* If found, update the data (default is set) */ \ + if (n != NULL) { \ + M_CALL_SET(oplist, n->data, data); \ + M_RBTR33_CONTRACT (tree); \ + return; \ + } \ + /* Create new node to store the data */ \ + n = M_C3(m_rbtr33_,name,_new)(); \ + if (M_UNLIKELY_NOMEM (n == NULL) ) { \ + M_MEMORY_FULL (sizeof (node_t)); \ + return; \ + } \ + /* Copy the data and mark the node as red */ \ + M_CALL_INIT_SET(oplist, n->data, data); \ + n->child[0] = n->child[1] = NULL; \ + M_RBTR33_SET_RED (n); \ + /* Add it in the iterator */ \ + M_ASSERT (tab[cpt] == NULL); \ + tab[cpt] = n; \ + /* Add it in the tree */ \ + tree->size ++; \ + M_ASSERT(tab[cpt-1]->child[0+which[cpt-1]] == NULL); \ + tab[cpt-1]->child[0+which[cpt-1]] = n; \ + /* Fix the tree to still respect the red/back properties */ \ + while (cpt >= 2 \ + && M_RBTR33_IS_RED(tab[cpt-1]) \ + && tab[cpt-2]->child[1-which[cpt-2]] != NULL \ + && M_RBTR33_IS_RED(tab[cpt-2]->child[1-which[cpt-2]])) { \ + M_RBTR33_SET_BLACK(tab[cpt-1]); \ + M_RBTR33_SET_BLACK(tab[cpt-2]->child[1-which[cpt-2]]); \ + M_RBTR33_SET_RED(tab[cpt-2]); \ + cpt-=2; \ + } \ + /* root is always black */ \ + M_RBTR33_SET_BLACK(tab[0]); \ + if (cpt <= 1 || M_RBTR33_IS_BLACK(tab[cpt-1])) { \ + M_RBTR33_CONTRACT (tree); \ + return; \ + } \ + /* Read the grand-parent, the parent and the element */ \ + node_t *pp = tab[cpt-2]; \ + node_t *p = tab[cpt-1]; \ + node_t *x = tab[cpt]; \ + int i = which[cpt-2]; \ + int j = 1 - i; \ + M_ASSERT (i == 0 || i == 1); \ + /* We need to do some rotations */ \ + if (i == which[cpt-1]) { \ + /* The child is the left child of its parent */ \ + /* OR The child is the right child of its parent */ \ + /* Right rotation: cpt is the new grand-parent. \ + x is its left child, the grand-parent is the right one */ \ + pp->child[i] = p->child[j]; \ + p->child[i] = x; \ + p->child[j] = pp; \ + M_RBTR33_SET_BLACK(p); \ + M_RBTR33_SET_RED(pp); \ + } else { \ + M_ASSERT (j == which[cpt-1]); \ + /* The child is the right child of its parent */ \ + /* OR The child is the left child of its parent */ \ + /* Left rotation */ \ + pp->child[i] = x->child[j]; \ + p->child[j] = x->child[i]; \ + x->child[i] = p; \ + x->child[j] = pp; \ + M_RBTR33_SET_BLACK(x); \ + M_RBTR33_SET_RED(p); \ + M_RBTR33_SET_RED(pp); \ + p = x; \ + } \ + /* Insert the new grand parent */ \ + if (cpt == 2) { \ + tree->node = p; \ + } else { \ + M_ASSERT (cpt >= 3); \ + tab[cpt-3]->child[which[cpt-3]] = p; \ + } \ + /* Done */ \ + M_RBTR33_CONTRACT (tree); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(const tree_t tree) \ + { \ + M_RBTR33_CONTRACT (tree); \ + return tree->size; \ + } \ + \ + /* Set the iterator to the first (child=0) or last (child=1) element */ \ + M_INLINE void \ + M_C3(m_rbtr33_,name,_it)(it_t it, const tree_t tree, int child) \ + { \ + M_RBTR33_CONTRACT (tree); \ + M_ASSERT (it != NULL); \ + M_ASSERT (child == 0 || child == 1); \ + unsigned int cpt = 0; \ + if (tree->node != NULL) { \ + it->which[cpt] = (int8_t) child; \ + node_t *n = it->stack[cpt++] = tree->node; \ + /* Go down the tree and fill in the iterator */ \ + while (n->child[child] != NULL) { \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + n = n->child[child]; \ + it->which[cpt] = (int8_t) child; \ + it->stack[cpt++] = n; \ + } \ + M_ASSERT (n == it->stack[cpt - 1]); \ + } \ + it->cpt = cpt; \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(it_t it, const tree_t tree) \ + { \ + M_C3(m_rbtr33_,name,_it)(it, tree, 0); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_last)(it_t it, const tree_t tree) \ + { \ + M_C3(m_rbtr33_,name,_it)(it, tree, 1); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_end)(it_t it, const tree_t tree) \ + { \ + M_RBTR33_CONTRACT (tree); \ + M_ASSERT (it != NULL); \ + (void) tree; /* parameter not used */ \ + it->cpt = 0; \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t it, const it_t ref) \ + { \ + M_ASSERT (it != NULL && ref != NULL); \ + *it = *ref; \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(const it_t it) \ + { \ + M_ASSERT (it != NULL); \ + return it->cpt == 0; \ + } \ + \ + /* Go to the next (child = 0)or previous element (child = 1) */ \ + M_INLINE void \ + M_C3(m_rbtr33_,name,_next)(it_t it, int child) \ + { \ + M_ASSERT (it != NULL); \ + M_ASSERT (child == 0 || child == 1); \ + if (it->cpt == 0) return; \ + unsigned int cpt = it->cpt - 1; \ + node_t *n = it->stack[cpt]; \ + /* Get the other child */ \ + const int right = 1 ^ child; \ + if (n->child[right] != NULL) { \ + /* Going right */ \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + n = n->child[right]; \ + it->which[cpt++] = (int8_t) right; \ + it->stack[cpt] = n; \ + it->which[cpt++] = (int8_t) child; \ + /* Going left */ \ + while (n->child[child] != NULL) { \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + n = n->child[child]; \ + it->which[cpt] = (int8_t) child; \ + it->stack[cpt++] = n; \ + } \ + M_ASSERT (n == it->stack[cpt - 1]); \ + } else { \ + /* Going up */ \ + while (cpt > 0 && it->which[cpt-1] == right) cpt--; \ + } \ + it->cpt = cpt; \ + } \ + \ + M_INLINE void \ + M_F(name, _next)(it_t it) \ + { \ + M_C3(m_rbtr33_,name,_next)(it, 0); \ + } \ + \ + M_INLINE void \ + M_F(name, _previous)(it_t it) \ + { \ + M_C3(m_rbtr33_,name,_next)(it, 1); \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(const it_t it) \ + { \ + M_ASSERT(it != NULL); \ + /* There shall be at least one element */ \ + M_ASSERT_INDEX(it->cpt-1, M_RBTR33_MAX_STACK); \ + /* NOTE: partially unsafe if the user modify the order of the el */ \ + return &(it->stack[it->cpt-1]->data); \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(const it_t it) \ + { \ + return M_CONST_CAST(type, M_F(name, _ref)(it)); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(const it_t it1, const it_t it2) \ + { \ + M_ASSERT(it1 != NULL && it2 != NULL); \ + /* There can be no element */ \ + M_ASSERT_INDEX(it1->cpt, M_RBTR33_MAX_STACK); \ + M_ASSERT_INDEX(it2->cpt, M_RBTR33_MAX_STACK); \ + return it1->cpt == it2->cpt \ + && (it1->cpt == 0 || it1->stack[it1->cpt-1] == it2->stack[it2->cpt-1]); \ + } \ + \ + M_INLINE void \ + M_F(name, _it_from)(it_t it, const tree_t tree, type const data) \ + { \ + M_RBTR33_CONTRACT (tree); \ + M_ASSERT (it != NULL); \ + unsigned int cpt = 0; \ + int cmp = 1; \ + node_t *n = tree->node; \ + /* Find the lowest element greater or equal than data in the tree */ \ + while (n != NULL) { \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + it->which[cpt] = 0; \ + it->stack[cpt++] = n; \ + cmp = M_CALL_CMP(oplist, n->data, data); \ + if (cmp == 0) \ + break; \ + int child = (cmp < 0); \ + it->which[cpt-1] = (int8_t) child; \ + n = n->child[child]; \ + } \ + /* Save the iterator */ \ + it->cpt = cpt; \ + /* The iterator found may be strictly lower than data. \ + In this case, go to the next element */ \ + if (cmp < 0) { \ + M_F(name, _next)(it); \ + } \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_until_p)(it_t it, type const data) \ + { \ + M_ASSERT (it != NULL); \ + if (M_UNLIKELY(it->cpt == 0)) return true; \ + M_ASSERT (it->cpt > 0 && it->cpt < M_RBTR33_MAX_STACK); \ + node_t *n = it->stack[it->cpt-1]; \ + int cmp = M_CALL_CMP(oplist, n->data, data); \ + return (cmp >= 0); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_while_p)(it_t it, type const data) \ + { \ + M_ASSERT (it != NULL); \ + if (M_UNLIKELY(it->cpt == 0)) return false; \ + M_ASSERT (it->cpt > 0 && it->cpt < M_RBTR33_MAX_STACK); \ + node_t *n = it->stack[it->cpt-1]; \ + int cmp = M_CALL_CMP(oplist, n->data, data); \ + return (cmp <= 0); \ + } \ + \ + M_INLINE type * \ + M_F(name, _min)(const tree_t tree) \ + { \ + M_RBTR33_CONTRACT (tree); \ + node_t *n = tree->node; \ + if (M_UNLIKELY (n == NULL) ) return NULL; \ + while (n->child[0] != NULL) { \ + M_RBTR33_CONTRACT_NODE (n); \ + n = n->child[0]; \ + } \ + return &n->data; \ + } \ + \ + M_INLINE type * \ + M_F(name, _max)(const tree_t tree) \ + { \ + M_RBTR33_CONTRACT (tree); \ + node_t *n = tree->node; \ + if (M_UNLIKELY (n == NULL) ) return NULL; \ + while (n->child[1] != NULL) { \ + M_RBTR33_CONTRACT_NODE (n); \ + n = n->child[1]; \ + } \ + return &n->data; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cmin)(const tree_t tree) \ + { \ + return M_CONST_CAST(type, M_F(name, _min)(tree)); \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cmax)(const tree_t tree) \ + { \ + return M_CONST_CAST(type, M_F(name, _max)(tree)); \ + } \ + \ + M_INLINE type * \ + M_F(name, _get)(const tree_t tree, type const data) \ + { \ + M_RBTR33_CONTRACT (tree); \ + node_t *n = tree->node; \ + /* Go down the tree */ \ + while (n != NULL) { \ + M_RBTR33_CONTRACT_NODE (n); \ + int cmp = M_CALL_CMP(oplist, n->data, data); \ + if (cmp == 0) { \ + return &n->data; \ + } else { \ + /* Go left (if cmp > 0) or right (if cmp < 0) */ \ + n = n->child[cmp < 0]; \ + } \ + } \ + return NULL; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cget)(const tree_t tree, type const data) \ + { \ + return M_CONST_CAST(type, M_F(name, _get)(tree, data)); \ + } \ + \ + /* Create a copy of the given node (recursively) */ \ + M_INLINE node_t * \ + M_C3(m_rbtr33_,name,_copy_node)(const node_t *o) \ + { \ + if (o == NULL) return NULL; \ + node_t *n = M_C3(m_rbtr33_,name,_new)(); \ + if (M_UNLIKELY_NOMEM (n == NULL) ) { \ + M_MEMORY_FULL (sizeof (node_t)); \ + return NULL; \ + } \ + M_CALL_INIT_SET(oplist, n->data, o->data); \ + n->child[0] = M_C3(m_rbtr33_,name,_copy_node)(o->child[0]); \ + n->child[1] = M_C3(m_rbtr33_,name,_copy_node)(o->child[1]); \ + M_RBTR33_COPY_COLOR (n, o); \ + return n; \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(tree_t tree, const tree_t ref) \ + { \ + M_RBTR33_CONTRACT (ref); \ + M_ASSERT (tree != NULL && tree != ref); \ + tree->size = ref->size; \ + /* Copy the root node recursively */ \ + tree->node = M_C3(m_rbtr33_,name,_copy_node)(ref->node); \ + M_RBTR33_CONTRACT (tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(tree_t tree, const tree_t ref) \ + { \ + M_RBTR33_CONTRACT (tree); \ + M_RBTR33_CONTRACT (ref); \ + if (tree == ref) return; \ + M_F(name,_clear)(tree); \ + M_F(name,_init_set)(tree, ref); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(tree_t tree, tree_t ref) \ + { \ + M_RBTR33_CONTRACT (ref); \ + M_ASSERT (tree != NULL && tree != ref); \ + tree->size = ref->size; \ + tree->node = ref->node; \ + ref->node = NULL; \ + ref->size = 0; \ + M_RBTR33_CONTRACT (tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(tree_t tree, tree_t ref) \ + { \ + M_RBTR33_CONTRACT (tree); \ + M_RBTR33_CONTRACT (ref); \ + M_ASSERT (tree != ref); \ + M_F(name,_clear)(tree); \ + M_F(name,_init_move)(tree, ref); \ + M_RBTR33_CONTRACT (tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(tree_t tree1, tree_t tree2) \ + { \ + M_RBTR33_CONTRACT (tree1); \ + M_RBTR33_CONTRACT (tree2); \ + M_SWAP(size_t, tree1->size, tree2->size); \ + M_SWAP(node_t *, tree1->node, tree2->node); \ + M_RBTR33_CONTRACT (tree1); \ + M_RBTR33_CONTRACT (tree2); \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(const tree_t tree) \ + { \ + M_RBTR33_CONTRACT (tree); \ + return tree->size == 0; \ + } \ + \ + /* Take care of the case n == NULL too */ \ + M_INLINE bool \ + M_C3(m_rbtr33_,name,_black_p)(const node_t *n) \ + { \ + return (n == NULL) ? true : M_RBTR33_IS_BLACK(n); \ + } \ + \ + M_INLINE void \ + M_C3(m_rbtr33_,name,_set_black)(node_t *n) \ + { \ + if (n != NULL) M_RBTR33_SET_BLACK(n); \ + } \ + \ + M_INLINE node_t * \ + M_C3(m_rbtr33_,name,_rotate)(node_t *pp, node_t *ppp, const bool right) \ + { \ + M_ASSERT (pp != NULL && ppp != NULL); \ + bool left = !right; \ + node_t *p = pp->child[right]; \ + M_ASSERT (p != NULL); \ + pp->child[right] = p->child[left]; \ + p->child[left] = pp; \ + /* Fix grandparent with new parent */ \ + M_ASSERT(ppp->child[0] == pp || ppp->child[1] == pp); \ + ppp->child[(ppp->child[0] != pp)] = p; \ + return p; \ + } \ + \ + M_IF_DEBUG( \ + /* Compute the depth of a node */ \ + M_INLINE size_t \ + M_C3(m_rbtr33_,name,_compute_depth)(const node_t *n) \ + { \ + if (n == NULL) return 1; \ + return M_RBTR33_IS_BLACK (n) \ + + M_C3(m_rbtr33_,name,_compute_depth)(n->child[0]); \ + } \ + ) \ + \ + M_INLINE bool \ + M_F(name, _pop_at)(type *data_ptr, tree_t tree, type const key) \ + { \ + M_RBTR33_CONTRACT (tree); \ + node_t *tab[M_RBTR33_MAX_STACK]; \ + int8_t which[M_RBTR33_MAX_STACK]; \ + unsigned int cpt = 0; \ + node_t root_dummy; \ + node_t *n = tree->node; \ + which[0] = 0; \ + root_dummy.child[0] = n; \ + tab[cpt++] = &root_dummy; \ + /* Search for the deletion point */ \ + tab[cpt] = n; \ + while (n != NULL) { \ + M_RBTR33_CONTRACT_NODE (n); \ + M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(n->child[0]) \ + == M_C3(m_rbtr33_,name,_compute_depth)(n->child[1])); \ + int cmp = M_CALL_CMP(oplist, n->data, key); \ + if (cmp == 0) { \ + break; \ + } \ + int i = (cmp < 0); \ + which[cpt++] = (int8_t) i; \ + n = n->child[i]; \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + tab[cpt] = n; \ + } \ + M_ASSERT (tab[cpt] == n); \ + /* If not found, fail */ \ + if (n == NULL) { \ + return false; \ + } \ + unsigned int cpt_n = cpt; \ + node_t *v = n; /* the replacement node */ \ + node_t *u; /* the deleted node */ \ + m_rbtr33_color_e v_color = M_RBTR33_GET_COLOR(v); \ + /* Classical removal of a node from a binary tree */ \ + if (v->child[0] != NULL && v->child[1] != NULL) { \ + /* node has 2 child. */ \ + /* Get the element right next to the deleted one */ \ + v = v->child[1]; \ + which[cpt++] = 1; \ + tab[cpt] = v; \ + while (v != NULL) { \ + /* Always left node */ \ + M_RBTR33_CONTRACT_NODE (v); \ + M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(v->child[0]) \ + == M_C3(m_rbtr33_,name,_compute_depth)(v->child[1])); \ + which[cpt++] = 0; \ + v = v->child[0]; \ + M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ + tab[cpt] = v; \ + } \ + /* Pop the last element to get the last non-null element */ \ + v = tab[--cpt]; \ + M_ASSERT (v != NULL); \ + u = v->child[1]; \ + /* Replace 'v' by 'u' in the tree */ \ + M_ASSERT(cpt >= 1 && tab[cpt-1] != NULL && tab[cpt-1]->child[which[cpt-1]] == v); \ + tab[cpt-1]->child[which[cpt-1]] = u; \ + /* Replace 'n' by 'v' in the tree */ \ + M_ASSERT(cpt_n >= 1 && tab[cpt_n-1] != NULL); \ + M_ASSERT(tab[cpt_n-1]->child[which[cpt_n-1]] == n); \ + tab[cpt_n-1]->child[which[cpt_n-1]] = v; \ + v->child[0] = n->child[0]; \ + v->child[1] = n->child[1]; \ + v_color = M_RBTR33_GET_COLOR(v); \ + M_RBTR33_COPY_COLOR(v, n); \ + tab[cpt_n] = v; \ + /* For the algorithm, 'u' is now the deleted node */ \ + } else { \ + /* 1 or no child to the node. Replace the element */ \ + v = n; \ + u = v->child[(n->child[0] == NULL)]; \ + M_ASSERT (cpt_n >= 1 &&tab[cpt_n-1] != NULL && tab[cpt_n-1]->child[which[cpt_n-1]] == n); \ + M_ASSERT (n->child[(n->child[0] != NULL)] == NULL); \ + tab[cpt_n-1]->child[which[cpt_n-1]] = u; \ + /* in all cases, this node shall be set to black */ \ + } \ + \ + /* Rebalance from child to root */ \ + if (v_color == M_RBTR33_BLACK \ + && M_C3(m_rbtr33_,name,_black_p)(u)) { \ + /* tab[0] is NULL, tab[1] is root, u is double black */ \ + node_t *p = u, *s; \ + while (cpt >= 2) { \ + p = tab[--cpt]; \ + bool nbChild = which[cpt]; \ + M_ASSERT (p != NULL && u == p->child[nbChild]); \ + s = p->child[!nbChild]; \ + /* if sibling is red, perform a rotation to move sibling up */ \ + if (!M_C3(m_rbtr33_,name,_black_p)(s)) { \ + p = M_C3(m_rbtr33_,name,_rotate) (p, tab[cpt-1], !nbChild); \ + M_RBTR33_SET_BLACK(p); /* was sibling */ \ + tab[cpt] = p; \ + which[cpt++] = nbChild; \ + p = p->child[nbChild]; /* was parent */ \ + M_ASSERT (p != NULL); \ + M_RBTR33_SET_RED(p); \ + s = p->child[!nbChild]; \ + M_ASSERT (M_C3(m_rbtr33_,name,_black_p)(s)); \ + } \ + M_ASSERT (p != NULL && u == p->child[nbChild]); \ + /* if both childreen of s are black */ \ + /* perform recoloring and recur on parent if black */ \ + if (s != NULL \ + && M_C3(m_rbtr33_,name,_black_p)(s->child[0]) \ + && M_C3(m_rbtr33_,name,_black_p)(s->child[1])) { \ + M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(s->child[0]) == M_C3(m_rbtr33_,name,_compute_depth)(s->child[1])); \ + M_RBTR33_SET_RED(s); \ + if (M_RBTR33_IS_RED(p)) { \ + M_RBTR33_SET_BLACK(p); \ + M_RBTR33_CONTRACT_NODE(p); \ + M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(p->child[0]) == M_C3(m_rbtr33_,name,_compute_depth)(p->child[1])); \ + break; \ + } \ + u = p; \ + } else { \ + M_ASSERT (s != NULL); \ + /* at least one child of 's' is red */ \ + /* perform rotation(s) */ \ + bool childIsRight = !M_C3(m_rbtr33_,name,_black_p)(s->child[1]); \ + m_rbtr33_color_e p_color = M_RBTR33_GET_COLOR (p); \ + if (childIsRight != nbChild) { \ + /* left-left or right-right case */ \ + p = M_C3(m_rbtr33_,name,_rotate) (p, tab[cpt-1], childIsRight); \ + } else { \ + s = M_C3(m_rbtr33_,name,_rotate) (s, p, childIsRight); \ + p = M_C3(m_rbtr33_,name,_rotate) (p, tab[cpt-1], !nbChild); \ + } \ + M_RBTR33_SET_COLOR(p, p_color); \ + M_ASSERT(p->child[0] != NULL && p->child[1] != NULL); \ + M_RBTR33_SET_BLACK(p->child[0]); \ + M_RBTR33_SET_BLACK(p->child[1]); \ + M_RBTR33_CONTRACT_NODE(p); \ + M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(p->child[0]) == M_C3(m_rbtr33_,name,_compute_depth)(p->child[1])); \ + break; \ + } \ + } /* while */ \ + if (cpt == 1 /* root has been reached? */ ) { \ + M_C3(m_rbtr33_,name,_set_black)(p); \ + M_ASSERT (root_dummy.child[0] == p); \ + } \ + } else { \ + M_C3(m_rbtr33_,name,_set_black)(u); \ + } \ + tree->node = root_dummy.child[0]; \ + M_ASSERT (tree->node == NULL || M_RBTR33_IS_BLACK(tree->node)); \ + /* delete it */ \ + if (data_ptr != NULL) \ + M_DO_MOVE(oplist, *data_ptr, n->data); \ + else \ + M_CALL_CLEAR(oplist, n->data); \ + M_C3(m_rbtr33_,name,_del) (n); \ + tree->size --; \ + M_RBTR33_CONTRACT (tree); \ + return true; \ + } \ + \ + M_INLINE void M_F(name,_remove)(tree_t t, it_t it) \ + { \ + /* Not optimum: another search in the tree is performed */ \ + type data; \ + M_CALL_INIT_SET(oplist, data, *M_F(name,_cref)(it)); \ + M_F(name,_next)(it); \ + M_F(name, _pop_at)(NULL, t, data); \ + /* We have changed the tree: the iterator is partialy invalid */ \ + if (!M_F(name, _end_p)(it)) { \ + M_F(name, _it_from)(it, t, *M_F(name,_cref)(it)); \ + } \ + M_CALL_CLEAR(oplist, data); \ + } \ + \ + M_IF_METHOD(EQUAL, oplist)( \ + M_INLINE bool M_F(name,_equal_p)(const tree_t t1, const tree_t t2) { \ + M_RBTR33_CONTRACT(t1); \ + M_RBTR33_CONTRACT(t2); \ + if (t1->size != t2->size) return false; \ + it_t it1; \ + it_t it2; \ + /* NOTE: We can't compare two tree directly as they can be \ + structuraly different but functionnaly equal (you get this by \ + constructing the tree in a different way). We have to \ + compare the ordered value within the tree. */ \ + M_F(name, _it)(it1, t1); \ + M_F(name, _it)(it2, t2); \ + while (!M_F(name, _end_p)(it1) \ + && !M_F(name, _end_p)(it2)) { \ + type const *ref1 = M_F(name, _cref)(it1); \ + type const *ref2 = M_F(name, _cref)(it2); \ + if (M_CALL_EQUAL(oplist, *ref1, *ref2) == false) \ + return false; \ + M_F(name, _next)(it1); \ + M_F(name, _next)(it2); \ + } \ + return M_F(name, _end_p)(it1) \ + && M_F(name, _end_p)(it2); \ + } \ + , /* NO EQUAL METHOD */ ) \ + \ + M_IF_METHOD(HASH, oplist)( \ + M_INLINE size_t M_F(name,_hash)(const tree_t t1) { \ + M_RBTR33_CONTRACT(t1); \ + M_HASH_DECL(hash); \ + /* NOTE: We can't compute the hash directly for the same reason \ + than for EQUAL operator. */ \ + it_t it1; \ + M_F(name, _it)(it1, t1); \ + while (!M_F(name, _end_p)(it1)) { \ + type const *ref1 = M_F(name, _cref)(it1); \ + M_HASH_UP(hash, M_CALL_HASH(oplist, *ref1)); \ + M_F(name, _next)(it1); \ + } \ + return M_HASH_FINAL (hash); \ + } \ + , /* NO HASH METHOD */ ) \ + +/* Define the I/O functions */ +#define M_RBTR33_DEF_IO(name, type, oplist, tree_t, node_t, it_t) \ + M_IF_METHOD(GET_STR, oplist)( \ + M_INLINE void M_F(name, _get_str)(m_string_t str, \ + tree_t const t1, bool append) { \ + M_RBTR33_CONTRACT(t1); \ + M_ASSERT(str != NULL); \ + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \ + /* NOTE: The print is really naive, and not really efficient */ \ + bool commaToPrint = false; \ + it_t it1; \ + M_F(name, _it)(it1, t1); \ + while (!M_F(name, _end_p)(it1)) { \ + if (commaToPrint) \ + m_string_push_back (str, M_GET_SEPARATOR oplist); \ + commaToPrint = true; \ + type const *ref1 = M_F(name, _cref)(it1); \ + M_CALL_GET_STR(oplist, str, *ref1, true); \ + M_F(name, _next)(it1); \ + } \ + m_string_push_back (str, ']'); \ + } \ + , /* NO GET_STR */ ) \ + \ + M_IF_METHOD(OUT_STR, oplist)( \ + M_INLINE void \ + M_F(name, _out_str)(FILE *file, tree_t const rbtree) \ + { \ + M_RBTR33_CONTRACT(rbtree); \ + M_ASSERT (file != NULL); \ + fputc ('[', file); \ + it_t it; \ + bool commaToPrint = false; \ + for (M_F(name, _it)(it, rbtree) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + if (commaToPrint) \ + fputc (M_GET_SEPARATOR oplist, file); \ + commaToPrint = true; \ + type const *item = M_F(name, _cref)(it); \ + M_CALL_OUT_STR(oplist, file, *item); \ + } \ + fputc (']', file); \ + } \ + , /* no out_str */ ) \ + \ + M_IF_METHOD(PARSE_STR, oplist)( \ + M_INLINE bool \ + M_F(name, _parse_str)(tree_t rbtree, const char str[], const char **endp) \ + { \ + M_RBTR33_CONTRACT(rbtree); \ + M_ASSERT (str != NULL); \ + M_F(name,_reset)(rbtree); \ + bool success = false; \ + int c = *str++; \ + if (M_UNLIKELY (c != '[')) goto exit; \ + c = *str++; \ + if (M_UNLIKELY (c == ']')) { success = true; goto exit; } \ + if (M_UNLIKELY (c == 0)) goto exit; \ + str--; \ + type item; \ + M_CALL_INIT(oplist, item); \ + do { \ + bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \ + do { c = *str++; } while (isspace(c)); \ + if (b == false || c == 0) goto exit_clear; \ + M_F(name, _push)(rbtree, item); \ + } while (c == M_GET_SEPARATOR oplist); \ + success = (c == ']'); \ + exit_clear: \ + M_CALL_CLEAR(oplist, item); \ + exit: \ + if (endp) *endp = str; \ + return success; \ + } \ + , /* no parse_str */ ) \ + \ + M_IF_METHOD(IN_STR, oplist)( \ + M_INLINE bool \ + M_F(name, _in_str)(tree_t rbtree, FILE *file) \ + { \ + M_RBTR33_CONTRACT(rbtree); \ + M_ASSERT (file != NULL); \ + M_F(name,_reset)(rbtree); \ + int c = fgetc(file); \ + if (M_UNLIKELY (c != '[')) return false; \ + c = fgetc(file); \ + if (M_UNLIKELY (c == ']')) return true; \ + if (M_UNLIKELY (c == EOF)) return false; \ + ungetc(c, file); \ + type item; \ + M_CALL_INIT(oplist, item); \ + do { \ + bool b = M_CALL_IN_STR(oplist, item, file); \ + do { c = fgetc(file); } while (isspace(c)); \ + if (b == false || c == EOF) break; \ + M_F(name, _push)(rbtree, item); \ + } while (c == M_GET_SEPARATOR oplist); \ + M_CALL_CLEAR(oplist, item); \ + return c == ']'; \ + } \ + , /* no in_str */ ) \ + \ + M_IF_METHOD(OUT_SERIAL, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, tree_t const t1) \ + { \ + M_RBTR33_CONTRACT(t1); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + M_F(name, _subtype_ct) const *item; \ + bool first_done = false; \ + it_t it; \ + ret = f->m_interface->write_array_start(local, f, t1->size); \ + for (M_F(name, _it)(it, t1) ; \ + !M_F(name, _end_p)(it); \ + M_F(name, _next)(it)){ \ + item = M_F(name, _cref)(it); \ + if (first_done) \ + ret |= f->m_interface->write_array_next(local, f); \ + ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \ + first_done = true; \ + } \ + ret |= f->m_interface->write_array_end(local, f); \ + return ret & M_SERIAL_FAIL; \ + } \ + , /* no OUT_SERIAL */ ) \ + \ + M_IF_METHOD(IN_SERIAL, oplist)( \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(tree_t t1, m_serial_read_t f) \ + { \ + M_RBTR33_CONTRACT(t1); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + size_t estimated_size = 0; \ + type key; \ + M_F(name,_reset)(t1); \ + ret = f->m_interface->read_array_start(local, f, &estimated_size); \ + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ + M_CALL_INIT(oplist, key); \ + do { \ + ret = M_CALL_IN_SERIAL(oplist, key, f); \ + if (ret != M_SERIAL_OK_DONE) { break; } \ + M_F(name, _push)(t1, key); \ + } while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \ + M_CALL_CLEAR(oplist, key); \ + return ret; \ + } \ + , /* no in_serial */ ) \ + \ + + +/********************************** INTERNAL *********************************/ + +// TODO: specialized _sort shall do nothing, but shall check the requested order. How ? +#if M_USE_SMALL_NAME +#define RBTREE_DEF M_RBTREE_DEF +#define RBTREE_DEF_AS M_RBTREE_DEF_AS +#define RBTREE_OPLIST M_RBTREE_OPLIST +#endif + +#endif diff --git a/components/mlib/m-serial-bin.h b/components/mlib/m-serial-bin.h new file mode 100644 index 00000000..1bf3cea1 --- /dev/null +++ b/components/mlib/m-serial-bin.h @@ -0,0 +1,552 @@ +/* + * M*LIB - Serial BIN + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_SERIAL_BIN_H +#define MSTARLIB_SERIAL_BIN_H + +#include + +#include "m-core.h" +#include "m-string.h" + +M_BEGIN_PROTECTED_CODE + + +/********************************************************************************/ +/************************** FILE / WRITE / BIN *******************************/ +/********************************************************************************/ + +/* Internal service: + * Write size_t in the stream in a compact form to reduce consumption + * (and I/O bandwidth) + */ +M_INLINE bool +m_ser1al_bin_write_size(FILE *f, const size_t size) +{ + bool b; + if (M_LIKELY(size < 253)) + { + b = EOF != fputc((unsigned char) size, f); + } else if (size < 1ULL << 16) { + b = EOF != fputc(253, f); // Save 16 bits encoding + b &= EOF != fputc((unsigned char) (size >> 8), f); + b &= EOF != fputc((unsigned char) size, f); + } +// For 32 bits systems, don't encode a 64 bits size_t + #if SIZE_MAX < 1ULL<< 32 + else { + b = EOF != fputc(254, f); // Save 32 bits encoding + b &= EOF != fputc((unsigned char) (size >> 24), f); + b &= EOF != fputc((unsigned char) (size >> 16), f); + b &= EOF != fputc((unsigned char) (size >> 8), f); + b &= EOF != fputc((unsigned char) size, f); + } + #else + else if (size < 1ULL<< 32) { + b = EOF != fputc(254, f); // Save 32 bits encoding + b &= EOF != fputc((unsigned char) (size >> 24), f); + b &= EOF != fputc((unsigned char) (size >> 16), f); + b &= EOF != fputc((unsigned char) (size >> 8), f); + b &= EOF != fputc((unsigned char) size, f); + } else { + b = EOF != fputc(255, f); // Save 64 bits encoding + b &= EOF != fputc((unsigned char) (size >> 56), f); + b &= EOF != fputc((unsigned char) (size >> 48), f); + b &= EOF != fputc((unsigned char) (size >> 40), f); + b &= EOF != fputc((unsigned char) (size >> 32), f); + b &= EOF != fputc((unsigned char) (size >> 24), f); + b &= EOF != fputc((unsigned char) (size >> 16), f); + b &= EOF != fputc((unsigned char) (size >> 8), f); + b &= EOF != fputc((unsigned char) size, f); + } +#endif + return b; +} + +/* Internal service: + * Read size_t in the stream from a compact form to reduce consumption + * (and I/O bandwidth) + */ +M_INLINE bool +m_ser1al_bin_read_size(FILE *f, size_t *size) +{ + int c; + c = fgetc(f); + if (M_UNLIKELY(c == EOF)) return false; + if (M_LIKELY(c < 253)) { + *size = (size_t) c; + return true; + } + size_t s = 0; + int l = (c == 255) ? 8 : (c == 254) ? 4 : 2; + for(int i = 0; i < l; i++) { + c = fgetc(f); + if (M_UNLIKELY(c == EOF)) return false; + s = (s << 8) | (size_t) c; + } + *size = s; + return true; +} + +/* Write the boolean 'data' into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_boolean(m_serial_write_t serial, const bool data) +{ + FILE *f = (FILE *)serial->data[0].p; + size_t n = fwrite (M_ASSIGN_CAST(const void*, &data), sizeof (bool), 1, f); + return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type) +{ + size_t n; + + FILE *f = (FILE *)serial->data[0].p; + if (size_of_type == 1) { + int8_t i8 = (int8_t) data; + n = fwrite (M_ASSIGN_CAST(const void*, &i8), sizeof i8, 1, f); + } else if (size_of_type == 2) { + int16_t i16 = (int16_t) data; + n = fwrite (M_ASSIGN_CAST(const void*, &i16), sizeof i16, 1, f); + } else if (size_of_type == 4) { + int32_t i32 = (int32_t) data; + n = fwrite (M_ASSIGN_CAST(const void*, &i32), sizeof i32, 1, f); + } else { + M_ASSERT(size_of_type == 8); + int64_t i64 = (int64_t) data; + n = fwrite (M_ASSIGN_CAST(const void*, &i64), sizeof i64, 1, f); + } + return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type) +{ + size_t n; + + FILE *f = (FILE *)serial->data[0].p; + if (size_of_type == sizeof (float) ) { + float f1 = (float) data; + n = fwrite (M_ASSIGN_CAST(const void*, &f1), sizeof f1, 1, f); + } else if (size_of_type == sizeof (double) ) { + double f2 = (double) data; + n = fwrite (M_ASSIGN_CAST(const void*, &f2), sizeof f2, 1, f); + } else { + M_ASSERT(size_of_type == sizeof (long double) ); + long double f3 = (long double) data; + n = fwrite (M_ASSIGN_CAST(const void*, &f3), sizeof f3, 1, f); + } + return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the null-terminated string 'data'into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_string(m_serial_write_t serial, const char data[], size_t length) +{ + M_ASSERT_SLOW(length == strlen(data) ); + FILE *f = (FILE *)serial->data[0].p; + M_ASSERT(f != NULL && data != NULL); + // Write first the number of (non null) characters + if (m_ser1al_bin_write_size(f, length) != true) return m_core_serial_fail(); + // Write the characters (excluding the final null char) + // NOTE: fwrite supports length == 0. + size_t n = fwrite (M_ASSIGN_CAST(const void*, data), 1, length, f); + return (n == length) ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start writing an array of 'number_of_elements' objects into the serial stream 'serial'. + If 'number_of_elements' is 0, then either the array has no data, + or the number of elements of the array is unkown. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements) +{ + (void) local; //Unused + if (number_of_elements == (size_t)-1) return M_SERIAL_FAIL_RETRY; + FILE *f = (FILE *)serial->data[0].p; + size_t n = fwrite (M_ASSIGN_CAST(const void*, &number_of_elements), sizeof number_of_elements, 1, f); + return n == 1 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Write an array separator between elements of an array into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_array_next(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // Unused + (void) serial; // Unused + return M_SERIAL_OK_CONTINUE; +} + +/* End the writing of an array into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_array_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // Unused + (void) serial; // Unused + return M_SERIAL_OK_CONTINUE; +} + +/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_map_value(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + (void) serial; + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing a tuple into the serial stream 'serial'. + Initialize 'local' so that it can serial the tuple + (local is an unique serialization object of the tuple). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_tuple_start(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + (void) serial; + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index) +{ + (void) local; // argument not used + (void) serial; + (void) field_name; + (void) max; + (void) index; // Assume index are write in order from 0 to max. + return M_SERIAL_OK_CONTINUE; +} + +/* End the write of a tuple into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_tuple_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + (void) serial; + return M_SERIAL_OK_DONE; +} + +/* Start writing a variant into the serial stream 'serial'. + If index <= 0, the variant is empty. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise + Otherwise, the field 'field_name[index]' will be filled. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index) +{ + (void) field_name; + (void) max; + (void) local; + FILE *f = (FILE *)serial->data[0].p; + size_t n = fwrite (M_ASSIGN_CAST(const void*, &index), sizeof index, 1, f); + return n == 1 ? ((index < 0) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE) : m_core_serial_fail(); +} + +/* End Writing a variant into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_write_variant_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + (void) serial; + return M_SERIAL_OK_DONE; +} + +/* The exported interface. */ +static const m_serial_write_interface_t m_ser1al_bin_write_interface = { + m_ser1al_bin_write_boolean, + m_ser1al_bin_write_integer, + m_ser1al_bin_write_float, + m_ser1al_bin_write_string, + m_ser1al_bin_write_array_start, + m_ser1al_bin_write_array_next, + m_ser1al_bin_write_array_end, + m_ser1al_bin_write_array_start, + m_ser1al_bin_write_map_value, + m_ser1al_bin_write_array_next, + m_ser1al_bin_write_array_end, + m_ser1al_bin_write_tuple_start, + m_ser1al_bin_write_tuple_id, + m_ser1al_bin_write_tuple_end, + m_ser1al_bin_write_variant_start, + m_ser1al_bin_write_variant_end +}; + +M_INLINE void m_serial_bin_write_init(m_serial_write_t serial, FILE *f) +{ + serial->m_interface = &m_ser1al_bin_write_interface; + serial->data[0].p = M_ASSIGN_CAST(void*, f); +} + +M_INLINE void m_serial_bin_write_clear(m_serial_write_t serial) +{ + (void) serial; // Nothing to do +} + + +/* Define a synonym of m_serial_read_t to the BIN serializer with its proper OPLIST */ +typedef m_serial_write_t m_serial_bin_write_t; + +#define M_OPL_m_serial_bin_write_t() \ + (INIT_WITH(m_serial_bin_write_init), CLEAR(m_serial_bin_write_clear), \ + TYPE(m_serial_bin_write_t), PROPERTIES(( LET_AS_INIT_WITH(1) )) ) + + + +/********************************************************************************/ +/************************** FILE / READ / BIN *******************************/ +/********************************************************************************/ + +/* Read from the stream 'serial' a boolean. + Set '*b' with the boolean value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_boolean(m_serial_read_t serial, bool *b){ + FILE *f = (FILE*) serial->data[0].p; + size_t n = fread (M_ASSIGN_CAST(void*, b), sizeof (bool), 1, f); + return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes. + Set '*i' with the integer value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){ + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + size_t n; + FILE *f = (FILE *)serial->data[0].p; + if (size_of_type == 1) { + n = fread (M_ASSIGN_CAST(void*, &i8), sizeof i8, 1, f); + *i = i8; + } else if (size_of_type == 2) { + n = fread (M_ASSIGN_CAST(void*, &i16), sizeof i16, 1, f); + *i = i16; + } else if (size_of_type == 4) { + n = fread (M_ASSIGN_CAST(void*, &i32), sizeof i32, 1, f); + *i = i32; + } else { + M_ASSERT(size_of_type == 8); + n = fread (M_ASSIGN_CAST(void*, &i64), sizeof i64, 1, f); + *i = i64; + } + return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes. + Set '*r' with the boolean value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){ + float f1; + double f2; + long double f3; + size_t n; + FILE *f = (FILE *)serial->data[0].p; + if (size_of_type == sizeof f1) { + n = fread (M_ASSIGN_CAST(void*, &f1), sizeof f1, 1, f); + *r = f1; + } else if (size_of_type == sizeof f2) { + n = fread (M_ASSIGN_CAST(void*, &f2), sizeof f2, 1, f); + *r = f2; + } else { + M_ASSERT(size_of_type == sizeof f3); + n = fread (M_ASSIGN_CAST(void*, &f3), sizeof f3, 1, f); + *r = f3; + } + return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' a string. + Set 's' with the string if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_string(m_serial_read_t serial, struct string_s *s){ + FILE *f = (FILE*) serial->data[0].p; + M_ASSERT(f != NULL && s != NULL); + // First read the number of non null characters + size_t length; + if (m_ser1al_bin_read_size(f, &length) != true) return m_core_serial_fail(); + // Use of internal string interface to dimension the string + char *p = m_str1ng_fit2size(s, length + 1); + m_str1ng_set_size(s, length); + // Read the characters excluding the final null one. + // NOTE: fread supports length == 0. + size_t n = fread(M_ASSIGN_CAST(void*, p), 1, length, f); + // Force the final null character + p[length] = 0; + return (n == length) ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading from the stream 'serial' an array. + Set '*num' with the number of elements, or 0 if it is not known. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue, + M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty), + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num) +{ + FILE *f = (FILE*) serial->data[0].p; + size_t n = fread (M_ASSIGN_CAST(void*, num), sizeof *num, 1, f); + local->data[1].s = *num; + return (n != 1) ? m_core_serial_fail() : (local->data[1].s == 0) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE; +} + +/* Continue reading from the stream 'serial' an array. + Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue, + M_SERIAL_OK_DONE if it succeeds and the array ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_array_next(m_serial_local_t local, m_serial_read_t serial) +{ + (void) serial; // Unused + M_ASSERT(local->data[1].s > 0); + local->data[1].s --; + return local->data[1].s == 0 ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE; +} + + +/* Continue reading from the stream 'serial' the value separator + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_map_value(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + (void) serial; + return M_SERIAL_OK_CONTINUE; +} + +/* Start reading a tuple from the stream 'serial'. + Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_tuple_start(m_serial_local_t local, m_serial_read_t serial) +{ + (void) serial; + local->data[1].i = 0; + return M_SERIAL_OK_CONTINUE; +} + +/* Continue reading a tuple from the stream 'serial'. + Set '*id' with the corresponding index of the table 'field_name[max]' + associated to the parsed field in the stream. + Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues, + Return M_SERIAL_OK_DONE if it succeeds and the tuple ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id) +{ + (void) serial; + (void) field_name; + (void) max; + *id = local->data[1].i; + local->data[1].i ++; + return (*id == max) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE; +} + +/* Start reading a variant from the stream 'serial'. + Set '*id' with the corresponding index of the table 'field_name[max]' + associated to the parsed field in the stream. + Return M_SERIAL_OK_CONTINUE if it succeeds and the variant continues, + Return M_SERIAL_OK_DONE if it succeeds and the variant ends(variant is empty), + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id) +{ + (void) field_name; + (void) max; + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + size_t n = fread (M_ASSIGN_CAST(void*, id), sizeof *id, 1, f); + return n == 1 ? ((*id < 0) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE) : m_core_serial_fail(); +} + +/* End reading a variant from the stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds and the variant ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_bin_read_variant_end(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + (void) serial; + return M_SERIAL_OK_DONE; +} + +static const m_serial_read_interface_t m_ser1al_bin_read_interface = { + m_ser1al_bin_read_boolean, + m_ser1al_bin_read_integer, + m_ser1al_bin_read_float, + m_ser1al_bin_read_string, + m_ser1al_bin_read_array_start, + m_ser1al_bin_read_array_next, + m_ser1al_bin_read_array_start, + m_ser1al_bin_read_map_value, + m_ser1al_bin_read_array_next, + m_ser1al_bin_read_tuple_start, + m_ser1al_bin_read_tuple_id, + m_ser1al_bin_read_variant_start, + m_ser1al_bin_read_variant_end +}; + +M_INLINE void m_serial_bin_read_init(m_serial_read_t serial, FILE *f) +{ + serial->m_interface = &m_ser1al_bin_read_interface; + serial->data[0].p = M_ASSIGN_CAST(void*, f); +} + +M_INLINE void m_serial_bin_read_clear(m_serial_read_t serial) +{ + (void) serial; // Nothing to do +} + +/* Define a synonym of m_serial_read_t to the BIN serializer with its proper OPLIST */ +typedef m_serial_read_t m_serial_bin_read_t; +#define M_OPL_m_serial_bin_read_t() \ + (INIT_WITH(m_serial_bin_read_init), CLEAR(m_serial_bin_read_clear), \ + TYPE(m_serial_bin_read_t), PROPERTIES(( LET_AS_INIT_WITH(1) )) ) + +M_END_PROTECTED_CODE + +#endif diff --git a/components/mlib/m-serial-json.h b/components/mlib/m-serial-json.h new file mode 100644 index 00000000..379b2004 --- /dev/null +++ b/components/mlib/m-serial-json.h @@ -0,0 +1,1199 @@ +/* + * M*LIB - Serial JSON + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_SERIAL_JSON_H +#define MSTARLIB_SERIAL_JSON_H + +#include "m-core.h" +#include "m-string.h" + +M_BEGIN_PROTECTED_CODE + +/********************************************************************************/ +/*************************** FILE / WRITE / JSON ********************************/ +/********************************************************************************/ + +/* Write the boolean 'data' into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_boolean(m_serial_write_t serial, const bool data) +{ + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "%s", data ? "true" : "false"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type) +{ + (void) size_of_type; // Ignored + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "%lld", data); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type) +{ + (void) size_of_type; // Ignored + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "%Lf", data); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the null-terminated string 'data'into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_string(m_serial_write_t serial, const char data[], size_t length) +{ + M_ASSERT_SLOW(length == strlen(data) ); + FILE *f = (FILE *)serial->data[0].p; + M_ASSERT(f != NULL && data != NULL); + /* HACK: Build dummy string to reuse m_string_out_str */ + m_string_t v2; + uintptr_t ptr = (uintptr_t) data; + v2->u.heap.size = length; + v2->u.heap.alloc = length + 1; + v2->ptr = (char*)ptr; + m_string_out_str(f, v2); + return M_UNLIKELY(ferror(f)) ? m_core_serial_fail() : M_SERIAL_OK_DONE; +} + +/* Start writing an array of 'number_of_elements' objects into the serial stream 'serial'. + If 'number_of_elements' is 0, then either the array has no data, + or the number of elements of the array is unkown. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements) +{ + (void) local; // argument not used + (void) number_of_elements; // Ignored + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "["); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Write an array separator between elements of an array into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_array_next(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, ","); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* End the writing of an array into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_array_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "]"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start writing a map of 'number_of_elements' pairs of objects into the serial stream 'serial'. + If 'number_of_elements' is 0, then either the map has no data, + or the number of elements is unkown. + Initialize 'local' so that it can be used to serialize the map + (local is an unique serialization object of the map). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements) +{ + (void) local; // argument not used + (void) number_of_elements; // Ignored + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "{"); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_map_value(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, ":"); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Write a map separator between elements of a map into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_map_next(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, ","); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* End the writing of a map into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_map_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "}"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start writing a tuple into the serial stream 'serial'. + Initialize 'local' so that it can serial the tuple + (local is an unique serialization object of the tuple). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "{"); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index) +{ + (void) local; // argument not used + (void) max; // Ignored + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* End the write of a tuple into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "}"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start writing a variant into the serial stream 'serial'. + If index <= 0, the variant is empty. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise + Otherwise, the field 'field_name[index]' will be filled. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index) +{ + (void) local; // argument not used + (void) max; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n; + if (index >= 0) { + M_ASSERT (index < max); + n = fprintf(f, "{\"%s\":", field_name[index]); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); + } else { + n = fprintf(f, "{}"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); + } +} + +/* End Writing a variant into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE *)serial->data[0].p; + int n = fprintf(f, "}"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* The internal exported interface of m_serial_write_json + If it is not used, it will be optimized away by the compiler. */ +static const m_serial_write_interface_t m_ser1al_json_write_interface = { + m_ser1al_json_write_boolean, + m_ser1al_json_write_integer, + m_ser1al_json_write_float, + m_ser1al_json_write_string, + m_ser1al_json_write_array_start, + m_ser1al_json_write_array_next, + m_ser1al_json_write_array_end, + m_ser1al_json_write_map_start, + m_ser1al_json_write_map_value, + m_ser1al_json_write_map_next, + m_ser1al_json_write_map_end, + m_ser1al_json_write_tuple_start, + m_ser1al_json_write_tuple_id, + m_ser1al_json_write_tuple_end, + m_ser1al_json_write_variant_start, + m_ser1al_json_write_variant_end +}; + +/* Initialize the JSON serial object for writing any object to JSON format in the given FILE */ +M_INLINE void m_serial_json_write_init(m_serial_write_t serial, FILE *f) +{ + serial->m_interface = &m_ser1al_json_write_interface; + serial->data[0].p = M_ASSIGN_CAST(void*, f); +} + +/* CLear the JSON serial object for writing*/ +M_INLINE void m_serial_json_write_clear(m_serial_write_t serial) +{ + (void) serial; // Nothing to do +} + +/* Define a synonym to the JSON serializer with a proper OPLIST */ +typedef m_serial_write_t m_serial_json_write_t; +#define M_OPL_m_serial_json_write_t() \ + (INIT_WITH(m_serial_json_write_init), CLEAR(m_serial_json_write_clear), \ + TYPE(m_serial_json_write_t), PROPERTIES(( LET_AS_INIT_WITH(1) )) ) + + + + +/********************************************************************************/ +/*************************** FILE / READ / JSON ********************************/ +/********************************************************************************/ + +/* Helper function to skip a space */ +M_INLINE int +m_ser1al_json_read_skip (FILE *f) +{ + int c; + do { + // c is an int, and is the value of the character read as unsigned char + c = fgetc(f); + } while (c != EOF && isspace(c)); + return c; +} + +/* Read from the stream 'serial' a boolean. + Set '*b' with the boolean value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_boolean(m_serial_read_t serial, bool *b){ + FILE *f = (FILE*) serial->data[0].p; + int c = m_ser1al_json_read_skip(f); + if (c == 't') { + *b = true; + c = fgetc(f); + if (c != 'r') return m_core_serial_fail(); + c = fgetc(f); + if (c != 'u') return m_core_serial_fail(); + c = fgetc(f); + if (c != 'e') return m_core_serial_fail(); + return M_SERIAL_OK_DONE; + } else if (c == 'f') { + *b = false; + c = fgetc(f); + if (c != 'a') return m_core_serial_fail(); + c = fgetc(f); + if (c != 'l') return m_core_serial_fail(); + c = fgetc(f); + if (c != 's') return m_core_serial_fail(); + c = fgetc(f); + if (c != 'e') return m_core_serial_fail(); + return M_SERIAL_OK_DONE; + } else { + return m_core_serial_fail(); + } +} + +/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes. + Set '*i' with the integer value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){ + (void) size_of_type; // Ignored + FILE *f = (FILE*) serial->data[0].p; + return m_core_fscanf(f, " %lld", i) == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes. + Set '*r' with the boolean value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){ + (void) size_of_type; // Ignored + FILE *f = (FILE*) serial->data[0].p; + return m_core_fscanf(f, " %Lf", r) == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' a string. + Set 's' with the string if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_string(m_serial_read_t serial, struct m_string_s *s){ + FILE *f = (FILE*) serial->data[0].p; + int n = m_core_fscanf(f, " "); // Skip any leading spaces. + (void) n; // Unused return value. + return m_string_in_str(s, f) ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading from the stream 'serial' an array. + Set '*num' with the number of elements, or 0 if it is not known. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue, + M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty), + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + int final1 = -1, final2 = -1; + int n = m_core_fscanf(f, " [%n ]%n", &final1, &final2); + (void) n; // Unused. Parsing is checked through c (more robust) + *num = 0; // don't know the size of the array. + // Test how much the parsing succeed. + if (final1 < 0) return m_core_serial_fail(); + return (final2 < 0) ? M_SERIAL_OK_CONTINUE : M_SERIAL_OK_DONE; +} + +/* Continue reading from the stream 'serial' an array. + Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue, + M_SERIAL_OK_DONE if it succeeds and the array ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_array_next(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + char c = 0; + int n = m_core_fscanf(f, " %c", m_core_arg_size(&c, 1) ); + (void) n; // Unused. Parsing is checked through c (more robust) + return c == ',' ? M_SERIAL_OK_CONTINUE : c == ']' ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading from the stream 'serial' a map. + Set '*num' with the number of elements, or 0 if it is not known. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_OK_DONE if it succeeds and the map ends (the map is empty), + m_core_serial_fail() otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_map_start(m_serial_local_t local, m_serial_read_t serial, size_t *num) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + int final1 = -1, final2 = -1; + int n = m_core_fscanf(f, " {%n }%n", &final1, &final2); + (void) n; // Unused. Parsing is checked through final (more robust) + *num = 0; // don't know the size of the map. + // Test how much the parsing succeed. + if (final1 < 0) return m_core_serial_fail(); + return (final2 < 0) ? M_SERIAL_OK_CONTINUE : M_SERIAL_OK_DONE; +} + +/* Continue reading from the stream 'serial' the value separator + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_map_value(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + int final = -1; + int n = m_core_fscanf(f, " :%n", &final); + (void) n; // Unused. Parsing is checked through final (more robust) + return final > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Continue reading from the stream 'serial' a map. + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_OK_DONE if it succeeds and the map ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_map_next(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + char c = 0; + int n = m_core_fscanf(f, " %c", m_core_arg_size(&c, 1) ); + (void) n; // Unused. Parsing is checked through c (more robust) + return c == ',' ? M_SERIAL_OK_CONTINUE : c == '}' ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading a tuple from the stream 'serial'. + Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_tuple_start(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + int final = -1; + int n = m_core_fscanf(f, " {%n", &final); + (void) n; // Unused. Parsing is checked through final (more robust) + return final > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Continue reading a tuple from the stream 'serial'. + Set '*id' with the corresponding index of the table 'field_name[max]' + associated to the parsed field in the stream. + Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues, + Return M_SERIAL_OK_DONE if it succeeds and the tuple ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + int c = m_ser1al_json_read_skip(f); + if (c == EOF) return m_core_serial_fail(); + if (c == '}') return M_SERIAL_OK_DONE; + if (c == ',') { + // If first call of read_tuple_id, it is a failure + if (*id == -1) return m_core_serial_fail(); + } else { + // c should be \" but let fscanf parse it. + ungetc(c, f); + } + /* Read the field in the JSON */ + c = -1; + char field[M_USE_IDENTIFIER_ALLOC+1]; + int nn = m_core_fscanf(f, " \"%" M_AS_STR(M_USE_IDENTIFIER_ALLOC) "[^ \t\n\"]\":%n", + m_core_arg_size(field, M_USE_IDENTIFIER_ALLOC+1), &c); + (void) nn ; // Unused. Check is done through 'c' (more robust). + if (c <= 0) + return m_core_serial_fail(); + /* Search for field in field_name */ + for(int n = 0; n < max; n++) { + if (strcmp(field, field_name[n]) == 0) { + *id = n; + return M_SERIAL_OK_CONTINUE; + } + } + return m_core_serial_fail(); +} + +/* Start reading a variant from the stream 'serial'. + Set '*id' with the corresponding index of the table 'field_name[max]' + associated to the parsed field in the stream. + Return M_SERIAL_OK_CONTINUE if it succeeds and the variant continues, + Return M_SERIAL_OK_DONE if it succeeds and the variant ends(variant is empty), + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + + int final1 = -1, final2 = -1; + // TODO: Accept 'null' as empty variant? + int nn = m_core_fscanf(f, " {%n }%n", &final1, &final2); + (void) nn ; // Unused. Check is done through final (more robust). + // Test how much the parsing succeed. + if (final1 <= 0) return m_core_serial_fail(); + if (final2 > 0) return M_SERIAL_OK_DONE; + + /* Read the field in the JSON */ + char field[M_USE_IDENTIFIER_ALLOC+1]; + nn = m_core_fscanf(f, " \"%" M_AS_STR(M_USE_IDENTIFIER_ALLOC) "[^ \t\n\"]\":%n", + m_core_arg_size(field, M_USE_IDENTIFIER_ALLOC+1), &final2); + (void) nn ; // Unused. Check is done through final (more robust). + if (final2 <= 0) return m_core_serial_fail(); + + /* Linear Search for field in field_name */ + for(int n = 0; n < max; n++) { + if (strcmp(field, field_name[n]) == 0) { + *id = n; + return M_SERIAL_OK_CONTINUE; + } + } + // Field not found + return m_core_serial_fail(); +} + +/* End reading a variant from the stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds and the variant ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_json_read_variant_end(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + FILE *f = (FILE*) serial->data[0].p; + int final = -1; + int n = m_core_fscanf(f, " }%n", &final); + (void) n ; // Unused. Check is done through final (more robust). + return final > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +static const m_serial_read_interface_t m_ser1al_json_read_interface = { + m_ser1al_json_read_boolean, + m_ser1al_json_read_integer, + m_ser1al_json_read_float, + m_ser1al_json_read_string, + m_ser1al_json_read_array_start, + m_ser1al_json_read_array_next, + m_ser1al_json_read_map_start, + m_ser1al_json_read_map_value, + m_ser1al_json_read_map_next, + m_ser1al_json_read_tuple_start, + m_ser1al_json_read_tuple_id, + m_ser1al_json_read_variant_start, + m_ser1al_json_read_variant_end +}; + +/* Initialize the JSON serial object for reading any object from JSON format in the given FILE */ +M_INLINE void m_serial_json_read_init(m_serial_read_t serial, FILE *f) +{ + serial->m_interface = &m_ser1al_json_read_interface; + serial->data[0].p = M_ASSIGN_CAST(void*, f); +} + +/* Clear the JSON serial object for reading from the FILE */ +M_INLINE void m_serial_json_read_clear(m_serial_read_t serial) +{ + (void) serial; // Nothing to do +} + +/* Define a synonym of m_serial_read_t + to the JSON serializer with its proper OPLIST */ +typedef m_serial_read_t m_serial_json_read_t; +#define M_OPL_m_serial_json_read_t() \ + (INIT_WITH(m_serial_json_read_init), CLEAR(m_serial_json_read_clear), \ + TYPE(m_serial_json_read_t) , PROPERTIES(( LET_AS_INIT_WITH(1) )) ) + + + +/********************************************************************************/ +/************************** STRING / WRITE / JSON *******************************/ +/********************************************************************************/ + +/* Write the boolean 'data' into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_boolean(m_serial_write_t serial, const bool data) +{ + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + int n = m_string_cat_printf(f, "%s", data ? "true" : "false"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type) +{ + (void) size_of_type; // Ignored + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + int n = m_string_cat_printf(f, "%lld", data); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type) +{ + (void) size_of_type; // Ignored + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + int n = m_string_cat_printf(f, "%Lf", data); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the null-terminated string 'data'into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_string(m_serial_write_t serial, const char data[], size_t length) +{ + M_ASSERT_SLOW(length == strlen(data) ); + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + M_ASSERT(f != NULL && data != NULL); + /* HACK: Build dummy string to reuse m_string_get_str */ + m_string_t v2; + uintptr_t ptr = (uintptr_t) data; + v2->u.heap.size = length; + v2->u.heap.alloc = length + 1; + v2->ptr = (char*)ptr; + m_string_get_str(f, v2, true); + return M_SERIAL_OK_DONE; +} + +/* Start writing an array of 'number_of_elements' objects into the serial stream 'serial'. + If 'number_of_elements' is 0, then either the array has no data, + or the number of elements of the array is unkown. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements) +{ + (void) local; // argument not used + (void) number_of_elements; // Ignored + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, '['); + return M_SERIAL_OK_CONTINUE; +} + +/* Write an array separator between elements of an array into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_array_next(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, ','); + return M_SERIAL_OK_CONTINUE; +} + +/* End the writing of an array into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_array_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, ']'); + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing a map of 'number_of_elements' pairs of objects into the serial stream 'serial'. + If 'number_of_elements' is 0, then either the map has no data, + or the number of elements is unkown. + Initialize 'local' so that it can be used to serialize the map + (local is an unique serialization object of the map). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements) +{ + (void) local; // argument not used + (void) number_of_elements; // Ignored + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, '{'); + return M_SERIAL_OK_CONTINUE; +} + +/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_map_value(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, ':'); + return M_SERIAL_OK_CONTINUE; +} + +/* Write a map separator between elements of a map into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_map_next(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, ','); + return M_SERIAL_OK_CONTINUE; +} + +/* End the writing of a map into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_map_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, '}'); + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing a tuple into the serial stream 'serial'. + Initialize 'local' so that it can serial the tuple + (local is an unique serialization object of the tuple). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, '{'); + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index) +{ + (void) local; // argument not used + (void) max; // Ignored + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + int n = m_string_cat_printf(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* End the write of a tuple into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, '}'); + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing a variant into the serial stream 'serial'. + If index <= 0, the variant is empty. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise + Otherwise, the field 'field_name[index]' will be filled. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index) +{ + (void) local; // argument not used + (void) max; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + int n; + if (index >= 0) { + M_ASSERT (index < max); + n = m_string_cat_printf(f, "{\"%s\":", field_name[index]); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); + } else { + n = m_string_cat_printf(f, "{}"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); + } +} + +/* End Writing a variant into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct m_string_s *f = (struct m_string_s *)serial->data[0].p; + m_string_push_back(f, '}'); + return M_SERIAL_OK_CONTINUE; +} + +/* The internal exported interface of m_serial_write_json. */ +static const m_serial_write_interface_t m_ser1al_str_json_write_interface = { + m_ser1al_str_json_write_boolean, + m_ser1al_str_json_write_integer, + m_ser1al_str_json_write_float, + m_ser1al_str_json_write_string, + m_ser1al_str_json_write_array_start, + m_ser1al_str_json_write_array_next, + m_ser1al_str_json_write_array_end, + m_ser1al_str_json_write_map_start, + m_ser1al_str_json_write_map_value, + m_ser1al_str_json_write_map_next, + m_ser1al_str_json_write_map_end, + m_ser1al_str_json_write_tuple_start, + m_ser1al_str_json_write_tuple_id, + m_ser1al_str_json_write_tuple_end, + m_ser1al_str_json_write_variant_start, + m_ser1al_str_json_write_variant_end +}; + +/* Initialize the JSON serial object for writing any object to JSON format in the given FILE */ +M_INLINE void m_serial_str_json_write_init(m_serial_write_t serial, m_string_t s) +{ + serial->m_interface = &m_ser1al_str_json_write_interface; + serial->data[0].p = M_ASSIGN_CAST(struct m_string_s *, s); +} + +/* CLear the JSON serial object for writing*/ +M_INLINE void m_serial_str_json_write_clear(m_serial_write_t serial) +{ + (void) serial; // Nothing to do +} + +/* Define a synonym to the JSON serializer with a proper OPLIST */ +typedef m_serial_write_t m_serial_str_json_write_t; +#define M_OPL_m_serial_str_json_write_t() \ + (INIT_WITH(m_serial_str_json_write_init), CLEAR(m_serial_str_json_write_clear), \ + TYPE(m_serial_str_json_write_t) , PROPERTIES(( LET_AS_INIT_WITH(1) )) ) + + + +/********************************************************************************/ +/************************** STRING / READ / JSON *******************************/ +/********************************************************************************/ + +/* Helper function to read a character and advance the stream */ +M_INLINE char +m_ser1al_str_json_getc(const char **p) +{ + char c = **p; + // Once it reaches the end of string character (== 0), stop advancing the stream. + *p = *p + (c != 0); + return c; +} + +/* Helper function to skip space(s) */ +M_INLINE void +m_ser1al_str_json_skip (const char **p) +{ + while (isspace (**p)) { + (*p)++; + } +} + +/* Helper function to read a string with characters not present in reject */ +M_INLINE void +m_ser1al_str_json_gets(size_t alloc, char field[], const char reject[], const char **s) +{ + M_ASSERT (alloc > 0); + size_t length = strcspn(*s, reject); + if (length >= alloc-1) + length = alloc - 1; + memcpy(field, *s, length); + field[length] = 0; + *s += length; +} + +/* Read from the stream 'serial' a boolean. + Set '*b' with the boolean value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_boolean(m_serial_read_t serial, bool *b){ + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + char c = m_ser1al_str_json_getc(f); + if (c == 't') { + *b = true; + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != 'r')) return m_core_serial_fail(); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != 'u')) return m_core_serial_fail(); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != 'e')) return m_core_serial_fail(); + return M_SERIAL_OK_DONE; + } else if (M_LIKELY(c == 'f')) { + *b = false; + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != 'a')) return m_core_serial_fail(); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != 'l')) return m_core_serial_fail(); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != 's')) return m_core_serial_fail(); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != 'e')) return m_core_serial_fail(); + return M_SERIAL_OK_DONE; + } else { + return m_core_serial_fail(); + } +} + +/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes. + Set '*i' with the integer value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){ + (void) size_of_type; // Ignored + const char **f = &serial->data[0].cstr; + char *e; + *i = strtoll(*f, &e, 10); + bool b = e != *f; + serial->data[0].cstr = (const char*) e; + return b ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes. + Set '*r' with the boolean value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){ + (void) size_of_type; // Ignored + const char **f = &serial->data[0].cstr; + char *e; + *r = strtold(*f, &e); + bool b = e != *f; + serial->data[0].cstr = (const char*) e; + return b ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' a string. + Set 's' with the string if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_string(m_serial_read_t serial, struct m_string_s *s){ + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + const char *e; + bool b = m_string_parse_str(s, *f, &e); + serial->data[0].cstr = e; + return b ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading from the stream 'serial' an array. + Set '*num' with the number of elements, or 0 if it is not known. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue, + M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty), + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + char c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != '[')) { + return m_core_serial_fail(); + } + *num = 0; // Don't know the size of the array. + m_ser1al_str_json_skip(f); + if (M_UNLIKELY (**f == ']')) { + c = m_ser1al_str_json_getc(f); + assert( c == ']'); + return M_SERIAL_OK_DONE; + } else { + return M_SERIAL_OK_CONTINUE; + } +} + +/* Continue reading from the stream 'serial' an array. + Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue, + M_SERIAL_OK_DONE if it succeeds and the array ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_array_next(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + char c = m_ser1al_str_json_getc(f); + return c == ',' ? M_SERIAL_OK_CONTINUE : c == ']' ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading from the stream 'serial' a map. + Set '*num' with the number of elements, or 0 if it is not known. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_OK_DONE if it succeeds and the map ends (the map is empty), + m_core_serial_fail() otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_map_start(m_serial_local_t local, m_serial_read_t serial, size_t *num) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + char c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY(c != '{')) { + return m_core_serial_fail(); + } + *num = 0; // Don't know the size of the map + m_ser1al_str_json_skip(f); + if (M_UNLIKELY (**f == '}')) { + c = m_ser1al_str_json_getc(f); + assert(c == '}'); + return M_SERIAL_OK_DONE; + } else { + return M_SERIAL_OK_CONTINUE; + } +} + +/* Continue reading from the stream 'serial' the value separator + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_map_value(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + char c = m_ser1al_str_json_getc(f); + return c ==':' ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Continue reading from the stream 'serial' a map. + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_OK_DONE if it succeeds and the map ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_map_next(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + char c = m_ser1al_str_json_getc(f); + return c == ',' ? M_SERIAL_OK_CONTINUE : c == '}' ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading a tuple from the stream 'serial'. + Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_tuple_start(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + char c = m_ser1al_str_json_getc(f); + return c == '{' ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Continue reading a tuple from the stream 'serial'. + Set '*id' with the corresponding index of the table 'field_name[max]' + associated to the parsed field in the stream. + Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues, + Return M_SERIAL_OK_DONE if it succeeds and the tuple ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + char c = m_ser1al_str_json_getc(f); + if (c == 0) return m_core_serial_fail(); + if (c == '}') return M_SERIAL_OK_DONE; + if (c == ',') { + // If first call of read_tuple_id, it is a failure + if (M_UNLIKELY (*id == -1)) return m_core_serial_fail(); + m_ser1al_str_json_skip(f); + c = m_ser1al_str_json_getc(f); + } + + /* Read the field in the JSON */ + char field[M_USE_IDENTIFIER_ALLOC+1]; + if (M_UNLIKELY(c != '"')) return m_core_serial_fail(); + m_ser1al_str_json_gets(M_USE_IDENTIFIER_ALLOC+1, field, " \t\n\"", f); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY (c != '"')) return m_core_serial_fail(); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY (c != ':')) return m_core_serial_fail(); + + /* Search for field in field_name */ + for(int n = 0; n < max; n++) { + if (strcmp(field, field_name[n]) == 0) { + *id = n; + return M_SERIAL_OK_CONTINUE; + } + } + return m_core_serial_fail(); +} + +/* Start reading a variant from the stream 'serial'. + Set '*id' with the corresponding index of the table 'field_name[max]' + associated to the parsed field in the stream. + Return M_SERIAL_OK_CONTINUE if it succeeds and the variant continues, + Return M_SERIAL_OK_DONE if it succeeds and the variant ends(variant is empty), + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + int c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY (c != '{')) + // TODO: Accept 'null' as empty variant? + return m_core_serial_fail(); + m_ser1al_str_json_skip(f); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY (c == '}')) return M_SERIAL_OK_DONE; // Empty variant + + /* Read the field in the JSON */ + char field[M_USE_IDENTIFIER_ALLOC+1]; + if (M_UNLIKELY(c != '"')) return m_core_serial_fail(); + m_ser1al_str_json_gets(M_USE_IDENTIFIER_ALLOC+1, field, " \t\n\"", f); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY (c != '"')) return m_core_serial_fail(); + c = m_ser1al_str_json_getc(f); + if (M_UNLIKELY (c != ':')) return m_core_serial_fail(); + + /* Linear Search for field in field_name */ + for(int n = 0; n < max; n++) { + if (strcmp(field, field_name[n]) == 0) { + *id = n; + return M_SERIAL_OK_CONTINUE; + } + } + // Field not found + return m_core_serial_fail(); +} + +/* End reading a variant from the stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds and the variant ends, + M_SERIAL_FAIL otherwise */ +M_INLINE m_serial_return_code_t +m_ser1al_str_json_read_variant_end(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].cstr; + m_ser1al_str_json_skip(f); + int c = m_ser1al_str_json_getc(f); + return (M_UNLIKELY (c != '}')) ? m_core_serial_fail() : M_SERIAL_OK_DONE; +} + +static const m_serial_read_interface_t m_ser1al_str_json_read_interface = { + m_ser1al_str_json_read_boolean, + m_ser1al_str_json_read_integer, + m_ser1al_str_json_read_float, + m_ser1al_str_json_read_string, + m_ser1al_str_json_read_array_start, + m_ser1al_str_json_read_array_next, + m_ser1al_str_json_read_map_start, + m_ser1al_str_json_read_map_value, + m_ser1al_str_json_read_map_next, + m_ser1al_str_json_read_tuple_start, + m_ser1al_str_json_read_tuple_id, + m_ser1al_str_json_read_variant_start, + m_ser1al_str_json_read_variant_end +}; + +/* Initialize the JSON serial object for reading any object from JSON format in the given const string */ +M_INLINE void m_serial_str_json_read_init(m_serial_read_t serial, const char str[]) +{ + serial->m_interface = &m_ser1al_str_json_read_interface; + serial->data[0].cstr = str; +} + +/* Clear the JSON serial object for reading from the FILE */ +M_INLINE const char *m_serial_str_json_read_clear(m_serial_read_t serial) +{ + // Nothing to clear. Return pointer to the last data parsed. + return serial->data[0].cstr; +} + +/* Define a synonym of m_serial_read_t + to the JSON serializer with its proper OPLIST */ +typedef m_serial_read_t m_serial_str_json_read_t; +#define M_OPL_m_serial_str_json_read_t() \ + (INIT_WITH(m_serial_str_json_read_init), CLEAR(m_serial_str_json_read_clear), \ + TYPE(m_serial_str_json_read_t), PROPERTIES(( LET_AS_INIT_WITH(1) )) ) + + +M_END_PROTECTED_CODE + +#endif diff --git a/components/mlib/m-shared.h b/components/mlib/m-shared.h new file mode 100644 index 00000000..8fba531d --- /dev/null +++ b/components/mlib/m-shared.h @@ -0,0 +1,553 @@ +/* + * M*LIB - SHARED Pointer Module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_SHARED_PTR_H +#define MSTARLIB_SHARED_PTR_H + +#include "m-core.h" +#include "m-atomic.h" +#include "m-genint.h" + +M_BEGIN_PROTECTED_CODE + +/* Define shared pointer and its function. + USAGE: SHARED_PTR_DEF(name, type, [, oplist]) */ +#define M_SHARED_PTR_DEF(name, ...) \ + M_SHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define shared pointer and its function + as the given name name_t + USAGE: SHARED_PTR_DEF_AS(name, name_t, type, [, oplist]) */ +#define M_SHARED_PTR_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_SHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_SHAR3D_ATOMIC_OPLIST, name_t ), \ + (name, __VA_ARGS__ , M_SHAR3D_ATOMIC_OPLIST, name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a shared pointer. + USAGE: SHARED_OPLIST(name [, oplist_of_the_type]) */ +#define M_SHARED_PTR_OPLIST(...) \ + M_SHAR3D_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_BASIC_OPLIST ), \ + (__VA_ARGS__ ))) + + +/* Define relaxed shared pointer and its function (thread unsafe). + USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */ +#define M_SHARED_PTR_RELAXED_DEF(name, ...) \ + M_SHARED_PTR_RELAXED_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define relaxed shared pointer and its function (thread unsafe) + as the given name name_t + USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */ +#define M_SHARED_PTR_RELAXED_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_SHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_SHAR3D_INTEGER_OPLIST, name_t ), \ + (name, __VA_ARGS__, M_SHAR3D_INTEGER_OPLIST, name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define shared resource and its function. + This is a bounded pool of resource shared by multiple owners. + USAGE: SHARED_RESOURCE_DEF(name, type, [, oplist]) */ +#define M_SHARED_RESOURCE_DEF(name, ...) \ + M_SHARED_RESOURCE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) + + +/* Define shared resource and its function + as the given name named_t and the iterator it_t + This is a bounded pool of resource shared by multiple owners. + USAGE: SHARED_RESOURCE_DEF_AS(name, name_t, it_t, type, [, oplist]) */ +#define M_SHARED_RESOURCE_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_SHAR3D_RESOURCE_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \ + (name, __VA_ARGS__, name_t, it_t ))) \ + M_END_PROTECTED_CODE + + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +// deferred evaluation +#define M_SHAR3D_PTR_OPLIST_P1(arg) M_SHAR3D_PTR_OPLIST_P2 arg + +/* Validation of the given, shared_t oplist */ +#define M_SHAR3D_PTR_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_SHAR3D_PTR_OPLIST_P3, M_SHAR3D_PTR_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_SHAR3D_PTR_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_SHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +#define M_SHAR3D_PTR_OPLIST_P3(name, oplist) ( \ + INIT(M_F(name, _init)), \ + CLEAR(M_F(name, _clear)), \ + INIT_SET(M_F(name, _init_set)), \ + SET(M_F(name, _set)) \ + INIT_MOVE(M_F(name, _init_move)), \ + RESET(M_F(name, _reset)), \ + MOVE(M_F(name, _move)), \ + SWAP(M_F(name, _swap)) \ + ,NAME(name) \ + ,TYPE(M_F(name, _ct)) \ + ) + +// OPLIST to handle a counter of atomic type +#define M_SHAR3D_ATOMIC_OPLIST (TYPE(atomic_int), \ + INIT_SET(atomic_init), \ + ADD(atomic_fetch_add), \ + SUB(atomic_fetch_sub), \ + IT_CREF(atomic_load)) + +// OPLIST to handle a counter of non-atomic type +#define M_SHAR3D_INTEGER_OPLIST (TYPE(int), \ + INIT_SET(m_shar3d_integer_init_set), \ + ADD(m_shar3d_integer_add), \ + SUB(m_shar3d_integer_sub), \ + IT_CREF(m_shar3d_integer_cref)) + +/* Atomic like interface for basic integers */ +M_INLINE void m_shar3d_integer_init_set(int *p, int val) { *p = val; } +M_INLINE int m_shar3d_integer_add(int *p, int val) { int r = *p; *p += val; return r; } +M_INLINE int m_shar3d_integer_sub(int *p, int val) { int r = *p; *p -= val; return r; } +M_INLINE int m_shar3d_integer_cref(int *p) { return *p; } + + +/********************************** INTERNAL *********************************/ + +/* Contract of a shared pointer */ +#define M_SHAR3D_CONTRACT(shared, cpt_oplist) do { \ + M_ASSERT(shared != NULL); \ + M_ASSERT(*shared == NULL || M_CALL_IT_CREF(cpt_oplist, &(*shared)->cpt) >= 1); \ + } while (0) + +// deferred evaluation +#define M_SHAR3D_PTR_DEF_P1(arg) M_ID( M_SHAR3D_PTR_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_SHAR3D_PTR_DEF_P2(name, type, oplist, cpt_oplist, shared_t) \ + M_IF_OPLIST(oplist)(M_SHAR3D_PTR_DEF_P3, M_SHAR3D_PTR_DEF_FAILURE)(name, type, oplist, cpt_oplist, shared_t) + +/* Stop processing with a compilation failure */ +#define M_SHAR3D_PTR_DEF_FAILURE(name, type, oplist, cpt_oplist, shared_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SHARED_PTR_DEF): the given argument is not a valid oplist: " #oplist) + +/* Code generation */ +#define M_SHAR3D_PTR_DEF_P3(name, type, oplist, cpt_oplist, shared_t) \ + M_SHAR3D_PTR_DEF_TYPE(name, type, oplist, cpt_oplist, shared_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_SHAR3D_PTR_DEF_CORE(name, type, oplist, cpt_oplist, shared_t) \ + M_EMPLACE_QUEUE_DEF(name, cpt_oplist, M_F(name, _init_with), oplist, M_SHAR3D_PTR_DEF_EMPLACE) + +/* Define the types */ +#define M_SHAR3D_PTR_DEF_TYPE(name, type, oplist, cpt_oplist, shared_t) \ + \ + typedef struct M_F(name, _s){ \ + type *data; /* Pointer to the data */ \ + M_GET_TYPE cpt_oplist cpt; /* Counter of how many refs the data */ \ + bool combineAlloc; /* Does the data and the ptr share the slot? */ \ + } *shared_t[1]; \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* Internal type for oplist */ \ + typedef shared_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + \ + typedef struct M_F(name, _combine_s) { \ + struct M_F(name, _s) ptr; \ + type data; \ + } M_F(name, combine_ct)[1]; \ + +/* Define the core functions */ +#define M_SHAR3D_PTR_DEF_CORE(name, type, oplist, cpt_oplist, shared_t) \ + \ + M_INLINE void \ + M_F(name, _init)(shared_t shared) \ + { \ + *shared = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _init2)(shared_t shared, type *data) \ + { \ + M_ASSERT (shared != NULL); \ + /* The shared ptr get exclusive access to data */ \ + struct M_F(name, _s) *ptr; \ + if (M_UNLIKELY (data == NULL)) { \ + *shared = NULL; \ + return; \ + } \ + ptr = M_CALL_NEW(oplist, struct M_F(name, _s)); \ + if (M_UNLIKELY_NOMEM (ptr == NULL)) { \ + M_MEMORY_FULL(sizeof(struct M_F(name, _s))); \ + return; \ + } \ + ptr->data = data; \ + M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1); \ + ptr->combineAlloc = false; \ + *shared = ptr; \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + } \ + \ + M_IF_METHOD(INIT, oplist)( \ + M_INLINE void \ + M_F(name, _init_new)(shared_t shared) \ + { \ + /* NOTE: Alloc 1 struct with both structures. */ \ + struct M_F(name, _combine_s) *p = \ + M_CALL_NEW(oplist, struct M_F(name, _combine_s)); \ + if (M_UNLIKELY_NOMEM (p == NULL)) { \ + M_MEMORY_FULL(sizeof(struct M_F(name, _combine_s))); \ + return; \ + } \ + struct M_F(name, _s) *ptr = &p->ptr; \ + ptr->combineAlloc = true; \ + type *data = &p->data; \ + M_CALL_INIT( oplist, *data); \ + ptr->data = data; \ + M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1); \ + *shared = ptr; \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + } \ + , /* No INIT */ ) \ + \ + M_INLINE bool \ + M_F(name, _NULL_p)(const shared_t shared) \ + { \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + return *shared == NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(shared_t dest, \ + const shared_t shared) \ + { \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + M_ASSERT (dest != shared); \ + *dest = *shared; \ + if (*dest != NULL) { \ + int n = M_CALL_ADD(cpt_oplist, &((*dest)->cpt), 1); \ + (void) n; /* unused return value */ \ + } \ + M_SHAR3D_CONTRACT(dest, cpt_oplist); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(shared_t dest) \ + { \ + M_SHAR3D_CONTRACT(dest, cpt_oplist); \ + if (*dest != NULL) { \ + if (M_CALL_SUB(cpt_oplist, &((*dest)->cpt), 1) == 1) { \ + bool combineAlloc = (*dest)->combineAlloc; \ + /* Note: if combineAlloc is true, the address of the slot \ + combining both data & ptr is the same as the address of the \ + first element, aka data itself. Static analyzer tools don't \ + seem to detect this and report error. */ \ + M_CALL_CLEAR(oplist, *(*dest)->data); \ + if (combineAlloc == false) { \ + M_CALL_DEL(oplist, (*dest)->data); \ + } \ + M_CALL_DEL(oplist, *dest); \ + } \ + *dest = NULL; \ + } \ + M_SHAR3D_CONTRACT(dest, cpt_oplist); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(shared_t dest) \ + { \ + /* NOTE: Clear will also set dest to NULL */ \ + M_F(name, _clear)(dest); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(shared_t dest, \ + const shared_t shared) \ + { \ + M_SHAR3D_CONTRACT(dest, cpt_oplist); \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + M_F(name, _clear)(dest); \ + M_F(name, _init_set)(dest, shared); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(shared_t dest, \ + shared_t shared) \ + { \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + M_ASSERT (dest != NULL && dest != shared); \ + *dest = *shared; \ + *shared = NULL; \ + M_SHAR3D_CONTRACT(dest, cpt_oplist); \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(shared_t dest, \ + shared_t shared) \ + { \ + M_SHAR3D_CONTRACT(dest, cpt_oplist); \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + M_ASSERT (dest != shared); \ + M_F(name, _clear)(dest); \ + M_F(name, _init_move)(dest, shared); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(shared_t p1, \ + shared_t p2) \ + { \ + M_SHAR3D_CONTRACT(p1, cpt_oplist); \ + M_SHAR3D_CONTRACT(p2, cpt_oplist); \ + /* NOTE: SWAP is not atomic */ \ + M_SWAP (struct M_F(name, _s)*, *p1, *p2); \ + M_SHAR3D_CONTRACT(p1, cpt_oplist); \ + M_SHAR3D_CONTRACT(p2, cpt_oplist); \ + } \ + \ + M_INLINE bool \ + M_F(name, _equal_p)(const shared_t p1, \ + const shared_t p2) \ + { \ + M_SHAR3D_CONTRACT(p1, cpt_oplist); \ + M_SHAR3D_CONTRACT(p2, cpt_oplist); \ + return *p1 == *p2; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(const shared_t shared) \ + { \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + M_ASSERT(*shared != NULL); \ + type *data = (*shared)->data; \ + M_ASSERT (data != NULL); \ + return M_CONST_CAST (type, data); \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(shared_t shared) \ + { \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + M_ASSERT(*shared != NULL); \ + type *data = (*shared)->data; \ + M_ASSERT (data != NULL); \ + return data; \ + } \ + +/* Definition of the emplace_back function for arrays */ +#define M_SHAR3D_PTR_DEF_EMPLACE(name, cpt_oplist, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(M_F(name, _ct) shared \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + /* NOTE: Alloc 1 struct with both structures. */ \ + struct M_F(name, _combine_s) *p = \ + M_CALL_NEW(oplist, struct M_F(name, _combine_s)); \ + if (M_UNLIKELY_NOMEM (p == NULL)) { \ + M_MEMORY_FULL(sizeof(struct M_F(name, _combine_s))); \ + return; \ + } \ + struct M_F(name, _s) *ptr = &p->ptr; \ + ptr->combineAlloc = true; \ + M_F(name, _subtype_ct) *data = &p->data; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ + ptr->data = data; \ + M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1); \ + *shared = ptr; \ + M_SHAR3D_CONTRACT(shared, cpt_oplist); \ + } \ + + + +/********************************** INTERNAL *********************************/ + +#define M_SHAR3D_RESOURCE_CONTRACT(s) do { \ + M_ASSERT (s != NULL); \ + M_ASSERT (s->buffer != NULL); \ + } while (0) + +// deferred +#define M_SHAR3D_RESOURCE_DEF_P1(arg) M_ID( M_SHAR3D_RESOURCE_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_SHAR3D_RESOURCE_DEF_P2(name, type, oplist, shared_t, it_t) \ + M_IF_OPLIST(oplist)(M_SHAR3D_RESOURCE_DEF_P3, M_SHAR3D_RESOURCE_DEF_FAILURE)(name, type, oplist, shared_t, it_t) + +/* Stop processing with a compilation failure */ +#define M_SHAR3D_RESOURCE_DEF_FAILURE(name, type, oplist, shared_t, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SHARED_RESOURCE_DEF): the given argument is not a valid oplist: " #oplist) + +#define M_SHAR3D_RESOURCE_DEF_P3(name, type, oplist, shared_t, it_t) \ + M_SHAR3D_RESOURCE_DEF_TYPE(name, type, oplist, shared_t, it_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_SHAR3D_RESOURCE_DEF_CORE(name, type, oplist, shared_t, it_t) \ + +/* Define the types */ +#define M_SHAR3D_RESOURCE_DEF_TYPE(name, type, oplist, shared_t, it_t) \ + \ + /* Create an aligned type to avoid false sharing between threads */ \ + typedef struct M_F(name, _atype_s) { \ + atomic_uint cpt; \ + type x; \ + M_CACHELINE_ALIGN(align, type, atomic_uint); \ + } M_F(name, _atype_ct); \ + \ + typedef struct M_F(name, _s) { \ + m_genint_t core; \ + M_F(name, _atype_ct) *buffer; \ + } shared_t[1]; \ + \ + typedef struct M_F(name, _it_s) { \ + unsigned int idx; \ + struct M_F(name, _s) *ref; \ + } it_t[1]; \ + \ + /* Internal Types for oplist */ \ + typedef shared_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + +/* Define the core functions */ +#define M_SHAR3D_RESOURCE_DEF_CORE(name, type, oplist, shared_t, it_t) \ + M_INLINE void \ + M_F(name, _init)(shared_t s, size_t n) \ + { \ + M_ASSERT(s != NULL); \ + M_ASSERT (n > 0 && n < UINT_MAX); \ + s->buffer = M_CALL_REALLOC(oplist, M_F(name, _atype_ct), NULL, n); \ + if (M_UNLIKELY_NOMEM (s->buffer == NULL)) { \ + M_MEMORY_FULL(sizeof(M_F(name, _atype_ct)) * n); \ + return; \ + } \ + for(size_t i = 0; i < n; i++) { \ + M_CALL_INIT(oplist, s->buffer[i].x); \ + atomic_init (&s->buffer[i].cpt, 0U); \ + } \ + m_genint_init(s->core, (unsigned int) n); \ + M_SHAR3D_RESOURCE_CONTRACT(s); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(shared_t s) \ + { \ + M_SHAR3D_RESOURCE_CONTRACT(s); \ + size_t n = m_genint_size(s->core); \ + for(size_t i = 0; i < n; i++) { \ + M_CALL_CLEAR(oplist, s->buffer[i].x); \ + } \ + M_CALL_FREE(oplist, s->buffer); \ + s->buffer = NULL; \ + m_genint_clear(s->core); \ + } \ + \ + M_INLINE void \ + M_F(name, _it)(it_t it, shared_t s) \ + { \ + M_SHAR3D_RESOURCE_CONTRACT(s); \ + M_ASSERT (it != NULL); \ + unsigned int idx = m_genint_pop(s->core); \ + it->idx = idx; \ + it->ref = s; \ + if (M_LIKELY (idx != M_GENINT_ERROR)) { \ + M_ASSERT(atomic_load(&s->buffer[idx].cpt) == 0); \ + atomic_store(&s->buffer[idx].cpt, 1U); \ + } \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(it_t it) \ + { \ + M_ASSERT (it != NULL); \ + return it->idx == M_GENINT_ERROR; \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(it_t it) \ + { \ + M_ASSERT (it != NULL && it->ref != NULL && it->idx != M_GENINT_ERROR); \ + M_SHAR3D_RESOURCE_CONTRACT(it->ref); \ + return &it->ref->buffer[it->idx].x; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(it_t it) \ + { \ + M_ASSERT (it != NULL && it->ref != NULL && it->idx != M_GENINT_ERROR); \ + M_SHAR3D_RESOURCE_CONTRACT(it->ref); \ + return M_CONST_CAST (type, &it->ref->buffer[it->idx].x); \ + } \ + \ + M_INLINE void \ + M_F(name, _end)(it_t it, shared_t s) \ + { \ + M_SHAR3D_RESOURCE_CONTRACT(s); \ + M_ASSERT (it != NULL); \ + M_ASSERT (it->ref == s); \ + unsigned int idx = it->idx; \ + if (M_LIKELY (idx != M_GENINT_ERROR)) { \ + unsigned int c = atomic_fetch_sub (&it->ref->buffer[idx].cpt, 1U); \ + if (c == 1) { \ + m_genint_push(it->ref->core, idx); \ + } \ + it->idx = M_GENINT_ERROR; \ + } \ + } \ + \ + M_INLINE void \ + M_F(name, _it_set)(it_t itd, it_t its) \ + { \ + M_ASSERT (itd != NULL && its != NULL); \ + M_SHAR3D_RESOURCE_CONTRACT(its->ref); \ + itd->ref = its->ref; \ + unsigned int idx = its->idx; \ + itd->idx = idx; \ + if (M_LIKELY (idx != M_GENINT_ERROR)) { \ + unsigned int c = atomic_fetch_add(&itd->ref->buffer[idx].cpt, 1U); \ + M_ASSERT (c >= 1); \ + } \ + } \ + +M_END_PROTECTED_CODE + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define SHARED_PTR_OPLIST M_SHARED_PTR_OPLIST +#define SHARED_PTR_DEF M_SHARED_PTR_DEF +#define SHARED_PTR_DEF_AS M_SHARED_PTR_DEF_AS +#define SHARED_PTR_RELAXED_DEF M_SHARED_PTR_RELAXED_DEF +#define SHARED_PTR_RELAXED_DEF_AS M_SHARED_PTR_RELAXED_DEF_AS +#define SHARED_RESOURCE_DEF M_SHARED_RESOURCE_DEF +#define SHARED_RESOURCE_DEF_AS M_SHARED_RESOURCE_DEF_AS +#endif + +#endif diff --git a/components/mlib/m-snapshot.h b/components/mlib/m-snapshot.h new file mode 100644 index 00000000..860ce57f --- /dev/null +++ b/components/mlib/m-snapshot.h @@ -0,0 +1,814 @@ +/* + * M*LIB - SNAPSHOT Module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_SNAPSHOT_H +#define MSTARLIB_SNAPSHOT_H + +#include "m-atomic.h" +#include "m-core.h" +#include "m-genint.h" + +M_BEGIN_PROTECTED_CODE + +/* Define a Single Producer Single Consummer snapshot and its functions + USAGE: SNAPSHOT_SPSC_DEF(name, type[, oplist]) */ +#define M_SNAPSHOT_SPSC_DEF(name, ...) \ + M_SNAPSHOT_SPSC_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define a Single Producer Single Consummer snapshot and its functions + as the given name name_t + USAGE: SNAPSHOT_SPSC_DEF_AS(name, name_t, type[, oplist]) */ +#define M_SNAPSHOT_SPSC_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_SNAPSH0T_SPSC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \ + (name, __VA_ARGS__ , name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a Single Producer Multiple Consummer snapshot and its functions + USAGE: SNAPSHOT_SPMC_DEF(name, type[, oplist]) */ +#define M_SNAPSHOT_SPMC_DEF(name, ...) \ + M_SNAPSHOT_SPMC_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define a Single Producer Multiple Consummer snapshot and its functions + as the given name name_t + USAGE: SNAPSHOT_SPMC_DEF_AS(name, type[, oplist]) */ +#define M_SNAPSHOT_SPMC_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_SNAPSH0T_SPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \ + (name, __VA_ARGS__ , name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define a Multiple Producer Multiple Consummer snapshot and its functions + USAGE: SNAPSHOT_MPMC_DEF(name, type[, oplist]) */ +#define M_SNAPSHOT_MPMC_DEF(name, ...) \ + M_SNAPSHOT_MPMC_DEF_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define a Multiple Producer Multiple Consummer snapshot and its functions + as the given name name_t + USAGE: SNAPSHOT_MPMC_DEF_AS(name, name_t, type[, oplist]) */ +#define M_SNAPSHOT_MPMC_DEF_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_SNAPSH0T_MPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \ + (name, __VA_ARGS__ , name_t ))) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a snapshot (SPSC, SPMC or MPMC). + USAGE: SNAPSHOT_OPLIST(name[, oplist]) */ +#define M_SNAPSHOT_OPLIST(...) \ + M_SNAPSH0T_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \ + (__VA_ARGS__ ))) + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +// deferred evaluation of the input +#define M_SNAPSH0T_OPLIST_P1(arg) M_SNAPSH0T_OPLIST_P2 arg + +/* Validation of the given oplist */ +#define M_SNAPSH0T_OPLIST_P2(name, oplist) \ + M_IF_OPLIST(oplist)(M_SNAPSH0T_OPLIST_P3, M_SNAPSH0T_OPLIST_FAILURE)(name, oplist) + +/* Prepare a clean compilation failure */ +#define M_SNAPSH0T_OPLIST_FAILURE(name, oplist) \ + ((M_LIB_ERROR(ARGUMENT_OF_SNAPSHOT_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) + +/* Define the oplist of a snapshot */ +#define M_SNAPSH0T_OPLIST_P3(name, oplist) \ + (INIT(M_F(name, _init)) \ + ,INIT_SET(M_F(name, _init_set)) \ + ,SET(M_F(name, _set)) \ + ,CLEAR(M_F(name, _clear)) \ + ,NAME(name) \ + ,TYPE(M_F(name, _ct)) \ + ,SUBTYPE(M_F(name, _subtype_ct)) \ + ,OPLIST(oplist) \ + ,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_F(name, _init_move)),) \ + ,M_IF_METHOD(MOVE, oplist)(MOVE(M_F(name, _move)),) \ + ) + + +/********************************** INTERNAL *********************************/ + +/* Flag defining the atomic state of a snapshot: + * - r: Index of the read buffer Range [0..2] + * - w: Index of the write buffer Range [0..2] + * - f: Next index of the write buffer when a shot is taken Range [0..2] + * - b: Boolean indicating that the read buffer shall be updated + * all fields packed in an unsigned char type. + */ +#define M_SNAPSH0T_SPSC_FLAG(r, w, f, b) \ + ((unsigned char)( ( (r) << 4) | ((w) << 2) | ((f)) | ((b) << 6))) +#define M_SNAPSH0T_SPSC_R(flags) \ + (((unsigned int) (flags) >> 4) & 0x03u) +#define M_SNAPSH0T_SPSC_W(flags) \ + (((unsigned int) (flags) >> 2) & 0x03u) +#define M_SNAPSH0T_SPSC_F(flags) \ + (((unsigned int) (flags) >> 0) & 0x03u) +#define M_SNAPSH0T_SPSC_B(flags) \ + (((unsigned int) (flags) >> 6) & 0x01u) + +/* NOTE: Due to atomic_load only accepting non-const pointer, + we can't have any const in the interface. */ +#define M_SNAPSH0T_SPSC_FLAGS_CONTRACT(flags) \ + M_ASSERT(M_SNAPSH0T_SPSC_R(flags) != M_SNAPSH0T_SPSC_W(flags) \ + && M_SNAPSH0T_SPSC_R(flags) != M_SNAPSH0T_SPSC_F(flags) \ + && M_SNAPSH0T_SPSC_W(flags) != M_SNAPSH0T_SPSC_F(flags)) + +#define M_SNAPSH0T_SPSC_CONTRACT(snap) do { \ + M_ASSERT((snap) != NULL); \ + unsigned char f = atomic_load (&(snap)->flags); \ + M_SNAPSH0T_SPSC_FLAGS_CONTRACT(f); \ + } while (0) + +// A snapshot is basically an atomic triple buffer (Lock Free) +// between a single producer thread and a single consummer thread. +#define M_SNAPSH0T_SPSC_MAX_BUFFER 3 + +// Defered evaluation of the arguments. +#define M_SNAPSH0T_SPSC_DEF_P1(arg) M_ID( M_SNAPSH0T_SPSC_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_SNAPSH0T_SPSC_DEF_P2(name, type, oplist, snapshot_t) \ + M_IF_OPLIST(oplist)(M_SNAPSH0T_SPSC_DEF_P3, M_SNAPSH0T_SPSC_DEF_FAILURE)(name, type, oplist, snapshot_t) + +/* Stop processing with a compilation failure */ +#define M_SNAPSH0T_SPSC_DEF_FAILURE(name, type, oplist, snapshot_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SNAPSHOT_SPSC_DEF): the given argument is not a valid oplist: " #oplist) + +/* Expand the type and the functions of a SPSC snapshot */ +#define M_SNAPSH0T_SPSC_DEF_P3(name, type, oplist, snapshot_t) \ + M_SNAPSH0T_SPSC_DEF_TYPE(name, type, oplist, snapshot_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_SNAPSH0T_SPSC_DEF_CORE(name, type, oplist, snapshot_t) \ + +/* Define the type */ +#define M_SNAPSH0T_SPSC_DEF_TYPE(name, type, oplist, snapshot_t) \ + \ + /* Create an aligned type to avoid false sharing between threads */ \ + typedef struct M_F(name, _aligned_type_s) { \ + type x; \ + M_CACHELINE_ALIGN(align, type); \ + } M_F(name, _aligned_type_ct); \ + \ + typedef struct M_F(name, _s) { \ + M_F(name, _aligned_type_ct) data[M_SNAPSH0T_SPSC_MAX_BUFFER]; \ + atomic_uchar flags; \ + } snapshot_t[1]; \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + \ + /* Define internal types for oplist */ \ + typedef snapshot_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + +/* Define the core functions */ +#define M_SNAPSH0T_SPSC_DEF_CORE(name, type, oplist, snapshot_t) \ + \ + M_INLINE void \ + M_F(name, _init)(snapshot_t snap) \ + { \ + M_ASSERT(snap != NULL); \ + for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \ + M_CALL_INIT(oplist, snap->data[i].x); \ + } \ + atomic_init (&snap->flags, M_SNAPSH0T_SPSC_FLAG(0, 1, 2, 0)); \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \ + M_CALL_CLEAR(oplist, snap->data[i].x); \ + } \ + } \ + \ + /* const is missing for org due to use of atomic_load of org */ \ + M_INLINE void \ + M_F(name, _init_set)(snapshot_t snap, snapshot_t org) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(org); \ + M_ASSERT(snap != NULL && snap != org); \ + for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \ + M_CALL_INIT_SET(oplist, snap->data[i].x, org->data[i].x); \ + } \ + atomic_init (&snap->flags, atomic_load(&org->flags)); \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + } \ + \ + /* const is missing for org due to use of atomic_load of org */ \ + M_INLINE void \ + M_F(name, _set)(snapshot_t snap, snapshot_t org) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + M_SNAPSH0T_SPSC_CONTRACT(org); \ + for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \ + M_CALL_SET(oplist, snap->data[i].x, org->data[i].x); \ + } \ + atomic_init (&snap->flags, atomic_load(&org->flags)); \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + } \ + \ + M_IF_METHOD(INIT_MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _init_move)(snapshot_t snap, snapshot_t org) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(org); \ + M_ASSERT(snap != NULL && snap != org); \ + for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \ + M_CALL_INIT_MOVE(oplist, snap->data[i].x, org->data[i].x); \ + } \ + atomic_store (&snap->flags, atomic_load(&org->flags)); \ + atomic_store (&org->flags, M_SNAPSH0T_SPSC_FLAG(0,0,0,0) ); \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + } \ + ,) /* IF_METHOD (INIT_MOVE) */ \ + \ + M_IF_METHOD(MOVE, oplist)( \ + M_INLINE void \ + M_F(name, _move)(snapshot_t snap, \ + snapshot_t org) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + M_SNAPSH0T_SPSC_CONTRACT(org); \ + M_ASSERT(snap != org); \ + for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \ + M_CALL_MOVE(oplist, snap->data[i].x, org->data[i].x); \ + } \ + atomic_store (&snap->flags, atomic_load(&org->flags)); \ + atomic_store (&org->flags, M_SNAPSH0T_SPSC_FLAG(0,0,0,0) ); \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + } \ + ,) /* IF_METHOD (MOVE) */ \ + \ + M_INLINE type * \ + M_F(name, _write)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + unsigned char nextFlags, origFlags = atomic_load (&snap->flags); \ + /* Atomic CAS operation */ \ + do { \ + /* Swap F and W buffer, setting exchange flag */ \ + nextFlags = M_SNAPSH0T_SPSC_FLAG(M_SNAPSH0T_SPSC_R(origFlags), \ + M_SNAPSH0T_SPSC_F(origFlags), \ + M_SNAPSH0T_SPSC_W(origFlags), 1); \ + /* exponential backoff is not needed as there can't be more \ + than 2 threads which try to update the data. */ \ + } while (!atomic_compare_exchange_weak (&snap->flags, &origFlags, \ + nextFlags)); \ + /* Return new write buffer for new updating */ \ + return &snap->data[M_SNAPSH0T_SPSC_W(nextFlags)].x; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _read)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + unsigned char nextFlags, origFlags = atomic_load (&snap->flags); \ + /* Atomic CAS operation */ \ + do { \ + /* If no exchange registered, do nothing and keep the same */ \ + if (!M_SNAPSH0T_SPSC_B(origFlags)) { \ + nextFlags = origFlags; \ + break; \ + } \ + /* Swap R and F buffer, clearing exchange flag */ \ + nextFlags = M_SNAPSH0T_SPSC_FLAG(M_SNAPSH0T_SPSC_F(origFlags), \ + M_SNAPSH0T_SPSC_W(origFlags), \ + M_SNAPSH0T_SPSC_R(origFlags), 0); \ + /* exponential backoff is not needed as there can't be more \ + than 2 threads which try to update the data. */ \ + } while (!atomic_compare_exchange_weak (&snap->flags, &origFlags, \ + nextFlags)); \ + /* Return current read buffer */ \ + return M_CONST_CAST(type, &snap->data[M_SNAPSH0T_SPSC_R(nextFlags)].x); \ + } \ + \ + /* Non const due to use of atomic_load */ \ + M_INLINE bool \ + M_F(name, _updated_p)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + unsigned char flags = atomic_load (&snap->flags); \ + return M_SNAPSH0T_SPSC_B(flags); \ + } \ + \ + /* Non const due to use of atomic_load */ \ + M_INLINE type * \ + M_F(name, _get_write_buffer)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + unsigned char flags = atomic_load(&snap->flags); \ + return &snap->data[M_SNAPSH0T_SPSC_W(flags)].x; \ + } \ + \ + /* Non const due to use of atomic_load */ \ + M_INLINE type const * \ + M_F(name, _get_read_buffer)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPSC_CONTRACT(snap); \ + unsigned char flags = atomic_load(&snap->flags); \ + return M_CONST_CAST(type, &snap->data[M_SNAPSH0T_SPSC_R(flags)].x); \ + } \ + + +/********************************** INTERNAL *********************************/ + +#define M_SNAPSH0T_SPMC_INT_FLAG(w, n) ( ((w) << 1) | (n) ) +#define M_SNAPSH0T_SPMC_INT_FLAG_W(f) ((f) >> 1) +#define M_SNAPSH0T_SPMC_INT_FLAG_N(f) ((f) & 1) + +// 2 more buffer than the number of readers are needed +#define M_SNAPSH0T_SPMC_EXTRA_BUFFER 2 + +#define M_SNAPSH0T_SPMC_MAX_READER (M_GENINT_MAX_ALLOC-M_SNAPSH0T_SPMC_EXTRA_BUFFER) + +/* Internal structure to handle SPMC snapshot but return an unique index in the buffer array. + - lastNext: last published written index + next flag (format M_SNAPSH0T_SPMC_INT_FLAG) + - currentWrite: the index being currently written. + - n_reader : number of readers + - cptTab: ref counter array to keep track of how many readers use the corresponding buffer. + - freeList: a pool of free integers. +*/ +typedef struct m_snapsh0t_mrsw_s { + atomic_uint lastNext; + unsigned int currentWrite; + size_t n_reader; + atomic_uint *cptTab; + m_genint_t freeList; +} m_snapsh0t_mrsw_ct[1]; + +// can't check currentWrite due to potential data race on it +#define M_SNAPSH0T_SPMC_INT_CONTRACT(s) do { \ + M_ASSERT (s != NULL); \ + M_ASSERT (s->n_reader > 0 && s->n_reader <= M_SNAPSH0T_SPMC_MAX_READER); \ + M_ASSERT ((size_t)M_SNAPSH0T_SPMC_INT_FLAG_W(atomic_load(&s->lastNext)) \ + <= s->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); \ + M_ASSERT (s->cptTab != NULL); \ + } while (0) + +/* Initialize m_snapsh0t_mrsw_ct for n readers (constructor) */ +M_INLINE void +m_snapsh0t_mrsw_init(m_snapsh0t_mrsw_ct s, size_t n) +{ + M_ASSERT (s != NULL); + M_ASSERT (n >= 1 && n <= M_SNAPSH0T_SPMC_MAX_READER); + s->n_reader = n; + n += M_SNAPSH0T_SPMC_EXTRA_BUFFER; + + // Initialize the counters to zero (no reader use it) + atomic_uint *ptr = M_MEMORY_REALLOC (atomic_uint, NULL, n); + if (M_UNLIKELY_NOMEM (ptr == NULL)) { + M_MEMORY_FULL(sizeof (atomic_uint) * n); + return; + } + s->cptTab = ptr; + for(size_t i = 0; i < n; i++) + atomic_init(&s->cptTab[i], 0U); + m_genint_init (s->freeList, (unsigned int) n); + + // Get a free buffer and set it as available for readers + unsigned int w = m_genint_pop(s->freeList); + M_ASSERT (w != M_GENINT_ERROR); + atomic_store(&s->cptTab[w], 1U); + atomic_init(&s->lastNext, M_SNAPSH0T_SPMC_INT_FLAG(w, true)); + + // Get working buffer + s->currentWrite = m_genint_pop(s->freeList); + M_ASSERT (s->currentWrite != M_GENINT_ERROR); + atomic_store(&s->cptTab[s->currentWrite], 1U); + M_SNAPSH0T_SPMC_INT_CONTRACT(s); +} + +/* Clear m_snapsh0t_mrsw_ct (destructor) */ +M_INLINE void +m_snapsh0t_mrsw_clear(m_snapsh0t_mrsw_ct s) +{ + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + M_MEMORY_FREE (s->cptTab); + m_genint_clear(s->freeList); + s->cptTab = NULL; + s->n_reader = 0; +} + +/* Return the current index that is written in the buffer */ +M_INLINE unsigned int +m_snapsh0t_mrsw_get_write_idx(m_snapsh0t_mrsw_ct s) +{ + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + return s->currentWrite; +} + +/* Return the number of readers */ +M_INLINE unsigned int +m_snapsh0t_mrsw_size(m_snapsh0t_mrsw_ct s) +{ + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + return (unsigned int) s->n_reader; +} + +/* Give the current index that is written to the readers, + and return new available index for the writer thread */ +M_INLINE unsigned int +m_snapsh0t_mrsw_write_idx(m_snapsh0t_mrsw_ct s, unsigned int idx) +{ + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + + // Provide the finalized written buffer to the readers. + unsigned int newNext, previous = atomic_load(&s->lastNext); + do { + newNext = M_SNAPSH0T_SPMC_INT_FLAG(idx, true); + } while (!atomic_compare_exchange_weak(&s->lastNext, &previous, newNext)); + + if (M_SNAPSH0T_SPMC_INT_FLAG_N(previous)) { + // Reuse previous buffer as it was not used by any reader + idx = M_SNAPSH0T_SPMC_INT_FLAG_W(previous); + // Some other read threads may already have try to reserve this index + // So atomic_load(&s->cptTab[idx]) can be greater than 1. + // However they will fail to ack it in lastNext, + // so they will remove their reservation later + } else { + // Remove the writer thread counter from the count of the previous buffer + idx = M_SNAPSH0T_SPMC_INT_FLAG_W(previous); + unsigned int c = atomic_fetch_sub(&s->cptTab[idx], 1U); + M_ASSERT (c != 0 && c <= s->n_reader + 1); + // Get a new buffer. + if (c != 1) { + // If someone else keeps a ref on the buffer, we can't reuse it + // get another free one. + idx = m_genint_pop(s->freeList); + M_ASSERT(idx != M_GENINT_ERROR); + } else { + // No other thread keep track of this buffer. + // Reuse it. + } + M_ASSERT (idx < s->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); + M_ASSERT (atomic_load(&s->cptTab[idx]) == 0); + atomic_store(&s->cptTab[idx], 1U); + } + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + return idx; +} + +/* Perform a swap of the current write buffer and return a new one */ +M_INLINE unsigned int +m_snapsh0t_mrsw_write(m_snapsh0t_mrsw_ct s) +{ + s->currentWrite = m_snapsh0t_mrsw_write_idx(s, s->currentWrite); + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + return s->currentWrite; +} + +/* Start writing to the write buffer and return its index */ +M_INLINE unsigned int +m_snapsh0t_mrsw_write_start(m_snapsh0t_mrsw_ct s) +{ + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + // Get a new buffer. + unsigned int idx = m_genint_pop(s->freeList); + M_ASSERT (idx != M_GENINT_ERROR); + M_ASSERT (idx < s->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); + M_ASSERT (atomic_load(&s->cptTab[idx]) == 0); + atomic_store(&s->cptTab[idx], 1U); + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + return idx; +} + +/* End writing to the given write buffer */ +M_INLINE void +m_snapsh0t_mrsw_write_end(m_snapsh0t_mrsw_ct s, unsigned int idx) +{ + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + + // Provide this write bufer to the readers + unsigned int newNext, previous = atomic_load(&s->lastNext); + do { + newNext = M_SNAPSH0T_SPMC_INT_FLAG(idx, true); + } while (!atomic_compare_exchange_weak(&s->lastNext, &previous, newNext)); + + // Free the previous write buffer + idx = M_SNAPSH0T_SPMC_INT_FLAG_W(previous); + unsigned int c = atomic_fetch_sub(&s->cptTab[idx], 1U); + M_ASSERT (c != 0 && c <= s->n_reader + 1); + if (c == 1) { + m_genint_push(s->freeList, idx); + } + M_SNAPSH0T_SPMC_INT_CONTRACT(s); +} + +/* Start reading the latest written buffer and return the index to it */ +M_INLINE unsigned int +m_snapsh0t_mrsw_read_start(m_snapsh0t_mrsw_ct s) +{ + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + unsigned int idx, previous; + reload: + // Load the last published index + Next flag + previous = atomic_load(&s->lastNext); + while (true) { + // Get the last published index + idx = M_SNAPSH0T_SPMC_INT_FLAG_W(previous); + // Load the number of threads using this index + unsigned int c = atomic_load(&s->cptTab[idx]); + M_ASSERT (c <= s->n_reader + 1); + // Reserve the index if it still being reserved by someone else + if (M_UNLIKELY (c == 0 + || !atomic_compare_exchange_strong(&s->cptTab[idx], &c, c+1))) + goto reload; + // Try to ack it + unsigned int newNext = M_SNAPSH0T_SPMC_INT_FLAG(idx, false); + reforce: + if (M_LIKELY (atomic_compare_exchange_strong(&s->lastNext, &previous, newNext))) + break; + // We have been preempted by another thread + if (idx == M_SNAPSH0T_SPMC_INT_FLAG_W(previous)) { + // This is still ok if the index has not changed + // We can get previous to true again if the writer has recycled the index, + // while we reserved it, and the reader get prempted until its CAS. + if (M_UNLIKELY (M_SNAPSH0T_SPMC_INT_FLAG_N(previous) == true)) goto reforce; + break; + } + // Free the reserved index as we failed it to ack it + c = atomic_fetch_sub(&s->cptTab[idx], 1U); + M_ASSERT (c != 0 && c <= s->n_reader + 1); + if (c == 1) { + m_genint_push(s->freeList, idx); + } + } + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + return idx; +} + +/* End the reading the given buffer */ +M_INLINE void +m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx) +{ + M_SNAPSH0T_SPMC_INT_CONTRACT(s); + M_ASSERT (idx < s->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); + // Decrement reference counter of the buffer + unsigned int c = atomic_fetch_sub(&s->cptTab[idx], 1U); + M_ASSERT (c != 0 && c <= s->n_reader + 1); + if (c == 1) { + // Buffer no longer used by any reader thread. + // Push back index in free list + m_genint_push(s->freeList, idx); + } + M_SNAPSH0T_SPMC_INT_CONTRACT(s); +} + + +/********************************** INTERNAL *********************************/ + +/* Contract of a SPMC snapshot. + Nothing notable as it can be accessed concurrently */ +#define M_SNAPSH0T_SPMC_CONTRACT(snap) do { \ + M_ASSERT (snap != NULL); \ + M_ASSERT (snap->data != NULL); \ + } while (0) + + +// Defered evaluation +#define M_SNAPSH0T_SPMC_DEF_P1(arg) M_ID( M_SNAPSH0T_SPMC_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_SNAPSH0T_SPMC_DEF_P2(name, type, oplist, snapshot_t) \ + M_IF_OPLIST(oplist)(M_SNAPSH0T_SPMC_DEF_P3, M_SNAPSH0T_SPMC_DEF_FAILURE)(name, type, oplist, snapshot_t) + +/* Stop processing with a compilation failure */ +#define M_SNAPSH0T_SPMC_DEF_FAILURE(name, type, oplist, snapshot_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SNAPSHOT_SPMC_DEF): the given argument is not a valid oplist: " #oplist) + +/* Expand the type and the functions of a SPMC snapshot */ +#define M_SNAPSH0T_SPMC_DEF_P3(name, type, oplist, snapshot_t) \ + M_SNAPSH0T_SPMC_DEF_TYPE(name, type, oplist, snapshot_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_SNAPSH0T_SPMC_DEF_CORE(name, type, oplist, snapshot_t) \ + +/* Define the type */ +#define M_SNAPSH0T_SPMC_DEF_TYPE(name, type, oplist, snapshot_t) \ + \ + /* Create an aligned type to avoid false sharing between threads */ \ + typedef struct M_F(name, _aligned_type_s) { \ + type x; \ + M_CACHELINE_ALIGN(align, type); \ + } M_F(name, _aligned_type_ct); \ + \ + typedef struct M_F(name, _s) { \ + M_F(name, _aligned_type_ct) *data; \ + m_snapsh0t_mrsw_ct core; \ + } snapshot_t[1]; \ + \ + /* Define internal types for oplist */ \ + typedef snapshot_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + +/* Define the core functions */ +#define M_SNAPSH0T_SPMC_DEF_CORE(name, type, oplist, snapshot_t) \ + \ + M_INLINE void \ + M_F(name, _init)(snapshot_t snap, size_t nReader) \ + { \ + M_ASSERT (snap != NULL); \ + M_ASSERT (nReader > 0 && nReader <= M_SNAPSH0T_SPMC_MAX_READER); \ + snap->data = M_CALL_REALLOC(oplist, M_F(name, _aligned_type_ct), \ + NULL, nReader+M_SNAPSH0T_SPMC_EXTRA_BUFFER); \ + if (M_UNLIKELY_NOMEM (snap->data == NULL)) { \ + M_MEMORY_FULL(sizeof(M_F(name, _aligned_type_ct)) * \ + (nReader+M_SNAPSH0T_SPMC_EXTRA_BUFFER)); \ + return; \ + } \ + for(size_t i = 0; i < nReader + M_SNAPSH0T_SPMC_EXTRA_BUFFER; i++) { \ + M_CALL_INIT(oplist, snap->data[i].x); \ + } \ + m_snapsh0t_mrsw_init(snap->core, nReader); \ + M_SNAPSH0T_SPMC_CONTRACT(snap); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPMC_CONTRACT(snap); \ + size_t nReader = m_snapsh0t_mrsw_size(snap->core); \ + for(size_t i = 0; i < nReader + M_SNAPSH0T_SPMC_EXTRA_BUFFER; i++) { \ + M_CALL_CLEAR(oplist, snap->data[i].x); \ + } \ + M_CALL_FREE(oplist, snap->data); \ + m_snapsh0t_mrsw_clear(snap->core); \ + } \ + \ + M_INLINE type * \ + M_F(name, _write)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPMC_CONTRACT(snap); \ + const unsigned int idx = m_snapsh0t_mrsw_write(snap->core); \ + return &snap->data[idx].x; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _read_start)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPMC_CONTRACT(snap); \ + const unsigned int idx = m_snapsh0t_mrsw_read_start(snap->core); \ + return M_CONST_CAST(type, &snap->data[idx].x); \ + } \ + \ + M_INLINE void \ + M_F(name, _read_end)(snapshot_t snap, type const *old) \ + { \ + M_SNAPSH0T_SPMC_CONTRACT(snap); \ + M_ASSERT (old != NULL); \ + const M_F(name, _aligned_type_ct) *oldx; \ + oldx = M_CTYPE_FROM_FIELD(M_F(name, _aligned_type_ct), old, type, x); \ + M_ASSERT (oldx >= snap->data); \ + M_ASSERT (oldx < snap->data + snap->core->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); \ + M_ASSERT(snap->core->n_reader +M_SNAPSH0T_SPMC_EXTRA_BUFFER < UINT_MAX); \ + const unsigned int idx = (unsigned int) (oldx - snap->data); \ + m_snapsh0t_mrsw_read_end(snap->core, idx); \ + } \ + \ + M_INLINE type * \ + M_F(name, _get_write_buffer)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPMC_CONTRACT(snap); \ + const unsigned int idx = m_snapsh0t_mrsw_get_write_idx(snap->core); \ + return &snap->data[idx].x; \ + } \ + \ + + +/********************************** INTERNAL *********************************/ + +// MPMC is built upon SPMC + +// Defered evaluation +#define M_SNAPSH0T_MPMC_DEF_P1(arg) M_ID( M_SNAPSH0T_MPMC_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_SNAPSH0T_MPMC_DEF_P2(name, type, oplist, snapshot_t) \ + M_IF_OPLIST(oplist)(M_SNAPSH0T_MPMC_DEF_P3, M_SNAPSH0T_MPMC_DEF_FAILURE)(name, type, oplist, snapshot_t) + +/* Stop processing with a compilation failure */ +#define M_SNAPSH0T_MPMC_DEF_FAILURE(name, type, oplist, snapshot_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SNAPSHOT_MPMC_DEF): the given argument is not a valid oplist: " #oplist) + +/* Expand the type and the functions of a MPMC snapshot */ +#define M_SNAPSH0T_MPMC_DEF_P3(name, type, oplist, snapshot_t) \ + M_SNAPSH0T_SPMC_DEF_P1((M_F(name, _mrsw), type, oplist, M_F(name, _mrsw_pct))) \ + M_SNAPSH0T_MPMC_DEF_TYPE(name, type, oplist, snapshot_t) \ + M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ + M_SNAPSH0T_MPMC_DEF_CORE(name, type, oplist, snapshot_t) \ + +/* Define the types */ +#define M_SNAPSH0T_MPMC_DEF_TYPE(name, type, oplist, snapshot_t) \ + \ + typedef struct M_F(name, _s) { \ + M_F(name, _mrsw_pct) core; \ + } snapshot_t[1]; \ + \ + /* Define internal types for oplist */ \ + typedef snapshot_t M_F(name, _ct); \ + typedef type M_F(name, _subtype_ct); \ + +/* Define the core functions */ +#define M_SNAPSH0T_MPMC_DEF_CORE(name, type, oplist, snapshot_t) \ + \ + M_INLINE void \ + M_F(name, _init)(snapshot_t snap, size_t nReader, size_t nWriter) \ + { \ + M_F(name, _mrsw_init)(snap->core, nReader + nWriter -1 ); \ + unsigned int idx = snap->core->core->currentWrite; \ + snap->core->core->currentWrite = M_GENINT_ERROR; \ + m_snapsh0t_mrsw_write_end(snap->core->core, idx); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(snapshot_t snap) \ + { \ + M_F(name, _mrsw_clear)(snap->core); \ + } \ + \ + M_INLINE type * \ + M_F(name, _write_start)(snapshot_t snap) \ + { \ + M_SNAPSH0T_SPMC_CONTRACT(snap->core); \ + const unsigned int idx = m_snapsh0t_mrsw_write_start(snap->core->core); \ + return &snap->core->data[idx].x; \ + } \ + \ + M_INLINE void \ + M_F(name, _write_end)(snapshot_t snap, type *old) \ + { \ + M_SNAPSH0T_SPMC_CONTRACT(snap->core); \ + const M_F(name, _mrsw_aligned_type_ct) *oldx; \ + oldx = M_CTYPE_FROM_FIELD(M_F(name, _mrsw_aligned_type_ct), old, type, x); \ + M_ASSERT (oldx >= snap->core->data); \ + M_ASSERT (oldx < snap->core->data + snap->core->core->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); \ + M_ASSERT(snap->core->core->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER < UINT_MAX); \ + const unsigned int idx = (unsigned int) (oldx - snap->core->data); \ + m_snapsh0t_mrsw_write_end(snap->core->core, idx); \ + } \ + \ + M_INLINE type const * \ + M_F(name, _read_start)(snapshot_t snap) \ + { \ + return M_F(name, _mrsw_read_start)(snap->core); \ + } \ + \ + M_INLINE void \ + M_F(name, _read_end)(snapshot_t snap, type const *old) \ + { \ + M_F(name, _mrsw_read_end)(snap->core, old); \ + } \ + \ + +//FIXME: Evaluate the needs for the methods _set_, _init_set. + +M_END_PROTECTED_CODE + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define SNAPSHOT_SPSC_DEF M_SNAPSHOT_SPSC_DEF +#define SNAPSHOT_SPSC_DEF_AS M_SNAPSHOT_SPSC_DEF_AS +#define SNAPSHOT_SPMC_DEF M_SNAPSHOT_SPMC_DEF +#define SNAPSHOT_SPMC_DEF_AS M_SNAPSHOT_SPMC_DEF_AS +#define SNAPSHOT_MPMC_DEF M_SNAPSHOT_MPMC_DEF +#define SNAPSHOT_MPMC_DEF_AS M_SNAPSHOT_MPMC_DEF_AS +#define SNAPSHOT_OPLIST M_SNAPSHOT_OPLIST +#endif + +#endif diff --git a/components/mlib/m-string.h b/components/mlib/m-string.h new file mode 100644 index 00000000..501ac1bb --- /dev/null +++ b/components/mlib/m-string.h @@ -0,0 +1,2787 @@ +/* + * M*LIB - Dynamic Size String Module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MSTARLIB_STRING_H +#define MSTARLIB_STRING_H + +#include "m-core.h" + +M_BEGIN_PROTECTED_CODE + +/********************************** INTERNAL ************************************/ + +// This macro defines the contract of a string. +// Note: A ==> B is represented as not(A) or B +// Note: use of strlen can slow down a lot the program in some cases. +#define M_STR1NG_CONTRACT(v) do { \ + M_ASSERT (v != NULL); \ + M_ASSERT_SLOW (m_string_size(v) == strlen(m_string_get_cstr(v))); \ + M_ASSERT (m_string_get_cstr(v)[m_string_size(v)] == 0); \ + M_ASSERT (m_string_size(v) < m_string_capacity(v)); \ + M_ASSERT (m_string_capacity(v) < sizeof (m_str1ng_heap_ct) || !m_str1ng_stack_p(v)); \ + } while(0) + +// string if it is heap allocated +typedef struct m_str1ng_heap_s { + size_t size; + size_t alloc; +} m_str1ng_heap_ct; +// string if it is stack allocated +typedef struct m_str1ng_stack_s { + char buffer[sizeof (m_str1ng_heap_ct)]; +} m_str1ng_stack_ct; +// both cases of string are possible +typedef union m_str1ng_union_u { + m_str1ng_heap_ct heap; + m_str1ng_stack_ct stack; +} m_str1ng_union_ct; + +/* State of the UTF8 decoding machine state */ +typedef enum { + M_STR1NG_UTF8_STARTING = 0, + M_STR1NG_UTF8_DECODING_1 = 8, + M_STR1NG_UTF8_DECODING_2 = 16, + M_STR1NG_UTF8_DECODING_3 = 24, + M_STR1NG_UTF8_ERROR = 32 +} m_str1ng_utf8_state_e; + +/* Maximum number of bytes per UTF8 code point */ +#define M_STR1NG_MAX_BYTE_UTF8 4 + +/* Contract for a string iterator */ +#define M_STR1NG_IT_CONTRACT(it) do { \ + M_ASSERT( (it) != NULL); \ + M_ASSERT( (it)->ptr != NULL); \ + M_ASSERT( (it)->string != NULL); \ + M_ASSERT( m_string_get_cstr((it)->string) <= (it)->ptr); \ + M_ASSERT( (it)->ptr <= &m_string_get_cstr((it)->string)[m_string_size((it)->string)]); \ +} while (0) + + +/****************************** EXTERNAL *******************************/ + +/***********************************************************************/ +/* */ +/* DYNAMIC STRING */ +/* */ +/***********************************************************************/ + +// The default behavior of M*LIB is to use the C library +// and not rewrite the algorithms. However, the fast code +// generates also smaller code, so we'll use it. +#ifndef M_USE_FAST_STRING_CONV +#define M_USE_FAST_STRING_CONV 1 +#endif + +/* Index returned in case of error instead of the position within the string */ +#define M_STRING_FAILURE ((size_t)-1) + +/* This is the main structure of this module, representing a dynamic string */ +typedef struct m_string_s { + m_str1ng_union_ct u; + char *ptr; +} m_string_t[1]; + +// Pointer to a Dynamic string +typedef struct m_string_s *m_string_ptr; + +// Constant pointer to a Dynamic string +typedef const struct m_string_s *m_string_srcptr; + +/* Input option for m_string_fgets + M_STRING_READ_LINE (read line), + M_STRING_READ_PURE_LINE (read line and remove final CR and/or LF) + M_STRING_READ_FILE (read all file) +*/ +typedef enum m_string_fgets_e { + M_STRING_READ_LINE = 0, M_STRING_READ_PURE_LINE = 1, M_STRING_READ_FILE = 2 +} m_string_fgets_t; + +/* An unicode code point */ +typedef uint32_t m_string_unicode_t; + +/* Error in case of decoding */ +#define M_STRING_UNICODE_ERROR (UINT_MAX) + +/* Iterator on a string over UTF8 encoded code points */ +typedef struct m_string_it_s { + m_string_unicode_t u; // Decoded Unicode code point for the iterator + const char *ptr; + m_string_srcptr string; +} m_string_it_t[1]; + +/* PREFIX: + m_str1ng_: private methods + m_string_: public methods +*/ + +/* Internal method to test if the string is stack based or heap based + We test if the ptr is NULL or not. + This is not particularly efficient from a memory point of view + (as we could reuse the pointer field to store some data) + but it should be enough for most of "small" strings as most + of the strings are less than 14 characters (64 bits architecture). + Moreover it generates quite efficient code. + NOTE: We set the pointer to NULL (instead of storing a pointer to + the stack buffer for example) for stack based allocation so that + the string structure remains trivially movable (which is an important + property to have). + */ +M_INLINE bool +m_str1ng_stack_p(const m_string_t s) +{ + // Function can be called when contract is not fulfilled + return (s->ptr == NULL); +} + +/* Internal method to set the size of the string (excluding final nul char) */ +M_INLINE void +m_str1ng_set_size(m_string_t s, size_t size) +{ + // Function can be called when contract is not fulfilled + if (m_str1ng_stack_p(s)) { + M_ASSERT (size < sizeof (m_str1ng_heap_ct) - 1); + // The size of the string is stored as the last char of the buffer. + s->u.stack.buffer[sizeof (m_str1ng_heap_ct) - 1] = (char) size; + } else { + s->u.heap.size = size; + } +} + +/* Return the number of bytes of the string (excluding the final nul char) */ +M_INLINE size_t +m_string_size(const m_string_t s) +{ + // Function can be called when contract is not fulfilled + // Reading both values before calling the '?' operator enables compiler to generate branchless code + const size_t s_stack = (size_t) s->u.stack.buffer[sizeof (m_str1ng_heap_ct) - 1]; + const size_t s_heap = s->u.heap.size; + return m_str1ng_stack_p(s) ? s_stack : s_heap; +} + +/* Return the capacity of the string (including the final nul char) */ +M_INLINE size_t +m_string_capacity(const m_string_t s) +{ + // Function can be called when contract is not fulfilled + // Reading both values before calling the '?' operator enables compiler to generate branchless code + const size_t c_stack = sizeof (m_str1ng_heap_ct) - 1; + const size_t c_heap = s->u.heap.alloc; + return m_str1ng_stack_p(s) ? c_stack : c_heap; +} + +/* Return a writable pointer to the array of char of the string */ +M_INLINE char* +m_str1ng_get_cstr(m_string_t v) +{ + // Function can be called when contract is not fulfilled + char *const ptr_stack = &v->u.stack.buffer[0]; + char *const ptr_heap = v->ptr; + return m_str1ng_stack_p(v) ? ptr_stack : ptr_heap; +} + +/* Return the string view a classic C string (const char *) */ +M_INLINE const char* +m_string_get_cstr(const m_string_t v) +{ + // Function cannot be called when contract is not fulfilled + // but it is called by contract (so no contract check to avoid infinite recursion). + const char *const ptr_stack = &v->u.stack.buffer[0]; + const char *const ptr_heap = v->ptr; + return m_str1ng_stack_p(v) ? ptr_stack : ptr_heap; +} + +/* Initialize the dynamic string (constructor) + and make it empty */ +M_INLINE void +m_string_init(m_string_t s) +{ + s->ptr = NULL; + s->u.stack.buffer[0] = 0; + m_str1ng_set_size(s, 0); + M_STR1NG_CONTRACT(s); +} + +/* Clear the Dynamic string (destructor) */ +M_INLINE void +m_string_clear(m_string_t v) +{ + M_STR1NG_CONTRACT(v); + if (!m_str1ng_stack_p(v)) { + M_MEMORY_FREE(v->ptr); + v->ptr = NULL; + } + /* This is not needed but is safer to make + the string invalid so that it can be detected. */ + v->u.stack.buffer[sizeof (m_str1ng_heap_ct) - 1] = CHAR_MAX; +} + +/* NOTE: Internaly used by M_STRING_DECL_INIT */ +M_INLINE void m_str1ng_clear2(m_string_t *v) { m_string_clear(*v); } +M_INLINE m_string_ptr m_str1ng_init_ref(m_string_t v) { m_string_init(v); return v; } + +/* Clear the Dynamic string (destructor) + and return a heap pointer to the string. + The ownership of the data is transfered back to the caller + and the returned pointer has to be released by M_MEMORY_FREE. */ +M_INLINE char * +m_string_clear_get_cstr(m_string_t v) +{ + M_STR1NG_CONTRACT(v); + char *p = v->ptr; + if (m_str1ng_stack_p(v)) { + // The string was stack allocated. + p = v->u.stack.buffer; + // Need to allocate a heap string to return the copy. + size_t alloc = m_string_size(v)+1; + char *ptr = M_MEMORY_REALLOC (char, NULL, alloc); + if (M_UNLIKELY_NOMEM (ptr == NULL)) { + M_MEMORY_FULL(sizeof (char) * alloc); + return NULL; + } + M_ASSERT(ptr != NULL && p != NULL); + memcpy(ptr, p, alloc); + p = ptr; + } + v->ptr = NULL; + v->u.stack.buffer[sizeof (m_str1ng_heap_ct) - 1] = CHAR_MAX; + return p; +} + +/* Make the string empty */ +M_INLINE void +m_string_reset(m_string_t v) +{ + M_STR1NG_CONTRACT (v); + m_str1ng_set_size(v, 0); + m_str1ng_get_cstr(v)[0] = 0; + M_STR1NG_CONTRACT (v); +} + +/* Return the selected byte-character of the string */ +M_INLINE char +m_string_get_char(const m_string_t v, size_t index) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT_INDEX(index, m_string_size(v)); + return m_string_get_cstr(v)[index]; +} + +/* Set the selected byte-character of the string */ +M_INLINE void +m_string_set_char(m_string_t v, size_t index, const char c) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT_INDEX(index, m_string_size(v)); + m_str1ng_get_cstr(v)[index] = c; +} + +/* Test if the string is empty or not */ +M_INLINE bool +m_string_empty_p(const m_string_t v) +{ + M_STR1NG_CONTRACT (v); + return m_string_size(v) == 0; +} + +/* Internal method to fit the string to the given size + Ensures that the string capacity is greater than size_alloc + (size_alloc shall include the final null char). + It may move the string from stack based to heap based. + Return a pointer to the writable string. +*/ +M_INLINE char * +m_str1ng_fit2size (m_string_t v, size_t size_alloc) +{ + M_ASSERT_INDEX (0, size_alloc); + // Note: this function may be called in context where the contract + // is not fulfilled. + const size_t old_alloc = m_string_capacity(v); + // This line enables the compiler to completly remove this function + // for very short constant strings. + M_ASSUME(old_alloc >= sizeof (m_str1ng_heap_ct) - 1); + if (M_UNLIKELY (size_alloc > old_alloc)) { + // Insufficient current allocation to store the new string + // Perform an allocation on the heap. + size_t alloc = size_alloc + size_alloc / 2; + if (M_UNLIKELY_NOMEM (alloc <= size_alloc)) { + /* Overflow in alloc computation */ + M_MEMORY_FULL(sizeof (char) * alloc); + // NOTE: Return is currently broken. + abort(); + return NULL; + } + char *ptr = M_MEMORY_REALLOC (char, v->ptr, alloc); + if (M_UNLIKELY_NOMEM (ptr == NULL)) { + M_MEMORY_FULL(sizeof (char) * alloc); + // NOTE: Return is currently broken. + abort(); + return NULL; + } + // The pointer cannot be the stack buffer of the string. + // as it is heap allocated + M_ASSERT(ptr != &v->u.stack.buffer[0]); + if (m_str1ng_stack_p(v)) { + // The string was stack allocated. + /* Copy the stack allocation into the new heap allocation */ + const size_t size = (size_t) v->u.stack.buffer[sizeof (m_str1ng_heap_ct) - 1] + 1U; + M_ASSERT( size <= alloc); + M_ASSERT( size <= sizeof (v->u.stack.buffer)-1); + memcpy(ptr, &v->u.stack.buffer[0], size); + } + // The string cannot be stack allocated anymore. + v->ptr = ptr; + v->u.heap.alloc = alloc; + return ptr; + } + return m_str1ng_get_cstr(v); +} + +/* Modify the string capacity to be able to handle at least 'alloc' + characters (including final nul char). + It may reduce the allocation of the string if possible */ +M_INLINE void +m_string_reserve(m_string_t v, size_t alloc) +{ + M_STR1NG_CONTRACT (v); + const size_t size = m_string_size(v); + /* NOTE: Reserve below needed size, perform a shrink to fit */ + if (size + 1 > alloc) { + alloc = size+1; + } + M_ASSERT (alloc > 0); + if (alloc < sizeof (m_str1ng_heap_ct)) { + // Allocation can fit in the stack space + if (!m_str1ng_stack_p(v)) { + /* Transform Heap Allocate to Stack Allocate */ + char *ptr = &v->u.stack.buffer[0]; + memcpy(ptr, v->ptr, size+1); + M_MEMORY_FREE(v->ptr); + v->ptr = NULL; + m_str1ng_set_size(v, size); + } else { + /* Already a stack based alloc: nothing to do */ + } + } else { + // Allocation cannot fit in the stack space + // Need to allocate in heap space + // If the string is stack allocated, v->ptr is NULL + // and it will therefore perform the initial allocation + char *ptr = M_MEMORY_REALLOC (char, v->ptr, alloc); + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { + M_MEMORY_FULL(sizeof (char) * alloc); + return; + } + if (m_str1ng_stack_p(v)) { + // Copy from stack space to heap space the string + char *ptr_stack = &v->u.stack.buffer[0]; + memcpy(ptr, ptr_stack, size+1); + v->u.heap.size = size; + } + v->ptr = ptr; + v->u.heap.alloc = alloc; + } + M_STR1NG_CONTRACT (v); +} + +/* Set the string to the C string str */ +M_INLINE void +m_string_set_cstr(m_string_t v, const char str[]) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT(str != NULL); + size_t size = strlen(str); + char *ptr = m_str1ng_fit2size(v, size+1); + // The memcpy will also copy the final null char of the string + memcpy(ptr, str, size+1); + m_str1ng_set_size(v, size); + M_STR1NG_CONTRACT (v); +} + +/* Set the string to the n first characters of the C string str */ +M_INLINE void +m_string_set_cstrn(m_string_t v, const char str[], size_t n) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT(str != NULL); + size_t len = strlen(str); + size_t size = M_MIN (len, n); + char *ptr = m_str1ng_fit2size(v, size+1); + // The memcpy will not copy the final null char of the string + memcpy(ptr, str, size); + ptr[size] = 0; + m_str1ng_set_size(v, size); + M_STR1NG_CONTRACT (v); +} + +/* Set the string to the other one */ +M_INLINE void +m_string_set (m_string_t v1, const m_string_t v2) +{ + M_STR1NG_CONTRACT (v1); + M_STR1NG_CONTRACT (v2); + if (M_LIKELY (v1 != v2)) { + const size_t size = m_string_size(v2); + char *ptr = m_str1ng_fit2size(v1, size+1); + memcpy(ptr, m_string_get_cstr(v2), size+1); + m_str1ng_set_size(v1, size); + } + M_STR1NG_CONTRACT (v1); +} + +/* Set the string to the n first characters of other one */ +M_INLINE void +m_string_set_n(m_string_t v, const m_string_t ref, size_t offset, size_t length) +{ + M_STR1NG_CONTRACT (v); + M_STR1NG_CONTRACT (ref); + M_ASSERT_INDEX (offset, m_string_size(ref) + 1); + size_t size = M_MIN (m_string_size(ref) - offset, length); + char *ptr = m_str1ng_fit2size(v, size+1); + // v may be equal to ref, so a memmove is needed instead of a memcpy + memmove(ptr, m_string_get_cstr(ref) + offset, size); + ptr[size] = 0; + m_str1ng_set_size(v, size); + M_STR1NG_CONTRACT (v); +} + +/* Initialize the string and set it to the other one + (constructor) */ +M_INLINE void +m_string_init_set(m_string_t v1, const m_string_t v2) +{ + m_string_init(v1); + m_string_set(v1,v2); +} + +/* Initialize the string and set it to the C string + (constructor) */ +M_INLINE void +m_string_init_set_cstr(m_string_t v1, const char str[]) +{ + m_string_init(v1); + m_string_set_cstr(v1, str); +} + +/* Initialize the string, set it to the other one, + and destroy the other one. + (constructor & destructor) */ +M_INLINE void +m_string_init_move(m_string_t v1, m_string_t v2) +{ + M_STR1NG_CONTRACT (v2); + memcpy(v1, v2, sizeof (m_string_t)); + // Note: nullify v2 to be safer + v2->ptr = NULL; + M_STR1NG_CONTRACT (v1); +} + +/* Swap the two strings v1 and v2 */ +M_INLINE void +m_string_swap(m_string_t v1, m_string_t v2) +{ + M_STR1NG_CONTRACT (v1); + M_STR1NG_CONTRACT (v2); + // Even if it is stack based, we swap the heap representation + // which alias the stack based + M_SWAP (size_t, v1->u.heap.size, v2->u.heap.size); + M_SWAP (size_t, v1->u.heap.alloc, v2->u.heap.alloc); + M_SWAP (char *, v1->ptr, v2->ptr); + M_STR1NG_CONTRACT (v1); + M_STR1NG_CONTRACT (v2); +} + +/* Set the string to the other one, + and destroy the other one. + (destructor) */ +M_INLINE void +m_string_move(m_string_t v1, m_string_t v2) +{ + m_string_clear(v1); + m_string_init_move(v1,v2); +} + +/* Push the byte-character 'c' in the string 'v' */ +M_INLINE void +m_string_push_back (m_string_t v, char c) +{ + M_STR1NG_CONTRACT (v); + const size_t size = m_string_size(v); + char *ptr = m_str1ng_fit2size(v, size+2); + ptr[size+0] = c; + ptr[size+1] = 0; + m_str1ng_set_size(v, size+1); + M_STR1NG_CONTRACT (v); +} + +/* Concatene the string with the C string */ +M_INLINE void +m_string_cat_cstr(m_string_t v, const char str[]) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (str != NULL); + const size_t old_size = m_string_size(v); + const size_t size = strlen(str); + char *ptr = m_str1ng_fit2size(v, old_size + size + 1); + memcpy(&ptr[old_size], str, size + 1); + m_str1ng_set_size(v, old_size + size); + M_STR1NG_CONTRACT (v); +} + +/* Concatene the string with the other string */ +M_INLINE void +m_string_cat(m_string_t v, const m_string_t v2) +{ + M_STR1NG_CONTRACT (v2); + M_STR1NG_CONTRACT (v); + const size_t size = m_string_size(v2); + if (M_LIKELY (size > 0)) { + const size_t old_size = m_string_size(v); + char *ptr = m_str1ng_fit2size(v, old_size + size + 1); + memcpy(&ptr[old_size], m_string_get_cstr(v2), size); + ptr[old_size + size] = 0; + m_str1ng_set_size(v, old_size + size); + } + M_STR1NG_CONTRACT (v); +} + +/* Compare the string to the C string and + return the sort order (negative if less, 0 if equal, positive if greater) */ +M_INLINE int +m_string_cmp_cstr(const m_string_t v1, const char str[]) +{ + M_STR1NG_CONTRACT (v1); + M_ASSERT (str != NULL); + return strcmp(m_string_get_cstr(v1), str); +} + +/* Compare the string to the other string and + return the sort order (negative if less, 0 if equal, positive if greater) */ +M_INLINE int +m_string_cmp(const m_string_t v1, const m_string_t v2) +{ + M_STR1NG_CONTRACT (v1); + M_STR1NG_CONTRACT (v2); + return strcmp(m_string_get_cstr(v1), m_string_get_cstr(v2)); +} + +/* Test if the string is equal to the given C string */ +M_INLINE bool +m_string_equal_cstr_p(const m_string_t v1, const char str[]) +{ + M_STR1NG_CONTRACT(v1); + M_ASSERT (str != NULL); + return m_string_cmp_cstr(v1, str) == 0; +} + +/* Test if the string is equal to the other string */ +M_INLINE bool +m_string_equal_p(const m_string_t v1, const m_string_t v2) +{ + /* m_string_equal_p can be called with (at most) one string which is an OOR value. + In case of OOR value, .ptr is NULL and .alloc is the maximum or the maximum-1. + As it will detect a stack based string, it will read the size from the alloc fied + as 0xFF or 0xFE. In both cases, the size cannot be equal to a normal string + so the test m_string_size(v1) == m_string_size(v2) is false in this case. + */ + M_ASSERT(v1 != NULL); + M_ASSERT(v2 != NULL); + /* Optimization: both strings shall have at least the same size */ + return m_string_size(v1) == m_string_size(v2) && m_string_cmp(v1, v2) == 0; +} + +/* Test if the string is equal to the C string + (case insentive according to the current locale) + Note: doesn't work with UTF-8 strings. +*/ +M_INLINE int +m_string_cmpi_cstr(const m_string_t v1, const char p2[]) +{ + M_STR1NG_CONTRACT (v1); + M_ASSERT (p2 != NULL); + // strcasecmp is POSIX only + const char *p1 = m_string_get_cstr(v1); + int c1, c2; + do { + // To avoid locale without 1 per 1 mapping. + c1 = toupper((unsigned char) *p1++); + c2 = toupper((unsigned char) *p2++); + c1 = tolower((unsigned char) c1); + c2 = tolower((unsigned char) c2); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} + +/* Test if the string is equal to the other string + (case insentive according to the current locale) + Note: doesn't work with UTF-8 strings. +*/ +M_INLINE int +m_string_cmpi(const m_string_t v1, const m_string_t v2) +{ + return m_string_cmpi_cstr(v1, m_string_get_cstr(v2)); +} + +/* Search for the position of the character c + from the position 'start' (include) in the string + Return M_STRING_FAILURE if not found. + By default, start is zero. +*/ +M_INLINE size_t +m_string_search_char (const m_string_t v, char c, size_t start) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT_INDEX (start, m_string_size(v) + 1); + const char *p = M_ASSIGN_CAST(const char*, + strchr(m_string_get_cstr(v)+start, c)); + return p == NULL ? M_STRING_FAILURE : (size_t) (p-m_string_get_cstr(v)); +} + +/* Reverse Search for the position of the character c + from the position 'start' (include) in the string + Return M_STRING_FAILURE if not found. + By default, start is zero. +*/ +M_INLINE size_t +m_string_search_rchar (const m_string_t v, char c, size_t start) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT_INDEX (start, m_string_size(v)+1); + // NOTE: Can be implemented in a faster way than the libc function + // by scanning backward from the bottom of the string (which is + // possible since we know the size). + // However, does it worth the effort? + const char *p = M_ASSIGN_CAST(const char*, + strrchr(m_string_get_cstr(v)+start, c)); + return p == NULL ? M_STRING_FAILURE : (size_t) (p-m_string_get_cstr(v)); +} + +/* Search for the sub C string in the string from the position start + Return M_STRING_FAILURE if not found. + By default, start is zero. */ +M_INLINE size_t +m_string_search_cstr(const m_string_t v, const char str[], size_t start) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT_INDEX (start, m_string_size(v)+1); + M_ASSERT (str != NULL); + const char *p = M_ASSIGN_CAST(const char*, + strstr(m_string_get_cstr(v)+start, str)); + return p == NULL ? M_STRING_FAILURE : (size_t) (p-m_string_get_cstr(v)); +} + +/* Search for the sub other string v2 in the string v1 from the position start + Return M_STRING_FAILURE if not found. + By default, start is zero. */ +M_INLINE size_t +m_string_search (const m_string_t v1, const m_string_t v2, size_t start) +{ + M_STR1NG_CONTRACT (v2); + M_ASSERT_INDEX (start, m_string_size(v1)+1); + return m_string_search_cstr(v1, m_string_get_cstr(v2), start); +} + +/* Search for the first matching character in the given C string + in the string v1 from the position start + Return M_STRING_FAILURE if not found. + By default, start is zero. */ +M_INLINE size_t +m_string_search_pbrk(const m_string_t v1, const char first_of[], size_t start) +{ + M_STR1NG_CONTRACT (v1); + M_ASSERT_INDEX (start, m_string_size(v1)+1); + M_ASSERT (first_of != NULL); + const char *p = M_ASSIGN_CAST(const char*, + strpbrk(m_string_get_cstr(v1)+start, first_of)); + return p == NULL ? M_STRING_FAILURE : (size_t) (p-m_string_get_cstr(v1)); +} + +/* Compare the string to the C string using strcoll */ +M_INLINE int +m_string_strcoll_cstr(const m_string_t v, const char str[]) +{ + M_STR1NG_CONTRACT (v); + return strcoll(m_string_get_cstr(v), str); +} + +/* Compare the string to the other string using strcoll */ +M_INLINE int +m_string_strcoll (const m_string_t v1, const m_string_t v2) +{ + M_STR1NG_CONTRACT (v2); + return m_string_strcoll_cstr(v1, m_string_get_cstr(v2)); +} + +/* Return the number of bytes of the segment of s + that consists entirely of bytes in accept */ +M_INLINE size_t +m_string_spn(const m_string_t v1, const char accept[]) +{ + M_STR1NG_CONTRACT (v1); + M_ASSERT (accept != NULL); + return strspn(m_string_get_cstr(v1), accept); +} + +/* Return the number of bytes of the segment of s + that consists entirely of bytes not in reject */ +M_INLINE size_t +m_string_cspn(const m_string_t v1, const char reject[]) +{ + M_STR1NG_CONTRACT (v1); + M_ASSERT (reject != NULL); + return strcspn(m_string_get_cstr(v1), reject); +} + +/* Return the string left truncated to the first 'index' bytes */ +M_INLINE void +m_string_left(m_string_t v, size_t index) +{ + M_STR1NG_CONTRACT (v); + const size_t size = m_string_size(v); + if (index >= size) + return; + m_str1ng_get_cstr(v)[index] = 0; + m_str1ng_set_size(v,index); + M_STR1NG_CONTRACT (v); +} + +/* Return the string right truncated from the 'index' position to the last position */ +M_INLINE void +m_string_right(m_string_t v, size_t index) +{ + M_STR1NG_CONTRACT (v); + char *ptr = m_str1ng_get_cstr(v); + const size_t size = m_string_size(v); + if (index >= size) { + ptr[0] = 0; + m_str1ng_set_size(v, 0); + M_STR1NG_CONTRACT (v); + return; + } + size_t s2 = size - index; + memmove (&ptr[0], &ptr[index], s2+1); + m_str1ng_set_size(v, s2); + M_STR1NG_CONTRACT (v); +} + +/* Return the string from position index to size bytes. + See also m_string_set_n + */ +M_INLINE void +m_string_mid (m_string_t v, size_t index, size_t size) +{ + m_string_right(v, index); + m_string_left(v, size); +} + +/* Replace in the string the firt occurence of the C string str1 + into the C string str2 from start + By default, start is zero. +*/ +M_INLINE size_t +m_string_replace_cstr (m_string_t v, const char str1[], const char str2[], size_t start) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (str1 != NULL && str2 != NULL); + size_t i = m_string_search_cstr(v, str1, start); + if (i != M_STRING_FAILURE) { + const size_t str1_l = strlen(str1); + const size_t str2_l = strlen(str2); + const size_t size = m_string_size(v); + M_ASSERT(size + 1 + str2_l > str1_l); + char *ptr = m_str1ng_fit2size (v, size + str2_l - str1_l + 1); + if (str1_l != str2_l) { + memmove(&ptr[i+str2_l], &ptr[i+str1_l], size - i - str1_l + 1); + m_str1ng_set_size(v, size + str2_l - str1_l); + } + memcpy (&ptr[i], str2, str2_l); + M_STR1NG_CONTRACT (v); + } + return i; +} + +/* Replace in the string the firt occurence of the C string v1 + into the C string v2 from start + By default, start is zero. +*/ +M_INLINE size_t +m_string_replace (m_string_t v, const m_string_t v1, const m_string_t v2, size_t start) +{ + M_STR1NG_CONTRACT (v); + M_STR1NG_CONTRACT (v1); + M_STR1NG_CONTRACT (v2); + return m_string_replace_cstr(v, m_string_get_cstr(v1), m_string_get_cstr(v2), start); +} + +/* Replace in the string the sub-string at position 'pos' for 'len' bytes + into the C string str2. */ +M_INLINE void +m_string_replace_at (m_string_t v, size_t pos, size_t len, const char str2[]) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT(str2 != NULL); + const size_t str1_l = len; + const size_t str2_l = strlen(str2); + const size_t size = m_string_size(v); + char *ptr; + if (str1_l != str2_l) { + // Move bytes from the string + M_ASSERT_INDEX (str1_l, size + str2_l + 1); + ptr = m_str1ng_fit2size (v, size + str2_l - str1_l + 1); + M_ASSERT_INDEX (pos + str1_l, size + 1); + M_ASSUME (pos + str1_l < size + 1); + memmove(&ptr[pos+str2_l], &ptr[pos+str1_l], size - pos - str1_l + 1); + m_str1ng_set_size(v, size + str2_l - str1_l); + } else { + ptr = m_str1ng_get_cstr(v); + } + memcpy (&ptr[pos], str2, str2_l); + M_STR1NG_CONTRACT (v); +} + +/* Replace all occurences of str1 into str2 when strlen(str1) >= strlen(str2) */ +M_INLINE void +m_str1ng_replace_all_cstr_1ge2 (m_string_t v, const char str1[], size_t str1len, const char str2[], size_t str2len) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT(str1len >= str2len); + + /* str1len >= str2len so the string doesn't need to be resized */ + size_t vlen = m_string_size(v); + char *org = m_str1ng_get_cstr(v); + char *src = org; + char *dst = org; + + // Go through all the characters of the string + while (*src != 0) { + // Get a new occurrence of str1 in the v string. + char *occ = strstr(src, str1); + if (occ == NULL) { + // No new occurrence + break; + } + M_ASSERT(occ >= src); + // Copy the data until the new occurrence + if (src != dst) { + memmove(dst, src, (size_t) (occ - src)); + } + dst += (occ - src); + // Copy the replaced string + memcpy(dst, str2, str2len); + dst += str2len; + // Advance src pointer + src = occ + str1len; + } + // Finish copying the string until the end + M_ASSERT (src <= org + vlen ); + if (src != dst) { + memmove(dst, src, (size_t) (org + vlen + 1 - src) ); + } + // Update string size + m_str1ng_set_size(v, (size_t) (dst + vlen - src) ); + M_STR1NG_CONTRACT (v); +} + +/* Reverse strstr from the end of the string + org is the start of the string + src is the current character pointer (shall be initialized to the end of the string) + pattern / pattern_size: the pattern to search. + */ +M_INLINE char * +m_str1ng_strstr_r(char org[], char src[], const char pattern[], size_t pattern_size) +{ + M_ASSERT(pattern_size >= 1); + src -= pattern_size - 1; + while (org <= src) { + if (src[0] == pattern[0] + && src[pattern_size-1] == pattern[pattern_size-1] + && memcmp(src, pattern, pattern_size) == 0) { + return src; + } + src --; + } + return NULL; +} + +/* Replace all occurences of str1 into str2 when strlen(str1) < strlen(str2) */ +M_INLINE void +m_str1ng_replace_all_cstr_1lo2 (m_string_t v, const char str1[], size_t str1len, const char str2[], size_t str2len) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT(str1len < str2len); + + /* str1len < str2len so the string may need to be resized + Worst case if when v is composed fully of str1 substrings. + Needed size : v.size / str1len * str2len + */ + size_t vlen = m_string_size(v); + size_t alloc = 1 + vlen / str1len * str2len; + char *org = m_str1ng_fit2size(v, alloc); + char *src = org + vlen - 1; + char *end = org + m_string_capacity(v); + char *dst = end; + + // Go through all the characters of the string in reverse ! + while (src >= org) { + char *occ = m_str1ng_strstr_r(org, src, str1, str1len); + if (occ == NULL) { + break; + } + M_ASSERT(occ + str1len - 1 <= src); + // Copy the data until the new occurrence + dst -= (src - (occ + str1len - 1)); + memmove(dst, occ+str1len, (size_t) (src - (occ + str1len - 1))); + // Copy the replaced string + dst -= str2len; + memcpy(dst, str2, str2len); + // Advance src pointer + src = occ - 1; + } + // Finish moving data back to their natural place + memmove(src + 1, dst, (size_t) (end - dst) ); + // Update string size + vlen = (size_t) (src - org + end - dst + 1); + org[vlen] = 0; + m_str1ng_set_size(v, vlen ); + M_STR1NG_CONTRACT (v); +} + +M_INLINE void +m_string_replace_all_cstr (m_string_t v, const char str1[], const char str2[]) +{ + size_t str1_l = strlen(str1); + size_t str2_l = strlen(str2); + M_ASSERT(str1_l > 0); + if (str1_l >= str2_l) { + m_str1ng_replace_all_cstr_1ge2(v, str1, str1_l, str2, str2_l ); + } else { + m_str1ng_replace_all_cstr_1lo2(v, str1, str1_l, str2, str2_l ); + } +} + +M_INLINE void +m_string_replace_all (m_string_t v, const m_string_t str1, const m_string_t str2) +{ + size_t str1_l = m_string_size(str1); + size_t str2_l = m_string_size(str2); + M_ASSERT(str1_l > 0); + M_ASSERT(str2_l > 0); + if (str1_l >= str2_l) { + m_str1ng_replace_all_cstr_1ge2(v, m_string_get_cstr(str1), str1_l, m_string_get_cstr(str2), str2_l ); + } else { + m_str1ng_replace_all_cstr_1lo2(v, m_string_get_cstr(str1), str1_l, m_string_get_cstr(str2), str2_l ); + } +} + +// Define the fast integer to string conversions if requested +// or if no support for stdarg. +#if M_USE_FAST_STRING_CONV == 1 || M_USE_STDARG == 0 + + // Compute the maximum number of characters needed for the buffer. +#if UINT_MAX == 4294967295U +#define M_STR1NG_INT_MAX_SIZE (10+1) +#elif UINT_MAX <= 18446744073709551615UL +#define M_STR1NG_INT_MAX_SIZE (20+1) +#else +# error Unexpected UINT_MAX value (workaround: Define M_USE_FAST_STRING_CONV to 0). +#endif + +M_INLINE void +m_string_set_ui(m_string_t v, unsigned int n) +{ + M_STR1NG_CONTRACT (v); + char buffer[M_STR1NG_INT_MAX_SIZE]; + m_str1ng_fit2size(v, M_STR1NG_INT_MAX_SIZE); + unsigned i = 0, j = 0; + do { + // 0123456789 are mandatory in this order as characters, as per C standard. + buffer[i++] = (char) ('0' + (n % 10U)); + n /= 10U; + } while (n != 0); + M_ASSERT_INDEX(i, M_STR1NG_INT_MAX_SIZE); + char *d = m_str1ng_get_cstr(v); + while (i > 0) { + d[j++] = buffer[--i]; + } + d[j] = 0; + m_str1ng_set_size(v, j); + M_STR1NG_CONTRACT (v); +} + +M_INLINE void +m_string_set_si(m_string_t v, int n) +{ + M_STR1NG_CONTRACT (v); + // Compute the maximum number of characters needed for the buffer. + char buffer[M_STR1NG_INT_MAX_SIZE]; + m_str1ng_fit2size(v, M_STR1NG_INT_MAX_SIZE); + unsigned i = 0, j = 0; + bool neg = n < 0; + unsigned n0 = neg ? 0U -(unsigned) n : (unsigned) n; + do { + // 0123456789 are mandatory in this order as characters, as per C standard. + buffer[i++] = (char) ('0' + (n0 % 10U)); + n0 /= 10U; + } while (n0 != 0); + M_ASSERT_INDEX(i, M_STR1NG_INT_MAX_SIZE); + char *d = m_str1ng_get_cstr(v); + if (neg) d[j++] = '-'; + while (i > 0) { + d[j++] = buffer[--i]; + } + d[j] = 0; + m_str1ng_set_size(v, j); + M_STR1NG_CONTRACT (v); +} +#endif + +#if M_USE_STDARG + +/* Format in the string the given printf format */ +M_INLINE int +m_string_vprintf (m_string_t v, const char format[], va_list args) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (format != NULL); + int size; + va_list args_org; + va_copy(args_org, args); + char *ptr = m_str1ng_get_cstr(v); + size_t alloc = m_string_capacity(v); + size = vsnprintf (ptr, alloc, format, args); + if (size > 0 && ((size_t) size+1 >= alloc) ) { + // We have to realloc our string to fit the needed size + ptr = m_str1ng_fit2size (v, (size_t) size + 1); + alloc = m_string_capacity(v); + // and redo the parsing. + size = vsnprintf (ptr, alloc, format, args_org); + M_ASSERT (size > 0 && (size_t)size < alloc); + } + if (M_LIKELY (size >= 0)) { + m_str1ng_set_size(v, (size_t) size); + } else { + // An error occured during the conversion: Assign an empty string. + m_str1ng_set_size(v, 0); + ptr[0] = 0; + } + va_end(args_org); + M_STR1NG_CONTRACT (v); + return size; +} + +/* Format in the string the given printf format */ +M_INLINE int +m_string_printf (m_string_t v, const char format[], ...) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (format != NULL); + va_list args; + va_start (args, format); + int ret = m_string_vprintf(v, format, args); + va_end (args); + return ret; +} + +/* Append to the string the formatted string of the given printf format */ +M_INLINE int +m_string_cat_vprintf (m_string_t v, const char format[], va_list args) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (format != NULL); + va_list args_org; + va_copy(args_org, args); + int size; + size_t old_size = m_string_size(v); + char *ptr = m_str1ng_get_cstr(v); + size_t alloc = m_string_capacity(v); + size = vsnprintf (&ptr[old_size], alloc - old_size, format, args); + if (size > 0 && (old_size+(size_t)size+1 >= alloc) ) { + // We have to realloc our string to fit the needed size + ptr = m_str1ng_fit2size (v, old_size + (size_t) size + 1); + alloc = m_string_capacity(v); + // and redo the parsing. + size = vsnprintf (&ptr[old_size], alloc - old_size, format, args_org); + M_ASSERT (size >= 0); + } + if (size >= 0) { + m_str1ng_set_size(v, old_size + (size_t) size); + } else { + // vsnprintf may have output some characters before returning an error. + // Undo this to have a clean state + ptr[old_size] = 0; + } + va_end (args_org); + M_STR1NG_CONTRACT (v); + return size; +} + +/* Append to the string the formatted string of the given printf format */ +M_INLINE int +m_string_cat_printf (m_string_t v, const char format[], ...) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (format != NULL); + va_list args; + va_start (args, format); + int ret = m_string_cat_vprintf(v, format, args); + va_end (args); + return ret; +} + +#if M_USE_FAST_STRING_CONV == 0 +M_INLINE void +m_string_set_ui(m_string_t v, unsigned int n) +{ + m_string_printf(v, "%u", n); +} +M_INLINE void +m_string_set_si(m_string_t v, int n) +{ + m_string_printf(v, "%d", n); +} +#endif + +#endif // Have stdarg + +#if M_USE_STDIO + +/* Get a line/pureline/file from the FILE and store it in the string */ +M_INLINE bool +m_string_fgets(m_string_t v, FILE *f, m_string_fgets_t arg) +{ + M_STR1NG_CONTRACT(v); + M_ASSERT (f != NULL); + char *ptr = m_str1ng_fit2size (v, 100); + size_t size = 0; + size_t alloc = m_string_capacity(v); + ptr[0] = 0; + bool retcode = false; /* Nothing has been read yet */ + /* alloc - size is very unlikely to be bigger than INT_MAX + but fgets accepts an int as the size argument */ + while (fgets(&ptr[size], (int) M_MIN( (alloc - size), (size_t) INT_MAX ), f) != NULL) { + retcode = true; /* Something has been read */ + // fgets doesn't return the number of characters read, so we need to count. + size += strlen(&ptr[size]); + M_ASSERT(size >= 1); + if (arg != M_STRING_READ_FILE && ptr[size-1] == '\n') { + if (arg == M_STRING_READ_PURE_LINE) { + size --; + ptr[size] = 0; /* Remove EOL */ + } + m_str1ng_set_size(v, size); + M_STR1NG_CONTRACT(v); + return retcode; /* Normal terminaison */ + } else if (ptr[size-1] != '\n' && !feof(f)) { + /* The string buffer is not big enough: + increase it and continue reading */ + /* v cannot be stack alloc */ + ptr = m_str1ng_fit2size (v, alloc + alloc/2); + alloc = m_string_capacity(v); + } + } + m_str1ng_set_size(v, size); + M_STR1NG_CONTRACT (v); + return retcode; /* Abnormal terminaison */ +} + +/* Get a word from the FILE and store it in the string. + Words are supposed to be separated each other by the given list of separator + separator shall be a CONSTANT C array. + */ +M_INLINE bool +m_string_fget_word (m_string_t v, const char separator[], FILE *f) +{ + char buffer[128]; + char c = 0; + int d; + M_STR1NG_CONTRACT(v); + M_ASSERT (f != NULL); + M_ASSERT_INDEX (1+20+2+strlen(separator)+3, sizeof buffer); + size_t size = 0; + bool retcode = false; + + /* Skip separator first */ + do { + d = fgetc(f); + if (d == EOF) { + return false; + } + } while (strchr(separator, d) != NULL); + ungetc(d, f); + + size_t alloc = m_string_capacity(v); + char *ptr = m_str1ng_get_cstr(v); + ptr[0] = 0; + + /* NOTE: We generate a buffer which we give to scanf to parse the string, + that it is to say, we generate the format dynamically! + The format is like " %49[^ \t.\n]%c" + So in this case, we parse up to 49 chars, up to the separators char, + and we read the next char. If the next char is a separator, we successful + read a word, otherwise we have to continue parsing. + The user shall give a constant string as the separator argument, + as a control over this argument may give an attacker + an opportunity for stack overflow */ + while (snprintf(buffer, sizeof buffer -1, " %%%zu[^%s]%%c", (size_t) alloc-1-size, separator) > 0 + /* We may read one or two argument(s) */ + && m_core_fscanf(f, buffer, m_core_arg_size(&ptr[size], alloc-size), &c) >= 1) { + retcode = true; + size += strlen(&ptr[size]); + /* If we read only one argument + or if the final read character is a separator */ + if (c == 0 || strchr(separator, c) != NULL) + break; + /* Next char is not a separator: continue parsing */ + m_str1ng_set_size(v, size); + ptr = m_str1ng_fit2size (v, alloc + alloc/2); + alloc = m_string_capacity(v); + M_ASSERT (alloc > size + 1); + ptr[size++] = c; + ptr[size] = 0; + // Invalid c character for next iteration + c= 0; + } + m_str1ng_set_size(v, size); + M_STR1NG_CONTRACT(v); + return retcode; +} + +/* Put the string in the given FILE without formatting */ +M_INLINE bool +m_string_fputs(FILE *f, const m_string_t v) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (f != NULL); + return fputs(m_string_get_cstr(v), f) >= 0; +} + +#endif // Have stdio + +/* Test if the string starts with the given C string */ +M_INLINE bool +m_string_start_with_str_p(const m_string_t v, const char str[]) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (str != NULL); + const char *v_str = m_string_get_cstr(v); + while (*str){ + if (*str != *v_str) + return false; + str++; + v_str++; + } + return true; +} + +/* Test if the string starts with the other string */ +M_INLINE bool +m_string_start_with_string_p(const m_string_t v, const m_string_t v2) +{ + M_STR1NG_CONTRACT (v2); + return m_string_start_with_str_p (v, m_string_get_cstr(v2)); +} + +/* Test if the string ends with the C string */ +M_INLINE bool +m_string_end_with_str_p(const m_string_t v, const char str[]) +{ + M_STR1NG_CONTRACT (v); + M_ASSERT (str != NULL); + const char *v_str = m_string_get_cstr(v); + const size_t v_l = m_string_size(v); + const size_t str_l = strlen(str); + if (v_l < str_l) + return false; + return (memcmp(str, &v_str[v_l - str_l], str_l) == 0); +} + +/* Test if the string ends with the other string */ +M_INLINE bool +m_string_end_with_string_p(const m_string_t v, const m_string_t v2) +{ + M_STR1NG_CONTRACT (v2); + return m_string_end_with_str_p (v, m_string_get_cstr(v2)); +} + +/* Compute a hash for the string */ +M_INLINE size_t +m_string_hash(const m_string_t v) +{ + M_STR1NG_CONTRACT (v); + return m_core_hash(m_string_get_cstr(v), m_string_size(v)); +} + +// Return true if c is a character from charac +M_INLINE bool +m_str1ng_strim_char(char c, const char charac[]) +{ + for(const char *s = charac; *s; s++) { + if (c == *s) + return true; + } + return false; +} + +/* Remove any characters from charac that are present + in the begining of the string and the end of the string. */ +M_INLINE void +m_string_strim(m_string_t v, const char charac[]) +{ + M_STR1NG_CONTRACT (v); + char *ptr = m_str1ng_get_cstr(v); + char *b = ptr; + size_t size = m_string_size(v); + while (size > 0 && m_str1ng_strim_char(b[size-1], charac)) + size --; + if (size > 0) { + while (m_str1ng_strim_char(*b, charac)) + b++; + M_ASSERT (b >= ptr && size >= (size_t) (b - ptr) ); + size -= (size_t) (b - ptr); + memmove (ptr, b, size); + } + ptr[size] = 0; + m_str1ng_set_size(v, size); + M_STR1NG_CONTRACT (v); +} + +/* Test if the string is equal to the OOR value */ +M_INLINE bool +m_string_oor_equal_p(const m_string_t s, unsigned char n) +{ + return (s->ptr == NULL) & (s->u.heap.alloc == ~(size_t)n); +} + +/* Set the unitialized string to the OOR value */ +M_INLINE void +m_string_oor_set(m_string_t s, unsigned char n) +{ + s->ptr = NULL; + s->u.heap.alloc = ~(size_t)n; +} + +/* I/O */ +/* Output: "string" with quote around + Replace " by \" within the string (and \ to \\) + \n, \t & \r by their standard representation + and other not printable character with \0xx */ + +/* Transform the string 'v2' into a formatted string + and set it to (or append in) the string 'v'. */ +M_INLINE void +m_string_get_str(m_string_t v, const m_string_t v2, bool append) +{ + M_STR1NG_CONTRACT(v2); + M_STR1NG_CONTRACT(v); + M_ASSERT (v != v2); // Limitation + size_t size = append ? m_string_size(v) : 0; + size_t v2_size = m_string_size(v2); + size_t targetSize = size + v2_size + 3; + char *ptr = m_str1ng_fit2size(v, targetSize); + ptr[size ++] = '"'; + for(size_t i = 0 ; i < v2_size; i++) { + const char c = m_string_get_char(v2,i); + switch (c) { + case '\\': + case '"': + case '\n': + case '\t': + case '\r': + // Special characters which can be displayed in a short form. + m_str1ng_set_size(v, size); + ptr = m_str1ng_fit2size(v, ++targetSize); + ptr[size ++] = '\\'; + // This string acts as a perfect hashmap which supposes an ASCII mapping + // and (c^(c>>5)) is the hash function + ptr[size ++] = " tn\" r\\"[(c ^ (c >>5)) & 0x07]; + break; + default: + if (M_UNLIKELY (!isprint((unsigned char) c))) { + targetSize += 3; + m_str1ng_set_size(v, size); + ptr = m_str1ng_fit2size(v, targetSize); + int d1 = c & 0x07, d2 = (c>>3) & 0x07, d3 = (c>>6) & 0x07; + ptr[size ++] = '\\'; + ptr[size ++] = (char) ('0' + d3); + ptr[size ++] = (char) ('0' + d2); + ptr[size ++] = (char) ('0' + d1); + } else { + ptr[size ++] = c; + } + break; + } + } + ptr[size ++] = '"'; + ptr[size] = 0; + m_str1ng_set_size(v, size); + M_ASSERT (size <= targetSize); + M_STR1NG_CONTRACT (v); +} + +#if M_USE_STDIO + +/* Transform the string 'v2' into a formatted string + and output it in the given FILE */ +M_INLINE void +m_string_out_str(FILE *f, const m_string_t v) +{ + M_STR1NG_CONTRACT(v); + M_ASSERT (f != NULL); + fputc('"', f); + size_t size = m_string_size(v); + for(size_t i = 0 ; i < size; i++) { + const char c = m_string_get_char(v, i); + switch (c) { + case '\\': + case '"': + case '\n': + case '\t': + case '\r': + fputc('\\', f); + fputc(" tn\" r\\"[(c ^ c >>5) & 0x07], f); + break; + default: + if (M_UNLIKELY (!isprint(c))) { + int d1 = c & 0x07, d2 = (c>>3) & 0x07, d3 = (c>>6) & 0x07; + fputc('\\', f); + fputc('0' + d3, f); + fputc('0' + d2, f); + fputc('0' + d1, f); + } else { + fputc(c, f); + } + break; + } + } + fputc('"', f); +} + +/* Read the formatted string from the FILE + and set the converted value in the string 'v'. + Return true in case of success */ +M_INLINE bool +m_string_in_str(m_string_t v, FILE *f) +{ + M_STR1NG_CONTRACT(v); + M_ASSERT (f != NULL); + int c = fgetc(f); + if (c != '"') return false; + m_string_reset(v); + c = fgetc(f); + while (c != '"' && c != EOF) { + if (M_UNLIKELY (c == '\\')) { + c = fgetc(f); + switch (c) { + case 'n': + case 't': + case 'r': + case '\\': + case '\"': + // This string acts as a perfect hashmap which supposes an ASCII mapping + // and (c^(c>>5)) is the hash function + c = " \r \" \n\\\t"[(c^(c>>5))& 0x07]; + break; + default: + if (!(c >= '0' && c <= '7')) + return false; + int d1 = c - '0'; + c = fgetc(f); + if (!(c >= '0' && c <= '7')) + return false; + int d2 = c - '0'; + c = fgetc(f); + if (!(c >= '0' && c <= '7')) + return false; + int d3 = c - '0'; + c = (d1 << 6) + (d2 << 3) + d3; + break; + } + } + m_string_push_back (v, (char) c); + c = fgetc(f); + } + return c == '"'; +} + +#endif // Have stdio + +/* Read the formatted string from the C string + and set the converted value in the string 'v'. + Return true in case of success + If endptr is not null, update the position of the parsing. +*/ +M_INLINE bool +m_string_parse_str(m_string_t v, const char str[], const char **endptr) +{ + M_STR1NG_CONTRACT(v); + M_ASSERT (str != NULL); + bool success = false; + int c = *str++; + if (c != '"') goto exit; + m_string_reset(v); + c = *str++; + while (c != '"' && c != 0) { + if (M_UNLIKELY (c == '\\')) { + c = *str++; + switch (c) { + case 'n': + case 't': + case 'r': + case '\\': + case '\"': + // This string acts as a perfect hashmap which supposes an ASCII mapping + // and (c^(c>>5)) is the hash function + c = " \r \" \n\\\t"[(c^(c>>5))& 0x07]; + break; + default: + if (!(c >= '0' && c <= '7')) + goto exit; + int d1 = c - '0'; + c = *str++; + if (!(c >= '0' && c <= '7')) + goto exit; + int d2 = c - '0'; + c = *str++; + if (!(c >= '0' && c <= '7')) + goto exit; + int d3 = c - '0'; + c = (d1 << 6) + (d2 << 3) + d3; + break; + } + } + m_string_push_back (v, (char) c); + c = *str++; + } + success = (c == '"'); + exit: + if (endptr != NULL) *endptr = str; + return success; +} + +/* Transform the string 'v2' into a formatted string + and output it in the given serializer + See serialization for return code. +*/ +M_INLINE m_serial_return_code_t +m_string_out_serial(m_serial_write_t serial, const m_string_t v) +{ + M_ASSERT (serial != NULL && serial->m_interface != NULL); + return serial->m_interface->write_string(serial, m_string_get_cstr(v), m_string_size(v) ); +} + +/* Read the formatted string from the serializer + and set the converted value in the string 'v'. + See serialization for return code. +*/ +M_INLINE m_serial_return_code_t +m_string_in_serial(m_string_t v, m_serial_read_t serial) +{ + M_ASSERT (serial != NULL && serial->m_interface != NULL); + return serial->m_interface->read_string(serial, v); +} + +/* UTF8 character classification: + * + * 0* --> type 1 byte A + * 10* --> chained byte B + * 110* --> type 2 byte C + * 1110* --> type 3 byte D + * 11110* --> type 4 byte E + * 111110* --> invalid I + */ +/* UTF8 State Transition table: + * ABCDEI + * +------ + * S|SI123III + * 1|ISIIIIII + * 2|I1IIIIII + * 3|I2IIIIII + * I|IIIIIIII + */ + +/* The use of a string enables the compiler/linker to factorize it. */ +#define M_STR1NG_UTF8_STATE_TAB \ + "\000\040\010\020\030\040\040\040" \ + "\040\000\040\040\040\040\040\040" \ + "\040\010\040\040\040\040\040\040" \ + "\040\020\040\040\040\040\040\040" \ + "\040\040\040\040\040\040\040\040" + +/* Main generic UTF8 decoder + It shall be (nearly) branchless on any CPU. + It takes a byte, and the previous state and the previous value of the unicode code point. + It updates the state and the decoded unicode code point. + A decoded unicoded code point is valid only when the state is STARTING. + */ +M_INLINE void +m_str1ng_utf8_decode(char c, m_str1ng_utf8_state_e *state, + m_string_unicode_t *unicode) +{ + const unsigned int type = m_core_clz32((unsigned char)~c) - (unsigned) (sizeof(uint32_t) - 1) * CHAR_BIT; + const m_string_unicode_t mask1 = (UINT32_MAX - (m_string_unicode_t)(*state != M_STR1NG_UTF8_STARTING) + 1); + const m_string_unicode_t mask2 = (0xFFU >> type); + const m_string_unicode_t c1 = (m_string_unicode_t) c; + *unicode = ((*unicode << 6) & mask1) | (c1 & mask2); + *state = (m_str1ng_utf8_state_e) M_STR1NG_UTF8_STATE_TAB[(unsigned int) *state + type]; +} + +/* Check if the given array of characters is a valid UTF8 stream */ +/* NOTE: Non-canonical representation are not always rejected */ +M_INLINE bool +m_str1ng_utf8_valid_str_p(const char str[]) +{ + m_str1ng_utf8_state_e s = M_STR1NG_UTF8_STARTING; + m_string_unicode_t u = 0; + while (*str) { + m_str1ng_utf8_decode(*str, &s, &u); + if ((s == M_STR1NG_UTF8_ERROR) + ||(s == M_STR1NG_UTF8_STARTING + &&(u > 0x10FFFF /* out of range */ + ||(u >= 0xD800 && u <= 0xDFFF) /* surrogate halves */))) + return false; + str++; + } + return true; +} + +/* Test if the given byte is the start of an UTF8 code point */ +M_INLINE bool +m_str1ng_utf8_start_p(unsigned char val) +{ + return ((val & 0xC0u) != 0x80u); +} + +/* Computer the number of unicode code points are encoded in the UTF8 stream */ +M_INLINE size_t +m_str1ng_utf8_length(const char str[]) +{ + size_t size = 0; + while (*str) { + unsigned char val = (unsigned char) *str++; + size += m_str1ng_utf8_start_p(val); + } + return size; +} + +/* Encode an unicode code point into an UTF8 stream */ +M_INLINE int +m_str1ng_utf8_encode(char buffer[5], m_string_unicode_t u) +{ + if (M_LIKELY (u <= 0x7Fu)) { + buffer[0] = (char) u; + buffer[1] = 0; + return 1; + } else if (u <= 0x7FFu) { + buffer[0] = (char) (0xC0u | (u >> 6)); + buffer[1] = (char) (0x80 | (u & 0x3Fu)); + buffer[2] = 0; + return 2; + } else if (u <= 0xFFFFu) { + buffer[0] = (char) (0xE0u | (u >> 12)); + buffer[1] = (char) (0x80u | ((u >> 6) & 0x3Fu)); + buffer[2] = (char) (0x80u | (u & 0x3Fu)); + buffer[3] = 0; + return 3; + } else { + buffer[0] = (char) (0xF0u | (u >> 18)); + buffer[1] = (char) (0x80u | ((u >> 12) & 0x3Fu)); + buffer[2] = (char) (0x80u | ((u >> 6) & 0x3Fu)); + buffer[3] = (char) (0x80u | (u & 0x3F)); + buffer[4] = 0; + return 4; + } +} + +/* Start iteration over the UTF8 encoded unicode code point */ +M_INLINE void +m_string_it(m_string_it_t it, const m_string_t str) +{ + M_STR1NG_CONTRACT(str); + M_ASSERT(it != NULL); + it->ptr = m_string_get_cstr(str); + it->u = 0; + it->string = str; + M_STR1NG_IT_CONTRACT(it); +} + +/* Set the iterator to the end of string + The iterator references therefore nothing. +*/ +M_INLINE void +m_string_it_end(m_string_it_t it, const m_string_t str) +{ + M_STR1NG_CONTRACT(str); + M_ASSERT(it != NULL); + it->ptr = &m_string_get_cstr(str)[m_string_size(str)]; + it->u = 0; + it->string = str; + M_STR1NG_IT_CONTRACT(it); +} + +/* Set the iterator to the same position than the other one */ +M_INLINE void +m_string_it_set(m_string_it_t it, const m_string_it_t itsrc) +{ + M_ASSERT(it != NULL && itsrc != NULL); + M_STR1NG_IT_CONTRACT(itsrc); + it->ptr = itsrc->ptr; + it->u = itsrc->u; + it->string = itsrc->string; + M_STR1NG_IT_CONTRACT(it); +} + +/* Set the iterator to the given position in the string. + The given position shall reference a valide code point in the string. + */ +M_INLINE void +m_string_it_pos(m_string_it_t it, const m_string_t str, const size_t n) +{ + M_ASSERT(it != NULL); + M_STR1NG_CONTRACT(str); + // The offset shall be within the string + M_ASSERT(n <= m_string_size(str)); + // The offset shall reference the first Byte of an UTF 8 Code point + M_ASSERT(m_str1ng_utf8_start_p ((unsigned char)m_string_get_cstr(str)[n])); + it->ptr = &m_string_get_cstr(str)[n]; + it->u = 0; + it->string = str; + M_STR1NG_IT_CONTRACT(it); +} + +/* Return the current offset in the string referenced by the iterator. + This references avalid code point. + */ +M_INLINE size_t +m_string_it_get_pos(m_string_it_t it) +{ + M_STR1NG_IT_CONTRACT(it); + return (size_t) (it->ptr - m_string_get_cstr(it->string)); +} + +/* Test if the iterator has reached the end of the string. */ +M_INLINE bool +m_string_end_p (m_string_it_t it) +{ + M_STR1NG_IT_CONTRACT(it); + return it->ptr[0] == 0; +} + +/* Test if the iterator is equal to the other one */ +M_INLINE bool +m_string_it_equal_p(const m_string_it_t it1, const m_string_it_t it2) +{ + M_STR1NG_IT_CONTRACT(it1); + M_STR1NG_IT_CONTRACT(it2); + return it1->ptr == it2->ptr; +} + +/* Advance the iterator to the next UTF8 unicode code point */ +M_INLINE void +m_string_next (m_string_it_t it) +{ + M_STR1NG_IT_CONTRACT(it); + const char *ptr = it->ptr; + while (*ptr != 0) { + ptr ++; + if (m_str1ng_utf8_start_p((unsigned char) *ptr) ) { + /* Start of an UTF 8 code point */ + break; + } + } + it->ptr = ptr; + return; +} + +/* Move the iterator to the previous code point */ +M_INLINE void +m_string_previous(m_string_it_t it) +{ + M_STR1NG_IT_CONTRACT(it); + const char *ptr = it->ptr; + const char *org = m_string_get_cstr(it->string); + while (ptr > org) { + ptr --; + if (m_str1ng_utf8_start_p((unsigned char) *ptr) ) { + /* Start of an UTF 8 code point */ + it->ptr = ptr; + return; + } + } + /* We reach the start of the string: mark the iterator to the end */ + it->ptr = &org[m_string_size(it->string)]; + M_STR1NG_IT_CONTRACT(it); +} + +/* Return the unicode code point associated to the iterator */ +M_INLINE m_string_unicode_t +m_string_get_cref (const m_string_it_t it) +{ + M_STR1NG_IT_CONTRACT(it); + M_ASSERT(*it->ptr != 0); + + m_str1ng_utf8_state_e state = M_STR1NG_UTF8_STARTING; + m_string_unicode_t u = 0; + const char *str = it->ptr; + do { + m_str1ng_utf8_decode(*str, &state, &u); + str++; + } while (state != M_STR1NG_UTF8_STARTING && state != M_STR1NG_UTF8_ERROR && *str != 0); + // Save where the current unicode value ends in the UTF8 steam + M_ASSERT( (str > it->ptr) && (str - it->ptr) <= M_STR1NG_MAX_BYTE_UTF8); + // Save the decoded unicode value + return M_UNLIKELY (state == M_STR1NG_UTF8_ERROR) ? M_STRING_UNICODE_ERROR : u; +} + +/* Return the unicode code point associated to the iterator */ +M_INLINE const m_string_unicode_t * +m_string_cref (m_string_it_t it) +{ + M_STR1NG_IT_CONTRACT(it); + it->u = m_string_get_cref(it); + return &it->u; +} + +/* Update the value referenced by the iterator to the given value */ +M_INLINE void +m_string_it_set_ref(m_string_it_t it, m_string_t s, m_string_unicode_t new_u) +{ + M_STR1NG_IT_CONTRACT(it); + M_STR1NG_CONTRACT(s); + M_ASSERT(s == it->string); + char *ptr = m_str1ng_get_cstr(s); + M_ASSUME( it->ptr >= ptr); + size_t offset = (size_t) (it->ptr - ptr); + // Special case if the unicode codepoint is 0 + if (new_u == 0) { + // Null the string + m_str1ng_set_size(s, offset); + ptr[offset] = 0; + M_STR1NG_IT_CONTRACT(it); + M_STR1NG_CONTRACT(s); + return; + } + // Encode the new unicode code point & compute its size + char buffer[4+1]; + m_str1ng_utf8_encode(buffer, new_u); + size_t new_u_size = strlen(buffer); + // Compute the size of the old unicode code point + m_str1ng_utf8_state_e state = M_STR1NG_UTF8_STARTING; + m_string_unicode_t old_u = 0; + const char *str = it->ptr; + do { + m_str1ng_utf8_decode(*str, &state, &old_u); + str++; + } while (state != M_STR1NG_UTF8_STARTING && state != M_STR1NG_UTF8_ERROR && *str != 0); + M_ASSUME( str > it->ptr); + size_t old_u_size = (size_t) (str - it->ptr); + // We need to replace old_u by new_u. Both are variable length + size_t str_size = m_string_size(s); + m_str1ng_fit2size(s, str_size + new_u_size - old_u_size + 1); + ptr = m_str1ng_get_cstr(s); // ptr may be reallocated! + if (new_u_size != old_u_size) { + M_ASSUME( str_size+1 > (offset + old_u_size) ); + memmove(&ptr[offset+new_u_size], &ptr[offset + old_u_size], + str_size+1 - offset - old_u_size); + } + memcpy(&ptr[offset], &buffer[0], new_u_size); + m_str1ng_set_size(s, str_size + new_u_size - old_u_size); + it->ptr = &ptr[offset]; + M_STR1NG_IT_CONTRACT(it); + M_STR1NG_CONTRACT(s); + return; +} + +/* Push unicode code point into string, encoding it in UTF8 */ +M_INLINE void +m_string_push_u (m_string_t str, m_string_unicode_t u) +{ + M_STR1NG_CONTRACT(str); + char buffer[4+1]; + m_str1ng_utf8_encode(buffer, u); + m_string_cat_cstr(str, buffer); +} + +/* Pop last unicode code point into string, encoding it in UTF8 */ +M_INLINE bool +m_string_pop_u(m_string_unicode_t *u, m_string_t str) +{ + M_STR1NG_CONTRACT(str); + char *org = m_str1ng_get_cstr(str); + size_t len = m_string_size(str); + // From the last byte in the string + while (len > 0) { + len--; + // Test if it is a start of an UTF8 code point + if (m_str1ng_utf8_start_p((unsigned char) org[len])) { + // Yes, so decode the UTF8 + m_str1ng_utf8_state_e state = M_STR1NG_UTF8_STARTING; + const char *tmp = &org[len]; + // Support of NULL pointer + m_string_unicode_t u_tmp; + m_string_unicode_t *u_ptr = u == NULL ? &u_tmp : u; + do { + m_str1ng_utf8_decode(*tmp++, &state, u_ptr); + } while (state != M_STR1NG_UTF8_STARTING && state != M_STR1NG_UTF8_ERROR); + // Final null char for the string + org[len] = 0; + m_str1ng_set_size(str, len); + M_STR1NG_CONTRACT(str); + return true; + } + } + // Fail to pop a unicode code + return false; +} + +/* Compute the length in UTF8 code points in the string */ +M_INLINE size_t +m_string_length_u(m_string_t str) +{ + M_STR1NG_CONTRACT(str); + return m_str1ng_utf8_length(m_string_get_cstr(str)); +} + +/* Check if a string is a valid UTF8 encoded stream */ +M_INLINE bool +m_string_utf8_p(m_string_t str) +{ + M_STR1NG_CONTRACT(str); + return m_str1ng_utf8_valid_str_p(m_string_get_cstr(str)); +} + + +/* Define the split & the join functions + in case of usage with the algorithm module */ +#define M_STR1NG_SPLIT(name, oplist, type_oplist) \ + M_INLINE void M_F(name, _split)(M_GET_TYPE oplist cont, \ + const m_string_t str, const char sep) \ + { \ + size_t begin = 0; \ + m_string_t tmp; \ + size_t size = m_string_size(str); \ + m_string_init(tmp); \ + M_CALL_RESET(oplist, cont); \ + for(size_t i = 0 ; i < size; i++) { \ + char c = m_string_get_char(str, i); \ + if (c == sep) { \ + m_string_set_cstrn(tmp, &m_string_get_cstr(str)[begin], i - begin); \ + /* If push move method is available, use it */ \ + M_IF_METHOD(PUSH_MOVE,oplist)( \ + M_CALL_PUSH_MOVE(oplist, cont, &tmp); \ + m_string_init(tmp); \ + , \ + M_CALL_PUSH(oplist, cont, tmp); \ + ) \ + begin = i + 1; \ + } \ + } \ + m_string_set_cstrn(tmp, &m_string_get_cstr(str)[begin], size - begin); \ + M_CALL_PUSH(oplist, cont, tmp); \ + /* HACK: if method reverse is defined, it is likely that we have */ \ + /* inserted the items in the wrong order (aka for a list) */ \ + M_IF_METHOD(REVERSE, oplist) (M_CALL_REVERSE(oplist, cont);, ) \ + m_string_clear(tmp); \ + } \ + \ + M_INLINE void M_F(name, _join)(m_string_t dst, M_GET_TYPE oplist cont, \ + const m_string_t str) \ + { \ + bool init_done = false; \ + m_string_reset (dst); \ + for M_EACH(item, cont, oplist) { \ + if (init_done) { \ + m_string_cat(dst, str); \ + } \ + m_string_cat (dst, *item); \ + init_done = true; \ + } \ + } \ + + +/* Use of Compound Literals to init a constant string. + NOTE: The use of the additional structure layer is to ensure + that the pointer to char is properly aligned to an int (this + is a needed asumption by m_string_hash). + Otherwise it could have been : + #define M_STRING_CTE(s) \ + ((const m_string_t){{.size = sizeof(s)-1, .alloc = sizeof(s), \ + .ptr = s}}) + which produces faster code. + Note: This code doesn't work with C++ (use of c99 feature + of recursive struct definition and compound literral). + As such, there is a separate C++ definition. +*/ +#ifndef __cplusplus +/* Initialize a constant string with the given C string */ +# define M_STRING_CTE(s) \ + (m_string_srcptr)((const m_string_t){{.u.heap = { .size = sizeof(s)-1, .alloc = sizeof(s) } , \ + .ptr = ((struct { long long _n; char _d[sizeof (s)]; }){ 0, s })._d }}) +#else +namespace m_lib { + template + struct m_aligned_string { + m_string_t string; + union { + char str[N]; + long long i; + }; + inline m_aligned_string(const char lstr[]) + { + this->string->u.heap.size = N -1; + this->string->u.heap.alloc = N; + memcpy (this->str, lstr, N); + this->string->ptr = this->str; + } + }; +} +/* Initialize a constant string with the given C string (C++ mode) */ +#define M_STRING_CTE(s) \ + m_lib::m_aligned_string(s).string +#endif + +/* Initialize and set a string to the given formatted value. */ +#define m_string_init_printf(v, ...) \ + (m_string_printf ( m_str1ng_init_ref(v), __VA_ARGS__) ) + +/* Initialize and set a string to the given formatted value. */ +#define m_string_init_vprintf(v, format, args) \ + (m_string_vprintf ( m_str1ng_init_ref(v), format, args) ) + +/* Initialize a string with the given list of arguments. + Check if it is a formatted input or not by counting the number of arguments. + If there is only one argument, it can only be a set to C string. + It is much faster in this case to call m_string_init_set_cstr. + In C11 mode, it uses the fact that m_string_init_set is overloaded to handle both + C string and string. */ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) +#define M_STR1NG_INIT_WITH(v, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__)(m_string_init_set, m_string_init_printf)(v, __VA_ARGS__) +#else +#define M_STR1NG_INIT_WITH(v, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__)(m_string_init_set_cstr, m_string_init_printf)(v, __VA_ARGS__) +#endif + +/* NOTE: Use GCC extension (OBSOLETE) */ +#define M_STRING_DECL_INIT(v) \ + m_string_t v __attribute__((cleanup(m_str1ng_clear2))) = {{ 0, 0, NULL}} + +/* NOTE: Use GCC extension (OBSOLETE) */ +#define M_STRING_DECL_INIT_PRINTF(v, format, ...) \ + M_STRING_DECL_INIT(v); \ + m_string_printf (v, format, __VA_ARGS__) + + +/* Define the OPLIST of a STRING */ +#define M_STRING_OPLIST \ + (INIT(m_string_init),INIT_SET(m_string_init_set), SET(m_string_set), \ + INIT_WITH(M_STR1NG_INIT_WITH), \ + INIT_MOVE(m_string_init_move), MOVE(m_string_move), \ + SWAP(m_string_swap), RESET(m_string_reset), \ + EMPTY_P(m_string_empty_p), \ + CLEAR(m_string_clear), HASH(m_string_hash), EQUAL(m_string_equal_p), \ + CMP(m_string_cmp), TYPE(m_string_t), \ + PARSE_STR(m_string_parse_str), GET_STR(m_string_get_str), \ + OUT_STR(m_string_out_str), IN_STR(m_string_in_str), \ + OUT_SERIAL(m_string_out_serial), IN_SERIAL(m_string_in_serial), \ + EXT_ALGO(M_STR1NG_SPLIT), \ + OOR_EQUAL(m_string_oor_equal_p), OOR_SET(m_string_oor_set) \ + ,SUBTYPE(m_string_unicode_t) \ + ,IT_TYPE(m_string_it_t) \ + ,IT_FIRST(m_string_it) \ + ,IT_END(m_string_it_end) \ + ,IT_SET(m_string_it_set) \ + ,IT_END_P(m_string_end_p) \ + ,IT_EQUAL_P(m_string_it_equal_p) \ + ,IT_NEXT(m_string_next) \ + ,IT_CREF(m_string_cref) \ + ,EMPLACE_TYPE(const char*) \ + ) + +/* Register the OPLIST as a global one */ +#define M_OPL_m_string_t() M_STRING_OPLIST + + +/***********************************************************************/ +/* */ +/* Macro encapsulation to give a default value of 0 for start offset */ +/* */ +/***********************************************************************/ + +/* Search for a character in a string (string, character[, start=0]) */ +#define m_string_search_char(...) \ + m_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/* Reverse Search for a character in a string (string, character[, start=0]) */ +#define m_string_search_rchar(...) \ + m_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/* Search for a C string in a string (string, c_string[, start=0]) */ +#define m_string_search_cstr(...) \ + m_string_search_cstr(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/* Search for a string in a string (string, string[, start=0]) */ +#define m_string_search(...) \ + m_string_search(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/* PBRK for a string in a string (string, string[, start=0]) */ +#define m_string_search_pbrk(...) \ + m_string_search_pbrk(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/* Replace a C string to another C string in a string (string, c_src_string, c_dst_string, [, start=0]) */ +#define m_string_replace_cstr(...) \ + m_string_replace_cstr(M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) + +/* Replace a string to another string in a string (string, src_string, dst_string, [, start=0]) */ +#define m_string_replace(...) \ + m_string_replace(M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) + +/* Strim a string from the given set of characters (default is " \n\r\t") */ +#define m_string_strim(...) \ + m_string_strim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__)) + +/* Concat a set strings (or const char * if C11)) into one string */ +#define m_string_cats(a, ...) \ + M_MAP2_C(m_string_cat, a, __VA_ARGS__) + +/* Set a string to a set strings (or const char * if C11)) */ +#define m_string_sets(a, ...) \ + (m_string_reset(a), M_MAP2_C(m_string_cat, a, __VA_ARGS__) ) + +/* Macro encapsulation for C11 */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + +/* Select either the string function or the C string function depending on + the b parameter of the function. + func1 is the string function / func2 is the str function. */ +# define M_STR1NG_SELECT2(func1,func2,a,b) \ + _Generic((b)+0, \ + char*: func2, \ + const char *: func2, \ + default : func1 \ + )(a,b) +# define M_STR1NG_SELECT3(func1,func2,a,b,c) \ + _Generic((b)+0, \ + char*: func2, \ + const char *: func2, \ + default : func1 \ + )(a,b,c) + +/* Init & Set the string a to the string (or C string) b (constructor) */ +#define m_string_init_set(a,b) M_STR1NG_SELECT2(m_string_init_set, m_string_init_set_cstr, a, b) + +/* Set the string a to the string (or C string) b */ +#define m_string_set(a,b) M_STR1NG_SELECT2(m_string_set, m_string_set_cstr, a, b) + +/* Concatene the string (or C string) b to the string a */ +#define m_string_cat(a,b) M_STR1NG_SELECT2(m_string_cat, m_string_cat_cstr, a, b) + +/* Compare the string a to the string (or C string) b and return the sort order */ +#define m_string_cmp(a,b) M_STR1NG_SELECT2(m_string_cmp, m_string_cmp_cstr, a, b) + +/* Compare for equality the string a to the string (or C string) b */ +#define m_string_equal_p(a,b) M_STR1NG_SELECT2(m_string_equal_p, m_string_equal_cstr_p, a, b) + +/* strcoll the string a to the string (or C string) b */ +#define m_string_strcoll(a,b) M_STR1NG_SELECT2(m_string_strcoll, m_string_strcoll_cstr, a, b) + +#undef m_string_search +/* Search for a string in a string (or C string) (string, string[, start=0]) */ +#define m_string_search(...) \ + M_APPLY(M_STR1NG_SELECT3, m_string_search, m_string_search_cstr, \ + M_DEFAULT_ARGS(3, (0), __VA_ARGS__) ) +#endif + + +/***********************************************************************/ +/* */ +/* Override m-core default macros to integrate m_string_t in core */ +/* */ +/***********************************************************************/ + +/* Internal Macro: Provide GET_STR method to default type */ +#undef M_GET_STR_METHOD_FOR_DEFAULT_TYPE +#define M_GET_STR_METHOD_FOR_DEFAULT_TYPE GET_STR(M_GET_STRING_ARG) + +/* Internal Macro: Provide support of m_string_t to the macro M_PRINT in C11 */ + +// Extend the format specifier to support m_string_t +#undef M_PRINTF_FORMAT_EXTEND +#define M_PRINTF_FORMAT_EXTEND() \ + , m_string_ptr: "%s" \ + , m_string_srcptr: "%s" + +// Add type conversion for m_string_t (into a const char*) +#undef M_CORE_PRINTF_ARG +#define M_CORE_PRINTF_ARG(x) _Generic( ((void)0,(x)) \ + , m_string_ptr: m_string_get_cstr( M_AS_TYPE(m_string_ptr, x)) \ + , m_string_srcptr: m_string_get_cstr(M_AS_TYPE(m_string_srcptr,x)) \ + , default: x ) + +/* Internal Macro: Provide GET_STR method to enum type */ +#undef M_GET_STR_METHOD_FOR_ENUM_TYPE +#define M_GET_STR_METHOD_FOR_ENUM_TYPE GET_STR(M_ENUM_GET_STR) + + + +/***********************************************************************/ +/* */ +/* BOUNDED STRING, aka char[N+1] */ +/* */ +/***********************************************************************/ + +/* Define a bounded (fixed) string of exactly 'max_size' characters + * (excluding the final nul char). + */ +#define M_BOUNDED_STRING_DEF(name, max_size) \ + M_BEGIN_PROTECTED_CODE \ + M_BOUNDED_STR1NG_DEF_P2(name, max_size, M_F(name, _t) ) \ + M_END_PROTECTED_CODE + +/* Define the OPLIST of a BOUNDED_STRING */ +#define M_BOUNDED_STRING_OPLIST(name) \ + (INIT(M_F(name,_init)), \ + INIT_SET(M_F(name,_init_set)), \ + SET(M_F(name,_set)), \ + CLEAR(M_F(name,_clear)), \ + NAME(name), \ + INIT_WITH( API_1(M_BOUNDED_STR1NG_INIT_WITH)), \ + HASH(M_F(name,_hash)), \ + EQUAL(M_F(name,_equal_p)), \ + CMP(M_F(name,_cmp)), \ + TYPE(M_F(name,_ct)), \ + OOR_EQUAL(M_F(name,_oor_equal_p)), \ + OOR_SET(M_F(name, _oor_set)), \ + PARSE_STR(M_F(name,_parse_str)), \ + GET_STR(M_F(name,_get_str)), \ + OUT_STR(M_F(name,_out_str)), \ + IN_STR(M_F(name,_in_str)), \ + OUT_SERIAL(M_F(name,_out_serial)), \ + IN_SERIAL(M_F(name,_in_serial)), \ + ) + +/************************** INTERNAL ***********************************/ + +/* Contract of a bounded string. + * A bounded string last characters is always zero. */ +#define M_BOUNDED_STR1NG_CONTRACT(var, max_size) do { \ + M_ASSERT(var != NULL); \ + M_ASSERT(var->s[max_size] == 0); \ + } while (0) + +/* Expand the functions for a bounded string */ +#define M_BOUNDED_STR1NG_DEF_P2(name, max_size, bounded_t) \ + \ + /* Define of an array with one more to store the final nul char */ \ + typedef char M_F(name, _array_t)[max_size+1]; \ + \ + typedef struct M_F(name, _s) { \ + char s[max_size+1]; \ + } bounded_t[1]; \ + \ + /* Internal types for oplist */ \ + typedef bounded_t M_F(name, _ct); \ + \ + M_INLINE void \ + M_F(name, _init)(bounded_t s) \ + { \ + M_ASSERT(s != NULL); \ + M_STATIC_ASSERT(max_size >= 1, M_LIB_INTERNAL, "M*LIB: max_size parameter shall be greater than 0."); \ + s->s[0] = 0; \ + s->s[max_size] = 0; \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + /* Make the object illegal to be able to detect use after free */ \ + s->s[max_size] = 0x1F; \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + s->s[0] = 0; \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(const bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + return strlen(s->s); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity)(const bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + (void)s; /* unused */ \ + return max_size+1; \ + } \ + \ + M_INLINE char \ + M_F(name, _get_char)(const bounded_t s, size_t index) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT_INDEX(index, max_size); \ + return s->s[index]; \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(const bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + return s->s[0] == 0; \ + } \ + \ + M_INLINE void \ + M_F(name, _set_cstr)(bounded_t s, const char str[]) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + m_core_strncpy(s->s, max_size+1, str, max_size); \ + s->s[max_size] = 0; \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + } \ + \ + M_INLINE void \ + M_F(name, _set_cstrn)(bounded_t s, const char str[], size_t n) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT(str != NULL); \ + size_t len = M_MIN(max_size, n); \ + m_core_strncpy(s->s, max_size+1, str, len); \ + s->s[len] = 0; \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + } \ + \ + M_INLINE const char * \ + M_F(name, _get_cstr)(const bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + return s->s; \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(bounded_t s, const bounded_t str) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_BOUNDED_STR1NG_CONTRACT(str, max_size); \ + M_F(name, _set_cstr)(s, str->s); \ + } \ + \ + M_INLINE void \ + M_F(name, _set_n)(bounded_t s, const bounded_t str, \ + size_t offset, size_t length) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_BOUNDED_STR1NG_CONTRACT(str, max_size); \ + M_ASSERT_INDEX (offset, max_size+1); \ + M_F(name, _set_cstrn)(s, str->s+offset, length); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set)(bounded_t s, const bounded_t str) \ + { \ + M_F(name,_init)(s); \ + M_F(name,_set)(s, str); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_set_cstr)(bounded_t s, const char str[]) \ + { \ + M_F(name,_init)(s); \ + M_F(name,_set_cstr)(s, str); \ + } \ + \ + M_INLINE void \ + M_F(name, _cat_cstr)(bounded_t s, const char str[]) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT (str != NULL); \ + M_ASSERT_INDEX (strlen(s->s), max_size+1); \ + m_core_strncat(s->s, max_size+1, str, max_size-strlen(s->s)); \ + } \ + \ + M_INLINE void \ + M_F(name, _cat)(bounded_t s, const bounded_t str) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(str, max_size); \ + M_F(name, _cat_cstr)(s, str->s); \ + } \ + \ + M_INLINE int \ + M_F(name, _cmp_cstr)(const bounded_t s, const char str[]) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT(str != NULL); \ + return strcmp(s->s, str); \ + } \ + \ + M_INLINE int \ + M_F(name, _cmp)(const bounded_t s, const bounded_t str) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_BOUNDED_STR1NG_CONTRACT(str, max_size); \ + return strcmp(s->s, str->s); \ + } \ + \ + M_INLINE bool \ + M_F(name, _equal_cstr_p)(const bounded_t s, const char str[]) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT(str != NULL); \ + return strcmp(s->s, str) == 0; \ + } \ + \ + M_INLINE bool \ + M_F(name, _equal_p)(const bounded_t s, const bounded_t str) \ + { \ + /* _equal_p may be called in context OOR. So contract cannot be verified */ \ + return (s->s[max_size] == str->s[max_size]) & (strcmp(s->s, str->s) == 0); \ + } \ + \ + M_INLINE int \ + M_F(name, _printf)(bounded_t s, const char format[], ...) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT(format != NULL); \ + va_list args; \ + int ret; \ + va_start (args, format); \ + ret = vsnprintf (s->s, max_size+1, format, args); \ + va_end (args); \ + return ret; \ + } \ + \ + M_INLINE int \ + M_F(name, _cat_printf)(bounded_t s, const char format[], ...) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT(format != NULL); \ + va_list args; \ + int ret; \ + va_start (args, format); \ + size_t length = strlen(s->s); \ + M_ASSERT(length <= max_size); \ + ret = vsnprintf (&s->s[length], max_size+1-length, format, args); \ + va_end (args); \ + return ret; \ + } \ + \ + M_INLINE bool \ + M_F(name, _fgets)(bounded_t s, FILE *f, m_string_fgets_t arg) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT(f != NULL); \ + M_ASSERT(arg != M_STRING_READ_FILE); \ + char *ret = fgets(s->s, max_size+1, f); \ + s->s[max_size] = 0; \ + if (ret != NULL && arg == M_STRING_READ_PURE_LINE) { \ + size_t length = strlen(s->s); \ + if (length > 0 && s->s[length-1] == '\n') \ + s->s[length-1] = 0; \ + } \ + return ret != NULL; \ + } \ + \ + M_INLINE bool \ + M_F(name, _fputs)(FILE *f, const bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT(f != NULL); \ + return fputs(s->s, f) >= 0; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _hash)(const bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + /* Cannot use m_core_hash: alignment not sufficent */ \ + return m_core_cstr_hash(s->s); \ + } \ + \ + M_INLINE bool \ + M_F(name, _oor_equal_p)(const bounded_t s, unsigned char n) \ + { \ + /* s may be invalid contract */ \ + M_ASSERT (s != NULL); \ + M_ASSERT ( (n == 0) || (n == 1)); \ + return s->s[max_size] == (char) (n+1); \ + } \ + \ + M_INLINE void \ + M_F(name, _oor_set)(bounded_t s, unsigned char n) \ + { \ + /* s may be invalid contract */ \ + M_ASSERT (s != NULL); \ + M_ASSERT ( (n == 0) || (n == 1)); \ + s->s[max_size] = (char) (n+1); \ + } \ + \ + M_INLINE void \ + M_F(name, _get_str)(m_string_t v, const bounded_t s, bool append) \ + { \ + M_STR1NG_CONTRACT(v); \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + /* Build dummy string to reuse m_string_get_str */ \ + uintptr_t ptr = (uintptr_t) &s->s[0]; \ + m_string_t v2; \ + v2->u.heap.size = strlen(s->s); \ + v2->u.heap.alloc = v2->u.heap.size + 1; \ + v2->ptr = (char*)ptr; \ + m_string_get_str(v, v2, append); \ + } \ + \ + M_INLINE void \ + M_F(name, _out_str)(FILE *f, const bounded_t s) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(s, max_size); \ + M_ASSERT(f != NULL); \ + /* Build dummy string to reuse m_string_get_str */ \ + uintptr_t ptr = (uintptr_t) &s->s[0]; \ + m_string_t v2; \ + v2->u.heap.size = strlen(s->s); \ + v2->u.heap.alloc = v2->u.heap.size + 1; \ + v2->ptr = (char*)ptr; \ + m_string_out_str(f, v2); \ + } \ + \ + M_INLINE bool \ + M_F(name, _in_str)(bounded_t v, FILE *f) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(v, max_size); \ + M_ASSERT(f != NULL); \ + m_string_t v2; \ + m_string_init(v2); \ + bool ret = m_string_in_str(v2, f); \ + m_core_strncpy(v->s, max_size+1, m_string_get_cstr(v2), max_size); \ + m_string_clear(v2); \ + return ret; \ + } \ + \ + M_INLINE bool \ + M_F(name, _parse_str)(bounded_t v, const char str[], const char **endptr) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(v, max_size); \ + M_ASSERT(str != NULL); \ + m_string_t v2; \ + m_string_init(v2); \ + bool ret = m_string_parse_str(v2, str, endptr); \ + m_core_strncpy(v->s, max_size+1, m_string_get_cstr(v2), max_size); \ + m_string_clear(v2); \ + return ret; \ + } \ + \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t serial, const bounded_t v) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(v, max_size); \ + M_ASSERT (serial != NULL && serial->m_interface != NULL); \ + return serial->m_interface->write_string(serial, v->s, strlen(v->s) ); \ + } \ + \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(bounded_t v, m_serial_read_t serial) \ + { \ + M_BOUNDED_STR1NG_CONTRACT(v, max_size); \ + M_ASSERT (serial != NULL && serial->m_interface != NULL); \ + m_string_t tmp; \ + /* TODO: Not optimum */ \ + m_string_init(tmp); \ + m_serial_return_code_t r = serial->m_interface->read_string(serial, tmp); \ + m_core_strncpy(v->s, max_size+1, m_string_get_cstr(tmp), max_size); \ + m_string_clear(tmp); \ + M_BOUNDED_STR1NG_CONTRACT(v, max_size); \ + return r; \ + } + + + +/* Init a constant bounded string. + Try to do a clean cast */ +/* Use of Compound Literals to init a constant string. + See above */ +#ifndef __cplusplus +#define M_BOUNDED_STRING_CTE(name, string) \ + ((const struct M_F(name, _s) *)((M_F(name, _array_t)){string})) +#else +namespace m_lib { + template + struct m_bounded_string { + char s[N]; + inline m_bounded_string(const char lstr[]) + { + memset(this->s, 0, N); + m_core_strncpy(this->s, N, lstr, N-1); + } + }; +} +#define M_BOUNDED_STRING_CTE(name, string) \ + ((const struct M_F(name, _s) *)(m_lib::m_bounded_string(string).s)) +#endif + + +/* Initialize a bounded string with the given list of arguments. + Check if it is a formatted input or not by counting the number of arguments. + If there is only one argument, it can only be a set to C string. + It is much faster in this case to call m_string_init_set_cstr. +*/ +#define M_BOUNDED_STR1NG_INIT_WITH(oplist, v, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__)(M_C(M_GET_NAME oplist, _init_set_cstr)(v, __VA_ARGS__), M_BOUNDED_STR1NG_INIT_PRINTF(oplist, v, __VA_ARGS__)) + +#define M_BOUNDED_STR1NG_INIT_PRINTF(oplist, v, ...) \ + (M_GET_INIT oplist (v), M_C(M_GET_NAME oplist, _printf)(v, __VA_ARGS__)) + + + +/********************************************************************************/ +/* */ +/* Define the small name (i.e. without the prefix) of the API provided by this */ +/* header if it is needed */ +/* */ +/********************************************************************************/ +#if M_USE_SMALL_NAME + +#define string_t m_string_t +#define string_s m_string_s +#define STRING_FAILURE M_STRING_FAILURE +#define string_ptr m_string_ptr +#define string_srcptr m_string_srcptr +#define STRING_READ_LINE M_STRING_READ_LINE +#define STRING_READ_PURE_LINE M_STRING_READ_PURE_LINE +#define STRING_READ_FILE M_STRING_READ_FILE +#define string_fgets_t m_string_fgets_t +#define string_unicode_t m_string_unicode_t +#define STRING_UNICODE_ERROR M_STRING_UNICODE_ERROR +#define string_it_t m_string_it_t + +#define string_size m_string_size +#define string_capacity m_string_capacity +#define string_get_cstr m_string_get_cstr +#define string_init m_string_init +#define string_clear m_string_clear +#define string_clear_get_str m_string_clear_get_cstr +#define string_reset m_string_reset +#define string_get_char m_string_get_char +#define string_set_char m_string_set_char +#define string_empty_p m_string_empty_p +#define string_reserve m_string_reserve +#define string_set_str m_string_set_cstr +#define string_set_strn m_string_set_cstrn +#define string_set m_string_set +#define string_set_n m_string_set_n +#define string_init_set m_string_init_set +#define string_init_set_str m_string_init_set_cstr +#define string_init_move m_string_init_move +#define string_swap m_string_swap +#define string_move m_string_move +#define string_push_back m_string_push_back +#define string_cat_str m_string_cat_cstr +#define string_cat m_string_cat +#define string_cmp_str m_string_cmp_cstr +#define string_cmp m_string_cmp +#define string_equal_str_p m_string_equal_cstr_p +#define string_equal_p m_string_equal_p +#define string_cmpi_str m_string_cmpi_cstr +#define string_cmpi m_string_cmpi +#define string_search_char m_string_search_char +#define string_search_rchar m_string_search_rchar +#define string_search_str m_string_search_cstr +#define string_search m_string_search +#define string_search_pbrk m_string_search_pbrk +#define string_strcoll_str m_string_strcoll_cstr +#define string_strcoll m_string_strcoll +#define string_spn m_string_spn +#define string_cspn m_string_cspn +#define string_left m_string_left +#define string_right m_string_right +#define string_mid m_string_mid +#define string_replace_str m_string_replace_cstr +#define string_replace m_string_replace +#define string_replace_at m_string_replace_at +#define string_replace_all_str m_string_replace_all_cstr +#define string_replace_all m_string_replace_all +#define string_vprintf m_string_vprintf +#define string_printf m_string_printf +#define string_cat_vprintf m_string_cat_vprintf +#define string_cat_printf m_string_cat_printf +#define string_fgets m_string_fgets +#define string_fget_word m_string_fget_word +#define string_fputs m_string_fputs +#define string_start_with_str_p m_string_start_with_str_p +#define string_start_with_string_p m_string_start_with_string_p +#define string_end_with_str_p m_string_end_with_str_p +#define string_end_with_string_p m_string_end_with_string_p +#define string_hash m_string_hash +#define string_strim m_string_strim +#define string_oor_equal_p m_string_oor_equal_p +#define string_oor_set m_string_oor_set +#define string_get_str m_string_get_str +#define string_out_str m_string_out_str +#define string_in_str m_string_in_str +#define string_parse_str m_string_parse_str +#define string_out_serial m_string_out_serial +#define string_in_serial m_string_in_serial +#define string_it m_string_it +#define string_it_end m_string_it_end +#define string_it_set m_string_it_set +#define string_end_p m_string_end_p +#define string_it_equal_p m_string_it_equal_p +#define string_next m_string_next +#define string_get_cref m_string_get_cref +#define string_cref m_string_cref +#define string_push_u m_string_push_u +#define string_pop_u m_string_pop_u +#define string_length_u m_string_length_u +#define string_utf8_p m_string_utf8_p +#define string_set_ui m_string_set_ui +#define string_set_si m_string_set_si +#define string_it_pos m_string_it_pos +#define string_it_get_pos m_string_it_get_pos +#define string_previous m_string_previous +#define string_it_set_ref m_string_it_set_ref + +#define STRING_CTE M_STRING_CTE +#define STRING_DECL_INIT M_STRING_DECL_INIT +#define STRING_DECL_INIT_PRINTF M_STRING_DECL_INIT_PRINTF +#define STRING_OPLIST M_STRING_OPLIST +#define M_OPL_string_t M_OPL_m_string_t +#define string_sets m_string_sets +#define string_cats m_string_cats +#define string_init_printf m_string_init_printf +#define string_init_vprintf m_string_init_vprintf + +#define BOUNDED_STRING_DEF M_BOUNDED_STRING_DEF +#define BOUNDED_STRING_OPLIST M_BOUNDED_STRING_OPLIST +#define BOUNDED_STRING_CTE M_BOUNDED_STRING_CTE + +#endif + +M_END_PROTECTED_CODE + +#endif diff --git a/components/mlib/m-thread.h b/components/mlib/m-thread.h new file mode 100644 index 00000000..4a7da750 --- /dev/null +++ b/components/mlib/m-thread.h @@ -0,0 +1,748 @@ +/* + * M*LIB - Thin Mutex & Thread wrapper + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_MUTEX_H +#define MSTARLIB_MUTEX_H + +/* Auto-detect the thread backend to use if the user has not override it */ +#ifndef M_USE_THREAD_BACKEND +# if defined(INC_FREERTOS_H) +# define M_USE_THREAD_BACKEND 4 +# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \ + && !defined(__STDC_NO_THREADS__) +# define M_USE_THREAD_BACKEND 1 +# elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) +# define M_USE_THREAD_BACKEND 2 +# else +# define M_USE_THREAD_BACKEND 3 +# endif +#endif + + +/****************************** C11 version ********************************/ +#if M_USE_THREAD_BACKEND == 1 + +#include +#include +#include +#include "m-core.h" + +M_BEGIN_PROTECTED_CODE + +/* Define a mutex type based on C11 definition */ +typedef mtx_t m_mutex_t[1]; + +/* Define a condition variable type based on C11 definition */ +typedef cnd_t m_cond_t[1]; + +/* Define a thread type based on C11 definition */ +typedef thrd_t m_thread_t[1]; + +/* Initialize the mutex (constructor) */ +M_INLINE void m_mutex_init(m_mutex_t m) +{ + int rc = mtx_init(m, mtx_plain); + // Abort program in case of initialization failure + // There is really nothing else to do if a mutex cannot be constructed + M_ASSERT_INIT (rc == thrd_success, "mutex"); +} + +/* Clear the mutex (destructor) */ +M_INLINE void m_mutex_clear(m_mutex_t m) +{ + mtx_destroy(m); +} + +/* Lock the mutex */ +M_INLINE void m_mutex_lock(m_mutex_t m) +{ + mtx_lock(m); +} + +/* Unlock the mutex */ +M_INLINE void m_mutex_unlock(m_mutex_t m) +{ + mtx_unlock(m); +} + +/* Initialize the condition variable (constructor) */ +M_INLINE void m_cond_init(m_cond_t c) +{ + int rc = cnd_init(c); + // Abort program in case of initialization failure + // There is really nothing else to do if the object cannot be constructed + M_ASSERT_INIT (rc == thrd_success, "conditional variable"); +} + +/* Clear the condition variable (destructor) */ +M_INLINE void m_cond_clear(m_cond_t c) +{ + cnd_destroy(c); +} + +/* Signal the condition variable to at least one waiting thread */ +M_INLINE void m_cond_signal(m_cond_t c) +{ + cnd_signal(c); +} + +/* Signal the condition variable to all waiting threads */ +M_INLINE void m_cond_broadcast(m_cond_t c) +{ + cnd_broadcast(c); +} + +/* Wait for signaling the condition variable by another thread */ +M_INLINE void m_cond_wait(m_cond_t c, m_mutex_t m) +{ + cnd_wait(c, m); +} + +/* Create the thread (constructor) and start it */ +M_INLINE void m_thread_create(m_thread_t t, void (*func)(void*), void* arg) +{ + int rc = thrd_create(t, (int(*)(void*))(void(*)(void))func, arg); + // Abort program in case of initialization failure + M_ASSERT_INIT (rc == thrd_success, "thread"); +} + +/* Wait for the thread to terminate and destroy it (destructor) */ +M_INLINE void m_thread_join(m_thread_t t) +{ + int rc = thrd_join(*t, NULL); + M_ASSERT (rc == thrd_success); + // Avoid warning about variable unused. + (void) rc; +} + +/* The thread has nothing meaningfull to do. + Inform the OS to let other threads be scheduled */ +M_INLINE void m_thread_yield(void) +{ + thrd_yield(); +} + +/* Sleep the thread for at least usec microseconds. + Return true if the sleep was successful (or we cannot know) */ +M_INLINE bool m_thread_sleep(unsigned long long usec) +{ + struct timespec tv; + tv.tv_sec = (long) (usec / 1000000ULL); + tv.tv_nsec = (long) ((usec % 1000000ULL) * 1000UL); + int retval = thrd_sleep(&tv, NULL); + return retval == 0; +} + +// a helper structure for m_once_call +typedef once_flag m_once_t[1]; + +// Initial value for m_once_t +#define M_ONCE_INIT_VALUE { ONCE_FLAG_INIT } + +// Call the function exactly once +M_INLINE void m_once_call(m_once_t o, void (*func)(void)) +{ + call_once(o,func); +} + +// Attribute to use to allocate a global variable to a thread. +#define M_THREAD_ATTR _Thread_local + +M_END_PROTECTED_CODE + + +/****************************** WIN32 version ******************************/ +#elif M_USE_THREAD_BACKEND == 2 + +/* CLANG provides some useless and wrong warnings: + * - _WIN32_WINNT starts with '_' which is reserved by the standard + * as per the MSVC compiler, it is needed to be defined by the user + * to define which version of windows it want to be compatible with. + * - windows.h may be different than the case used by the file sytem + * there is however no normalized case. + * + * So, theses warnings have to be ignored and are disabled. + * + * We cannot add theses warnings in M_BEGIN_PROTECTED_CODE + * as they need to be disabled **BEFORE** including any system header + * and m-core includes some system headers. + * So we need to disable them explictly here. + */ +#if defined(__clang__) && __clang_major__ >= 4 + _Pragma("clang diagnostic push") + _Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"") + _Pragma("clang diagnostic ignored \"-Wnonportable-system-include-path\"") +#endif + +/* CriticalSection & ConditionVariable are available from Windows Vista */ +#ifndef WINVER +#define WINVER _WIN32_WINNT_VISTA +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif + +/* Include system headers */ +#include +#include +#include +#include "m-core.h" + +#if defined(__clang__) && __clang_major__ >= 4 + _Pragma("clang diagnostic pop") +#endif + +M_BEGIN_PROTECTED_CODE + +/* Define a thread type based on WINDOWS definition */ +typedef HANDLE m_thread_t[1]; + +/* Define a mutex type based on WINDOWS definition */ +typedef CRITICAL_SECTION m_mutex_t[1]; + +/* Define a condition variable type based on WINDOWS definition */ +typedef CONDITION_VARIABLE m_cond_t[1]; + +/* Initialize a mutex (Constructor)*/ +M_INLINE void m_mutex_init(m_mutex_t m) +{ + InitializeCriticalSection(m); +} + +/* Clear a mutex (destructor) */ +M_INLINE void m_mutex_clear(m_mutex_t m) +{ + DeleteCriticalSection(m); +} + +/* Lock a mutex */ +M_INLINE void m_mutex_lock(m_mutex_t m) +{ + EnterCriticalSection(m); +} + +/* Unlock a mutex */ +M_INLINE void m_mutex_unlock(m_mutex_t m) +{ + LeaveCriticalSection(m); +} + +/* Initialize a condition variable (constructor) */ +M_INLINE void m_cond_init(m_cond_t c) +{ + InitializeConditionVariable(c); +} + +/* Clear a condition variable (destructor) */ +M_INLINE void m_cond_clear(m_cond_t c) +{ + (void) c; // There is no destructor for this object. +} + +/* Signal a condition variable to at least one waiting thread */ +M_INLINE void m_cond_signal(m_cond_t c) +{ + WakeConditionVariable(c); +} + +/* Signal a condition variable to all waiting threads */ +M_INLINE void m_cond_broadcast(m_cond_t c) +{ + WakeAllConditionVariable(c); +} + +/* Wait for a condition variable */ +M_INLINE void m_cond_wait(m_cond_t c, m_mutex_t m) +{ + SleepConditionVariableCS(c, m, INFINITE); +} + +/* Create a thread (constructor) and start it */ +M_INLINE void m_thread_create(m_thread_t t, void (*func)(void*), void *arg) +{ + *t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (uintptr_t) func, arg, 0, NULL); + M_ASSERT_INIT (*t != NULL, "thread"); +} + +/* Wait for the thread to terminate and destroy it (destructor) */ +M_INLINE void m_thread_join(m_thread_t t) +{ + DWORD dwWaitResult = WaitForSingleObject(*t, INFINITE); + (void) dwWaitResult; + M_ASSERT (dwWaitResult == WAIT_OBJECT_0); + CloseHandle(*t); +} + +/* The thread has nothing meaningfull to do. + Inform the OS to let other threads be scheduled */ +M_INLINE void m_thread_yield(void) +{ + Sleep(0); +} + +/* Sleep the thread for at least usec microseconds + Return true if the sleep was successful */ +M_INLINE bool m_thread_sleep(unsigned long long usec) +{ + LARGE_INTEGER ft; + M_ASSERT (usec <= LLONG_MAX); + ft.QuadPart = -(10LL*(long long) usec); + HANDLE hd = CreateWaitableTimer(NULL, TRUE, NULL); + M_ASSERT_INIT (hd != NULL, "timer"); + SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0); + DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE); + CloseHandle(hd); + return dwWaitResult == WAIT_OBJECT_0; +} + + +typedef INIT_ONCE m_once_t[1]; +#define M_ONCE_INIT_VALUE { INIT_ONCE_STATIC_INIT } +M_INLINE BOOL CALLBACK m_once_callback( PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext) +{ + void (*func)(void); + (void) InitOnce; + (void) lpContext; + func = (void (*)(void))(uintptr_t) Parameter; + (*func)(); + return TRUE; +} +M_INLINE void m_once_call(m_once_t o, void (*func)(void)) +{ + InitOnceExecuteOnce(o, m_once_callback, (void*)(intptr_t)func, NULL); +} + +#if defined(_MSC_VER) +// Attribute to use to allocate a global variable to a thread (MSVC def). +# define M_THREAD_ATTR __declspec( thread ) +#else +// Attribute to use to allocate a global variable to a thread (GCC def). +# define M_THREAD_ATTR __thread +#endif + +M_END_PROTECTED_CODE + + +/**************************** PTHREAD version ******************************/ +#elif M_USE_THREAD_BACKEND == 3 + +#include +#ifdef _POSIX_PRIORITY_SCHEDULING +#include +#endif +#include +#include +#include +#include +#include +#include "m-core.h" + +M_BEGIN_PROTECTED_CODE + +/* Define a mutex type based on PTHREAD definition */ +typedef pthread_mutex_t m_mutex_t[1]; + +/* Define a condition variable type based on PTHREAD definition */ +typedef pthread_cond_t m_cond_t[1]; + +/* Define a thread type based on PTHREAD definition */ +typedef pthread_t m_thread_t[1]; + +/* Initialize the mutex (constructor) */ +M_INLINE void m_mutex_init(m_mutex_t m) +{ + int _rc = pthread_mutex_init(m, NULL); + // Abort program in case of initialization failure + // There is really nothing else to do if a mutex cannot be constructed + M_ASSERT_INIT (_rc == 0, "mutex"); +} + +/* Clear the mutex (destructor) */ +M_INLINE void m_mutex_clear(m_mutex_t m) +{ + pthread_mutex_destroy(m); +} + +/* Lock the mutex */ +M_INLINE void m_mutex_lock(m_mutex_t m) +{ + pthread_mutex_lock(m); +} + +/* Unlock the mutex */ +M_INLINE void m_mutex_unlock(m_mutex_t m) +{ + pthread_mutex_unlock(m); +} + +/* Lazy lock initialization */ +#define M_MUTEXI_INIT_VALUE { PTHREAD_MUTEX_INITIALIZER } + +/* Internal function compatible with lazy lock */ +M_INLINE void m_mutexi_lazy_lock(m_mutex_t m) +{ + pthread_mutex_lock(m); +} + +/* Initialize the condition variable (constructor) */ +M_INLINE void m_cond_init(m_cond_t c) +{ + int _rc = pthread_cond_init(c, NULL); + // Abort program in case of initialization failure + // There is really nothing else to do if a mutex cannot be constructed + M_ASSERT_INIT (_rc == 0, "conditional variable"); +} + +/* Clear the condition variable (destructor) */ +M_INLINE void m_cond_clear(m_cond_t c) +{ + pthread_cond_destroy(c); +} + +/* Signal a condition variable to at least a waiting thread */ +M_INLINE void m_cond_signal(m_cond_t c) +{ + pthread_cond_signal(c); +} + +/* Signal a condition variable to all waiting threads */ +M_INLINE void m_cond_broadcast(m_cond_t c) +{ + pthread_cond_broadcast(c); +} + +/* Waiting for a condition variable */ +M_INLINE void m_cond_wait(m_cond_t c, m_mutex_t m) +{ + pthread_cond_wait(c, m); +} + +/* Create a thread (constructor) and start it */ +M_INLINE void m_thread_create(m_thread_t t, void (*func)(void*), void *arg) +{ + int _rc = pthread_create(t, NULL, (void*(*)(void*))(void(*)(void))func, arg); + M_ASSERT_INIT (_rc == 0, "thread"); +} + +/* Wait for the thread to terminate and destroy it (destructor) */ +M_INLINE void m_thread_join(m_thread_t t) +{ + int _rc = pthread_join(*t, NULL); + (void)_rc; // Avoid warning about variable unused. + M_ASSERT (_rc == 0); +} + +/* The thread has nothing meaningfull to do. + Inform the OS to let other threads be scheduled */ +M_INLINE void m_thread_yield(void) +{ +#ifdef _POSIX_PRIORITY_SCHEDULING + sched_yield(); +#endif +} + +/* Sleep for at least usec microseconds + Return true if the sleep was successful */ +M_INLINE bool m_thread_sleep(unsigned long long usec) +{ + struct timeval tv; + /* We don't want to use usleep or nanosleep so that + we remain compatible with strict C99 build */ + tv.tv_sec = (time_t) (usec / 1000000ULL); + tv.tv_usec = (suseconds_t) (usec % 1000000ULL); + int retval = select(1, NULL, NULL, NULL, &tv); + return retval == 0; +} + +typedef pthread_once_t m_once_t[1]; +#define M_ONCE_INIT_VALUE { PTHREAD_ONCE_INIT } +M_INLINE void m_once_call(m_once_t o, void (*func)(void)) +{ + pthread_once(o,func); +} + +#if defined(__GNUC__) +# define M_THREAD_ATTR __thread +#else +# define M_THREAD_ATTR /* Not supported */ +#endif + +M_END_PROTECTED_CODE + +/****************************** FreeRTOS version ********************************/ +#elif M_USE_THREAD_BACKEND == 4 + +#include +#include +#include +#include "m-core.h" + +M_BEGIN_PROTECTED_CODE + +/* Default value for the stack */ +#ifndef M_USE_TASK_STACK_SIZE +#define M_USE_TASK_STACK_SIZE configMINIMAL_STACK_SIZE +#endif + +/* Default value for the priority tasks */ +#ifndef M_USE_TASK_PRIORITY +#define M_USE_TASK_PRIORITY ( tskIDLE_PRIORITY ) +#endif + +/* Define a mutex type based on FreeRTOS definition */ +typedef struct m_mutex_s { + SemaphoreHandle_t handle; + StaticSemaphore_t MutexBuffer; +} m_mutex_t[1]; + +/* Define a thread type based on FreeRTOS definition */ +typedef struct m_cond_s { + SemaphoreHandle_t handle; + StaticSemaphore_t SemBuffer; + unsigned int NumThreadWaiting; +} m_cond_t[1]; + +/* Define a thread type based on FreeRTOS definition */ +typedef struct m_thread_s { + SemaphoreHandle_t SemHandle; + StaticSemaphore_t SemBuffer; + TaskHandle_t TaskHandle; + StaticTask_t TaskBuffer; + void (*EntryPoint)(void *); + void* ArgsEntryPoint; + StackType_t* StackBuffer; +} m_thread_t[1]; + +/* Initialize the mutex (constructor) */ +M_INLINE void m_mutex_init(m_mutex_t m) +{ + /* Create a mutex semaphore without using any dynamic allocation */ + m->handle = xSemaphoreCreateMutexStatic(&m->MutexBuffer); + // It cannot fail, so we won't use M_ASSERT_INIT + M_ASSERT(m->handle); +} + +/* Clear the mutex (destructor) */ +M_INLINE void m_mutex_clear(m_mutex_t m) +{ + vSemaphoreDelete(m->handle); +} + +/* Lock the mutex */ +M_INLINE void m_mutex_lock(m_mutex_t m) +{ + xSemaphoreTake(m->handle, portMAX_DELAY); +} + +/* Unlock the mutex */ +M_INLINE void m_mutex_unlock(m_mutex_t m) +{ + xSemaphoreGive(m->handle); +} + + +/* Initialize the condition variable (constructor) */ +M_INLINE void m_cond_init(m_cond_t c) +{ + c->NumThreadWaiting = 0; + // Create a semaphore to implement the conditional variable + // Initial value is 0 and valid range is <= 0 + c->handle = xSemaphoreCreateCountingStatic( INT_MAX, 0, &c->SemBuffer ); + // It cannot fail, so we won't use M_ASSERT_INIT + M_ASSERT(c->handle); +} + +/* Clear the condition variable (destructor) */ +M_INLINE void m_cond_clear(m_cond_t c) +{ + vSemaphoreDelete(c->handle); +} + +/* Signal the condition variable to at least one waiting thread */ +M_INLINE void m_cond_signal(m_cond_t c) +{ + // This function is called within the mutex lock + // NumThreadWaiting doesn't need to be atomic + if (c->NumThreadWaiting > 0) { + // Wakeup one thread by posting on the semaphore + xSemaphoreGive(c->handle); + } // Otherwise there is no waiting thread, so nothing to signal +} + +/* Signal the condition variable to all waiting threads */ +M_INLINE void m_cond_broadcast(m_cond_t c) +{ + // This function is called within the mutex lock + // NumThreadWaiting doesn't need to be atomic + if (c->NumThreadWaiting > 0) { + // Wakeup all thread by posting on the semaphore + // as many times as there are waiting threads + for(unsigned i = 0; i < c->NumThreadWaiting; i++) { + xSemaphoreGive(c->handle); + } + } // Otherwise there is no waiting thread, so nothing to signal +} + +/* Wait for signaling the condition variable by another thread */ +M_INLINE void m_cond_wait(m_cond_t c, m_mutex_t m) +{ + // This function is called within the mutex lock + // Increment the number of waiting thread + c->NumThreadWaiting ++; + m_mutex_unlock(m); + // Wait for post in the semaphore + xSemaphoreTake(c->handle, portMAX_DELAY); + m_mutex_lock(m); + c->NumThreadWaiting --; +} + +M_INLINE void m_thr3ad_wrapper( void *args) +{ + struct m_thread_s *thread_ptr = args; + thread_ptr->EntryPoint(thread_ptr->ArgsEntryPoint); + // Give back the semaphore. + xSemaphoreGive(thread_ptr->SemHandle); + // Wait for destruction + while (true) { vTaskSuspend(NULL); } +} + +/* Create the thread (constructor) and start it */ +M_INLINE void m_thread_create(m_thread_t t, void (*func)(void*), void* arg) +{ + // Create a semaphore to implement the final wait + t->SemHandle = xSemaphoreCreateCountingStatic( 1, 0, &t->SemBuffer ); + M_ASSERT(t->SemHandle); + // Save the argument to the thread + t->EntryPoint = func; + t->ArgsEntryPoint = arg; + + // Allocate the stack + t->StackBuffer = pvPortMalloc( sizeof (StackType_t) * M_USE_TASK_STACK_SIZE); + M_ASSERT_INIT(t->StackBuffer, "STACK"); + + // Create the task without using any dynamic allocation + t->TaskHandle = xTaskCreateStatic(m_thr3ad_wrapper, "M*LIB", M_USE_TASK_STACK_SIZE, (void*) t, M_USE_TASK_PRIORITY, t->StackBuffer, &t->TaskBuffer); + // It cannot fail, so we won't use M_ASSERT_INIT + M_ASSERT(t->TaskHandle); +} + +/* Wait for the thread to terminate and destroy it (destructor) */ +M_INLINE void m_thread_join(m_thread_t t) +{ + xSemaphoreTake(t->SemHandle, portMAX_DELAY); + vTaskDelete(t->TaskHandle); + vPortFree(t->StackBuffer); + vSemaphoreDelete(t->SemHandle); + t->TaskHandle = 0; + t->StackBuffer = 0; + t->SemHandle = 0; +} + +/* The thread has nothing meaningfull to do. + Inform the OS to let other threads be scheduled */ +M_INLINE void m_thread_yield(void) +{ + taskYIELD(); +} + +/* Sleep the thread for at least usec microseconds. + Return true if the sleep was successful */ +M_INLINE bool m_thread_sleep(unsigned long long usec) +{ + TickType_t delay = (TickType_t) (usec / portTICK_PERIOD_MS / 1000ULL); + vTaskDelay(delay); + return true; +} + +// a helper structure for m_once_call +typedef struct { + atomic_int count; +} m_once_t[1]; + +// Initial value for m_once_t +#define M_ONCE_INIT_VALUE { { M_ATOMIC_VAR_INIT(0) } } + +// Call the function exactly once +M_INLINE void m_once_call(m_once_t o, void (*func)(void)) +{ + if (atomic_load(&o->count) != 2) { + int n = 0; + if (atomic_compare_exchange_strong( &o->count, &n, 1)) { + // First thread success + func(); + atomic_store(&o->count, 2); + } + // Wait for function call (FIXME: priority inversion possible?) + while (atomic_load(&o->count) != 2) { m_thread_yield(); } + } // Already called. Nothing to do +} + +// Attribute to use to allocate a global variable to a thread. +#define M_THREAD_ATTR __thread + +M_END_PROTECTED_CODE + +/******************************** INVALID VALUE **********************************/ + +#else +# error Value of M_USE_THREAD_BACKEND is incorrect. Please see the documentation for valid usage. +#endif + +// TODO: Obsolete M_LOCK macro. + +/* M_LOCK macro. Allow simple locking encapsulation. + USAGE: + static M_LOCK_DECL(name); + int f(int n) { + M_LOCK(name) { + // Exclusive access + } + } +*/ +/* NOTE: Either using direct support by the OS (WIN32/PTHREAD) + or using C11's ONCE mechanism */ +#ifdef M_MUTEXI_INIT_VALUE +# define M_LOCK_DECL(name) m_mutex_t name = M_MUTEXI_INIT_VALUE +# define M_LOCK(name) \ + M_LOCKI_DO(name, M_C(local_cont_, __LINE__), m_mutexi_lazy_lock, m_mutex_unlock) +#else +# define M_LOCK_DECL(name) \ + m_mutex_t name; \ + static void M_C(m_mutex_init_, name)(void) { \ + m_mutex_init(name); \ + } \ + m_once_t M_C(m_once_, name) = M_ONCE_INIT_VALUE +# define M_LOCKI_BY_ONCE(name) \ + (m_once_call(M_C(m_once_, name), M_C(m_mutex_init_, name)), \ + m_mutex_lock(name), (void) 0 ) +# define M_LOCK(name) \ + M_LOCKI_DO(name, M_C(local_cont_, __LINE__), M_LOCKI_BY_ONCE, m_mutex_unlock) +#endif + +#define M_LOCKI_DO(name, cont, lock_func, unlock_func) \ + for(bool cont = true \ + ; cont && (lock_func (name), true); \ + (unlock_func (name), cont = false)) + +#endif diff --git a/components/mlib/m-tree.h b/components/mlib/m-tree.h new file mode 100644 index 00000000..7b1d9c58 --- /dev/null +++ b/components/mlib/m-tree.h @@ -0,0 +1,1603 @@ +/* + * M*LIB - TREE module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_TREE_H +#define MSTARLIB_TREE_H + +#include "m-core.h" + +/* Define a complete tree of 'type' + https://en.wikipedia.org/wiki/Tree_(data_structure) + USAGE: + TREE_DEF(name, type_t[, type_oplist]) +*/ +#define M_TREE_DEF(name, ...) \ + M_TREE_DEF_AS(name, M_F(name, _t), M_F(name, _it_t), __VA_ARGS__) + +/* Define a complete tree of 'type' + as the given name name_t with its associated functions. + USAGE: + TREE_DEF_AS(name, name_t, it_t, type_t[, type_oplist]) +*/ +#define M_TREE_DEF_AS(name, name_t, it_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_TR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \ + (name, __VA_ARGS__, name_t, it_t ))) \ + M_END_PROTECTED_CODE + +/* Maximum number of child per node. + Used for assertion. */ +#ifndef M_USE_TREE_MAX_CHILD_PER_PARENT +#define M_USE_TREE_MAX_CHILD_PER_PARENT 10000000 +#endif + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +# define M_TR33_NODE_CONTRACT(node, tree) do { \ + M_ASSERT( (tree)->size > 0); \ + M_ASSERT( (node)->parent != M_TR33_NO_NODE); \ + M_ASSERT( (node)->parent == M_TR33_ROOT_NODE || (node)->parent >= 0); \ + M_ASSERT( (node)->parent != M_TR33_ROOT_NODE || (node) == &(tree)->tab[(tree)->root_index] ); \ +} while (0) + +# define M_TR33_CONTRACT(tree) do { \ + M_ASSERT( (tree)->size >= 0 && (tree)->size <= (tree)->capacity); \ + M_ASSERT( (tree)->capacity >= 0 ); \ + M_ASSERT( (tree)->capacity == 0 || (tree)->tab != NULL); \ + M_ASSERT( (tree)->allow_realloc == 0 || (tree)->allow_realloc == INT32_MAX ); \ + M_ASSERT( (tree)->free_index >= M_TR33_NO_NODE && (tree)->free_index < (tree)->capacity); \ + M_ASSERT( (tree)->root_index >= M_TR33_NO_NODE && (tree)->root_index < (tree)->capacity); \ + M_ASSERT( (tree)->allow_realloc != 0 || (tree)->free_index < 0 || (tree)->tab[(tree)->free_index].parent == M_TR33_NO_NODE); \ + M_ASSERT( (tree)->allow_realloc != 0 || (tree)->root_index < 0 || (tree)->tab[(tree)->root_index].parent == M_TR33_ROOT_NODE); \ + M_ASSERT( (tree)->root_index != M_TR33_NO_NODE || (tree)->size == 0); \ +} while (0) + +# define M_TR33_IT_CONTRACT(it, valid) do { \ + M_TR33_CONTRACT( (it).tree); \ + M_ASSERT(valid == false || (it).index >= 0); \ + if ((it).index >= 0) { \ + M_ASSERT( (it).index < (it).tree->capacity); \ + M_TR33_NODE_CONTRACT(&(it).tree->tab[(it).index], (it).tree); \ + /* Don't deref table if realloc is disabled as the tree can be \ + accessed concurrently (for _ref methods) */ \ + if ( (it).tree->allow_realloc == 0) { \ + /* All children of this node have the parent field correctly set */ \ + m_tr33_index_t itj = (it).tree->tab[(it).index].child; \ + /* They all have their siblings as the left node */ \ + m_tr33_index_t lftj = M_TR33_NO_NODE; \ + /* We don't have any infinite loop */ \ + unsigned cpt = 0; \ + while (itj >= 0) { \ + M_ASSERT( (it).tree->tab[itj].parent == (it).index ); \ + M_ASSERT( (it).tree->tab[itj].left == lftj ); \ + lftj = itj; \ + itj = (it).tree->tab[itj].right; \ + M_ASSERT( ++cpt < M_USE_TREE_MAX_CHILD_PER_PARENT); \ + } \ + (void) cpt; /* may be unused in release mode */ \ + (void) lftj; /* may be unused in release mode */ \ + } \ + } \ +} while (0) + +/* Deferred evaluation */ +#define M_TR33_DEF_P1(arg) M_ID( M_TR33_DEF_P2 arg ) + +/* Validate the oplist before going further */ +#define M_TR33_DEF_P2(name, type, oplist, tree_t, it_t) \ + M_IF_OPLIST(oplist)(M_TR33_DEF_P3, M_TR33_DEF_FAILURE)(name, type, oplist, tree_t, it_t) + +/* Stop processing with a compilation failure */ +#define M_TR33_DEF_FAILURE(name, type, oplist, tree_t, it_t) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(TREE_DEF): the given argument is not a valid oplist: " #oplist) + +/* type of an index in the tree. Up to 2^31-1 nodes can be created.*/ +typedef int32_t m_tr33_index_t; + +/* Special value for the parent field of a node: + * M_TR33_ROOT_NODE is only for the root node. + * M_TR33_NO_NODE is for nodes without parent, i.e. the pool of free nodes. + */ +#define M_TR33_ROOT_NODE (-2) +#define M_TR33_NO_NODE (-1) + +/* Internal definition: + - name: prefix to be used + - type: type of the elements of the tree + - oplist: oplist of the type of the elements of the tree + - tree_t: alias for the type of the tree + - it_t: alias for the iterator of the tree + + A free node is identified as parent as M_TR33_NO_NODE, in which case child is the next item in the list of free node. + A root node is identified as parent as M_TR33_ROOT_NODE +*/ +#define M_TR33_DEF_P3(name, type, oplist, tree_t, it_t) \ + M_TR33_DEF_TYPE(name, type, oplist, tree_t, it_t) \ + M_TR33_DEF_P4_CORE(name, type, oplist, tree_t, it_t) \ + M_TR33_DEF_P4_EXT(name, type, oplist, tree_t, it_t) \ + M_TR33_DEF_P4_IO(name, type, oplist, tree_t, it_t) \ + M_TR33_DEF_P4_EMPLACE(name, type, oplist, tree_t, it_t) + +#define M_TR33_DEF_TYPE(name, type, oplist, tree_t, it_t) \ + \ + /* Define a node of the tree. \ + Each node of a tree is present in the array of the tree and as such \ + we don't store the reference of other nodes using pointers but using \ + integers. It is shorter and avoids allocating too much data. \ + As such, a node may move on inserting another one, and \ + an iterator is not a pointer on the node but an index in this array. \ + + 'parent' is the parent node of the current node, \ + or M_TR33_ROOT_NODE if it is a root. \ + + 'child' is the first child node of the current node (the left one) \ + or M_TR33_NO_NODE if there is none. \ + + 'left' is the left sibling or M_TR33_NO_NODE if there is none \ + + 'right' is the right sibling or M_TR33_NO_NODE if there is none \ + + 'data' if the data field of the node containing the given type \ + */ \ + typedef struct M_F(name, _node_s) { \ + m_tr33_index_t parent; \ + m_tr33_index_t child; \ + m_tr33_index_t left; \ + m_tr33_index_t right; \ + type data; \ + } M_F(name, _node_ct); \ + \ + /* Define a tree: \ + + size is the number of nodes in the tree, \ + + capacity is the allocated size of the array 'tab' \ + + root_index is the index of the "first" root in the tree. \ + + free_index is the list of free nodes in the array 'tab'. \ + + allow_realloc is a bool encoded as true=0 and false=INT32_MAX \ + + tab is a pointer to the allocated nodes. \ + */ \ + typedef struct M_F(name, _s) { \ + m_tr33_index_t size; \ + m_tr33_index_t capacity; \ + m_tr33_index_t root_index; \ + m_tr33_index_t free_index; \ + uint32_t allow_realloc; \ + M_F(name, _node_ct) *tab; \ + } tree_t[1]; \ + \ + /* Define an iterator that references a node. \ + A node is an internal type, whereas the iterator is not. \ + A node can be moved, whereas the iterator will always remain valid \ + until the node is destroyed. \ + It is composed of: \ + + tree is a pointer to the tree structure \ + + index is an index in the array tab, identifying the node. \ + NOTE: we don't define then using [1] as we want to pass then by value \ + so that we can return iterator on the inserted elements. \ + */ \ + typedef struct M_F(name, _it_s) { \ + struct M_F(name, _s) *tree; \ + m_tr33_index_t index; \ + } it_t; \ + \ + typedef it_t M_F(name, _it_ct); + +/* Define the core & unique methods of a tree */ +#define M_TR33_DEF_P4_CORE(name, type, oplist, tree_t, it_t) \ + /* Initialize a generic tree (empty) */ \ + M_INLINE void \ + M_F(name, _init)(tree_t tree) { \ + tree->size = 0; \ + tree->capacity = 0; \ + tree->root_index = M_TR33_NO_NODE; \ + tree->free_index = M_TR33_NO_NODE; \ + tree->allow_realloc = 0; \ + tree->tab = NULL; \ + M_TR33_CONTRACT(tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _reset)(tree_t tree) { \ + M_TR33_CONTRACT(tree); \ + if (tree->size > 0) { \ + /* We don't scan recursively the node tree, but sequentially */ \ + m_tr33_index_t free_index = tree->free_index; \ + for(m_tr33_index_t i = 0 ; i < tree->capacity ; i ++) { \ + /* If the node is not a free node */ \ + if (tree->tab[i].parent != M_TR33_NO_NODE) { \ + /* free it */ \ + M_CALL_CLEAR(oplist, tree->tab[i].data); \ + tree->tab[i].parent = M_TR33_NO_NODE; \ + tree->tab[i].child = free_index; \ + tree->tab[i].left = M_TR33_NO_NODE; \ + tree->tab[i].right = M_TR33_NO_NODE; \ + free_index = i; \ + } \ + } \ + /* Update the free index */ \ + tree->free_index = free_index; \ + tree->size = 0; \ + tree->root_index = M_TR33_NO_NODE; \ + } \ + M_TR33_CONTRACT(tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _clear)(tree_t tree) { \ + M_F(name, _reset)(tree); \ + struct M_F(name,_node_s)*ptr = tree->tab == NULL ? NULL : tree->tab-1;\ + M_CALL_FREE(oplist, ptr); \ + /* This is so reusing the object implies an assertion failure */ \ + tree->size = 1; \ + tree->tab = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _reserve)(tree_t tree, size_t alloc) { \ + M_TR33_CONTRACT(tree); \ + /* Nothing to do if the request is lower than the current capacity. */ \ + if (alloc <= (size_t) tree->capacity) { \ + return; \ + } \ + /* Realloc the array */ \ + if (M_UNLIKELY_NOMEM (alloc >= INT32_MAX)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _node_s)) * alloc); \ + return; \ + } \ + /* Allocate one more term in the array so that tab[-1] exists. \ + This enables performing latter tab[M_TR33_NO_NODE].x = something; \ + as M_TR33_NO_NODE is -1. This enables avoiding testing for \ + M_TR33_NO_NODE in some cases, performing branchless code. */ \ + struct M_F(name,_node_s)*ptr = tree->tab == NULL ? NULL : tree->tab-1;\ + ptr = M_CALL_REALLOC(oplist, struct M_F(name, _node_s), ptr, alloc+1);\ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _node_s)) * alloc); \ + return; \ + } \ + /* Skip the first term to keep it as empty & unused */ \ + ptr++; \ + /* Free the list */ \ + m_tr33_index_t *free_index = &tree->free_index; \ + if (*free_index != M_TR33_NO_NODE) { \ + while (ptr[*free_index].child != M_TR33_NO_NODE) { \ + free_index = &ptr[*free_index].child; \ + } \ + } \ + *free_index = tree->capacity; \ + /* Construct the list of free node in the extra allocated pool */ \ + for(size_t i = (size_t)tree->capacity; i < alloc; i++) { \ + ptr[i].parent = M_TR33_NO_NODE; \ + ptr[i].left = M_TR33_NO_NODE; \ + ptr[i].right = M_TR33_NO_NODE; \ + ptr[i].child = (m_tr33_index_t)(i+1); \ + } \ + /* The last node has no child in the free node list */ \ + ptr[alloc-1].child = M_TR33_NO_NODE; \ + /* Save the free list state in the tree */ \ + tree->tab = ptr; \ + tree->capacity = (m_tr33_index_t) alloc; \ + M_TR33_CONTRACT(tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _lock)(tree_t tree, bool lock) { \ + M_TR33_CONTRACT(tree); \ + tree->allow_realloc = lock ? INT32_MAX : 0; \ + M_TR33_CONTRACT(tree); \ + } \ + \ + M_INLINE m_tr33_index_t \ + M_C3(m_tr33_, name, _alloc_node)(tree_t tree) { \ + m_tr33_index_t ret = tree->free_index; \ + if (M_UNLIKELY(ret < 0)) { \ + /* No more enough space: realloc the array */ \ + size_t alloc = M_CALL_INC_ALLOC(oplist, (size_t) tree->capacity); \ + /* Take into account if realloc is allowed */ \ + alloc += tree->allow_realloc; \ + if (M_UNLIKELY_NOMEM (alloc >= INT32_MAX)) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _node_s)) * alloc); \ + return M_TR33_NO_NODE; \ + } \ + /* Allocate one more term in the array so that tab[-1] exists. \ + This enables performing latter tab[M_TR33_NO_NODE].x = something; \ + as M_TR33_NO_NODE is -1. This enables avoiding testing for \ + M_TR33_NO_NODE in some cases, performing branchless code. */ \ + struct M_F(name,_node_s)*ptr = tree->tab == NULL ? NULL : tree->tab-1; \ + ptr = M_CALL_REALLOC(oplist, struct M_F(name, _node_s), ptr, alloc+1); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (struct M_F(name, _node_s)) * alloc); \ + return M_TR33_NO_NODE; \ + } \ + /* Skip the first term to keep it as empty & unused */ \ + ptr++; \ + /* Construct the list of free node in the extra allocated pool */ \ + M_ASSERT(tree->capacity >= 0); \ + for(size_t i = (size_t) tree->capacity; i < alloc; i++) { \ + ptr[i].parent = M_TR33_NO_NODE; \ + ptr[i].left = M_TR33_NO_NODE; \ + ptr[i].right = M_TR33_NO_NODE; \ + ptr[i].child = (m_tr33_index_t) i + 1; \ + } \ + /* The last node has no child in the free node list */ \ + ptr[alloc-1].child = M_TR33_NO_NODE; \ + /* Save the free list state in the tree */ \ + tree->tab = ptr; \ + tree->capacity = (m_tr33_index_t) alloc; \ + ret = tree->size; \ + } \ + /* Pop an element in the list of free nodes */ \ + tree->free_index = tree->tab[ret].child; \ + tree->size ++; \ + return ret; \ + } \ + \ + M_INLINE void \ + M_C3(m_tr33_, name, _free_node)(tree_t tree, m_tr33_index_t i) { \ + tree->tab[i].parent = M_TR33_NO_NODE; \ + tree->tab[i].left = M_TR33_NO_NODE; \ + tree->tab[i].right = M_TR33_NO_NODE; \ + tree->tab[i].child = tree->free_index; \ + tree->size --; \ + tree->free_index = i; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _set_root)(tree_t tree, type const data) { \ + M_TR33_CONTRACT(tree); \ + M_F(name, _reset)(tree); \ + m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(tree); \ + tree->tab[i].parent = M_TR33_ROOT_NODE; \ + tree->tab[i].left = M_TR33_NO_NODE; \ + tree->tab[i].right = M_TR33_NO_NODE; \ + tree->tab[i].child = M_TR33_NO_NODE; \ + tree->root_index = i; \ + M_CALL_INIT_SET(oplist, tree->tab[i].data, data); \ + it_t it; \ + it.tree = tree; \ + it.index = tree->root_index; \ + M_TR33_CONTRACT(tree); \ + return it; \ + } \ + \ + /* The iterator references the first root node */ \ + /* usually for pre-order walk */ \ + M_INLINE it_t \ + M_F(name, _it)(tree_t tree) { \ + M_TR33_CONTRACT(tree); \ + it_t it; \ + it.tree = tree; \ + it.index = tree->root_index; \ + M_TR33_IT_CONTRACT(it, false); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _it_end)(tree_t tree) { \ + M_TR33_CONTRACT(tree); \ + it_t it; \ + it.tree = tree; \ + it.index = M_TR33_NO_NODE; \ + M_TR33_IT_CONTRACT(it, false); \ + return it; \ + } \ + \ + M_INLINE bool \ + M_F(name, _end_p)(it_t it) { \ + M_TR33_IT_CONTRACT(it, false); \ + return it.index < 0; \ + } \ + \ + M_INLINE type * \ + M_F(name, _ref)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + return &it.tree->tab[it.index].data; \ + } \ + \ + M_INLINE type const * \ + M_F(name, _cref)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + return M_CONST_CAST(type, &it.tree->tab[it.index].data); \ + } \ + \ + M_INLINE type * \ + M_F(name, _up_ref)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + m_tr33_index_t i = it.tree->tab[it.index].parent; \ + return i < 0 ? NULL : &it.tree->tab[i].data; \ + } \ + \ + M_INLINE type * \ + M_F(name, _down_ref)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + m_tr33_index_t i = it.tree->tab[it.index].child; \ + return i < 0 ? NULL : &it.tree->tab[i].data; \ + } \ + \ + M_INLINE type * \ + M_F(name, _left_ref)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + m_tr33_index_t i = it.tree->tab[it.index].left; \ + return i < 0 ? NULL : &it.tree->tab[i].data; \ + } \ + \ + M_INLINE type * \ + M_F(name, _right_ref)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + m_tr33_index_t i = it.tree->tab[it.index].right; \ + return i < 0 ? NULL : &it.tree->tab[i].data; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_up_raw)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \ + m_tr33_index_t parent = it.tree->tab[it.index].parent; \ + m_tr33_index_t left = it.tree->tab[it.index].left; \ + m_tr33_index_t right = it.tree->tab[it.index].right; \ + it.tree->tab[i].parent = parent; \ + it.tree->tab[i].left = left; \ + it.tree->tab[i].right = right; \ + it.tree->tab[i].child = it.index; \ + it.tree->tab[it.index].parent = i; \ + it.tree->tab[it.index].left = M_TR33_NO_NODE; \ + it.tree->tab[it.index].right = M_TR33_NO_NODE; \ + if (M_UNLIKELY(it.tree->root_index == it.index)) { \ + /* We have added a parent to the root node. Update root index */ \ + it.tree->root_index = i; \ + M_ASSERT( it.tree->tab[i].parent == M_TR33_ROOT_NODE); \ + } else { if (it.tree->tab[parent].child == it.index) { \ + /* Update the parent to point to the new child */ \ + it.tree->tab[parent].child = i; \ + } } \ + it.tree->tab[left].right = i; \ + it.tree->tab[right].left = i; \ + /* Return updated iterator on the inserted node */ \ + it.index = i; \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_up)(it_t pos, type const data) { \ + it_t it = M_F(name, _insert_up_raw)(pos); \ + M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _move_up)(it_t pos, type *data) { \ + it_t it = M_F(name, _insert_up_raw)(pos); \ + M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_down_raw)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \ + m_tr33_index_t child = it.tree->tab[it.index].child; \ + it.tree->tab[i].parent = it.index; \ + it.tree->tab[i].left = M_TR33_NO_NODE; \ + it.tree->tab[i].right = M_TR33_NO_NODE; \ + it.tree->tab[i].child = child; \ + it.tree->tab[it.index].child = i; \ + /* Update the parent of all the childs if at least one exists */ \ + while (child != M_TR33_NO_NODE) { \ + it.tree->tab[child].parent = i; \ + child = it.tree->tab[child].right; \ + } \ + /* Return updated iterator on the inserted node */ \ + it.index = i; \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_down)(it_t pos, type const data) { \ + it_t it = M_F(name, _insert_down_raw)(pos); \ + M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _move_down)(it_t pos, type *data) { \ + it_t it = M_F(name, _insert_down_raw)(pos); \ + M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_child_raw)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + /* Insert a node as a child of another, making the current childreen \ + of the nodes their siblings */ \ + m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \ + m_tr33_index_t child = it.tree->tab[it.index].child; \ + it.tree->tab[i].parent = it.index; \ + it.tree->tab[i].left = M_TR33_NO_NODE; \ + it.tree->tab[i].right = child; \ + it.tree->tab[i].child = M_TR33_NO_NODE; \ + /* Update the parent */ \ + it.tree->tab[it.index].child = i; \ + /* Update the sibling */ \ + it.tree->tab[child].left = i; \ + /* Return updated iterator on the inserted node */ \ + it.index = i; \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_child)(it_t pos, type const data) { \ + it_t it = M_F(name, _insert_child_raw)(pos); \ + M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _move_child)(it_t pos, type *data) { \ + it_t it = M_F(name, _insert_child_raw)(pos); \ + M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_left_raw)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + M_ASSERT(it.index != it.tree->root_index); \ + m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \ + m_tr33_index_t left = it.tree->tab[it.index].left; \ + m_tr33_index_t parent = it.tree->tab[it.index].parent; \ + it.tree->tab[i].parent = parent; \ + it.tree->tab[i].left = left; \ + it.tree->tab[i].right = it.index; \ + it.tree->tab[i].child = M_TR33_NO_NODE; \ + it.tree->tab[it.index].left = i; \ + /* If there is a left node, update its right */ \ + it.tree->tab[left].right = i; \ + if (it.tree->tab[parent].child == it.index) { \ + /* Update the first child of the parent */ \ + it.tree->tab[parent].child = i; \ + } \ + /* Return updated iterator on the inserted node */ \ + it.index = i; \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_left)(it_t pos, type const data) { \ + it_t it = M_F(name, _insert_left_raw)(pos); \ + M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _move_left)(it_t pos, type *data) { \ + it_t it = M_F(name, _insert_left_raw)(pos); \ + M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_right_raw)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + M_ASSERT(it.index != it.tree->root_index); \ + m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \ + m_tr33_index_t right = it.tree->tab[it.index].right; \ + it.tree->tab[i].parent = it.tree->tab[it.index].parent; \ + it.tree->tab[i].left = it.index; \ + it.tree->tab[i].right = right; \ + it.tree->tab[i].child = M_TR33_NO_NODE; \ + it.tree->tab[right].left = i; \ + it.tree->tab[it.index].right = i; \ + /* Return updated iterator on the inserted node */ \ + it.index = i; \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _insert_right)(it_t pos, type const data) { \ + it_t it = M_F(name, _insert_right_raw)(pos); \ + M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE it_t \ + M_F(name, _move_right)(it_t pos, type *data) { \ + it_t it = M_F(name, _insert_right_raw)(pos); \ + M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \ + M_TR33_IT_CONTRACT(it, true); \ + return it; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_up)(it_t *it) { \ + M_ASSERT(it != NULL); \ + M_TR33_IT_CONTRACT(*it, true); \ + m_tr33_index_t i = it->tree->tab[it->index].parent; \ + bool ret = i >= 0; \ + if (M_LIKELY(ret)) { \ + it->index = i; \ + } \ + M_TR33_IT_CONTRACT(*it, true); \ + return ret; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_down)(it_t *it) { \ + M_ASSERT(it != NULL); \ + M_TR33_IT_CONTRACT(*it, true); \ + m_tr33_index_t i = it->tree->tab[it->index].child; \ + bool ret = i >= 0; \ + if (M_LIKELY(ret)) { \ + it->index = i; \ + } \ + M_TR33_IT_CONTRACT(*it, true); \ + return ret; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_left)(it_t *it) { \ + M_ASSERT(it != NULL); \ + M_TR33_IT_CONTRACT(*it, true); \ + m_tr33_index_t i = it->tree->tab[it->index].left; \ + bool ret = i >= 0; \ + if (M_LIKELY(ret)) { \ + it->index = i; \ + } \ + M_TR33_IT_CONTRACT(*it, true); \ + return ret; \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_right)(it_t *it) { \ + M_ASSERT(it != NULL); \ + M_TR33_IT_CONTRACT(*it, true); \ + m_tr33_index_t i = it->tree->tab[it->index].right; \ + bool ret = i >= 0; \ + if (M_LIKELY(ret)) { \ + it->index = i; \ + } \ + M_TR33_IT_CONTRACT(*it, true); \ + return ret; \ + } \ + \ + M_INLINE bool \ + M_F(name, _root_p)(const it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + return it.tree->tab[it.index].parent == M_TR33_ROOT_NODE; \ + } \ + \ + M_INLINE bool \ + M_F(name, _node_p)(const it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + return it.tree->tab[it.index].child != M_TR33_NO_NODE; \ + } \ + \ + M_INLINE bool \ + M_F(name, _leaf_p)(const it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + return it.tree->tab[it.index].child == M_TR33_NO_NODE; \ + } \ + \ + /* Compute the degree of a node in linear time */ \ + M_INLINE int32_t \ + M_F(name, _degree)(const it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + int32_t ret = 0; \ + m_tr33_index_t i = it.tree->tab[it.index].child; \ + while (i >= 0) { \ + ret ++; \ + i = it.tree->tab[i].right; \ + } \ + return ret; \ + } \ + \ + /* Compute the depth of a node in linear time */ \ + M_INLINE int32_t \ + M_F(name, _depth)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + int32_t ret = 0; \ + m_tr33_index_t i = it.tree->tab[it.index].parent; \ + while (i >= 0) { \ + ret ++; \ + i = it.tree->tab[i].parent; \ + } \ + return ret; \ + } \ + \ + M_INLINE struct M_F(name, _s) * \ + M_F(name, _tree)(it_t it) { \ + M_TR33_IT_CONTRACT(it, false); \ + return it.tree; \ + } \ + \ + M_INLINE void \ + M_F(name, _swap_at)(it_t it1, it_t it2, bool swapChild) { \ + M_ASSUME(it1.tree == it2.tree); \ + M_TR33_IT_CONTRACT(it1, true); \ + M_TR33_IT_CONTRACT(it2, true); \ + if (M_UNLIKELY(it1.index == it2.index)) { return; } \ + /* Read all references before modifying anything */ \ + m_tr33_index_t tmp1_l = it1.tree->tab[it1.index].left; \ + m_tr33_index_t tmp2_l = it2.tree->tab[it2.index].left; \ + m_tr33_index_t tmp1_r = it1.tree->tab[it1.index].right; \ + m_tr33_index_t tmp2_r = it2.tree->tab[it2.index].right; \ + m_tr33_index_t tmp1_d = it1.tree->tab[it1.index].child; \ + m_tr33_index_t tmp2_d = it2.tree->tab[it2.index].child; \ + m_tr33_index_t tmp1_u = it1.tree->tab[it1.index].parent; \ + m_tr33_index_t tmp2_u = it2.tree->tab[it2.index].parent; \ + /* Special cases if both nodes are siblings of the same node */ \ + if (tmp1_r == it2.index) { \ + M_ASSERT(tmp2_l == it1.index); \ + tmp1_r = it1.index; \ + tmp2_l = it2.index; \ + } \ + if (tmp2_r == it1.index) { \ + M_ASSERT(tmp1_l == it2.index); \ + tmp2_r = it2.index; \ + tmp1_l = it1.index; \ + } \ + if (tmp1_u == it2.index) { \ + tmp1_u = it1.index; \ + if (tmp2_d == it1.index) { tmp2_d = it2.index; } \ + } \ + if (tmp2_u == it1.index) { \ + tmp2_u = it2.index; \ + if (tmp1_d == it2.index) { tmp1_d = it1.index; } \ + } \ + /* Swap left references */ \ + it1.tree->tab[it1.index].left = tmp2_l; \ + it2.tree->tab[it2.index].left = tmp1_l; \ + it1.tree->tab[tmp1_l].right = it2.index; \ + it2.tree->tab[tmp2_l].right = it1.index; \ + /* Swap right references */ \ + it1.tree->tab[it1.index].right = tmp2_r; \ + it2.tree->tab[it2.index].right = tmp1_r; \ + it1.tree->tab[tmp1_r].left = it2.index; \ + it2.tree->tab[tmp2_r].left = it1.index; \ + /* Swap down references */ \ + if (swapChild == false) { \ + it1.tree->tab[it1.index].child = tmp2_d; \ + it2.tree->tab[it2.index].child = tmp1_d; \ + while (tmp1_d >= 0) { \ + it1.tree->tab[tmp1_d].parent = it2.index; \ + tmp1_d = it1.tree->tab[tmp1_d].right; \ + } \ + while (tmp2_d >= 0) { \ + it2.tree->tab[tmp2_d].parent = it1.index; \ + tmp2_d = it2.tree->tab[tmp2_d].right; \ + } \ + } \ + /* Swap up references */ \ + bool dont_swap_back = true; \ + it1.tree->tab[it1.index].parent = tmp2_u; \ + it2.tree->tab[it2.index].parent = tmp1_u; \ + if (tmp1_u >= 0 && it1.tree->tab[tmp1_u].child == it1.index) { \ + it1.tree->tab[tmp1_u].child = it2.index; \ + dont_swap_back = tmp1_u != tmp2_u; \ + } \ + if (tmp1_u == M_TR33_ROOT_NODE) { \ + it1.tree->root_index = it2.index; \ + M_ASSERT(tmp1_u != tmp2_u); \ + } \ + /* Both may have the same parent (don't swap back in this case) */ \ + if (tmp2_u >= 0 && dont_swap_back && it2.tree->tab[tmp2_u].child == it2.index) { \ + it2.tree->tab[tmp2_u].child = it1.index; \ + } \ + if (tmp2_u == M_TR33_ROOT_NODE) { \ + it2.tree->root_index = it1.index; \ + M_ASSERT(tmp1_u != tmp2_u); \ + } \ + M_TR33_IT_CONTRACT(it1, true); \ + M_TR33_IT_CONTRACT(it2, true); \ + } \ + \ + M_INLINE type * \ + M_F(name, _unlink)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + m_tr33_index_t parent, child, left, right, child_r; \ + parent = it.tree->tab[it.index].parent; \ + child = it.tree->tab[it.index].child; \ + left = it.tree->tab[it.index].left; \ + right = it.tree->tab[it.index].right; \ + /* Test if No child for this node */ \ + if (child == M_TR33_NO_NODE) { \ + remove_child_no_node: \ + /* Remove node from sibling */ \ + it.tree->tab[left].right = right; \ + it.tree->tab[right].left = left; \ + /* Remove node from parent if it is the first child */ \ + if (parent >= 0 && it.tree->tab[parent].child == it.index) { \ + M_ASSERT(left == M_TR33_NO_NODE); \ + it.tree->tab[parent].child = right; \ + } else if (parent == M_TR33_ROOT_NODE) { \ + it.tree->root_index = right; \ + } \ + } else { \ + if (M_UNLIKELY(it.index == it.tree->root_index)) { \ + /* complex case. Swap root with its first child */ \ + it_t it_child = { it.tree, child }; \ + M_F(name, _swap_at)(it, it_child, false); \ + parent = it.tree->tab[it.index].parent; \ + child = it.tree->tab[it.index].child; \ + left = it.tree->tab[it.index].left; \ + right = it.tree->tab[it.index].right; \ + if (child == M_TR33_NO_NODE) { goto remove_child_no_node; } \ + } \ + /* Merge the child with the current siblings */ \ + /* Compute the range of childs & update their parent */ \ + size_t num_child = 1; \ + child_r = child; \ + it.tree->tab[child_r].parent = parent; \ + while (it.tree->tab[child_r].right != M_TR33_NO_NODE) { \ + child_r = it.tree->tab[child_r].right; \ + it.tree->tab[child_r].parent = parent; \ + num_child ++; \ + } \ + (void) num_child; \ + M_ASSERT(it.index != it.tree->root_index || num_child == 1); \ + /* Remove node from sibling */ \ + it.tree->tab[left].right = child; \ + it.tree->tab[child].left = left; \ + it.tree->tab[right].left = child_r; \ + it.tree->tab[child_r].right = right; \ + /* Remove node from parent if it is the first child */ \ + if (parent >= 0 && it.tree->tab[parent].child == it.index) { \ + M_ASSERT(left == M_TR33_NO_NODE); \ + it.tree->tab[parent].child = child; \ + } \ + M_ASSERT (parent != M_TR33_ROOT_NODE); \ + } \ + /* Free the node to the allocator */ \ + M_C3(m_tr33_, name, _free_node)(it.tree, it.index); \ + return &it.tree->tab[it.index].data; \ + } \ + \ + M_INLINE bool \ + M_F(name, _remove)(it_t it) { \ + M_TR33_IT_CONTRACT(it, false); \ + if (M_UNLIKELY(it.index < 0)) { return false; } \ + type *ptr = M_F(name, _unlink)(it); \ + M_CALL_CLEAR(oplist, *ptr); \ + return true; \ + } \ + \ + /* Scan all nodes, first the parent then the children (uses with _it) */ \ + /* pre-order walk */ \ + M_INLINE void \ + M_F(name, _next)(it_t *it) { \ + M_TR33_IT_CONTRACT(*it, true); \ + /* First go down, if impossible go right */ \ + if (M_F(name, _it_down)(it) || M_F(name, _it_right)(it)) { \ + return; \ + } \ + /* If impossible to go right, move up and then right until impossible */ \ + while (M_F(name, _it_up)(it)) { \ + if (M_F(name, _it_right)(it)) { \ + return; \ + } \ + } \ + /* Reach end of tree */ \ + it->index = M_TR33_NO_NODE; \ + M_TR33_IT_CONTRACT(*it, false); \ + } \ + \ + /* Scan all nodes, first the children then the parent */ \ + /* post-order walk */ \ + M_INLINE it_t \ + M_F(name, _it_post)(tree_t tree) { \ + M_TR33_CONTRACT(tree); \ + it_t it; \ + it.tree = tree; \ + it.index = tree->root_index; \ + /* Evaluate child first, so go down to the lowest child */ \ + while (M_F(name, _it_down)(&it)) {} \ + M_TR33_IT_CONTRACT(it, false); \ + return it; \ + } \ + \ + /* Scan all nodes, first the children then the parent (uses with _it_post) */ \ + /* post-order walk */ \ + M_INLINE void \ + M_F(name, _next_post)(it_t *it) { \ + M_TR33_IT_CONTRACT(*it, true); \ + /* First go right */ \ + if (M_F(name, _it_right)(it)) { \ + /* then go down */ \ + while (M_F(name, _it_down)(it)) {} \ + return; \ + } \ + /* If impossible to go right, move up */ \ + if (M_F(name, _it_up)(it)) { \ + return; \ + } \ + /* Reach end of tree */ \ + it->index = M_TR33_NO_NODE; \ + M_TR33_IT_CONTRACT(*it, false); \ + } \ + \ + M_INLINE bool \ + M_F(name, _it_equal_p)(it_t it1, it_t it2) { \ + M_TR33_IT_CONTRACT(it1, false); \ + M_TR33_IT_CONTRACT(it2, false); \ + return it1.tree == it2.tree && it1.index == it2.index; \ + } \ + \ + /* Scan all nodes, first the parent, then the children */ \ + /* post-order walk */ \ + M_INLINE it_t \ + M_F(name, _it_subpre)(it_t it) { \ + /* Nothing to do as it is already on the parent! */ \ + return it; \ + } \ + \ + /* Scan the nodes of it_ref, first the parent then the children */ \ + /* pre-order walk */ \ + M_INLINE void \ + M_F(name, _next_subpre)(it_t *it, it_t it_ref) { \ + M_TR33_IT_CONTRACT(*it, true); \ + M_TR33_IT_CONTRACT(it_ref, true); \ + M_ASSERT(it->tree == it_ref.tree); \ + /* First go down, if impossible go right */ \ + if (M_F(name, _it_down)(it)) { return; } \ + if (M_F(name, _it_right)(it)) { return;} \ + /* If impossible to go right, move up and then right for the all section */ \ + while (M_F(name, _it_up)(it) && it->index != it_ref.index) { \ + if (M_F(name, _it_right)(it)) { \ + return; \ + } \ + } \ + /* Reach end of section */ \ + it->index = M_TR33_NO_NODE; \ + M_TR33_IT_CONTRACT(*it, false); \ + } \ + \ + /* Scan all nodes, first the children then the parent */ \ + /* post-order walk */ \ + M_INLINE it_t \ + M_F(name, _it_subpost)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + /* Evaluate child first, so go down to the lowest child */ \ + while (M_F(name, _it_down)(&it)) {} \ + M_TR33_IT_CONTRACT(it, false); \ + return it; \ + } \ + \ + /* Scan all nodes, first the children then the parent (uses with _it_subpost) */ \ + /* post-order walk */ \ + M_INLINE void \ + M_F(name, _next_subpost)(it_t *it, it_t ref) { \ + M_TR33_IT_CONTRACT(*it, true); \ + M_TR33_IT_CONTRACT(ref, true); \ + M_ASSERT(it->tree == ref.tree); \ + if (it->index == ref.index) { \ + /* Reach end of tree */ \ + it->index = M_TR33_NO_NODE; \ + return; \ + } \ + /* First go right */ \ + if (M_F(name, _it_right)(it)) { \ + /* then go down */ \ + while (M_F(name, _it_down)(it)) {} \ + return; \ + } \ + /* If impossible to go right, move up */ \ + bool b = M_F(name, _it_up)(it); \ + (void) b; /* parameter not used */ \ + assert(b); \ + } \ + \ + M_INLINE void \ + M_F(name, _prune)(it_t it) { \ + M_TR33_IT_CONTRACT(it, true); \ + /* remove the node, including its childs */ \ + it_t child = M_F(name, _it_subpost)(it); \ + while (!M_F(name, _end_p)(child)) { \ + it_t next = child; \ + M_F(name, _next_subpost)(&next, it); \ + bool b = M_F(name, _remove)(child); \ + (void) b; /* parameter not used */ \ + M_ASSERT(b); \ + child = next; \ + } \ + } \ + \ + M_INLINE it_t \ + M_F(name, _lca)(it_t it1, it_t it2) { \ + M_TR33_IT_CONTRACT(it1, true); \ + M_TR33_IT_CONTRACT(it2, true); \ + M_ASSERT(it1.tree == it2.tree); \ + /* Compute the Lowest Common Ancestor in linear time */ \ + int32_t d1 = M_F(name, _depth)(it1); \ + int32_t d2 = M_F(name, _depth)(it2); \ + bool b = true; \ + if (d1 > d2) { \ + M_SWAP(int32_t, d1, d2); \ + M_SWAP(it_t, it1, it2); \ + } \ + /* it2 is deeper than it1 */ \ + while (d1 < d2) { \ + b = M_F(name, _it_up)(&it2); \ + d2--; \ + assert(b); \ + } \ + /* Move up both iterators until we found the common node */ \ + while (b && it1.index != it2.index) { \ + b = M_F(name, _it_up)(&it1); \ + b = M_F(name, _it_up)(&it2); \ + } \ + /* If we went back to the root node, we must have found a common node*/ \ + M_ASSERT(b); \ + return it1; \ + } \ + \ + M_INLINE void \ + M_F(name, _graft_child)(it_t it1, const it_t it2) { \ + M_ASSERT(it1.tree == it2.tree); \ + M_TR33_IT_CONTRACT(it1, true); \ + M_TR33_IT_CONTRACT(it2, true); \ + M_ASSERT(it2.index != it2.tree->root_index); \ + /* Move the node it2 and its child down the node *it1 */ \ + /* Both belongs to the same tree */ \ + const m_tr33_index_t i = it2.index; \ + /* Unlink it2 except its child */ \ + const m_tr33_index_t parent = it1.tree->tab[i].parent; \ + if (parent >= 0 && it1.tree->tab[parent].child == i) { \ + it1.tree->tab[parent].child = it1.tree->tab[i].right; \ + } \ + const m_tr33_index_t left = it1.tree->tab[i].left; \ + it1.tree->tab[left].right = it1.tree->tab[i].right; \ + const m_tr33_index_t right = it1.tree->tab[i].right; \ + it1.tree->tab[right].left = it1.tree->tab[i].left; \ + /* Add the new node */ \ + const m_tr33_index_t child = it1.tree->tab[it1.index].child; \ + it1.tree->tab[i].parent = it1.index; \ + it1.tree->tab[i].left = M_TR33_NO_NODE; \ + it1.tree->tab[i].right = child; \ + /* Update the parent */ \ + it1.tree->tab[it1.index].child = i; \ + /* Update the sibling */ \ + it1.tree->tab[child].left = i; \ + M_TR33_IT_CONTRACT(it1, true); \ + M_TR33_IT_CONTRACT(it2, true); \ + } \ + \ + M_IF_METHOD(CMP,oplist)( \ + M_INLINE void \ + M_F(name, _sort_child)(it_t it0) { \ + M_TR33_IT_CONTRACT(it0, true); \ + it_t it1 = it0; \ + /* Go to the child, if it doesn't exist, nothing to sort */ \ + if (!M_F(name, _it_down)(&it1) ) return ; \ + /* Selection sort */ \ + do { \ + it_t it_min = it1; \ + it_t it2 = it1; \ + while (M_F(name, _it_right)(&it2)) { \ + if (M_CALL_CMP(oplist, *M_F(name, _cref)(it2), *M_F(name, _cref)(it_min)) < 0) { \ + it_min = it2; \ + } \ + } \ + if (M_F(name, _it_equal_p)(it_min, it1) == false) { \ + M_F(name, _swap_at)(it1, it_min, true); \ + /* The iterator it1 is no longer the min */ \ + it1 = it_min; \ + } \ + } while (M_F(name, _it_right)(&it1)); \ + M_TR33_IT_CONTRACT(it0, true); \ + } \ + , /* No CMP */ ) \ + + +/* Define the classic extended missing methods of a tree */ +#define M_TR33_DEF_P4_EXT(name, type, oplist, tree_t, it_t) \ + M_INLINE void \ + M_F(name, _init_set)(tree_t tree, const tree_t ref) { \ + tree->size = ref->size; \ + tree->capacity = ref->capacity; \ + tree->root_index = ref->root_index; \ + tree->free_index = ref->free_index; \ + tree->allow_realloc = ref->allow_realloc; \ + size_t alloc = (size_t) ref->capacity; \ + if (ref->tab == NULL) { \ + tree->tab = NULL; \ + } else { \ + struct M_F(name, _node_s) *ptr = \ + M_CALL_REALLOC(oplist, struct M_F(name, _node_s), NULL, alloc+1); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof(struct M_F(name, _node_s)) * alloc); \ + return; \ + } \ + tree->tab = ++ptr; \ + /* We don't scan recursively the node tree, but sequentially */ \ + for(m_tr33_index_t i = 0 ; i < ref->capacity ; i ++) { \ + tree->tab[i].parent = ref->tab[i].parent; \ + tree->tab[i].child = ref->tab[i].child; \ + tree->tab[i].left = ref->tab[i].left; \ + tree->tab[i].right = ref->tab[i].right; \ + /* If the node is not a free node, copy the data */ \ + if (tree->tab[i].parent != M_TR33_NO_NODE) { \ + M_CALL_INIT_SET(oplist, tree->tab[i].data, ref->tab[i].data); \ + } \ + } \ + } \ + M_TR33_CONTRACT(tree); \ + } \ + \ + M_INLINE void \ + M_F(name, _set)(tree_t tree, const tree_t ref) { \ + /* No optimum, but good enought for present time */ \ + M_F(name, _clear)(tree); \ + M_F(name, _init_set)(tree, ref); \ + } \ + \ + M_INLINE void \ + M_F(name, _init_move)(tree_t tree, tree_t ref) { \ + tree->size = ref->size; \ + tree->capacity = ref->capacity; \ + tree->root_index = ref->root_index; \ + tree->free_index = ref->free_index; \ + tree->allow_realloc = ref->allow_realloc; \ + tree->tab = ref->tab; \ + /* This is so reusing the object implies an assertion failure */ \ + ref->size = 1; \ + ref->tab = NULL; \ + } \ + \ + M_INLINE void \ + M_F(name, _move)(tree_t tree, tree_t ref) { \ + M_F(name, _clear)(tree); \ + M_F(name, _init_move)(tree, ref); \ + } \ + \ + M_INLINE void \ + M_F(name, _swap)(tree_t tree1, tree_t tree2) { \ + M_TR33_CONTRACT(tree1); \ + M_TR33_CONTRACT(tree2); \ + M_SWAP(m_tr33_index_t, tree1->size, tree2->size); \ + M_SWAP(m_tr33_index_t, tree1->capacity, tree2->capacity); \ + M_SWAP(m_tr33_index_t, tree1->root_index, tree2->root_index); \ + M_SWAP(m_tr33_index_t, tree1->free_index, tree2->free_index); \ + M_SWAP(unsigned, tree1->allow_realloc, tree2->allow_realloc); \ + M_SWAP(M_F(name, _node_ct) *, tree1->tab, tree2->tab); \ + M_TR33_CONTRACT(tree1); \ + M_TR33_CONTRACT(tree2); \ + } \ + \ + M_INLINE size_t \ + M_F(name, _size)(const tree_t tree) { \ + M_TR33_CONTRACT(tree); \ + return (size_t) tree->size; \ + } \ + \ + M_INLINE bool \ + M_F(name, _empty_p)(const tree_t tree) { \ + M_TR33_CONTRACT(tree); \ + return tree->size == 0; \ + } \ + \ + M_INLINE size_t \ + M_F(name, _capacity)(const tree_t tree) { \ + M_TR33_CONTRACT(tree); \ + return (size_t) tree->capacity; \ + } \ + \ + /* Service not really usefull as the affectation operator works with it */\ + M_INLINE void \ + M_F(name, _it_set)(it_t *dst, it_t src ){ \ + *dst = src; \ + } \ + \ + M_IF_METHOD(EQUAL, oplist)( \ + M_INLINE bool \ + M_F(name, _equal_p)(/*const*/ tree_t t1, /*const*/ tree_t t2) { \ + M_TR33_CONTRACT(t1); \ + M_TR33_CONTRACT(t2); \ + /* Fast case if the sizes don't match */ \ + if (M_LIKELY(t1->size != t2->size)) { \ + return false; \ + } \ + /* Slow case. We need to scan both tree \ + and check if we move in the same way \ + while checking also the data \ + */ \ + it_t it1 = M_F(name, _it)(t1); \ + it_t it2 = M_F(name, _it)(t2); \ + while (!M_F(name, _end_p)(it1)) { \ + /* Since both trees have the same size, \ + both iterators shall end at the same time. */ \ + M_ASSERT(!M_F(name, _end_p)(it2)); \ + bool b = M_CALL_EQUAL(oplist, *M_F(name, _cref)(it1), *M_F(name, _cref)(it2)); \ + if (!b) return false; \ + /* First go down, if impossible go right */ \ + if (M_F(name, _it_down)(&it1) ) { \ + b = M_F(name, _it_down)(&it2); \ + if (!b) return false; \ + continue; \ + } \ + if (M_F(name, _it_right)(&it1)) { \ + b = M_F(name, _it_right)(&it2); \ + if (!b) return false; \ + continue; \ + } \ + /* If impossible, move up and then right until impossible */ \ + while (M_F(name, _it_up)(&it1)) { \ + /* Both iterators have move down the same tree by the same \ + amount. Therefore if we can move up one iterator, we can \ + move the other one up too*/ \ + b = M_F(name, _it_up)(&it2); \ + M_ASSERT (b); \ + if (M_F(name, _it_right)(&it1)) { \ + /* it2 right child may not exist */ \ + b = M_F(name, _it_right)(&it2); \ + if (!b) return false; \ + goto do_continue; \ + } \ + } \ + /* Both tree have the same size and the same "local" depth \ + Since there is no longer any node up iterator it1, \ + there shall not be any node for iterator it2 (same size \ + same depth) \ + */ \ + M_ASSERT( M_F(name, _it_up)(&it2) == false); \ + return true; \ + /* Reach end of tree */ \ + do_continue: \ + continue; \ + } \ + M_ASSERT(M_F(name, _end_p)(it2)); \ + return true; \ + } \ + , /* No EQUAL */ ) \ + \ + M_IF_METHOD(HASH, oplist)( \ + M_INLINE size_t \ + M_F(name, _hash)(/* const */ tree_t t1) { \ + M_HASH_DECL(hash); \ + for(it_t it = M_F(name, _it)(t1); \ + !M_F(name, _end_p)(it) ; \ + M_F(name, _next)(&it)) { \ + size_t h = M_CALL_HASH(oplist, *M_F(name, _cref)(it)); \ + M_HASH_UP(hash, h); \ + } \ + return M_HASH_FINAL(hash); \ + } \ + , /* No HASH */ ) \ + + +/* Define the IO methods of a tree */ +#define M_TR33_DEF_P4_IO(name, type, oplist, tree_t, it_t) \ +M_IF_METHOD(GET_STR, oplist)( \ +M_INLINE void \ +M_F(name, _get_str)(string_t str, /*const*/ tree_t tree, bool append) { \ + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \ + it_t it = M_F(name, _it)(tree); \ + while (!M_F(name, _end_p)(it)) { \ + m_string_push_back (str, '{'); \ + type const *item = M_F(name, _cref)(it); \ + M_CALL_GET_STR(oplist, str, *item, true); \ + /* Go down the tree */ \ + if (M_F(name, _it_down)(&it)) { \ + m_string_push_back (str, M_GET_SEPARATOR oplist); \ + m_string_push_back (str, '['); \ + continue; \ + } \ + m_string_push_back (str, '}'); \ + if (M_F(name, _it_right)(&it)) { \ + m_string_push_back (str, M_GET_SEPARATOR oplist); \ + continue; \ + } \ + while (M_F(name, _it_up)(&it)) { \ + m_string_push_back (str, ']'); \ + m_string_push_back (str, '}'); \ + if (M_F(name, _it_right)(&it)) { \ + m_string_push_back (str, M_GET_SEPARATOR oplist); \ + goto continue_tree; \ + } \ + } \ + it = M_F(name, _it_end)(tree); \ + continue_tree: \ + (void) 0; \ + } \ + m_string_push_back (str, ']'); \ +} \ +, /* No GET_STR */ ) \ + \ +M_IF_METHOD(PARSE_STR, oplist)( \ +M_INLINE bool \ +M_F(name, _parse_str)(tree_t tree, const char str[], const char **endp) { \ + M_TR33_CONTRACT(tree); \ + int cmd = 0; \ + type item; \ + it_t it; \ + M_F(name, _reset)(tree); \ + bool success = false; \ + int c = *str; \ + if (M_UNLIKELY (c != '[')) goto exit; \ + c = *++str; \ + if (M_UNLIKELY (c == ']')) { success = true; str++; goto exit; } \ + if (M_UNLIKELY (c == 0)) goto exit; \ + M_CALL_INIT(oplist, item); \ + it = M_F(name, _it_end)(tree); \ + while (true) { \ + c = *str++; \ + if (c != '{') goto exit_clear; \ + bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \ + c = *str++; \ + if (b == false || c == 0) { goto exit_clear; } \ + /* Insert the item as the root or as a right sibling or as a child */ \ + switch (cmd) { \ + case 0: it = M_F(name, _set_root)(tree, item); break; \ + case 1: it = M_F(name, _insert_right)(it, item); break; \ + case 2: it = M_F(name, _insert_child)(it, item); break; \ + default: M_ASSERT(0); \ + } \ + if (c == ',') { \ + /* The current item has some children */ \ + c = *str++; \ + if (c != '[') { goto exit_clear; } \ + /* next is a child: push_down */ \ + cmd = 2; \ + continue; \ + } \ + /* The current item has no children. */ \ + if (c != '}') { goto exit_clear; } \ + /* Scan the next character to decide where to move (right or up) */ \ + c = *str++; \ + if (c == ']') { \ + do { \ + /* move up. It we cannot, we have reached the end */ \ + if (!M_F(name, _it_up)(&it)) { goto exit_success; } \ + c = *str++; \ + if (c != '}') { goto exit_clear; } \ + c = *str++; \ + } while (c == ']'); \ + } \ + if (c != ',') { goto exit_clear; } \ + /* next is a sibling: push_right */ \ + cmd = 1; \ + } \ +exit_success: \ + success = true; \ +exit_clear: \ + M_CALL_CLEAR(oplist, item); \ +exit: \ + if (endp) *endp = str; \ + M_TR33_CONTRACT(tree); \ + return success; \ +} \ +, /* No PARSE_STR */ ) \ + \ +M_IF_METHOD(OUT_STR, oplist)( \ +M_INLINE void \ +M_F(name, _out_str)(FILE *f, /*const*/ tree_t tree) { \ + fputc('[', f); \ + it_t it = M_F(name, _it)(tree); \ + while (!M_F(name, _end_p)(it)) { \ + fputc('{', f); \ + type const *item = M_F(name, _cref)(it); \ + M_CALL_OUT_STR(oplist, f, *item); \ + /* Go down the tree */ \ + if (M_F(name, _it_down)(&it)) { \ + fputc (M_GET_SEPARATOR oplist, f); \ + fputc ('[', f); \ + continue; \ + } \ + fputc ('}', f); \ + if (M_F(name, _it_right)(&it)) { \ + fputc (M_GET_SEPARATOR oplist, f); \ + continue; \ + } \ + while (M_F(name, _it_up)(&it)) { \ + fputc (']', f); \ + fputc ('}', f); \ + if (M_F(name, _it_right)(&it)) { \ + fputc (M_GET_SEPARATOR oplist, f); \ + goto continue_tree; \ + } \ + } \ + it = M_F(name, _it_end)(tree); \ + continue_tree: \ + (void) 0; \ + } \ + fputc (']', f); \ +} \ +, /* No OUT_STR */ ) \ + \ +M_IF_METHOD(IN_STR, oplist)( \ +M_INLINE bool \ +M_F(name, _in_str)(tree_t tree, FILE *f) { \ + M_TR33_CONTRACT(tree); \ + int cmd = 0; \ + type item; \ + it_t it; \ + M_F(name, _reset)(tree); \ + bool success = false; \ + int c = fgetc(f); \ + if (M_UNLIKELY (c != '[')) goto exit; \ + c = fgetc(f); \ + if (M_UNLIKELY (c == ']')) { success = true; goto exit; } \ + ungetc(c, f); \ + if (M_UNLIKELY (c == 0)) goto exit; \ + M_CALL_INIT(oplist, item); \ + it = M_F(name, _it_end)(tree); \ + while (true) { \ + c = fgetc(f); \ + if (c != '{') goto exit_clear; \ + bool b = M_CALL_IN_STR(oplist, item, f); \ + c = fgetc(f); \ + if (b == false || c == 0) { goto exit_clear; } \ + /* Insert the item as the root or as a right sibling or as a child */ \ + switch (cmd) { \ + case 0: it = M_F(name, _set_root)(tree, item); break; \ + case 1: it = M_F(name, _insert_right)(it, item); break; \ + case 2: it = M_F(name, _insert_child)(it, item); break; \ + default: M_ASSERT(0); \ + } \ + if (c == ',') { \ + /* The current item has some children */ \ + c = fgetc(f); \ + if (c != '[') { goto exit_clear; } \ + /* next is a child: push_down */ \ + cmd = 2; \ + continue; \ + } \ + /* The current item has no children. */ \ + if (c != '}') { goto exit_clear; } \ + /* Scan the next character to decide where to move (right or up) */ \ + c = fgetc(f); \ + if (c == ']') { \ + do { \ + /* move up. It we cannot, we have reached the end */ \ + if (!M_F(name, _it_up)(&it)) { goto exit_success; } \ + c = fgetc(f); \ + if (c != '}') { goto exit_clear; } \ + c = fgetc(f); \ + } while (c == ']'); \ + } \ + if (c != ',') { goto exit_clear; } \ + /* next is a sibling: push_right */ \ + cmd = 1; \ + } \ +exit_success: \ + success = true; \ +exit_clear: \ + M_CALL_CLEAR(oplist, item); \ +exit: \ + M_TR33_CONTRACT(tree); \ + return success; \ +} \ +, /* No IN_STR */ ) \ + +/* Define the emplace methods of a tree */ +#define M_TR33_DEF_P4_EMPLACE(name, type, oplist, tree_t, it_t) \ + M_EMPLACE_QUEUE_DEF(name, tree_t, M_F(name, _emplace_root), oplist, M_TR33_EMPLACE_ROOT_DEF) \ + M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_up), oplist, M_TR33_EMPLACE_UP_DEF) \ + M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_down), oplist, M_TR33_EMPLACE_DOWN_DEF) \ + M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_child), oplist, M_TR33_EMPLACE_CHILD_DEF) \ + M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_left), oplist, M_TR33_EMPLACE_LEFT_DEF) \ + M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_right), oplist, M_TR33_EMPLACE_RIGHT_DEF) + +/* Definition of the emplace_back functions */ +#define M_TR33_EMPLACE_ROOT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE M_F(name, _it_ct) \ + function_name(name_t tree \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_TR33_CONTRACT(tree); \ + M_F(name, _reset)(tree); \ + m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(tree); \ + tree->tab[i].parent = M_TR33_ROOT_NODE; \ + tree->tab[i].left = M_TR33_NO_NODE; \ + tree->tab[i].right = M_TR33_NO_NODE; \ + tree->tab[i].child = M_TR33_NO_NODE; \ + tree->root_index = i; \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, tree->tab[i].data, \ + exp_emplace_type); \ + M_F(name, _it_ct) it; \ + it.tree = tree; \ + it.index = tree->root_index; \ + M_TR33_CONTRACT(tree); \ + return it; \ + } + +#define M_TR33_EMPLACE_UP_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE name_t \ + function_name(name_t pos \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + name_t it = M_F(name, _insert_up_raw)(pos); \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \ + exp_emplace_type); \ + return it; \ + } + +#define M_TR33_EMPLACE_DOWN_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE name_t \ + function_name(name_t pos \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + name_t it = M_F(name, _insert_down_raw)(pos); \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \ + exp_emplace_type); \ + return it; \ + } + +#define M_TR33_EMPLACE_CHILD_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE name_t \ + function_name(name_t pos \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + name_t it = M_F(name, _insert_child_raw)(pos); \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \ + exp_emplace_type); \ + return it; \ + } + +#define M_TR33_EMPLACE_LEFT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE name_t \ + function_name(name_t pos \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + name_t it = M_F(name, _insert_left_raw)(pos); \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \ + exp_emplace_type); \ + return it; \ + } + +#define M_TR33_EMPLACE_RIGHT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE name_t \ + function_name(name_t pos \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + name_t it = M_F(name, _insert_right_raw)(pos); \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \ + exp_emplace_type); \ + return it; \ + } + +// TODO: +// * serialization +// * OPLIST ? +// * _previous ? + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define TREE_DEF M_TREE_DEF +#define TREE_DEF_AS M_TREE_DEF_AS +#define TREE_OPLIST M_TREE_OPLIST +#endif + +#endif diff --git a/components/mlib/m-try.h b/components/mlib/m-try.h new file mode 100644 index 00000000..504f8d63 --- /dev/null +++ b/components/mlib/m-try.h @@ -0,0 +1,578 @@ +/* + * M*LIB - try / catch mechanism for M*LIB + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_TRY_H +#define MSTARLIB_TRY_H + +#include "m-core.h" +#include "m-thread.h" + +/* + * Select mechanism to use for support of RAII and exception, + * so that for each variable defined using M_LET, + * its destructor is still called when exceptions are thrown. + * It is either the C++ try, + * or it uses a GCC or CLANG extension, + * or the standard C compliant way (much slower). + * The user can override the desired mechanism. + */ +#ifndef M_USE_TRY_MECHANISM +# if defined(__has_extension) +# if __has_extension(blocks) +# define M_TRY_CLANG_BLOCKS +# endif +# endif +# if defined(__cplusplus) +# define M_USE_TRY_MECHANISM 1 +# elif defined(M_TRY_CLANG_BLOCKS) +# define M_USE_TRY_MECHANISM 2 +# elif defined(__GNUC__) && !defined(__clang__) +# define M_USE_TRY_MECHANISM 3 +# else +# define M_USE_TRY_MECHANISM 4 +# endif +#endif + + +/* + * Start a protected section of code 'name' where all exceptions are catched + * by the associated CATCH section. + */ +#define M_TRY(name) \ + M_TRY_B( M_C(m_try_bool_, name), M_C(m_try_buf_, name), name) + + +/* + * Catch an exception associated to the TRY block 'name' that matches the given error_code + * If error_code is 0, it catches all error codes. + * error code shall be a constant positive integer. + */ +#define M_CATCH(name, error_code) M_CATCH_B(name, error_code) + + +/* + * Throw an exception to the upper try block + * error_code shall be the first argument. + * Other arguments are integers or pointers stored in the exception. + * error code shall be a constant positive integer. + * There is no genericity of the exception data structure itself. + */ +#define M_THROW(...) do { \ + M_STATIC_ASSERT(M_RET_ARG1 (__VA_ARGS__) != 0, \ + M_LIB_NOT_A_CONSTANT_NON_NULL_INTEGER, \ + "The error code shall be a non null positive constant"); \ + M_STATIC_ASSERT(M_NARGS (__VA_ARGS__) <= 1+M_USE_MAX_CONTEXT, \ + M_LIB_TOO_MANY_ARGUMENTS, \ + "There are too many arguments for an exception."); \ + M_IF_NARGS_EQ1(__VA_ARGS__)(M_THROW_1, M_THROW_N)(__VA_ARGS__); \ + } while (0) + + +/* + * Size of the context data that are stored in an exception data structure. + */ +#ifndef M_USE_MAX_CONTEXT +#define M_USE_MAX_CONTEXT 10 +#endif + +/* + * The exception itself. + * + * It is POD data where every fields can be used by the user. + * It has been decided to have only one exception data structure + * to simplify error code and because : + * - using generic types is much harder in C to do (still possible) + * - it will make exceptions more usable for errors which should not + * be handled by exceptions. + * + * For C++, we need to encapsulate it in a template, + * so that it can be a unique type for each error code, + * which is needed for the catch mechanism. + * We all need to override the operator -> since the C++ + * throw the type and catch the type, whereas the C back-end + * throw the type and catch a pointer to the type: + * within the catch block you are supposed to use the arrow + * operator to test the content of the exception. + */ +#if M_USE_TRY_MECHANISM == 1 +namespace m_lib { +template +#endif +struct m_exception_s { + unsigned error_code; // Error code + unsigned short line; // Line number where the error was detected + unsigned short num; // Number of entries in 'context' table + const char *filename; // filename where the error was detected + intptr_t context[M_USE_MAX_CONTEXT]; // Specific context of the exception +#ifdef __cplusplus + m_exception_s *operator->() { return this; } +#endif +}; +#if M_USE_TRY_MECHANISM == 1 +} +#endif + +// Typical Error codes (TODO: add more classic?) +#define M_ERROR_MEMORY 1 +#define M_ERROR_ACCESS 2 +#define M_ERROR_BUSY 3 + +/* + * Define all global needed by the try mechanism with a + * thread attribute. It needs to be defined once in all the program + */ +#define M_TRY_DEF_ONCE() M_TRY_DEF_ONCE_B() + + +/* + * Re-throw the last exception + * It shall be done in a CATCH block. + */ +#define M_RETHROW() m_rethrow() + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* + * Define the C++ back-end. + * It is fully different from C back-end as it reuses the classic try of the C++. + * Surprisingly it has more constraints than the C one. + * error_code shall be a positive, constant integer. + * the catch all block shall always be the last block. + * at least catch block is mandatory for each try block. + * Note that theses constraints are meaningless in real code, + * and simply good behavior. + * Notice also that you won't have any access to the exception for a catch all error. + */ +#if M_USE_TRY_MECHANISM == 1 + +// Define the CATCH block. If error_code is 0, it shall catch all errors. +// NOTE: It will even catch non M*LIB errors. +#define M_CATCH_B(name, error_code) \ + M_IF(M_BOOL(error_code)) \ + (catch (m_lib::m_exception_s &name), catch (...)) + +// No global to define in C++ +#define M_TRY_DEF_ONCE_B() /* Nothing to do */ + +// Reuse the try keyword of the C++ +#define M_TRY_B(cont, buf, exception) \ + try + +// Reuse the throw keyword of the C++ +// by throwing the type m_lib::m_exception_s +#define M_THROW_1(error_code) \ + throw m_lib::m_exception_s{ error_code, __LINE__, 0, __FILE__, { 0 } } + +// Reuse the throw keyword of the C++ +// by throwing the type m_lib::m_exception_s +#define M_THROW_N(error_code, ...) \ + throw m_lib::m_exception_s{ error_code, __LINE__, \ + M_NARGS(__VA_ARGS__), __FILE__, { __VA_ARGS__ } } + +// Nothing to inject for a pre initialization of a M*LIB object +#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) /* Nothing to do */ + +// Code to inject for a post initialization of a M*LIB object +// We create a C++ object with a destructor that will call the CLEAR operator of the M*LIB object +// by using a lambda function. +// If the CLEAR operator is called naturally, we disable the destructor of the C++ object. +#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \ + for(m_lib::m_regclear M_C(m_try_regclear_, name){[&](void) { M_CALL_CLEAR(oplist, name); } } \ + ; cont ; M_C(m_try_regclear_, name).disable() ) + +// M_DEFER Injection / pre initialization +#define M_DEFER_TRY_INJECT_PRE_B(cont, ...) /* Nothing to do */ + +// M_DEFER Injection / post initialization +// Register the stack frame and tests for the longjmp. +// In which case call the 'clear' operations (...), unstack the error list and rethrow the error. +#define M_DEFER_TRY_INJECT_POST_B(cont, ...) \ + for(m_lib::m_regclear M_C(m_try_regclear_, cont){[&](void) { __VA_ARGS__; } } \ + ; cont ; M_C(m_try_regclear_, cont).disable() ) + +// Definition of the C++ object wrapper +// The registered function is called by the destructor, +// except if the disable function has been called. +#include +namespace m_lib { + class m_regclear { + std::function function; + bool done; + public: + inline m_regclear(const std::function &f) : function{f}, done{false} { } + inline void disable(void) { done = true; } + inline ~m_regclear() { if (done == false) { function(); done = true; } } + }; +} + +// Rethrow is simply throw without any argument +#define m_rethrow() throw + + +/*****************************************************************************/ + +/* The C back-end. + * It is fully different from the C++ back-end and is based on setjmp/lonjmp + * (classic implementation). + * The main difficulty is the mechanism to register the CLEAR operators + * to call when throwing an exception. + * Contrary to the C++ back-end, it is not cost-free as it adds some + * instructions to the normal behavior of the program. + */ +#else + +#if (M_USE_TRY_MECHANISM == 3) +// Use of builtin setjmp / longjmp for GCC +// There are at least twice faster at worst, and reduce stack consumption +// See https://gcc.gnu.org/onlinedocs/gcc/Nonlocal-Gotos.html +// CLANG doesn't support these builtins officialy (https://groups.google.com/g/llvm-dev/c/9QgfdW23K8M) +#define m_try_setjmp(x) __builtin_setjmp(x) +#define m_try_longjmp(x,v) __builtin_longjmp(x, v) +typedef intptr_t m_try_jmp_buf[5]; +#define m_try_jmp_buf m_try_jmp_buf +#else +// C compliant setjmp +#include +#define m_try_setjmp(x) setjmp(x) +#define m_try_longjmp(x,v) longjmp(x, v) +#define m_try_jmp_buf jmp_buf +#endif + +// Define the CATCH block associated to the 'name' TRY to catch the exception +// associated to 'error_code' and provide 'name' as a pointer to the exception +// if the exception matches the error code. +// If error code is 0, it matches all errors. +#define M_CATCH_B(name, error_code) \ + else if (m_catch( M_C(m_try_buf_, name), (error_code), &name)) + +// Define the operator to define nested functions (GCC) or blocks (CLANG) +#if M_USE_TRY_MECHANISM == 2 +# define M_TRY_FUNC_OPERATOR ^ +#else +# define M_TRY_FUNC_OPERATOR * +#endif + +// Define the linked structure used to identify what is present in the C stack. +// We create for each M_TRY and each M_LET a new node in the stack that represents +// this point in the stack frame. Each nodes are linked together, so that we can +// analyze the stack frame on exception. +typedef struct m_try_s { + enum { M_STATE_TRY, M_STATE_EXCEPTION_IN_PROGRESS, M_STATE_EXCEPTION_CATCHED, + M_STATE_CLEAR_JMPBUF, M_STATE_CLEAR_CB } kind; + struct m_try_s *next; + union { + m_try_jmp_buf buf; + struct { void (M_TRY_FUNC_OPERATOR func)(void*); void *data; } clear; + } data; +} m_try_t[1]; + +// Define the TRY block. +// Classic usage of the for trick to push destructor on the exit path. +#define M_TRY_B(cont, buf, exception) \ + for(bool cont = true ; cont ; cont = false) \ + for(m_try_t buf ; cont ; m_try_clear(buf), cont = false ) \ + for(const struct m_exception_s *exception = NULL; cont; cont = false, exception = exception) \ + if (m_try_init(buf)) + +// Throw the error code +#define M_THROW_1(error_code) \ + m_throw( &(const struct m_exception_s) { error_code, __LINE__, 0, __FILE__, { 0 } } ) + +// Throw the error code +#define M_THROW_N(error_code, ...) \ + m_throw( &(const struct m_exception_s) { error_code, __LINE__, M_NARGS(__VA_ARGS__), __FILE__, \ + { __VA_ARGS__ } } ) + +// Copy an exception to another. +M_INLINE void +m_exception_set(struct m_exception_s *out, const struct m_exception_s *in) +{ + if (in != out) { + memcpy(out, in, sizeof *out); + } +} + +// The global thread attribute variables and functions. +extern M_THREAD_ATTR struct m_try_s *m_global_error_list; +extern M_THREAD_ATTR struct m_exception_s m_global_exception; +extern M_ATTR_NO_RETURN M_ATTR_COLD_FUNCTION void m_throw(const struct m_exception_s *exception); + +// Macro to add once in one source file to define theses global: +#define M_TRY_DEF_ONCE_B() \ + M_THREAD_ATTR struct m_try_s *m_global_error_list; \ + M_THREAD_ATTR struct m_exception_s m_global_exception; \ + \ + /* Throw the given exception \ + This function should be rarely called. */ \ + M_ATTR_NO_RETURN M_ATTR_COLD_FUNCTION void \ + m_throw(const struct m_exception_s *exception) \ + { \ + /* Analyze the error list to see what has been registered */ \ + struct m_try_s *e = m_global_error_list; \ + while (e != NULL) { \ + /* A CLEAR operator has been registered: call it */ \ + if (e->kind == M_STATE_CLEAR_CB) { \ + e->data.clear.func(e->data.clear.data); \ + } \ + else { \ + /* A JUMP command has been registered. \ + * Either due to the M_TRY block or \ + * because of the jump to the CLEAR operator of the object to clear. */ \ + M_ASSERT(e->kind == M_STATE_TRY || e->kind == M_STATE_CLEAR_JMPBUF); \ + /* If the exception is already m_global_exception, it won't be copied */ \ + m_exception_set(&m_global_exception, exception); \ + e->kind = M_STATE_EXCEPTION_IN_PROGRESS; \ + m_global_error_list = e; \ + m_try_longjmp(e->data.buf, 1); \ + } \ + /* Next stack frame */ \ + e = e->next; \ + } \ + /* No exception found. \ + Display the information and halt program . */ \ + M_RAISE_FATAL("Exception '%u' raised by (%s:%d) is not catched. Program aborted.\n", \ + exception->error_code, exception->filename, exception->line); \ + } + +// Rethrow the error +M_INLINE void +m_rethrow(void) +{ + M_ASSERT(m_global_error_list != NULL); + m_throw(&m_global_exception); +} + +// Catch the error code associated to the TRY block state +// and provide a pointer to the exception (which is a global). +M_INLINE bool +m_catch(m_try_t state, unsigned error_code, const struct m_exception_s **exception) +{ + M_ASSERT(m_global_error_list == state); + M_ASSERT(state->kind == M_STATE_EXCEPTION_IN_PROGRESS); + *exception = &m_global_exception; + if (error_code != 0 && m_global_exception.error_code != error_code) + return false; + // The exception has been catched. + state->kind = M_STATE_EXCEPTION_CATCHED; + // Unstack the try block, so that next throw command in the CATCH block + // will reach the upper TRY block. + m_global_error_list = state->next; + return true; +} + +// Initialize the state to a TRY state. +M_INLINE void +m_try_init(m_try_t state) +{ + state->kind = M_STATE_TRY; + state->next = m_global_error_list; + m_global_error_list = state; + // setjmp needs to be done in the MACRO. +} +#define m_try_init(s) \ + M_LIKELY ((m_try_init(s), m_try_setjmp(((s)->data.buf)) != 1)) + +// Disable the current TRY block. +M_INLINE void +m_try_clear(m_try_t state) +{ + // Even if there is a CATCH block and an unstack of the exception + // m_global_error_list won't be changed. + m_global_error_list = state->next; + if (M_UNLIKELY (state->kind == M_STATE_EXCEPTION_IN_PROGRESS)) { + // There was no catch for this error. + // Forward it to the upper level. + m_rethrow(); + } +} + + +// Implement the M_LET injection macros, so that the CLEAR operator is called on exception +// Helper functions +// Each mechanisme provide 3 helper functions: +// * pre: which is called before the constructor +// * post: which is called after the constructor +// * final: which is called before the destructor. + +// We register a call to the CLEAR callback. +// We don't modify m_global_error_list until we have successfully called the INIT operator +// to avoid registering the CLEAR operator on exception whereas the object is not initialized yet. +// However we register the position in the stack frame now so that in case of partial initialization +// of the object (if the INIT operator of the object calls other INIT operators of composed fields), +// since partial initialization will be unstacked naturally by the composing object. +M_INLINE bool +m_try_cb_pre(m_try_t state) +{ + state->kind = M_STATE_CLEAR_CB; + state->next = m_global_error_list; + return true; +} + +// We register the function to call of the initialized object. +M_INLINE bool +m_try_cb_post(m_try_t state, void (M_TRY_FUNC_OPERATOR func)(void*), void *data) +{ + state->data.clear.func = func; + state->data.clear.data = data; + m_global_error_list = state; + return true; +} + +// The object will be cleared. +// We can pop the stack frame of the errors. +M_INLINE void +m_try_cb_final(m_try_t state) +{ + m_global_error_list = state->next; +} + +// Pre initialization function. Save the stack frame for a longjmp +M_INLINE bool +m_try_jump_pre(m_try_t state) +{ + state->kind = M_STATE_CLEAR_JMPBUF; + state->next = m_global_error_list; + return true; +} + +// Post initialization function. Register the stack frame for a longjmp +M_INLINE void +m_try_jump_post(m_try_t state) +{ + m_global_error_list = state; +} +// And call setjmp to register the position in the code. +#define m_try_jump_post(s) \ + M_LIKELY ((m_try_jump_post(s), m_try_setjmp(((s)->data.buf)) != 1)) + +// The object will be cleared. +// We can pop the stack frame of the errors. +M_INLINE void +m_try_jump_final(m_try_t state) +{ + m_global_error_list = state->next; +} + + +// Implement the M_LET injection macros, so that the CLEAR operator is called on exception +// +#if M_USE_TRY_MECHANISM == 1 +# error M*LIB: Internal error. C++ back-end requested within C implementation. + +#elif M_USE_TRY_MECHANISM == 2 +// Use of CLANG blocks + +#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \ + for(m_try_t M_C(m_try_state_, name); cont && \ + m_try_cb_pre(M_C(m_try_state_, name) ); ) + +#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \ + for(m_try_cb_post(M_C(m_try_state_, name), \ + ^ void (void *_data) { M_GET_TYPE oplist *_t = _data; M_CALL_CLEAR(oplist, *_t); }, \ + (void*) &name); cont; m_try_cb_final(M_C(m_try_state_, name)) ) + +#elif M_USE_TRY_MECHANISM == 3 +// Use of GCC nested functions. + +#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \ + for(m_try_t M_C(m_try_state_, name); cont && \ + m_try_cb_pre(M_C(m_try_state_, name) ); ) + +#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \ + for(m_try_cb_post(M_C(m_try_state_, name), \ + __extension__ ({ __extension__ void _callback (void *_data) { M_GET_TYPE oplist *_t = _data; M_CALL_CLEAR(oplist, *_t); } _callback; }), \ + (void*) &name); cont; m_try_cb_final(M_C(m_try_state_, name)) ) + +#elif M_USE_TRY_MECHANISM == 4 +// STD C compliant (without compiler extension): use of setjmp +// This is the basic implementation in case of compiler unknown. +// It uses setjmp/longjmp, and as such, is much slower than +// other implementations. + +// M_LET Injection / pre initialization +// Initialize the stack frame. +#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \ + for(m_try_t M_C(m_try_state_, name); cont && \ + m_try_jump_pre(M_C(m_try_state_, name)); ) + +// M_LET Injection / post initialization +// Register the stack frame and tests for the longjmp. +// In which case call the CLEAR operator, unstack the error list and rethrow the error. +#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \ + for( ; cont ; m_try_jump_final(M_C(m_try_state_, name))) \ + if (m_try_jump_post(M_C(m_try_state_, name)) \ + || (M_CALL_CLEAR(oplist, name), m_try_jump_final(M_C(m_try_state_, name)), m_rethrow(), false)) + +#else +# error M*LIB: Invalid value for M_USE_TRY_MECHANISM [1..4] +#endif + + +// M_DEFER Injection / pre initialization +// Initialize the stack frame. +#define M_DEFER_TRY_INJECT_PRE_B(cont, ...) \ + for(m_try_t M_C(m_try_state_, cont); cont && \ + m_try_jump_pre(M_C(m_try_state_, cont)); ) + +// M_DEFER Injection / post initialization +// Register the stack frame and tests for the longjmp. +// In which case call the CLEAR operator, unstack the error list and rethrow the error. +#define M_DEFER_TRY_INJECT_POST_B(cont, ...) \ + for( ; cont ; m_try_jump_final(M_C(m_try_state_, cont))) \ + if (m_try_jump_post(M_C(m_try_state_, cont)) \ + || (__VA_ARGS__ , m_try_jump_final(M_C(m_try_state_, cont)), m_rethrow(), false)) + +#endif /* cplusplus */ + +/*****************************************************************************/ + +// Macro injection for M_LET. +// If the oplist defined NOCLEAR property, we won't register this variable for clear on exception +#undef M_LET_TRY_INJECT_PRE +#define M_LET_TRY_INJECT_PRE(cont, oplist, name) \ + M_IF(M_GET_PROPERTY(oplist, NOCLEAR))(M_EAT, M_LET_TRY_INJECT_PRE_B) \ + (cont, oplist, name) + +#undef M_LET_TRY_INJECT_POST +#define M_LET_TRY_INJECT_POST(cont, oplist, name) \ + M_IF(M_GET_PROPERTY(oplist, NOCLEAR))(M_EAT, M_LET_TRY_INJECT_POST_B) \ + (cont, oplist, name) + + +// Macro injection for M_DEFER. +#undef M_DEFER_TRY_INJECT_PRE +#define M_DEFER_TRY_INJECT_PRE(cont, ...) M_DEFER_TRY_INJECT_PRE_B(cont, __VA_ARGS__) +#undef M_DEFER_TRY_INJECT_POST +#define M_DEFER_TRY_INJECT_POST(cont, ...) M_DEFER_TRY_INJECT_POST_B(cont, __VA_ARGS__) + + +// In case of MEMORY FULL errors, throw an error instead of aborting. +#undef M_MEMORY_FULL +#define M_MEMORY_FULL(size) M_THROW(M_ERROR_MEMORY, (intptr_t)(size)) + +#endif + diff --git a/components/mlib/m-tuple.h b/components/mlib/m-tuple.h new file mode 100644 index 00000000..55edc1ae --- /dev/null +++ b/components/mlib/m-tuple.h @@ -0,0 +1,784 @@ +/* + * M*LIB - TUPLE module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_TUPLE_H +#define MSTARLIB_TUPLE_H + +#include "m-core.h" + +/* Define the tuple type and functions. + USAGE: + TUPLE_DEF2(name, [(field1, type1[, oplist1]), (field2, type2[, oplist2]), ...] ) */ +#define M_TUPLE_DEF2(name, ...) \ + M_TUPLE_DEF2_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define the tuple type and functions + as the given name. + USAGE: + TUPLE_DEF2_AS(name, name_t, [(field1, type1[, oplist1]), (field2, type2[, oplist2]), ...] ) */ +#define M_TUPLE_DEF2_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_TUPL3_DEF2_P1( (name, name_t M_TUPL3_INJECT_GLOBAL(__VA_ARGS__)) ) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a tuple. + USAGE: TUPLE_OPLIST(name[, oplist of the first type, ...]) */ +#define M_TUPLE_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (M_TUPL3_OPLIST_P1((__VA_ARGS__, M_BASIC_OPLIST )), \ + M_TUPL3_OPLIST_P1((__VA_ARGS__ ))) + + +/* Return an array suitable for the WIP _cmp_order function. + As compound literals are not supported in C++, + provide a separate definition for C++ using initializer_list + (shall be constexpr, but only supported in C++14). +*/ +#ifndef __cplusplus +#define M_TUPLE_ORDER(name, ...) \ + ( (const int[]) {M_MAP2_C(M_TUPL3_ORDER_CONVERT, name, __VA_ARGS__), 0}) +#else +#include +namespace m_lib { + template + struct m_tupl3_integer_va { + int data[N]; + /*constexpr*/ inline m_tupl3_integer_va(std::initializer_list init){ + int j = 0; + for(auto i:init) { + data[j++] = i; + } + } + }; +} +#define M_TUPLE_ORDER(name, ...) \ + (m_lib::m_tupl3_integer_va({M_MAP2_C(M_TUPL3_ORDER_CONVERT, name, __VA_ARGS__), 0}).data) +#endif + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* Contract of a tuple. Nothing notable */ +#define M_TUPL3_CONTRACT(tup) do { \ + M_ASSERT(tup != NULL); \ +} while (0) + +/* Inject the oplist within the list of arguments */ +#define M_TUPL3_INJECT_GLOBAL(...) \ + M_MAP(M_TUPL3_INJECT_OPLIST_A, __VA_ARGS__) + +/* Transform (x, type) into (x, type, oplist) if there is global registered oplist + or (x, type, M_BASIC_OPLIST) if there is no global one, + or keep (x, type, oplist) if oplist was already present */ +#define M_TUPL3_INJECT_OPLIST_A( duo_or_trio ) \ + M_TUPL3_INJECT_OPLIST_B duo_or_trio + +#define M_TUPL3_INJECT_OPLIST_B( f, ... ) \ + M_DEFERRED_COMMA \ + M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) ) + +// Deferred evaluation +#define M_TUPL3_DEF2_P1(...) M_ID( M_TUPL3_DEF2_P2 __VA_ARGS__ ) + +// Test if all third argument of all arguments is an oplist +#define M_TUPL3_IF_ALL_OPLIST(...) \ + M_IF(M_REDUCE(M_TUPL3_IS_OPLIST_P, M_AND, __VA_ARGS__)) + +// Test if the third argument of (name, type, oplist) is an oplist +#define M_TUPL3_IS_OPLIST_P(a) \ + M_OPLIST_P(M_RET_ARG3 a) + +/* Validate the oplist before going further */ +#define M_TUPL3_DEF2_P2(name, name_t, ...) \ + M_TUPL3_IF_ALL_OPLIST(__VA_ARGS__)(M_TUPL3_DEF2_P3, M_TUPL3_DEF2_FAILURE)(name, name_t, __VA_ARGS__) + +/* Stop processing with a compilation failure */ +#define M_TUPL3_DEF2_FAILURE(name, name_t, ...) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(TUPLE_DEF2): at least one of the given argument is not a valid oplist: " #__VA_ARGS__) + +/* Define the tuple */ +#define M_TUPL3_DEF2_P3(name, name_t, ...) \ + M_TUPL3_DEFINE_TYPE(name, name_t, __VA_ARGS__) \ + M_TUPL3_DEFINE_ENUM(name, __VA_ARGS__) \ + M_TUPL3_CONTROL_ALL_OPLIST(name, __VA_ARGS__) \ + M_TUPL3_IF_ALL(INIT, __VA_ARGS__)(M_TUPL3_DEFINE_INIT(name, __VA_ARGS__),) \ + M_TUPL3_DEFINE_INIT_SET(name, __VA_ARGS__) \ + M_TUPL3_DEFINE_INIT_SET2(name, __VA_ARGS__) \ + M_TUPL3_DEFINE_SET(name, __VA_ARGS__) \ + M_TUPL3_DEFINE_SET2(name, __VA_ARGS__) \ + M_TUPL3_DEFINE_CLEAR(name, __VA_ARGS__) \ + M_TUPL3_DEFINE_GETTER_FIELD(name, __VA_ARGS__) \ + M_TUPL3_DEFINE_SETTER_FIELD(name, __VA_ARGS__) \ + M_TUPL3_DEFINE_EMPLACE_FIELD(name, __VA_ARGS__) \ + M_TUPL3_IF_ONE(CMP, __VA_ARGS__)(M_TUPL3_DEFINE_CMP(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(CMP, __VA_ARGS__)(M_TUPL3_DEFINE_CMP_ORDER(name, __VA_ARGS__),) \ + M_TUPL3_DEFINE_CMP_FIELD(name, __VA_ARGS__) \ + M_TUPL3_IF_ONE(HASH, __VA_ARGS__)(M_TUPL3_DEFINE_HASH(name, __VA_ARGS__),) \ + M_TUPL3_IF_ONE(EQUAL, __VA_ARGS__)(M_TUPL3_DEFINE_EQUAL(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(GET_STR, __VA_ARGS__)(M_TUPL3_DEFINE_GET_STR(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(OUT_STR, __VA_ARGS__)(M_TUPL3_DEFINE_OUT_STR(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(IN_STR, __VA_ARGS__)(M_TUPL3_DEFINE_IN_STR(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(PARSE_STR, __VA_ARGS__)(M_TUPL3_DEFINE_PARSE_STR(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(OUT_SERIAL, __VA_ARGS__)(M_TUPL3_DEFINE_OUT_SERIAL(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(IN_SERIAL, __VA_ARGS__)(M_TUPL3_DEFINE_IN_SERIAL(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(INIT_MOVE, __VA_ARGS__)(M_TUPL3_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(MOVE, __VA_ARGS__)(M_TUPL3_DEFINE_MOVE(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(SWAP, __VA_ARGS__)(M_TUPL3_DEFINE_SWAP(name, __VA_ARGS__),) \ + M_TUPL3_IF_ALL(RESET, __VA_ARGS__)(M_TUPL3_DEFINE_RESET(name, __VA_ARGS__),) + +/* Provide order for _cmp_order */ +#define M_TUPL3_ORDER_CONVERT(name, x) M_F(name, M_C(M_TUPL3_ORDER_CONVERT_, x)) +#define M_TUPL3_ORDER_CONVERT_ASC(x) M_C3(_,x,_value) +#define M_TUPL3_ORDER_CONVERT_DSC(x) M_C3(_,x,_value)*-1 + +/* Get the field name, the type, the oplist or the methods + based on the tuple (field, type, oplist) */ +#define M_TUPL3_GET_FIELD(f,t,o) f +#define M_TUPL3_GET_TYPE(f,t,o) t +#define M_TUPL3_GET_OPLIST(f,t,o) o +#define M_TUPL3_GET_INIT(f,t,o) M_GET_INIT o +#define M_TUPL3_GET_INIT_SET(f,t,o) M_GET_INIT_SET o +#define M_TUPL3_GET_INIT_MOVE(f,t,o) M_GET_INIT_MOVE o +#define M_TUPL3_GET_MOVE(f,t,o) M_GET_MOVE o +#define M_TUPL3_GET_SET(f,t,o) M_GET_SET o +#define M_TUPL3_GET_CLEAR(f,t,o) M_GET_CLEAR o +#define M_TUPL3_GET_CMP(f,t,o) M_GET_CMP o +#define M_TUPL3_GET_HASH(f,t,o) M_GET_HASH o +#define M_TUPL3_GET_EQUAL(f,t,o) M_GET_EQUAL o +#define M_TUPL3_GET_STR(f,t,o) M_GET_GET_STR o +#define M_TUPL3_GET_OUT_STR(f,t,o) M_GET_OUT_STR o +#define M_TUPL3_GET_IN_STR(f,t,o) M_GET_IN_STR o +#define M_TUPL3_GET_OUT_SERIAL(f,t,o) M_GET_OUT_SERIAL o +#define M_TUPL3_GET_IN_SERIAL(f,t,o) M_GET_IN_SERIAL o +#define M_TUPL3_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o +#define M_TUPL3_GET_SWAP(f,t,o) M_GET_SWAP o +#define M_TUPL3_GET_RESET(f,t,o) M_GET_RESET o + +/* Call the method associated to the given operator for the given parameter + of the tuple t=(name, type, oplist) */ +#define M_TUPL3_CALL_INIT(t, ...) M_APPLY_API(M_TUPL3_GET_INIT t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_INIT_SET(t, ...) M_APPLY_API(M_TUPL3_GET_INIT_SET t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_INIT_MOVE(t, ...) M_APPLY_API(M_TUPL3_GET_INIT_MOVE t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_MOVE(t, ...) M_APPLY_API(M_TUPL3_GET_MOVE t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_SET(t, ...) M_APPLY_API(M_TUPL3_GET_SET t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_CLEAR(t, ...) M_APPLY_API(M_TUPL3_GET_CLEAR t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_CMP(t, ...) M_APPLY_API(M_TUPL3_GET_CMP t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_HASH(t, ...) M_APPLY_API(M_TUPL3_GET_HASH t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_EQUAL(t, ...) M_APPLY_API(M_TUPL3_GET_EQUAL t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_GET_STR(t, ...) M_APPLY_API(M_TUPL3_GET_STR t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_OUT_STR(t, ...) M_APPLY_API(M_TUPL3_GET_OUT_STR t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_IN_STR(t, ...) M_APPLY_API(M_TUPL3_GET_IN_STR t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_PARSE_STR(t, ...) M_APPLY_API(M_TUPL3_GET_PARSE_STR t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_OUT_SERIAL(t, ...) M_APPLY_API(M_TUPL3_GET_OUT_SERIAL t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_IN_SERIAL(t, ...) M_APPLY_API(M_TUPL3_GET_IN_SERIAL t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_SWAP(t, ...) M_APPLY_API(M_TUPL3_GET_SWAP t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) +#define M_TUPL3_CALL_RESET(t, ...) M_APPLY_API(M_TUPL3_GET_RESET t, M_TUPL3_GET_OPLIST t, __VA_ARGS__) + + +/* Define the type of a tuple */ +#define M_TUPL3_DEFINE_TYPE(name, name_t, ...) \ + typedef struct M_F(name, _s) { \ + M_MAP(M_TUPL3_DEFINE_RECUR_TYPE_ELE , __VA_ARGS__) \ + } name_t[1]; \ + \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + /* Define internal type for oplist */ \ + typedef name_t M_F(name, _ct); \ + /* Save constant as the number of arguments (internal) */ \ + typedef enum { \ + M_C3(m_tupl3_, name, _num_args) = M_NARGS(__VA_ARGS__) \ + } M_C3(m_tupl3_, name, _num_args_ct); \ + /* Save alias for the types of arguments */ \ + M_MAP3(M_TUPL3_DEFINE_TYPE_ELE, name, __VA_ARGS__) + +#define M_TUPL3_DEFINE_TYPE_ELE(name, num, a) \ + typedef M_TUPL3_GET_TYPE a M_C4(name, _type_, num, _ct); + +#define M_TUPL3_DEFINE_RECUR_TYPE_ELE(a) \ + M_TUPL3_GET_TYPE a M_TUPL3_GET_FIELD a ; + +/* Define the basic enumerate, identifying a parameter */ +#define M_TUPL3_DEFINE_ENUM(name, ...) \ + typedef enum { \ + M_F(name, _first_one_val), \ + M_MAP2_C(M_TUPL3_DEFINE_ENUM_ELE , name, __VA_ARGS__) \ + } M_F(name,_field_e); + +#define M_TUPL3_DEFINE_ENUM_ELE(name, a) \ + M_C4(name, _, M_TUPL3_GET_FIELD a, _value) + +/* Control that all given oplists of all parameters are really oplists */ +#define M_TUPL3_CONTROL_ALL_OPLIST(name, ...) \ + M_MAP2(M_TUPL3_CONTROL_OPLIST, name, __VA_ARGS__) + +#define M_TUPL3_CONTROL_OPLIST(name, a) \ + M_CHECK_COMPATIBLE_OPLIST(name, M_TUPL3_GET_FIELD a, \ + M_TUPL3_GET_TYPE a, M_TUPL3_GET_OPLIST a) + +/* Define the INIT method calling the INIT method for all params */ +#define M_TUPL3_DEFINE_INIT(name, ...) \ + M_INLINE void M_F(name, _init)(M_F(name,_ct) my) { \ + M_MAP(M_TUPL3_DEFINE_INIT_FUNC , __VA_ARGS__) {} \ + } + +#define M_TUPL3_DEFINE_INIT_FUNC(a) \ + M_CHAIN_OBJ(M_TUPL3_GET_FIELD a, M_TUPL3_GET_OPLIST a, my -> M_TUPL3_GET_FIELD a) + +/* Define the INIT_SET method calling the INIT_SET method for all params */ +#define M_TUPL3_DEFINE_INIT_SET(name, ...) \ + M_INLINE void M_F(name, _init_set)(M_F(name,_ct) my , M_F(name,_ct) const org) { \ + M_TUPL3_CONTRACT(org); \ + M_MAP(M_TUPL3_DEFINE_INIT_SET_FUNC , __VA_ARGS__) {} \ + } +#define M_TUPL3_DEFINE_INIT_SET_FUNC(a) \ + M_CHAIN_OBJ(M_TUPL3_GET_FIELD a, M_TUPL3_GET_OPLIST a, \ + my -> M_TUPL3_GET_FIELD a , org -> M_TUPL3_GET_FIELD a ) + +/* Define the INIT_WITH method calling the INIT_SET method for all params. */ +#define M_TUPL3_DEFINE_INIT_SET2(name, ...) \ + M_INLINE void M_F(name, _init_emplace)(M_F(name,_ct) my \ + M_MAP(M_TUPL3_DEFINE_INIT_SET2_PROTO, __VA_ARGS__) \ + ) { \ + M_MAP(M_TUPL3_DEFINE_INIT_SET2_FUNC , __VA_ARGS__) {} \ + } + +#define M_TUPL3_DEFINE_INIT_SET2_PROTO(a) \ + , M_TUPL3_GET_TYPE a const M_TUPL3_GET_FIELD a + +#define M_TUPL3_DEFINE_INIT_SET2_FUNC(a) \ + M_CHAIN_OBJ(M_TUPL3_GET_FIELD a, M_TUPL3_GET_OPLIST a, \ + my -> M_TUPL3_GET_FIELD a , M_TUPL3_GET_FIELD a ) + + +/* Define the SET method calling the SET method for all params. */ +#define M_TUPL3_DEFINE_SET(name, ...) \ + M_INLINE void M_F(name, _set)(M_F(name,_ct) my , \ + M_F(name,_ct) const org) { \ + M_TUPL3_CONTRACT(my); \ + M_TUPL3_CONTRACT(org); \ + M_MAP(M_TUPL3_DEFINE_SET_FUNC , __VA_ARGS__) \ + } + +#define M_TUPL3_DEFINE_SET_FUNC(a) \ + M_TUPL3_CALL_SET(a, my -> M_TUPL3_GET_FIELD a , org -> M_TUPL3_GET_FIELD a ); + + +/* Define the SET_WITH method calling the SET method for all params. */ +#define M_TUPL3_DEFINE_SET2(name, ...) \ + M_INLINE void M_F(name, _emplace)(M_F(name,_ct) my \ + M_MAP(M_TUPL3_DEFINE_SET2_PROTO, __VA_ARGS__) \ + ) { \ + M_TUPL3_CONTRACT(my); \ + M_MAP(M_TUPL3_DEFINE_SET2_FUNC , __VA_ARGS__) \ + } +#define M_TUPL3_DEFINE_SET2_PROTO(a) \ + , M_TUPL3_GET_TYPE a const M_TUPL3_GET_FIELD a + +#define M_TUPL3_DEFINE_SET2_FUNC(a) \ + M_TUPL3_CALL_SET(a, my -> M_TUPL3_GET_FIELD a , M_TUPL3_GET_FIELD a ); + + +/* Define the CLEAR method calling the CLEAR method for all params. */ +#define M_TUPL3_DEFINE_CLEAR(name, ...) \ + M_INLINE void M_F(name, _clear)(M_F(name,_ct) my) { \ + M_TUPL3_CONTRACT(my); \ + M_MAP(M_TUPL3_DEFINE_CLEAR_FUNC , __VA_ARGS__) \ + } + +#define M_TUPL3_DEFINE_CLEAR_FUNC(a) \ + M_TUPL3_CALL_CLEAR(a, my -> M_TUPL3_GET_FIELD a ); + + +/* Define the GET_AT_field & CGET_AT methods for all params. */ +#define M_TUPL3_DEFINE_GETTER_FIELD(name, ...) \ + M_MAP3(M_TUPL3_DEFINE_GETTER_FIELD_PROTO, name, __VA_ARGS__) + +#define M_TUPL3_DEFINE_GETTER_FIELD_PROTO(name, num, a) \ + M_INLINE M_TUPL3_GET_TYPE a * M_C3(name, _get_at_, M_TUPL3_GET_FIELD a) \ + (M_F(name,_ct) my) { \ + M_TUPL3_CONTRACT(my); \ + return &(my->M_TUPL3_GET_FIELD a); \ + } \ + M_INLINE M_TUPL3_GET_TYPE a const * M_C3(name, _cget_at_, M_TUPL3_GET_FIELD a) \ + (M_F(name,_ct) const my) { \ + M_TUPL3_CONTRACT(my); \ + return &(my->M_TUPL3_GET_FIELD a); \ + } \ + /* Same but uses numerical index for accessing the field (internal) */ \ + M_INLINE M_TUPL3_GET_TYPE a * M_C4(m_tupl3_, name, _get_at_, num) \ + (M_F(name,_ct) my) { \ + return &(my->M_TUPL3_GET_FIELD a); \ + } \ + + +/* Define the SET_field methods for all params. */ +#define M_TUPL3_DEFINE_SETTER_FIELD(name, ...) \ + M_MAP2(M_TUPL3_DEFINE_SETTER_FIELD_PROTO, name, __VA_ARGS__) + +#define M_TUPL3_DEFINE_SETTER_FIELD_PROTO(name, a) \ + M_INLINE void M_C3(name, _set_, M_TUPL3_GET_FIELD a) \ + (M_F(name,_ct) my, M_TUPL3_GET_TYPE a const M_TUPL3_GET_FIELD a) { \ + M_TUPL3_CONTRACT(my); \ + M_TUPL3_CALL_SET(a, my ->M_TUPL3_GET_FIELD a, M_TUPL3_GET_FIELD a); \ + } + + +/* Define the EMPLACE_field methods for all params. */ +#define M_TUPL3_DEFINE_EMPLACE_FIELD(name, ...) \ + M_REDUCE3(M_TUPL3_DEFINE_EMPLACE_FIELD_PROTO, M_TUPL3_DEFINE_EMPLACE_G, name, __VA_ARGS__) + +#define M_TUPL3_DEFINE_EMPLACE_G(a, b) a b + +#define M_TUPL3_DEFINE_EMPLACE_FIELD_PROTO(name, id, a) \ + M_EMPLACE_QUEUE_DEF(M_TUPL3_GET_FIELD a, M_F(name, _ct), M_C3(name, _emplace_, M_TUPL3_GET_FIELD a), M_TUPL3_GET_OPLIST a, M_TUPL3_EMPLACE_DEF) + +#define M_TUPL3_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t v \ + M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ + { \ + M_CALL_CLEAR(oplist, v->name); \ + M_EMPLACE_CALL_FUNC(a, init_func, oplist, v->name, exp_emplace_type); \ + } + + +/* Define the CMP method by calling CMP methods for all params. */ +#define M_TUPL3_DEFINE_CMP(name, ...) \ + M_INLINE int M_F(name, _cmp)(M_F(name,_ct) const e1 , \ + M_F(name,_ct) const e2) { \ + int i; \ + M_TUPL3_CONTRACT(e1); \ + M_TUPL3_CONTRACT(e2); \ + M_MAP(M_TUPL3_DEFINE_CMP_FUNC_P0, __VA_ARGS__) \ + return 0; \ + } + +#define M_TUPL3_DEFINE_CMP_FUNC_P0(a) \ + M_IF(M_TUPL3_TEST_METHOD_P(CMP, a))(M_TUPL3_DEFINE_CMP_FUNC_P1, M_EAT)(a) +#define M_TUPL3_DEFINE_CMP_FUNC_P1(a) \ + i = M_TUPL3_CALL_CMP(a, e1 -> M_TUPL3_GET_FIELD a , e2 -> M_TUPL3_GET_FIELD a ); \ + if (i != 0) return i; + +/* Define the CMP_ORDER method by calling CMP methods for all params + In the right order + FIXME: _cmp_order is not supported by algorithm yet. + FIXME: All oplists shall define the CMP operator or at least one? +*/ +#define M_TUPL3_DEFINE_CMP_ORDER(name, ...) \ + M_INLINE int M_F(name, _cmp_order)(M_F(name,_ct) const e1 , \ + M_F(name,_ct) const e2, \ + const int order[]) { \ + int i, r; \ + M_TUPL3_CONTRACT(e1); \ + M_TUPL3_CONTRACT(e2); \ + while (true) { \ + i=*order++; \ + switch (i) { \ + case 0: return 0; \ + M_MAP2(M_TUPL3_DEFINE_CMP_ORDER_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(0); \ + } \ + } \ + } + +#define M_TUPL3_DEFINE_CMP_ORDER_FUNC(name, a) \ + case M_C4(name, _, M_TUPL3_GET_FIELD a, _value): \ + case -M_C4(name, _, M_TUPL3_GET_FIELD a, _value): \ + r = M_TUPL3_CALL_CMP(a, e1 -> M_TUPL3_GET_FIELD a , e2 -> M_TUPL3_GET_FIELD a ); \ + if (r != 0) return i < 0 ? -r : r; \ + break; + + +/* Define a CMP_field method for all given params that export a CMP method */ +#define M_TUPL3_DEFINE_CMP_FIELD(name, ...) \ + M_MAP2(M_TUPL3_MAP_CMP_FIELD, name, __VA_ARGS__) + +#define M_TUPL3_MAP_CMP_FIELD(name, a) \ + M_IF_METHOD(CMP, M_TUPL3_GET_OPLIST a)( \ + M_TUPL3_DEFINE_CMP_FIELD_FUNC(name, M_TUPL3_GET_FIELD a, M_TUPL3_GET_CMP a, M_TUPL3_GET_OPLIST a), \ + ) + +#define M_TUPL3_DEFINE_CMP_FIELD_FUNC(name, field, func_cmp, oplist) \ + M_INLINE int M_C3(name, _cmp_, field)(M_F(name,_ct) const e1 , \ + M_F(name,_ct) const e2) { \ + M_TUPL3_CONTRACT(e1); \ + M_TUPL3_CONTRACT(e2); \ + return M_APPLY_API(func_cmp, oplist, e1 -> field , e2 -> field ); \ + } + + +/* Define a EQUAL method by calling the EQUAL methods for all params */ +#define M_TUPL3_DEFINE_EQUAL(name, ...) \ + M_INLINE bool M_F(name, _equal_p)(M_F(name,_ct) const e1 , \ + M_F(name,_ct) const e2) { \ + bool b; \ + M_TUPL3_CONTRACT(e1); \ + M_TUPL3_CONTRACT(e2); \ + M_MAP(M_TUPL3_DEFINE_EQUAL_FUNC_P0, __VA_ARGS__) \ + return true; \ + } + +#define M_TUPL3_DEFINE_EQUAL_FUNC_P0(a) \ + M_IF(M_TUPL3_TEST_METHOD_P(EQUAL, a))(M_TUPL3_DEFINE_EQUAL_FUNC_P1, M_EAT)(a) +#define M_TUPL3_DEFINE_EQUAL_FUNC_P1(a) \ + b = M_TUPL3_CALL_EQUAL(a, e1 -> M_TUPL3_GET_FIELD a , e2 -> M_TUPL3_GET_FIELD a ); \ + if (!b) return false; + + +/* Define a HASH method by calling the HASH methods for all params */ +#define M_TUPL3_DEFINE_HASH(name, ...) \ + M_INLINE size_t M_F(name, _hash)(M_F(name,_ct) const e1) { \ + M_TUPL3_CONTRACT(e1); \ + M_HASH_DECL(hash); \ + M_MAP(M_TUPL3_DEFINE_HASH_FUNC_P0, __VA_ARGS__) \ + return M_HASH_FINAL (hash); \ + } + +#define M_TUPL3_DEFINE_HASH_FUNC_P0(a) \ + M_IF(M_TUPL3_TEST_METHOD_P(HASH, a))(M_TUPL3_DEFINE_HASH_FUNC_P1, M_EAT)(a) +#define M_TUPL3_DEFINE_HASH_FUNC_P1(a) \ + M_HASH_UP(hash, M_TUPL3_CALL_HASH(a, e1 -> M_TUPL3_GET_FIELD a) ); + + +/* Define a GET_STR method by calling the GET_STR methods for all params */ +#define M_TUPL3_DEFINE_GET_STR(name, ...) \ + M_INLINE void M_F(name, _get_str)(m_string_t str, \ + M_F(name,_ct) const el, \ + bool append) { \ + bool comma = false; \ + M_TUPL3_CONTRACT(el); \ + M_ASSERT (str != NULL); \ + (append ? m_string_cat_cstr : m_string_set_cstr) (str, "("); \ + M_MAP(M_TUPL3_DEFINE_GET_STR_FUNC , __VA_ARGS__) \ + m_string_push_back (str, ')'); \ + } + +#define M_TUPL3_DEFINE_GET_STR_FUNC(a) \ + if (comma) m_string_push_back (str, ','); \ + comma = true; \ + M_TUPL3_CALL_GET_STR(a, str, el -> M_TUPL3_GET_FIELD a, true); \ + + +/* Define a OUT_STR method by calling the OUT_STR methods for all params */ +#define M_TUPL3_DEFINE_OUT_STR(name, ...) \ + M_INLINE void M_F(name, _out_str)(FILE *f, \ + M_F(name,_ct) const el) { \ + bool comma = false; \ + M_TUPL3_CONTRACT(el); \ + M_ASSERT (f != NULL); \ + fputc('(', f); \ + M_MAP(M_TUPL3_DEFINE_OUT_STR_FUNC , __VA_ARGS__) \ + fputc (')', f); \ + } + +#define M_TUPL3_DEFINE_OUT_STR_FUNC(a) \ + if (comma) fputc (',', f); \ + comma = true; \ + M_TUPL3_CALL_OUT_STR(a, f, el -> M_TUPL3_GET_FIELD a); \ + + +/* Define a IN_STR method by calling the IN_STR methods for all params */ +#define M_TUPL3_DEFINE_IN_STR(name, ...) \ + M_INLINE bool M_F(name, _in_str)(M_F(name,_ct) el, FILE *f) { \ + bool comma = false; \ + M_TUPL3_CONTRACT(el); \ + M_ASSERT (f != NULL); \ + int c = fgetc(f); \ + if (c != '(') return false; \ + M_MAP(M_TUPL3_DEFINE_IN_STR_FUNC , __VA_ARGS__) \ + c = fgetc(f); \ + return (c == ')'); \ + } + +#define M_TUPL3_DEFINE_IN_STR_FUNC(a) \ + if (comma) { \ + c = fgetc (f); \ + if (c != ',' || c == EOF) return false; \ + } \ + comma = true; \ + if (M_TUPL3_CALL_IN_STR(a, el -> M_TUPL3_GET_FIELD a, f) == false) \ + return false ; \ + + +/* Define a PARSE_STR method by calling the PARSE_STR methods for all params */ +#define M_TUPL3_DEFINE_PARSE_STR(name, ...) \ + M_INLINE bool M_F(name, _parse_str)(M_F(name,_ct) el, \ + const char str[], \ + const char **endptr) { \ + M_TUPL3_CONTRACT(el); \ + M_ASSERT (str != NULL); \ + bool success = false; \ + bool comma = false; \ + int c = *str++; \ + if (c != '(') goto exit; \ + M_MAP(M_TUPL3_DEFINE_PARSE_STR_FUNC , __VA_ARGS__) \ + c = *str++; \ + success = (c == ')'); \ + exit: \ + if (endptr) *endptr = str; \ + return success; \ + } + +#define M_TUPL3_DEFINE_PARSE_STR_FUNC(a) \ + if (comma) { \ + c = *str++; \ + if (c != ',' || c == 0) goto exit; \ + } \ + comma = true; \ + if (M_TUPL3_CALL_PARSE_STR(a, el -> M_TUPL3_GET_FIELD a, str, &str) == false) \ + goto exit ; \ + + +/* Return the parameter name as a C string */ +#define M_TUPL3_STRINGIFY_NAME(a) \ + M_AS_STR(M_TUPL3_GET_FIELD a) + + +/* Define a OUT_SERIAL method by calling the OUT_SERIAL methods for all params */ +#define M_TUPL3_DEFINE_OUT_SERIAL(name, ...) \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, \ + M_F(name,_ct) const el) { \ + M_TUPL3_CONTRACT(el); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + const int field_max = M_NARGS(__VA_ARGS__); \ + /* Define a constant static table of all fields names */ \ + static const char *const field_name[] = \ + { M_REDUCE(M_TUPL3_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \ + int index = 0; \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + ret = f->m_interface->write_tuple_start(local, f); \ + M_MAP(M_TUPL3_DEFINE_OUT_SERIAL_FUNC , __VA_ARGS__) \ + M_ASSERT( index == field_max); \ + ret |= f->m_interface->write_tuple_end(local, f); \ + return ret & M_SERIAL_FAIL; \ + } + +#define M_TUPL3_DEFINE_OUT_SERIAL_FUNC(a) \ + f->m_interface->write_tuple_id(local, f, field_name, field_max, index); \ + M_TUPL3_CALL_OUT_SERIAL(a, f, el -> M_TUPL3_GET_FIELD a); \ + index++; \ + + +/* Define a IN_SERIAL method by calling the IN_SERIAL methods for all params */ +#define M_TUPL3_DEFINE_IN_SERIAL(name, ...) \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(M_F(name,_ct) el, m_serial_read_t f) { \ + M_TUPL3_CONTRACT(el); \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + int index = -1; \ + const int field_max = M_NARGS(__VA_ARGS__); \ + static const char *const field_name[] = \ + { M_REDUCE(M_TUPL3_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + ret = f->m_interface->read_tuple_start(local, f); \ + while (ret == M_SERIAL_OK_CONTINUE) { \ + ret = f->m_interface->read_tuple_id(local, f, field_name, field_max, &index); \ + if (ret == M_SERIAL_OK_CONTINUE) { \ + M_ASSERT (index >= 0 && index < field_max); \ + switch (1+index) { \ + M_MAP2(M_TUPL3_DEFINE_IN_SERIAL_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(0); \ + } \ + ret = (ret == M_SERIAL_OK_DONE) ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL; \ + } \ + } \ + return ret; \ + } + +#define M_TUPL3_DEFINE_IN_SERIAL_FUNC(name, a) \ + case M_C4(name, _, M_TUPL3_GET_FIELD a, _value): \ + ret = M_TUPL3_CALL_IN_SERIAL(a, el -> M_TUPL3_GET_FIELD a, f); \ + break; \ + + +/* Define a INIT_MOVE method by calling the INIT_MOVE methods for all params + INIT_MOVE cannot fail and cannot throw any exception */ +#define M_TUPL3_DEFINE_INIT_MOVE(name, ...) \ + M_INLINE void M_F(name, _init_move)(M_F(name,_ct) el, M_F(name,_ct) org) { \ + M_TUPL3_CONTRACT(el); \ + M_MAP(M_TUPL3_DEFINE_INIT_MOVE_FUNC , __VA_ARGS__) \ + } + +#define M_TUPL3_DEFINE_INIT_MOVE_FUNC(a) \ + M_TUPL3_CALL_INIT_MOVE(a, el -> M_TUPL3_GET_FIELD a, org -> M_TUPL3_GET_FIELD a); + + +/* Define a MOVE method by calling the MOVE methods for all params */ +#define M_TUPL3_DEFINE_MOVE(name, ...) \ + M_INLINE void M_F(name, _move)(M_F(name,_ct) el, M_F(name,_ct) org) { \ + M_TUPL3_CONTRACT(el); \ + M_MAP(M_TUPL3_DEFINE_MOVE_FUNC , __VA_ARGS__) \ + } + +#define M_TUPL3_DEFINE_MOVE_FUNC(a) \ + M_TUPL3_CALL_MOVE(a, el -> M_TUPL3_GET_FIELD a, org -> M_TUPL3_GET_FIELD a); + + +/* Define a SWAP method by calling the SWAP methods for all params */ +#define M_TUPL3_DEFINE_SWAP(name, ...) \ + M_INLINE void M_F(name, _swap)(M_F(name,_ct) el1, M_F(name,_ct) el2) { \ + M_TUPL3_CONTRACT(el1); \ + M_TUPL3_CONTRACT(el2); \ + M_MAP(M_TUPL3_DEFINE_SWAP_FUNC , __VA_ARGS__) \ + } + +#define M_TUPL3_DEFINE_SWAP_FUNC(a) \ + M_TUPL3_CALL_SWAP(a, el1 -> M_TUPL3_GET_FIELD a, el2 -> M_TUPL3_GET_FIELD a); + + +/* Define a RESET method by calling the RESET methods for all params */ +#define M_TUPL3_DEFINE_RESET(name, ...) \ + M_INLINE void M_F(name, _reset)(M_F(name,_ct) el1) { \ + M_TUPL3_CONTRACT(el1); \ + M_MAP(M_TUPL3_DEFINE_RESET_FUNC , __VA_ARGS__) \ + } \ + +#define M_TUPL3_DEFINE_RESET_FUNC(a) \ + M_TUPL3_CALL_RESET(a, el1 -> M_TUPL3_GET_FIELD a); + + +/********************************** INTERNAL *********************************/ + +/* INIT_WITH macro enabling recursive INIT_WITH initialization + tuple = { int, m_string_t, array } + USAGE: + M_LET( (x, 2, ("John"), ( ("Bear"), ("Rabbit") )), tuple_t) + + "If you think it's simple, you're deluding yourself." + + Several pass are done: + 1) If the number of arguments doesn't match the number of oplists of the + tuple oplist, it is assumed something is wrong. It uses the _init_emplace + function to provide proper warning in such case. + 2) Otherwise, it checks that the number of arguments matches the number + of arguments of the tuple definition. + 3) Mix all arguments with their associated oplists to have pair (arg, oplist), + 4) Map the following macro for each computed pair : + 4.a) If INIT_WITH macro is not defined for this pair, it uses INIT_SET + 4.b) If the argument is encapsulated with parenthesis, it uses INIT_WITH + 4.c) If the oplist property LET_AS_INIT_WITH is defined, it uses INIT_WITH + 4.d) Otherwise it uses INIT_SET. +*/ +#define M_TUPL3_INIT_WITH(oplist, dest, ...) \ + M_TUPL3_INIT_WITH_P1(M_GET_NAME oplist, M_GET_OPLIST oplist, dest, __VA_ARGS__) +#define M_TUPL3_INIT_WITH_P1(name, oplist_arglist, dest, ...) \ + M_IF(M_NOTEQUAL( M_NARGS oplist_arglist, M_NARGS (__VA_ARGS__))) \ + (M_TUPL3_INIT_WITH_P1_FUNC, M_TUPL3_INIT_WITH_P1_MACRO)(name, oplist_arglist, dest, __VA_ARGS__) +#define M_TUPL3_INIT_WITH_P1_FUNC(name, oplist_arglist, dest, ...) \ + M_F(name, _init_emplace)(dest, __VA_ARGS__) +#define M_TUPL3_INIT_WITH_P1_MACRO(name, oplist_arglist, dest, ...) \ + ( M_STATIC_ASSERT( M_NARGS oplist_arglist == M_C3(m_tupl3_, name, _num_args), M_LIB_DIMENSION_ERROR, "The number of oplists given to TUPLE_OPLIST don't match the number of oplists used to create the tuple." ), \ + M_STATIC_ASSERT( M_NARGS(__VA_ARGS__) == M_C3(m_tupl3_, name, _num_args), M_LIB_DIMENSION_ERROR, "Missing / Too many arguments for tuple"), \ + M_MAP3(M_TUPL3_INIT_WITH_P2, (name, dest), M_OPFLAT M_MERGE_ARGLIST( oplist_arglist, (__VA_ARGS__) ) ) \ + (void) 0) +#define M_TUPL3_INIT_WITH_P2(name_dest, num, pair) \ + M_TUPL3_INIT_WITH_P3( M_PAIR_1 name_dest, M_PAIR_2 name_dest, num, M_PAIR_1 pair, M_PAIR_2 pair ) +#define M_TUPL3_INIT_WITH_P3(name, dest, num, oplist, param) \ + M_IF(M_TEST_METHOD_P(INIT_WITH, oplist))(M_TUPL3_INIT_WITH_P4, M_TUPL3_INIT_WITH_SET)(name, dest, num, oplist, param) +#define M_TUPL3_INIT_WITH_SET(name, dest, num, oplist, param) \ + M_CALL_INIT_SET (oplist, *M_C4(m_tupl3_, name, _get_at_, num)(dest), param) , +#define M_TUPL3_INIT_WITH_P4(name, dest, num, oplist, param) \ + M_IF(M_PARENTHESIS_P( param))(M_TUPL3_INIT_WITH_P5, M_TUPL3_INIT_WITH_P6)(name, dest, num, oplist, param) +#define M_TUPL3_INIT_WITH_P5(name, dest, num, oplist, param) \ + M_CALL_INIT_WITH(oplist, *M_C4(m_tupl3_, name, _get_at_, num)(dest), M_REMOVE_PARENTHESIS (param) ) , +#define M_TUPL3_INIT_WITH_P6(name, dest, num, oplist, param) \ + M_IF(M_GET_PROPERTY(oplist, LET_AS_INIT_WITH))(M_TUPL3_INIT_WITH_P5, M_TUPL3_INIT_WITH_SET)(name, dest, num, oplist, param) + +/* Macros for testing for the presence of a method in the parameter (name, type, oplist) */ +#define M_TUPL3_TEST_METHOD_P(method, trio) \ + M_APPLY(M_TUPL3_TEST_METHOD2_P, method, M_OPFLAT trio) + +#define M_TUPL3_TEST_METHOD2_P(method, f, t, op) \ + M_TEST_METHOD_P(method, op) + + +/********************************** INTERNAL *********************************/ + +/* Macros for testing for the presence of a method in all the params */ +#define M_TUPL3_IF_ALL(method, ...) \ + M_IF(M_REDUCE2(M_TUPL3_TEST_METHOD_P, M_AND, method, __VA_ARGS__)) + +/* Macros for testing for the presence of a method in at least one params */ +#define M_TUPL3_IF_ONE(method, ...) \ + M_IF(M_REDUCE2(M_TUPL3_TEST_METHOD_P, M_OR, method, __VA_ARGS__)) + +// deferred evaluation +#define M_TUPL3_OPLIST_P1(arg) M_TUPL3_OPLIST_P2 arg + +/* Validate the oplist before going further */ +#define M_TUPL3_OPLIST_P2(name, ...) \ + M_IF(M_REDUCE(M_OPLIST_P, M_AND, __VA_ARGS__))(M_TUPL3_OPLIST_P3, M_TUPL3_OPLIST_FAILURE)(name, __VA_ARGS__) + +/* Prepare a clean compilation failure */ +#define M_TUPL3_OPLIST_FAILURE(name, ...) \ + ((M_LIB_ERROR(ONE_ARGUMENT_OF_M_TUPL3_OPLIST_IS_NOT_AN_OPLIST, name, __VA_ARGS__))) + +/* Define the TUPLE oplist */ +#define M_TUPL3_OPLIST_P3(name, ...) \ + (M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_F(name,_init)),), \ + INIT_SET(M_F(name, _init_set)), \ + INIT_WITH(API_1(M_TUPL3_INIT_WITH)), \ + SET(M_F(name,_set)), \ + CLEAR(M_F(name, _clear)), \ + NAME(name), \ + TYPE(M_F(name,_ct)), \ + OPLIST( (__VA_ARGS__) ), \ + M_IF_METHOD_ALL(CMP, __VA_ARGS__)(CMP(M_F(name, _cmp)),), \ + M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_F(name, _hash)),), \ + M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_F(name, _equal_p)),), \ + M_IF_METHOD_ALL(GET_STR, __VA_ARGS__)(GET_STR(M_F(name, _get_str)),), \ + M_IF_METHOD_ALL(PARSE_STR, __VA_ARGS__)(PARSE_STR(M_F(name, _parse_str)),), \ + M_IF_METHOD_ALL(IN_STR, __VA_ARGS__)(IN_STR(M_F(name, _in_str)),), \ + M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_F(name, _out_str)),), \ + M_IF_METHOD_ALL(IN_SERIAL, __VA_ARGS__)(IN_SERIAL(M_F(name, _in_serial)),), \ + M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(M_F(name, _out_serial)),), \ + M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_F(name, _init_move)),), \ + M_IF_METHOD_ALL(MOVE, __VA_ARGS__)(MOVE(M_F(name, _move)),), \ + M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_F(name, _swap)),), \ + M_IF_METHOD_ALL(RESET, __VA_ARGS__)(RESET(M_F(name, _reset)),), \ + EMPLACE_TYPE( ( M_REDUCE2(M_TUPL3_OPLIST_SUBTYPE, M_ID, name, M_SEQ(1, M_NARGS(__VA_ARGS__))) ) ) \ + ) + +/* Support for EMPLACE_TYPE in OPLIST. It refers the created internal type alias */ +#define M_TUPL3_OPLIST_SUBTYPE(name, num) \ + M_C4(name, _type_, num, _ct) + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define TUPLE_DEF2 M_TUPLE_DEF2 +#define TUPLE_DEF2_AS M_TUPLE_DEF2_AS +#define TUPLE_OPLIST M_TUPLE_OPLIST +#define TUPLE_ORDER M_TUPLE_ORDER +#endif + +#endif diff --git a/components/mlib/m-variant.h b/components/mlib/m-variant.h new file mode 100644 index 00000000..1644bc19 --- /dev/null +++ b/components/mlib/m-variant.h @@ -0,0 +1,819 @@ +/* + * M*LIB - VARIANT module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_VARIANT_H +#define MSTARLIB_VARIANT_H + +#include "m-core.h" + +/* Define the variant type and functions. + USAGE: + VARIANT_DEF2(name, [(field1, type1, oplist1), (field2, type2, oplist2), ...] ) */ +#define M_VARIANT_DEF2(name, ...) \ + M_VARIANT_DEF2_AS(name, M_F(name,_t), __VA_ARGS__) + + +/* Define the variant type and functions + as the given name_t + USAGE: + VARIANT_DEF2_AS(name, name_t, [(field1, type1, oplist1), (field2, type2, oplist2), ...] ) */ +#define M_VARIANT_DEF2_AS(name, name_t, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_VAR1ANT_DEF2_P1( (name, name_t M_VAR1ANT_INJECT_GLOBAL(__VA_ARGS__)) ) \ + M_END_PROTECTED_CODE + + +/* Define the oplist of a variant. + USAGE: VARIANT_OPLIST(name[, oplist of the first type, ...]) */ +#define M_VARIANT_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (M_VAR1ANT_OPLIST_P1((__VA_ARGS__, M_BASIC_OPLIST)), \ + M_VAR1ANT_OPLIST_P1((__VA_ARGS__ ))) + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* Contract of a variant. */ +#define M_VAR1ANT_CONTRACT(name, my) do { \ + M_ASSERT(my != NULL); \ + M_ASSERT(my->type >= M_F(name, _EMPTY)); \ + M_ASSERT(my->type <= (enum M_F(name, _enum)) M_F(name, _MAX_TYPE)); \ +} while (0) + +/* Inject the oplist within the list of arguments */ +#define M_VAR1ANT_INJECT_GLOBAL(...) \ + M_MAP(M_VAR1ANT_INJECT_OPLIST_A, __VA_ARGS__) + +/* Transform (x, type) into (x, type, oplist) if there is global registered oplist + or (x, type, M_BASIC_OPLIST) if there is no global one, + or keep (x, type, oplist) if oplist was already present */ +#define M_VAR1ANT_INJECT_OPLIST_A( duo_or_trio ) \ + M_VAR1ANT_INJECT_OPLIST_B duo_or_trio + +#define M_VAR1ANT_INJECT_OPLIST_B( f, ... ) \ + M_DEFERRED_COMMA \ + M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) ) + +// Deferred evaluation +#define M_VAR1ANT_DEF2_P1(...) M_ID( M_VAR1ANT_DEF2_P2 __VA_ARGS__ ) + +// Test if all third argument of all arguments is an oplist +#define M_VAR1ANT_IF_ALL_OPLIST(...) \ + M_IF(M_REDUCE(M_VAR1ANT_IS_OPLIST_P, M_AND, __VA_ARGS__)) + +// Test if the third argument is an oplist +#define M_VAR1ANT_IS_OPLIST_P(a) \ + M_OPLIST_P(M_RET_ARG3 a) + +/* Validate the oplist before going further */ +#define M_VAR1ANT_DEF2_P2(name, name_t, ...) \ + M_VAR1ANT_IF_ALL_OPLIST(__VA_ARGS__)(M_VAR1ANT_DEF2_P3, M_VAR1ANT_DEF2_FAILURE)(name, name_t, __VA_ARGS__) + +/* Stop processing with a compilation failure */ +#define M_VAR1ANT_DEF2_FAILURE(name, name_t, ...) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(VARIANT_DEF2): at least one of the given argument is not a valid oplist: " #__VA_ARGS__) + +/* Define the variant */ +#define M_VAR1ANT_DEF2_P3(name, name_t, ...) \ + M_VAR1ANT_DEFINE_TYPE(name, name_t, __VA_ARGS__) \ + M_VAR1ANT_CONTROL_ALL_OPLIST(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_INIT(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_CLEAR(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_INIT_SET(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_SET(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_EMPLACE(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_TEST_P(name, __VA_ARGS__) \ + M_VAR1ANT_IF_ALL(INIT, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_INIT_FIELD(name, __VA_ARGS__),) \ + M_VAR1ANT_DEFINE_INIT_SETTER_FIELD(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_SETTER_FIELD(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_GETTER_FIELD(name, __VA_ARGS__) \ + M_VAR1ANT_DEFINE_RESET_FUNC(name, __VA_ARGS__) \ + M_VAR1ANT_IF_ALL(HASH, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_HASH(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL(EQUAL, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_EQUAL(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL(GET_STR, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_GET_STR(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL2(PARSE_STR, INIT, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_PARSE_STR(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL(OUT_STR, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_OUT_STR(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL2(IN_STR, INIT, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_IN_STR(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL(OUT_SERIAL, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_OUT_SERIAL(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL2(IN_SERIAL, INIT, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_IN_SERIAL(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL(INIT_MOVE, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL(INIT_MOVE, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_MOVE(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL(INIT_MOVE, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_MOVER(name, __VA_ARGS__),) \ + M_VAR1ANT_IF_ALL(SWAP, __VA_ARGS__) \ + (M_VAR1ANT_DEFINE_SWAP(name, __VA_ARGS__),) + + +/* Get the field name, the type, the oplist or the methods + based on the variant (field, type, oplist) */ +#define M_VAR1ANT_GET_FIELD(f,t,o) f +#define M_VAR1ANT_GET_TYPE(f,t,o) t +#define M_VAR1ANT_GET_OPLIST(f,t,o) o +#define M_VAR1ANT_GET_INIT(f,t,o) M_GET_INIT o +#define M_VAR1ANT_GET_INIT_SET(f,t,o) M_GET_INIT_SET o +#define M_VAR1ANT_GET_INIT_MOVE(f,t,o) M_GET_INIT_MOVE o +#define M_VAR1ANT_GET_MOVE(f,t,o) M_GET_MOVE o +#define M_VAR1ANT_GET_SET(f,t,o) M_GET_SET o +#define M_VAR1ANT_GET_CLEAR(f,t,o) M_GET_CLEAR o +#define M_VAR1ANT_GET_CMP(f,t,o) M_GET_CMP o +#define M_VAR1ANT_GET_HASH(f,t,o) M_GET_HASH o +#define M_VAR1ANT_GET_EQUAL(f,t,o) M_GET_EQUAL o +#define M_VAR1ANT_GET_STR(f,t,o) M_GET_GET_STR o +#define M_VAR1ANT_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o +#define M_VAR1ANT_GET_OUT_STR(f,t,o) M_GET_OUT_STR o +#define M_VAR1ANT_GET_IN_STR(f,t,o) M_GET_IN_STR o +#define M_VAR1ANT_GET_OUT_SERIAL(f,t,o) M_GET_OUT_SERIAL o +#define M_VAR1ANT_GET_IN_SERIAL(f,t,o) M_GET_IN_SERIAL o +#define M_VAR1ANT_GET_SWAP(f,t,o) M_GET_SWAP o + +/* Call the methods through API */ +#define M_VAR1ANT_CALL_INIT(t, ...) M_APPLY_API(M_VAR1ANT_GET_INIT t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_INIT_SET(t, ...) M_APPLY_API(M_VAR1ANT_GET_INIT_SET t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_INIT_MOVE(t, ...) M_APPLY_API(M_VAR1ANT_GET_INIT_MOVE t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_MOVE(t, ...) M_APPLY_API(M_VAR1ANT_GET_MOVE t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_SET(t, ...) M_APPLY_API(M_VAR1ANT_GET_SET t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_CLEAR(t, ...) M_APPLY_API(M_VAR1ANT_GET_CLEAR t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_CMP(t, ...) M_APPLY_API(M_VAR1ANT_GET_CMP t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_HASH(t, ...) M_APPLY_API(M_VAR1ANT_GET_HASH t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_EQUAL(t, ...) M_APPLY_API(M_VAR1ANT_GET_EQUAL t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_GET_STR(t, ...) M_APPLY_API(M_VAR1ANT_GET_STR t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_PARSE_STR(t, ...) M_APPLY_API(M_VAR1ANT_GET_PARSE_STR t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_OUT_STR(t, ...) M_APPLY_API(M_VAR1ANT_GET_OUT_STR t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_IN_STR(t, ...) M_APPLY_API(M_VAR1ANT_GET_IN_STR t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_OUT_SERIAL(t, ...) M_APPLY_API(M_VAR1ANT_GET_OUT_SERIAL t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_IN_SERIAL(t, ...) M_APPLY_API(M_VAR1ANT_GET_IN_SERIAL t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) +#define M_VAR1ANT_CALL_SWAP(t, ...) M_APPLY_API(M_VAR1ANT_GET_SWAP t, M_VAR1ANT_GET_OPLIST t, __VA_ARGS__) + +/* Define the type */ +#define M_VAR1ANT_DEFINE_TYPE(name, name_t, ...) \ + /* Define enum of all types of the variant */ \ + enum M_F(name, _enum) { M_F(name, _EMPTY) \ + M_MAP2(M_VAR1ANT_DEFINE_UNION_ELE, name, __VA_ARGS__) \ + }; \ + /* Define enum equal to the number of types of the variant */ \ + enum M_F(name, _enum_max) { \ + M_F(name, _MAX_TYPE) = M_NARGS(__VA_ARGS__) \ + }; \ + /* Define the variant */ \ + typedef struct M_F(name, _s) { \ + enum M_F(name, _enum) type; \ + union { \ + M_MAP(M_VAR1ANT_DEFINE_TYPE_ELE , __VA_ARGS__) \ + } value; \ + } name_t[1]; \ + \ + typedef struct M_F(name, _s) *M_F(name, _ptr); \ + typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ + /* Define internal type for oplist */ \ + typedef name_t M_F(name, _ct); + +#define M_VAR1ANT_DEFINE_UNION_ELE(name, a) \ + , M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) + +#define M_VAR1ANT_DEFINE_TYPE_ELE(a) \ + M_VAR1ANT_GET_TYPE a M_VAR1ANT_GET_FIELD a ; + + +/* Control that all given oplists of all parameters are really oplists */ +#define M_VAR1ANT_CONTROL_ALL_OPLIST(name, ...) \ + M_MAP2(M_VAR1ANT_CONTROL_OPLIST, name, __VA_ARGS__) + +#define M_VAR1ANT_CONTROL_OPLIST(name, a) \ + M_CHECK_COMPATIBLE_OPLIST(name, M_VAR1ANT_GET_FIELD a, \ + M_VAR1ANT_GET_TYPE a, M_VAR1ANT_GET_OPLIST a) + + +/* Define the INIT function. Init the variant to empty */ +#define M_VAR1ANT_DEFINE_INIT(name, ...) \ + M_INLINE void M_F(name, _init)(M_F(name,_ct) my) { \ + my->type = M_F(name, _EMPTY); \ + } + + +/* Define the INIT_SET function. */ +#define M_VAR1ANT_DEFINE_INIT_SET(name, ...) \ + M_INLINE void M_F(name, _init_set)(M_F(name,_ct) my , \ + M_F(name,_ct) const org) { \ + M_VAR1ANT_CONTRACT(name, org); \ + my->type = org->type; \ + switch (org->type) { \ + M_MAP2(M_VAR1ANT_DEFINE_INIT_SET_FUNC, name, __VA_ARGS__) \ + case M_F(name, _EMPTY): /* fallthrough */ \ + default: M_ASSUME(org->type == M_F(name, _EMPTY)); break; \ + } \ + } + +#define M_VAR1ANT_DEFINE_INIT_SET_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + M_VAR1ANT_CALL_INIT_SET(a, my -> value. M_VAR1ANT_GET_FIELD a , \ + org -> value.M_VAR1ANT_GET_FIELD a ); \ + break; + + +/* Define the SET function. */ +#define M_VAR1ANT_DEFINE_SET(name, ...) \ + M_INLINE void M_F(name, _set)(M_F(name,_ct) my , \ + M_F(name,_ct) const org) { \ + M_VAR1ANT_CONTRACT(name, my); \ + M_VAR1ANT_CONTRACT(name, org); \ + if (my->type != org->type) { \ + /* Different types: clear previous one and create new */ \ + M_F(name, _clear)(my); \ + M_F(name, _init_set)(my, org); \ + } else { \ + /* Same type: optimize the set */ \ + switch (org->type) { \ + M_MAP2(M_VAR1ANT_DEFINE_SET_FUNC, name, __VA_ARGS__) \ + case M_F(name, _EMPTY): /* fallthrough */ \ + default: M_ASSUME(org->type == M_F(name, _EMPTY)); break; \ + } \ + } \ + } + +#define M_VAR1ANT_DEFINE_SET_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + M_VAR1ANT_CALL_SET(a, my -> value. M_VAR1ANT_GET_FIELD a , \ + org -> value.M_VAR1ANT_GET_FIELD a ); \ + break; + + +/* Define the CLEAR function. */ +#define M_VAR1ANT_DEFINE_CLEAR(name, ...) \ + M_INLINE void M_F(name, _clear)(M_F(name,_ct) my) { \ + M_VAR1ANT_CONTRACT(name, my); \ + switch (my->type) { \ + M_MAP2(M_VAR1ANT_DEFINE_CLEAR_FUNC, name, __VA_ARGS__) \ + case M_F(name, _EMPTY): /* fallthrough */ \ + default: M_ASSUME(my->type == M_F(name, _EMPTY)); break; \ + } \ + my->type = M_F(name, _EMPTY); \ + } + +#define M_VAR1ANT_DEFINE_CLEAR_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + M_VAR1ANT_CALL_CLEAR(a, my -> value. M_VAR1ANT_GET_FIELD a); \ + break; + + +/* Define the TEST_P function. */ +#define M_VAR1ANT_DEFINE_TEST_P(name, ...) \ + M_INLINE bool M_F(name, _empty_p)(M_F(name,_ct) const my) { \ + M_VAR1ANT_CONTRACT(name, my); \ + return my->type == M_F(name, _EMPTY); \ + } \ + M_INLINE enum M_F(name, _enum) \ + M_F(name, _type)(M_F(name,_ct) my) { \ + M_VAR1ANT_CONTRACT(name, my); \ + return my->type; \ + } \ + M_MAP2(M_VAR1ANT_DEFINE_TEST_FUNC, name, __VA_ARGS__) + +#define M_VAR1ANT_DEFINE_TEST_FUNC(name, a) \ + M_INLINE bool \ + M_C4(name, _, M_VAR1ANT_GET_FIELD a, _p)(M_F(name,_ct) const my) { \ + M_VAR1ANT_CONTRACT(name, my); \ + return my->type == M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \ + } + + +/* Define the INIT function. */ +#define M_VAR1ANT_DEFINE_INIT_FIELD(name, ...) \ + M_MAP2(M_VAR1ANT_DEFINE_INIT_FIELD_FUNC, name, __VA_ARGS__) + +#define M_VAR1ANT_DEFINE_INIT_FIELD_FUNC(name, a) \ + M_INLINE void \ + M_C3(name, _init_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my) { \ + /* Reinit variable with the given value */ \ + my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \ + M_VAR1ANT_CALL_INIT(a, my -> value. M_VAR1ANT_GET_FIELD a); \ + } + + +/* Define the INIT_SET of a given type function. */ +#define M_VAR1ANT_DEFINE_INIT_SETTER_FIELD(name, ...) \ + M_MAP2(M_VAR1ANT_DEFINE_INIT_SETTER_FIELD_FUNC, name, __VA_ARGS__) + +#define M_VAR1ANT_DEFINE_INIT_SETTER_FIELD_FUNC(name, a) \ + M_INLINE void \ + M_C3(name, _init_set_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my, \ + M_VAR1ANT_GET_TYPE a const M_VAR1ANT_GET_FIELD a ) { \ + my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \ + M_VAR1ANT_CALL_INIT_SET(a, my -> value. M_VAR1ANT_GET_FIELD a, \ + M_VAR1ANT_GET_FIELD a); \ + } + + +/* Define the SET of a given type function. */ +#define M_VAR1ANT_DEFINE_SETTER_FIELD(name, ...) \ + M_MAP2(M_VAR1ANT_DEFINE_SETTER_FIELD_FUNC, name, __VA_ARGS__) + +#define M_VAR1ANT_DEFINE_SETTER_FIELD_FUNC(name, a) \ + M_INLINE void \ + M_C3(name, _set_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my, \ + M_VAR1ANT_GET_TYPE a const M_VAR1ANT_GET_FIELD a ) { \ + M_VAR1ANT_CONTRACT(name, my); \ + if (my->type == M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) ) { \ + M_VAR1ANT_CALL_SET(a, my -> value. M_VAR1ANT_GET_FIELD a, \ + M_VAR1ANT_GET_FIELD a); \ + } else { \ + M_F(name, _clear)(my); \ + /* Reinit variable with the given value */ \ + my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \ + M_VAR1ANT_CALL_INIT_SET(a, my -> value. M_VAR1ANT_GET_FIELD a, \ + M_VAR1ANT_GET_FIELD a); \ + } \ + } + + +/* Define the GET_field of a given type function. */ +#define M_VAR1ANT_DEFINE_GETTER_FIELD(name, ...) \ + M_MAP2(M_VAR1ANT_DEFINE_GETTER_FIELD_FUNC, name, __VA_ARGS__) + +#define M_VAR1ANT_DEFINE_GETTER_FIELD_FUNC(name, a) \ + M_INLINE M_VAR1ANT_GET_TYPE a * \ + M_C3(name, _get_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my) { \ + M_VAR1ANT_CONTRACT(name, my); \ + if (my->type != M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) ) { \ + return NULL; \ + } \ + return &my -> value . M_VAR1ANT_GET_FIELD a; \ + } \ + \ + M_INLINE M_VAR1ANT_GET_TYPE a const * \ + M_C3(name, _cget_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) const my) { \ + M_VAR1ANT_CONTRACT(name, my); \ + if (my->type != M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) ) { \ + return NULL; \ + } \ + return &my -> value . M_VAR1ANT_GET_FIELD a; \ + } + + +/* Define the EMPLACE of a given type function. + NOTE: Use of a variant of MAP3 because of recursive use of MAP2/MAP3/REDUCE2 ! + */ +#define M_VAR1ANT_DEFINE_EMPLACE(name, ...) \ + M_VAR1ANT_MAP3_ALT(M_VAR1ANT_DEFINE_EMPLACE_FUNC, name, __VA_ARGS__) +// Variant of M_MAP3 using M_REDUCE3 +#define M_VAR1ANT_MAP3_ALT(f, d, ...) M_REDUCE3(f, M_VAR1ANT_MAP3_ALT_ID, d, __VA_ARGS__) +#define M_VAR1ANT_MAP3_ALT_ID(a, b) a b + +#define M_VAR1ANT_DEFINE_EMPLACE_FUNC(name, num, a) \ + M_EMPLACE_QUEUE_DEF( (name, M_VAR1ANT_GET_FIELD a), M_F(name,_ct), M_C3(name, _init_emplace_, M_VAR1ANT_GET_FIELD a), M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_DEFINE_INIT_EMPLACE_DEF) \ + M_EMPLACE_QUEUE_DEF( (name, M_VAR1ANT_GET_FIELD a), M_F(name,_ct), M_C3(name, _emplace_, M_VAR1ANT_GET_FIELD a), M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_DEFINE_EMPLACE_DEF) + +#define M_VAR1ANT_DEFINE_INIT_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ +M_INLINE void \ + function_name(name_t my \ + M_EMPLACE_LIST_TYPE_VAR(ab, exp_emplace_type) ) \ + { \ + my->type = M_C4(M_PAIR_1 name, _, M_PAIR_2 name, _value); \ + M_EMPLACE_CALL_FUNC(ab, init_func, oplist, my -> value. M_PAIR_2 name, exp_emplace_type); \ + } \ + +#define M_VAR1ANT_DEFINE_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ + M_INLINE void \ + function_name(name_t my \ + M_EMPLACE_LIST_TYPE_VAR(ab, exp_emplace_type) ) \ + { \ + /* No optimization done */ \ + M_C(M_PAIR_1 name, _clear)(my); \ + my->type = M_C4(M_PAIR_1 name, _, M_PAIR_2 name, _value); \ + M_EMPLACE_CALL_FUNC(ab, init_func, oplist, my -> value. M_PAIR_2 name, exp_emplace_type); \ + } \ + +/* Define the EQUAL_P function. */ +#define M_VAR1ANT_DEFINE_EQUAL(name, ...) \ + M_INLINE bool M_F(name, _equal_p)(M_F(name,_ct) const e1 , \ + M_F(name,_ct) const e2) { \ + bool b; \ + M_VAR1ANT_CONTRACT(name, e1); \ + M_VAR1ANT_CONTRACT(name, e2); \ + if (e1->type != e2->type) return false; \ + switch (e1->type) { \ + case M_F(name, _EMPTY): break; \ + M_MAP2(M_VAR1ANT_DEFINE_EQUAL_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(false); break; \ + } \ + return true; \ + } + +#define M_VAR1ANT_DEFINE_EQUAL_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + b = M_VAR1ANT_CALL_EQUAL(a, e1 -> value . M_VAR1ANT_GET_FIELD a , \ + e2 -> value . M_VAR1ANT_GET_FIELD a ); \ + return b; \ + break; + + +/* Define the HASH function. */ +#define M_VAR1ANT_DEFINE_HASH(name, ...) \ + M_INLINE size_t M_F(name, _hash)(M_F(name,_ct) const e1) { \ + M_VAR1ANT_CONTRACT(name, e1); \ + M_HASH_DECL(hash); \ + M_HASH_UP (hash, (unsigned int) (e1 -> type)); \ + switch (e1->type) { \ + case M_F(name, _EMPTY): break; \ + M_MAP2(M_VAR1ANT_DEFINE_HASH_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(false); break; \ + } \ + return M_HASH_FINAL (hash); \ + } + +#define M_VAR1ANT_DEFINE_HASH_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + M_HASH_UP(hash, M_VAR1ANT_CALL_HASH(a, e1 -> value . M_VAR1ANT_GET_FIELD a) ); \ + break; + + +/* Define the INIT_MOVE function. */ +#define M_VAR1ANT_DEFINE_INIT_MOVE(name, ...) \ + M_INLINE void \ + M_F(name, _init_move)(M_F(name,_ct) el, M_F(name,_ct) org) { \ + M_VAR1ANT_CONTRACT(name, org); \ + el -> type = org -> type; \ + switch (el->type) { \ + case M_F(name, _EMPTY): break; \ + M_MAP2(M_VAR1ANT_DEFINE_INIT_MOVE_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(false); break; \ + } \ + org -> type = M_F(name, _EMPTY); \ + } + +#define M_VAR1ANT_DEFINE_INIT_MOVE_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + M_VAR1ANT_CALL_INIT_MOVE(a, el -> value . M_VAR1ANT_GET_FIELD a, \ + org -> value . M_VAR1ANT_GET_FIELD a); \ + break; + + +/* Define the MOVE function. + This is not optimized version. + It can be optimized if both types are the same. +*/ +#define M_VAR1ANT_DEFINE_MOVE(name, ...) \ + M_INLINE void \ + M_F(name, _move)(M_F(name,_ct) el, M_F(name,_ct) org) { \ + M_VAR1ANT_CONTRACT(name, el); \ + M_VAR1ANT_CONTRACT(name, org); \ + M_F(name, _clear)(el); \ + M_F(name, _init_move)(el , org); \ + } + + +/* Define the MOVE function of a given type */ +#define M_VAR1ANT_DEFINE_MOVER(name, ...) \ + M_MAP2(M_VAR1ANT_DEFINE_MOVER_FUNC, name, __VA_ARGS__) + +#define M_VAR1ANT_DEFINE_MOVER_FUNC(name, a) \ + M_INLINE void \ + M_C3(name, _move_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my, \ + M_VAR1ANT_GET_TYPE a M_VAR1ANT_GET_FIELD a ) { \ + M_VAR1ANT_CONTRACT(name, my); \ + M_F(name, _clear)(my); \ + /* Reinit variable with the given value */ \ + my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \ + M_VAR1ANT_CALL_INIT_MOVE(a, my -> value. M_VAR1ANT_GET_FIELD a, \ + M_VAR1ANT_GET_FIELD a); \ + } + + +/* Define the SWAP function */ +#define M_VAR1ANT_DEFINE_SWAP(name, ...) \ + M_INLINE void \ + M_F(name, _swap)(M_F(name,_ct) el1, M_F(name,_ct) el2) { \ + M_VAR1ANT_CONTRACT(name, el1); \ + M_VAR1ANT_CONTRACT(name, el2); \ + if (el1->type == el2->type) { \ + switch (el1->type) { \ + case M_F(name, _EMPTY): break; \ + M_MAP2(M_VAR1ANT_DEFINE_INIT_SWAP_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(false); break; \ + } \ + } else { \ + M_F(name,_ct) tmp; \ + M_VAR1ANT_IF_ALL(INIT_MOVE, __VA_ARGS__) \ + ( /* NOTE: Slow implementation */ \ + M_F(name, _init_move)(tmp, el1); \ + M_F(name, _init_move)(el1, el2); \ + M_F(name, _init_move)(el2, tmp); \ + , \ + /* NOTE: Very slow implementation */ \ + M_F(name, _init_set)(tmp, el1); \ + M_F(name, _set)(el1, el2); \ + M_F(name, _set)(el2, tmp); \ + M_F(name, _clear)(tmp); \ + ) \ + } \ + } + +#define M_VAR1ANT_DEFINE_INIT_SWAP_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + M_VAR1ANT_CALL_SWAP(a, el1 -> value . M_VAR1ANT_GET_FIELD a, \ + el2 -> value . M_VAR1ANT_GET_FIELD a); \ + break; + + +/* Define the GET_STR function */ +#define M_VAR1ANT_DEFINE_GET_STR(name, ...) \ + M_INLINE void M_F(name, _get_str)(m_string_t str, \ + M_F(name,_ct) const el, \ + bool append) { \ + M_VAR1ANT_CONTRACT(name, el); \ + M_ASSERT (str != NULL); \ + void (*func)(m_string_t, const char *); \ + func = append ? m_string_cat_cstr : m_string_set_cstr; \ + switch (el->type) { \ + case M_F(name, _EMPTY): func(str, "@EMPTY@"); break; \ + M_MAP2(M_VAR1ANT_DEFINE_GET_STR_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(false); break; \ + } \ + m_string_push_back (str, '@'); \ + } + +#define M_VAR1ANT_DEFINE_GET_STR_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + func(str, "@" M_AS_STR(M_VAR1ANT_GET_FIELD a) "@"); \ + M_VAR1ANT_CALL_GET_STR(a, str, el -> value . M_VAR1ANT_GET_FIELD a, true); \ + break; + + +/* Define the PARSE_STR function */ +#define M_VAR1ANT_DEFINE_PARSE_STR(name, ...) \ + M_INLINE bool M_F(name, _parse_str)(M_F(name,_ct) el, \ + const char str[], \ + const char **endp) { \ + M_VAR1ANT_CONTRACT(name, el); \ + M_ASSERT (str != NULL); \ + bool success = false; \ + char variantTypeBuf[M_USE_IDENTIFIER_ALLOC+1]; \ + int c = *str++; \ + unsigned int i = 0; \ + M_F(name, _reset)(el); \ + if (c != '@') goto exit; \ + /* First read the name of the type */ \ + c = *str++; \ + while (c != '@' && c != 0 && i < sizeof(variantTypeBuf) - 1) { \ + variantTypeBuf[i++] = (char) c; \ + c = *str++; \ + } \ + if (c != '@') goto exit; \ + variantTypeBuf[i++] = 0; \ + M_ASSERT(i < sizeof(variantTypeBuf)); \ + /* In function of the type */ \ + if (strcmp(variantTypeBuf, "EMPTY") == 0) { \ + el->type = M_F(name, _EMPTY); \ + } \ + M_MAP2(M_VAR1ANT_DEFINE_PARSE_STR_FUNC , name, __VA_ARGS__) \ + else goto exit; \ + success = (*str++ == '@'); \ + exit: \ + if (endp) *endp = str; \ + return success; \ + } + +#define M_VAR1ANT_DEFINE_PARSE_STR_FUNC(name, a) \ + else if (strcmp (variantTypeBuf, M_AS_STR(M_VAR1ANT_GET_FIELD a)) == 0) { \ + el->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \ + M_VAR1ANT_CALL_INIT(a, el ->value . M_VAR1ANT_GET_FIELD a ); \ + bool b = M_VAR1ANT_CALL_PARSE_STR(a, el -> value . M_VAR1ANT_GET_FIELD a, str, &str); \ + if (!b) goto exit; \ + } + + +/* Define the OUT_STR function */ +#define M_VAR1ANT_DEFINE_OUT_STR(name, ...) \ + M_INLINE void M_F(name, _out_str)(FILE *f, \ + M_F(name,_ct) const el) { \ + M_VAR1ANT_CONTRACT(name, el); \ + M_ASSERT (f != NULL); \ + switch (el->type) { \ + case M_F(name, _EMPTY): fprintf(f, "@EMPTY@"); break; \ + M_MAP2(M_VAR1ANT_DEFINE_OUT_STR_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(false); break; \ + } \ + fputc ('@', f); \ + } + +#define M_VAR1ANT_DEFINE_OUT_STR_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + fprintf(f, "@" M_AS_STR(M_VAR1ANT_GET_FIELD a) "@"); \ + M_VAR1ANT_CALL_OUT_STR(a, f, el -> value . M_VAR1ANT_GET_FIELD a); \ + break; + + +/* Define the IN_STR function */ +#define M_VAR1ANT_DEFINE_IN_STR(name, ...) \ + M_INLINE bool M_F(name, _in_str)(M_F(name,_ct) el, \ + FILE *f) { \ + M_VAR1ANT_CONTRACT(name, el); \ + M_ASSERT (f != NULL); \ + char variantTypeBuf[M_USE_IDENTIFIER_ALLOC+1]; \ + M_F(name, _reset)(el); \ + if (fgetc(f) != '@') return false; \ + /* First read the name of the type */ \ + bool b = true; \ + int c = fgetc(f); \ + unsigned int i = 0; \ + while (c != '@' && c != EOF && i < sizeof(variantTypeBuf) - 1) { \ + variantTypeBuf[i++] = (char) c; \ + c = fgetc(f); \ + } \ + if (c != '@') return false; \ + variantTypeBuf[i++] = 0; \ + M_ASSERT(i < sizeof(variantTypeBuf)); \ + /* In function of the type */ \ + if (strcmp(variantTypeBuf, "EMPTY") == 0) { \ + el->type = M_F(name, _EMPTY); \ + } \ + M_MAP2(M_VAR1ANT_DEFINE_IN_STR_FUNC , name, __VA_ARGS__) \ + else { b = false; } \ + return b && (fgetc(f) == '@'); \ + } + +#define M_VAR1ANT_DEFINE_IN_STR_FUNC(name, a) \ + else if (strcmp (variantTypeBuf, M_AS_STR(M_VAR1ANT_GET_FIELD a)) == 0) { \ + el->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \ + M_VAR1ANT_CALL_INIT(a, el ->value . M_VAR1ANT_GET_FIELD a ); \ + b = M_VAR1ANT_CALL_IN_STR(a, el -> value . M_VAR1ANT_GET_FIELD a, f); \ + } + + +/* Return the STRING version of a parameter name */ +#define M_VAR1ANT_STRINGIFY_NAME(a) \ + M_AS_STR(M_VAR1ANT_GET_FIELD a) + + +/* Define the OUT_SERIAL function */ +#define M_VAR1ANT_DEFINE_OUT_SERIAL(name, ...) \ + M_INLINE m_serial_return_code_t \ + M_F(name, _out_serial)(m_serial_write_t f, \ + M_F(name,_ct) const el) { \ + M_VAR1ANT_CONTRACT(name, el); \ + const int field_max = M_NARGS(__VA_ARGS__); \ + static const char *const field_name[] = \ + { M_REDUCE(M_VAR1ANT_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + switch (el->type) { \ + case M_F(name, _EMPTY): \ + return f->m_interface->write_variant_start(local, f, field_name, field_max, -1); \ + break; \ + M_MAP2(M_VAR1ANT_DEFINE_OUT_SERIAL_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(false); break; \ + } \ + ret |= f->m_interface->write_variant_end(local, f); \ + return ret & M_SERIAL_FAIL; \ + } + +#define M_VAR1ANT_DEFINE_OUT_SERIAL_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + ret = f->m_interface->write_variant_start(local, f, field_name, field_max, \ + M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) -1); \ + M_VAR1ANT_CALL_OUT_SERIAL(a, f, el -> value . M_VAR1ANT_GET_FIELD a); \ + break; + + +/* Define the IN_SERIAL function */ +#define M_VAR1ANT_DEFINE_IN_SERIAL(name, ...) \ + M_INLINE m_serial_return_code_t \ + M_F(name, _in_serial)(M_F(name,_ct) el, \ + m_serial_read_t f) { \ + M_VAR1ANT_CONTRACT(name, el); \ + const int field_max = M_NARGS(__VA_ARGS__); \ + static const char *const field_name[] = \ + { M_REDUCE(M_VAR1ANT_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \ + M_ASSERT (f != NULL && f->m_interface != NULL); \ + m_serial_local_t local; \ + m_serial_return_code_t ret; \ + int id = -1; \ + M_F(name, _reset)(el); \ + ret = f->m_interface->read_variant_start(local, f, field_name, field_max, &id); \ + if (ret != M_SERIAL_OK_CONTINUE) return ret; \ + M_ASSERT (id >= 0 && id < field_max); \ + el->type = (enum M_F(name, _enum))(id+1); \ + switch (id+1) { \ + M_MAP2(M_VAR1ANT_DEFINE_IN_SERIAL_FUNC , name, __VA_ARGS__) \ + default: M_ASSUME(false); break; \ + } \ + if (ret == M_SERIAL_OK_DONE) \ + ret = f->m_interface->read_variant_end(local, f); \ + return ret; \ + } + +#define M_VAR1ANT_DEFINE_IN_SERIAL_FUNC(name, a) \ + case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \ + M_VAR1ANT_CALL_INIT(a, el ->value . M_VAR1ANT_GET_FIELD a ); \ + ret = M_VAR1ANT_CALL_IN_SERIAL(a, el -> value . M_VAR1ANT_GET_FIELD a, f); \ + break; \ + + +/* Define the RESET function */ +#define M_VAR1ANT_DEFINE_RESET_FUNC(name, ...) \ + M_INLINE void M_F(name, _reset)(M_F(name,_ct) my) \ + { \ + M_VAR1ANT_CONTRACT(name, my); \ + M_F(name, _clear)(my); \ + M_F(name, _init)(my); \ + } \ + + +/********************************** INTERNAL *********************************/ + +/* deferred evaluation of the oplist */ +#define M_VAR1ANT_OPLIST_P1(arg) M_VAR1ANT_OPLIST_P2 arg + +/* Validate the oplist before going further */ +#define M_VAR1ANT_OPLIST_P2(name, ...) \ + M_IF(M_REDUCE(M_OPLIST_P, M_AND, __VA_ARGS__))(M_VAR1ANT_OPLIST_P3, M_VAR1ANT_OPLIST_FAILURE)(name, __VA_ARGS__) + +/* Prepare a clean compilation failure */ +#define M_VAR1ANT_OPLIST_FAILURE(name, ...) \ + ((M_LIB_ERROR(ONE_ARGUMENT_OF_VARIANT_OPLIST_IS_NOT_AN_OPLIST, name, __VA_ARGS__))) + +/* Define the oplist */ +#define M_VAR1ANT_OPLIST_P3(name, ...) \ + (INIT(M_F(name,_init)), \ + INIT_SET(M_F(name, _init_set)), \ + SET(M_F(name,_set)), \ + CLEAR(M_F(name, _clear)), \ + RESET(M_F(name, _reset)), \ + NAME(name), \ + TYPE(M_F(name,_ct)), \ + EMPTY_P(M_F(name,_empty_p)), \ + M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_F(name, _hash)),), \ + M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_F(name, _equal_p)),), \ + M_IF_METHOD_ALL(GET_STR, __VA_ARGS__)(GET_STR(M_F(name, _get_str)),), \ + M_IF_METHOD2_ALL(PARSE_STR, INIT, __VA_ARGS__)(PARSE_STR(M_F(name, _parse_str)),), \ + M_IF_METHOD2_ALL(IN_STR, INIT, __VA_ARGS__)(IN_STR(M_F(name, _in_str)),), \ + M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_F(name, _out_str)),), \ + M_IF_METHOD2_ALL(IN_SERIAL, INIT, __VA_ARGS__)(IN_SERIAL(M_F(name, _in_serial)),), \ + M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(M_F(name, _out_serial)),), \ + M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_F(name, _init_move)),), \ + M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(MOVE(M_F(name, _move)),), \ + M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_F(name, _swap)),), \ + ) + + +/********************************** INTERNAL *********************************/ + +/* Macros for testing for method presence */ +#define M_VAR1ANT_TEST_METHOD_P2(method, f, t, op) \ + M_TEST_METHOD_P(method, op) +#define M_VAR1ANT_TEST_METHOD_P(method, trio) \ + M_APPLY(M_VAR1ANT_TEST_METHOD_P2, method, M_OPFLAT trio) +#define M_VAR1ANT_IF_ALL(method, ...) \ + M_IF(M_REDUCE2(M_VAR1ANT_TEST_METHOD_P, M_AND, method, __VA_ARGS__)) + +#define M_VAR1ANT_TEST_METHOD2_P2(method1, method2, f, t, op) \ + M_AND(M_TEST_METHOD_P(method1, op), M_TEST_METHOD_P(method2, op)) +#define M_VAR1ANT_TEST_METHOD2_P(method, trio) \ + M_APPLY(M_VAR1ANT_TEST_METHOD2_P2, M_PAIR_1 method, M_PAIR_2 method, M_OPFLAT trio) +#define M_VAR1ANT_IF_ALL2(method1, method2, ...) \ + M_IF(M_REDUCE2(M_VAR1ANT_TEST_METHOD2_P, M_AND, (method1, method2), __VA_ARGS__)) + + +/********************************** INTERNAL *********************************/ + +#if M_USE_SMALL_NAME +#define VARIANT_DEF2 M_VARIANT_DEF2 +#define VARIANT_DEF2_AS M_VARIANT_DEF2_AS +#define VARIANT_OPLIST M_VARIANT_OPLIST +#endif + +#endif diff --git a/components/mlib/m-worker.h b/components/mlib/m-worker.h new file mode 100644 index 00000000..22a59c77 --- /dev/null +++ b/components/mlib/m-worker.h @@ -0,0 +1,698 @@ +/* + * M*LIB / WORKER - Extra worker interface + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_WORKER_H +#define MSTARLIB_WORKER_H + +/* The User Code can define M_USE_WORKER to 0 to disable the use of workers. + The macros / functions are then defined to only use one core. + By default, the behavior is to use workers. +*/ +#ifndef M_USE_WORKER +# define M_USE_WORKER 1 +#endif + + +#if M_USE_WORKER + +#include "m-atomic.h" +#include "m-buffer.h" +#include "m-thread.h" + +/* Include needed system header for detection of how many cores are available in the system */ +#if defined(_WIN32) +# include +#elif (defined(__APPLE__) && defined(__MACH__)) \ + || defined(__DragonFly__) || defined(__FreeBSD__) \ + || defined(__NetBSD__) || defined(__OpenBSD__) +# include +# include +# define M_USE_WORKER_SYSCTL 1 +#else +# include +#endif + +/* Support for CLANG block since CLANG doesn't support nested function. + M-WORKER uses its 'blocks' extension instead, but it is not compatible + with function. + So you need to compile with "-fblocks" and link with "-lBlocksRuntime" + if you use clang & want to use the MACRO version. + + if C++, it will use Lambda function (and std::function) instead + (It doesn't support pre-C++11 compiler). + + Otherwise go with nested function (GCC) for the MACRO version. + + This behavior can be overriden by User Code by defining to 1 or 0 the + following macros: + * M_USE_WORKER_CPP_FUNCTION + * M_USE_WORKER_CLANG_BLOCK +*/ + +#if defined(__cplusplus) && !defined(M_USE_WORKER_CPP_FUNCTION) +# define M_USE_WORKER_CPP_FUNCTION 1 +# include +#elif defined(__has_extension) && !defined(M_USE_WORKER_CLANG_BLOCK) +# if __has_extension(blocks) +# define M_USE_WORKER_CLANG_BLOCK 1 +# endif +#endif + +#ifndef M_USE_WORKER_CLANG_BLOCK +# define M_USE_WORKER_CLANG_BLOCK 0 +#endif +#ifndef M_USE_WORKER_CPP_FUNCTION +# define M_USE_WORKER_CPP_FUNCTION 0 +#endif + +/* Control that not both options are selected at the same time. + Note: there are not really incompatible, but if we use C++ we shall go to + lambda directly (there is no need to support blocks). */ +#if M_USE_WORKER_CLANG_BLOCK && M_USE_WORKER_CPP_FUNCTION +# error M_USE_WORKER_CPP_FUNCTION and M_USE_WORKER_CLANG_BLOCK are both defined. This is not supported. +#endif + +M_BEGIN_PROTECTED_CODE + +/* Definition of a work order */ +typedef struct m_work3r_order_s { + struct m_worker_sync_s *block; // Reference to the shared Synchronization block + void * data; // The work order data + void (*func) (void *data); // The work order function (for GCC) +#if M_USE_WORKER_CLANG_BLOCK + void (^blockFunc)(void *data); // The work order function (block for clang) +#endif +#if M_USE_WORKER_CPP_FUNCTION + std::function function; // The work order function (for C++) +#endif +} m_work3r_order_ct; + +/* Define the macros needed to initialize an order. + * * MACRO to be used to send an empty order to stop the thread + * * MACRO to complete the not-used fields + */ +#if M_USE_WORKER_CLANG_BLOCK || M_USE_WORKER_CPP_FUNCTION +# define M_WORK3R_EMPTY_ORDER { NULL, NULL, NULL, NULL } +# define M_WORK3R_EXTRA_ORDER , NULL +#else +# define M_WORK3R_EMPTY_ORDER { NULL, NULL, NULL } +# define M_WORK3R_EXTRA_ORDER +#endif + +/* As it is C++, it uses std::function, M_POD_OPLIST + is not sufficient for initialization of the structure. + So let's use C++ constructor, destructor and copy constructor */ +#if M_USE_WORKER_CPP_FUNCTION +# define M_WORK3R_CPP_INIT(x) (new (&(x)) m_work3r_order_ct()) +# define M_WORK3R_CPP_INIT_SET(x, y) (new (&(x)) m_work3r_order_ct(y)) +# define M_WORK3R_CPP_SET(x, y) ((x) = (y)) +# define M_WORK3R_CPP_CLEAR(x) ((&(x))->~m_work3r_order_ct()) +# define M_WORK3R_CPP_INIT_MOVE(x,y) (new (&(x)) m_work3r_order_ct(y), ((&(y))->~m_work3r_order_ct())) +# define M_WORK3R_OPLIST \ + (INIT(M_WORK3R_CPP_INIT), INIT_SET(M_WORK3R_CPP_INIT_SET), \ + SET(M_WORK3R_CPP_SET), CLEAR(M_WORK3R_CPP_CLEAR), INIT_MOVE(M_WORK3R_CPP_INIT_MOVE) ) +#else +# define M_WORK3R_OPLIST M_POD_OPLIST +#endif + +/* Definition of the identity of a worker thread */ +typedef struct m_work3r_thread_s { + m_thread_t id; +} m_work3r_thread_ct; + +/* Definition of the queue that will record the work orders */ +BUFFER_DEF(m_work3r_queue, m_work3r_order_ct, 0, + BUFFER_QUEUE|BUFFER_UNBLOCKING_PUSH|BUFFER_BLOCKING_POP|BUFFER_THREAD_SAFE|BUFFER_DEFERRED_POP, M_WORK3R_OPLIST) + +/* Definition the global pool of workers */ +typedef struct m_worker_s { + /* The work order queue */ + m_work3r_queue_t queue_g; + + /* The table of available workers */ + m_work3r_thread_ct *worker; + + /* Number of workers in the table */ + unsigned int numWorker_g; + + /* The global reset function */ + void (*resetFunc_g)(void); + + /* The global clear function */ + void (*clearFunc_g)(void); + + m_mutex_t lock; + m_cond_t a_thread_ends; // EVENT: A worker has ended + +} m_worker_t[1]; + +/* Definition of the synchronization point for workers */ +typedef struct m_worker_sync_s { + atomic_int num_spawn; // Number of spawned workers accord this synchronization point + atomic_int num_terminated_spawn; // Number of terminated spawned workers + struct m_worker_s *worker; // Reference to the pool of workers +} m_worker_sync_t[1]; + + +/* Extend m_worker_spawn by defining a specialization function + with the given arguments. + Generate the needed encapsulation for the user. + USAGE: name, oplists of arguments */ +#define M_WORKER_SPAWN_DEF2(name, ...) \ + M_BEGIN_PROTECTED_CODE \ + M_WORK3R_SPAWN_EXTEND_P1( (name, M_MAP_C(M_WORK3R_SPAWN_EXTEND_P0, __VA_ARGS__) ) ) \ + M_END_PROTECTED_CODE + +/* Output a valid oplist with the given type. + input is (fieldname, type) or (fieldname, type, oplist) + Output shall be : M_OPEXTEND(M_GLOBAL_OPLIST_OR_DEF(type_or_oplist)(), TYPE(type)) / M_OPEXTEND(oplist, TYPE(type)) + */ +#define M_WORK3R_SPAWN_EXTEND_P0(...) M_BY_NARGS(M_WORK3R_SPAWN_EXTEND_P0, M_ID __VA_ARGS__) __VA_ARGS__ +#define M_WORK3R_SPAWN_EXTEND_P0__2(field, type) M_OPEXTEND(M_GLOBAL_OPLIST_OR_DEF(type)(), TYPE(type)) +#define M_WORK3R_SPAWN_EXTEND_P0__3(field, type, oplist) M_IF_OPLIST(oplist)(M_WORK3R_SPAWN_EXTEND_P0__3_OK, M_WORK3R_SPAWN_EXTEND_P0__3_KO)(field, type, oplist) +#define M_WORK3R_SPAWN_EXTEND_P0__3_OK(field, type, oplist) M_OPEXTEND(oplist, TYPE(type)) +#define M_WORK3R_SPAWN_EXTEND_P0__3_KO(field, type, oplist) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(M_WORKER_SPAWN_EXTEND): the argument is not a valid oplist: " M_MAP(M_AS_STR, oplist)) + +/* Deferred evaluation for the definition, + so that all arguments are evaluated before further expansion */ +#define M_WORK3R_SPAWN_EXTEND_P1(arg) M_ID( M_WORK3R_SPAWN_EXTEND_P2 arg ) + +/* Validate the oplist before going further */ +#define M_WORK3R_SPAWN_EXTEND_P2(name, ...) \ + M_IF(M_REDUCE(M_OPLIST_P, M_AND, __VA_ARGS__)) \ + (M_WORK3R_SPAWN_EXTEND_P3, M_WORK3R_SPAWN_EXTEND_FAILURE)(name, __VA_ARGS__) + +/* Stop processing with a compilation failure */ +#define M_WORK3R_SPAWN_EXTEND_FAILURE(name, ...) \ + M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, \ + "(M_WORKER_SPAWN_EXTEND): at least one of the given argument is not a valid oplist: " \ + M_MAP(M_AS_STR, __VA_ARGS__)) + +/* Define the extension of spawn */ +#define M_WORK3R_SPAWN_EXTEND_P3(name, ...) \ + M_WORK3R_SPAWN_EXTEND_DEF_TYPE(name, __VA_ARGS__) \ + M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK(name, __VA_ARGS__) \ + M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE(name, __VA_ARGS__) \ + +/* Define the type */ +#define M_WORK3R_SPAWN_EXTEND_DEF_TYPE(name, ...) \ + typedef void (*M_C3(m_worker_,name, _callback_ct))(M_MAP_C(M_WORK3R_SPAWN_EXTEND_DEF_TYPE_TYPE, __VA_ARGS__)); \ + struct M_C3(m_worker_, name, _s){ \ + M_C3(m_worker_, name, _callback_ct) callback; \ + M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_TYPE_FIELD, data, __VA_ARGS__) \ + }; + +#define M_WORK3R_SPAWN_EXTEND_DEF_TYPE_FIELD(data, num, oplist) \ + M_GET_TYPE oplist M_C(field, num); + +#define M_WORK3R_SPAWN_EXTEND_DEF_TYPE_TYPE(oplist) \ + M_GET_TYPE oplist + +/* Define the callback */ +#define M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK(name, ...) \ + M_INLINE void \ + M_C3(m_work3r_, name, _clear)(struct M_C3(m_worker_, name, _s) *p) \ + { \ + M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_CLEAR, data, __VA_ARGS__) \ + /* TODO: Overload */ \ + M_MEMORY_DEL(p); \ + } \ + \ + M_INLINE void \ + M_C3(m_work3r_, name, _callback)(void *data) \ + { \ + struct M_C3(m_worker_, name, _s) *p = (struct M_C3(m_worker_, name, _s) *) data; \ + (*p->callback)( \ + M_MAP3_C(M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_FIELD, data, __VA_ARGS__) \ + ); \ + M_C3(m_work3r_, name, _clear)(p); \ + } + +#define M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_FIELD(data, num, oplist) \ + p->M_C(field, num) + +#define M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_CLEAR(data, num, oplist) \ + M_CALL_CLEAR(oplist, p->M_C(field, num)) ; + +/* Define the emplace like spawn method */ +#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE(name, ...) \ + M_INLINE void \ + M_C(m_worker_spawn_, name)(m_worker_sync_t block, M_C3(m_worker_, name, _callback_ct) callback, \ + M_MAP3_C(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD, data, __VA_ARGS__) \ + ) \ + { \ + if (!m_work3r_queue_full_p(block->worker->queue_g)) { \ + struct M_C3(m_worker_, name, _s) *p = M_MEMORY_ALLOC ( struct M_C3(m_worker_, name, _s)); \ + if (M_UNLIKELY_NOMEM(p == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_C3(m_worker_, name, _s))); \ + } \ + p->callback = callback; \ + M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_COPY, data, __VA_ARGS__) \ + const m_work3r_order_ct w = { block, p, M_C3(m_work3r_, name, _callback) M_WORK3R_EXTRA_ORDER }; \ + if (m_work3r_queue_push (block->worker->queue_g, w) == true) { \ + atomic_fetch_add (&block->num_spawn, 1); \ + return; \ + } \ + /* No worker available now. Call the function ourself */ \ + /* But before clear the allocated data */ \ + M_C3(m_work3r_, name, _clear)(p); \ + } \ + /* No worker available. Call the function ourself */ \ + (*callback) ( \ + M_MAP3_C(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_ALONE, data, __VA_ARGS__) \ + ); \ + } + +#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD(data, num, oplist) \ + M_GET_TYPE oplist M_C(param, num) + +#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_COPY(data, num, oplist) \ + M_CALL_INIT_SET(oplist, p-> M_C(field, num), M_C(param, num) ); + +#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_ALONE(data, num, oplist) \ + M_C(param, num) + + + +/* Return the number of CPU cores available in the system. + Works for WINDOWS, MACOS, *BSD, LINUX. + */ +M_INLINE int +m_work3r_get_cpu_count(void) +{ +#if defined(_WIN32) + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + M_ASSERT(sysinfo.dwNumberOfProcessors <= INT_MAX); + return (int) sysinfo.dwNumberOfProcessors; +#elif defined(M_USE_WORKER_SYSCTL) + int nm[2]; + int count = 0; + size_t len = sizeof (count); + nm[0] = CTL_HW; + nm[1] = HW_NCPU; + sysctl(nm, 2, &count, &len, NULL, 0); + return M_MAX(1, count); +#elif defined (_SC_NPROCESSORS_ONLN) + return (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined (_SC_NPROCESSORS_CONF) + return (int) sysconf(_SC_NPROCESSORS_CONF); +#else + return 1; +#endif +} + +// (INTERNAL) Debug support for workers +#if 1 +#define M_WORK3R_DEBUG(...) (void) 0 +#else +#define M_WORK3R_DEBUG(...) printf(__VA_ARGS__) +#endif + +/* Execute the registered work order **synchronously** */ +M_INLINE void +m_work3r_exec(m_work3r_order_ct *w) +{ + M_ASSERT (w!= NULL && w->block != NULL); + M_WORK3R_DEBUG ("Starting thread with data %p\n", w->data); +#if M_USE_WORKER_CLANG_BLOCK + M_WORK3R_DEBUG ("Running %s f=%p b=%p\n", (w->func == NULL) ? "Blocks" : "Function", w->func, w->blockFunc); + if (w->func == NULL) + w->blockFunc(w->data); + else +#endif +#if M_USE_WORKER_CPP_FUNCTION + M_WORK3R_DEBUG ("Running %s f=%p b=%p\n", (w->function == NULL) ? "Lambda" : "Function", w->func, w->blockFunc); + if (w->function) + w->function(w->data); + else +#endif + w->func(w->data); + /* Increment the number of terminated work order for the synchronous point */ + atomic_fetch_add (&w->block->num_terminated_spawn, 1); +} + + +/* The worker thread main loop*/ +M_INLINE void +m_work3r_thread(void *arg) +{ + // Get back the given argument + struct m_worker_s *g = M_ASSIGN_CAST(struct m_worker_s *, arg); + while (true) { + m_work3r_order_ct w; + // If needed, reset the global state of the worker + if (g->resetFunc_g != NULL) { + g->resetFunc_g(); + } + // Waiting for data + M_WORK3R_DEBUG ("Waiting for data (queue: %lu / %lu)\n", m_work3r_queue_size(g->queue_g), m_work3r_queue_capacity(g->queue_g)); + m_work3r_queue_pop(&w, g->queue_g); + // We received a work order + // Note: that the work order is still present in the queue + // preventing further work order to be pushed in the queue until it finishes doing the work + // If a stop request is received, terminate the thread + if (w.block == NULL) break; + // Execute the work order + m_work3r_exec(&w); + // Consumme fully the work order in the queue + m_work3r_queue_pop_release(g->queue_g); + // Signal that a worker has finished. + m_mutex_lock(g->lock); + m_cond_broadcast(g->a_thread_ends); + m_mutex_unlock(g->lock); + } + // If needed, clear global state of the thread + if (g->clearFunc_g != NULL) { + g->clearFunc_g(); + } +} + +/* Initialization of the worker module (constructor) + Input: + @numWorker: number of worker to create (0=autodetect, -1=2*autodetect) + @extraQueue: number of extra work order we can get if all workers are full + @resetFunc: function to reset the state of a worker between work orders (or NULL if none) + @clearFunc: function to clear the state of a worker before terminaning (or NULL if none) +*/ +M_INLINE void +m_worker_init(m_worker_t g, int numWorker, unsigned int extraQueue, void (*resetFunc)(void), void (*clearFunc)(void)) +{ + M_ASSERT (numWorker >= -1); + // Auto compute number of workers if the argument is 0 + if (numWorker <= 0) + numWorker = (1 + (numWorker == -1))*m_work3r_get_cpu_count()-1; + M_WORK3R_DEBUG ("Starting queue with: %d\n", numWorker + extraQueue); + // Initialization + // numWorker can still be 0 if it is a single core cpu (no worker available) + M_ASSERT(numWorker >= 0); + size_t numWorker_st = (size_t) numWorker; + g->worker = M_MEMORY_REALLOC(m_work3r_thread_ct, NULL, numWorker_st); + if (M_UNLIKELY_NOMEM (g->worker == NULL)) { + M_MEMORY_FULL(sizeof (m_work3r_thread_ct) * numWorker_st); + return; + } + m_work3r_queue_init(g->queue_g, numWorker_st + extraQueue); + g->numWorker_g = (unsigned int) numWorker_st; + g->resetFunc_g = resetFunc; + g->clearFunc_g = clearFunc; + m_mutex_init(g->lock); + m_cond_init(g->a_thread_ends); + + // Create & start the workers + for(size_t i = 0; i < numWorker_st; i++) { + m_thread_create(g->worker[i].id, m_work3r_thread, M_ASSIGN_CAST(void*, g)); + } +} +/* Initialization of the worker module (constructor) + Provide default values for the arguments. + Input: + @numWorker: number of worker to create (0=autodetect, -1=2*autodetect) + @extraQueue: number of extra work order we can get if all workers are full + @resetFunc: function to reset the state of a worker between work orders (optional) + @clearFunc: function to clear the state of a worker before terminaning (optional) +*/ +#define m_worker_init(...) m_worker_init(M_DEFAULT_ARGS(5, (0, 0, NULL, NULL), __VA_ARGS__)) + +/* Clear of the worker module (destructor) */ +M_INLINE void +m_worker_clear(m_worker_t g) +{ + M_ASSERT (m_work3r_queue_empty_p (g->queue_g)); + // Push the terminate order on the queue + for(unsigned int i = 0; i < g->numWorker_g; i++) { + m_work3r_order_ct w = M_WORK3R_EMPTY_ORDER; + // Normaly all worker threads shall be waiting at this + // stage, so all push won't block as the queue is empty. + // But for robustness, let's wait. + m_work3r_queue_push_blocking (g->queue_g, w, true); + } + // Wait for thread terminanison + for(unsigned int i = 0; i < g->numWorker_g; i++) { + m_thread_join(g->worker[i].id); + } + // Clear memory + M_MEMORY_FREE(g->worker); + m_mutex_clear(g->lock); + m_cond_clear(g->a_thread_ends); + m_work3r_queue_clear(g->queue_g); +} + +/* Start a new collaboration between workers of pool 'g' + by defining the synchronization point 'block' */ +M_INLINE void +m_worker_start(m_worker_sync_t block, m_worker_t g) +{ + atomic_init (&block->num_spawn, 0); + atomic_init (&block->num_terminated_spawn, 0); + block->worker = g; +} + +/* Spawn the given work order to workers if possible, + or do it ourself if no worker is available. + The synchronization point is defined a 'block' + The work order if composed of the function 'func' and its 'data' +*/ +M_INLINE void +m_worker_spawn(m_worker_sync_t block, void (*func)(void *data), void *data) +{ + const m_work3r_order_ct w = { block, data, func M_WORK3R_EXTRA_ORDER }; + if (M_UNLIKELY (!m_work3r_queue_full_p(block->worker->queue_g)) + && m_work3r_queue_push (block->worker->queue_g, w) == true) { + M_WORK3R_DEBUG ("Sending data to thread: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn); + atomic_fetch_add (&block->num_spawn, 1); + return; + } + M_WORK3R_DEBUG ("Running data ourself: %p\n", data); + /* No worker available. Call the function ourself */ + (*func) (data); +} + +#if M_USE_WORKER_CLANG_BLOCK +/* Spawn or not the given work order to workers, + or do it ourself if no worker is available */ +M_INLINE void +m_work3r_spawn_block(m_worker_sync_t block, void (^func)(void *data), void *data) +{ + const m_work3r_order_ct w = { block, data, NULL, func }; + if (M_UNLIKELY (!m_work3r_queue_full_p(block->worker->queue_g)) + && m_work3r_queue_push (block->worker->queue_g, w) == true) { + M_WORK3R_DEBUG ("Sending data to thread as block: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn); + atomic_fetch_add (&block->num_spawn, 1); + return; + } + M_WORK3R_DEBUG ("Running data ourself as block: %p\n", data); + /* No worker available. Call the function ourself */ + func (data); +} +#endif + +#if M_USE_WORKER_CPP_FUNCTION +/* Spawn or not the given work order to workers, + or do it ourself if no worker is available */ +M_INLINE void +m_work3r_spawn_function(m_worker_sync_t block, std::function func, void *data) +{ + const m_work3r_order_ct w = { block, data, NULL, func }; + if (M_UNLIKELY (!m_work3r_queue_full_p(block->worker->queue_g)) + && m_work3r_queue_push (block->worker->queue_g, w) == true) { + M_WORK3R_DEBUG ("Sending data to thread as block: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn); + atomic_fetch_add (&block->num_spawn, 1); + return; + } + M_WORK3R_DEBUG ("Running data ourself as block: %p\n", data); + /* No worker available. Call the function ourself */ + func (data); +} +#endif + +/* Test if all work orders of the given synchronization point are finished */ +M_INLINE bool +m_worker_sync_p(m_worker_sync_t block) +{ + /* If the number of spawns is greated than the number + of terminated spawns, some spawns are still working. + So wait for terminaison */ + return (atomic_load(&block->num_spawn) == atomic_load (&block->num_terminated_spawn)); +} + +/* Wait for all work orders of the given synchronization point to be finished */ +M_INLINE void +m_worker_sync(m_worker_sync_t block) +{ + M_WORK3R_DEBUG ("Waiting for thread terminasion.\n"); + // Fast case: all workers have finished + if (m_worker_sync_p(block)) return; + // Slow case: perform a locked wait to put this thread to waiting state + m_mutex_lock(block->worker->lock); + while (!m_worker_sync_p(block)) { + m_cond_wait(block->worker->a_thread_ends, block->worker->lock); + } + m_mutex_unlock(block->worker->lock); +} + +/* Flush any work order in the queue ourself if some remains.*/ +M_INLINE void +m_worker_flush(m_worker_t g) +{ + m_work3r_order_ct w; + while (m_work3r_queue_pop_blocking (&w, g->queue_g, false) == true) { + m_work3r_exec(&w); + m_work3r_queue_pop_release(g->queue_g); + } +} + + +/* Return the number of workers */ +M_INLINE size_t +m_worker_count(m_worker_t g) +{ + return g->numWorker_g + 1; +} + +/* Spawn the 'core' block computation into another thread if + a worker thread is available. Compute it in the current thread otherwise. + 'block' shall be the initialised synchronised block for all threads. + 'input' is the list of input variables of the 'core' block within "( )" + 'output' is the list of output variables of the 'core' block within "( )" + Output variables are only available after a synchronisation block. + TODO: Support oplist for input & outputs parameters +*/ +#if M_USE_WORKER_CLANG_BLOCK +#define M_WORKER_SPAWN(_block, _input, _core, _output) \ + M_WORK3R_DEF_DATA(_input, _output) \ + M_WORK3R_DEF_SUBBLOCK(_input, _output, _core) \ + m_work3r_spawn_block ((_block), M_WORK3R_SPAWN_SUBFUNC_NAME, &M_WORK3R_SPAWN_DATA_NAME) +#elif M_USE_WORKER_CPP_FUNCTION +// TODO: Explicit pass all arguments by reference. +#define M_WORKER_SPAWN(_block, _input, _core, _output) \ + m_work3r_spawn_function ((_block), [&](void *param) {(void)param ; _core } , NULL) +#else +#define M_WORKER_SPAWN(_block, _input, _core, _output) \ + M_WORK3R_DEF_DATA(_input, _output) \ + M_WORK3R_DEF_SUBFUNC(_input, _output, _core) \ + m_worker_spawn ((_block), M_WORK3R_SPAWN_SUBFUNC_NAME, &M_WORK3R_SPAWN_DATA_NAME) +#endif + +#define M_WORK3R_SPAWN_STRUCT_NAME M_C(m_work3r_data_s_, __LINE__) +#define M_WORK3R_SPAWN_DATA_NAME M_C(m_work3r_data_, __LINE__) +#define M_WORK3R_SPAWN_SUBFUNC_NAME M_C(m_work3r_subfunc_, __LINE__) +#define M_WORK3R_DEF_DATA(_input, _output) \ + struct M_WORK3R_SPAWN_STRUCT_NAME { \ + M_WORK3R_DEF_DATA_INPUT _input \ + M_IF_EMPTY _output ( , M_WORK3R_DEF_DATA_OUTPUT _output) \ + } M_WORK3R_SPAWN_DATA_NAME = { \ + M_WORK3R_INIT_DATA_INPUT _input \ + M_IF_EMPTY _output (, M_WORK3R_INIT_DATA_OUTPUT _output) \ + }; +#define M_WORK3R_DEF_SINGLE_INPUT(var) __typeof__(var) var; +#define M_WORK3R_DEF_DATA_INPUT(...) \ + M_MAP(M_WORK3R_DEF_SINGLE_INPUT, __VA_ARGS__) +#define M_WORK3R_DEF_SINGLE_OUTPUT(var) \ + __typeof__(var) *M_C(var, _ptr); +#define M_WORK3R_DEF_DATA_OUTPUT(...) \ + M_MAP(M_WORK3R_DEF_SINGLE_OUTPUT, __VA_ARGS__) +#define M_WORK3R_INIT_SINGLE_INPUT(var) \ + .var = var, +#define M_WORK3R_INIT_DATA_INPUT(...) \ + M_MAP(M_WORK3R_INIT_SINGLE_INPUT, __VA_ARGS__) +#define M_WORK3R_INIT_SINGLE_OUTPUT(var) \ + .M_C(var, _ptr) = &var, +#define M_WORK3R_INIT_DATA_OUTPUT(...) \ + M_MAP(M_WORK3R_INIT_SINGLE_OUTPUT, __VA_ARGS__) +#define M_WORK3R_DEF_SUBFUNC(_input, _output, _core) \ + __extension__ auto void M_WORK3R_SPAWN_SUBFUNC_NAME(void *) ; \ + __extension__ void M_WORK3R_SPAWN_SUBFUNC_NAME(void *_data) \ + { \ + struct M_WORK3R_SPAWN_STRUCT_NAME *_s_data = _data ; \ + M_WORK3R_INIT_LOCAL_INPUT _input \ + M_IF_EMPTY _output ( , M_WORK3R_INIT_LOCAL_OUTPUT _output) \ + do { _core } while (0); \ + M_IF_EMPTY _output ( , M_WORK3R_PROPAGATE_LOCAL_OUTPUT _output) \ + }; +#define M_WORK3R_DEF_SUBBLOCK(_input, _output, _core) \ + void (^M_WORK3R_SPAWN_SUBFUNC_NAME) (void *) = ^ void (void * _data) \ + { \ + struct M_WORK3R_SPAWN_STRUCT_NAME *_s_data = _data ; \ + M_WORK3R_INIT_LOCAL_INPUT _input \ + M_IF_EMPTY _output ( , M_WORK3R_INIT_LOCAL_OUTPUT _output) \ + do { _core } while (0); \ + M_IF_EMPTY _output ( , M_WORK3R_PROPAGATE_LOCAL_OUTPUT _output) \ + }; +#define M_WORK3R_INIT_SINGLE_LOCAL_INPUT(var) \ + __typeof__(var) var = _s_data->var; +#define M_WORK3R_INIT_LOCAL_INPUT(...) \ + M_MAP(M_WORK3R_INIT_SINGLE_LOCAL_INPUT, __VA_ARGS__) +#define M_WORK3R_INIT_SINGLE_LOCAL_OUTPUT(var) \ + __typeof__(var) var; +#define M_WORK3R_INIT_LOCAL_OUTPUT(...) \ + M_MAP(M_WORK3R_INIT_SINGLE_LOCAL_OUTPUT, __VA_ARGS__) +#define M_WORK3R_PROPAGATE_SINGLE_OUTPUT(var) \ + *(_s_data->M_C(var, _ptr)) = var; +#define M_WORK3R_PROPAGATE_LOCAL_OUTPUT(...) \ + M_MAP(M_WORK3R_PROPAGATE_SINGLE_OUTPUT, __VA_ARGS__) + +M_END_PROTECTED_CODE + +#else /* M_USE_WORKER */ + +/* Define empty types and empty functions to not use any worker */ + +typedef struct m_worker_block_s { + int x; +} m_worker_sync_t[1]; + +typedef struct m_worker_s { + int x; +} m_worker_t[1]; + +#define m_worker_init(g, numWorker, extraQueue, resetFunc) do { (void) g; } while (0) +#define m_worker_clear(g) do { (void) g; } while (0) +#define m_worker_start(b, w) do { (void) b; } while (0) +#define m_worker_spawn(b, f, d) do { f(d); } while (0) +#define m_worker_sync_p(b) true +#define m_worker_sync(b) do { (void) b; } while (0) +#define m_worker_count(w) 1 +#define m_worker_flush(w) do { (void) w; } while (0) +#define M_WORKER_SPAWN(b, i, c, o) do { c } while (0) + +#endif /* M_USE_WORKER */ + + +#if M_USE_SMALL_NAME +#define worker_t m_worker_t +#define worker_sync_t m_worker_sync_t +#define worker_init m_worker_init +#define worker_clear m_worker_clear +#define worker_start m_worker_start +#define worker_spawn m_worker_spawn +#define worker_sync_p m_worker_sync_p +#define worker_sync m_worker_sync +#define worker_count m_worker_count +#define worker_flush m_worker_flush +#define WORKER_SPAWN M_WORKER_SPAWN +#endif + +#endif diff --git a/components/nanobake/CMakeLists.txt b/components/nanobake/CMakeLists.txt index de3af734..5f9f0a03 100644 --- a/components/nanobake/CMakeLists.txt +++ b/components/nanobake/CMakeLists.txt @@ -1,7 +1,11 @@ idf_component_register( SRC_DIRS "src" + "src/applications" "src/applications/main/system_info" + "src/applications/services/desktop" + "src/applications/services/loader" + "src/applications/services/gui" INCLUDE_DIRS "inc" PRIV_INCLUDE_DIRS "src" - REQUIRES esp_lvgl_port esp_lcd esp_lcd_touch driver + REQUIRES esp_lvgl_port esp_lcd esp_lcd_touch driver mlib cmsis_core furi ) diff --git a/components/nanobake/inc/nanobake.h b/components/nanobake/inc/nanobake.h index 2fafb07b..e524f4db 100644 --- a/components/nanobake/inc/nanobake.h +++ b/components/nanobake/inc/nanobake.h @@ -1,11 +1,19 @@ -#ifndef NANOBAKE_H -#define NANOBAKE_H +#pragma once -#include "nb_platform.h" +#include "nb_hardware.h" #include "nb_app.h" -extern void nanobake_run(nb_platform_config_t _Nonnull * config); +#ifdef __cplusplus +extern "C" { +#endif -//extern nb_app_config_t[32] nanobake_apps(nb_app_config_t app_configs, ...); +extern void nanobake_start(nb_config_t _Nonnull * config); -#endif //NANOBAKE_H +typedef void* FuriThreadId; + +extern FuriThreadId nanobake_get_app_thread_id(size_t index); +extern size_t nanobake_get_app_thread_count(); + +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/inc/nb_app.h b/components/nanobake/inc/nb_app.h index a0864bb3..05ef6970 100644 --- a/components/nanobake/inc/nb_app.h +++ b/components/nanobake/inc/nb_app.h @@ -1,49 +1,38 @@ -#ifndef NANOBAKE_NB_APP_H -#define NANOBAKE_NB_APP_H - -#define NB_APP_ID_LENGTH 32 -#define NB_APP_NAME_LENGTH 32 +#pragma once #include #include #include -// region Forward declarations -struct nb_platform; -typedef struct nb_platform nb_platform_t; -//endregion +#ifdef __cplusplus +extern "C" { +#endif + +#define NB_APP_ID_LENGTH 32 +#define NB_APP_NAME_LENGTH 32 typedef enum nb_app_type nb_app_type_t; enum nb_app_type { SERVICE, SYSTEM, - USER + USER, + STARTUP }; typedef struct nb_app nb_app_t; -typedef void (*nb_app_callback_on_create) (nb_platform_t* platform, lv_obj_t* lv_parent); -typedef void (*nb_app_callback_update) (nb_platform_t* platform, lv_obj_t* lv_parent); -typedef void (*nb_app_callback_on_destroy) (nb_platform_t* platform); +typedef int32_t (*nb_app_entry_point) (void _Nonnull* parameter); struct nb_app { - char id[NB_APP_ID_LENGTH]; - char name[NB_APP_NAME_LENGTH]; - nb_app_type_t type; - nb_app_callback_on_create _Nullable on_create; - nb_app_callback_on_destroy _Nullable on_destroy; - nb_app_callback_update _Nullable on_update; - size_t update_task_stack_size; - uint32_t update_task_priority; + const char id[NB_APP_ID_LENGTH]; + const char name[NB_APP_NAME_LENGTH]; + const nb_app_type_t type; + const nb_app_entry_point _Nullable entry_point; + const size_t stack_size; + const uint32_t priority; }; -typedef struct nb_app_instance nb_app_instance_t; - -struct nb_app_instance { - nb_app_t config; -}; - -esp_err_t nb_app_validate(nb_app_t* _Nonnull app); - -#endif //NANOBAKE_NB_APP_H +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/inc/nb_config.h b/components/nanobake/inc/nb_config.h new file mode 100644 index 00000000..e0b3597a --- /dev/null +++ b/components/nanobake/inc/nb_config.h @@ -0,0 +1,26 @@ +#pragma once + +#include "nb_display.h" +#include "nb_touch.h" +#include "nb_app.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef nb_touch_driver_t (*create_touch_driver)(); +typedef nb_display_driver_t (*create_display_driver)(); + +typedef struct nb_config nb_config_t; +struct nb_config { + // Required driver for display + const create_display_driver _Nonnull display_driver; + // Optional driver for touch input + const create_touch_driver _Nullable touch_driver; + // List of user applications + const nb_app_t* apps[]; +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/inc/nb_display.h b/components/nanobake/inc/nb_display.h index ca09ac37..01faa63c 100644 --- a/components/nanobake/inc/nb_display.h +++ b/components/nanobake/inc/nb_display.h @@ -1,8 +1,11 @@ -#ifndef NANOBAKE_NB_DISPLAY_H -#define NANOBAKE_NB_DISPLAY_H +#pragma once #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct nb_display nb_display_t; struct nb_display { @@ -25,6 +28,8 @@ struct nb_display_driver { * @param[in] driver * @return allocated display object */ -nb_display_t _Nonnull* nb_display_create(nb_display_driver_t _Nonnull* driver); +nb_display_t _Nonnull* nb_display_alloc(nb_display_driver_t _Nonnull* driver); -#endif // NANOBAKE_NB_DISPLAY_H +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/inc/nb_hardware.h b/components/nanobake/inc/nb_hardware.h new file mode 100644 index 00000000..8fe6e7aa --- /dev/null +++ b/components/nanobake/inc/nb_hardware.h @@ -0,0 +1,24 @@ +#pragma once + +#include "nb_config.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct nb_hardware nb_hardware_t; +struct nb_hardware { + nb_display_t* _Nonnull display; + nb_touch_t* _Nullable touch; +}; + +/** + * @param[in] config + * @return a newly allocated platform instance (caller takes ownership) + */ +nb_hardware_t _Nonnull* nb_hardware_alloc(nb_config_t _Nonnull* config); + +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/inc/nb_platform.h b/components/nanobake/inc/nb_platform.h deleted file mode 100644 index 0bf2045c..00000000 --- a/components/nanobake/inc/nb_platform.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef NANOBAKE_NB_PLATFORM_H -#define NANOBAKE_NB_PLATFORM_H - -#include "nb_display.h" -#include "nb_touch.h" -#include "nb_app.h" -#include -#include - -typedef nb_touch_driver_t (*create_touch_driver)(); -typedef nb_display_driver_t (*create_display_driver)(); - -typedef struct nb_platform_config nb_platform_config_t; -struct nb_platform_config { - // Required driver for display - create_display_driver _Nonnull display_driver; - // Optional driver for touch input - create_touch_driver _Nullable touch_driver; - // List of user applications - nb_app_t* apps[]; -}; - -typedef struct nb_lvgl nb_lvgl_t; -struct nb_lvgl { - lv_disp_t* _Nonnull disp; - lv_indev_t* _Nullable touch_indev; -}; - -typedef struct nb_platform nb_platform_t; -struct nb_platform { - nb_display_t* _Nonnull display; - nb_touch_t* _Nullable touch; - nb_lvgl_t* _Nonnull lvgl; -}; - -/** - * @param[in] config - * @return a newly allocated platform instance (caller takes ownership) - */ -nb_platform_t _Nonnull* nb_platform_create(nb_platform_config_t _Nonnull* config); - -#endif // NANOBAKE_NB_PLATFORM_H \ No newline at end of file diff --git a/components/nanobake/inc/nb_touch.h b/components/nanobake/inc/nb_touch.h index 57f5f197..78043024 100644 --- a/components/nanobake/inc/nb_touch.h +++ b/components/nanobake/inc/nb_touch.h @@ -1,9 +1,12 @@ -#ifndef NANOBAKE_NB_TOUCH_H -#define NANOBAKE_NB_TOUCH_H +#pragma once #include "esp_lcd_touch.h" #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct nb_touch_driver nb_touch_driver_t; struct nb_touch_driver { @@ -22,6 +25,8 @@ struct nb_touch { * @param[in] driver * @return a newly allocated instance */ -nb_touch_t _Nonnull* nb_touch_create(nb_touch_driver_t _Nonnull* driver); +nb_touch_t _Nonnull* nb_touch_alloc(nb_touch_driver_t _Nonnull* driver); -#endif // NANOBAKE_NB_TOUCH_H +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/src/applications/main/system_info/system_info.c b/components/nanobake/src/applications/main/system_info/system_info.c index 0bd79e72..727efde9 100644 --- a/components/nanobake/src/applications/main/system_info/system_info.c +++ b/components/nanobake/src/applications/main/system_info/system_info.c @@ -1,33 +1,40 @@ #include "system_info.h" +#include "nanobake.h" +#include +#include -#include -#include +static int32_t system_info_entry_point(void* param) { + UNUSED(param); -static void prv_on_create(nb_platform_t _Nonnull* platform, lv_obj_t _Nonnull* lv_parent) { - lvgl_port_lock(0); + // Wait for all apps to start + vTaskDelay(1000 / portTICK_PERIOD_MS); - lv_obj_t* cpu_label = lv_label_create(lv_parent); - lv_label_set_recolor(cpu_label, true); - lv_obj_set_width(cpu_label, (lv_coord_t)platform->display->horizontal_resolution); - lv_obj_set_style_text_align(cpu_label, LV_TEXT_ALIGN_LEFT, 0); - lv_label_set_text(cpu_label, "CPU usage: ?"); - lv_obj_align(cpu_label, LV_ALIGN_TOP_LEFT, 0, 0); + size_t system_service_count = nanobake_get_app_thread_count(); + printf("Running apps:\n"); + for (int i = 0; i < system_service_count; ++i) { + FuriThreadId thread_id = nanobake_get_app_thread_id(i); + const char* appid = furi_thread_get_appid(thread_id); + const char* name = furi_thread_get_name(thread_id); + bool is_suspended = furi_thread_is_suspended(thread_id); + const char* status = is_suspended ? "suspended" : "active"; + bool is_service = furi_thread_mark_is_service(thread_id); + const char* type = is_service ? "service" : "app"; + printf(" - [%s, %s] %s (%s)\n", type, status, name, appid); + } - lv_obj_t* mem_free_label = lv_label_create(lv_parent); - lv_label_set_recolor(mem_free_label, true); - lv_obj_set_width(mem_free_label, (lv_coord_t)platform->display->horizontal_resolution); - lv_obj_set_style_text_align(mem_free_label, LV_TEXT_ALIGN_LEFT, 0); - lv_label_set_text(mem_free_label, "Memory: ?"); - lv_obj_align(mem_free_label, LV_ALIGN_TOP_LEFT, 0, 15); + printf("Heap memory available: %d / %d\n", + heap_caps_get_free_size(MALLOC_CAP_DEFAULT), + heap_caps_get_total_size(MALLOC_CAP_DEFAULT) + ); - lvgl_port_unlock(); + return 0; } nb_app_t system_info_app = { .id = "systeminfo", .name = "System Info", .type = SYSTEM, - .on_create = &prv_on_create, - .on_update = NULL, - .on_destroy = NULL + .entry_point = &system_info_entry_point, + .stack_size = 2048, + .priority = 10 }; diff --git a/components/nanobake/src/applications/main/system_info/system_info.h b/components/nanobake/src/applications/main/system_info/system_info.h index 1aaad037..88b0cb04 100644 --- a/components/nanobake/src/applications/main/system_info/system_info.h +++ b/components/nanobake/src/applications/main/system_info/system_info.h @@ -1,8 +1,13 @@ -#ifndef NANOBAKE_SYSTEM_INFO_H -#define NANOBAKE_SYSTEM_INFO_H +#pragma once #include "nb_app.h" +#ifdef __cplusplus +extern "C" { +#endif + extern nb_app_t system_info_app; -#endif // NANOBAKE_SYSTEM_INFO_H \ No newline at end of file +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/src/applications/nb_applications.c b/components/nanobake/src/applications/nb_applications.c new file mode 100644 index 00000000..300b2714 --- /dev/null +++ b/components/nanobake/src/applications/nb_applications.c @@ -0,0 +1,28 @@ +#include "nb_applications.h" + +// System services +extern const nb_app_t desktop_app; +extern const nb_app_t gui_app; +extern const nb_app_t loader_app; + +// System apps +extern const nb_app_t system_info_app; + +const nb_app_t* const FLIPPER_SERVICES[] = { + &desktop_app, + &gui_app, + &loader_app +}; + +const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(nb_app_t*); + +const nb_app_t* const FLIPPER_SYSTEM_APPS[] = { + &system_info_app +}; + +const size_t FLIPPER_SYSTEM_APPS_COUNT = sizeof(FLIPPER_SYSTEM_APPS) / sizeof(nb_app_t*); + +const FlipperInternalOnStartHook FLIPPER_ON_SYSTEM_START[] = { +}; + +const size_t FLIPPER_ON_SYSTEM_START_COUNT = sizeof(FLIPPER_ON_SYSTEM_START) / sizeof(FlipperInternalOnStartHook); diff --git a/components/nanobake/src/applications/nb_applications.h b/components/nanobake/src/applications/nb_applications.h new file mode 100644 index 00000000..4c807043 --- /dev/null +++ b/components/nanobake/src/applications/nb_applications.h @@ -0,0 +1,22 @@ +#pragma once + +#include "nb_app.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*FlipperInternalOnStartHook)(void); + +extern const nb_app_t* const FLIPPER_SERVICES[]; +extern const size_t FLIPPER_SERVICES_COUNT; + +extern const nb_app_t* const FLIPPER_SYSTEM_APPS[]; +extern const size_t FLIPPER_SYSTEM_APPS_COUNT; + +extern const FlipperInternalOnStartHook FLIPPER_ON_SYSTEM_START[]; +extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; + +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/src/applications/services/desktop/desktop.c b/components/nanobake/src/applications/services/desktop/desktop.c new file mode 100644 index 00000000..1525c210 --- /dev/null +++ b/components/nanobake/src/applications/services/desktop/desktop.c @@ -0,0 +1,35 @@ +#include "desktop.h" +#include "nb_hardware.h" +#include +#include "core_defines.h" +#include + +//nb_desktop_t* shared_desktop = NULL; + +static int32_t prv_desktop_main(void* param) { + UNUSED(param); + printf("desktop app init\n"); +// nb_desktop_t* desktop = desktop_alloc(); +// shared_desktop = desktop; + +// lvgl_port_lock(0); +// +// lv_obj_t* label = lv_label_create(lv_parent); +// lv_label_set_recolor(label, true); +// lv_obj_set_width(label, (lv_coord_t)platform->display->horizontal_resolution); +// lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, 0); +// lv_label_set_text(label, "Desktop app"); +// lv_obj_align(label, LV_ALIGN_TOP_LEFT, 0, 0); +// +// lvgl_port_unlock(); + return 0; +} + +const nb_app_t desktop_app = { + .id = "desktop", + .name = "Desktop", + .type = SERVICE, + .entry_point = &prv_desktop_main, + .stack_size = 2048, + .priority = 10 +}; diff --git a/components/nanobake/src/applications/services/desktop/desktop.h b/components/nanobake/src/applications/services/desktop/desktop.h new file mode 100644 index 00000000..09ec5b0d --- /dev/null +++ b/components/nanobake/src/applications/services/desktop/desktop.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nb_app.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const nb_app_t desktop_app; + +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/src/applications/services/gui/gui.c b/components/nanobake/src/applications/services/gui/gui.c new file mode 100644 index 00000000..ed514ba5 --- /dev/null +++ b/components/nanobake/src/applications/services/gui/gui.c @@ -0,0 +1,18 @@ +#include "gui.h" +#include "core_defines.h" +#include "check.h" + +static int32_t prv_gui_main(void* param) { + UNUSED(param); + printf("gui app init\n"); + return 0; +} + +const nb_app_t gui_app = { + .id = "gui", + .name = "GUI", + .type = STARTUP, + .entry_point = &prv_gui_main, + .stack_size = 2048, + .priority = 10 +}; diff --git a/components/nanobake/src/applications/services/gui/gui.h b/components/nanobake/src/applications/services/gui/gui.h new file mode 100644 index 00000000..07090ef4 --- /dev/null +++ b/components/nanobake/src/applications/services/gui/gui.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nb_app.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const nb_app_t gui_app; + +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/src/applications/services/loader/loader.c b/components/nanobake/src/applications/services/loader/loader.c new file mode 100644 index 00000000..a5b5fa4e --- /dev/null +++ b/components/nanobake/src/applications/services/loader/loader.c @@ -0,0 +1,17 @@ +#include "loader.h" +#include "core_defines.h" + +static int32_t prv_loader_main(void* param) { + UNUSED(param); + printf("loader app init\n"); + return 0; +} + +const nb_app_t loader_app = { + .id = "loader", + .name = "Loader", + .type = STARTUP, + .entry_point = &prv_loader_main, + .stack_size = 2048, + .priority = 10 +}; diff --git a/components/nanobake/src/applications/services/loader/loader.h b/components/nanobake/src/applications/services/loader/loader.h new file mode 100644 index 00000000..512ba0a2 --- /dev/null +++ b/components/nanobake/src/applications/services/loader/loader.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nb_app.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const nb_app_t loader_app; + +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/src/nanobake.c b/components/nanobake/src/nanobake.c index 849e3ee9..aa218c4e 100644 --- a/components/nanobake/src/nanobake.c +++ b/components/nanobake/src/nanobake.c @@ -1,15 +1,104 @@ #include "nanobake.h" -#include "applications/main/system_info/system_info.h" +#include "nb_hardware.h" +#include "nb_lvgl.h" +#include "applications/nb_applications.h" +#include +#include +// Furi +#include +#include +#include +#include -void nb_app_start(nb_platform_t _Nonnull* platform, nb_app_t _Nonnull* config) { - lv_obj_t* scr = lv_scr_act(); - ESP_ERROR_CHECK(nb_app_validate(config)); - config->on_create(platform, scr); +static const char* TAG = "nanobake"; + +M_LIST_DEF(thread_ids, FuriThreadId); + +static void prv_furi_init() { + // TODO: can we remove the suspend-resume logic? + if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { + vTaskSuspendAll(); + } + + furi_record_init(); + + xTaskResumeAll(); } -extern void nanobake_run(nb_platform_config_t _Nonnull* config) { - nb_platform_t _Nonnull* platform = nb_platform_create(config); +thread_ids_t prv_thread_ids; - nb_app_start(platform, config->apps[0]); -// nb_app_start(platform, &system_info_app); +FuriThreadId nanobake_get_app_thread_id(size_t index) { + return *thread_ids_get(prv_thread_ids, index); +} + +size_t nanobake_get_app_thread_count() { + return thread_ids_size(prv_thread_ids); +} + +extern void nanobake_start(nb_config_t _Nonnull* config) { + prv_furi_init(); + + nb_hardware_t _Nonnull* hardware = nb_hardware_alloc(config); + nb_lvgl_init(hardware); + + thread_ids_init(prv_thread_ids); + + ESP_LOGI(TAG, "Starting services"); + + for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { + ESP_LOGI(TAG, "Starting system service \"%s\"", FLIPPER_SERVICES[i]->name); + + FuriThread* thread = furi_thread_alloc_ex( + FLIPPER_SERVICES[i]->name, + FLIPPER_SERVICES[i]->stack_size, + FLIPPER_SERVICES[i]->entry_point, + NULL + ); + furi_thread_mark_as_service(thread); + furi_thread_set_appid(thread, FLIPPER_SERVICES[i]->id); + furi_thread_start(thread); + + FuriThreadId thread_id = furi_thread_get_id(thread); + thread_ids_push_back(prv_thread_ids, thread_id); + } + + ESP_LOGI(TAG, "Starting system apps"); + + for(size_t i = 0; i < FLIPPER_SYSTEM_APPS_COUNT; i++) { + ESP_LOGI(TAG, "Starting system app \"%s\"", FLIPPER_SYSTEM_APPS[i]->name); + + FuriThread* thread = furi_thread_alloc_ex( + FLIPPER_SYSTEM_APPS[i]->name, + FLIPPER_SYSTEM_APPS[i]->stack_size, + FLIPPER_SYSTEM_APPS[i]->entry_point, + NULL + ); + furi_thread_mark_as_service(thread); + furi_thread_set_appid(thread, FLIPPER_SYSTEM_APPS[i]->id); + furi_thread_start(thread); + + FuriThreadId thread_id = furi_thread_get_id(thread); + thread_ids_push_back(prv_thread_ids, thread_id); + } + +// ESP_LOGI(TAG, "Starting external apps"); +// +// size_t external_apps_count = sizeof(*config->apps); +// for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { +// ESP_LOGI(TAG, "Starting external app \"%s\"", FLIPPER_[i]->name); +// +// FuriThread* thread = furi_thread_alloc_ex( +// FLIPPER_SERVICES[i]->name, +// FLIPPER_SERVICES[i]->stack_size, +// FLIPPER_SERVICES[i]->entry_point, +// NULL +// ); +// furi_thread_set_appid(thread, FLIPPER_SERVICES[i]->id); +// furi_thread_start(thread); +// +// FuriThreadId thread_id = furi_thread_get_id(thread); +// thread_ids_push_back(prv_thread_ids, thread_id); +// } + + ESP_LOGI(TAG, "Startup complete"); } diff --git a/components/nanobake/src/nb_app.c b/components/nanobake/src/nb_app.c deleted file mode 100644 index 27b81ec5..00000000 --- a/components/nanobake/src/nb_app.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "nb_app.h" -#include -#include - -static const char* TAG = "nb_app"; - -esp_err_t nb_app_validate(nb_app_t* _Nonnull app) { - ESP_RETURN_ON_FALSE( - strlen(app->id) < NB_APP_ID_LENGTH, - ESP_FAIL, - TAG, - "app id cannot be larger than %d characters", - NB_APP_ID_LENGTH - 1 - ); - - ESP_RETURN_ON_FALSE( - strlen(app->name) < NB_APP_NAME_LENGTH, - ESP_FAIL, - TAG, - "app name cannot be larger than %d characters", - NB_APP_NAME_LENGTH - 1 - ); - - ESP_RETURN_ON_FALSE( - (app->on_update == NULL) == (app->update_task_priority == 0 && app->update_task_priority == 0), - ESP_FAIL, - TAG, - "app update is inconsistently configured" - ); - - return ESP_OK; -} diff --git a/components/nanobake/src/nb_assert.h b/components/nanobake/src/nb_assert.h deleted file mode 100644 index 807fc201..00000000 --- a/components/nanobake/src/nb_assert.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef NANOBAKE_NB_ASSERT_H -#define NANOBAKE_NB_ASSERT_H - -#include -#include - -#define NB_ASSERT(x, message) do { \ - if (!(x)) { \ - ESP_LOGE("assert", message); \ - _esp_error_check_failed( \ - x, \ - __FILE__, \ - __LINE__, \ - __ASSERT_FUNC, \ - #x \ - ); \ - } \ - } while(0) - -#endif //NANOBAKE_NB_ASSERT_H diff --git a/components/nanobake/src/nb_display.c b/components/nanobake/src/nb_display.c index 02329c91..4e8d9d8d 100644 --- a/components/nanobake/src/nb_display.c +++ b/components/nanobake/src/nb_display.c @@ -1,8 +1,8 @@ #include "nb_display.h" -#include "nb_assert.h" +#include -nb_display_t _Nonnull* nb_display_create(nb_display_driver_t _Nonnull* driver) { +nb_display_t _Nonnull* nb_display_alloc(nb_display_driver_t _Nonnull* driver) { nb_display_t _Nonnull* display = malloc(sizeof(nb_display_t)); - NB_ASSERT(driver->create_display(display), "failed to create display"); + furi_check(driver->create_display(display), "failed to create display"); return display; } diff --git a/components/nanobake/src/nb_hardware.c b/components/nanobake/src/nb_hardware.c new file mode 100644 index 00000000..12c1decf --- /dev/null +++ b/components/nanobake/src/nb_hardware.c @@ -0,0 +1,32 @@ +#include "nb_hardware.h" +#include "nb_display.h" +#include "nb_touch.h" + +#include +#include +#include + +#include + +static const char* TAG = "nb_hardware"; + +nb_hardware_t _Nonnull* nb_hardware_alloc(nb_config_t _Nonnull* config) { + nb_hardware_t* platform = malloc(sizeof(nb_hardware_t)); + + furi_check(config->display_driver != NULL, "no display driver configured"); + nb_display_driver_t display_driver = config->display_driver(); + ESP_LOGI(TAG, "display with driver %s", display_driver.name); + platform->display = nb_display_alloc(&display_driver); + + if (config->touch_driver != NULL) { + nb_touch_driver_t touch_driver = config->touch_driver(); + ESP_LOGI(TAG, "touch with driver %s", touch_driver.name); + platform->touch = nb_touch_alloc(&touch_driver); + } else { + ESP_LOGI(TAG, "no touch configured"); + platform->touch = NULL; + } + + + return platform; +} diff --git a/components/nanobake/src/nb_internal.c b/components/nanobake/src/nb_internal.c deleted file mode 100644 index 9bfb45ab..00000000 --- a/components/nanobake/src/nb_internal.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "nb_internal.h" - -const char* nbi_tag = "nanobake"; diff --git a/components/nanobake/src/nb_internal.h b/components/nanobake/src/nb_internal.h deleted file mode 100644 index f1d6578c..00000000 --- a/components/nanobake/src/nb_internal.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef NANOBAKE_NB_INTERNAL_H -#define NANOBAKE_NB_INTERNAL_H - -extern const char* nbi_tag; - -#endif // NANOBAKE_NB_INTERNAL_H \ No newline at end of file diff --git a/components/nanobake/src/nb_lvgl.c b/components/nanobake/src/nb_lvgl.c new file mode 100644 index 00000000..016701aa --- /dev/null +++ b/components/nanobake/src/nb_lvgl.c @@ -0,0 +1,54 @@ +#include "nb_lvgl.h" +#include "nb_hardware.h" +#include +#include + +static const char* TAG = "nb_lvgl"; + +nb_lvgl_t nb_lvgl_init(nb_hardware_t* platform) { + nb_lvgl_t lvgl; + + const lvgl_port_cfg_t lvgl_cfg = { + .task_priority = 4, + .task_stack = 4096, + .task_affinity = -1, // core pinning + .task_max_sleep_ms = 500, + .timer_period_ms = 5 + }; + furi_check(lvgl_port_init(&lvgl_cfg) == ESP_OK, "lvgl port init failed"); + nb_display_t _Nonnull* display = platform->display; + // Add display + ESP_LOGD(TAG, "lvgl add display"); + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = display->io_handle, + .panel_handle = display->display_handle, + .buffer_size = display->horizontal_resolution * display->draw_buffer_height * (display->bits_per_pixel / 8), + .double_buffer = 0, + .hres = display->horizontal_resolution, + .vres = display->vertical_resolution, + .monochrome = false, + /* Rotation values must be same as defined in driver */ + // TODO: expose data from driver + .rotation = { + .swap_xy = false, + .mirror_x = true, + .mirror_y = false, + }, + .flags = { + .buff_dma = true, + } + }; + lvgl.disp = lvgl_port_add_disp(&disp_cfg); + + // Add touch + if (platform->touch != NULL) { + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = lvgl.disp, + .handle = platform->touch->touch_handle, + }; + lvgl.touch_indev = lvgl_port_add_touch(&touch_cfg); + furi_check(lvgl.touch_indev != NULL, "failed to add touch to lvgl"); + } + + return lvgl; +} diff --git a/components/nanobake/src/nb_lvgl.h b/components/nanobake/src/nb_lvgl.h new file mode 100644 index 00000000..0e0130cc --- /dev/null +++ b/components/nanobake/src/nb_lvgl.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct nb_lvgl nb_lvgl_t; +struct nb_lvgl { + lv_disp_t* _Nonnull disp; + lv_indev_t* _Nullable touch_indev; +}; + +typedef struct nb_hardware nb_hardware_t; + +extern nb_lvgl_t nb_lvgl_init(nb_hardware_t* platform); + +#ifdef __cplusplus +} +#endif diff --git a/components/nanobake/src/nb_platform.c b/components/nanobake/src/nb_platform.c deleted file mode 100644 index 8983b717..00000000 --- a/components/nanobake/src/nb_platform.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "nb_platform.h" -#include "nb_display.h" -#include "nb_touch.h" - -#include -#include -#include - -#include - -static const char* TAG = "nb_platform"; - -static esp_err_t prv_lvgl_init( - nb_platform_t* platform -) { - const lvgl_port_cfg_t lvgl_cfg = { - .task_priority = 4, - .task_stack = 4096, - .task_affinity = -1, // core pinning - .task_max_sleep_ms = 500, - .timer_period_ms = 5 - }; - ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "lvgl port init failed"); - nb_display_t _Nonnull* display = platform->display; - // Add display - ESP_LOGD(TAG, "lvgl add display"); - const lvgl_port_display_cfg_t disp_cfg = { - .io_handle = display->io_handle, - .panel_handle = display->display_handle, - .buffer_size = display->horizontal_resolution * display->draw_buffer_height * (display->bits_per_pixel / 8), - .double_buffer = 1, - .hres = display->horizontal_resolution, - .vres = display->vertical_resolution, - .monochrome = false, - /* Rotation values must be same as defined in driver */ - // TODO: expose data from driver - .rotation = { - .swap_xy = false, - .mirror_x = true, - .mirror_y = false, - }, - .flags = { - .buff_dma = true, - } - }; - platform->lvgl->disp = lvgl_port_add_disp(&disp_cfg); - - // Add touch - if (platform->touch != NULL) { - const lvgl_port_touch_cfg_t touch_cfg = { - .disp = platform->lvgl->disp, - .handle = platform->touch->touch_handle, - }; - platform->lvgl->touch_indev = lvgl_port_add_touch(&touch_cfg); - ESP_RETURN_ON_FALSE(platform->lvgl->touch_indev != NULL, ESP_FAIL, TAG, "failed to add touch to lvgl"); - } - - return ESP_OK; -} - -nb_platform_t _Nonnull* nb_platform_create(nb_platform_config_t _Nonnull* config) { - nb_platform_t* platform = malloc(sizeof(nb_platform_t)); - - NB_ASSERT(config->display_driver != NULL, "no display driver configured"); - nb_display_driver_t display_driver = config->display_driver(); - ESP_LOGI(TAG, "display with driver %s", display_driver.name); - platform->display = nb_display_create(&display_driver); - - if (config->touch_driver != NULL) { - nb_touch_driver_t touch_driver = config->touch_driver(); - ESP_LOGI(TAG, "touch with driver %s", touch_driver.name); - platform->touch = nb_touch_create(&touch_driver); - } else { - ESP_LOGI(TAG, "no touch configured"); - platform->touch = NULL; - } - - NB_ASSERT(prv_lvgl_init(platform) == ESP_OK, "failed to init lvgl"); - - return platform; -} diff --git a/components/nanobake/src/nb_touch.c b/components/nanobake/src/nb_touch.c index 6d345cfb..faee86eb 100644 --- a/components/nanobake/src/nb_touch.c +++ b/components/nanobake/src/nb_touch.c @@ -1,13 +1,12 @@ #include "nb_touch.h" -#include "nb_assert.h" -#include +#include "check.h" -nb_touch_t _Nonnull* nb_touch_create(nb_touch_driver_t _Nonnull* driver) { +nb_touch_t _Nonnull* nb_touch_alloc(nb_touch_driver_t _Nonnull* driver) { nb_touch_t _Nonnull* touch = malloc(sizeof(nb_touch_t)); bool success = driver->create_touch( &(touch->io_handle), &(touch->touch_handle) ); - NB_ASSERT(success, "touch driver failed"); + furi_check(success, "touch driver failed"); return touch; } diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index a9d59e36..39f3bf83 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,3 +1,4 @@ + idf_component_register( SRC_DIRS "src" "src/hello_world" diff --git a/main/src/hello_world/hello_world.c b/main/src/hello_world/hello_world.c index aa16b344..ef796fdc 100644 --- a/main/src/hello_world/hello_world.c +++ b/main/src/hello_world/hello_world.c @@ -1,8 +1,9 @@ #include "hello_world.h" +#include "core_defines.h" #include "esp_lvgl_port.h" #include -#include "nb_platform.h" +#include "nb_hardware.h" static const char* TAG = "app_helloworld"; @@ -10,30 +11,32 @@ static void prv_on_button_click(lv_event_t _Nonnull* event) { ESP_LOGI(TAG, "button clicked"); } -static void prv_on_create(nb_platform_t _Nonnull* platform, lv_obj_t _Nonnull* lv_parent) { - lvgl_port_lock(0); - - lv_obj_t* label = lv_label_create(lv_parent); - lv_label_set_recolor(label, true); - lv_obj_set_width(label, (lv_coord_t)platform->display->horizontal_resolution); - lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_text(label, "Hello, world!"); - lv_obj_align(label, LV_ALIGN_CENTER, 0, -30); - - lv_obj_t* btn = lv_btn_create(lv_parent); - label = lv_label_create(btn); - lv_label_set_text_static(label, "Button"); - lv_obj_align(btn, LV_ALIGN_CENTER, 0, 30); - lv_obj_add_event_cb(btn, prv_on_button_click, LV_EVENT_CLICKED, NULL); - - lvgl_port_unlock(); +static int32_t prv_on_create(void* param) { + UNUSED(param); +// lvgl_port_lock(0); +// +// lv_obj_t* label = lv_label_create(lv_parent); +// lv_label_set_recolor(label, true); +// lv_obj_set_width(label, (lv_coord_t)platform->display->horizontal_resolution); +// lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); +// lv_label_set_text(label, "Hello, world!"); +// lv_obj_align(label, LV_ALIGN_CENTER, 0, -30); +// +// lv_obj_t* btn = lv_btn_create(lv_parent); +// label = lv_label_create(btn); +// lv_label_set_text_static(label, "Button"); +// lv_obj_align(btn, LV_ALIGN_CENTER, 0, 30); +// lv_obj_add_event_cb(btn, prv_on_button_click, LV_EVENT_CLICKED, NULL); +// +// lvgl_port_unlock(); + return 0; } -nb_app_t hello_world_app = { +const nb_app_t hello_world_app = { .id = "helloworld", .name = "Hello World", .type = USER, - .on_create = &prv_on_create, - .on_update = NULL, - .on_destroy = NULL + .entry_point = &prv_on_create, + .stack_size = 2048, + .priority = 10 }; diff --git a/main/src/hello_world/hello_world.h b/main/src/hello_world/hello_world.h index 2107223e..c9539e5d 100644 --- a/main/src/hello_world/hello_world.h +++ b/main/src/hello_world/hello_world.h @@ -1,8 +1,5 @@ -#ifndef NANOBAKE_HELLO_WORLD_H -#define NANOBAKE_HELLO_WORLD_H +#pragma once #include "nb_app.h" -extern nb_app_t hello_world_app; - -#endif //NANOBAKE_HELLO_WORLD_H \ No newline at end of file +extern const nb_app_t hello_world_app; diff --git a/main/src/main.c b/main/src/main.c index 502af391..aeb9e77f 100644 --- a/main/src/main.c +++ b/main/src/main.c @@ -8,7 +8,7 @@ #include "hello_world/hello_world.h" void app_main(void) { - static nb_platform_config_t platform_config = { + static nb_config_t platform_config = { .display_driver = &board_2432s024_create_display_driver, .touch_driver = &board_2432s024_create_touch_driver, .apps = { @@ -16,5 +16,5 @@ void app_main(void) { } }; - nanobake_run(&platform_config); + nanobake_start(&platform_config); }