diff --git a/.github/workflows/esp.yml b/.github/workflows/esp.yml index 9ac94b5a..caf6c96c 100644 --- a/.github/workflows/esp.yml +++ b/.github/workflows/esp.yml @@ -13,7 +13,7 @@ jobs: - name: build uses: espressif/esp-idf-ci-action@main with: - esp_idf_version: v5.2.3 + esp_idf_version: v5.3.1 target: esp32 path: './' build-lilygo-t-deck: @@ -28,7 +28,7 @@ jobs: - name: build uses: espressif/esp-idf-ci-action@main with: - esp_idf_version: v5.2.3 + esp_idf_version: v5.3.1 target: esp32s3 path: './' build-waveshare-s3-touch: @@ -43,7 +43,7 @@ jobs: - name: build uses: espressif/esp-idf-ci-action@main with: - esp_idf_version: v5.2.3 + esp_idf_version: v5.3.1 target: esp32s3 path: './' build-m5stack-core2: @@ -58,7 +58,7 @@ jobs: - name: build uses: espressif/esp-idf-ci-action@main with: - esp_idf_version: v5.2.3 + esp_idf_version: v5.3.1 target: esp32 path: './' build-m5stack-cores3: @@ -73,6 +73,6 @@ jobs: - name: build uses: espressif/esp-idf-ci-action@main with: - esp_idf_version: v5.2.3 + esp_idf_version: v5.3.1 target: esp32s3 path: './' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cbd46261..70384ddd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,4 +17,4 @@ jobs: - name: Build Tests run: cmake --build build --target build-tests - name: Run Tests - run: build/tests/tactility-core/tactility-core-tests --exit + run: build/Tests/TactilityCore/TactilityCoreTests --exit diff --git a/.gitmodules b/.gitmodules index 77a1ef1d..fa5006af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,15 @@ -[submodule "libs/M5GFX"] - path = libs/M5GFX +[submodule "Libraries/M5GFX"] + path = Libraries/M5GFX url = https://github.com/m5stack/M5GFX.git -[submodule "libs/M5Unified"] - path = libs/M5Unified +[submodule "Libraries/M5Unified"] + path = Libraries/M5Unified url = https://github.com/m5stack/M5Unified.git -[submodule "libs/FreeRTOS-Kernel"] - path = libs/FreeRTOS-Kernel +[submodule "Libraries/FreeRTOS-Kernel"] + path = Libraries/FreeRTOS-Kernel url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git -[submodule "libs/mbedtls"] - path = libs/mbedtls +[submodule "Libraries/mbedtls"] + path = Libraries/mbedtls url = https://github.com/Mbed-TLS/mbedtls.git -[submodule "libs/lvgl"] - path = libs/lvgl +[submodule "Libraries/lvgl"] + path = Libraries/lvgl url = https://github.com/lvgl/lvgl.git diff --git a/app-esp/CMakeLists.txt b/AppEsp32/CMakeLists.txt similarity index 60% rename from app-esp/CMakeLists.txt rename to AppEsp32/CMakeLists.txt index a0c2c55c..eb024cfb 100644 --- a/app-esp/CMakeLists.txt +++ b/AppEsp32/CMakeLists.txt @@ -1,25 +1,25 @@ cmake_minimum_required(VERSION 3.16) -set(BOARD_COMPONENTS tactility) +set(BOARD_COMPONENTS Tactility) if("${IDF_TARGET}" STREQUAL "esp32") list(APPEND BOARD_COMPONENTS - yellow_board - m5stack_core2 + YellowBoard + M5stackCore2 ) endif() # T-Deck is an S3 platform if("${IDF_TARGET}" STREQUAL "esp32s3") list(APPEND BOARD_COMPONENTS - lilygo_tdeck - m5stack_cores3 - waveshare_s3_touch + LilygoTdeck + M5stackCoreS3 + WaveshareS3Touch ) endif() idf_component_register( - SRC_DIRS "src" - "src/hello_world" + SRC_DIRS "Source" + "Source/HelloWorld" REQUIRES ${BOARD_COMPONENTS} ) diff --git a/app-esp/Kconfig b/AppEsp32/Kconfig similarity index 100% rename from app-esp/Kconfig rename to AppEsp32/Kconfig diff --git a/app-esp/src/board_config.h b/AppEsp32/Source/Boards.h similarity index 96% rename from app-esp/src/board_config.h rename to AppEsp32/Source/Boards.h index ee393ef9..b5fb1386 100644 --- a/app-esp/src/board_config.h +++ b/AppEsp32/Source/Boards.h @@ -10,7 +10,7 @@ #include "yellow_board.h" #define TT_BOARD_HARDWARE &yellow_board_24inch_cap #elif defined(CONFIG_TT_BOARD_M5STACK_CORE2) -#include "m5stack_core2.h" +#include "M5stackCore2.h" #define TT_BOARD_HARDWARE &m5stack_core2 #elif defined(CONFIG_TT_BOARD_M5STACK_CORES3) #include "m5stack_cores3.h" diff --git a/AppEsp32/Source/HelloWorld/HelloWorld.cpp b/AppEsp32/Source/HelloWorld/HelloWorld.cpp new file mode 100644 index 00000000..3231c51d --- /dev/null +++ b/AppEsp32/Source/HelloWorld/HelloWorld.cpp @@ -0,0 +1,18 @@ +#include "lvgl.h" +#include "Ui/Toolbar.h" + +static void app_show(tt::App app, lv_obj_t* parent) { + lv_obj_t* toolbar = tt::lvgl::toolbar_create_for_app(parent, app); + lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); + + lv_obj_t* label = lv_label_create(parent); + lv_label_set_text(label, "Hello, world!"); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); +} + +extern const tt::AppManifest hello_world_app = { + .id = "HelloWorld", + .name = "Hello World", + .type = tt::AppTypeUser, + .on_show = &app_show, +}; diff --git a/AppEsp32/Source/Main.cpp b/AppEsp32/Source/Main.cpp new file mode 100644 index 00000000..ea99c8ce --- /dev/null +++ b/AppEsp32/Source/Main.cpp @@ -0,0 +1,33 @@ +#include "Boards.h" + +// Apps +#include "Tactility.h" + +namespace tt::service::wifi { + extern void wifi_main(void*); +} + +extern const tt::AppManifest hello_world_app; + +extern "C" { + +void app_main() { + static const tt::Configuration config = { + /** + * Auto-select a board based on the ./sdkconfig.board.* file + * that you copied to ./sdkconfig before you opened this project. + */ + .hardware = TT_BOARD_HARDWARE, + .apps = { + &hello_world_app, + }, + .services = {}, + .auto_start_app_id = nullptr + }; + + tt::init(&config); + + tt::service::wifi::wifi_main(nullptr); +} + +} // extern diff --git a/app-esp/idf_component.yml b/AppEsp32/idf_component.yml similarity index 91% rename from app-esp/idf_component.yml rename to AppEsp32/idf_component.yml index 7a174bbc..cff373c8 100644 --- a/app-esp/idf_component.yml +++ b/AppEsp32/idf_component.yml @@ -3,4 +3,4 @@ dependencies: espressif/esp_lcd_touch_cst816s: "1.0.3" espressif/esp_lcd_touch_gt911: "1.1.1" espressif/esp_lcd_touch: "1.1.2" - idf: '~5.2' + idf: '5.3.1' diff --git a/AppSim/CMakeLists.txt b/AppSim/CMakeLists.txt new file mode 100644 index 00000000..87bd7a56 --- /dev/null +++ b/AppSim/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.16) + +file(GLOB_RECURSE SOURCES "Source/*.c*") +add_executable(AppSim ${SOURCES}) +target_link_libraries(AppSim + PRIVATE Tactility + PRIVATE TactilityCore + PRIVATE TactilityHeadless + PRIVATE lvgl +) + +find_package(SDL2 REQUIRED CONFIG) +target_link_libraries(AppSim PRIVATE ${SDL2_LIBRARIES}) +include_directories(${SDL2_INCLUDE_DIRS}) + +add_definitions(-D_Nullable=) +add_definitions(-D_Nonnull=) diff --git a/app-sim/src/FreeRTOSConfig.h b/AppSim/Source/FreeRTOSConfig.h similarity index 100% rename from app-sim/src/FreeRTOSConfig.h rename to AppSim/Source/FreeRTOSConfig.h diff --git a/AppSim/Source/HelloWorld/hello_world.cpp b/AppSim/Source/HelloWorld/hello_world.cpp new file mode 100644 index 00000000..f623cb13 --- /dev/null +++ b/AppSim/Source/HelloWorld/hello_world.cpp @@ -0,0 +1,17 @@ +#include "Ui/Toolbar.h" + +static void app_show(tt::App app, lv_obj_t* parent) { + lv_obj_t* toolbar = tt::lvgl::toolbar_create_for_app(parent, app); + lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); + + lv_obj_t* label = lv_label_create(parent); + lv_label_set_text(label, "Hello, world!"); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); +} + +extern const tt::AppManifest hello_world_app = { + .id = "HelloWorld", + .name = "Hello World", + .type = tt::AppTypeUser, + .on_show = &app_show +}; diff --git a/app-sim/src/freertos.c b/AppSim/Source/freertos.cpp similarity index 88% rename from app-sim/src/freertos.c rename to AppSim/Source/freertos.cpp index a4400a41..68af132b 100644 --- a/app-sim/src/freertos.c +++ b/AppSim/Source/freertos.cpp @@ -1,5 +1,5 @@ -#include "tactility.h" -#include "thread.h" +#include "Tactility.h" +#include "Thread.h" #include "FreeRTOS.h" #include "task.h" @@ -12,7 +12,7 @@ static void main_task(TT_UNUSED void* parameter) { TT_LOG_I(TAG, "starting app_main()"); app_main(); TT_LOG_I(TAG, "returned from app_main()"); - vTaskDelete(NULL); + vTaskDelete(nullptr); } int main() { @@ -20,9 +20,9 @@ int main() { main_task, "main", 8192, - NULL, - ThreadPriorityNormal, - NULL + nullptr, + tt::ThreadPriorityNormal, + nullptr ); tt_assert(task_result == pdTRUE); diff --git a/app-sim/src/hardware_config.c b/AppSim/Source/hardware_config.cpp similarity index 60% rename from app-sim/src/hardware_config.c rename to AppSim/Source/hardware_config.cpp index 4912a433..8f9f4558 100644 --- a/app-sim/src/hardware_config.c +++ b/AppSim/Source/hardware_config.cpp @@ -1,11 +1,10 @@ -#include "hardware_config.h" +#include "Hal/Configuration.h" #include "lvgl_task.h" #include "src/lv_init.h" -#include #define TAG "hardware" -extern const Power power; +extern const tt::hal::Power power; static bool lvgl_init() { lv_init(); @@ -16,7 +15,7 @@ static bool lvgl_init() { TT_UNUSED static void lvgl_deinit() { lvgl_task_interrupt(); while (lvgl_task_is_running()) { - tt_delay_ms(10); + tt::delay_ms(10); } #if LV_ENABLE_GC || !LV_MEM_CUSTOM @@ -24,12 +23,12 @@ TT_UNUSED static void lvgl_deinit() { #endif } -const HardwareConfig sim_hardware = { - .bootstrap = NULL, +extern const tt::hal::Configuration sim_hardware = { + .bootstrap = nullptr, .init_graphics = &lvgl_init, .display = { - .set_backlight_duty = NULL, + .set_backlight_duty = nullptr, }, - .power = &power, - .sdcard = NULL + .sdcard = nullptr, + .power = &power }; diff --git a/app-sim/src/lv_conf.h b/AppSim/Source/lv_conf.h similarity index 100% rename from app-sim/src/lv_conf.h rename to AppSim/Source/lv_conf.h diff --git a/app-sim/src/lvgl_hal.c b/AppSim/Source/lvgl_hal.cpp similarity index 82% rename from app-sim/src/lvgl_hal.c rename to AppSim/Source/lvgl_hal.cpp index 7fb89218..98d5c7f1 100644 --- a/app-sim/src/lvgl_hal.c +++ b/AppSim/Source/lvgl_hal.cpp @@ -1,5 +1,5 @@ #include "lvgl.h" -#include "ui/lvgl_keypad.h" +#include "Ui/LvglKeypad.h" lv_display_t* lvgl_hal_init() { static lv_display_t* display = NULL; @@ -9,7 +9,7 @@ lv_display_t* lvgl_hal_init() { display = lv_sdl_window_create(320, 240); mouse = lv_sdl_mouse_create(); keyboard = lv_sdl_keyboard_create(); - tt_lvgl_keypad_set_indev(keyboard); + tt::lvgl::keypad_set_indev(keyboard); return display; } diff --git a/AppSim/Source/lvgl_hal.h b/AppSim/Source/lvgl_hal.h new file mode 100644 index 00000000..6eef2636 --- /dev/null +++ b/AppSim/Source/lvgl_hal.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +lv_display_t* lvgl_hal_init(); diff --git a/app-sim/src/lvgl_task.c b/AppSim/Source/lvgl_task.cpp similarity index 91% rename from app-sim/src/lvgl_task.c rename to AppSim/Source/lvgl_task.cpp index 68f9ebd2..04b8e38d 100644 --- a/app-sim/src/lvgl_task.c +++ b/AppSim/Source/lvgl_task.cpp @@ -1,11 +1,11 @@ #include "lvgl_task.h" #include "lvgl.h" +#include "Log.h" #include "lvgl_hal.h" -#include "tactility_core.h" -#include "thread.h" -#include "ui/lvgl_sync.h" -#include +#include "TactilityCore.h" +#include "Thread.h" +#include "Ui/LvglSync.h" #include "FreeRTOS.h" #include "semphr.h" @@ -55,7 +55,7 @@ static void lvgl_unlock() { } void lvgl_task_interrupt() { - tt_check(lvgl_lock(TtWaitForever)); + tt_check(lvgl_lock(tt::TtWaitForever)); task_set_running(false); // interrupt task with boolean as flag lvgl_unlock(); } @@ -73,7 +73,7 @@ void lvgl_task_start() { task_mutex = xSemaphoreCreateRecursiveMutex(); } - tt_lvgl_sync_set(&lvgl_lock, &lvgl_unlock); + tt::lvgl::sync_set(&lvgl_lock, &lvgl_unlock); // Create the main app loop, like ESP-IDF BaseType_t task_result = xTaskCreate( @@ -81,7 +81,7 @@ void lvgl_task_start() { "lvgl", 8192, NULL, - ThreadPriorityHigh, // Should be higher than main app task + tt::ThreadPriorityHigh, // Should be higher than main app task NULL ); diff --git a/app-sim/src/lvgl_task.h b/AppSim/Source/lvgl_task.h similarity index 51% rename from app-sim/src/lvgl_task.h rename to AppSim/Source/lvgl_task.h index 60fcb625..efd22c50 100644 --- a/app-sim/src/lvgl_task.h +++ b/AppSim/Source/lvgl_task.h @@ -1,15 +1,5 @@ #pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - void lvgl_task_start(); bool lvgl_task_is_running(); void lvgl_task_interrupt(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/AppSim/Source/main.cpp b/AppSim/Source/main.cpp new file mode 100644 index 00000000..ccb649bf --- /dev/null +++ b/AppSim/Source/main.cpp @@ -0,0 +1,17 @@ +#include "Tactility.h" + +extern const tt::hal::Configuration sim_hardware; +extern const tt::AppManifest hello_world_app; + +void app_main() { + static const tt::Configuration config = { + .hardware = &sim_hardware, + .apps = { + &hello_world_app + }, + .services = {}, + .auto_start_app_id = nullptr + }; + + tt::init(&config); +} diff --git a/app-sim/src/power.c b/AppSim/Source/power.cpp similarity index 85% rename from app-sim/src/power.c rename to AppSim/Source/power.cpp index eaa46e81..6983eb2d 100644 --- a/app-sim/src/power.c +++ b/AppSim/Source/power.cpp @@ -1,8 +1,4 @@ -#include "power.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "Hal/Power.h" static bool is_charging_enabled = false; @@ -26,14 +22,10 @@ static int32_t power_get_current() { return is_charging_enabled ? 100 : -50; } -const Power power = { +extern const tt::hal::Power power = { .is_charging = &power_is_charging, .is_charging_enabled = &power_is_charging_enabled, .set_charging_enabled = &power_set_charging_enabled, .get_charge_level = &power_get_charge_level, .get_current = &power_get_current }; - -#ifdef __cplusplus -} -#endif diff --git a/boards/lilygo_tdeck/CMakeLists.txt b/Boards/LilygoTdeck/CMakeLists.txt similarity index 56% rename from boards/lilygo_tdeck/CMakeLists.txt rename to Boards/LilygoTdeck/CMakeLists.txt index c86e52e5..6269fd9b 100644 --- a/boards/lilygo_tdeck/CMakeLists.txt +++ b/Boards/LilygoTdeck/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "." INCLUDE_DIRS "." - REQUIRES tactility esp_lvgl_port esp_lcd esp_lcd_touch_gt911 driver vfs fatfs + REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_touch_gt911 driver vfs fatfs ) diff --git a/boards/lilygo_tdeck/bootstrap.c b/Boards/LilygoTdeck/bootstrap.cpp similarity index 95% rename from boards/lilygo_tdeck/bootstrap.c rename to Boards/LilygoTdeck/bootstrap.cpp index ef9bc1c8..8d08e4d9 100644 --- a/boards/lilygo_tdeck/bootstrap.c +++ b/Boards/LilygoTdeck/bootstrap.cpp @@ -2,7 +2,7 @@ #include "display_i.h" #include "driver/spi_common.h" #include "keyboard.h" -#include "tactility_core.h" +#include "TactilityCore.h" #define TAG "tdeck_bootstrap" @@ -30,10 +30,12 @@ static bool init_i2c() { const i2c_config_t i2c_conf = { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_18, - .sda_pullup_en = GPIO_PULLUP_DISABLE, .scl_io_num = GPIO_NUM_8, + .sda_pullup_en = GPIO_PULLUP_DISABLE, .scl_pullup_en = GPIO_PULLUP_DISABLE, - .master.clk_speed = 400000 + .master = { + .clk_speed = 400000 + } }; return i2c_param_config(TDECK_I2C_BUS_HANDLE, &i2c_conf) == ESP_OK && i2c_driver_install(TDECK_I2C_BUS_HANDLE, i2c_conf.mode, 0, 0, 0) == ESP_OK; @@ -41,9 +43,9 @@ static bool init_i2c() { static bool init_spi() { spi_bus_config_t bus_config = { - .sclk_io_num = TDECK_SPI_PIN_SCLK, .mosi_io_num = TDECK_SPI_PIN_MOSI, .miso_io_num = TDECK_SPI_PIN_MISO, + .sclk_io_num = TDECK_SPI_PIN_SCLK, .quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported .quadhd_io_num = -1, // Quad SPI LCD driver is not yet supported .max_transfer_sz = TDECK_SPI_TRANSFER_SIZE_LIMIT, @@ -72,7 +74,7 @@ bool tdeck_bootstrap() { * It doesn't work, because it never recovers from the error. */ TT_LOG_I(TAG, "Waiting after power-on"); - tt_delay_ms(TDECK_POWERON_DELAY); + tt::delay_ms(TDECK_POWERON_DELAY); TT_LOG_I(TAG, "Init I2C"); if (!init_i2c()) { diff --git a/boards/lilygo_tdeck/config.h b/Boards/LilygoTdeck/config.h similarity index 98% rename from boards/lilygo_tdeck/config.h rename to Boards/LilygoTdeck/config.h index be08431a..2b3d5292 100644 --- a/boards/lilygo_tdeck/config.h +++ b/Boards/LilygoTdeck/config.h @@ -4,7 +4,7 @@ #include "driver/gpio.h" // Main bus, used by GT911 touch hardware and keyboard -#define TDECK_I2C_BUS_HANDLE (0) +#define TDECK_I2C_BUS_HANDLE I2C_NUM_0 // SPI #define TDECK_SPI_HOST SPI2_HOST diff --git a/boards/lilygo_tdeck/display.c b/Boards/LilygoTdeck/display.cpp similarity index 91% rename from boards/lilygo_tdeck/display.c rename to Boards/LilygoTdeck/display.cpp index 4541334e..2c9e4eaa 100644 --- a/boards/lilygo_tdeck/display.c +++ b/Boards/LilygoTdeck/display.cpp @@ -5,15 +5,15 @@ #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_vendor.h" #include "esp_lvgl_port.h" -#include "log.h" +#include "Log.h" #define TAG "tdeck_display" bool tdeck_backlight_init() { ledc_timer_config_t ledc_timer = { .speed_mode = TDECK_LCD_BACKLIGHT_LEDC_MODE, - .timer_num = TDECK_LCD_BACKLIGHT_LEDC_TIMER, .duty_resolution = TDECK_LCD_BACKLIGHT_LEDC_DUTY_RES, + .timer_num = TDECK_LCD_BACKLIGHT_LEDC_TIMER, .freq_hz = TDECK_LCD_BACKLIGHT_LEDC_FREQUENCY, .clk_cfg = LEDC_AUTO_CLK }; @@ -28,11 +28,11 @@ bool tdeck_backlight_init() { void tdeck_backlight_set(uint8_t duty) { ledc_channel_config_t ledc_channel = { + .gpio_num = TDECK_LCD_PIN_BACKLIGHT, .speed_mode = TDECK_LCD_BACKLIGHT_LEDC_MODE, .channel = TDECK_LCD_BACKLIGHT_LEDC_CHANNEL, - .timer_sel = TDECK_LCD_BACKLIGHT_LEDC_TIMER, .intr_type = LEDC_INTR_DISABLE, - .gpio_num = TDECK_LCD_PIN_BACKLIGHT, + .timer_sel = TDECK_LCD_BACKLIGHT_LEDC_TIMER, .duty = duty, .hpoint = 0 }; @@ -51,8 +51,8 @@ lv_display_t* tdeck_display_init() { .spi_mode = 0, .pclk_hz = TDECK_LCD_SPI_FREQUENCY, .trans_queue_depth = 10, - .on_color_trans_done = NULL, - .user_ctx = NULL, + .on_color_trans_done = nullptr, + .user_ctx = nullptr, .lcd_cmd_bits = 8, .lcd_param_bits = 8, .flags = { @@ -68,7 +68,7 @@ lv_display_t* tdeck_display_init() { esp_lcd_panel_io_handle_t io_handle; if (esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TDECK_LCD_SPI_HOST, &panel_io_config, &io_handle) != ESP_OK) { TT_LOG_E(TAG, "failed to create panel IO"); - return false; + return nullptr; } const esp_lcd_panel_dev_config_t panel_config = { @@ -79,43 +79,43 @@ lv_display_t* tdeck_display_init() { .flags = { .reset_active_high = 0 }, - .vendor_config = NULL + .vendor_config = nullptr }; esp_lcd_panel_handle_t panel_handle; if (esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle) != ESP_OK) { TT_LOG_E(TAG, "failed to create panel"); - return false; + return nullptr; } if (esp_lcd_panel_reset(panel_handle) != ESP_OK) { TT_LOG_E(TAG, "failed to reset panel"); - return false; + return nullptr; } if (esp_lcd_panel_init(panel_handle) != ESP_OK) { TT_LOG_E(TAG, "failed to init panel"); - return false; + return nullptr; } if (esp_lcd_panel_invert_color(panel_handle, true) != ESP_OK) { TT_LOG_E(TAG, "failed to init panel"); - return false; + return nullptr; } if (esp_lcd_panel_swap_xy(panel_handle, true) != ESP_OK) { TT_LOG_E(TAG, "failed to init panel"); - return false; + return nullptr; } if (esp_lcd_panel_mirror(panel_handle, true, false) != ESP_OK) { TT_LOG_E(TAG, "failed to init panel"); - return false; + return nullptr; } if (esp_lcd_panel_disp_on_off(panel_handle, true) != ESP_OK) { TT_LOG_E(TAG, "failed to turn display on"); - return false; + return nullptr; } const lvgl_port_display_cfg_t disp_cfg = { @@ -139,7 +139,5 @@ lv_display_t* tdeck_display_init() { } }; - lv_display_t* display = lvgl_port_add_disp(&disp_cfg); - - return display; + return lvgl_port_add_disp(&disp_cfg); } diff --git a/Boards/LilygoTdeck/display_i.h b/Boards/LilygoTdeck/display_i.h new file mode 100644 index 00000000..17bcf4ec --- /dev/null +++ b/Boards/LilygoTdeck/display_i.h @@ -0,0 +1,9 @@ +#pragma once + +#include "lvgl.h" + +lv_display_t* tdeck_display_init(); + +bool tdeck_backlight_init(); + +void tdeck_backlight_set(uint8_t duty); \ No newline at end of file diff --git a/boards/lilygo_tdeck/keyboard.c b/Boards/LilygoTdeck/keyboard.cpp similarity index 89% rename from boards/lilygo_tdeck/keyboard.c rename to Boards/LilygoTdeck/keyboard.cpp index 5142e963..1c15494e 100644 --- a/boards/lilygo_tdeck/keyboard.c +++ b/Boards/LilygoTdeck/keyboard.cpp @@ -1,8 +1,8 @@ #include "keyboard.h" #include "config.h" #include "lvgl.h" -#include "tactility_core.h" -#include "ui/lvgl_keypad.h" +#include "TactilityCore.h" +#include "Ui/LvglKeypad.h" #include #define TAG "tdeck_keyboard" @@ -28,7 +28,7 @@ void keyboard_wait_for_response() { do { awake = keyboard_i2c_read(&read_buffer) == ESP_OK; if (!awake) { - tt_delay_ms(100); + tt::delay_ms(100); } } while (!awake); TT_LOG_I(TAG, "Keyboard responded"); @@ -67,20 +67,20 @@ static void keyboard_read_callback(TT_UNUSED lv_indev_t* indev, lv_indev_data_t* } Keyboard keyboard_alloc(_Nullable lv_disp_t* display) { - KeyboardData* data = malloc(sizeof(KeyboardData)); + auto* data = static_cast(malloc(sizeof(KeyboardData))); data->device = lv_indev_create(); lv_indev_set_type(data->device, LV_INDEV_TYPE_KEYPAD); lv_indev_set_read_cb(data->device, &keyboard_read_callback); lv_indev_set_display(data->device, display); - tt_lvgl_keypad_set_indev(data->device); + tt::lvgl::keypad_set_indev(data->device); return data; } void keyboard_free(Keyboard keyboard) { - KeyboardData* data = (KeyboardData*)keyboard; + auto* data = static_cast(keyboard); lv_indev_delete(data->device); free(data); } diff --git a/boards/lilygo_tdeck/keyboard.h b/Boards/LilygoTdeck/keyboard.h similarity index 58% rename from boards/lilygo_tdeck/keyboard.h rename to Boards/LilygoTdeck/keyboard.h index fda164f3..26a3784a 100644 --- a/boards/lilygo_tdeck/keyboard.h +++ b/Boards/LilygoTdeck/keyboard.h @@ -2,17 +2,9 @@ #include "lvgl.h" -#ifdef __cplusplus -extern "C" { -#endif - void keyboard_wait_for_response(); typedef void* Keyboard; Keyboard keyboard_alloc(_Nullable lv_disp_t* display); -void keyboard_free(Keyboard keyboard); - -#ifdef __cplusplus -} -#endif \ No newline at end of file +void keyboard_free(Keyboard keyboard); \ No newline at end of file diff --git a/boards/lilygo_tdeck/lilygo_tdeck.c b/Boards/LilygoTdeck/lilygo_tdeck.cpp similarity index 69% rename from boards/lilygo_tdeck/lilygo_tdeck.c rename to Boards/LilygoTdeck/lilygo_tdeck.cpp index 59b5108f..e94652f9 100644 --- a/boards/lilygo_tdeck/lilygo_tdeck.c +++ b/Boards/LilygoTdeck/lilygo_tdeck.cpp @@ -1,18 +1,17 @@ #include "lilygo_tdeck.h" #include "display_i.h" -#include bool tdeck_bootstrap(); bool tdeck_init_lvgl(); -extern const SdCard tdeck_sdcard; +extern const tt::hal::sdcard::SdCard tdeck_sdcard; -const HardwareConfig lilygo_tdeck = { +extern const tt::hal::Configuration lilygo_tdeck = { .bootstrap = &tdeck_bootstrap, + .init_graphics = &tdeck_init_lvgl, .display = { .set_backlight_duty = &tdeck_backlight_set }, - .init_graphics = &tdeck_init_lvgl, .sdcard = &tdeck_sdcard, - .power = NULL + .power = nullptr }; diff --git a/Boards/LilygoTdeck/lilygo_tdeck.h b/Boards/LilygoTdeck/lilygo_tdeck.h new file mode 100644 index 00000000..16c12a03 --- /dev/null +++ b/Boards/LilygoTdeck/lilygo_tdeck.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Hal/Configuration.h" + +extern const tt::hal::Configuration lilygo_tdeck; diff --git a/boards/lilygo_tdeck/lvgl.c b/Boards/LilygoTdeck/lvgl.cpp similarity index 83% rename from boards/lilygo_tdeck/lvgl.c rename to Boards/LilygoTdeck/lvgl.cpp index ec98e421..2e168da0 100644 --- a/boards/lilygo_tdeck/lvgl.c +++ b/Boards/LilygoTdeck/lvgl.cpp @@ -2,21 +2,21 @@ #include "display_i.h" #include "esp_lvgl_port.h" #include "keyboard.h" -#include "log.h" -#include "ui/lvgl_sync.h" -#include "thread.h" +#include "Log.h" +#include "Ui/LvglSync.h" +#include "Thread.h" #define TAG "tdeck_lvgl" bool tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle); bool tdeck_init_lvgl() { - static lv_disp_t* display = NULL; + static lv_disp_t* display = nullptr; static esp_lcd_panel_io_handle_t touch_io_handle; static esp_lcd_touch_handle_t touch_handle; const lvgl_port_cfg_t lvgl_cfg = { - .task_priority = THREAD_PRIORITY_RENDER, + .task_priority = tt::THREAD_PRIORITY_RENDER, .task_stack = TDECK_LVGL_TASK_STACK_DEPTH, .task_affinity = -1, // core pinning .task_max_sleep_ms = 500, @@ -33,7 +33,7 @@ bool tdeck_init_lvgl() { TT_LOG_D(TAG, "Creating display"); display = tdeck_display_init(); - if (display == NULL) { + if (display == nullptr) { TT_LOG_E(TAG, "Creating display failed"); return false; } @@ -53,13 +53,13 @@ bool tdeck_init_lvgl() { TT_LOG_D(TAG, "Adding touch"); lv_indev_t _Nullable* touch_indev = lvgl_port_add_touch(&touch_cfg); - if (touch_indev == NULL) { + if (touch_indev == nullptr) { TT_LOG_E(TAG, "Adding touch failed"); return false; } // Set syncing functions - tt_lvgl_sync_set(&lvgl_port_lock, &lvgl_port_unlock); + tt::lvgl::sync_set(&lvgl_port_lock, &lvgl_port_unlock); keyboard_alloc(display); diff --git a/boards/lilygo_tdeck/sdcard.c b/Boards/LilygoTdeck/sdcard.cpp similarity index 84% rename from boards/lilygo_tdeck/sdcard.c rename to Boards/LilygoTdeck/sdcard.cpp index 54ea05d6..12c7b7ab 100644 --- a/boards/lilygo_tdeck/sdcard.c +++ b/Boards/LilygoTdeck/sdcard.cpp @@ -1,11 +1,11 @@ -#include "sdcard.h" -#include "check.h" -#include "log.h" +#include "Hal/Sdcard.h" +#include "Check.h" +#include "Log.h" #include "config.h" #include "esp_vfs_fat.h" #include "sdmmc_cmd.h" -#include "ui/lvgl_sync.h" +#include "Ui/LvglSync.h" #define TAG "tdeck_sdcard" @@ -88,10 +88,10 @@ static void* _Nullable sdcard_mount(const char* mount_point) { return NULL; } - MountData* data = malloc(sizeof(MountData)); + auto* data = static_cast(malloc(sizeof(MountData))); *data = (MountData) { + .mount_point = mount_point, .card = card, - .mount_point = mount_point }; return data; @@ -102,10 +102,10 @@ static void* sdcard_init_and_mount(const char* mount_point) { TT_LOG_E(TAG, "Failed to set SPI CS pins high. This is a pre-requisite for mounting."); return NULL; } - MountData* data = sdcard_mount(mount_point); - if (data == NULL) { + auto* data = static_cast(sdcard_mount(mount_point)); + if (data == nullptr) { TT_LOG_E(TAG, "Mount failed for %s", mount_point); - return NULL; + return nullptr; } sdmmc_card_print_info(stdout, data->card); @@ -114,10 +114,10 @@ static void* sdcard_init_and_mount(const char* mount_point) { } static void sdcard_unmount(void* context) { - MountData* data = (MountData*)context; + auto* data = static_cast(context); TT_LOG_I(TAG, "Unmounting %s", data->mount_point); - tt_assert(data != NULL); + tt_assert(data != nullptr); if (esp_vfs_fat_sdcard_unmount(data->mount_point, data->card) != ESP_OK) { TT_LOG_E(TAG, "Unmount failed for %s", data->mount_point); } @@ -126,24 +126,24 @@ static void sdcard_unmount(void* context) { } static bool sdcard_is_mounted(void* context) { - MountData* data = (MountData*)context; + auto* data = static_cast(context); /** * The SD card and the screen are on the same SPI bus. * Writing and reading to the bus from 2 devices at the same time causes crashes. * This work-around ensures that this check is only happening when LVGL isn't rendering. */ - if (tt_lvgl_lock(100)) { - bool result = (data != NULL) && (sdmmc_get_status(data->card) == ESP_OK); - tt_lvgl_unlock(); + if (tt::lvgl::lock(100)) { + bool result = (data != nullptr) && (sdmmc_get_status(data->card) == ESP_OK); + tt::lvgl::unlock(); return result; } else { return false; } } -const SdCard tdeck_sdcard = { +extern const tt::hal::sdcard::SdCard tdeck_sdcard = { .mount = &sdcard_init_and_mount, .unmount = &sdcard_unmount, .is_mounted = &sdcard_is_mounted, - .mount_behaviour = SdcardMountBehaviourAtBoot + .mount_behaviour = tt::hal::sdcard::MountBehaviourAtBoot }; diff --git a/boards/lilygo_tdeck/touch.c b/Boards/LilygoTdeck/touch.cpp similarity index 74% rename from boards/lilygo_tdeck/touch.c rename to Boards/LilygoTdeck/touch.cpp index 5106dcd0..8d37565f 100644 --- a/boards/lilygo_tdeck/touch.c +++ b/Boards/LilygoTdeck/touch.cpp @@ -2,13 +2,22 @@ #include "esp_err.h" #include "esp_lcd_panel_io_interface.h" #include "esp_lcd_touch_gt911.h" -#include "log.h" -#include +#include "Log.h" +#include "Kernel.h" #define TAG "tdeck_touch" bool tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) { const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); + + // TODO: Revert on new ESP-IDF version + static_assert(ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 3, 1)); + esp_lcd_new_panel_io_i2c( + (esp_lcd_i2c_bus_handle_t)TDECK_TOUCH_I2C_BUS_HANDLE, + &touch_io_config, + io_handle + ); + /* if ( esp_lcd_new_panel_io_i2c( (esp_lcd_i2c_bus_handle_t)TDECK_TOUCH_I2C_BUS_HANDLE, @@ -19,6 +28,7 @@ bool tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle TT_LOG_E(TAG, "touch io i2c creation failed"); return false; } + */ esp_lcd_touch_config_t config = { .x_max = TDECK_TOUCH_X_MAX, @@ -34,9 +44,9 @@ bool tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle .mirror_x = 1, .mirror_y = 0, }, - .process_coordinates = NULL, - .interrupt_callback = NULL, - .user_data = NULL + .process_coordinates = nullptr, + .interrupt_callback = nullptr, + .user_data = nullptr }; if (esp_lcd_touch_new_i2c_gt911(*io_handle, &config, touch_handle) != ESP_OK) { diff --git a/Boards/M5stackCore2/CMakeLists.txt b/Boards/M5stackCore2/CMakeLists.txt new file mode 100644 index 00000000..7fa44bd8 --- /dev/null +++ b/Boards/M5stackCore2/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "Source" + INCLUDE_DIRS "Source" + REQUIRES Tactility M5stackShared vfs fatfs M5Unified +) diff --git a/boards/m5stack_core2/source/m5stack_core2.c b/Boards/M5stackCore2/Source/M5stackCore2.cpp similarity index 53% rename from boards/m5stack_core2/source/m5stack_core2.c rename to Boards/M5stackCore2/Source/M5stackCore2.cpp index 460fd022..0c057681 100644 --- a/boards/m5stack_core2/source/m5stack_core2.c +++ b/Boards/M5stackCore2/Source/M5stackCore2.cpp @@ -1,14 +1,14 @@ -#include "m5stack_core2.h" +#include "M5stackCore2.h" #include "m5stack_shared.h" -extern const SdCard m5stack_core2_sdcard; +extern const tt::hal::sdcard::SdCard m5stack_core2_sdcard; -const HardwareConfig m5stack_core2 = { +extern const tt::hal::Configuration m5stack_core2 = { .bootstrap = &m5stack_bootstrap, - .display = { - .set_backlight_duty = NULL - }, .init_graphics = &m5stack_lvgl_init, + .display = { + .set_backlight_duty = nullptr + }, .sdcard = &m5stack_core2_sdcard, .power = &m5stack_power }; diff --git a/Boards/M5stackCore2/Source/M5stackCore2.h b/Boards/M5stackCore2/Source/M5stackCore2.h new file mode 100644 index 00000000..12feadce --- /dev/null +++ b/Boards/M5stackCore2/Source/M5stackCore2.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Hal/Configuration.h" + +extern const tt::hal::Configuration m5stack_core2; diff --git a/boards/m5stack_core2/source/m5stack_core2_sdcard.c b/Boards/M5stackCore2/Source/M5stackCore2Sdcard.cpp similarity index 68% rename from boards/m5stack_core2/source/m5stack_core2_sdcard.c rename to Boards/M5stackCore2/Source/M5stackCore2Sdcard.cpp index 5ce1655d..afe165fe 100644 --- a/boards/m5stack_core2/source/m5stack_core2_sdcard.c +++ b/Boards/M5stackCore2/Source/M5stackCore2Sdcard.cpp @@ -1,13 +1,20 @@ -#include "sdcard.h" -#include "check.h" -#include "log.h" -#include "config.h" +#include "Check.h" +#include "Log.h" +#include "Hal/Sdcard.h" #include "esp_vfs_fat.h" #include "sdmmc_cmd.h" #define TAG "m5stack_core2_sdcard" +#define CORE2_SDCARD_SPI_HOST SPI2_HOST +#define CORE2_SDCARD_PIN_CS GPIO_NUM_4 +#define CORE2_SDCARD_SPI_FREQUENCY 800000U +#define CORE2_SDCARD_FORMAT_ON_MOUNT_FAILED false +#define CORE2_SDCARD_MAX_OPEN_FILES 4 +#define CORE2_SDCARD_ALLOC_UNIT_SIZE (16 * 1024) +#define CORE2_SDCARD_STATUS_CHECK_ENABLED false + typedef struct { const char* mount_point; sdmmc_card_t* card; @@ -40,13 +47,13 @@ static void* sdcard_mount(const char* mount_point) { } else { TT_LOG_E(TAG, "Mounting failed (%s)", esp_err_to_name(ret)); } - return NULL; + return nullptr; } - MountData* data = malloc(sizeof(MountData)); + auto* data = static_cast(malloc(sizeof(MountData))); *data = (MountData) { + .mount_point = mount_point, .card = card, - .mount_point = mount_point }; sdmmc_card_print_info(stdout, data->card); @@ -55,10 +62,10 @@ static void* sdcard_mount(const char* mount_point) { } static void sdcard_unmount(void* context) { - MountData* data = (MountData*)context; + auto* data = static_cast(context); TT_LOG_I(TAG, "Unmounting %s", data->mount_point); - tt_assert(data != NULL); + tt_assert(data != nullptr); if (esp_vfs_fat_sdcard_unmount(data->mount_point, data->card) != ESP_OK) { TT_LOG_E(TAG, "Unmount failed for %s", data->mount_point); } @@ -67,13 +74,13 @@ static void sdcard_unmount(void* context) { } static bool sdcard_is_mounted(void* context) { - MountData* data = (MountData*)context; - return (data != NULL) && (sdmmc_get_status(data->card) == ESP_OK); + auto* data = static_cast(context); + return (data != nullptr) && (sdmmc_get_status(data->card) == ESP_OK); } -const SdCard m5stack_core2_sdcard = { +extern const tt::hal::sdcard::SdCard m5stack_core2_sdcard = { .mount = &sdcard_mount, .unmount = &sdcard_unmount, .is_mounted = &sdcard_is_mounted, - .mount_behaviour = SdcardMountBehaviourAnytime + .mount_behaviour = tt::hal::sdcard::MountBehaviourAnytime }; diff --git a/Boards/M5stackCoreS3/CMakeLists.txt b/Boards/M5stackCoreS3/CMakeLists.txt new file mode 100644 index 00000000..6dfb2371 --- /dev/null +++ b/Boards/M5stackCoreS3/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register( + SRC_DIRS "Source" + INCLUDE_DIRS "Source" + PRIV_INCLUDE_DIRS "Private" + REQUIRES Tactility M5stackShared vfs fatfs M5Unified +) diff --git a/boards/m5stack_cores3/private/config.h b/Boards/M5stackCoreS3/Private/config.h similarity index 100% rename from boards/m5stack_cores3/private/config.h rename to Boards/M5stackCoreS3/Private/config.h diff --git a/boards/m5stack_cores3/source/m5stack_cores3.c b/Boards/M5stackCoreS3/Source/m5stack_cores3.cpp similarity index 61% rename from boards/m5stack_cores3/source/m5stack_cores3.c rename to Boards/M5stackCoreS3/Source/m5stack_cores3.cpp index 67e1ce7a..0923f1e2 100644 --- a/boards/m5stack_cores3/source/m5stack_cores3.c +++ b/Boards/M5stackCoreS3/Source/m5stack_cores3.cpp @@ -1,14 +1,14 @@ #include "m5stack_cores3.h" #include "m5stack_shared.h" -extern const SdCard m5stack_cores3_sdcard; +extern const tt::hal::sdcard::SdCard m5stack_cores3_sdcard; -const HardwareConfig m5stack_cores3 = { +const tt::hal::Configuration m5stack_cores3 = { .bootstrap = &m5stack_bootstrap, - .display = { - .set_backlight_duty = NULL - }, .init_graphics = &m5stack_lvgl_init, + .display = { + .set_backlight_duty = nullptr + }, .sdcard = &m5stack_cores3_sdcard, .power = &m5stack_power }; diff --git a/Boards/M5stackCoreS3/Source/m5stack_cores3.h b/Boards/M5stackCoreS3/Source/m5stack_cores3.h new file mode 100644 index 00000000..f396fcc8 --- /dev/null +++ b/Boards/M5stackCoreS3/Source/m5stack_cores3.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Hal/Configuration.h" + +extern const tt::hal::Configuration m5stack_cores3; diff --git a/boards/m5stack_cores3/source/m5stack_cores3_sdcard.c b/Boards/M5stackCoreS3/Source/m5stack_cores3_sdcard.cpp similarity index 77% rename from boards/m5stack_cores3/source/m5stack_cores3_sdcard.c rename to Boards/M5stackCoreS3/Source/m5stack_cores3_sdcard.cpp index ad354608..93957150 100644 --- a/boards/m5stack_cores3/source/m5stack_cores3_sdcard.c +++ b/Boards/M5stackCoreS3/Source/m5stack_cores3_sdcard.cpp @@ -1,6 +1,6 @@ -#include "sdcard.h" -#include "check.h" -#include "log.h" +#include "Hal/Sdcard.h" +#include "Check.h" +#include "Log.h" #include "config.h" #include "esp_vfs_fat.h" @@ -40,13 +40,13 @@ static void* sdcard_mount(const char* mount_point) { } else { TT_LOG_E(TAG, "Mounting failed (%s)", esp_err_to_name(ret)); } - return NULL; + return nullptr; } - MountData* data = malloc(sizeof(MountData)); + auto* data = static_cast(malloc(sizeof(MountData))); *data = (MountData) { - .card = card, - .mount_point = mount_point + .mount_point = mount_point, + .card = card }; sdmmc_card_print_info(stdout, data->card); @@ -55,10 +55,10 @@ static void* sdcard_mount(const char* mount_point) { } static void sdcard_unmount(void* context) { - MountData* data = (MountData*)context; + auto* data = static_cast(context); TT_LOG_I(TAG, "Unmounting %s", data->mount_point); - tt_assert(data != NULL); + tt_assert(data != nullptr); if (esp_vfs_fat_sdcard_unmount(data->mount_point, data->card) != ESP_OK) { TT_LOG_E(TAG, "Unmount failed for %s", data->mount_point); } @@ -67,13 +67,13 @@ static void sdcard_unmount(void* context) { } static bool sdcard_is_mounted(void* context) { - MountData* data = (MountData*)context; - return (data != NULL) && (sdmmc_get_status(data->card) == ESP_OK); + auto* data = static_cast(context); + return (data != nullptr) && (sdmmc_get_status(data->card) == ESP_OK); } -const SdCard m5stack_cores3_sdcard = { +extern const tt::hal::sdcard::SdCard m5stack_cores3_sdcard = { .mount = &sdcard_mount, .unmount = &sdcard_unmount, .is_mounted = &sdcard_is_mounted, - .mount_behaviour = SdcardMountBehaviourAnytime + .mount_behaviour = tt::hal::sdcard::MountBehaviourAnytime }; diff --git a/Boards/M5stackShared/CMakeLists.txt b/Boards/M5stackShared/CMakeLists.txt new file mode 100644 index 00000000..ae41f2a6 --- /dev/null +++ b/Boards/M5stackShared/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "Source" + INCLUDE_DIRS "Source" + REQUIRES Tactility esp_lvgl_port M5Unified +) diff --git a/boards/m5stack_shared/src/m5stack_bootstrap.cpp b/Boards/M5stackShared/Source/m5stack_bootstrap.cpp similarity index 65% rename from boards/m5stack_shared/src/m5stack_bootstrap.cpp rename to Boards/M5stackShared/Source/m5stack_bootstrap.cpp index bf526c25..3341ff20 100644 --- a/boards/m5stack_shared/src/m5stack_bootstrap.cpp +++ b/Boards/M5stackShared/Source/m5stack_bootstrap.cpp @@ -1,9 +1,5 @@ #include "M5Unified.hpp" -#include "log.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "Log.h" #define TAG "m5stack_bootstrap" @@ -12,7 +8,3 @@ bool m5stack_bootstrap() { M5.begin(); return true; } - -#ifdef __cplusplus -} -#endif diff --git a/boards/m5stack_shared/src/m5stack_lvgl.c b/Boards/M5stackShared/Source/m5stack_lvgl.cpp similarity index 66% rename from boards/m5stack_shared/src/m5stack_lvgl.c rename to Boards/M5stackShared/Source/m5stack_lvgl.cpp index 6f02fb31..ccd9553f 100644 --- a/boards/m5stack_shared/src/m5stack_lvgl.c +++ b/Boards/M5stackShared/Source/m5stack_lvgl.cpp @@ -1,18 +1,18 @@ #include "esp_lvgl_port.h" -#include "log.h" -#include "thread.h" -#include "ui/lvgl_sync.h" +#include "Log.h" +#include "Thread.h" +#include "Ui/LvglSync.h" #define TAG "cores3_lvgl" -extern _Nullable lv_disp_t* m5stack_lvgl_display(bool usePsram); -extern _Nullable lv_indev_t* m5stack_lvgl_touch(); +_Nullable lv_disp_t* m5stack_lvgl_display(bool usePsram); +_Nullable lv_indev_t* m5stack_lvgl_touch(); bool m5stack_lvgl_init() { - static lv_display_t* display = NULL; + static lv_display_t* display = nullptr; const lvgl_port_cfg_t lvgl_cfg = { - .task_priority = ThreadPriorityHigh, + .task_priority = tt::ThreadPriorityHigh, .task_stack = 8096, .task_affinity = -1, // core pinning .task_max_sleep_ms = 500, @@ -26,21 +26,21 @@ bool m5stack_lvgl_init() { // Add display display = m5stack_lvgl_display(true); - if (display == NULL) { + if (display == nullptr) { TT_LOG_E(TAG, "failed to add display"); return false; } // Add touch lv_indev_t* touch_indev = m5stack_lvgl_touch(); - if (touch_indev == NULL) { + if (touch_indev == nullptr) { TT_LOG_E(TAG, "failed to add touch"); return false; } lv_indev_set_display(touch_indev, display); // Set syncing functions - tt_lvgl_sync_set(&lvgl_port_lock, &lvgl_port_unlock); + tt::lvgl::sync_set(&lvgl_port_lock, &lvgl_port_unlock); return true; } diff --git a/boards/m5stack_shared/src/m5stack_lvgl_display.cpp b/Boards/M5stackShared/Source/m5stack_lvgl_display.cpp similarity index 94% rename from boards/m5stack_shared/src/m5stack_lvgl_display.cpp rename to Boards/M5stackShared/Source/m5stack_lvgl_display.cpp index 7b0c7764..98da1dc2 100644 --- a/boards/m5stack_shared/src/m5stack_lvgl_display.cpp +++ b/Boards/M5stackShared/Source/m5stack_lvgl_display.cpp @@ -1,13 +1,9 @@ -#include "tactility_core.h" +#include "TactilityCore.h" #include "M5Unified.hpp" #include "esp_err.h" #include "esp_lvgl_port.h" -#ifdef __cplusplus -extern "C" { -#endif - static void flush_callback(lv_display_t* display, const lv_area_t* area, uint8_t* px_map) { M5GFX& gfx = *(M5GFX*)lv_display_get_driver_data(display); @@ -63,7 +59,3 @@ _Nullable lv_disp_t* m5stack_lvgl_display(bool usePsram) { return display; } - -#ifdef __cplusplus -} -#endif diff --git a/boards/m5stack_shared/src/m5stack_lvgl_touch.cpp b/Boards/M5stackShared/Source/m5stack_lvgl_touch.cpp similarity index 88% rename from boards/m5stack_shared/src/m5stack_lvgl_touch.cpp rename to Boards/M5stackShared/Source/m5stack_lvgl_touch.cpp index b50ab18e..0c3696ae 100644 --- a/boards/m5stack_shared/src/m5stack_lvgl_touch.cpp +++ b/Boards/M5stackShared/Source/m5stack_lvgl_touch.cpp @@ -1,13 +1,9 @@ #include "M5Unified.hpp" #include "lvgl.h" -#include "tactility_core.h" +#include "TactilityCore.h" #define TAG "cores3_touch" -#ifdef __cplusplus -extern "C" { -#endif - static void touch_read_callback(TT_UNUSED lv_indev_t* indev, lv_indev_data_t* data) { lgfx::touch_point_t point; // Making it static makes it unreliable bool touched = M5.Lcd.getTouch(&point) > 0; @@ -31,7 +27,3 @@ _Nullable lv_indev_t* m5stack_lvgl_touch() { lv_indev_set_read_cb(indev, touch_read_callback); return indev; } - -#ifdef __cplusplus -} -#endif diff --git a/boards/m5stack_shared/src/m5stack_power.cpp b/Boards/M5stackShared/Source/m5stack_power.cpp similarity index 90% rename from boards/m5stack_shared/src/m5stack_power.cpp rename to Boards/M5stackShared/Source/m5stack_power.cpp index 00a46d2f..54c5b18c 100644 --- a/boards/m5stack_shared/src/m5stack_power.cpp +++ b/Boards/M5stackShared/Source/m5stack_power.cpp @@ -1,10 +1,6 @@ -#include "power.h" +#include "Hal/Power.h" #include "M5Unified.hpp" -#ifdef __cplusplus -extern "C" { -#endif - /** * M5.Power by default doesn't have a check to see if charging is enabled. * However, it's always enabled by default after boot, so we cover that here: @@ -33,14 +29,10 @@ static int32_t power_get_current() { return M5.Power.getBatteryCurrent(); } -Power m5stack_power = { +extern const tt::hal::Power m5stack_power = { .is_charging = &power_is_charging, .is_charging_enabled = &power_is_charging_enabled, .set_charging_enabled = &power_set_charging_enabled, .get_charge_level = &power_get_charge_level, .get_current = &power_get_current }; - -#ifdef __cplusplus -} -#endif diff --git a/Boards/M5stackShared/Source/m5stack_shared.h b/Boards/M5stackShared/Source/m5stack_shared.h new file mode 100644 index 00000000..b964b3e0 --- /dev/null +++ b/Boards/M5stackShared/Source/m5stack_shared.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Hal/Power.h" +#include "Hal/Sdcard.h" + +extern bool m5stack_lvgl_init(); +extern bool m5stack_bootstrap(); + +extern const tt::hal::Power m5stack_power; diff --git a/boards/waveshare_s3_touch/CMakeLists.txt b/Boards/WaveshareS3Touch/CMakeLists.txt similarity index 53% rename from boards/waveshare_s3_touch/CMakeLists.txt rename to Boards/WaveshareS3Touch/CMakeLists.txt index 442e5ebe..ebb9df4a 100644 --- a/boards/waveshare_s3_touch/CMakeLists.txt +++ b/Boards/WaveshareS3Touch/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "." INCLUDE_DIRS "." - REQUIRES tactility lvgl esp_lcd esp_lcd_touch_gt911 + REQUIRES Tactility lvgl esp_lcd esp_lcd_touch_gt911 ) diff --git a/boards/waveshare_s3_touch/bootstrap.c b/Boards/WaveshareS3Touch/bootstrap.cpp similarity index 87% rename from boards/waveshare_s3_touch/bootstrap.c rename to Boards/WaveshareS3Touch/bootstrap.cpp index afae04b6..f0139a9d 100644 --- a/boards/waveshare_s3_touch/bootstrap.c +++ b/Boards/WaveshareS3Touch/bootstrap.cpp @@ -1,5 +1,5 @@ #include "config.h" -#include "tactility_core.h" +#include "TactilityCore.h" #include #define TAG "waveshare_bootstrap" @@ -7,14 +7,16 @@ #define WAVESHARE_I2C_MASTER_TX_BUF_DISABLE 0 #define WAVESHARE_I2C_MASTER_RX_BUF_DISABLE 0 -static esp_err_t i2c_init(void) { +static esp_err_t i2c_init() { const i2c_config_t i2c_conf = { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_8, - .sda_pullup_en = GPIO_PULLUP_DISABLE, .scl_io_num = GPIO_NUM_9, + .sda_pullup_en = GPIO_PULLUP_DISABLE, .scl_pullup_en = GPIO_PULLUP_DISABLE, - .master.clk_speed = 400000 + .master = { + .clk_speed = 400000 + } }; i2c_param_config(WAVESHARE_TOUCH_I2C_PORT, &i2c_conf); diff --git a/Boards/WaveshareS3Touch/bootstrap_i.h b/Boards/WaveshareS3Touch/bootstrap_i.h new file mode 100644 index 00000000..ffb4c9d1 --- /dev/null +++ b/Boards/WaveshareS3Touch/bootstrap_i.h @@ -0,0 +1,3 @@ +#pragma once + +bool ws3t_bootstrap(); diff --git a/boards/waveshare_s3_touch/config.h b/Boards/WaveshareS3Touch/config.h similarity index 97% rename from boards/waveshare_s3_touch/config.h rename to Boards/WaveshareS3Touch/config.h index 59a0bb59..21e21f15 100644 --- a/boards/waveshare_s3_touch/config.h +++ b/Boards/WaveshareS3Touch/config.h @@ -35,4 +35,4 @@ #define WAVESHARE_LVGL_TICK_PERIOD_MS 2 // TODO: Setting it to 5 causes a crash - why? #define LVGL_MAX_SLEEP 500 -#define WAVESHARE_TOUCH_I2C_PORT 0 +#define WAVESHARE_TOUCH_I2C_PORT I2C_NUM_0 diff --git a/boards/waveshare_s3_touch/display.c b/Boards/WaveshareS3Touch/display.cpp similarity index 88% rename from boards/waveshare_s3_touch/display.c rename to Boards/WaveshareS3Touch/display.cpp index 6d1450f0..9829a4d8 100644 --- a/boards/waveshare_s3_touch/display.c +++ b/Boards/WaveshareS3Touch/display.cpp @@ -1,9 +1,8 @@ #include "config.h" #include "lvgl.h" -#include "lvgl_i.h" -#include "tactility_core.h" -#include "thread.h" +#include "TactilityCore.h" +#include "Thread.h" #include "esp_err.h" #include "esp_lcd_panel_ops.h" @@ -16,9 +15,9 @@ #define TAG "waveshare_s3_touch_display" -SemaphoreHandle_t sem_vsync_end = NULL; -SemaphoreHandle_t sem_gui_ready = NULL; -SemaphoreHandle_t lvgl_mux = NULL; +SemaphoreHandle_t sem_vsync_end = nullptr; +SemaphoreHandle_t sem_gui_ready = nullptr; +SemaphoreHandle_t lvgl_mux = nullptr; #if WAVESHARE_LCD_USE_DOUBLE_FB #define WAVESHARE_LCD_NUM_FB 2 @@ -30,7 +29,7 @@ static bool lvgl_is_running = false; bool ws3t_display_lock(uint32_t timeout_ms) { assert(lvgl_mux && "lvgl_port_init must be called first"); - const TickType_t timeout_ticks = (timeout_ms == 0) ? TtWaitForever : pdMS_TO_TICKS(timeout_ms); + const TickType_t timeout_ticks = (timeout_ms == 0) ? tt::TtWaitForever : pdMS_TO_TICKS(timeout_ms); return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE; } @@ -58,7 +57,7 @@ static int32_t display_task(TT_UNUSED void* parameter) { vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); } - vTaskDelete(NULL); + vTaskDelete(nullptr); return 0; } @@ -82,7 +81,7 @@ static void lvgl_tick_task(TT_UNUSED void* arg) { } static void display_flush_callback(lv_display_t* disp, const lv_area_t* area, uint8_t* px_map) { - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp); + auto panel_handle = static_cast(lv_display_get_user_data(disp)); int offsetx1 = area->x1; int offsetx2 = area->x2; int offsety1 = area->y1; @@ -105,9 +104,9 @@ lv_disp_t* ws3t_display_create() { lvgl_mux = xSemaphoreCreateRecursiveMutex(); tt_assert(lvgl_mux); - Thread* thread = tt_thread_alloc_ex("display_task", 8192, &display_task, NULL); - tt_thread_set_priority(thread, ThreadPriorityHigh); // TODO: try out THREAD_PRIORITY_RENDER - tt_thread_start(thread); + tt::Thread* thread = tt::thread_alloc_ex("display_task", 8192, &display_task, nullptr); + tt::thread_set_priority(thread, tt::ThreadPriorityHigh); // TODO: try out THREAD_PRIORITY_RENDER + tt::thread_start(thread); esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_rgb_panel_config_t panel_config = { @@ -116,13 +115,13 @@ lv_disp_t* ws3t_display_create() { .pclk_hz = WAVESHARE_LCD_PIXEL_CLOCK_HZ, .h_res = WAVESHARE_LCD_HOR_RES, .v_res = WAVESHARE_LCD_VER_RES, + .hsync_pulse_width = 10, // The following parameters should refer to LCD spec .hsync_back_porch = 10, .hsync_front_porch = 20, - .hsync_pulse_width = 10, + .vsync_pulse_width = 10, .vsync_back_porch = 10, .vsync_front_porch = 10, - .vsync_pulse_width = 10, }, .data_width = 16, // RGB565 in parallel mode, thus 16bit in width .bits_per_pixel = 16, @@ -141,32 +140,31 @@ lv_disp_t* ws3t_display_create() { if (esp_lcd_new_rgb_panel(&panel_config, &panel_handle) != ESP_OK) { TT_LOG_E(TAG, "Failed to create RGB panel"); - return NULL; + return nullptr; } esp_lcd_rgb_panel_event_callbacks_t cbs = { .on_vsync = on_vsync_event, - .on_bounce_empty = NULL, - .on_bounce_frame_finish = NULL + .on_bounce_empty = nullptr, + .on_bounce_frame_finish = nullptr }; - if (esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, NULL) != ESP_OK) { + if (esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, nullptr) != ESP_OK) { TT_LOG_E(TAG, "Failed to register callbacks"); - return NULL; + return nullptr; } if (esp_lcd_panel_reset(panel_handle) != ESP_OK) { TT_LOG_E(TAG, "Failed to reset panel"); - return NULL; + return nullptr; } if (esp_lcd_panel_init(panel_handle) != ESP_OK) { TT_LOG_E(TAG, "Failed to init panel"); - return NULL; + return nullptr; } lv_init(); - NULL; #if WAVESHARE_LCD_USE_DOUBLE_FB // initialize LVGL draw buffers lv_draw_buf_t* buffer1 = lv_draw_buf_create(WAVESHARE_LCD_HOR_RES, WAVESHARE_LCD_BUFFER_HEIGHT, LV_COLOR_FORMAT_RGB565, 0); @@ -190,7 +188,7 @@ lv_disp_t* ws3t_display_create() { .callback = &lvgl_tick_task, .name = "lvgl_tick" }; - esp_timer_handle_t lvgl_tick_timer = NULL; + esp_timer_handle_t lvgl_tick_timer = nullptr; ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, WAVESHARE_LVGL_TICK_PERIOD_MS * 1000)); @@ -202,7 +200,7 @@ void ws3t_display_destroy() { // TODO: see esp_lvlg_port.c for more info if (lvgl_mux) { vSemaphoreDelete(lvgl_mux); - lvgl_mux = NULL; + lvgl_mux = nullptr; } #if LV_ENABLE_GC || !LV_MEM_CUSTOM lv_deinit(); diff --git a/boards/waveshare_s3_touch/display_i.h b/Boards/WaveshareS3Touch/display_i.h similarity index 51% rename from boards/waveshare_s3_touch/display_i.h rename to Boards/WaveshareS3Touch/display_i.h index 2000f194..595b0bad 100644 --- a/boards/waveshare_s3_touch/display_i.h +++ b/Boards/WaveshareS3Touch/display_i.h @@ -1,19 +1,10 @@ #pragma once #include "lvgl.h" -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif +#include bool ws3t_display_lock(uint32_t timeout_ms); void ws3t_display_unlock(void); lv_display_t* ws3t_display_create(); -void ws3t_display_destroy(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file +void ws3t_display_destroy(); \ No newline at end of file diff --git a/boards/waveshare_s3_touch/lvgl.c b/Boards/WaveshareS3Touch/lvgl.cpp similarity index 64% rename from boards/waveshare_s3_touch/lvgl.c rename to Boards/WaveshareS3Touch/lvgl.cpp index bfd7cd25..889abee4 100644 --- a/boards/waveshare_s3_touch/lvgl.c +++ b/Boards/WaveshareS3Touch/lvgl.cpp @@ -2,13 +2,13 @@ #include "display_i.h" #include "touch_i.h" -#include "ui/lvgl_sync.h" +#include "Ui/LvglSync.h" bool ws3t_init_lvgl() { - tt_lvgl_sync_set(&ws3t_display_lock, &ws3t_display_unlock); + tt::lvgl::sync_set(&ws3t_display_lock, &ws3t_display_unlock); lv_display_t* display = ws3t_display_create(); - if (display == NULL) { + if (display == nullptr) { return false; } diff --git a/Boards/WaveshareS3Touch/lvgl_i.h b/Boards/WaveshareS3Touch/lvgl_i.h new file mode 100644 index 00000000..479ffe27 --- /dev/null +++ b/Boards/WaveshareS3Touch/lvgl_i.h @@ -0,0 +1,3 @@ +#pragma once + +bool ws3t_init_lvgl(); \ No newline at end of file diff --git a/boards/waveshare_s3_touch/touch.c b/Boards/WaveshareS3Touch/touch.cpp similarity index 70% rename from boards/waveshare_s3_touch/touch.c rename to Boards/WaveshareS3Touch/touch.cpp index 680ffe27..78e8a285 100644 --- a/boards/waveshare_s3_touch/touch.c +++ b/Boards/WaveshareS3Touch/touch.cpp @@ -9,16 +9,20 @@ #define TAG "waveshare_s3_touch_i2c" static esp_lcd_touch_handle_t touch_init_internal() { - static esp_lcd_panel_io_handle_t tp_io_handle = NULL; + static esp_lcd_panel_io_handle_t tp_io_handle = nullptr; static esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); ESP_LOGI(TAG, "Initialize touch IO"); - /* Touch IO handle */ - ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)WAVESHARE_TOUCH_I2C_PORT, &tp_io_config, &tp_io_handle)); + + // TODO: Revert on new ESP-IDF version + static_assert(ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 3, 1)); + // ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)WAVESHARE_TOUCH_I2C_PORT, &tp_io_config, &tp_io_handle)); + esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)WAVESHARE_TOUCH_I2C_PORT, &tp_io_config, &tp_io_handle); + esp_lcd_touch_config_t tp_cfg = { .x_max = WAVESHARE_LCD_VER_RES, .y_max = WAVESHARE_LCD_HOR_RES, - .rst_gpio_num = -1, - .int_gpio_num = -1, + .rst_gpio_num = GPIO_NUM_NC, + .int_gpio_num = GPIO_NUM_NC, .flags = { .swap_xy = 0, .mirror_x = 0, @@ -27,7 +31,7 @@ static esp_lcd_touch_handle_t touch_init_internal() { }; /* Initialize touch */ ESP_LOGI(TAG, "Initialize touch controller GT911"); - esp_lcd_touch_handle_t tp = NULL; + esp_lcd_touch_handle_t tp = nullptr; ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp)); return tp; @@ -39,11 +43,11 @@ static void touch_callback(lv_indev_t* indev, lv_indev_data_t* data) { uint8_t touchpad_cnt = 0; /* Read touch controller data */ - esp_lcd_touch_handle_t touch_handle = lv_indev_get_user_data(indev); + auto touch_handle = static_cast(lv_indev_get_user_data(indev)); esp_lcd_touch_read_data(touch_handle); /* Get coordinates */ - bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_handle, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1); + bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_handle, touchpad_x, touchpad_y, nullptr, &touchpad_cnt, 1); if (touchpad_pressed && touchpad_cnt > 0) { data->point.x = touchpad_x[0]; @@ -59,7 +63,7 @@ void ws3t_touch_init(lv_display_t* display) { ESP_LOGI(TAG, "Register display indev to LVGL"); - static lv_indev_t* device = NULL; + static lv_indev_t* device = nullptr; device = lv_indev_create(); lv_indev_set_type(device, LV_INDEV_TYPE_POINTER); lv_indev_set_read_cb(device, &touch_callback); diff --git a/Boards/WaveshareS3Touch/touch_i.h b/Boards/WaveshareS3Touch/touch_i.h new file mode 100644 index 00000000..b51cb935 --- /dev/null +++ b/Boards/WaveshareS3Touch/touch_i.h @@ -0,0 +1,5 @@ +#pragma once + +#include "lvgl.h" + +void ws3t_touch_init(lv_display_t* display); \ No newline at end of file diff --git a/Boards/WaveshareS3Touch/waveshare_s3_touch.cpp b/Boards/WaveshareS3Touch/waveshare_s3_touch.cpp new file mode 100644 index 00000000..c1bf35c5 --- /dev/null +++ b/Boards/WaveshareS3Touch/waveshare_s3_touch.cpp @@ -0,0 +1,15 @@ +#include "waveshare_s3_touch.h" + +#include "lvgl_i.h" + +bool ws3t_bootstrap(); + +extern const tt::hal::Configuration waveshare_s3_touch = { + .bootstrap = &ws3t_bootstrap, + .init_graphics = &ws3t_init_lvgl, + .display = { + .set_backlight_duty = nullptr // TODO: This requires implementing the CH422G IO expander + }, + .sdcard = nullptr, + .power = nullptr +}; diff --git a/Boards/WaveshareS3Touch/waveshare_s3_touch.h b/Boards/WaveshareS3Touch/waveshare_s3_touch.h new file mode 100644 index 00000000..2bc97826 --- /dev/null +++ b/Boards/WaveshareS3Touch/waveshare_s3_touch.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Hal/Configuration.h" + +// Waveshare S3 Touch LCD 4.3 +extern const tt::hal::Configuration waveshare_s3_touch; diff --git a/boards/yellow_board/CMakeLists.txt b/Boards/YellowBoard/CMakeLists.txt similarity index 58% rename from boards/yellow_board/CMakeLists.txt rename to Boards/YellowBoard/CMakeLists.txt index 8fe057d5..45cb70d8 100644 --- a/boards/yellow_board/CMakeLists.txt +++ b/Boards/YellowBoard/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "." INCLUDE_DIRS "." - REQUIRES tactility esp_lvgl_port esp_lcd_touch_cst816s esp_lcd_ili9341 driver vfs fatfs + REQUIRES Tactility esp_lvgl_port esp_lcd_touch_cst816s esp_lcd_ili9341 driver vfs fatfs ) diff --git a/boards/yellow_board/bootstrap.c b/Boards/YellowBoard/bootstrap.cpp similarity index 92% rename from boards/yellow_board/bootstrap.c rename to Boards/YellowBoard/bootstrap.cpp index 039332f7..817dd925 100644 --- a/boards/yellow_board/bootstrap.c +++ b/Boards/YellowBoard/bootstrap.cpp @@ -1,5 +1,5 @@ #include "config.h" -#include "tactility_core.h" +#include "TactilityCore.h" #include "display_i.h" #include @@ -9,19 +9,21 @@ static bool init_i2c() { const i2c_config_t i2c_conf = { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_33, - .sda_pullup_en = GPIO_PULLUP_DISABLE, .scl_io_num = GPIO_NUM_32, + .sda_pullup_en = GPIO_PULLUP_DISABLE, .scl_pullup_en = GPIO_PULLUP_DISABLE, - .master.clk_speed = 400000 + .master = { + .clk_speed = 400000 + } }; if (i2c_param_config(TWODOTFOUR_TOUCH_I2C_PORT, &i2c_conf) != ESP_OK) { - ESP_LOGE(TAG, "i2c config failed"); + TT_LOG_E(TAG, "i2c config failed"); return false; } if (i2c_driver_install(TWODOTFOUR_TOUCH_I2C_PORT, i2c_conf.mode, 0, 0, 0) != ESP_OK) { - ESP_LOGE(TAG, "i2c driver install failed"); + TT_LOG_E(TAG, "i2c driver install failed"); return false; } @@ -30,11 +32,11 @@ static bool init_i2c() { static bool init_spi2() { const spi_bus_config_t bus_config = { - .sclk_io_num = TWODOTFOUR_SPI2_PIN_SCLK, .mosi_io_num = TWODOTFOUR_SPI2_PIN_MOSI, .miso_io_num = GPIO_NUM_NC, - .quadhd_io_num = GPIO_NUM_NC, + .sclk_io_num = TWODOTFOUR_SPI2_PIN_SCLK, .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, .max_transfer_sz = TWODOTFOUR_SPI2_TRANSACTION_LIMIT }; @@ -48,11 +50,11 @@ static bool init_spi2() { static bool init_spi3() { const spi_bus_config_t bus_config = { - .sclk_io_num = TWODOTFOUR_SPI3_PIN_SCLK, .mosi_io_num = TWODOTFOUR_SPI3_PIN_MOSI, .miso_io_num = TWODOTFOUR_SPI3_PIN_MISO, - .quadhd_io_num = GPIO_NUM_NC, + .sclk_io_num = TWODOTFOUR_SPI3_PIN_SCLK, .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, .max_transfer_sz = TWODOTFOUR_SPI3_TRANSACTION_LIMIT }; diff --git a/boards/yellow_board/config.h b/Boards/YellowBoard/config.h similarity index 97% rename from boards/yellow_board/config.h rename to Boards/YellowBoard/config.h index 2b9a3297..098f7488 100644 --- a/boards/yellow_board/config.h +++ b/Boards/YellowBoard/config.h @@ -27,7 +27,7 @@ #define TWODOTFOUR_LCD_PIN_BACKLIGHT GPIO_NUM_27 // Touch -#define TWODOTFOUR_TOUCH_I2C_PORT 0 +#define TWODOTFOUR_TOUCH_I2C_PORT I2C_NUM_0 // SD Card #define TWODOTFOUR_SDCARD_SPI_HOST SPI3_HOST diff --git a/boards/yellow_board/display.c b/Boards/YellowBoard/display.cpp similarity index 93% rename from boards/yellow_board/display.c rename to Boards/YellowBoard/display.cpp index c4c4aedd..24ab262b 100644 --- a/boards/yellow_board/display.c +++ b/Boards/YellowBoard/display.cpp @@ -1,5 +1,5 @@ #include "config.h" -#include "tactility_core.h" +#include "TactilityCore.h" #include "driver/gpio.h" #include "driver/ledc.h" @@ -21,8 +21,8 @@ bool twodotfour_backlight_init() { ledc_timer_config_t ledc_timer = { .speed_mode = TWODOTFOUR_LCD_BACKLIGHT_LEDC_MODE, - .timer_num = TWODOTFOUR_LCD_BACKLIGHT_LEDC_TIMER, .duty_resolution = TWODOTFOUR_LCD_BACKLIGHT_LEDC_DUTY_RES, + .timer_num = TWODOTFOUR_LCD_BACKLIGHT_LEDC_TIMER, .freq_hz = TWODOTFOUR_LCD_BACKLIGHT_LEDC_FREQUENCY, .clk_cfg = LEDC_AUTO_CLK }; @@ -37,11 +37,11 @@ bool twodotfour_backlight_init() { void twodotfour_backlight_set(uint8_t duty) { ledc_channel_config_t ledc_channel = { + .gpio_num = TWODOTFOUR_LCD_PIN_BACKLIGHT, .speed_mode = TWODOTFOUR_LCD_BACKLIGHT_LEDC_MODE, .channel = TWODOTFOUR_LCD_BACKLIGHT_LEDC_CHANNEL, - .timer_sel = TWODOTFOUR_LCD_BACKLIGHT_LEDC_TIMER, .intr_type = LEDC_INTR_DISABLE, - .gpio_num = TWODOTFOUR_LCD_PIN_BACKLIGHT, + .timer_sel = TWODOTFOUR_LCD_BACKLIGHT_LEDC_TIMER, .duty = duty, .hpoint = 0 }; @@ -59,14 +59,14 @@ lv_disp_t* twodotfour_display_init() { const esp_lcd_panel_io_spi_config_t panel_io_config = ILI9341_PANEL_IO_SPI_CONFIG( TWODOTFOUR_LCD_PIN_CS, TWODOTFOUR_LCD_PIN_DC, - NULL, - NULL + nullptr, + nullptr ); esp_lcd_panel_io_handle_t io_handle; if (esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TWODOTFOUR_LCD_SPI_HOST, &panel_io_config, &io_handle) != ESP_OK) { TT_LOG_E(TAG, "Failed to create panel"); - return false; + return nullptr; } const esp_lcd_panel_dev_config_t panel_config = { @@ -78,27 +78,27 @@ lv_disp_t* twodotfour_display_init() { esp_lcd_panel_handle_t panel_handle; if (esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle) != ESP_OK) { TT_LOG_E(TAG, "Failed to create ili9341"); - return false; + return nullptr; } if (esp_lcd_panel_reset(panel_handle) != ESP_OK) { TT_LOG_E(TAG, "Failed to reset panel"); - return false; + return nullptr; } if (esp_lcd_panel_init(panel_handle) != ESP_OK) { TT_LOG_E(TAG, "Failed to init panel"); - return false; + return nullptr; } if (esp_lcd_panel_mirror(panel_handle, true, false) != ESP_OK) { TT_LOG_E(TAG, "Failed to set panel to mirror"); - return false; + return nullptr; } if (esp_lcd_panel_disp_on_off(panel_handle, true) != ESP_OK) { TT_LOG_E(TAG, "Failed to turn display on"); - return false; + return nullptr; } const lvgl_port_display_cfg_t disp_cfg = { @@ -122,7 +122,5 @@ lv_disp_t* twodotfour_display_init() { } }; - lv_display_t* display = lvgl_port_add_disp(&disp_cfg); - - return display; + return lvgl_port_add_disp(&disp_cfg); } diff --git a/Boards/YellowBoard/display_i.h b/Boards/YellowBoard/display_i.h new file mode 100644 index 00000000..4c9bedfa --- /dev/null +++ b/Boards/YellowBoard/display_i.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +bool twodotfour_backlight_init(); +void twodotfour_backlight_set(uint8_t duty); \ No newline at end of file diff --git a/boards/yellow_board/lvgl.c b/Boards/YellowBoard/lvgl.cpp similarity index 77% rename from boards/yellow_board/lvgl.c rename to Boards/YellowBoard/lvgl.cpp index 2446ee8f..3065a953 100644 --- a/boards/yellow_board/lvgl.c +++ b/Boards/YellowBoard/lvgl.cpp @@ -1,7 +1,7 @@ #include "esp_lvgl_port.h" -#include "log.h" -#include "ui/lvgl_sync.h" -#include +#include "Log.h" +#include "Ui/LvglSync.h" +#include "Thread.h" #define TAG "twodotfour_lvgl" @@ -9,12 +9,12 @@ lv_display_t* twodotfour_display_init(); bool twodotfour_touch_init(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle); bool twodotfour_lvgl_init() { - static lv_display_t* display = NULL; + static lv_display_t* display = nullptr; static esp_lcd_panel_io_handle_t touch_io_handle; static esp_lcd_touch_handle_t touch_handle; const lvgl_port_cfg_t lvgl_cfg = { - .task_priority = ThreadPriorityHigh, + .task_priority = tt::ThreadPriorityHigh, .task_stack = 8096, .task_affinity = -1, // core pinning .task_max_sleep_ms = 500, @@ -28,7 +28,7 @@ bool twodotfour_lvgl_init() { // Add display display = twodotfour_display_init(); - if (display == NULL) { + if (display == nullptr) { TT_LOG_E(TAG, "failed to add display"); return false; } @@ -43,14 +43,14 @@ bool twodotfour_lvgl_init() { .handle = touch_handle, }; - lv_indev_t _Nullable* touch_indev = lvgl_port_add_touch(&touch_cfg); - if (touch_indev == NULL) { + auto* touch_indev= lvgl_port_add_touch(&touch_cfg); + if (touch_indev == nullptr) { TT_LOG_E(TAG, "failed to add touch to lvgl"); return false; } // Set syncing functions - tt_lvgl_sync_set(&lvgl_port_lock, &lvgl_port_unlock); + tt::lvgl::sync_set(&lvgl_port_lock, &lvgl_port_unlock); return true; } diff --git a/boards/yellow_board/sdcard.c b/Boards/YellowBoard/sdcard.cpp similarity index 77% rename from boards/yellow_board/sdcard.c rename to Boards/YellowBoard/sdcard.cpp index ee3aecba..2884250d 100644 --- a/boards/yellow_board/sdcard.c +++ b/Boards/YellowBoard/sdcard.cpp @@ -1,6 +1,6 @@ -#include "sdcard.h" -#include "check.h" -#include "log.h" +#include "Hal/Sdcard.h" +#include "Check.h" +#include "Log.h" #include "config.h" #include "esp_vfs_fat.h" @@ -40,13 +40,13 @@ static void* sdcard_mount(const char* mount_point) { } else { TT_LOG_E(TAG, "Mounting failed (%s)", esp_err_to_name(ret)); } - return NULL; + return nullptr; } - MountData* data = malloc(sizeof(MountData)); + auto* data = static_cast(malloc(sizeof(MountData))); *data = (MountData) { - .card = card, - .mount_point = mount_point + .mount_point = mount_point, + .card = card }; sdmmc_card_print_info(stdout, data->card); @@ -55,10 +55,10 @@ static void* sdcard_mount(const char* mount_point) { } static void sdcard_unmount(void* context) { - MountData* data = (MountData*)context; + auto* data = static_cast(context); TT_LOG_I(TAG, "Unmounting %s", data->mount_point); - tt_assert(data != NULL); + tt_assert(data != nullptr); if (esp_vfs_fat_sdcard_unmount(data->mount_point, data->card) != ESP_OK) { TT_LOG_E(TAG, "Unmount failed for %s", data->mount_point); } @@ -67,13 +67,13 @@ static void sdcard_unmount(void* context) { } static bool sdcard_is_mounted(void* context) { - MountData* data = (MountData*)context; - return (data != NULL) && (sdmmc_get_status(data->card) == ESP_OK); + auto* data = static_cast(context); + return (data != nullptr) && (sdmmc_get_status(data->card) == ESP_OK); } -const SdCard twodotfour_sdcard = { +extern const tt::hal::sdcard::SdCard twodotfour_sdcard = { .mount = &sdcard_mount, .unmount = &sdcard_unmount, .is_mounted = &sdcard_is_mounted, - .mount_behaviour = SdcardMountBehaviourAnytime + .mount_behaviour = tt::hal::sdcard::MountBehaviourAnytime }; diff --git a/boards/yellow_board/touch.c b/Boards/YellowBoard/touch.cpp similarity index 73% rename from boards/yellow_board/touch.c rename to Boards/YellowBoard/touch.cpp index ec3d737f..811880f2 100644 --- a/boards/yellow_board/touch.c +++ b/Boards/YellowBoard/touch.cpp @@ -1,8 +1,9 @@ #include "config.h" #include "driver/i2c.h" -#include "log.h" #include "esp_err.h" #include "esp_lcd_touch_cst816s.h" +#include "esp_lcd_touch.h" +#include "Log.h" #define TAG "twodotfour_cst816" @@ -11,10 +12,15 @@ bool twodotfour_touch_init(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_h const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG(); + // TODO: Check when ESP-IDF publishes fix (5.3.2 or 5.4.x) + static_assert(ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 3, 1)); + esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TWODOTFOUR_TOUCH_I2C_PORT, &touch_io_config, io_handle); + /* if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TWODOTFOUR_TOUCH_I2C_PORT, &touch_io_config, io_handle) != ESP_OK) { TT_LOG_E(TAG, "Touch I2C IO init failed"); return false; } + */ esp_lcd_touch_config_t config = { .x_max = 240, @@ -30,9 +36,9 @@ bool twodotfour_touch_init(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_h .mirror_x = 0, .mirror_y = 0, }, - .process_coordinates = NULL, - .interrupt_callback = NULL, - .user_data = NULL + .process_coordinates = nullptr, + .interrupt_callback = nullptr, + .user_data = nullptr }; if (esp_lcd_touch_new_i2c_cst816s(*io_handle, &config, touch_handle) != ESP_OK) { diff --git a/boards/yellow_board/yellow_board.c b/Boards/YellowBoard/yellow_board.cpp similarity index 69% rename from boards/yellow_board/yellow_board.c rename to Boards/YellowBoard/yellow_board.cpp index 0a57eee5..6b1494b8 100644 --- a/boards/yellow_board/yellow_board.c +++ b/Boards/YellowBoard/yellow_board.cpp @@ -4,14 +4,14 @@ bool twodotfour_lvgl_init(); bool twodotfour_bootstrap(); -extern const SdCard twodotfour_sdcard; +extern const tt::hal::sdcard::SdCard twodotfour_sdcard; -const HardwareConfig yellow_board_24inch_cap = { +const tt::hal::Configuration yellow_board_24inch_cap = { .bootstrap = &twodotfour_bootstrap, + .init_graphics = &twodotfour_lvgl_init, .display = { .set_backlight_duty = &twodotfour_backlight_set }, - .init_graphics = &twodotfour_lvgl_init, .sdcard = &twodotfour_sdcard, - .power = NULL + .power = nullptr }; diff --git a/Boards/YellowBoard/yellow_board.h b/Boards/YellowBoard/yellow_board.h new file mode 100644 index 00000000..99ceb8d3 --- /dev/null +++ b/Boards/YellowBoard/yellow_board.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Hal/Configuration.h" + +// Capacitive touch version of the 2.4" yellow board +extern const tt::hal::Configuration yellow_board_24inch_cap; diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fad55dc..cf985d63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.16) add_definitions(-DTT_DEBUG) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_CXX_COMPILER_TARGET}") + if (DEFINED ENV{ESP_IDF_VERSION}) message("Building with ESP-IDF v$ENV{ESP_IDF_VERSION}") include($ENV{IDF_PATH}/tools/cmake/project.cmake) @@ -12,78 +16,72 @@ if (DEFINED ENV{ESP_IDF_VERSION}) add_definitions(-DARDUINO_M5STACK_CORE2) add_compile_definitions(ARDUINO_M5STACK_CORE2) - set(COMPONENTS app-esp) + set(COMPONENTS AppEsp32) set(EXTRA_COMPONENT_DIRS - "boards" - "app-esp" - "tactility" - "tactility-headless" - "libs/esp_lvgl_port" - "libs/lvgl" - "libs/M5Unified" - "libs/M5GFX" + "Boards" + "AppEsp32" + "Tactility" + "TactilityHeadless" + "Libraries/esp_lvgl_port" + "Libraries/lvgl" + "Libraries/M5Unified" + "Libraries/M5GFX" ) # ESP32 boards if(NOT "${IDF_TARGET}" STREQUAL "esp32") - set(EXCLUDE_COMPONENTS "yellow_board_2432s024") - set(EXCLUDE_COMPONENTS "m5stack_core2") - set(EXCLUDE_COMPONENTS "m5stack_cores3") + set(EXCLUDE_COMPONENTS "YellowBoard") + set(EXCLUDE_COMPONENTS "M5stackCore2") + set(EXCLUDE_COMPONENTS "M5stackCoreS3") endif() # ESP32-S3 boards if(NOT "${IDF_TARGET}" STREQUAL "esp32s3") - set(EXCLUDE_COMPONENTS "lilygo_tdeck") - set(EXCLUDE_COMPONENTS "waveshare_s3_touch") + set(EXCLUDE_COMPONENTS "LilygoTdeck") + set(EXCLUDE_COMPONENTS "WaveshareS3Touch") endif() else() message("Building for sim target") endif() -project(tactility-root) +project(TactilityRoot) # Defined as regular project for PC and component for ESP if (NOT DEFINED ENV{ESP_IDF_VERSION}) - add_subdirectory(tactility) - add_subdirectory(tactility-headless) + add_subdirectory(Tactility) + add_subdirectory(TactilityHeadless) endif() -add_subdirectory(tactility-core) +add_subdirectory(TactilityCore) -add_subdirectory(libs/mlib) -add_subdirectory(libs/lv_screenshot) +add_subdirectory(Libraries/lv_screenshot) if (NOT DEFINED ENV{ESP_IDF_VERSION}) - set(CMAKE_C_STANDARD 99) # C99 # lvgl officially support C99 and above - set(CMAKE_CXX_STANDARD 17) # C17 - set(CMAKE_CXX_STANDARD_REQUIRED ON) - set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_C_COMPILER_TARGET}") - # FreeRTOS - set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/app-sim/src CACHE STRING "") + set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/AppSim/Source CACHE STRING "") set(FREERTOS_PORT GCC_POSIX CACHE STRING "") - add_subdirectory(libs/FreeRTOS-Kernel) + add_subdirectory(Libraries/FreeRTOS-Kernel) target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0") target_include_directories(freertos_kernel - PUBLIC app-sim/src # for FreeRTOSConfig.h + PUBLIC AppSim/Source # for FreeRTOSConfig.h ) # EmbedTLS - add_subdirectory(libs/mbedtls) + add_subdirectory(Libraries/mbedtls) # Sim app if (NOT DEFINED ENV{SKIP_SDL}) - add_subdirectory(app-sim) + add_subdirectory(AppSim) endif() # Tests - add_subdirectory(tests) + add_subdirectory(Tests) # SDL & LVGL - set(LV_CONF_PATH ${PROJECT_SOURCE_DIR}/app-sim/src/lv_conf.h) - add_subdirectory(libs/lvgl) # Added as idf component for ESP and as library for other targets + set(LV_CONF_PATH ${PROJECT_SOURCE_DIR}/AppSim/Source/lv_conf.h) + add_subdirectory(Libraries/lvgl) # Added as idf component for ESP and as library for other targets target_include_directories(lvgl - PUBLIC app-sim/src # for lv_conf.h and lv_drv_conf.h + PUBLIC AppSim/Source # for lv_conf.h and lv_drv_conf.h ) # TODO: This is a temporary skipping option for running unit tests @@ -109,13 +107,13 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION}) # `brew install sdl2_image` find_package(SDL2_image REQUIRED) target_include_directories(lvgl PUBLIC ${SDL2_IMAGE_INCLUDE_DIRS}) - target_link_libraries(app-sim ${SDL2_IMAGE_LIBRARIES}) + target_link_libraries(AppSim ${SDL2_IMAGE_LIBRARIES}) endif(LV_USE_DRAW_SDL) if (LV_USE_LIBPNG) find_package(PNG REQUIRED) target_include_directories(lvgl PUBLIC ${PNG_INCLUDE_DIR}) - target_link_libraries(app-sim ${PNG_LIBRARY}) + target_link_libraries(AppSim ${PNG_LIBRARY}) endif(LV_USE_LIBPNG) if (LV_USE_LIBJPEG_TURBO) @@ -124,7 +122,7 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION}) # `brew install libjpeg-turbo` find_package(JPEG REQUIRED) target_include_directories(lvgl PUBLIC ${JPEG_INCLUDE_DIRS}) - target_link_libraries(app-sim ${JPEG_LIBRARIES}) + target_link_libraries(AppSim ${JPEG_LIBRARIES}) endif(LV_USE_LIBJPEG_TURBO) if (LV_USE_FFMPEG) @@ -133,7 +131,7 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION}) if (LV_USE_FREETYPE) find_package(Freetype REQUIRED) - target_link_libraries(app-sim ${FREETYPE_LIBRARIES}) + target_link_libraries(AppSim ${FREETYPE_LIBRARIES}) target_include_directories(lvgl PUBLIC ${FREETYPE_INCLUDE_DIRS}) endif(LV_USE_FREETYPE) endif() diff --git a/data/assets/app_icon_display_settings.png b/Data/assets/app_icon_display_settings.png similarity index 100% rename from data/assets/app_icon_display_settings.png rename to Data/assets/app_icon_display_settings.png diff --git a/data/assets/app_icon_fallback.png b/Data/assets/app_icon_fallback.png similarity index 100% rename from data/assets/app_icon_fallback.png rename to Data/assets/app_icon_fallback.png diff --git a/data/assets/app_icon_files.png b/Data/assets/app_icon_files.png similarity index 100% rename from data/assets/app_icon_files.png rename to Data/assets/app_icon_files.png diff --git a/data/assets/app_icon_power_settings.png b/Data/assets/app_icon_power_settings.png similarity index 100% rename from data/assets/app_icon_power_settings.png rename to Data/assets/app_icon_power_settings.png diff --git a/data/assets/app_icon_settings.png b/Data/assets/app_icon_settings.png similarity index 100% rename from data/assets/app_icon_settings.png rename to Data/assets/app_icon_settings.png diff --git a/data/assets/app_icon_system_info.png b/Data/assets/app_icon_system_info.png similarity index 100% rename from data/assets/app_icon_system_info.png rename to Data/assets/app_icon_system_info.png diff --git a/data/assets/power_020.png b/Data/assets/power_020.png similarity index 100% rename from data/assets/power_020.png rename to Data/assets/power_020.png diff --git a/data/assets/power_040.png b/Data/assets/power_040.png similarity index 100% rename from data/assets/power_040.png rename to Data/assets/power_040.png diff --git a/data/assets/power_060.png b/Data/assets/power_060.png similarity index 100% rename from data/assets/power_060.png rename to Data/assets/power_060.png diff --git a/data/assets/power_080.png b/Data/assets/power_080.png similarity index 100% rename from data/assets/power_080.png rename to Data/assets/power_080.png diff --git a/data/assets/power_100.png b/Data/assets/power_100.png similarity index 100% rename from data/assets/power_100.png rename to Data/assets/power_100.png diff --git a/data/assets/sdcard.png b/Data/assets/sdcard.png similarity index 100% rename from data/assets/sdcard.png rename to Data/assets/sdcard.png diff --git a/data/assets/sdcard_alert.png b/Data/assets/sdcard_alert.png similarity index 100% rename from data/assets/sdcard_alert.png rename to Data/assets/sdcard_alert.png diff --git a/data/assets/wifi_connection_issue.png b/Data/assets/wifi_connection_issue.png similarity index 100% rename from data/assets/wifi_connection_issue.png rename to Data/assets/wifi_connection_issue.png diff --git a/data/assets/wifi_find.png b/Data/assets/wifi_find.png similarity index 100% rename from data/assets/wifi_find.png rename to Data/assets/wifi_find.png diff --git a/data/assets/wifi_off.png b/Data/assets/wifi_off.png similarity index 100% rename from data/assets/wifi_off.png rename to Data/assets/wifi_off.png diff --git a/data/assets/wifi_perm_scan.png b/Data/assets/wifi_perm_scan.png similarity index 100% rename from data/assets/wifi_perm_scan.png rename to Data/assets/wifi_perm_scan.png diff --git a/data/assets/wifi_signal_0.png b/Data/assets/wifi_signal_0.png similarity index 100% rename from data/assets/wifi_signal_0.png rename to Data/assets/wifi_signal_0.png diff --git a/data/assets/wifi_signal_0_locked.png b/Data/assets/wifi_signal_0_locked.png similarity index 100% rename from data/assets/wifi_signal_0_locked.png rename to Data/assets/wifi_signal_0_locked.png diff --git a/data/assets/wifi_signal_1.png b/Data/assets/wifi_signal_1.png similarity index 100% rename from data/assets/wifi_signal_1.png rename to Data/assets/wifi_signal_1.png diff --git a/data/assets/wifi_signal_1_locked.png b/Data/assets/wifi_signal_1_locked.png similarity index 100% rename from data/assets/wifi_signal_1_locked.png rename to Data/assets/wifi_signal_1_locked.png diff --git a/data/assets/wifi_signal_2.png b/Data/assets/wifi_signal_2.png similarity index 100% rename from data/assets/wifi_signal_2.png rename to Data/assets/wifi_signal_2.png diff --git a/data/assets/wifi_signal_2_locked.png b/Data/assets/wifi_signal_2_locked.png similarity index 100% rename from data/assets/wifi_signal_2_locked.png rename to Data/assets/wifi_signal_2_locked.png diff --git a/data/assets/wifi_signal_3.png b/Data/assets/wifi_signal_3.png similarity index 100% rename from data/assets/wifi_signal_3.png rename to Data/assets/wifi_signal_3.png diff --git a/data/assets/wifi_signal_3_locked.png b/Data/assets/wifi_signal_3_locked.png similarity index 100% rename from data/assets/wifi_signal_3_locked.png rename to Data/assets/wifi_signal_3_locked.png diff --git a/data/assets/wifi_signal_4.png b/Data/assets/wifi_signal_4.png similarity index 100% rename from data/assets/wifi_signal_4.png rename to Data/assets/wifi_signal_4.png diff --git a/data/assets/wifi_signal_4_locked.png b/Data/assets/wifi_signal_4_locked.png similarity index 100% rename from data/assets/wifi_signal_4_locked.png rename to Data/assets/wifi_signal_4_locked.png diff --git a/data/assets_sources/Desktop Icons.svg b/Data/assets_sources/Desktop Icons.svg similarity index 100% rename from data/assets_sources/Desktop Icons.svg rename to Data/assets_sources/Desktop Icons.svg diff --git a/data/assets_sources/sdcard.svg b/Data/assets_sources/sdcard.svg similarity index 100% rename from data/assets_sources/sdcard.svg rename to Data/assets_sources/sdcard.svg diff --git a/data/assets_sources/sdcard_alert.svg b/Data/assets_sources/sdcard_alert.svg similarity index 100% rename from data/assets_sources/sdcard_alert.svg rename to Data/assets_sources/sdcard_alert.svg diff --git a/data/assets_sources/wifi_connection_issue.svg b/Data/assets_sources/wifi_connection_issue.svg similarity index 100% rename from data/assets_sources/wifi_connection_issue.svg rename to Data/assets_sources/wifi_connection_issue.svg diff --git a/data/assets_sources/wifi_find.svg b/Data/assets_sources/wifi_find.svg similarity index 100% rename from data/assets_sources/wifi_find.svg rename to Data/assets_sources/wifi_find.svg diff --git a/data/assets_sources/wifi_off.svg b/Data/assets_sources/wifi_off.svg similarity index 100% rename from data/assets_sources/wifi_off.svg rename to Data/assets_sources/wifi_off.svg diff --git a/data/assets_sources/wifi_perm_scan.svg b/Data/assets_sources/wifi_perm_scan.svg similarity index 100% rename from data/assets_sources/wifi_perm_scan.svg rename to Data/assets_sources/wifi_perm_scan.svg diff --git a/data/assets_sources/wifi_signal_0.svg b/Data/assets_sources/wifi_signal_0.svg similarity index 100% rename from data/assets_sources/wifi_signal_0.svg rename to Data/assets_sources/wifi_signal_0.svg diff --git a/data/assets_sources/wifi_signal_0_lock.svg b/Data/assets_sources/wifi_signal_0_lock.svg similarity index 100% rename from data/assets_sources/wifi_signal_0_lock.svg rename to Data/assets_sources/wifi_signal_0_lock.svg diff --git a/data/assets_sources/wifi_signal_1.svg b/Data/assets_sources/wifi_signal_1.svg similarity index 100% rename from data/assets_sources/wifi_signal_1.svg rename to Data/assets_sources/wifi_signal_1.svg diff --git a/data/assets_sources/wifi_signal_1_locked.svg b/Data/assets_sources/wifi_signal_1_locked.svg similarity index 100% rename from data/assets_sources/wifi_signal_1_locked.svg rename to Data/assets_sources/wifi_signal_1_locked.svg diff --git a/data/assets_sources/wifi_signal_2.svg b/Data/assets_sources/wifi_signal_2.svg similarity index 100% rename from data/assets_sources/wifi_signal_2.svg rename to Data/assets_sources/wifi_signal_2.svg diff --git a/data/assets_sources/wifi_signal_2_locked.svg b/Data/assets_sources/wifi_signal_2_locked.svg similarity index 100% rename from data/assets_sources/wifi_signal_2_locked.svg rename to Data/assets_sources/wifi_signal_2_locked.svg diff --git a/data/assets_sources/wifi_signal_3.svg b/Data/assets_sources/wifi_signal_3.svg similarity index 100% rename from data/assets_sources/wifi_signal_3.svg rename to Data/assets_sources/wifi_signal_3.svg diff --git a/data/assets_sources/wifi_signal_3_locked.svg b/Data/assets_sources/wifi_signal_3_locked.svg similarity index 100% rename from data/assets_sources/wifi_signal_3_locked.svg rename to Data/assets_sources/wifi_signal_3_locked.svg diff --git a/data/assets_sources/wifi_signal_4.svg b/Data/assets_sources/wifi_signal_4.svg similarity index 100% rename from data/assets_sources/wifi_signal_4.svg rename to Data/assets_sources/wifi_signal_4.svg diff --git a/data/assets_sources/wifi_signal_4_locked.svg b/Data/assets_sources/wifi_signal_4_locked.svg similarity index 100% rename from data/assets_sources/wifi_signal_4_locked.svg rename to Data/assets_sources/wifi_signal_4_locked.svg diff --git a/data/assets_sources/wifi_signal_5.svg b/Data/assets_sources/wifi_signal_5.svg similarity index 100% rename from data/assets_sources/wifi_signal_5.svg rename to Data/assets_sources/wifi_signal_5.svg diff --git a/data/config/placeholder.txt b/Data/config/placeholder.txt similarity index 100% rename from data/config/placeholder.txt rename to Data/config/placeholder.txt diff --git a/docs/app-lifecycle.md b/Documentation/app-lifecycle.md similarity index 100% rename from docs/app-lifecycle.md rename to Documentation/app-lifecycle.md diff --git a/docs/app-lifecycle.puml b/Documentation/app-lifecycle.puml similarity index 100% rename from docs/app-lifecycle.puml rename to Documentation/app-lifecycle.puml diff --git a/docs/ideas.md b/Documentation/ideas.md similarity index 96% rename from docs/ideas.md rename to Documentation/ideas.md index eee3fe81..4643eeea 100644 --- a/docs/ideas.md +++ b/Documentation/ideas.md @@ -16,6 +16,8 @@ - Explore LVGL9's ILI93414 driver for 2.4" Yellow Board - Bug: in LVGL9 with M5Core2, crash when bottom item is clicked without scrolling first - Publish firmwares with upload tool +- De-duplicate WiFi SSIDs. +- Move libs/M5 projects to boards/M5Shared # Core Ideas - Make a HAL? It would mainly be there to support PC development. It's a lot of effort for supporting what's effectively a dev-only feature. diff --git a/docs/pics/app-lifecycle.png b/Documentation/pics/app-lifecycle.png similarity index 100% rename from docs/pics/app-lifecycle.png rename to Documentation/pics/app-lifecycle.png diff --git a/docs/pics/screenshot-desktop.png b/Documentation/pics/screenshot-desktop.png similarity index 100% rename from docs/pics/screenshot-desktop.png rename to Documentation/pics/screenshot-desktop.png diff --git a/docs/pics/screenshot-files.png b/Documentation/pics/screenshot-files.png similarity index 100% rename from docs/pics/screenshot-files.png rename to Documentation/pics/screenshot-files.png diff --git a/docs/pics/screenshot-gpio.png b/Documentation/pics/screenshot-gpio.png similarity index 100% rename from docs/pics/screenshot-gpio.png rename to Documentation/pics/screenshot-gpio.png diff --git a/docs/pics/screenshot-settings.png b/Documentation/pics/screenshot-settings.png similarity index 100% rename from docs/pics/screenshot-settings.png rename to Documentation/pics/screenshot-settings.png diff --git a/docs/pics/screenshot-wifi_connect.png b/Documentation/pics/screenshot-wifi_connect.png similarity index 100% rename from docs/pics/screenshot-wifi_connect.png rename to Documentation/pics/screenshot-wifi_connect.png diff --git a/docs/pics/screenshot-wifi_manage.png b/Documentation/pics/screenshot-wifi_manage.png similarity index 100% rename from docs/pics/screenshot-wifi_manage.png rename to Documentation/pics/screenshot-wifi_manage.png diff --git a/docs/pics/tactility-devices.webp b/Documentation/pics/tactility-devices.webp similarity index 100% rename from docs/pics/tactility-devices.webp rename to Documentation/pics/tactility-devices.webp diff --git a/docs/project-structure.puml b/Documentation/project-structure.puml similarity index 60% rename from docs/project-structure.puml rename to Documentation/project-structure.puml index 5a9d99e2..06166531 100644 --- a/docs/project-structure.puml +++ b/Documentation/project-structure.puml @@ -1,19 +1,19 @@ @startuml skinparam componentStyle uml1 -[tactility] as t +[Tactility] as t note right of t : to build and use graphical apps -[tactility-headless] as theadless +[TactilityHeadless] as theadless note right of theadless : to build and use background services -[tactility-core] as tcore +[TactilityCore] as tcore note right of tcore : defines, data types, logging, async, etc. package "Applications" { - [app-sim] as appsim - [app-esp] as appesp + [AppSim] as appsim + [AppEsp32] as appesp } -note right of appesp : app-esp depends on the board \n projects for configuration +note right of appesp : AppEsp depends on the board \n projects for configuration [t] ..> [theadless] [theadless] ..> [tcore] diff --git a/libs/FreeRTOS-Kernel b/Libraries/FreeRTOS-Kernel similarity index 100% rename from libs/FreeRTOS-Kernel rename to Libraries/FreeRTOS-Kernel diff --git a/libs/M5GFX b/Libraries/M5GFX similarity index 100% rename from libs/M5GFX rename to Libraries/M5GFX diff --git a/libs/M5Unified b/Libraries/M5Unified similarity index 100% rename from libs/M5Unified rename to Libraries/M5Unified diff --git a/libs/esp_lvgl_port/CHANGELOG.md b/Libraries/esp_lvgl_port/CHANGELOG.md similarity index 100% rename from libs/esp_lvgl_port/CHANGELOG.md rename to Libraries/esp_lvgl_port/CHANGELOG.md diff --git a/libs/esp_lvgl_port/CMakeLists.txt b/Libraries/esp_lvgl_port/CMakeLists.txt similarity index 100% rename from libs/esp_lvgl_port/CMakeLists.txt rename to Libraries/esp_lvgl_port/CMakeLists.txt diff --git a/libs/esp_lvgl_port/README.md b/Libraries/esp_lvgl_port/README.md similarity index 100% rename from libs/esp_lvgl_port/README.md rename to Libraries/esp_lvgl_port/README.md diff --git a/libs/esp_lvgl_port/docs/frame_buffer_settings.png b/Libraries/esp_lvgl_port/docs/frame_buffer_settings.png similarity index 100% rename from libs/esp_lvgl_port/docs/frame_buffer_settings.png rename to Libraries/esp_lvgl_port/docs/frame_buffer_settings.png diff --git a/libs/esp_lvgl_port/docs/performance.md b/Libraries/esp_lvgl_port/docs/performance.md similarity index 100% rename from libs/esp_lvgl_port/docs/performance.md rename to Libraries/esp_lvgl_port/docs/performance.md diff --git a/libs/esp_lvgl_port/examples/touchscreen/CMakeLists.txt b/Libraries/esp_lvgl_port/examples/touchscreen/CMakeLists.txt similarity index 100% rename from libs/esp_lvgl_port/examples/touchscreen/CMakeLists.txt rename to Libraries/esp_lvgl_port/examples/touchscreen/CMakeLists.txt diff --git a/libs/esp_lvgl_port/examples/touchscreen/README.md b/Libraries/esp_lvgl_port/examples/touchscreen/README.md similarity index 100% rename from libs/esp_lvgl_port/examples/touchscreen/README.md rename to Libraries/esp_lvgl_port/examples/touchscreen/README.md diff --git a/libs/esp_lvgl_port/examples/touchscreen/main/CMakeLists.txt b/Libraries/esp_lvgl_port/examples/touchscreen/main/CMakeLists.txt similarity index 100% rename from libs/esp_lvgl_port/examples/touchscreen/main/CMakeLists.txt rename to Libraries/esp_lvgl_port/examples/touchscreen/main/CMakeLists.txt diff --git a/libs/esp_lvgl_port/examples/touchscreen/main/idf_component.yml b/Libraries/esp_lvgl_port/examples/touchscreen/main/idf_component.yml similarity index 100% rename from libs/esp_lvgl_port/examples/touchscreen/main/idf_component.yml rename to Libraries/esp_lvgl_port/examples/touchscreen/main/idf_component.yml diff --git a/libs/esp_lvgl_port/examples/touchscreen/main/main.c b/Libraries/esp_lvgl_port/examples/touchscreen/main/main.c similarity index 100% rename from libs/esp_lvgl_port/examples/touchscreen/main/main.c rename to Libraries/esp_lvgl_port/examples/touchscreen/main/main.c diff --git a/libs/esp_lvgl_port/examples/touchscreen/sdkconfig.defaults b/Libraries/esp_lvgl_port/examples/touchscreen/sdkconfig.defaults similarity index 100% rename from libs/esp_lvgl_port/examples/touchscreen/sdkconfig.defaults rename to Libraries/esp_lvgl_port/examples/touchscreen/sdkconfig.defaults diff --git a/libs/esp_lvgl_port/idf_component.yml b/Libraries/esp_lvgl_port/idf_component.yml similarity index 100% rename from libs/esp_lvgl_port/idf_component.yml rename to Libraries/esp_lvgl_port/idf_component.yml diff --git a/libs/esp_lvgl_port/images/img_cursor.png b/Libraries/esp_lvgl_port/images/img_cursor.png similarity index 100% rename from libs/esp_lvgl_port/images/img_cursor.png rename to Libraries/esp_lvgl_port/images/img_cursor.png diff --git a/libs/esp_lvgl_port/images/img_cursor_20px.png b/Libraries/esp_lvgl_port/images/img_cursor_20px.png similarity index 100% rename from libs/esp_lvgl_port/images/img_cursor_20px.png rename to Libraries/esp_lvgl_port/images/img_cursor_20px.png diff --git a/libs/esp_lvgl_port/images/lvgl8/img_cursor.c b/Libraries/esp_lvgl_port/images/lvgl8/img_cursor.c similarity index 100% rename from libs/esp_lvgl_port/images/lvgl8/img_cursor.c rename to Libraries/esp_lvgl_port/images/lvgl8/img_cursor.c diff --git a/libs/esp_lvgl_port/images/lvgl9/img_cursor.c b/Libraries/esp_lvgl_port/images/lvgl9/img_cursor.c similarity index 100% rename from libs/esp_lvgl_port/images/lvgl9/img_cursor.c rename to Libraries/esp_lvgl_port/images/lvgl9/img_cursor.c diff --git a/libs/esp_lvgl_port/include/esp_lvgl_port.h b/Libraries/esp_lvgl_port/include/esp_lvgl_port.h similarity index 100% rename from libs/esp_lvgl_port/include/esp_lvgl_port.h rename to Libraries/esp_lvgl_port/include/esp_lvgl_port.h diff --git a/libs/esp_lvgl_port/include/esp_lvgl_port_button.h b/Libraries/esp_lvgl_port/include/esp_lvgl_port_button.h similarity index 100% rename from libs/esp_lvgl_port/include/esp_lvgl_port_button.h rename to Libraries/esp_lvgl_port/include/esp_lvgl_port_button.h diff --git a/libs/esp_lvgl_port/include/esp_lvgl_port_compatibility.h b/Libraries/esp_lvgl_port/include/esp_lvgl_port_compatibility.h similarity index 100% rename from libs/esp_lvgl_port/include/esp_lvgl_port_compatibility.h rename to Libraries/esp_lvgl_port/include/esp_lvgl_port_compatibility.h diff --git a/libs/esp_lvgl_port/include/esp_lvgl_port_disp.h b/Libraries/esp_lvgl_port/include/esp_lvgl_port_disp.h similarity index 100% rename from libs/esp_lvgl_port/include/esp_lvgl_port_disp.h rename to Libraries/esp_lvgl_port/include/esp_lvgl_port_disp.h diff --git a/libs/esp_lvgl_port/include/esp_lvgl_port_knob.h b/Libraries/esp_lvgl_port/include/esp_lvgl_port_knob.h similarity index 100% rename from libs/esp_lvgl_port/include/esp_lvgl_port_knob.h rename to Libraries/esp_lvgl_port/include/esp_lvgl_port_knob.h diff --git a/libs/esp_lvgl_port/include/esp_lvgl_port_touch.h b/Libraries/esp_lvgl_port/include/esp_lvgl_port_touch.h similarity index 100% rename from libs/esp_lvgl_port/include/esp_lvgl_port_touch.h rename to Libraries/esp_lvgl_port/include/esp_lvgl_port_touch.h diff --git a/libs/esp_lvgl_port/include/esp_lvgl_port_usbhid.h b/Libraries/esp_lvgl_port/include/esp_lvgl_port_usbhid.h similarity index 100% rename from libs/esp_lvgl_port/include/esp_lvgl_port_usbhid.h rename to Libraries/esp_lvgl_port/include/esp_lvgl_port_usbhid.h diff --git a/libs/esp_lvgl_port/license.txt b/Libraries/esp_lvgl_port/license.txt similarity index 100% rename from libs/esp_lvgl_port/license.txt rename to Libraries/esp_lvgl_port/license.txt diff --git a/libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c b/Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c rename to Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c diff --git a/libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_button.c b/Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_button.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_button.c rename to Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_button.c diff --git a/libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_disp.c b/Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_disp.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_disp.c rename to Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_disp.c diff --git a/libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_knob.c b/Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_knob.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_knob.c rename to Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_knob.c diff --git a/libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_touch.c b/Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_touch.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_touch.c rename to Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_touch.c diff --git a/libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_usbhid.c b/Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_usbhid.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl8/esp_lvgl_port_usbhid.c rename to Libraries/esp_lvgl_port/src/lvgl8/esp_lvgl_port_usbhid.c diff --git a/libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port.c b/Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port.c rename to Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port.c diff --git a/libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_button.c b/Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_button.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_button.c rename to Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_button.c diff --git a/libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c b/Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c rename to Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c diff --git a/libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_knob.c b/Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_knob.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_knob.c rename to Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_knob.c diff --git a/libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_touch.c b/Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_touch.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_touch.c rename to Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_touch.c diff --git a/libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_usbhid.c b/Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_usbhid.c similarity index 100% rename from libs/esp_lvgl_port/src/lvgl9/esp_lvgl_port_usbhid.c rename to Libraries/esp_lvgl_port/src/lvgl9/esp_lvgl_port_usbhid.c diff --git a/libs/lv_screenshot/CMakeLists.txt b/Libraries/lv_screenshot/CMakeLists.txt similarity index 88% rename from libs/lv_screenshot/CMakeLists.txt rename to Libraries/lv_screenshot/CMakeLists.txt index ab5d9524..8ca85ee8 100644 --- a/libs/lv_screenshot/CMakeLists.txt +++ b/Libraries/lv_screenshot/CMakeLists.txt @@ -4,8 +4,8 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -file(GLOB SOURCES "src/*.c") -file(GLOB HEADERS "src/*.h") +file(GLOB SOURCES "src/*.c*") +file(GLOB HEADERS "src/*.h*") add_library(lv_screenshot STATIC) diff --git a/libs/lv_screenshot/LICENSE-original b/Libraries/lv_screenshot/LICENSE-original similarity index 100% rename from libs/lv_screenshot/LICENSE-original rename to Libraries/lv_screenshot/LICENSE-original diff --git a/libs/lv_screenshot/README.md b/Libraries/lv_screenshot/README.md similarity index 100% rename from libs/lv_screenshot/README.md rename to Libraries/lv_screenshot/README.md diff --git a/libs/lv_screenshot/private/save_bmp.h b/Libraries/lv_screenshot/private/save_bmp.h similarity index 100% rename from libs/lv_screenshot/private/save_bmp.h rename to Libraries/lv_screenshot/private/save_bmp.h diff --git a/libs/lv_screenshot/private/save_png.h b/Libraries/lv_screenshot/private/save_png.h similarity index 100% rename from libs/lv_screenshot/private/save_png.h rename to Libraries/lv_screenshot/private/save_png.h diff --git a/libs/lv_screenshot/src/lv_screenshot.c b/Libraries/lv_screenshot/src/lv_screenshot.c similarity index 100% rename from libs/lv_screenshot/src/lv_screenshot.c rename to Libraries/lv_screenshot/src/lv_screenshot.c diff --git a/libs/lv_screenshot/src/lv_screenshot.h b/Libraries/lv_screenshot/src/lv_screenshot.h similarity index 100% rename from libs/lv_screenshot/src/lv_screenshot.h rename to Libraries/lv_screenshot/src/lv_screenshot.h diff --git a/libs/lv_screenshot/src/save_bmp.c b/Libraries/lv_screenshot/src/save_bmp.c similarity index 100% rename from libs/lv_screenshot/src/save_bmp.c rename to Libraries/lv_screenshot/src/save_bmp.c diff --git a/libs/lv_screenshot/src/save_png.c b/Libraries/lv_screenshot/src/save_png.c similarity index 100% rename from libs/lv_screenshot/src/save_png.c rename to Libraries/lv_screenshot/src/save_png.c diff --git a/libs/lvgl b/Libraries/lvgl similarity index 100% rename from libs/lvgl rename to Libraries/lvgl diff --git a/libs/mbedtls b/Libraries/mbedtls similarity index 100% rename from libs/mbedtls rename to Libraries/mbedtls diff --git a/README.md b/README.md index fab5561f..600a1102 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,21 @@ Tactility is a front-end application platform for ESP32. It is mainly intended for touchscreen devices. It provides an application framework that is based on code from the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware/) project. -![photo of devices running Tactility](docs/pics/tactility-devices.webp) +![photo of devices running Tactility](Documentation/pics/tactility-devices.webp) **Status: Alpha** A modern desktop with built-in apps: -![screenshot of desktop app](docs/pics/screenshot-desktop.png) ![screenshot of settings app](docs/pics/screenshot-settings.png) +![screenshot of desktop app](Documentation/pics/screenshot-desktop.png) ![screenshot of settings app](Documentation/pics/screenshot-settings.png) Configure your Wi-Fi without having to hard-code credentials: -![screenshot of wifi management app](docs/pics/screenshot-wifi_manage.png) ![screenshot of wifi app to connect to wifi](docs/pics/screenshot-wifi_connect.png) +![screenshot of wifi management app](Documentation/pics/screenshot-wifi_manage.png) ![screenshot of wifi app to connect to wifi](Documentation/pics/screenshot-wifi_connect.png) And much more! -![screenshot of GPIO app](docs/pics/screenshot-gpio.png) ![screenshot of files app](docs/pics/screenshot-files.png) +![screenshot of GPIO app](Documentation/pics/screenshot-gpio.png) ![screenshot of files app](Documentation/pics/screenshot-files.png) Play with the built-in apps or build your own! Use one of the supported devices or set up the drivers for your own hardware platform. @@ -35,16 +35,16 @@ Requirements: ## Making apps is easy! The app manifest provides basic information like the application name. -It also describes the [app lifecycle](docs/app-lifecycle.md): +It also describes the [app lifecycle](Documentation/app-lifecycle.md): It tells Tactility which functions need to be called when the app is started/shown/etc. -UI is created with [lvgl](https://github.com/lvgl/lvgl) which has lots of [widgets](https://docs.lvgl.io/8.3/widgets/index.html)! -Creating a touch-capable UI is [easy](https://docs.lvgl.io/8.3/get-started/quick-overview.html) and doesn't require your own render loop! +UI is created with [lvgl](https://github.com/lvgl/lvgl) which has lots of [widgets](https://docs.lvgl.io/9.0/widgets/index.html)! +Creating a touch-capable UI is [easy](https://docs.lvgl.io/9.0/get-started/quick-overview.html) and doesn't require your own render loop! -```c +```C++ static void app_show(App app, lv_obj_t* parent) { // Default toolbar with app name and close button - lv_obj_t* toolbar = tt_toolbar_create_for_app(parent, app); + lv_obj_t* toolbar = tt::toolbar_create_for_app(parent, app); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); // Label widget @@ -54,19 +54,19 @@ static void app_show(App app, lv_obj_t* parent) { // Widgets are auto-removed from the parent when the app is closed } -const AppManifest hello_world_app = { - .id = "helloworld", // Used to identify and start an app +extern const AppManifest manifest = { + .id = "HelloWorld", // Used to identify and start an app .name = "Hello World", // Shown on the desktop and app's toolbar .icon = NULL, .type = AppTypeUser, - .on_start = NULL, - .on_stop = NULL, + .on_start = nullptr, + .on_stop = nullptr, .on_show = &app_show, // A minimal setup sets the on_show() function - .on_hide = NULL + .on_hide = nullptr }; ``` -![hello world app screenshot](docs/pics/screenshot-helloworld.png) +![hello world app screenshot](Documentation/pics/screenshot-helloworld.png) ## Supported Hardware @@ -108,7 +108,7 @@ git clone --recurse-submodules -j8 https://github.com/ByteWelder/Tactility.git ### Build environment setup -Ensure you have [esp-idf 5.2](https://docs.espressif.com/projects/esp-idf/en/v5.2/esp32/get-started/index.html) installed, then select the correct device: +Ensure you have [esp-idf 5.3](https://docs.espressif.com/projects/esp-idf/en/release-v5.3/esp32/get-started/index.html) installed, then select the correct device: Copy the `sdkconfig.board.YOUR_BOARD` into `sdkconfig`. Use `sdkconfig.defaults` if you are setting up a custom board. @@ -124,17 +124,17 @@ The build scripts will detect if ESP-IDF is available. They will adapter if you ### Development -Take a look at the [App Lifecycle](docs/app-lifecycle.md) or the [dependency diagram](docs/project-structure.puml) (this uses [PlantUML](https://plantuml.com)). +Take a look at the [App Lifecycle](Documentation/app-lifecycle.md) or the [dependency diagram](Documentation/project-structure.puml) (this uses [PlantUML](https://plantuml.com)). Directories explained: -- `app-esp`: The ESP32 application example -- `app-sim`: The PC/simulator application example -- `boards`: Contains ESP modules with drivers -- `tactility`: The main application platform code ([src/](./tactility/src)) -- `tactility-headless`: Service framework and default services. -- `tactility-core`: Core functionality regarding threads, stdlib, etc. ([src/](./tactility-core/src)) -- `libs`: Contains a mix of regular libraries and ESP modules +- `AppEsp32`: The ESP32 application example +- `AppSim`: The PC/simulator application example +- `Boards`: Contains ESP modules with drivers +- `Tactility`: The main application platform code ([src/](Tactility/Source)) +- `TactilityHeadless`: Service framework and default services. +- `TactilityCore`: Core functionality regarding threads, stdlib, etc. ([src/](TactilityCore/Source)) +- `Libraries`: Contains a mix of regular libraries and ESP modules Until there is proper documentation, here are some pointers: diff --git a/Tactility/CMakeLists.txt b/Tactility/CMakeLists.txt new file mode 100644 index 00000000..065abf6c --- /dev/null +++ b/Tactility/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.16) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (DEFINED ENV{ESP_IDF_VERSION}) + file(GLOB_RECURSE SOURCE_FILES Source/*.c*) + + idf_component_register( + SRCS ${SOURCE_FILES} + INCLUDE_DIRS "Source/" + PRIV_INCLUDE_DIRS "Private/" + REQUIRES TactilityHeadless lvgl driver + ) + + target_link_libraries(${COMPONENT_LIB} + PUBLIC lv_screenshot + ) + + add_definitions(-DESP_PLATFORM) +else() + file(GLOB_RECURSE SOURCES "Source/*.c*") + file(GLOB_RECURSE HEADERS "Source/*.h*") + + add_library(Tactility OBJECT) + + target_sources(Tactility + PRIVATE ${SOURCES} + PUBLIC ${HEADERS} + ) + + include_directories( + PRIVATE Private/ + ) + + target_include_directories(Tactility + PUBLIC Source/ + ) + + add_definitions(-D_Nullable=) + add_definitions(-D_Nonnull=) + target_link_libraries(Tactility + PUBLIC TactilityHeadless + PUBLIC lvgl + PUBLIC freertos_kernel + PUBLIC lv_screenshot + ) +endif() + diff --git a/tactility/src/app_i.h b/Tactility/Private/App_i.h similarity index 68% rename from tactility/src/app_i.h rename to Tactility/Private/App_i.h index 616f04e1..6bd500e6 100644 --- a/tactility/src/app_i.h +++ b/Tactility/Private/App_i.h @@ -1,35 +1,33 @@ #pragma once -#include "app.h" +#include "App.h" -#include "app_manifest.h" -#include "mutex.h" -#include +#include "AppManifest.h" +#include "Mutex.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { -typedef struct { +class AppData { +public: Mutex* mutex; const AppManifest* manifest; - AppState state; + AppState state = AppStateInitial; /** @brief Memory marker at start of app, to detect memory leaks */ - size_t memory; - AppFlags flags; + size_t memory = 0; + AppFlags flags = { + .flags = 0 + }; /** @brief Optional parameters to start the app with * When these are stored in the app struct, the struct takes ownership. * Do not mutate after app creation. */ - Bundle _Nullable parameters; + Bundle parameters; /** @brief @brief Contextual data related to the running app's instance * The app can attach its data to this. * The lifecycle is determined by the on_start and on_stop methods in the AppManifest. * These manifest methods can optionally allocate/free data that is attached here. */ - void* _Nullable data; -} AppData; + void* _Nullable data = nullptr; +}; -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/Tactility/Private/LvglInit_i.h b/Tactility/Private/LvglInit_i.h new file mode 100644 index 00000000..792ab954 --- /dev/null +++ b/Tactility/Private/LvglInit_i.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Hal/Configuration.h" + +namespace tt { + +void lvgl_init(const hal::Configuration* config); + +} // namespace diff --git a/tactility/src/services/gui/gui_i.h b/Tactility/Private/Services/Gui/Gui_i.h similarity index 68% rename from tactility/src/services/gui/gui_i.h rename to Tactility/Private/Services/Gui/Gui_i.h index 8abb95b8..2dcc9005 100644 --- a/tactility/src/services/gui/gui_i.h +++ b/Tactility/Private/Services/Gui/Gui_i.h @@ -1,12 +1,14 @@ #pragma once -#include "gui.h" -#include "message_queue.h" -#include "mutex.h" -#include "pubsub.h" -#include "view_port.h" -#include "view_port_i.h" -#include +#include "MessageQueue.h" +#include "Mutex.h" +#include "Pubsub.h" +#include "Services/Gui/Gui.h" +#include "Services/Gui/ViewPort.h" +#include "Services/Gui/ViewPort_i.h" +#include + +namespace tt::service::gui { #define GUI_THREAD_FLAG_DRAW (1 << 0) #define GUI_THREAD_FLAG_INPUT (1 << 1) @@ -31,10 +33,12 @@ struct Gui { }; /** Update GUI, request redraw */ -void gui_request_draw(); +void request_draw(); /** Lock GUI */ -void gui_lock(); +void lock(); /** Unlock GUI */ -void gui_unlock(); +void unlock(); + +} // namespace diff --git a/tactility/src/services/gui/view_port_i.h b/Tactility/Private/Services/Gui/ViewPort_i.h similarity index 84% rename from tactility/src/services/gui/view_port_i.h rename to Tactility/Private/Services/Gui/ViewPort_i.h index f8e24742..1253f8ef 100644 --- a/tactility/src/services/gui/view_port_i.h +++ b/Tactility/Private/Services/Gui/ViewPort_i.h @@ -1,6 +1,8 @@ #pragma once -#include "view_port.h" +#include "Services/Gui/ViewPort.h" + +namespace tt::service::gui { /** Process draw call. Calls on_show callback. * To be used by GUI, called on redraw. @@ -17,3 +19,5 @@ void view_port_show(ViewPort* view_port, lv_obj_t* parent); * @param view_port */ void view_port_hide(ViewPort* view_port); + +} // namespace diff --git a/Tactility/Private/Services/Loader/Loader_i.h b/Tactility/Private/Services/Loader/Loader_i.h new file mode 100644 index 00000000..8e9e7607 --- /dev/null +++ b/Tactility/Private/Services/Loader/Loader_i.h @@ -0,0 +1,117 @@ +#pragma once + +#include "ApiLock.h" +#include "AppManifest.h" +#include "MessageQueue.h" +#include "Pubsub.h" +#include "Thread.h" +#include "Services/Gui/ViewPort.h" +#include "Services/Loader/Loader.h" + +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#else +#include "FreeRTOS.h" +#include "semphr.h" +#endif + +namespace tt::service::loader { + +#define APP_STACK_SIZE 32 + +typedef enum { + LoaderMessageTypeNone, + LoaderMessageTypeAppStart, + LoaderMessageTypeAppStop, + LoaderMessageTypeServiceStop, +} LoaderMessageType; + +class LoaderMessageAppStart { +public: + std::string id; + Bundle bundle; + + LoaderMessageAppStart() = default; + + LoaderMessageAppStart(const std::string& id, const Bundle& bundle) { + this->id = id; + this->bundle = bundle; + } +}; + +typedef struct { + LoaderStatus value; +} LoaderMessageLoaderStatusResult; + +typedef struct { + bool value; +} LoaderMessageBoolResult; + +class LoaderMessage { +public: + // This lock blocks anyone from starting an app as long + // as an app is already running via loader_start() + // This lock's lifecycle is not owned by this class. + // TODO: Convert to smart pointer + ApiLock api_lock; + LoaderMessageType type; + + struct { + union { + // TODO: Convert to smart pointer + const LoaderMessageAppStart* start; + }; + } payload; + + struct { + union { + LoaderMessageLoaderStatusResult status_value; + LoaderMessageBoolResult bool_value; + void* raw_value; + }; + } result; + + LoaderMessage() { + api_lock = nullptr; + type = LoaderMessageTypeNone; + payload = { .start = nullptr }; + result = { .raw_value = nullptr }; + } + + LoaderMessage(const LoaderMessageAppStart* start, const LoaderMessageLoaderStatusResult& statusResult) { + api_lock = nullptr; + type = LoaderMessageTypeAppStart; + payload.start = start; + result.status_value = statusResult; + } + + LoaderMessage(LoaderMessageType messageType) { + api_lock = nullptr; + type = messageType; + payload = { .start = nullptr }; + result = { .raw_value = nullptr }; + } + + void setLock(ApiLock lock) { + api_lock = lock; + } + + void cleanup() { + if (type == LoaderMessageTypeAppStart) { + delete payload.start; + } + } +}; + +struct Loader { + Thread* thread; + PubSub* pubsub_internal; + PubSub* pubsub_external; + MessageQueue queue = MessageQueue(1, sizeof(LoaderMessage)); + Mutex* mutex; + int8_t app_stack_index; + App app_stack[APP_STACK_SIZE]; +}; + +} // namespace diff --git a/Tactility/Source/App.cpp b/Tactility/Source/App.cpp new file mode 100644 index 00000000..66ae2fde --- /dev/null +++ b/Tactility/Source/App.cpp @@ -0,0 +1,102 @@ +#include "App_i.h" + +namespace tt { + +#define TAG "app" + +void AppInstance::setState(AppState newState) { + mutex.acquire(TtWaitForever); + state = newState; + mutex.release(); +} + +AppState AppInstance::getState() { + mutex.acquire(TtWaitForever); + auto result = state; + mutex.release(); + return result; +} + +/** TODO: Make this thread-safe. + * In practice, the bundle is writeable, so someone could be writing to it + * while it is being accessed from another thread. + * Consider creating MutableBundle vs Bundle. + * Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it. + */ +const AppManifest& AppInstance::getManifest() { + return manifest; +} + +AppFlags AppInstance::getFlags() { + mutex.acquire(TtWaitForever); + auto result = flags; + mutex.release(); + return result; +} + +void AppInstance::setFlags(AppFlags newFlags) { + mutex.acquire(TtWaitForever); + flags = newFlags; + mutex.release(); +} + +_Nullable void* AppInstance::getData() { + mutex.acquire(TtWaitForever); + auto result = data; + mutex.release(); + return result; +} + +void AppInstance::setData(void* newData) { + mutex.acquire(TtWaitForever); + data = newData; + mutex.release(); +} + +const Bundle& AppInstance::getParameters() { + return parameters; +} + +App tt_app_alloc(const AppManifest& manifest, const Bundle& parameters) { + auto* instance = new AppInstance(manifest, parameters); + return static_cast(instance); +} + +void tt_app_free(App app) { + auto* instance = static_cast(app); + delete instance; +} + +void tt_app_set_state(App app, AppState state) { + static_cast(app)->setState(state); +} + +AppState tt_app_get_state(App app) { + return static_cast(app)->getState(); +} + +const AppManifest& tt_app_get_manifest(App app) { + return static_cast(app)->getManifest(); +} + +AppFlags tt_app_get_flags(App app) { + return static_cast(app)->getFlags(); +} + +void tt_app_set_flags(App app, AppFlags flags) { + return static_cast(app)->setFlags(flags); +} + +void* tt_app_get_data(App app) { + return static_cast(app)->getData(); +} + +void tt_app_set_data(App app, void* data) { + return static_cast(app)->setData(data); +} + +const Bundle& tt_app_get_parameters(App app) { + return static_cast(app)->getParameters(); +} + +} // namespace diff --git a/Tactility/Source/App.h b/Tactility/Source/App.h new file mode 100644 index 00000000..731a363b --- /dev/null +++ b/Tactility/Source/App.h @@ -0,0 +1,94 @@ +#pragma once + +#include "AppManifest.h" +#include "Bundle.h" +#include "Mutex.h" + +namespace tt { + +typedef enum { + AppStateInitial, // App is being activated in loader + AppStateStarted, // App is in memory + AppStateShowing, // App view is created + AppStateHiding, // App view is destroyed + AppStateStopped // App is not in memory +} AppState; + +typedef union { + struct { + bool show_statusbar : 1; + }; + unsigned char flags; +} AppFlags; + + +class AppInstance { + Mutex mutex = Mutex(MutexTypeNormal); + const AppManifest& manifest; + AppState state = AppStateInitial; + AppFlags flags = { .show_statusbar = true }; + /** @brief Optional parameters to start the app with + * When these are stored in the app struct, the struct takes ownership. + * Do not mutate after app creation. + */ + tt::Bundle parameters; + /** @brief @brief Contextual data related to the running app's instance + * The app can attach its data to this. + * The lifecycle is determined by the on_start and on_stop methods in the AppManifest. + * These manifest methods can optionally allocate/free data that is attached here. + */ + void* _Nullable data = nullptr; +public: + AppInstance(const AppManifest& manifest) : + manifest(manifest) {} + + AppInstance(const AppManifest& manifest, const Bundle& parameters) : + manifest(manifest), + parameters(parameters) {} + + void setState(AppState state); + AppState getState(); + + const AppManifest& getManifest(); + + AppFlags getFlags(); + void setFlags(AppFlags flags); + + _Nullable void* getData(); + void setData(void* data); + + const Bundle& getParameters(); +}; + +/** @brief Create an app + * @param manifest + * @param parameters optional bundle. memory ownership is transferred to App + * @return + */ +[[deprecated("use class")]] +App tt_app_alloc(const AppManifest& manifest, const Bundle& parameters); +[[deprecated("use class")]] +void tt_app_free(App app); + +[[deprecated("use class")]] +void tt_app_set_state(App app, AppState state); +[[deprecated("use class")]] +AppState tt_app_get_state(App app); + +[[deprecated("use class")]] +const AppManifest& tt_app_get_manifest(App app); + +[[deprecated("use class")]] +AppFlags tt_app_get_flags(App app); +[[deprecated("use class")]] +void tt_app_set_flags(App app, AppFlags flags); + +[[deprecated("use class")]] +void* _Nullable tt_app_get_data(App app); +[[deprecated("use class")]] +void tt_app_set_data(App app, void* data); + +[[deprecated("use class")]] +const Bundle& tt_app_get_parameters(App app); + +} // namespace diff --git a/tactility/src/app_manifest.h b/Tactility/Source/AppManifest.h similarity index 76% rename from tactility/src/app_manifest.h rename to Tactility/Source/AppManifest.h index c4e7701a..8663bba8 100644 --- a/tactility/src/app_manifest.h +++ b/Tactility/Source/AppManifest.h @@ -1,13 +1,13 @@ #pragma once -#include "core_defines.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include +#include "CoreDefines.h" // Forward declarations typedef struct _lv_obj_t lv_obj_t; + +namespace tt { + typedef void* App; typedef enum { @@ -28,48 +28,46 @@ typedef void (*AppOnStop)(App app); typedef void (*AppOnShow)(App app, lv_obj_t* parent); typedef void (*AppOnHide)(App app); -typedef struct { +typedef struct AppManifest { /** * The identifier by which the app is launched by the system and other apps. */ - const char* id; + std::string id; /** * The user-readable name of the app. Used in UI. */ - const char* name; + std::string name; /** * Optional icon. */ - const char* _Nullable icon; + std::string icon = {}; /** * App type affects launch behaviour. */ - const AppType type; + const AppType type = AppTypeUser; /** * Non-blocking method to call when app is started. */ - const AppOnStart _Nullable on_start; + const AppOnStart on_start = nullptr; /** * Non-blocking method to call when app is stopped. */ - const AppOnStop _Nullable on_stop; + const AppOnStop _Nullable on_stop = nullptr; /** * Non-blocking method to create the GUI */ - const AppOnShow _Nullable on_show; + const AppOnShow _Nullable on_show = nullptr; /** * Non-blocking method, called before gui is destroyed */ - const AppOnHide _Nullable on_hide; + const AppOnHide _Nullable on_hide = nullptr; } AppManifest; -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/Tactility/Source/AppManifestRegistry.cpp b/Tactility/Source/AppManifestRegistry.cpp new file mode 100644 index 00000000..b9ab56f2 --- /dev/null +++ b/Tactility/Source/AppManifestRegistry.cpp @@ -0,0 +1,63 @@ +#include "AppManifestRegistry.h" +#include "Mutex.h" +#include "TactilityCore.h" +#include + +#define TAG "app_registry" + +namespace tt { + +typedef std::unordered_map AppManifestMap; + +static AppManifestMap app_manifest_map; +static Mutex* hash_mutex = nullptr; + +static void app_registry_lock() { + tt_assert(hash_mutex != nullptr); + tt_mutex_acquire(hash_mutex, TtWaitForever); +} + +static void app_registry_unlock() { + tt_assert(hash_mutex != nullptr); + tt_mutex_release(hash_mutex); +} + +void app_manifest_registry_init() { + tt_assert(hash_mutex == nullptr); + hash_mutex = tt_mutex_alloc(MutexTypeNormal); +} +void app_manifest_registry_add(const AppManifest* manifest) { + TT_LOG_I(TAG, "adding %s", manifest->id.c_str()); + + app_registry_lock(); + app_manifest_map[manifest->id] = manifest; + app_registry_unlock(); +} + +_Nullable const AppManifest * app_manifest_registry_find_by_id(const std::string& id) { + app_registry_lock(); + auto iterator = app_manifest_map.find(id); + _Nullable const AppManifest* result = iterator != app_manifest_map.end() ? iterator->second : nullptr; + app_registry_unlock(); + return result; +} + +void app_manifest_registry_for_each_of_type(AppType type, void* _Nullable context, AppManifestCallback callback) { + app_registry_lock(); + for (auto& it : app_manifest_map) { + if (it.second->type == type) { + callback(it.second, context); + } + } + app_registry_unlock(); +} + +void app_manifest_registry_for_each(AppManifestCallback callback, void* _Nullable context) { + app_registry_lock(); + for (auto& it : app_manifest_map) { + callback(it.second, context); + } + app_registry_unlock(); +} + +} // namespace diff --git a/Tactility/Source/AppManifestRegistry.h b/Tactility/Source/AppManifestRegistry.h new file mode 100644 index 00000000..9864552a --- /dev/null +++ b/Tactility/Source/AppManifestRegistry.h @@ -0,0 +1,17 @@ +#pragma once + +#include "AppManifest.h" +#include + +namespace tt { + +typedef void (*AppManifestCallback)(const AppManifest*, void* context); + +void app_manifest_registry_init(); +void app_manifest_registry_add(const AppManifest* manifest); +void app_manifest_registry_remove(const AppManifest* manifest); +const AppManifest _Nullable* app_manifest_registry_find_by_id(const std::string& id); +void app_manifest_registry_for_each(AppManifestCallback callback, void* _Nullable context); +void app_manifest_registry_for_each_of_type(AppType type, void* _Nullable context, AppManifestCallback callback); + +} // namespace diff --git a/tactility/src/apps/desktop/desktop.c b/Tactility/Source/Apps/Desktop/Desktop.cpp similarity index 53% rename from tactility/src/apps/desktop/desktop.c rename to Tactility/Source/Apps/Desktop/Desktop.cpp index ee502abb..553892d7 100644 --- a/tactility/src/apps/desktop/desktop.c +++ b/Tactility/Source/Apps/Desktop/Desktop.cpp @@ -1,22 +1,24 @@ -#include "app_manifest_registry.h" -#include "assets.h" -#include "check.h" +#include "AppManifestRegistry.h" +#include "Assets.h" +#include "Check.h" #include "lvgl.h" -#include "services/loader/loader.h" +#include "Services/Loader/Loader.h" + +namespace tt::app::desktop { static void on_app_pressed(lv_event_t* e) { lv_event_code_t code = lv_event_get_code(e); if (code == LV_EVENT_CLICKED) { - const AppManifest* manifest = lv_event_get_user_data(e); - loader_start_app(manifest->id, false, NULL); + const auto* manifest = static_cast(lv_event_get_user_data(e)); + service::loader::start_app(manifest->id, false, Bundle()); } } static void create_app_widget(const AppManifest* manifest, void* parent) { tt_check(parent); - lv_obj_t* list = (lv_obj_t*)parent; - const char* icon = manifest->icon ?: TT_ASSETS_APP_ICON_FALLBACK; - lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name); + auto* list = static_cast(parent); + const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK; + lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str()); lv_obj_add_event_cb(btn, &on_app_pressed, LV_EVENT_CLICKED, (void*)manifest); } @@ -26,18 +28,16 @@ static void desktop_show(TT_UNUSED App app, lv_obj_t* parent) { lv_obj_center(list); lv_list_add_text(list, "User"); - tt_app_manifest_registry_for_each_of_type(AppTypeUser, list, create_app_widget); + app_manifest_registry_for_each_of_type(AppTypeUser, list, create_app_widget); lv_list_add_text(list, "System"); - tt_app_manifest_registry_for_each_of_type(AppTypeSystem, list, create_app_widget); + app_manifest_registry_for_each_of_type(AppTypeSystem, list, create_app_widget); } -const AppManifest desktop_app = { +extern const AppManifest manifest = { .id = "desktop", .name = "Desktop", - .icon = NULL, .type = AppTypeDesktop, - .on_start = NULL, - .on_stop = NULL, .on_show = &desktop_show, - .on_hide = NULL }; + +} // namespace diff --git a/tactility/src/apps/settings/display/display.c b/Tactility/Source/Apps/Display/Display.cpp similarity index 71% rename from tactility/src/apps/settings/display/display.c rename to Tactility/Source/Apps/Display/Display.cpp index fe5a2521..946e9118 100644 --- a/tactility/src/apps/settings/display/display.c +++ b/Tactility/Source/Apps/Display/Display.cpp @@ -1,9 +1,11 @@ -#include "app.h" -#include "assets.h" +#include "App.h" +#include "Assets.h" +#include "DisplayPreferences.h" +#include "Tactility.h" #include "lvgl.h" -#include "preferences.h" -#include "tactility.h" -#include "ui/toolbar.h" +#include "Ui/Toolbar.h" + +namespace tt::app::settings::display { #define TAG "display" @@ -11,11 +13,11 @@ static bool backlight_duty_set = false; static uint8_t backlight_duty = 255; static void slider_event_cb(lv_event_t* event) { - lv_obj_t* slider = lv_event_get_target(event); - const Config* config = tt_get_config(); - SetBacklightDuty set_backlight_duty = config->hardware->display.set_backlight_duty; + auto* slider = static_cast(lv_event_get_target(event)); + const Configuration* config = get_config(); + hal::SetBacklightDuty set_backlight_duty = config->hardware->display.set_backlight_duty; - if (set_backlight_duty != NULL) { + if (set_backlight_duty != nullptr) { int32_t slider_value = lv_slider_get_value(slider); backlight_duty = (uint8_t)slider_value; @@ -30,7 +32,7 @@ static void slider_event_cb(lv_event_t* event) { #define ORIENTATION_PORTRAIT_LEFT 2 #define ORIENTATION_PORTRAIT_RIGHT 3 -static int orientation_setting_to_display_orientation(int setting) { +static lv_display_rotation_t orientation_setting_to_display_rotation(uint32_t setting) { if (setting == ORIENTATION_LANDSCAPE_FLIPPED) { return LV_DISPLAY_ROTATION_180; } else if (setting == ORIENTATION_PORTRAIT_LEFT) { @@ -42,7 +44,7 @@ static int orientation_setting_to_display_orientation(int setting) { } } -static int display_orientation_to_orientation_setting(int orientation) { +static uint32_t display_rotation_to_orientation_setting(lv_display_rotation_t orientation) { if (orientation == LV_DISPLAY_ROTATION_90) { return ORIENTATION_PORTRAIT_RIGHT; } else if (orientation == LV_DISPLAY_ROTATION_180) { @@ -55,20 +57,20 @@ static int display_orientation_to_orientation_setting(int orientation) { } static void on_orientation_set(lv_event_t* event) { - lv_obj_t* dropdown = lv_event_get_target(event); - int selected = lv_dropdown_get_selected(dropdown); - TT_LOG_I(TAG, "Selected %d", selected); - int rotation = orientation_setting_to_display_orientation(selected); + auto* dropdown = static_cast(lv_event_get_target(event)); + uint32_t selected = lv_dropdown_get_selected(dropdown); + TT_LOG_I(TAG, "Selected %ld", selected); + lv_display_rotation_t rotation = orientation_setting_to_display_rotation(selected); if (lv_display_get_rotation(lv_display_get_default()) != rotation) { lv_display_set_rotation(lv_display_get_default(), rotation); - tt_preferences()->put_int32("display", "rotation", rotation); + preferences_set_rotation(rotation); } } static void app_show(App app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - tt_toolbar_create_for_app(parent, app); + lvgl::toolbar_create_for_app(parent, app); lv_obj_t* wrapper = lv_obj_create(parent); lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); @@ -87,14 +89,13 @@ static void app_show(App app, lv_obj_t* parent) { lv_obj_add_event_cb(brightness_slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_set_pos(brightness_slider, 0, 30); - const Config* config = tt_get_config(); - SetBacklightDuty set_backlight_duty = config->hardware->display.set_backlight_duty; - if (set_backlight_duty == NULL) { + const Configuration* config = get_config(); + hal::SetBacklightDuty set_backlight_duty = config->hardware->display.set_backlight_duty; + if (set_backlight_duty == nullptr) { lv_slider_set_value(brightness_slider, 255, LV_ANIM_OFF); lv_obj_add_state(brightness_slider, LV_STATE_DISABLED); } else { - int32_t value = 255; - tt_preferences()->opt_int32("display", "backlight_duty", &value); + uint8_t value = preferences_get_backlight_duty(); lv_slider_set_value(brightness_slider, value, LV_ANIM_OFF); } @@ -106,8 +107,8 @@ static void app_show(App app, lv_obj_t* parent) { lv_obj_t* orientation_dropdown = lv_dropdown_create(orientation_wrapper); lv_dropdown_set_options(orientation_dropdown, "Landscape\nLandscape (flipped)\nPortrait Left\nPortrait Right"); lv_obj_align(orientation_dropdown, LV_ALIGN_RIGHT_MID, 0, 0); - lv_obj_add_event_cb(orientation_dropdown, on_orientation_set, LV_EVENT_VALUE_CHANGED, NULL); - int orientation_selected = display_orientation_to_orientation_setting( + lv_obj_add_event_cb(orientation_dropdown, on_orientation_set, LV_EVENT_VALUE_CHANGED, nullptr); + uint32_t orientation_selected = display_rotation_to_orientation_setting( lv_display_get_rotation(lv_display_get_default()) ); lv_dropdown_set_selected(orientation_dropdown, orientation_selected); @@ -115,17 +116,19 @@ static void app_show(App app, lv_obj_t* parent) { static void app_hide(TT_UNUSED App app) { if (backlight_duty_set) { - tt_preferences()->put_int32("display", "backlight_duty", backlight_duty); + preferences_set_backlight_duty(backlight_duty); } } -const AppManifest display_app = { +extern const AppManifest manifest = { .id = "display", .name = "Display", .icon = TT_ASSETS_APP_ICON_DISPLAY_SETTINGS, .type = AppTypeSettings, - .on_start = NULL, - .on_stop = NULL, + .on_start = nullptr, + .on_stop = nullptr, .on_show = &app_show, .on_hide = &app_hide }; + +} // namespace diff --git a/Tactility/Source/Apps/Display/DisplayPreferences.cpp b/Tactility/Source/Apps/Display/DisplayPreferences.cpp new file mode 100644 index 00000000..e0061ca7 --- /dev/null +++ b/Tactility/Source/Apps/Display/DisplayPreferences.cpp @@ -0,0 +1,37 @@ +#include "DisplayPreferences.h" +#include "Preferences.h" + +namespace tt::app::settings::display { + +tt::Preferences preferences("display"); + +#define BACKLIGHT_DUTY_KEY "backlight_duty" +#define ROTATION_KEY "rotation" + +void preferences_set_backlight_duty(uint8_t value) { + preferences.putInt32(BACKLIGHT_DUTY_KEY, (int32_t)value); +} + +uint8_t preferences_get_backlight_duty() { + int32_t result; + if (preferences.optInt32(BACKLIGHT_DUTY_KEY, result)) { + return (uint8_t)(result % 255); + } else { + return 200; + } +} + +void preferences_set_rotation(lv_display_rotation_t rotation) { + preferences.putInt32(ROTATION_KEY, (int32_t)rotation); +} + +lv_display_rotation_t preferences_get_rotation() { + int32_t rotation; + if (preferences.optInt32(ROTATION_KEY, rotation)) { + return (lv_display_rotation_t)rotation; + } else { + return LV_DISPLAY_ROTATION_0; + } +} + +} // namespace diff --git a/Tactility/Source/Apps/Display/DisplayPreferences.h b/Tactility/Source/Apps/Display/DisplayPreferences.h new file mode 100644 index 00000000..3155fa9c --- /dev/null +++ b/Tactility/Source/Apps/Display/DisplayPreferences.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace tt::app::settings::display { + +void preferences_set_backlight_duty(uint8_t value); +uint8_t preferences_get_backlight_duty(); +void preferences_set_rotation(lv_display_rotation_t rotation); +lv_display_rotation_t preferences_get_rotation(); + +} // namespace diff --git a/tactility/src/apps/files/file_utils.c b/Tactility/Source/Apps/Files/FileUtils.cpp similarity index 68% rename from tactility/src/apps/files/file_utils.c rename to Tactility/Source/Apps/Files/FileUtils.cpp index 99f488fb..b30ffee6 100644 --- a/tactility/src/apps/files/file_utils.c +++ b/Tactility/Source/Apps/Files/FileUtils.cpp @@ -1,15 +1,19 @@ -#include "file_utils.h" -#include "tactility_core.h" +#include "FileUtils.h" +#include "TactilityCore.h" +#include +#include + +namespace tt::app::files { #define TAG "file_utils" #define SCANDIR_LIMIT 128 -int tt_dirent_filter_dot_entries(const struct dirent* entry) { +int dirent_filter_dot_entries(const struct dirent* entry) { return (strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, ".") == 0) ? -1 : 0; } -int tt_dirent_sort_alpha_and_type(const struct dirent** left, const struct dirent** right) { +int dirent_sort_alpha_and_type(const struct dirent** left, const struct dirent** right) { bool left_is_dir = (*left)->d_type == TT_DT_DIR; bool right_is_dir = (*right)->d_type == TT_DT_DIR; if (left_is_dir == right_is_dir) { @@ -19,29 +23,29 @@ int tt_dirent_sort_alpha_and_type(const struct dirent** left, const struct diren } } -int tt_dirent_sort_alpha(const struct dirent** left, const struct dirent** right) { +int dirent_sort_alpha(const struct dirent** left, const struct dirent** right) { return strcmp((*left)->d_name, (*right)->d_name); } -int tt_scandir( +int scandir( const char* path, struct dirent*** output, ScandirFilter _Nullable filter, ScandirSort _Nullable sort ) { DIR* dir = opendir(path); - if (dir == NULL) { + if (dir == nullptr) { return -1; } - *output = malloc(sizeof(void*) * SCANDIR_LIMIT); + *output = static_cast(malloc(sizeof(void*) * SCANDIR_LIMIT)); struct dirent** dirent_array = *output; int dirent_buffer_index = 0; struct dirent* current_entry; - while ((current_entry = readdir(dir)) != NULL) { + while ((current_entry = readdir(dir)) != nullptr) { if (filter(current_entry) == 0) { - dirent_array[dirent_buffer_index] = malloc(sizeof(struct dirent)); + dirent_array[dirent_buffer_index] = static_cast(malloc(sizeof(struct dirent))); memcpy(dirent_array[dirent_buffer_index], current_entry, sizeof(struct dirent)); dirent_buffer_index++; @@ -54,7 +58,7 @@ int tt_scandir( if (dirent_buffer_index == 0) { free(*output); - *output = NULL; + *output = nullptr; } else { if (sort) { qsort(dirent_array, dirent_buffer_index, sizeof(struct dirent*), (__compar_fn_t)sort); @@ -64,3 +68,5 @@ int tt_scandir( closedir(dir); return dirent_buffer_index; }; + +} diff --git a/tactility/src/apps/files/file_utils.h b/Tactility/Source/Apps/Files/FileUtils.h similarity index 80% rename from tactility/src/apps/files/file_utils.h rename to Tactility/Source/Apps/Files/FileUtils.h index f724a719..4445af6a 100644 --- a/tactility/src/apps/files/file_utils.h +++ b/Tactility/Source/Apps/Files/FileUtils.h @@ -2,7 +2,9 @@ #include -/** File types for `dirent`'s `d_type`. */ +namespace tt::app::files { + + /** File types for `dirent`'s `d_type`. */ enum { TT_DT_UNKNOWN = 0, #define TT_DT_UNKNOWN TT_DT_UNKNOWN @@ -28,21 +30,17 @@ typedef int (*ScandirFilter)(const struct dirent*); typedef int (*ScandirSort)(const struct dirent**, const struct dirent**); -#ifdef __cplusplus -extern "C" { -#endif - /** * Alphabetic sorting function for tt_scandir() * @param left left-hand side part for comparison * @param right right-hand side part for comparison * @return 0, -1 or 1 */ -int tt_dirent_sort_alpha(const struct dirent** left, const struct dirent** right); +int dirent_sort_alpha(const struct dirent** left, const struct dirent** right); -int tt_dirent_sort_alpha_and_type(const struct dirent** left, const struct dirent** right); +int dirent_sort_alpha_and_type(const struct dirent** left, const struct dirent** right); -int tt_dirent_filter_dot_entries(const struct dirent* entry); +int dirent_filter_dot_entries(const struct dirent* entry); /** * A scandir()-like implementation that works on ESP32. @@ -56,13 +54,11 @@ int tt_dirent_filter_dot_entries(const struct dirent* entry); * @param[in] sort an optional sorting function * @return the amount of items that were stored in "output" or -1 when an error occurred */ -int tt_scandir( +int scandir( const char* path, struct dirent*** output, ScandirFilter _Nullable filter, ScandirSort _Nullable sort ); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/tactility/src/apps/files/files.c b/Tactility/Source/Apps/Files/Files.cpp similarity index 69% rename from tactility/src/apps/files/files.c rename to Tactility/Source/Apps/Files/Files.cpp index d6705169..773e781f 100644 --- a/tactility/src/apps/files/files.c +++ b/Tactility/Source/Apps/Files/Files.cpp @@ -1,18 +1,20 @@ -#include "files_data.h" +#include "FilesData.h" -#include "app.h" -#include "apps/image_viewer/image_viewer.h" -#include "apps/text_viewer/text_viewer.h" -#include "assets.h" -#include "check.h" -#include "file_utils.h" +#include "App.h" +#include "Assets.h" +#include "Check.h" +#include "FileUtils.h" +#include "StringUtils.h" +#include "Apps/ImageViewer/ImageViewer.h" +#include "Apps/TextViewer/TextViewer.h" #include "lvgl.h" -#include "services/loader/loader.h" -#include "string_utils.h" -#include "ui/toolbar.h" +#include "Services/Loader/Loader.h" +#include "Ui/Toolbar.h" #include #include +namespace tt::app::files { + #define TAG "files_app" /** @@ -55,36 +57,36 @@ static bool is_supported_text_file(const char* filename) { // region Views -static void update_views(FilesData* data); +static void update_views(Data* data); static void on_navigate_up_pressed(lv_event_t* event) { - FilesData* files_data = (FilesData*)lv_event_get_user_data(event); + auto* files_data = (Data*)lv_event_get_user_data(event); if (strcmp(files_data->current_path, "/") != 0) { TT_LOG_I(TAG, "Navigating upwards"); char new_absolute_path[MAX_PATH_LENGTH]; - if (tt_string_get_path_parent(files_data->current_path, new_absolute_path)) { - files_data_set_entries_for_path(files_data, new_absolute_path); + if (string_get_path_parent(files_data->current_path, new_absolute_path)) { + data_set_entries_for_path(files_data, new_absolute_path); } } update_views(files_data); } static void on_exit_app_pressed(TT_UNUSED lv_event_t* event) { - loader_stop_app(); + service::loader::stop_app(); } static void view_file(const char* path, const char* filename) { size_t path_len = strlen(path); size_t filename_len = strlen(filename); - char* filepath = malloc(path_len + filename_len + 2); + char* filepath = static_cast(malloc(path_len + filename_len + 2)); sprintf(filepath, "%s/%s", path, filename); // For PC we need to make the path relative to the current work directory, // because that's how LVGL maps its 'drive letter' to the file system. char* processed_filepath; - if (tt_get_platform() == PlatformPc) { + if (get_platform() == PlatformPc) { char cwd[PATH_MAX]; - if (getcwd(cwd, sizeof(cwd)) == NULL) { + if (getcwd(cwd, sizeof(cwd)) == nullptr) { TT_LOG_E(TAG, "Failed to get current working directory"); return; } @@ -101,18 +103,18 @@ static void view_file(const char* path, const char* filename) { TT_LOG_I(TAG, "Clicked %s", filepath); if (is_supported_image_file(filename)) { - Bundle bundle = tt_bundle_alloc(); - tt_bundle_put_string(bundle, IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath); - loader_start_app("image_viewer", false, bundle); + Bundle bundle; + bundle.putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath); + service::loader::start_app("ImageViewer", false, bundle); } else if (is_supported_text_file(filename)) { - Bundle bundle = tt_bundle_alloc(); - if (tt_get_platform() == PlatformEsp) { - tt_bundle_put_string(bundle, TEXT_VIEWER_FILE_ARGUMENT, processed_filepath); + Bundle bundle; + if (get_platform() == PlatformEsp) { + bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath); } else { // Remove forward slash, because we need a relative path - tt_bundle_put_string(bundle, TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1); + bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1); } - loader_start_app("text_viewer", false, bundle); + service::loader::start_app("TextViewer", false, bundle); } else { TT_LOG_W(TAG, "opening files of this type is not supported"); } @@ -124,14 +126,14 @@ static void on_file_pressed(lv_event_t* event) { lv_event_code_t code = lv_event_get_code(event); if (code == LV_EVENT_CLICKED) { lv_obj_t* button = lv_event_get_current_target_obj(event); - FilesData* files_data = lv_obj_get_user_data(button); + auto* files_data = static_cast(lv_obj_get_user_data(button)); - struct dirent* dir_entry = lv_event_get_user_data(event); + auto* dir_entry = static_cast(lv_event_get_user_data(event)); TT_LOG_I(TAG, "Pressed %s %d", dir_entry->d_name, dir_entry->d_type); switch (dir_entry->d_type) { case TT_DT_DIR: - files_data_set_entries_for_child_path(files_data, dir_entry->d_name); + data_set_entries_for_child_path(files_data, dir_entry->d_name); update_views(files_data); break; case TT_DT_LNK: @@ -149,9 +151,9 @@ static void on_file_pressed(lv_event_t* event) { } } -static void create_file_widget(FilesData* files_data, lv_obj_t* parent, struct dirent* dir_entry) { +static void create_file_widget(Data* files_data, lv_obj_t* parent, struct dirent* dir_entry) { tt_check(parent); - lv_obj_t* list = (lv_obj_t*)parent; + auto* list = (lv_obj_t*)parent; const char* symbol; if (dir_entry->d_type == TT_DT_DIR) { symbol = LV_SYMBOL_DIRECTORY; @@ -167,7 +169,7 @@ static void create_file_widget(FilesData* files_data, lv_obj_t* parent, struct d lv_obj_add_event_cb(button, &on_file_pressed, LV_EVENT_CLICKED, (void*)dir_entry); } -static void update_views(FilesData* data) { +static void update_views(Data* data) { lv_obj_clean(data->list); for (int i = 0; i < data->dir_entries_count; ++i) { create_file_widget(data, data->list, data->dir_entries[i]); @@ -179,13 +181,13 @@ static void update_views(FilesData* data) { // region Lifecycle static void on_show(App app, lv_obj_t* parent) { - FilesData* data = tt_app_get_data(app); + auto* data = static_cast(tt_app_get_data(app)); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - lv_obj_t* toolbar = tt_toolbar_create(parent, "Files"); - tt_toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &on_exit_app_pressed, NULL); - tt_toolbar_add_action(toolbar, LV_SYMBOL_UP, "Navigate up", &on_navigate_up_pressed, data); + lv_obj_t* toolbar = lvgl::toolbar_create(parent, "Files"); + lvgl::toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &on_exit_app_pressed, nullptr); + lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, "Navigate up", &on_navigate_up_pressed, data); data->list = lv_list_create(parent); lv_obj_set_width(data->list, LV_PCT(100)); @@ -195,31 +197,31 @@ static void on_show(App app, lv_obj_t* parent) { } static void on_start(App app) { - FilesData* data = files_data_alloc(); + auto* data = data_alloc(); // PC platform is bound to current work directory because of the LVGL file system mapping - if (tt_get_platform() == PlatformPc) { + if (get_platform() == PlatformPc) { char cwd[PATH_MAX]; - if (getcwd(cwd, sizeof(cwd)) != NULL) { - files_data_set_entries_for_path(data, cwd); + if (getcwd(cwd, sizeof(cwd)) != nullptr) { + data_set_entries_for_path(data, cwd); } else { TT_LOG_E(TAG, "Failed to get current work directory files"); - files_data_set_entries_for_path(data, "/"); + data_set_entries_for_path(data, "/"); } } else { - files_data_set_entries_for_path(data, "/"); + data_set_entries_for_path(data, "/"); } tt_app_set_data(app, data); } static void on_stop(App app) { - FilesData* data = tt_app_get_data(app); - files_data_free(data); + auto* data = static_cast(tt_app_get_data(app)); + data_free(data); } // endregion Lifecycle -const AppManifest files_app = { +extern const AppManifest manifest = { .id = "files", .name = "Files", .icon = TT_ASSETS_APP_ICON_FILES, @@ -227,5 +229,6 @@ const AppManifest files_app = { .on_start = &on_start, .on_stop = &on_stop, .on_show = &on_show, - .on_hide = NULL }; + +} // namespace diff --git a/tactility/src/apps/files/files_data.c b/Tactility/Source/Apps/Files/FilesData.cpp similarity index 63% rename from tactility/src/apps/files/files_data.c rename to Tactility/Source/Apps/Files/FilesData.cpp index e0a6d279..a3a11de1 100644 --- a/tactility/src/apps/files/files_data.c +++ b/Tactility/Source/Apps/Files/FilesData.cpp @@ -1,7 +1,9 @@ -#include "files_data.h" -#include "file_utils.h" -#include "tactility_core.h" -#include +#include "FilesData.h" +#include "FileUtils.h" +#include "StringUtils.h" +#include "TactilityCore.h" + +namespace tt::app::files { #define TAG "files_app" @@ -24,71 +26,71 @@ static bool get_child_path(char* base_path, const char* child_path, char* out_pa } } -FilesData* files_data_alloc() { - FilesData* data = malloc(sizeof(FilesData)); - *data = (FilesData) { +Data* data_alloc() { + auto* data = static_cast(malloc(sizeof(Data))); + *data = (Data) { .current_path = { 0x00 }, - .dir_entries = NULL, + .dir_entries = nullptr, .dir_entries_count = 0 }; return data; } -void files_data_free(FilesData* data) { - files_data_free_entries(data); +void data_free(Data* data) { + data_free_entries(data); free(data); } -void files_data_free_entries(FilesData* data) { +void data_free_entries(Data* data) { for (int i = 0; i < data->dir_entries_count; ++i) { free(data->dir_entries[i]); } free(data->dir_entries); - data->dir_entries = NULL; + data->dir_entries = nullptr; data->dir_entries_count = 0; } -static void files_data_set_entries(FilesData* data, struct dirent** entries, int count) { - if (data->dir_entries != NULL) { - files_data_free_entries(data); +static void data_set_entries(Data* data, struct dirent** entries, int count) { + if (data->dir_entries != nullptr) { + data_free_entries(data); } data->dir_entries = entries; data->dir_entries_count = count; } -bool files_data_set_entries_for_path(FilesData* data, const char* path) { +bool data_set_entries_for_path(Data* data, const char* path) { TT_LOG_I(TAG, "Changing path: %s -> %s", data->current_path, path); /** * ESP32 does not have a root directory, so we have to create it manually. * We'll add the NVS Flash partitions and the binding for the sdcard. */ - if (tt_get_platform() == PlatformEsp && strcmp(path, "/") == 0) { + if (get_platform() == PlatformEsp && strcmp(path, "/") == 0) { int dir_entries_count = 3; - struct dirent** dir_entries = malloc(sizeof(struct dirent*) * 3); + auto** dir_entries = static_cast(malloc(sizeof(struct dirent*) * 3)); - dir_entries[0] = malloc(sizeof(struct dirent)); + dir_entries[0] = static_cast(malloc(sizeof(struct dirent))); dir_entries[0]->d_type = TT_DT_DIR; strcpy(dir_entries[0]->d_name, "assets"); - dir_entries[1] = malloc(sizeof(struct dirent)); + dir_entries[1] = static_cast(malloc(sizeof(struct dirent))); dir_entries[1]->d_type = TT_DT_DIR; strcpy(dir_entries[1]->d_name, "config"); - dir_entries[2] = malloc(sizeof(struct dirent)); + dir_entries[2] = static_cast(malloc(sizeof(struct dirent))); dir_entries[2]->d_type = TT_DT_DIR; strcpy(dir_entries[2]->d_name, "sdcard"); - files_data_set_entries(data, dir_entries, dir_entries_count); + data_set_entries(data, dir_entries, dir_entries_count); strcpy(data->current_path, path); return true; } else { - struct dirent** entries = NULL; - int count = tt_scandir(path, &entries, &tt_dirent_filter_dot_entries, &tt_dirent_sort_alpha_and_type); + struct dirent** entries = nullptr; + int count = tt::app::files::scandir(path, &entries, &dirent_filter_dot_entries, &dirent_sort_alpha_and_type); if (count >= 0) { TT_LOG_I(TAG, "%s has %u entries", path, count); - files_data_set_entries(data, entries, count); + data_set_entries(data, entries, count); strcpy(data->current_path, path); return true; } else { @@ -98,13 +100,15 @@ bool files_data_set_entries_for_path(FilesData* data, const char* path) { } } -bool files_data_set_entries_for_child_path(FilesData* data, const char* child_path) { +bool data_set_entries_for_child_path(Data* data, const char* child_path) { char new_absolute_path[MAX_PATH_LENGTH + 1]; if (get_child_path(data->current_path, child_path, new_absolute_path, MAX_PATH_LENGTH)) { TT_LOG_I(TAG, "Navigating from %s to %s", data->current_path, new_absolute_path); - return files_data_set_entries_for_path(data, new_absolute_path); + return data_set_entries_for_path(data, new_absolute_path); } else { TT_LOG_I(TAG, "Failed to get child path for %s/%s", data->current_path, child_path); return false; } } + +} // namespace diff --git a/Tactility/Source/Apps/Files/FilesData.h b/Tactility/Source/Apps/Files/FilesData.h new file mode 100644 index 00000000..ca959c4d --- /dev/null +++ b/Tactility/Source/Apps/Files/FilesData.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "lvgl.h" + +namespace tt::app::files { + +#define MAX_PATH_LENGTH 256 + +typedef struct { + char current_path[MAX_PATH_LENGTH]; + struct dirent** dir_entries; + int dir_entries_count; + lv_obj_t* list; +} Data; + +Data* data_alloc(); +void data_free(Data* data); +void data_free_entries(Data* data); +bool data_set_entries_for_child_path(Data* data, const char* child_path); +bool data_set_entries_for_path(Data* data, const char* path); + +} // namespace diff --git a/tactility/src/apps/gpio/gpio.c b/Tactility/Source/Apps/Gpio/Gpio.cpp similarity index 82% rename from tactility/src/apps/gpio/gpio.c rename to Tactility/Source/Apps/Gpio/Gpio.cpp index 98f49ade..8ad4449a 100644 --- a/tactility/src/apps/gpio/gpio.c +++ b/Tactility/Source/Apps/Gpio/Gpio.cpp @@ -1,10 +1,12 @@ -#include "services/loader/loader.h" -#include "ui/toolbar.h" -#include "thread.h" -#include "mutex.h" +#include "Mutex.h" +#include "Thread.h" +#include "Services/Loader/Loader.h" +#include "Ui/Toolbar.h" -#include "gpio_hal.h" -#include "ui/lvgl_sync.h" +#include "GpioHal.h" +#include "Ui/LvglSync.h" + +namespace tt::app::gpio { typedef struct { lv_obj_t* lv_pins[GPIO_NUM_MAX]; @@ -26,13 +28,17 @@ static void update_pin_states(Gpio* gpio) { lock(gpio); // Update pin states for (int i = 0; i < GPIO_NUM_MAX; ++i) { +#ifdef ESP_PLATFORM + gpio->pin_states[i] = gpio_get_level((gpio_num_t)i); +#else gpio->pin_states[i] = gpio_get_level(i); +#endif } unlock(gpio); } static void update_pin_widgets(Gpio* gpio) { - if (tt_lvgl_lock(100)) { + if (lvgl::lock(100)) { lock(gpio); for (int j = 0; j < GPIO_NUM_MAX; ++j) { int level = gpio->pin_states[j]; @@ -48,7 +54,7 @@ static void update_pin_widgets(Gpio* gpio) { } } } - tt_lvgl_unlock(); + lvgl::unlock(); unlock(gpio); } } @@ -68,7 +74,7 @@ static int32_t gpio_task(void* context) { bool interrupted = false; while (!interrupted) { - tt_delay_ms(100); + delay_ms(100); update_pin_states(gpio); update_pin_widgets(gpio); @@ -82,30 +88,30 @@ static int32_t gpio_task(void* context) { } static void task_start(Gpio* gpio) { - tt_check(gpio->thread == NULL); + tt_assert(gpio->thread == nullptr); lock(gpio); - gpio->thread = tt_thread_alloc_ex( + gpio->thread = thread_alloc_ex( "gpio", 4096, &gpio_task, gpio ); gpio->thread_interrupted = false; - tt_thread_start(gpio->thread); + thread_start(gpio->thread); unlock(gpio); } static void task_stop(Gpio* gpio) { - tt_check(gpio->thread); + tt_assert(gpio->thread); lock(gpio); gpio->thread_interrupted = true; unlock(gpio); - tt_thread_join(gpio->thread); + thread_join(gpio->thread); lock(gpio); - tt_thread_free(gpio->thread); - gpio->thread = NULL; + thread_free(gpio->thread); + gpio->thread = nullptr; unlock(gpio); } @@ -114,10 +120,10 @@ static void task_stop(Gpio* gpio) { // region App lifecycle static void app_show(App app, lv_obj_t* parent) { - Gpio* gpio = tt_app_get_data(app); + auto* gpio = static_cast(tt_app_get_data(app)); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - lv_obj_t* toolbar = tt_toolbar_create_for_app(parent, app); + lv_obj_t* toolbar = lvgl::toolbar_create_for_app(parent, app); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); // Main content wrapper, enables scrolling content without scrolling the toolbar @@ -170,37 +176,38 @@ static void app_show(App app, lv_obj_t* parent) { } static void on_hide(App app) { - Gpio* gpio = tt_app_get_data(app); + auto* gpio = static_cast(tt_app_get_data(app)); task_stop(gpio); } static void on_start(App app) { - Gpio* gpio = malloc(sizeof(Gpio)); + auto* gpio = static_cast(malloc(sizeof(Gpio))); *gpio = (Gpio) { - .lv_pins = { 0 }, + .lv_pins = { nullptr }, .pin_states = { 0 }, + .thread = nullptr, .mutex = tt_mutex_alloc(MutexTypeNormal), .thread_interrupted = true, - .thread = NULL }; tt_app_set_data(app, gpio); } static void on_stop(App app) { - Gpio* gpio = tt_app_get_data(app); + auto* gpio = static_cast(tt_app_get_data(app)); tt_mutex_free(gpio->mutex); free(gpio); } // endregion App lifecycle -const AppManifest gpio_app = { +extern const AppManifest manifest = { .id = "gpio", .name = "GPIO", - .icon = NULL, .type = AppTypeSystem, .on_start = &on_start, .on_stop = &on_stop, .on_show = &app_show, .on_hide = &on_hide }; + +} // namespace diff --git a/tactility/src/apps/gpio/gpio_hal.c b/Tactility/Source/Apps/Gpio/GpioHal.cpp similarity index 100% rename from tactility/src/apps/gpio/gpio_hal.c rename to Tactility/Source/Apps/Gpio/GpioHal.cpp diff --git a/tactility/src/apps/gpio/gpio_hal.h b/Tactility/Source/Apps/Gpio/GpioHal.h similarity index 75% rename from tactility/src/apps/gpio/gpio_hal.h rename to Tactility/Source/Apps/Gpio/GpioHal.h index f90708a1..d0bac02d 100644 --- a/tactility/src/apps/gpio/gpio_hal.h +++ b/Tactility/Source/Apps/Gpio/GpioHal.h @@ -8,14 +8,6 @@ #define GPIO_NUM_MAX 50 #endif -#ifdef __cplusplus -extern "C" { -#endif - #ifndef ESP_PLATFORM int gpio_get_level(int gpio_num); #endif - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/Tactility/Source/Apps/ImageViewer/ImageViewer.cpp b/Tactility/Source/Apps/ImageViewer/ImageViewer.cpp new file mode 100644 index 00000000..4ee3d136 --- /dev/null +++ b/Tactility/Source/Apps/ImageViewer/ImageViewer.cpp @@ -0,0 +1,41 @@ +#include "ImageViewer.h" +#include "Log.h" +#include "lvgl.h" +#include "Ui/Style.h" +#include "Ui/Toolbar.h" + +namespace tt::app::image_viewer { + +#define TAG "image_viewer" + +static void on_show(App app, lv_obj_t* parent) { + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + lvgl::toolbar_create_for_app(parent, app); + + lv_obj_t* wrapper = lv_obj_create(parent); + lv_obj_set_width(wrapper, LV_PCT(100)); + lv_obj_set_flex_grow(wrapper, 1); + lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); + lvgl::obj_set_style_no_padding(wrapper); + lvgl::obj_set_style_bg_invisible(wrapper); + + lv_obj_t* image = lv_img_create(wrapper); + lv_obj_align(image, LV_ALIGN_CENTER, 0, 0); + + const Bundle& bundle = tt_app_get_parameters(app); + std::string file_argument; + if (bundle.optString(IMAGE_VIEWER_FILE_ARGUMENT, file_argument)) { + std::string prefixed_path = "A:" + file_argument; + TT_LOG_I(TAG, "Opening %s", prefixed_path.c_str()); + lv_img_set_src(image, prefixed_path.c_str()); + } +} + +extern const AppManifest manifest = { + .id = "ImageViewer", + .name = "Image Viewer", + .type = AppTypeHidden, + .on_show = &on_show +}; + +} // namespace diff --git a/Tactility/Source/Apps/ImageViewer/ImageViewer.h b/Tactility/Source/Apps/ImageViewer/ImageViewer.h new file mode 100644 index 00000000..18dd8ecc --- /dev/null +++ b/Tactility/Source/Apps/ImageViewer/ImageViewer.h @@ -0,0 +1,3 @@ +#pragma once + +#define IMAGE_VIEWER_FILE_ARGUMENT "file" \ No newline at end of file diff --git a/tactility/src/apps/settings/power/power.c b/Tactility/Source/Apps/Power/Power.cpp similarity index 69% rename from tactility/src/apps/settings/power/power.c rename to Tactility/Source/Apps/Power/Power.cpp index 63d28aff..eb3cd9ca 100644 --- a/tactility/src/apps/settings/power/power.c +++ b/Tactility/Source/Apps/Power/Power.cpp @@ -1,18 +1,19 @@ -#include "app.h" -#include "assets.h" +#include "App.h" +#include "Assets.h" #include "lvgl.h" -#include "preferences.h" -#include "tactility.h" -#include "timer.h" -#include "ui/lvgl_sync.h" -#include "ui/style.h" -#include "ui/toolbar.h" +#include "Tactility.h" +#include "Timer.h" +#include "Ui/LvglSync.h" +#include "Ui/Style.h" +#include "Ui/Toolbar.h" + +namespace tt::app::settings::power { #define TAG "power" typedef struct { Timer* update_timer; - const Power* power; + const hal::Power* power; lv_obj_t* enable_switch; lv_obj_t* charge_state; lv_obj_t* charge_level; @@ -20,7 +21,7 @@ typedef struct { } AppData; static void app_update_ui(App app) { - AppData* data = tt_app_get_data(app); + auto* data = static_cast(tt_app_get_data(app)); bool charging_enabled = data->power->is_charging_enabled(); const char* charge_state = data->power->is_charging() ? "yes" : "no"; @@ -28,7 +29,7 @@ static void app_update_ui(App app) { uint16_t charge_level_scaled = (int16_t)charge_level * 100 / 255; int32_t current = data->power->get_current(); - tt_lvgl_lock(tt_ms_to_ticks(1000)); + lvgl::lock(ms_to_ticks(1000)); lv_obj_set_state(data->enable_switch, LV_STATE_CHECKED, charging_enabled); lv_label_set_text_fmt(data->charge_state, "Charging: %s", charge_state); lv_label_set_text_fmt(data->charge_level, "Charge level: %d%%", charge_level_scaled); @@ -37,16 +38,16 @@ static void app_update_ui(App app) { #else lv_label_set_text_fmt(data->current, "Current: %d mAh", current); #endif - tt_lvgl_unlock(); + lvgl::unlock(); } static void on_power_enabled_change(lv_event_t* event) { lv_event_code_t code = lv_event_get_code(event); - lv_obj_t* enable_switch = lv_event_get_target(event); + auto* enable_switch = static_cast(lv_event_get_target(event)); if (code == LV_EVENT_VALUE_CHANGED) { bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED); App app = lv_event_get_user_data(event); - AppData* data = tt_app_get_data(app); + auto* data = static_cast(tt_app_get_data(app)); if (data->power->is_charging_enabled() != is_on) { data->power->set_charging_enabled(is_on); app_update_ui(app); @@ -57,7 +58,7 @@ static void on_power_enabled_change(lv_event_t* event) { static void app_show(App app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - tt_toolbar_create_for_app(parent, app); + lvgl::toolbar_create_for_app(parent, app); lv_obj_t* wrapper = lv_obj_create(parent); lv_obj_set_width(wrapper, LV_PCT(100)); @@ -65,14 +66,14 @@ static void app_show(App app, lv_obj_t* parent) { lv_obj_set_flex_grow(wrapper, 1); lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); - AppData* data = tt_app_get_data(app); + auto* data = static_cast(tt_app_get_data(app)); // Top row: enable/disable lv_obj_t* switch_container = lv_obj_create(wrapper); lv_obj_set_width(switch_container, LV_PCT(100)); lv_obj_set_height(switch_container, LV_SIZE_CONTENT); - tt_lv_obj_set_style_no_padding(switch_container); - tt_lv_obj_set_style_bg_invisible(switch_container); + lvgl::obj_set_style_no_padding(switch_container); + lvgl::obj_set_style_bg_invisible(switch_container); lv_obj_t* enable_label = lv_label_create(switch_container); lv_label_set_text(enable_label, "Charging enabled"); @@ -88,29 +89,29 @@ static void app_show(App app, lv_obj_t* parent) { data->current = lv_label_create(wrapper); app_update_ui(app); - tt_timer_start(data->update_timer, tt_ms_to_ticks(1000)); + timer_start(data->update_timer, ms_to_ticks(1000)); } static void app_hide(TT_UNUSED App app) { - AppData* data = tt_app_get_data(app); - tt_timer_stop(data->update_timer); + auto* data = static_cast(tt_app_get_data(app)); + timer_stop(data->update_timer); } static void app_start(App app) { - AppData* data = malloc(sizeof(AppData)); - data->update_timer = tt_timer_alloc(&app_update_ui, TimerTypePeriodic, app); - data->power = tt_get_config()->hardware->power; - assert(data->power != NULL); // The Power app only shows up on supported devices + auto* data = static_cast(malloc(sizeof(AppData))); tt_app_set_data(app, data); + data->update_timer = timer_alloc(&app_update_ui, TimerTypePeriodic, app); + data->power = get_config()->hardware->power; + assert(data->power != nullptr); // The Power app only shows up on supported devices } static void app_stop(App app) { - AppData* data = tt_app_get_data(app); - tt_timer_free(data->update_timer); + auto* data = static_cast(tt_app_get_data(app)); + timer_free(data->update_timer); free(data); } -const AppManifest power_app = { +extern const AppManifest manifest = { .id = "power", .name = "Power", .icon = TT_ASSETS_APP_ICON_POWER_SETTINGS, @@ -120,3 +121,5 @@ const AppManifest power_app = { .on_show = &app_show, .on_hide = &app_hide }; + +} // namespace diff --git a/tactility/src/apps/screenshot/screenshot.c b/Tactility/Source/Apps/Screenshot/Screenshot.cpp similarity index 50% rename from tactility/src/apps/screenshot/screenshot.c rename to Tactility/Source/Apps/Screenshot/Screenshot.cpp index 7f6d87ef..5d4d45e2 100644 --- a/tactility/src/apps/screenshot/screenshot.c +++ b/Tactility/Source/Apps/Screenshot/Screenshot.cpp @@ -1,23 +1,23 @@ -#include "tactility_core.h" -#include "ui/toolbar.h" -#include "screenshot_ui.h" +#include "ScreenshotUi.h" + +namespace tt::app::screenshot { static void on_show(App app, lv_obj_t* parent) { - ScreenshotUi* ui = tt_app_get_data(app); - create_screenshot_ui(app, ui, parent); + auto* ui = static_cast(tt_app_get_data(app)); + create_ui(app, ui, parent); } static void on_start(App app) { - ScreenshotUi* ui = malloc(sizeof(ScreenshotUi)); + auto* ui = static_cast(malloc(sizeof(ScreenshotUi))); tt_app_set_data(app, ui); } static void on_stop(App app) { - ScreenshotUi* ui = tt_app_get_data(app); + auto* ui = static_cast(tt_app_get_data(app)); free(ui); } -const AppManifest screenshot_app = { +extern const AppManifest manifest = { .id = "screenshot", .name = "Screenshot", .icon = LV_SYMBOL_IMAGE, @@ -25,5 +25,6 @@ const AppManifest screenshot_app = { .on_start = &on_start, .on_stop = &on_stop, .on_show = &on_show, - .on_hide = NULL }; + +} // namespace diff --git a/tactility/src/apps/screenshot/screenshot_ui.c b/Tactility/Source/Apps/Screenshot/ScreenshotUi.cpp similarity index 84% rename from tactility/src/apps/screenshot/screenshot_ui.c rename to Tactility/Source/Apps/Screenshot/ScreenshotUi.cpp index 400053c6..16e3fbb0 100644 --- a/tactility/src/apps/screenshot/screenshot_ui.c +++ b/Tactility/Source/Apps/Screenshot/ScreenshotUi.cpp @@ -1,16 +1,18 @@ -#include "screenshot_ui.h" +#include "ScreenshotUi.h" -#include "sdcard.h" -#include "services/gui/gui.h" -#include "services/screenshot/screenshot.h" -#include "tactility_core.h" -#include "ui/toolbar.h" +#include "TactilityCore.h" +#include "Hal/Sdcard.h" +#include "Services/Gui/Gui.h" +#include "Services/Screenshot/Screenshot.h" +#include "Ui/Toolbar.h" + +namespace tt::app::screenshot { #define TAG "screenshot_ui" static void update_mode(ScreenshotUi* ui) { lv_obj_t* label = ui->start_stop_button_label; - if (tt_screenshot_is_started()) { + if (service::screenshot::is_started()) { lv_label_set_text(label, "Stop"); } else { lv_label_set_text(label, "Start"); @@ -25,16 +27,16 @@ static void update_mode(ScreenshotUi* ui) { } static void on_mode_set(lv_event_t* event) { - ScreenshotUi* ui = (ScreenshotUi*)lv_event_get_user_data(event); + auto* ui = (ScreenshotUi*)lv_event_get_user_data(event); update_mode(ui); } static void on_start_pressed(lv_event_t* event) { - ScreenshotUi* ui = lv_event_get_user_data(event); + auto* ui = static_cast(lv_event_get_user_data(event)); - if (tt_screenshot_is_started()) { + if (service::screenshot::is_started()) { TT_LOG_I(TAG, "Stop screenshot"); - tt_screenshot_stop(); + service::screenshot::stop(); } else { uint32_t selected = lv_dropdown_get_selected(ui->mode_dropdown); const char* path = lv_textarea_get_text(ui->path_textarea); @@ -43,13 +45,13 @@ static void on_start_pressed(lv_event_t* event) { const char* delay_text = lv_textarea_get_text(ui->delay_textarea); int delay = atoi(delay_text); if (delay > 0) { - tt_screenshot_start_timed(path, delay, 1); + service::screenshot::start_timed(path, delay, 1); } else { TT_LOG_W(TAG, "Ignored screenshot start because delay was 0"); } } else { TT_LOG_I(TAG, "Start app screenshots"); - tt_screenshot_start_apps(path); + service::screenshot::start_apps(path); } } @@ -71,8 +73,8 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) { lv_obj_align_to(mode_dropdown, mode_label, LV_ALIGN_OUT_RIGHT_MID, 8, 0); lv_obj_add_event_cb(mode_dropdown, on_mode_set, LV_EVENT_VALUE_CHANGED, ui); ui->mode_dropdown = mode_dropdown; - ScreenshotMode mode = tt_screenshot_get_mode(); - if (mode == ScreenshotModeApps) { + service::screenshot::ScreenshotMode mode = service::screenshot::get_mode(); + if (mode == service::screenshot::ScreenshotModeApps) { lv_dropdown_set_selected(mode_dropdown, 1); } @@ -103,8 +105,8 @@ static void create_path_ui(ScreenshotUi* ui, lv_obj_t* parent) { lv_textarea_set_one_line(path_textarea, true); lv_obj_set_flex_grow(path_textarea, 1); ui->path_textarea = path_textarea; - if (tt_get_platform() == PlatformEsp) { - if (tt_sdcard_get_state() == SdcardStateMounted) { + if (get_platform() == PlatformEsp) { + if (hal::sdcard::get_state() == hal::sdcard::StateMounted) { lv_textarea_set_text(path_textarea, "A:/sdcard"); } else { lv_textarea_set_text(path_textarea, "Error: no SD card"); @@ -151,9 +153,9 @@ static void create_timer_settings_ui(ScreenshotUi* ui, lv_obj_t* parent) { lv_label_set_text(delay_unit_label, "seconds"); } -void create_screenshot_ui(App app, ScreenshotUi* ui, lv_obj_t* parent) { +void create_ui(App app, ScreenshotUi* ui, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - lv_obj_t* toolbar = tt_toolbar_create_for_app(parent, app); + lv_obj_t* toolbar = lvgl::toolbar_create_for_app(parent, app); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); lv_obj_t* wrapper = lv_obj_create(parent); @@ -166,8 +168,10 @@ void create_screenshot_ui(App app, ScreenshotUi* ui, lv_obj_t* parent) { create_path_ui(ui, wrapper); create_timer_settings_ui(ui, wrapper); - gui_keyboard_add_textarea(ui->delay_textarea); - gui_keyboard_add_textarea(ui->path_textarea); + service::gui::keyboard_add_textarea(ui->delay_textarea); + service::gui::keyboard_add_textarea(ui->path_textarea); update_mode(ui); } + +} // namespace diff --git a/tactility/src/apps/screenshot/screenshot_ui.h b/Tactility/Source/Apps/Screenshot/ScreenshotUi.h similarity index 59% rename from tactility/src/apps/screenshot/screenshot_ui.h rename to Tactility/Source/Apps/Screenshot/ScreenshotUi.h index 1df9fbd8..3a0b68d3 100644 --- a/tactility/src/apps/screenshot/screenshot_ui.h +++ b/Tactility/Source/Apps/Screenshot/ScreenshotUi.h @@ -1,11 +1,9 @@ #pragma once -#include "app.h" +#include "App.h" #include "lvgl.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::app::screenshot { typedef struct { lv_obj_t* mode_dropdown; @@ -15,8 +13,6 @@ typedef struct { lv_obj_t* delay_textarea; } ScreenshotUi; -void create_screenshot_ui(App app, ScreenshotUi* ui, lv_obj_t* parent); +void create_ui(App app, ScreenshotUi* ui, lv_obj_t* parent); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/tactility/src/apps/settings/settings.c b/Tactility/Source/Apps/Settings/Settings.cpp similarity index 51% rename from tactility/src/apps/settings/settings.c rename to Tactility/Source/Apps/Settings/Settings.cpp index 41294f0c..7b312724 100644 --- a/tactility/src/apps/settings/settings.c +++ b/Tactility/Source/Apps/Settings/Settings.cpp @@ -1,45 +1,49 @@ -#include "app_manifest_registry.h" -#include "assets.h" -#include "check.h" +#include "AppManifestRegistry.h" +#include "Assets.h" +#include "Check.h" #include "lvgl.h" -#include "services/loader/loader.h" -#include "ui/toolbar.h" +#include "Services/Loader/Loader.h" +#include "Ui/Toolbar.h" + +namespace tt::app::settings { static void on_app_pressed(lv_event_t* e) { lv_event_code_t code = lv_event_get_code(e); if (code == LV_EVENT_CLICKED) { - const AppManifest* manifest = lv_event_get_user_data(e); - loader_start_app(manifest->id, false, NULL); + const auto* manifest = static_cast(lv_event_get_user_data(e)); + service::loader::start_app(manifest->id, false, Bundle()); } } static void create_app_widget(const AppManifest* manifest, void* parent) { tt_check(parent); - lv_obj_t* list = (lv_obj_t*)parent; - const char* icon = manifest->icon ?: TT_ASSETS_APP_ICON_FALLBACK; - lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name); + auto* list = (lv_obj_t*)parent; + const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK; + lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str()); lv_obj_add_event_cb(btn, &on_app_pressed, LV_EVENT_CLICKED, (void*)manifest); } static void on_show(App app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - tt_toolbar_create_for_app(parent, app); + lvgl::toolbar_create_for_app(parent, app); lv_obj_t* list = lv_list_create(parent); lv_obj_set_width(list, LV_PCT(100)); lv_obj_set_flex_grow(list, 1); - tt_app_manifest_registry_for_each_of_type(AppTypeSettings, list, create_app_widget); + app_manifest_registry_for_each_of_type(AppTypeSettings, list, create_app_widget); } -const AppManifest settings_app = { +extern const AppManifest manifest = { .id = "settings", .name = "Settings", .icon = TT_ASSETS_APP_ICON_SETTINGS, .type = AppTypeSystem, - .on_start = NULL, - .on_stop = NULL, + .on_start = nullptr, + .on_stop = nullptr, .on_show = &on_show, - .on_hide = NULL + .on_hide = nullptr }; + +} // namespace diff --git a/tactility/src/apps/system_info/system_info.c b/Tactility/Source/Apps/SystemInfo/SystemInfo.cpp similarity index 89% rename from tactility/src/apps/system_info/system_info.c rename to Tactility/Source/Apps/SystemInfo/SystemInfo.cpp index 864454a1..223357c7 100644 --- a/tactility/src/apps/system_info/system_info.c +++ b/Tactility/Source/Apps/SystemInfo/SystemInfo.cpp @@ -1,8 +1,9 @@ -#include "app.h" -#include "assets.h" +#include "Assets.h" #include "lvgl.h" -#include "tactility.h" -#include "ui/toolbar.h" +#include "Tactility.h" +#include "Ui/Toolbar.h" + +namespace tt::app::system_info { static size_t get_heap_free() { #ifdef ESP_PLATFORM @@ -56,7 +57,7 @@ static void add_memory_bar(lv_obj_t* parent, const char* label, size_t used, siz lv_bar_set_range(bar, 0, 1); } - lv_bar_set_value(bar, (int32_t)used, 0); + lv_bar_set_value(bar, (int32_t)used, LV_ANIM_OFF); lv_obj_t* bottom_label = lv_label_create(parent); lv_label_set_text_fmt(bottom_label, "%u / %u kB", (used / 1024), (total / 1024)); @@ -64,9 +65,9 @@ static void add_memory_bar(lv_obj_t* parent, const char* label, size_t used, siz lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0); } -static void app_show(App app, lv_obj_t* parent) { +static void on_show(App app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - tt_toolbar_create_for_app(parent, app); + lvgl::toolbar_create_for_app(parent, app); // This wrapper automatically has its children added vertically underneath eachother lv_obj_t* wrapper = lv_obj_create(parent); @@ -100,12 +101,15 @@ static void app_show(App app, lv_obj_t* parent) { } -AppManifest system_info_app = { +extern const AppManifest manifest = { .id = "systeminfo", .name = "System Info", .icon = TT_ASSETS_APP_ICON_SYSTEM_INFO, .type = AppTypeSystem, - .on_start = NULL, - .on_stop = NULL, - .on_show = &app_show + .on_start = nullptr, + .on_stop = nullptr, + .on_show = &on_show }; + +} // namespace + diff --git a/Tactility/Source/Apps/TextViewer/TextViewer.cpp b/Tactility/Source/Apps/TextViewer/TextViewer.cpp new file mode 100644 index 00000000..add4dcc8 --- /dev/null +++ b/Tactility/Source/Apps/TextViewer/TextViewer.cpp @@ -0,0 +1,41 @@ +#include "Log.h" +#include "TextViewer.h" +#include "lvgl.h" +#include "Ui/LabelUtils.h" +#include "Ui/Style.h" +#include "Ui/Toolbar.h" + +#define TAG "text_viewer" + +namespace tt::app::text_viewer { + +static void on_show(App app, lv_obj_t* parent) { + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + lvgl::toolbar_create_for_app(parent, app); + + lv_obj_t* wrapper = lv_obj_create(parent); + lv_obj_set_width(wrapper, LV_PCT(100)); + lv_obj_set_flex_grow(wrapper, 1); + lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); + lvgl::obj_set_style_no_padding(wrapper); + lvgl::obj_set_style_bg_invisible(wrapper); + + lv_obj_t* label = lv_label_create(wrapper); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); + const Bundle& bundle = tt_app_get_parameters(app); + std::string file_argument; + if (bundle.optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) { + std::string prefixed_path = "A:" + file_argument; + TT_LOG_I(TAG, "Opening %s", prefixed_path.c_str()); + lvgl::label_set_text_file(label, prefixed_path.c_str()); + } +} + +extern const AppManifest manifest = { + .id = "TextViewer", + .name = "Text Viewer", + .type = AppTypeHidden, + .on_show = &on_show +}; + +} // namespace diff --git a/Tactility/Source/Apps/TextViewer/TextViewer.h b/Tactility/Source/Apps/TextViewer/TextViewer.h new file mode 100644 index 00000000..de52710b --- /dev/null +++ b/Tactility/Source/Apps/TextViewer/TextViewer.h @@ -0,0 +1,3 @@ +#pragma once + +#define TEXT_VIEWER_FILE_ARGUMENT "file" \ No newline at end of file diff --git a/Tactility/Source/Apps/WifiConnect/WifiConnect.cpp b/Tactility/Source/Apps/WifiConnect/WifiConnect.cpp new file mode 100644 index 00000000..fd5b0686 --- /dev/null +++ b/Tactility/Source/Apps/WifiConnect/WifiConnect.cpp @@ -0,0 +1,146 @@ +#include "WifiConnect.h" + +#include "App.h" +#include "TactilityCore.h" +#include "WifiConnectStateUpdating.h" +#include "Services/Loader/Loader.h" +#include "Services/Wifi/Wifi.h" +#include "Ui/LvglSync.h" + +namespace tt::app::wifi_connect { + +#define TAG "wifi_connect" + +// Forward declarations +static void event_callback(const void* message, void* context); + +static void on_connect(const service::wifi::settings::WifiApSettings* ap_settings, bool remember, TT_UNUSED void* parameter) { + auto* wifi = static_cast(parameter); + state_set_ap_settings(wifi, ap_settings); + state_set_connecting(wifi, true); + service::wifi::connect(ap_settings, remember); +} + +static WifiConnect* wifi_connect_alloc() { + auto* wifi = static_cast(malloc(sizeof(WifiConnect))); + + PubSub* wifi_pubsub = service::wifi::get_pubsub(); + wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &event_callback, wifi); + wifi->mutex = tt_mutex_alloc(MutexTypeNormal); + wifi->state = (WifiConnectState) { + .settings = { + .ssid = { 0 }, + .password = { 0 }, + .auto_connect = false, + }, + .connection_error = false, + .is_connecting = false + }; + wifi->bindings = (WifiConnectBindings) { + .on_connect_ssid = &on_connect, + .on_connect_ssid_context = wifi, + }; + wifi->view_enabled = false; + + return wifi; +} + +static void wifi_connect_free(WifiConnect* wifi) { + PubSub* wifi_pubsub = service::wifi::get_pubsub(); + tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription); + tt_mutex_free(wifi->mutex); + + free(wifi); +} + +void lock(WifiConnect* wifi) { + tt_assert(wifi); + tt_assert(wifi->mutex); + tt_mutex_acquire(wifi->mutex, TtWaitForever); +} + +void unlock(WifiConnect* wifi) { + tt_assert(wifi); + tt_assert(wifi->mutex); + tt_mutex_release(wifi->mutex); +} + +void request_view_update(WifiConnect* wifi) { + lock(wifi); + if (wifi->view_enabled) { + if (lvgl::lock(1000)) { + view_update(&wifi->view, &wifi->bindings, &wifi->state); + lvgl::unlock(); + } else { + TT_LOG_E(TAG, "Failed to lock lvgl"); + } + } + unlock(wifi); +} + +static void event_callback(const void* message, void* context) { + auto* event = static_cast(message); + auto* wifi = static_cast(context); + switch (event->type) { + case service::wifi::WifiEventTypeConnectionFailed: + if (wifi->state.is_connecting) { + state_set_connecting(wifi, false); + state_set_radio_error(wifi, true); + request_view_update(wifi); + } + break; + case service::wifi::WifiEventTypeConnectionSuccess: + if (wifi->state.is_connecting) { + state_set_connecting(wifi, false); + service::loader::stop_app(); + } + break; + default: + break; + } + request_view_update(wifi); +} + +static void app_show(App app, lv_obj_t* parent) { + auto* wifi = static_cast(tt_app_get_data(app)); + + lock(wifi); + wifi->view_enabled = true; + view_create(app, wifi, parent); + view_update(&wifi->view, &wifi->bindings, &wifi->state); + unlock(wifi); +} + +static void app_hide(App app) { + auto* wifi = static_cast(tt_app_get_data(app)); + // No need to lock view, as this is called from within Gui's LVGL context + view_destroy(&wifi->view); + lock(wifi); + wifi->view_enabled = false; + unlock(wifi); +} + +static void app_start(App app) { + auto* wifi_connect = wifi_connect_alloc(); + tt_app_set_data(app, wifi_connect); +} + +static void app_stop(App app) { + auto* wifi = static_cast(tt_app_get_data(app)); + tt_assert(wifi != nullptr); + wifi_connect_free(wifi); + tt_app_set_data(app, nullptr); +} + +extern const AppManifest manifest = { + .id = "wifi_connect", + .name = "Wi-Fi Connect", + .icon = LV_SYMBOL_WIFI, + .type = AppTypeSettings, + .on_start = &app_start, + .on_stop = &app_stop, + .on_show = &app_show, + .on_hide = &app_hide +}; + +} // namespace diff --git a/Tactility/Source/Apps/WifiConnect/WifiConnect.h b/Tactility/Source/Apps/WifiConnect/WifiConnect.h new file mode 100644 index 00000000..366169b0 --- /dev/null +++ b/Tactility/Source/Apps/WifiConnect/WifiConnect.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Mutex.h" +#include "WifiConnectBindings.h" +#include "WifiConnectState.h" +#include "WifiConnectView.h" +#include "Services/Wifi/Wifi.h" + +namespace tt::app::wifi_connect { + +typedef struct { + PubSubSubscription* wifi_subscription; + Mutex* mutex; + WifiConnectState state; + WifiConnectView view; + bool view_enabled; + WifiConnectBindings bindings; +} WifiConnect; + +void lock(WifiConnect* wifi); + +void unlock(WifiConnect* wifi); + +void view_update(WifiConnect* wifi); + +} // namespace diff --git a/Tactility/Source/Apps/WifiConnect/WifiConnectBindings.h b/Tactility/Source/Apps/WifiConnect/WifiConnectBindings.h new file mode 100644 index 00000000..5153861f --- /dev/null +++ b/Tactility/Source/Apps/WifiConnect/WifiConnectBindings.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Services/Wifi/WifiSettings.h" + +namespace tt::app::wifi_connect { + +typedef void (*OnConnectSsid)(const service::wifi::settings::WifiApSettings* settings, bool store, void* context); + +typedef struct { + OnConnectSsid on_connect_ssid; + void* on_connect_ssid_context; +} WifiConnectBindings; + +} // namespace diff --git a/tactility/src/apps/wifi_connect/wifi_connect_bundle.h b/Tactility/Source/Apps/WifiConnect/WifiConnectBundle.h similarity index 63% rename from tactility/src/apps/wifi_connect/wifi_connect_bundle.h rename to Tactility/Source/Apps/WifiConnect/WifiConnectBundle.h index c7c0864f..88ab3af5 100644 --- a/tactility/src/apps/wifi_connect/wifi_connect_bundle.h +++ b/Tactility/Source/Apps/WifiConnect/WifiConnectBundle.h @@ -1,12 +1,4 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #define WIFI_CONNECT_PARAM_SSID "ssid" // String #define WIFI_CONNECT_PARAM_PASSWORD "password" // String - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/Tactility/Source/Apps/WifiConnect/WifiConnectState.h b/Tactility/Source/Apps/WifiConnect/WifiConnectState.h new file mode 100644 index 00000000..7d736a89 --- /dev/null +++ b/Tactility/Source/Apps/WifiConnect/WifiConnectState.h @@ -0,0 +1,18 @@ +#pragma once + +#include "App.h" +#include "Services/Wifi/Wifi.h" +#include "Services/Wifi/WifiSettings.h" + +namespace tt::app::wifi_connect { + +/** + * View's state + */ +typedef struct { + service::wifi::settings::WifiApSettings settings; + bool connection_error; + bool is_connecting; +} WifiConnectState; + +} // namespace diff --git a/Tactility/Source/Apps/WifiConnect/WifiConnectStateUpdating.cpp b/Tactility/Source/Apps/WifiConnect/WifiConnectStateUpdating.cpp new file mode 100644 index 00000000..23cd6ce8 --- /dev/null +++ b/Tactility/Source/Apps/WifiConnect/WifiConnectStateUpdating.cpp @@ -0,0 +1,23 @@ +#include "WifiConnectStateUpdating.h" + +namespace tt::app::wifi_connect { + +void state_set_radio_error(WifiConnect* wifi, bool error) { + lock(wifi); + wifi->state.connection_error = error; + unlock(wifi); +} + +void state_set_ap_settings(WifiConnect* wifi, const service::wifi::settings::WifiApSettings* settings) { + lock(wifi); + memcpy(&(wifi->state.settings), settings, sizeof(service::wifi::settings::WifiApSettings)); + unlock(wifi); +} + +void state_set_connecting(WifiConnect* wifi, bool is_connecting) { + lock(wifi); + wifi->state.is_connecting = is_connecting; + unlock(wifi); +} + +} // namespace diff --git a/Tactility/Source/Apps/WifiConnect/WifiConnectStateUpdating.h b/Tactility/Source/Apps/WifiConnect/WifiConnectStateUpdating.h new file mode 100644 index 00000000..585a425d --- /dev/null +++ b/Tactility/Source/Apps/WifiConnect/WifiConnectStateUpdating.h @@ -0,0 +1,11 @@ +#pragma once + +#include "WifiConnect.h" + +namespace tt::app::wifi_connect { + +void state_set_radio_error(WifiConnect* wifi, bool error); +void state_set_ap_settings(WifiConnect* wifi, const service::wifi::settings::WifiApSettings* settings); +void state_set_connecting(WifiConnect* wifi, bool is_connecting); + +} // namespace diff --git a/tactility/src/apps/wifi_connect/wifi_connect_view.c b/Tactility/Source/Apps/WifiConnect/WifiConnectView.cpp similarity index 79% rename from tactility/src/apps/wifi_connect/wifi_connect_view.c rename to Tactility/Source/Apps/WifiConnect/WifiConnectView.cpp index 11d6851e..2e47c235 100644 --- a/tactility/src/apps/wifi_connect/wifi_connect_view.c +++ b/Tactility/Source/Apps/WifiConnect/WifiConnectView.cpp @@ -1,21 +1,23 @@ -#include "wifi_connect_view.h" +#include "WifiConnectView.h" -#include "log.h" +#include "Log.h" +#include "WifiConnect.h" +#include "WifiConnectBundle.h" +#include "WifiConnectState.h" +#include "WifiConnectStateUpdating.h" #include "lvgl.h" -#include "services/gui/gui.h" -#include "services/wifi/wifi_settings.h" -#include "ui/style.h" -#include "ui/toolbar.h" -#include "wifi_connect.h" -#include "wifi_connect_bundle.h" -#include "wifi_connect_state.h" -#include "wifi_connect_state_updating.h" +#include "Services/Gui/Gui.h" +#include "Services/Wifi/WifiSettings.h" +#include "Ui/Style.h" +#include "Ui/Toolbar.h" + +namespace tt::app::wifi_connect { #define TAG "wifi_connect" -static void wifi_connect_view_set_loading(WifiConnectView* view, bool loading); +static void view_set_loading(WifiConnectView* view, bool loading); -static void wifi_reset_errors(WifiConnectView* view) { +static void reset_errors(WifiConnectView* view) { lv_obj_add_flag(view->password_error, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(view->ssid_error, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(view->connection_error, LV_OBJ_FLAG_HIDDEN); @@ -25,8 +27,8 @@ static void on_connect(lv_event_t* event) { WifiConnect* wifi = (WifiConnect*)lv_event_get_user_data(event); WifiConnectView* view = &wifi->view; - wifi_connect_state_set_radio_error(wifi, false); - wifi_reset_errors(view); + state_set_radio_error(wifi, false); + reset_errors(view); const char* ssid = lv_textarea_get_text(view->ssid_textarea); size_t ssid_len = strlen(ssid); @@ -48,9 +50,9 @@ static void on_connect(lv_event_t* event) { bool store = lv_obj_get_state(view->remember_switch) & LV_STATE_CHECKED; - wifi_connect_view_set_loading(view, true); + view_set_loading(view, true); - WifiApSettings settings; + service::wifi::settings::WifiApSettings settings; strcpy((char*)settings.password, password); strcpy((char*)settings.ssid, ssid); settings.auto_connect = TT_WIFI_AUTO_CONNECT; // No UI yet, so use global setting:w @@ -63,7 +65,7 @@ static void on_connect(lv_event_t* event) { ); } -static void wifi_connect_view_set_loading(WifiConnectView* view, bool loading) { +static void view_set_loading(WifiConnectView* view, bool loading) { if (loading) { lv_obj_add_flag(view->connect_button, LV_OBJ_FLAG_HIDDEN); lv_obj_remove_flag(view->connecting_spinner, LV_OBJ_FLAG_HIDDEN); @@ -80,13 +82,13 @@ static void wifi_connect_view_set_loading(WifiConnectView* view, bool loading) { } -void wifi_connect_view_create_bottom_buttons(WifiConnect* wifi, lv_obj_t* parent) { +void view_create_bottom_buttons(WifiConnect* wifi, lv_obj_t* parent) { WifiConnectView* view = &wifi->view; lv_obj_t* button_container = lv_obj_create(parent); lv_obj_set_width(button_container, LV_PCT(100)); lv_obj_set_height(button_container, LV_SIZE_CONTENT); - tt_lv_obj_set_style_no_padding(button_container); + lvgl::obj_set_style_no_padding(button_container); lv_obj_set_style_border_width(button_container, 0, 0); view->remember_switch = lv_switch_create(button_container); @@ -111,12 +113,12 @@ void wifi_connect_view_create_bottom_buttons(WifiConnect* wifi, lv_obj_t* parent } // TODO: Standardize dialogs -void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) { +void view_create(App app, void* wifi, lv_obj_t* parent) { WifiConnect* wifi_connect = (WifiConnect*)wifi; WifiConnectView* view = &wifi_connect->view; lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - tt_toolbar_create_for_app(parent, app); + lvgl::toolbar_create_for_app(parent, app); lv_obj_t* wrapper = lv_obj_create(parent); lv_obj_set_width(wrapper, LV_PCT(100)); @@ -128,7 +130,7 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) { lv_obj_t* ssid_wrapper = lv_obj_create(wrapper); lv_obj_set_width(ssid_wrapper, LV_PCT(100)); lv_obj_set_height(ssid_wrapper, LV_SIZE_CONTENT); - tt_lv_obj_set_style_no_padding(ssid_wrapper); + lvgl::obj_set_style_no_padding(ssid_wrapper); lv_obj_set_style_border_width(ssid_wrapper, 0, 0); lv_obj_t* ssid_label_wrapper = lv_obj_create(ssid_wrapper); @@ -156,7 +158,7 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) { lv_obj_t* password_wrapper = lv_obj_create(wrapper); lv_obj_set_width(password_wrapper, LV_PCT(100)); lv_obj_set_height(password_wrapper, LV_SIZE_CONTENT); - tt_lv_obj_set_style_no_padding(password_wrapper); + lvgl::obj_set_style_no_padding(password_wrapper); lv_obj_set_style_border_width(password_wrapper, 0, 0); lv_obj_t* password_label_wrapper = lv_obj_create(password_wrapper); @@ -186,40 +188,40 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) { lv_obj_add_flag(view->connection_error, LV_OBJ_FLAG_HIDDEN); // Bottom buttons - wifi_connect_view_create_bottom_buttons(wifi, wrapper); + view_create_bottom_buttons(wifi_connect, wrapper); // Keyboard bindings - gui_keyboard_add_textarea(view->ssid_textarea); - gui_keyboard_add_textarea(view->password_textarea); + service::gui::keyboard_add_textarea(view->ssid_textarea); + service::gui::keyboard_add_textarea(view->password_textarea); // Init from app parameters - _Nullable Bundle bundle = tt_app_get_parameters(app); - if (bundle) { - char* ssid; - if (tt_bundle_opt_string(bundle, WIFI_CONNECT_PARAM_SSID, &ssid)) { - lv_textarea_set_text(view->ssid_textarea, ssid); - } + const Bundle& bundle = tt_app_get_parameters(app); + std::string ssid; + if (bundle.optString(WIFI_CONNECT_PARAM_SSID, ssid)) { + lv_textarea_set_text(view->ssid_textarea, ssid.c_str()); + } - char* password; - if (tt_bundle_opt_string(bundle, WIFI_CONNECT_PARAM_PASSWORD, &password)) { - lv_textarea_set_text(view->password_textarea, password); - } + std::string password; + if (bundle.optString(WIFI_CONNECT_PARAM_PASSWORD, password)) { + lv_textarea_set_text(view->password_textarea, password.c_str()); } } -void wifi_connect_view_destroy(TT_UNUSED WifiConnectView* view) { +void view_destroy(TT_UNUSED WifiConnectView* view) { // NO-OP } -void wifi_connect_view_update( +void view_update( WifiConnectView* view, TT_UNUSED WifiConnectBindings* bindings, WifiConnectState* state ) { if (state->connection_error) { - wifi_connect_view_set_loading(view, false); - wifi_reset_errors(view); + view_set_loading(view, false); + reset_errors(view); lv_label_set_text(view->connection_error, "Connection failed"); lv_obj_remove_flag(view->connection_error, LV_OBJ_FLAG_HIDDEN); } } + +} // namespace diff --git a/tactility/src/apps/wifi_connect/wifi_connect_view.h b/Tactility/Source/Apps/WifiConnect/WifiConnectView.h similarity index 50% rename from tactility/src/apps/wifi_connect/wifi_connect_view.h rename to Tactility/Source/Apps/WifiConnect/WifiConnectView.h index 5ce35cfb..979b63e7 100644 --- a/tactility/src/apps/wifi_connect/wifi_connect_view.h +++ b/Tactility/Source/Apps/WifiConnect/WifiConnectView.h @@ -1,12 +1,10 @@ #pragma once +#include "WifiConnectBindings.h" +#include "WifiConnectState.h" #include "lvgl.h" -#include "wifi_connect_state.h" -#include "wifi_connect_bindings.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::app::wifi_connect { typedef struct { lv_obj_t* ssid_textarea; @@ -21,10 +19,8 @@ typedef struct { lv_group_t* group; } WifiConnectView; -void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent); -void wifi_connect_view_update(WifiConnectView* view, WifiConnectBindings* bindings, WifiConnectState* state); -void wifi_connect_view_destroy(WifiConnectView* view); +void view_create(App app, void* wifi, lv_obj_t* parent); +void view_update(WifiConnectView* view, WifiConnectBindings* bindings, WifiConnectState* state); +void view_destroy(WifiConnectView* view); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/Tactility/Source/Apps/WifiManager/WifiManage.cpp b/Tactility/Source/Apps/WifiManager/WifiManage.cpp new file mode 100644 index 00000000..1b72033d --- /dev/null +++ b/Tactility/Source/Apps/WifiManager/WifiManage.cpp @@ -0,0 +1,180 @@ +#include "WifiManage.h" + +#include "App.h" +#include "Apps/WifiConnect/WifiConnectBundle.h" +#include "TactilityCore.h" +#include "Services/Loader/Loader.h" +#include "Services/Wifi/WifiSettings.h" +#include "Ui/LvglSync.h" +#include "WifiManageStateUpdating.h" +#include "WifiManageView.h" + +namespace tt::app::wifi_manage { + +#define TAG "wifi_manage" + +// Forward declarations +static void event_callback(const void* message, void* context); + +static void on_connect(const char* ssid) { + service::wifi::settings::WifiApSettings settings; + if (service::wifi::settings::load(ssid, &settings)) { + TT_LOG_I(TAG, "Connecting with known credentials"); + service::wifi::connect(&settings, false); + } else { + TT_LOG_I(TAG, "Starting connection dialog"); + Bundle bundle; + bundle.putString(WIFI_CONNECT_PARAM_SSID, ssid); + bundle.putString(WIFI_CONNECT_PARAM_PASSWORD, ""); + service::loader::start_app("wifi_connect", false, bundle); + } +} + +static void on_disconnect() { + service::wifi::disconnect(); +} + +static void on_wifi_toggled(bool enabled) { + service::wifi::set_enabled(enabled); +} + +static WifiManage* wifi_manage_alloc() { + auto* wifi = static_cast(malloc(sizeof(WifiManage))); + + wifi->wifi_subscription = nullptr; + wifi->mutex = tt_mutex_alloc(MutexTypeNormal); + wifi->state = (WifiManageState) { + .scanning = service::wifi::is_scanning(), + .radio_state = service::wifi::get_radio_state(), + .connect_ssid = { 0 }, + .ap_records = { }, + .ap_records_count = 0 + }; + wifi->view_enabled = false; + wifi->bindings = (WifiManageBindings) { + .on_wifi_toggled = &on_wifi_toggled, + .on_connect_ssid = &on_connect, + .on_disconnect = &on_disconnect + }; + + return wifi; +} + +static void wifi_manage_free(WifiManage* wifi) { + tt_mutex_free(wifi->mutex); + + free(wifi); +} + +void lock(WifiManage* wifi) { + tt_assert(wifi); + tt_assert(wifi->mutex); + tt_mutex_acquire(wifi->mutex, TtWaitForever); +} + +void unlock(WifiManage* wifi) { + tt_assert(wifi); + tt_assert(wifi->mutex); + tt_mutex_release(wifi->mutex); +} + +void request_view_update(WifiManage* wifi) { + lock(wifi); + if (wifi->view_enabled) { + if (lvgl::lock(1000)) { + view_update(&wifi->view, &wifi->bindings, &wifi->state); + lvgl::unlock(); + } else { + TT_LOG_E(TAG, "failed to lock lvgl"); + } + } + unlock(wifi); +} + +static void wifi_manage_event_callback(const void* message, void* context) { + auto* event = (service::wifi::WifiEvent*)message; + auto* wifi = (WifiManage*)context; + TT_LOG_I(TAG, "Update with state %d", service::wifi::get_radio_state()); + state_set_radio_state(wifi, service::wifi::get_radio_state()); + switch (event->type) { + case tt::service::wifi::WifiEventTypeScanStarted: + state_set_scanning(wifi, true); + break; + case tt::service::wifi::WifiEventTypeScanFinished: + state_set_scanning(wifi, false); + state_update_scanned_records(wifi); + break; + case tt::service::wifi::WifiEventTypeRadioStateOn: + if (!service::wifi::is_scanning()) { + service::wifi::scan(); + } + break; + default: + break; + } + + request_view_update(wifi); +} + +static void app_show(App app, lv_obj_t* parent) { + auto* wifi = (WifiManage*)tt_app_get_data(app); + + PubSub* wifi_pubsub = service::wifi::get_pubsub(); + wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &wifi_manage_event_callback, wifi); + + // State update (it has its own locking) + state_set_radio_state(wifi, service::wifi::get_radio_state()); + state_set_scanning(wifi, service::wifi::is_scanning()); + state_update_scanned_records(wifi); + + // View update + lock(wifi); + wifi->view_enabled = true; + strcpy((char*)wifi->state.connect_ssid, "Connected"); // TODO update with proper SSID + view_create(app, &wifi->view, &wifi->bindings, parent); + view_update(&wifi->view, &wifi->bindings, &wifi->state); + unlock(wifi); + + service::wifi::WifiRadioState radio_state = service::wifi::get_radio_state(); + bool can_scan = radio_state == service::wifi::WIFI_RADIO_ON || + radio_state == service::wifi::WIFI_RADIO_CONNECTION_PENDING || + radio_state == service::wifi::WIFI_RADIO_CONNECTION_ACTIVE; + if (can_scan && !service::wifi::is_scanning()) { + service::wifi::scan(); + } +} + +static void app_hide(App app) { + auto* wifi = (WifiManage*)tt_app_get_data(app); + lock(wifi); + PubSub* wifi_pubsub = service::wifi::get_pubsub(); + tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription); + wifi->wifi_subscription = nullptr; + wifi->view_enabled = false; + unlock(wifi); +} + +static void app_start(App app) { + WifiManage* wifi = wifi_manage_alloc(); + tt_app_set_data(app, wifi); +} + +static void app_stop(App app) { + auto* wifi = (WifiManage*)tt_app_get_data(app); + tt_assert(wifi != nullptr); + wifi_manage_free(wifi); + tt_app_set_data(app, nullptr); +} + +extern const AppManifest manifest = { + .id = "WifiManage", + .name = "Wi-Fi", + .icon = LV_SYMBOL_WIFI, + .type = AppTypeSettings, + .on_start = &app_start, + .on_stop = &app_stop, + .on_show = &app_show, + .on_hide = &app_hide +}; + +} // namespace diff --git a/Tactility/Source/Apps/WifiManager/WifiManage.h b/Tactility/Source/Apps/WifiManager/WifiManage.h new file mode 100644 index 00000000..c47655fe --- /dev/null +++ b/Tactility/Source/Apps/WifiManager/WifiManage.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Mutex.h" +#include "WifiManageView.h" +#include "Services/Wifi/Wifi.h" + +namespace tt::app::wifi_manage { + +typedef struct { + PubSubSubscription* wifi_subscription; + Mutex* mutex; + WifiManageState state; + WifiManageView view; + bool view_enabled; + WifiManageBindings bindings; +} WifiManage; + +void lock(WifiManage* wifi); + +void unlock(WifiManage* wifi); + +void request_view_update(WifiManage* wifi); + +} // namespace diff --git a/tactility/src/apps/wifi_manage/wifi_manage_bindings.h b/Tactility/Source/Apps/WifiManager/WifiManageBindings.h similarity index 85% rename from tactility/src/apps/wifi_manage/wifi_manage_bindings.h rename to Tactility/Source/Apps/WifiManager/WifiManageBindings.h index e79ef1fe..e1501799 100644 --- a/tactility/src/apps/wifi_manage/wifi_manage_bindings.h +++ b/Tactility/Source/Apps/WifiManager/WifiManageBindings.h @@ -1,6 +1,6 @@ #pragma once -#include +namespace tt::app::wifi_manage { typedef void (*OnWifiToggled)(bool enable); typedef void (*OnConnectSsid)(const char* ssid); @@ -11,3 +11,5 @@ typedef struct { OnConnectSsid on_connect_ssid; OnDisconnect on_disconnect; } WifiManageBindings; + +} // namespace diff --git a/Tactility/Source/Apps/WifiManager/WifiManageState.h b/Tactility/Source/Apps/WifiManager/WifiManageState.h new file mode 100644 index 00000000..ec613e6e --- /dev/null +++ b/Tactility/Source/Apps/WifiManager/WifiManageState.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Services/Wifi/Wifi.h" + +namespace tt::app::wifi_manage { + +#define WIFI_SCAN_AP_RECORD_COUNT 16 + +/** + * View's state + */ +typedef struct { + bool scanning; + service::wifi::WifiRadioState radio_state; + uint8_t connect_ssid[33]; + service::wifi::WifiApRecord ap_records[WIFI_SCAN_AP_RECORD_COUNT]; + uint16_t ap_records_count; +} WifiManageState; + +} // namespace diff --git a/Tactility/Source/Apps/WifiManager/WifiManageStateUpdating.cpp b/Tactility/Source/Apps/WifiManager/WifiManageStateUpdating.cpp new file mode 100644 index 00000000..a6d94a6f --- /dev/null +++ b/Tactility/Source/Apps/WifiManager/WifiManageStateUpdating.cpp @@ -0,0 +1,27 @@ +#include "WifiManage.h" + +namespace tt::app::wifi_manage { + +void state_set_scanning(WifiManage* wifi, bool is_scanning) { + lock(wifi); + wifi->state.scanning = is_scanning; + unlock(wifi); +} + +void state_set_radio_state(WifiManage* wifi, service::wifi::WifiRadioState state) { + lock(wifi); + wifi->state.radio_state = state; + unlock(wifi); +} + +void state_update_scanned_records(WifiManage* wifi) { + lock(wifi); + service::wifi::get_scan_results( + wifi->state.ap_records, + WIFI_SCAN_AP_RECORD_COUNT, + &wifi->state.ap_records_count + ); + unlock(wifi); +} + +} // namespace diff --git a/Tactility/Source/Apps/WifiManager/WifiManageStateUpdating.h b/Tactility/Source/Apps/WifiManager/WifiManageStateUpdating.h new file mode 100644 index 00000000..7cbdbce7 --- /dev/null +++ b/Tactility/Source/Apps/WifiManager/WifiManageStateUpdating.h @@ -0,0 +1,11 @@ +#pragma once + +#include "WifiManage.h" + +namespace tt::app::wifi_manage { + +void state_set_scanning(WifiManage* wifi, bool is_scanning); +void state_set_radio_state(WifiManage* wifi, service::wifi::WifiRadioState state); +void state_update_scanned_records(WifiManage* wifi); + +} // namespace diff --git a/tactility/src/apps/wifi_manage/wifi_manage_view.c b/Tactility/Source/Apps/WifiManager/WifiManageView.cpp similarity index 74% rename from tactility/src/apps/wifi_manage/wifi_manage_view.c rename to Tactility/Source/Apps/WifiManager/WifiManageView.cpp index a304e3a0..35ee6449 100644 --- a/tactility/src/apps/wifi_manage/wifi_manage_view.c +++ b/Tactility/Source/Apps/WifiManager/WifiManageView.cpp @@ -1,27 +1,29 @@ -#include "wifi_manage_view.h" +#include "WifiManageView.h" -#include "log.h" -#include "services/statusbar_updater/statusbar_updater.h" -#include "services/wifi/wifi.h" -#include "ui/style.h" -#include "ui/toolbar.h" -#include "wifi_manage_state.h" +#include "Log.h" +#include "WifiManageState.h" +#include "Services/Statusbar/Statusbar.h" +#include "Services/Wifi/Wifi.h" +#include "Ui/Style.h" +#include "Ui/Toolbar.h" + +namespace tt::app::wifi_manage { #define TAG "wifi_main_view" #define SPINNER_HEIGHT 40 static void on_enable_switch_changed(lv_event_t* event) { lv_event_code_t code = lv_event_get_code(event); - lv_obj_t* enable_switch = lv_event_get_target(event); + auto* enable_switch = static_cast(lv_event_get_target(event)); if (code == LV_EVENT_VALUE_CHANGED) { bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED); - WifiManageBindings* bindings = (WifiManageBindings*)lv_event_get_user_data(event); + auto* bindings = static_cast(lv_event_get_user_data(event)); bindings->on_wifi_toggled(is_on); } } static void on_disconnect_pressed(lv_event_t* event) { - WifiManageBindings* bindings = (WifiManageBindings*)lv_event_get_user_data(event); + auto* bindings = static_cast(lv_event_get_user_data(event)); bindings->on_disconnect(); } @@ -35,13 +37,13 @@ static void connect(lv_event_t* event) { // our own and passing it as the event data const char* ssid = lv_label_get_text(label); TT_LOG_I(TAG, "Clicked AP: %s", ssid); - WifiManageBindings* bindings = (WifiManageBindings*)lv_event_get_user_data(event); + auto* bindings = static_cast(lv_event_get_user_data(event)); bindings->on_connect_ssid(ssid); } -static void create_network_button(WifiManageView* view, WifiManageBindings* bindings, WifiApRecord* record) { +static void create_network_button(WifiManageView* view, WifiManageBindings* bindings, service::wifi::WifiApRecord* record) { const char* ssid = (const char*)record->ssid; - const char* icon = wifi_get_status_icon_for_rssi(record->rssi, record->auth_mode != WIFI_AUTH_OPEN); + const char* icon = service::statusbar::get_status_icon_for_rssi(record->rssi, record->auth_mode != WIFI_AUTH_OPEN); lv_obj_t* ap_button = lv_list_add_btn( view->networks_list, icon, @@ -53,10 +55,10 @@ static void create_network_button(WifiManageView* view, WifiManageBindings* bind static void update_network_list(WifiManageView* view, WifiManageState* state, WifiManageBindings* bindings) { lv_obj_clean(view->networks_list); switch (state->radio_state) { - case WIFI_RADIO_ON_PENDING: - case WIFI_RADIO_ON: - case WIFI_RADIO_CONNECTION_PENDING: - case WIFI_RADIO_CONNECTION_ACTIVE: { + case service::wifi::WIFI_RADIO_ON_PENDING: + case service::wifi::WIFI_RADIO_ON: + case service::wifi::WIFI_RADIO_CONNECTION_PENDING: + case service::wifi::WIFI_RADIO_CONNECTION_ACTIVE: { lv_obj_clear_flag(view->networks_label, LV_OBJ_FLAG_HIDDEN); if (state->ap_records_count > 0) { for (int i = 0; i < state->ap_records_count; ++i) { @@ -72,8 +74,8 @@ static void update_network_list(WifiManageView* view, WifiManageState* state, Wi } break; } - case WIFI_RADIO_OFF_PENDING: - case WIFI_RADIO_OFF: { + case service::wifi::WIFI_RADIO_OFF_PENDING: + case service::wifi::WIFI_RADIO_OFF: { lv_obj_add_flag(view->networks_list, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(view->networks_label, LV_OBJ_FLAG_HIDDEN); break; @@ -82,7 +84,7 @@ static void update_network_list(WifiManageView* view, WifiManageState* state, Wi } void update_scanning(WifiManageView* view, WifiManageState* state) { - if (state->radio_state == WIFI_RADIO_ON && state->scanning) { + if (state->radio_state == service::wifi::WIFI_RADIO_ON && state->scanning) { lv_obj_clear_flag(view->scanning_spinner, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(view->scanning_spinner, LV_OBJ_FLAG_HIDDEN); @@ -92,26 +94,28 @@ void update_scanning(WifiManageView* view, WifiManageState* state) { static void update_wifi_toggle(WifiManageView* view, WifiManageState* state) { lv_obj_clear_state(view->enable_switch, LV_STATE_ANY); switch (state->radio_state) { - case WIFI_RADIO_ON: - case WIFI_RADIO_CONNECTION_PENDING: - case WIFI_RADIO_CONNECTION_ACTIVE: + case service::wifi::WIFI_RADIO_ON: + case service::wifi::WIFI_RADIO_CONNECTION_PENDING: + case service::wifi::WIFI_RADIO_CONNECTION_ACTIVE: lv_obj_add_state(view->enable_switch, LV_STATE_CHECKED); break; - case WIFI_RADIO_ON_PENDING: + case service::wifi::WIFI_RADIO_ON_PENDING: lv_obj_add_state(view->enable_switch, LV_STATE_CHECKED | LV_STATE_DISABLED); break; - case WIFI_RADIO_OFF: + case service::wifi::WIFI_RADIO_OFF: + lv_obj_remove_state(view->enable_switch, LV_STATE_CHECKED | LV_STATE_DISABLED); break; - case WIFI_RADIO_OFF_PENDING: + case service::wifi::WIFI_RADIO_OFF_PENDING: + lv_obj_remove_state(view->enable_switch, LV_STATE_CHECKED); lv_obj_add_state(view->enable_switch, LV_STATE_DISABLED); break; } } -static void update_connected_ap(WifiManageView* view, WifiManageState* state, WifiManageBindings* bindings) { +static void update_connected_ap(WifiManageView* view, WifiManageState* state, TT_UNUSED WifiManageBindings* bindings) { switch (state->radio_state) { - case WIFI_RADIO_CONNECTION_PENDING: - case WIFI_RADIO_CONNECTION_ACTIVE: + case service::wifi::WIFI_RADIO_CONNECTION_PENDING: + case service::wifi::WIFI_RADIO_CONNECTION_ACTIVE: lv_obj_clear_flag(view->connected_ap_container, LV_OBJ_FLAG_HIDDEN); lv_label_set_text(view->connected_ap_label, (const char*)state->connect_ssid); break; @@ -125,11 +129,11 @@ static void update_connected_ap(WifiManageView* view, WifiManageState* state, Wi // region Main -void wifi_manage_view_create(App app, WifiManageView* view, WifiManageBindings* bindings, lv_obj_t* parent) { +void view_create(App app, WifiManageView* view, WifiManageBindings* bindings, lv_obj_t* parent) { view->root = parent; lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - tt_toolbar_create_for_app(parent, app); + lvgl::toolbar_create_for_app(parent, app); lv_obj_t* wrapper = lv_obj_create(parent); lv_obj_set_width(wrapper, LV_PCT(100)); @@ -140,8 +144,8 @@ void wifi_manage_view_create(App app, WifiManageView* view, WifiManageBindings* lv_obj_t* switch_container = lv_obj_create(wrapper); lv_obj_set_width(switch_container, LV_PCT(100)); lv_obj_set_height(switch_container, LV_SIZE_CONTENT); - tt_lv_obj_set_style_no_padding(switch_container); - tt_lv_obj_set_style_bg_invisible(switch_container); + lvgl::obj_set_style_no_padding(switch_container); + lvgl::obj_set_style_bg_invisible(switch_container); lv_obj_t* enable_label = lv_label_create(switch_container); lv_label_set_text(enable_label, "Wi-Fi"); @@ -154,7 +158,7 @@ void wifi_manage_view_create(App app, WifiManageView* view, WifiManageBindings* view->connected_ap_container = lv_obj_create(wrapper); lv_obj_set_size(view->connected_ap_container, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_style_min_height(view->connected_ap_container, SPINNER_HEIGHT, 0); - tt_lv_obj_set_style_no_padding(view->connected_ap_container); + lvgl::obj_set_style_no_padding(view->connected_ap_container); lv_obj_set_style_border_width(view->connected_ap_container, 0, 0); view->connected_ap_label = lv_label_create(view->connected_ap_container); @@ -171,7 +175,7 @@ void wifi_manage_view_create(App app, WifiManageView* view, WifiManageBindings* lv_obj_t* networks_header = lv_obj_create(wrapper); lv_obj_set_size(networks_header, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_style_min_height(networks_header, SPINNER_HEIGHT, 0); - tt_lv_obj_set_style_no_padding(networks_header); + lvgl::obj_set_style_no_padding(networks_header); lv_obj_set_style_border_width(networks_header, 0, 0); view->networks_label = lv_label_create(networks_header); @@ -193,9 +197,11 @@ void wifi_manage_view_create(App app, WifiManageView* view, WifiManageBindings* lv_obj_set_style_pad_bottom(view->networks_list, 8, 0); } -void wifi_manage_view_update(WifiManageView* view, WifiManageBindings* bindings, WifiManageState* state) { +void view_update(WifiManageView* view, WifiManageBindings* bindings, WifiManageState* state) { update_wifi_toggle(view, state); update_scanning(view, state); update_network_list(view, state, bindings); update_connected_ap(view, state, bindings); } + +} // namespace diff --git a/Tactility/Source/Apps/WifiManager/WifiManageView.h b/Tactility/Source/Apps/WifiManager/WifiManageView.h new file mode 100644 index 00000000..27f6912c --- /dev/null +++ b/Tactility/Source/Apps/WifiManager/WifiManageView.h @@ -0,0 +1,23 @@ +#pragma once + +#include "App.h" +#include "WifiManageBindings.h" +#include "WifiManageState.h" +#include "lvgl.h" + +namespace tt::app::wifi_manage { + +typedef struct { + lv_obj_t* root; + lv_obj_t* enable_switch; + lv_obj_t* scanning_spinner; + lv_obj_t* networks_label; + lv_obj_t* networks_list; + lv_obj_t* connected_ap_container; + lv_obj_t* connected_ap_label; +} WifiManageView; + +void view_create(App app, WifiManageView* view, WifiManageBindings* bindings, lv_obj_t* parent); +void view_update(WifiManageView* view, WifiManageBindings* bindings, WifiManageState* state); + +} // namespace diff --git a/Tactility/Source/LvglInit.cpp b/Tactility/Source/LvglInit.cpp new file mode 100644 index 00000000..b57f2df5 --- /dev/null +++ b/Tactility/Source/LvglInit.cpp @@ -0,0 +1,20 @@ +#include "Apps/Display/DisplayPreferences.h" +#include "lvgl.h" +#include "LvglInit_i.h" + +namespace tt { + +void lvgl_init(const hal::Configuration* config) { + hal::SetBacklightDuty set_backlight_duty = config->display.set_backlight_duty; + if (set_backlight_duty != nullptr) { + int32_t backlight_duty = app::settings::display::preferences_get_backlight_duty(); + set_backlight_duty(backlight_duty); + } + + lv_display_rotation_t rotation = app::settings::display::preferences_get_rotation(); + if (rotation != lv_disp_get_rotation(lv_disp_get_default())) { + lv_disp_set_rotation(lv_disp_get_default(), static_cast(rotation)); + } +} + +} // namespace diff --git a/Tactility/Source/Services/Gui/Gui.cpp b/Tactility/Source/Services/Gui/Gui.cpp new file mode 100644 index 00000000..8deed48d --- /dev/null +++ b/Tactility/Source/Services/Gui/Gui.cpp @@ -0,0 +1,166 @@ +#include "Services/Gui/Gui_i.h" + +#include "Tactility.h" +#include "Services/Loader/Loader.h" +#include "Ui/LvglKeypad.h" +#include "Ui/LvglSync.h" + +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#else +#include "FreeRTOS.h" +#endif + +namespace tt::service::gui { + +#define TAG "gui" + +// Forward declarations +void redraw(Gui*); +static int32_t gui_main(void*); + +Gui* gui = nullptr; + +typedef void (*PubSubCallback)(const void* message, void* context); +void loader_callback(const void* message, TT_UNUSED void* context) { + auto* event = static_cast(message); + if (event->type == loader::LoaderEventTypeApplicationShowing) { + App* app = event->app_showing.app; + const AppManifest& app_manifest = tt_app_get_manifest(app); + show_app(app, app_manifest.on_show, app_manifest.on_hide); + } else if (event->type == loader::LoaderEventTypeApplicationHiding) { + hide_app(); + } +} + +Gui* gui_alloc() { + auto* instance = static_cast(malloc(sizeof(Gui))); + memset(instance, 0, sizeof(Gui)); + tt_check(instance != NULL); + instance->thread = thread_alloc_ex( + "gui", + 4096, // Last known minimum was 2800 for launching desktop + &gui_main, + nullptr + ); + instance->mutex = tt_mutex_alloc(MutexTypeRecursive); + instance->keyboard = nullptr; + instance->loader_pubsub_subscription = tt_pubsub_subscribe(loader::get_pubsub(), &loader_callback, instance); + tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); + instance->keyboard_group = lv_group_create(); + instance->lvgl_parent = lv_scr_act(); + lvgl::unlock(); + + return instance; +} + +void gui_free(Gui* instance) { + tt_assert(instance != nullptr); + thread_free(instance->thread); + tt_mutex_free(instance->mutex); + + tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); + lv_group_del(instance->keyboard_group); + lvgl::unlock(); + + free(instance); +} + +void lock() { + tt_assert(gui); + tt_assert(gui->mutex); + tt_check(tt_mutex_acquire(gui->mutex, configTICK_RATE_HZ) == TtStatusOk); +} + +void unlock() { + tt_assert(gui); + tt_assert(gui->mutex); + tt_check(tt_mutex_release(gui->mutex) == TtStatusOk); +} + +void request_draw() { + tt_assert(gui); + ThreadId thread_id = thread_get_id(gui->thread); + thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW); +} + +void show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) { + lock(); + tt_check(gui->app_view_port == NULL); + gui->app_view_port = view_port_alloc(app, on_show, on_hide); + unlock(); + request_draw(); +} + +void hide_app() { + lock(); + ViewPort* view_port = gui->app_view_port; + tt_check(view_port != NULL); + + // We must lock the LVGL port, because the viewport hide callbacks + // might call LVGL APIs (e.g. to remove the keyboard from the screen root) + tt_check(lvgl::lock(configTICK_RATE_HZ)); + view_port_hide(view_port); + lvgl::unlock(); + + view_port_free(view_port); + gui->app_view_port = nullptr; + unlock(); +} + +static int32_t gui_main(TT_UNUSED void* p) { + tt_check(gui); + Gui* local_gui = gui; + + while (true) { + uint32_t flags = thread_flags_wait( + GUI_THREAD_FLAG_ALL, + TtFlagWaitAny, + TtWaitForever + ); + // Process and dispatch draw call + if (flags & GUI_THREAD_FLAG_DRAW) { + thread_flags_clear(GUI_THREAD_FLAG_DRAW); + redraw(local_gui); + } + + if (flags & GUI_THREAD_FLAG_EXIT) { + thread_flags_clear(GUI_THREAD_FLAG_EXIT); + break; + } + } + + return 0; +} + +// region AppManifest + +static void start(TT_UNUSED Service& service) { + gui = gui_alloc(); + + thread_set_priority(gui->thread, THREAD_PRIORITY_SERVICE); + thread_start(gui->thread); +} + +static void stop(TT_UNUSED Service& service) { + lock(); + + ThreadId thread_id = thread_get_id(gui->thread); + thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT); + thread_join(gui->thread); + thread_free(gui->thread); + + unlock(); + + gui_free(gui); +} + +extern const ServiceManifest manifest = { + .id = "Gui", + .on_start = &start, + .on_stop = &stop +}; + +// endregion + +} // namespace diff --git a/tactility/src/services/gui/gui.h b/Tactility/Source/Services/Gui/Gui.h similarity index 72% rename from tactility/src/services/gui/gui.h rename to Tactility/Source/Services/Gui/Gui.h index 3d713734..3767d370 100644 --- a/tactility/src/services/gui/gui.h +++ b/Tactility/Source/Services/Gui/Gui.h @@ -1,13 +1,10 @@ #pragma once -#include "app.h" -#include "service_manifest.h" -#include "view_port.h" -#include +#include "App.h" +#include "ServiceManifest.h" +#include "ViewPort.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::service::gui { typedef struct Gui Gui; @@ -18,26 +15,26 @@ typedef struct Gui Gui; * @param on_show * @param on_hide */ -void gui_show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide); +void show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide); /** * Hide the current app's viewport. * Does not request a re-draw because after hiding the current app, * we always show the previous app, and there is always at least 1 app running. */ -void gui_hide_app(); +void hide_app(); /** * Show the on-screen keyboard. * @param textarea the textarea to focus the input for */ -void gui_keyboard_show(lv_obj_t* textarea); +void keyboard_show(lv_obj_t* textarea); /** * Hide the on-screen keyboard. * Has no effect when the keyboard is not visible. */ -void gui_keyboard_hide(); +void keyboard_hide(); /** * The on-screen keyboard is only shown when both of these conditions are true: @@ -45,7 +42,7 @@ void gui_keyboard_hide(); * - TT_CONFIG_FORCE_ONSCREEN_KEYBOARD is set to true in tactility_config.h * @return if we should show a on-screen keyboard for text input inside our apps */ -bool gui_keyboard_is_enabled(); +bool keyboard_is_enabled(); /** * Glue code for the on-screen keyboard and the hardware keyboard: @@ -53,8 +50,6 @@ bool gui_keyboard_is_enabled(); * - Registers the textarea to the default lv_group_t for hardware keyboards. * @param textarea */ -void gui_keyboard_add_textarea(lv_obj_t* textarea); +void keyboard_add_textarea(lv_obj_t* textarea); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility/src/services/gui/gui_draw.c b/Tactility/Source/Services/Gui/GuiDraw.cpp similarity index 67% rename from tactility/src/services/gui/gui_draw.c rename to Tactility/Source/Services/Gui/GuiDraw.cpp index ef49c59a..2d6a9a7e 100644 --- a/tactility/src/services/gui/gui_draw.c +++ b/Tactility/Source/Services/Gui/GuiDraw.cpp @@ -1,52 +1,54 @@ -#include "check.h" -#include "gui_i.h" -#include "log.h" -#include "ui/lvgl_sync.h" -#include "ui/statusbar.h" -#include "ui/style.h" +#include "Check.h" +#include "Log.h" +#include "Services/Gui/Gui_i.h" +#include "Ui/LvglSync.h" +#include "Ui/Statusbar.h" +#include "Ui/Style.h" + +namespace tt::service::gui { #define TAG "gui" static lv_obj_t* create_app_views(Gui* gui, lv_obj_t* parent, App app) { - tt_lv_obj_set_style_bg_blacken(parent); + lvgl::obj_set_style_bg_blacken(parent); lv_obj_t* vertical_container = lv_obj_create(parent); lv_obj_set_size(vertical_container, LV_PCT(100), LV_PCT(100)); lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN); - tt_lv_obj_set_style_no_padding(vertical_container); - tt_lv_obj_set_style_bg_blacken(vertical_container); + lvgl::obj_set_style_no_padding(vertical_container); + lvgl::obj_set_style_bg_blacken(vertical_container); // TODO: Move statusbar into separate ViewPort AppFlags flags = tt_app_get_flags(app); if (flags.show_statusbar) { - tt_statusbar_create(vertical_container); + lvgl::statusbar_create(vertical_container); } lv_obj_t* child_container = lv_obj_create(vertical_container); lv_obj_set_width(child_container, LV_PCT(100)); lv_obj_set_flex_grow(child_container, 1); - if (gui_keyboard_is_enabled()) { + if (keyboard_is_enabled()) { gui->keyboard = lv_keyboard_create(vertical_container); lv_obj_add_flag(gui->keyboard, LV_OBJ_FLAG_HIDDEN); } else { - gui->keyboard = NULL; + gui->keyboard = nullptr; } return child_container; } -void gui_redraw(Gui* gui) { +void redraw(Gui* gui) { tt_assert(gui); // Lock GUI and LVGL - gui_lock(); + lock(); - if (tt_lvgl_lock(1000)) { + if (lvgl::lock(1000)) { lv_obj_clean(gui->lvgl_parent); ViewPort* view_port = gui->app_view_port; - if (view_port != NULL) { + if (view_port != nullptr) { App app = view_port->app; lv_obj_t* container = create_app_views(gui, gui->lvgl_parent, app); view_port_show(view_port, container); @@ -55,10 +57,12 @@ void gui_redraw(Gui* gui) { } // Unlock GUI and LVGL - tt_lvgl_unlock(); + lvgl::unlock(); } else { TT_LOG_E(TAG, "failed to lock lvgl"); } - gui_unlock(); + unlock(); } + +} // namespace diff --git a/tactility/src/services/gui/gui_keyboard.c b/Tactility/Source/Services/Gui/Keyboard.cpp similarity index 55% rename from tactility/src/services/gui/gui_keyboard.c rename to Tactility/Source/Services/Gui/Keyboard.cpp index cc853b96..49e1ce8a 100644 --- a/tactility/src/services/gui/gui_keyboard.c +++ b/Tactility/Source/Services/Gui/Keyboard.cpp @@ -1,61 +1,65 @@ -#include "gui_i.h" +#include "Services/Gui/Gui_i.h" -#include "tactility_config.h" -#include "ui/lvgl_keypad.h" -#include "ui/lvgl_sync.h" +#include "TactilityConfig.h" +#include "Ui/LvglKeypad.h" +#include "Ui/LvglSync.h" + +namespace tt::service::gui { extern Gui* gui; static void show_keyboard(lv_event_t* event) { lv_obj_t* target = lv_event_get_current_target_obj(event); - gui_keyboard_show(target); + keyboard_show(target); lv_obj_scroll_to_view(target, LV_ANIM_ON); } static void hide_keyboard(TT_UNUSED lv_event_t* event) { - gui_keyboard_hide(); + keyboard_hide(); } -bool gui_keyboard_is_enabled() { - return !tt_lvgl_keypad_is_available() || TT_CONFIG_FORCE_ONSCREEN_KEYBOARD; +bool keyboard_is_enabled() { + return !lvgl::keypad_is_available() || TT_CONFIG_FORCE_ONSCREEN_KEYBOARD; } -void gui_keyboard_show(lv_obj_t* textarea) { - gui_lock(); +void keyboard_show(lv_obj_t* textarea) { + lock(); if (gui->keyboard) { lv_obj_clear_flag(gui->keyboard, LV_OBJ_FLAG_HIDDEN); lv_keyboard_set_textarea(gui->keyboard, textarea); } - gui_unlock(); + unlock(); } -void gui_keyboard_hide() { - gui_lock(); +void keyboard_hide() { + lock(); if (gui->keyboard) { lv_obj_add_flag(gui->keyboard, LV_OBJ_FLAG_HIDDEN); } - gui_unlock(); + unlock(); } -void gui_keyboard_add_textarea(lv_obj_t* textarea) { - gui_lock(); - tt_check(tt_lvgl_lock(0), "lvgl should already be locked before calling this method"); +void keyboard_add_textarea(lv_obj_t* textarea) { + lock(); + tt_check(lvgl::lock(0), "lvgl should already be locked before calling this method"); - if (gui_keyboard_is_enabled()) { - lv_obj_add_event_cb(textarea, show_keyboard, LV_EVENT_FOCUSED, NULL); - lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_DEFOCUSED, NULL); - lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_READY, NULL); + if (keyboard_is_enabled()) { + lv_obj_add_event_cb(textarea, show_keyboard, LV_EVENT_FOCUSED, nullptr); + lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_DEFOCUSED, nullptr); + lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_READY, nullptr); } // lv_obj_t auto-remove themselves from the group when they are destroyed (last checked in LVGL 8.3) lv_group_add_obj(gui->keyboard_group, textarea); - tt_lvgl_keypad_activate(gui->keyboard_group); + lvgl::keypad_activate(gui->keyboard_group); - tt_lvgl_unlock(); - gui_unlock(); + lvgl::unlock(); + unlock(); } + +} // namespace diff --git a/tactility/src/services/gui/view_port.c b/Tactility/Source/Services/Gui/ViewPort.cpp similarity index 72% rename from tactility/src/services/gui/view_port.c rename to Tactility/Source/Services/Gui/ViewPort.cpp index bcd683fe..bcafbcd8 100644 --- a/tactility/src/services/gui/view_port.c +++ b/Tactility/Source/Services/Gui/ViewPort.cpp @@ -1,8 +1,10 @@ -#include "view_port.h" +#include "ViewPort.h" -#include "check.h" -#include "ui/style.h" -#include "view_port_i.h" +#include "Check.h" +#include "Services/Gui/ViewPort_i.h" +#include "Ui/Style.h" + +namespace tt::service::gui { #define TAG "viewport" @@ -11,7 +13,7 @@ ViewPort* view_port_alloc( ViewPortShowCallback on_show, ViewPortHideCallback on_hide ) { - ViewPort* view_port = malloc(sizeof(ViewPort)); + auto* view_port = static_cast(malloc(sizeof(ViewPort))); view_port->app = app; view_port->on_show = on_show; view_port->on_hide = on_hide; @@ -27,7 +29,7 @@ void view_port_show(ViewPort* view_port, lv_obj_t* parent) { tt_assert(view_port); tt_assert(parent); if (view_port->on_show) { - tt_lv_obj_set_style_no_padding(parent); + lvgl::obj_set_style_no_padding(parent); view_port->on_show(view_port->app, parent); } } @@ -38,3 +40,5 @@ void view_port_hide(ViewPort* view_port) { view_port->on_hide(view_port->app); } } + +} // namespace diff --git a/tactility/src/services/gui/view_port.h b/Tactility/Source/Services/Gui/ViewPort.h similarity index 91% rename from tactility/src/services/gui/view_port.h rename to Tactility/Source/Services/Gui/ViewPort.h index cc474f98..9e848f05 100644 --- a/tactility/src/services/gui/view_port.h +++ b/Tactility/Source/Services/Gui/ViewPort.h @@ -1,12 +1,10 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - -#include "app.h" +#include "App.h" #include "lvgl.h" +namespace tt::service::gui { + /** ViewPort Draw callback * @warning called from GUI thread */ @@ -45,6 +43,4 @@ ViewPort* view_port_alloc( */ void view_port_free(ViewPort* view_port); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility/src/services/loader/loader.c b/Tactility/Source/Services/Loader/Loader.cpp similarity index 66% rename from tactility/src/services/loader/loader.c rename to Tactility/Source/Services/Loader/Loader.cpp index 8d647aec..665471eb 100644 --- a/tactility/src/services/loader/loader.c +++ b/Tactility/Source/Services/Loader/Loader.cpp @@ -1,20 +1,20 @@ -#include "app_i.h" -#include "app_manifest.h" -#include "app_manifest_registry.h" -#include "loader_i.h" -#include "service_manifest.h" -#include "services/gui/gui.h" -#include +#include "ApiLock.h" +#include "AppManifest.h" +#include "AppManifestRegistry.h" +#include "App_i.h" +#include "ServiceManifest.h" +#include "Services/Gui/Gui.h" +#include "Services/Loader/Loader_i.h" #ifdef ESP_PLATFORM #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" #else #include "FreeRTOS.h" -#include "semphr.h" #endif +namespace tt::service::loader { + #define TAG "loader" typedef struct { @@ -24,19 +24,18 @@ typedef struct { // Forward declarations static int32_t loader_main(void* p); -static Loader* loader_singleton = NULL; +static Loader* loader_singleton = nullptr; static Loader* loader_alloc() { - tt_check(loader_singleton == NULL); - loader_singleton = malloc(sizeof(Loader)); + assert(loader_singleton == nullptr); + loader_singleton = new Loader(); loader_singleton->pubsub_internal = tt_pubsub_alloc(); loader_singleton->pubsub_external = tt_pubsub_alloc(); - loader_singleton->queue = tt_message_queue_alloc(1, sizeof(LoaderMessage)); - loader_singleton->thread = tt_thread_alloc_ex( + loader_singleton->thread = thread_alloc_ex( "loader", 4096, // Last known minimum was 2400 for starting Hello World app &loader_main, - NULL + nullptr ); loader_singleton->mutex = tt_mutex_alloc(MutexTypeRecursive); loader_singleton->app_stack_index = -1; @@ -45,67 +44,67 @@ static Loader* loader_alloc() { } static void loader_free() { - tt_check(loader_singleton != NULL); - tt_thread_free(loader_singleton->thread); + tt_assert(loader_singleton != nullptr); + thread_free(loader_singleton->thread); tt_pubsub_free(loader_singleton->pubsub_internal); tt_pubsub_free(loader_singleton->pubsub_external); - tt_message_queue_free(loader_singleton->queue); tt_mutex_free(loader_singleton->mutex); - free(loader_singleton); + delete loader_singleton; + loader_singleton = nullptr; } -void loader_lock() { +static void loader_lock() { tt_assert(loader_singleton); tt_assert(loader_singleton->mutex); tt_check(tt_mutex_acquire(loader_singleton->mutex, TtWaitForever) == TtStatusOk); } -void loader_unlock() { +static void loader_unlock() { tt_assert(loader_singleton); tt_assert(loader_singleton->mutex); tt_check(tt_mutex_release(loader_singleton->mutex) == TtStatusOk); } -LoaderStatus loader_start_app(const char* id, bool blocking, Bundle _Nullable bundle) { - tt_check(loader_singleton); +LoaderStatus start_app(const std::string& id, bool blocking, const Bundle& bundle) { + tt_assert(loader_singleton); LoaderMessageLoaderStatusResult result = { .value = LoaderStatusOk }; - LoaderMessage message = { - .type = LoaderMessageTypeAppStart, - .start.id = id, - .start.bundle = bundle, - .status_value = &result, - .api_lock = blocking ? tt_api_lock_alloc_locked() : NULL - }; + auto* start_message = new LoaderMessageAppStart(id, bundle); + LoaderMessage message(start_message, result); - tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever); + ApiLock lock = blocking ? tt_api_lock_alloc_locked() : nullptr; + if (lock != nullptr) { + message.setLock(lock); + } - if (blocking) { + loader_singleton->queue.put(&message, TtWaitForever); + + if (lock != nullptr) { tt_api_lock_wait_unlock_and_free(message.api_lock); } return result.value; } -void loader_stop_app() { +void stop_app() { tt_check(loader_singleton); - LoaderMessage message = {.type = LoaderMessageTypeAppStop}; - tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever); + LoaderMessage message(LoaderMessageTypeAppStop); + loader_singleton->queue.put(&message, TtWaitForever); } -App _Nullable loader_get_current_app() { - tt_check(loader_singleton); +App _Nullable get_current_app() { + tt_assert(loader_singleton); loader_lock(); App app = (loader_singleton->app_stack_index >= 0) ? loader_singleton->app_stack[loader_singleton->app_stack_index] - : NULL; + : nullptr; loader_unlock(); return app; } -PubSub* loader_get_pubsub() { +PubSub* get_pubsub() { tt_assert(loader_singleton); // it's safe to return pubsub without locking // because it's never freed and loader is never exited @@ -131,13 +130,13 @@ static const char* app_state_to_string(AppState state) { } static void app_transition_to_state(App app, AppState state) { - const AppManifest* manifest = tt_app_get_manifest(app); + const AppManifest& manifest = tt_app_get_manifest(app); const AppState old_state = tt_app_get_state(app); TT_LOG_I( TAG, "app \"%s\" state: %s -> %s", - manifest->id, + manifest.id.c_str(), app_state_to_string(old_state), app_state_to_string(state) ); @@ -147,46 +146,48 @@ static void app_transition_to_state(App app, AppState state) { tt_app_set_state(app, AppStateInitial); break; case AppStateStarted: - if (manifest->on_start != NULL) { - manifest->on_start(app); + if (manifest.on_start != nullptr) { + manifest.on_start(app); } tt_app_set_state(app, AppStateStarted); break; - case AppStateShowing: + case AppStateShowing: { LoaderEvent event_showing = { .type = LoaderEventTypeApplicationShowing, .app_showing = { - .app = app + .app = static_cast(app) } }; tt_pubsub_publish(loader_singleton->pubsub_external, &event_showing); tt_app_set_state(app, AppStateShowing); break; - case AppStateHiding: + } + case AppStateHiding: { LoaderEvent event_hiding = { .type = LoaderEventTypeApplicationHiding, .app_hiding = { - .app = app + .app = static_cast(app) } }; tt_pubsub_publish(loader_singleton->pubsub_external, &event_hiding); tt_app_set_state(app, AppStateHiding); break; + } case AppStateStopped: - if (manifest->on_stop) { - manifest->on_stop(app); + if (manifest.on_stop) { + manifest.on_stop(app); } - tt_app_set_data(app, NULL); + tt_app_set_data(app, nullptr); tt_app_set_state(app, AppStateStopped); break; } } -LoaderStatus loader_do_start_app_with_manifest( +static LoaderStatus loader_do_start_app_with_manifest( const AppManifest* manifest, - Bundle _Nullable bundle + const Bundle& bundle ) { - TT_LOG_I(TAG, "start with manifest %s", manifest->id); + TT_LOG_I(TAG, "start with manifest %s", manifest->id.c_str()); loader_lock(); @@ -198,8 +199,8 @@ LoaderStatus loader_do_start_app_with_manifest( int8_t previous_index = loader_singleton->app_stack_index; loader_singleton->app_stack_index++; - App app = tt_app_alloc(manifest, bundle); - tt_assert(loader_singleton->app_stack[loader_singleton->app_stack_index] == NULL); + App app = tt_app_alloc(*manifest, bundle); + tt_check(loader_singleton->app_stack[loader_singleton->app_stack_index] == nullptr); loader_singleton->app_stack[loader_singleton->app_stack_index] = app; app_transition_to_state(app, AppStateInitial); app_transition_to_state(app, AppStateStarted); @@ -220,7 +221,7 @@ LoaderStatus loader_do_start_app_with_manifest( LoaderEvent event_external = { .type = LoaderEventTypeApplicationStarted, .app_started = { - .app = app + .app = static_cast(app) } }; tt_pubsub_publish(loader_singleton->pubsub_external, &event_external); @@ -228,14 +229,14 @@ LoaderStatus loader_do_start_app_with_manifest( return LoaderStatusOk; } -static LoaderStatus loader_do_start_by_id( - const char* id, - Bundle _Nullable bundle +static LoaderStatus do_start_by_id( + const std::string& id, + const Bundle& bundle ) { - TT_LOG_I(TAG, "Start by id %s", id); + TT_LOG_I(TAG, "Start by id %s", id.c_str()); - const AppManifest* manifest = tt_app_manifest_registry_find_by_id(id); - if (manifest == NULL) { + const AppManifest* manifest = app_manifest_registry_find_by_id(id); + if (manifest == nullptr) { return LoaderStatusErrorUnknownApp; } else { return loader_do_start_app_with_manifest(manifest, bundle); @@ -243,7 +244,7 @@ static LoaderStatus loader_do_start_by_id( } -static void loader_do_stop_app() { +static void do_stop_app() { loader_lock(); int8_t current_app_index = loader_singleton->app_stack_index; @@ -262,12 +263,12 @@ static void loader_do_stop_app() { // Stop current app App app_to_stop = loader_singleton->app_stack[current_app_index]; - const AppManifest* manifest = tt_app_get_manifest(app_to_stop); + const AppManifest& manifest = tt_app_get_manifest(app_to_stop); app_transition_to_state(app_to_stop, AppStateHiding); app_transition_to_state(app_to_stop, AppStateStopped); tt_app_free(app_to_stop); - loader_singleton->app_stack[current_app_index] = NULL; + loader_singleton->app_stack[current_app_index] = nullptr; loader_singleton->app_stack_index--; #ifdef ESP_PLATFORM @@ -275,7 +276,7 @@ static void loader_do_stop_app() { #endif // Resume previous app - tt_assert(loader_singleton->app_stack[loader_singleton->app_stack_index] != NULL); + tt_assert(loader_singleton->app_stack[loader_singleton->app_stack_index] != nullptr); App app_to_resume = loader_singleton->app_stack[loader_singleton->app_stack_index]; app_transition_to_state(app_to_resume, AppStateShowing); @@ -287,7 +288,7 @@ static void loader_do_stop_app() { LoaderEvent event_external = { .type = LoaderEventTypeApplicationStopped, .app_stopped = { - .manifest = manifest + .manifest = &manifest } }; tt_pubsub_publish(loader_singleton->pubsub_external, &event_external); @@ -298,25 +299,28 @@ static int32_t loader_main(TT_UNUSED void* parameter) { LoaderMessage message; bool exit_requested = false; while (!exit_requested) { - tt_check(loader_singleton != NULL); - if (tt_message_queue_get(loader_singleton->queue, &message, TtWaitForever) == TtStatusOk) { + tt_assert(loader_singleton != nullptr); + if (loader_singleton->queue.get(&message, TtWaitForever) == TtStatusOk) { TT_LOG_I(TAG, "Processing message of type %d", message.type); switch (message.type) { case LoaderMessageTypeAppStart: - message.status_value->value = loader_do_start_by_id( - message.start.id, - message.start.bundle + message.result.status_value.value = do_start_by_id( + message.payload.start->id, + message.payload.start->bundle ); - if (message.api_lock) { + if (message.api_lock != nullptr) { tt_api_lock_unlock(message.api_lock); } + message.cleanup(); break; case LoaderMessageTypeAppStop: - loader_do_stop_app(); + do_stop_app(); break; case LoaderMessageTypeServiceStop: exit_requested = true; break; + case LoaderMessageTypeNone: + break; } } } @@ -326,34 +330,33 @@ static int32_t loader_main(TT_UNUSED void* parameter) { // region AppManifest -static void loader_start(TT_UNUSED Service service) { - tt_check(loader_singleton == NULL); +static void loader_start(TT_UNUSED Service& service) { + tt_check(loader_singleton == nullptr); loader_singleton = loader_alloc(); - tt_thread_set_priority(loader_singleton->thread, THREAD_PRIORITY_SERVICE); - tt_thread_start(loader_singleton->thread); + thread_set_priority(loader_singleton->thread, THREAD_PRIORITY_SERVICE); + thread_start(loader_singleton->thread); } -static void loader_stop(TT_UNUSED Service service) { - tt_check(loader_singleton != NULL); +static void loader_stop(TT_UNUSED Service& service) { + tt_check(loader_singleton != nullptr); // Send stop signal to thread and wait for thread to finish - LoaderMessage message = { - .api_lock = NULL, - .type = LoaderMessageTypeServiceStop - }; - tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever); - tt_thread_join(loader_singleton->thread); - tt_thread_free(loader_singleton->thread); + LoaderMessage message(LoaderMessageTypeServiceStop); + loader_singleton->queue.put(&message, TtWaitForever); + thread_join(loader_singleton->thread); + thread_free(loader_singleton->thread); loader_free(); - loader_singleton = NULL; + loader_singleton = nullptr; } -const ServiceManifest loader_service = { - .id = "loader", +extern const ServiceManifest manifest = { + .id = "Loader", .on_start = &loader_start, .on_stop = &loader_stop }; // endregion + +} // namespace diff --git a/tactility/src/services/loader/loader.h b/Tactility/Source/Services/Loader/Loader.h similarity index 77% rename from tactility/src/services/loader/loader.h rename to Tactility/Source/Services/Loader/Loader.h index be0d6565..2a81f8ca 100644 --- a/tactility/src/services/loader/loader.h +++ b/Tactility/Source/Services/Loader/Loader.h @@ -1,14 +1,12 @@ #pragma once -#include "app_manifest.h" -#include "bundle.h" -#include "pubsub.h" -#include "service_manifest.h" -#include "tactility_core.h" +#include "AppManifest.h" +#include "Bundle.h" +#include "Pubsub.h" +#include "ServiceManifest.h" +#include "TactilityCore.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::service::loader { typedef struct Loader Loader; @@ -59,20 +57,18 @@ typedef struct { * @param[in] bundle optional bundle. Ownership is transferred to Loader. * @return LoaderStatus */ -LoaderStatus loader_start_app(const char* id, bool blocking, Bundle _Nullable bundle); +LoaderStatus start_app(const std::string& id, bool blocking, const Bundle& bundle); /** * @brief Stop the currently showing app. Show the previous app if any app was still running. */ -void loader_stop_app(); +void stop_app(); -App _Nullable loader_get_current_app(); +App _Nullable get_current_app(); /** * @brief PubSub for LoaderEvent */ -PubSub* loader_get_pubsub(); +PubSub* get_pubsub(); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/Tactility/Source/Services/Screenshot/Screenshot.cpp b/Tactility/Source/Services/Screenshot/Screenshot.cpp new file mode 100644 index 00000000..0ebf42ed --- /dev/null +++ b/Tactility/Source/Services/Screenshot/Screenshot.cpp @@ -0,0 +1,141 @@ +#include "Screenshot.h" +#include + +#include "Mutex.h" +#include "ScreenshotTask.h" +#include "Service.h" +#include "ServiceRegistry.h" +#include "TactilityCore.h" + +namespace tt::service::screenshot { + +#define TAG "sdcard_service" + +extern const ServiceManifest manifest; + +typedef struct { + Mutex* mutex; + ScreenshotTask* task; + ScreenshotMode mode; +} ServiceData; + +static ServiceData* service_data_alloc() { + auto* data = static_cast(malloc(sizeof(ServiceData))); + *data = (ServiceData) { + .mutex = tt_mutex_alloc(MutexTypeNormal), + .task = nullptr, + .mode = ScreenshotModeNone + }; + return data; +} + +static void service_data_free(ServiceData* data) { + tt_mutex_free(data->mutex); +} + +static void service_data_lock(ServiceData* data) { + tt_check(tt_mutex_acquire(data->mutex, TtWaitForever) == TtStatusOk); +} + +static void service_data_unlock(ServiceData* data) { + tt_check(tt_mutex_release(data->mutex) == TtStatusOk); +} + +static void on_start(Service& service) { + ServiceData* data = service_data_alloc(); + service.setData(data); +} + +static void on_stop(Service& service) { + auto* data = static_cast(service.getData()); + if (data->task) { + task_free(data->task); + data->task = nullptr; + } + tt_mutex_free(data->mutex); + service_data_free(data); +} + +void start_apps(const char* path) { + _Nullable auto* service = service_find(manifest.id); + if (service == nullptr) { + TT_LOG_E(TAG, "Service not found"); + return; + } + + auto* data = static_cast(service->getData()); + service_data_lock(data); + if (data->task == nullptr) { + data->task = task_alloc(); + data->mode = ScreenshotModeApps; + task_start_apps(data->task, path); + } else { + TT_LOG_E(TAG, "Screenshot task already running"); + } + service_data_unlock(data); +} + +void start_timed(const char* path, uint8_t delay_in_seconds, uint8_t amount) { + _Nullable auto* service = service_find(manifest.id); + if (service == nullptr) { + TT_LOG_E(TAG, "Service not found"); + return; + } + + auto* data = static_cast(service->getData()); + service_data_lock(data); + if (data->task == nullptr) { + data->task = task_alloc(); + data->mode = ScreenshotModeTimed; + task_start_timed(data->task, path, delay_in_seconds, amount); + } else { + TT_LOG_E(TAG, "Screenshot task already running"); + } + service_data_unlock(data); +} + +void stop() { + _Nullable Service* service = service_find(manifest.id); + if (service == nullptr) { + TT_LOG_E(TAG, "Service not found"); + return; + } + + auto data = static_cast(service->getData()); + service_data_lock(data); + if (data->task != nullptr) { + task_stop(data->task); + task_free(data->task); + data->task = nullptr; + data->mode = ScreenshotModeNone; + } else { + TT_LOG_E(TAG, "Screenshot task not running"); + } + service_data_unlock(data); +} + +ScreenshotMode get_mode() { + _Nullable auto* service = service_find(manifest.id); + if (service == nullptr) { + TT_LOG_E(TAG, "Service not found"); + return ScreenshotModeNone; + } else { + auto* data = static_cast(service->getData()); + service_data_lock(data); + ScreenshotMode mode = data->mode; + service_data_unlock(data); + return mode; + } +} + +bool is_started() { + return get_mode() != ScreenshotModeNone; +} + +extern const ServiceManifest manifest = { + .id = "Screenshot", + .on_start = &on_start, + .on_stop = &on_stop +}; + +} // namespace diff --git a/tactility/src/services/screenshot/screenshot.h b/Tactility/Source/Services/Screenshot/Screenshot.h similarity index 58% rename from tactility/src/services/screenshot/screenshot.h rename to Tactility/Source/Services/Screenshot/Screenshot.h index a6e967a7..ee68872a 100644 --- a/tactility/src/services/screenshot/screenshot.h +++ b/Tactility/Source/Services/Screenshot/Screenshot.h @@ -1,11 +1,8 @@ #pragma once -#include -#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::service::screenshot { typedef enum { ScreenshotModeNone, @@ -18,19 +15,17 @@ typedef enum { * @param delay_in_seconds the delay before starting (and between successive screenshots) * @param amount 0 = indefinite, >0 for a specific */ -void tt_screenshot_start_timed(const char* path, uint8_t delay_in_seconds, uint8_t amount); +void start_timed(const char* path, uint8_t delay_in_seconds, uint8_t amount); /** @brief Starts taking screenshot when an app is started * @param path the path to store the screenshots in */ -void tt_screenshot_start_apps(const char* path); +void start_apps(const char* path); -void tt_screenshot_stop(); +void stop(); -ScreenshotMode tt_screenshot_get_mode(); +ScreenshotMode get_mode(); -bool tt_screenshot_is_started(); +bool is_started(); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/tactility/src/services/screenshot/screenshot_task.c b/Tactility/Source/Services/Screenshot/ScreenshotTask.cpp similarity index 58% rename from tactility/src/services/screenshot/screenshot_task.c rename to Tactility/Source/Services/Screenshot/ScreenshotTask.cpp index b35d292c..e405716a 100644 --- a/tactility/src/services/screenshot/screenshot_task.c +++ b/Tactility/Source/Services/Screenshot/ScreenshotTask.cpp @@ -1,12 +1,14 @@ -#include "screenshot_task.h" +#include "ScreenshotTask.h" #include "lv_screenshot.h" -#include "app.h" -#include "mutex.h" -#include "services/loader/loader.h" -#include "tactility_core.h" -#include "thread.h" -#include "ui/lvgl_sync.h" +#include "App.h" +#include "Mutex.h" +#include "TactilityCore.h" +#include "Thread.h" +#include "Services/Loader/Loader.h" +#include "Ui/LvglSync.h" + +namespace tt::service::screenshot { #define TAG "screenshot_task" @@ -29,44 +31,44 @@ typedef struct { ScreenshotTaskWork work; } ScreenshotTaskData; -static void screenshot_task_lock(ScreenshotTaskData* data) { +static void task_lock(ScreenshotTaskData* data) { tt_check(tt_mutex_acquire(data->mutex, TtWaitForever) == TtStatusOk); } -static void screenshot_task_unlock(ScreenshotTaskData* data) { +static void task_unlock(ScreenshotTaskData* data) { tt_check(tt_mutex_release(data->mutex) == TtStatusOk); } -ScreenshotTask* screenshot_task_alloc() { - ScreenshotTaskData* data = malloc(sizeof(ScreenshotTaskData)); +ScreenshotTask* task_alloc() { + auto* data = static_cast(malloc(sizeof(ScreenshotTaskData))); *data = (ScreenshotTaskData) { - .thread = NULL, + .thread = nullptr, .mutex = tt_mutex_alloc(MutexTypeRecursive), .interrupted = false }; return data; } -void screenshot_task_free(ScreenshotTask* task) { - ScreenshotTaskData* data = (ScreenshotTaskData*)task; +void task_free(ScreenshotTask* task) { + auto* data = static_cast(task); if (data->thread) { - screenshot_task_stop(data); + task_stop(data); } } static bool is_interrupted(ScreenshotTaskData* data) { - screenshot_task_lock(data); + task_lock(data); bool interrupted = data->interrupted; - screenshot_task_unlock(data); + task_unlock(data); return interrupted; } static int32_t screenshot_task(void* context) { - ScreenshotTaskData* data = (ScreenshotTaskData*)context; + auto* data = static_cast(context); bool interrupted = false; uint8_t screenshots_taken = 0; - const char* last_app_id = NULL; + std::string last_app_id; while (!interrupted) { interrupted = is_interrupted(data); @@ -74,7 +76,7 @@ static int32_t screenshot_task(void* context) { if (data->work.type == TASK_WORK_TYPE_DELAY) { // Splitting up the delays makes it easier to stop the service for (int i = 0; i < (data->work.delay_in_seconds * 10) && !is_interrupted(data); ++i){ - tt_delay_ms(100); + delay_ms(100); } if (is_interrupted(data)) { @@ -84,100 +86,102 @@ static int32_t screenshot_task(void* context) { screenshots_taken++; char filename[SCREENSHOT_PATH_LIMIT + 32]; sprintf(filename, "%s/screenshot-%d.png", data->work.path, screenshots_taken); - tt_lvgl_lock(TtWaitForever); + lvgl::lock(TtWaitForever); if (lv_screenshot_create(lv_scr_act(), LV_COLOR_FORMAT_NATIVE, LV_100ASK_SCREENSHOT_SV_PNG, filename)){ TT_LOG_I(TAG, "Screenshot saved to %s", filename); } else { TT_LOG_E(TAG, "Screenshot not saved to %s", filename); } - tt_lvgl_unlock(); + lvgl::unlock(); if (data->work.amount > 0 && screenshots_taken >= data->work.amount) { break; // Interrupted loop } } else if (data->work.type == TASK_WORK_TYPE_APPS) { - App _Nullable app = loader_get_current_app(); + App _Nullable app = loader::get_current_app(); if (app) { - const AppManifest* manifest = tt_app_get_manifest(app); - if (manifest->id != last_app_id) { - tt_delay_ms(100); - last_app_id = manifest->id; + const AppManifest& manifest = tt_app_get_manifest(app); + if (manifest.id != last_app_id) { + delay_ms(100); + last_app_id = manifest.id; char filename[SCREENSHOT_PATH_LIMIT + 32]; - sprintf(filename, "%s/screenshot-%s.png", data->work.path, manifest->id); - tt_lvgl_lock(TtWaitForever); + sprintf(filename, "%s/screenshot-%s.png", data->work.path, manifest.id.c_str()); + lvgl::lock(TtWaitForever); if (lv_screenshot_create(lv_scr_act(), LV_COLOR_FORMAT_NATIVE, LV_100ASK_SCREENSHOT_SV_PNG, filename)){ TT_LOG_I(TAG, "Screenshot saved to %s", filename); } else { TT_LOG_E(TAG, "Screenshot not saved to %s", filename); } - tt_lvgl_unlock(); + lvgl::unlock(); } } - tt_delay_ms(250); + delay_ms(250); } } return 0; } -static void screenshot_task_start(ScreenshotTaskData* data) { - screenshot_task_lock(data); +static void task_start(ScreenshotTaskData* data) { + task_lock(data); tt_check(data->thread == NULL); - data->thread = tt_thread_alloc_ex( + data->thread = thread_alloc_ex( "screenshot", 8192, &screenshot_task, data ); - tt_thread_start(data->thread); - screenshot_task_unlock(data); + thread_start(data->thread); + task_unlock(data); } -void screenshot_task_start_apps(ScreenshotTask* task, const char* path) { +void task_start_apps(ScreenshotTask* task, const char* path) { tt_check(strlen(path) < (SCREENSHOT_PATH_LIMIT - 1)); - ScreenshotTaskData* data = (ScreenshotTaskData*)task; - screenshot_task_lock(data); - if (data->thread == NULL) { + auto* data = static_cast(task); + task_lock(data); + if (data->thread == nullptr) { data->interrupted = false; data->work.type = TASK_WORK_TYPE_APPS; strcpy(data->work.path, path); - screenshot_task_start(data); + task_start(data); } else { TT_LOG_E(TAG, "Task was already running"); } - screenshot_task_unlock(data); + task_unlock(data); } -void screenshot_task_start_timed(ScreenshotTask* task, const char* path, uint8_t delay_in_seconds, uint8_t amount) { +void task_start_timed(ScreenshotTask* task, const char* path, uint8_t delay_in_seconds, uint8_t amount) { tt_check(strlen(path) < (SCREENSHOT_PATH_LIMIT - 1)); - ScreenshotTaskData* data = (ScreenshotTaskData*)task; - screenshot_task_lock(data); - if (data->thread == NULL) { + auto* data = static_cast(task); + task_lock(data); + if (data->thread == nullptr) { data->interrupted = false; data->work.type = TASK_WORK_TYPE_DELAY; data->work.delay_in_seconds = delay_in_seconds; data->work.amount = amount; strcpy(data->work.path, path); - screenshot_task_start(data); + task_start(data); } else { TT_LOG_E(TAG, "Task was already running"); } - screenshot_task_unlock(data); + task_unlock(data); } -void screenshot_task_stop(ScreenshotTask* task) { - ScreenshotTaskData* data = (ScreenshotTaskData*)task; - if (data->thread != NULL) { - screenshot_task_lock(data); +void task_stop(ScreenshotTask* task) { + auto* data = static_cast(task); + if (data->thread != nullptr) { + task_lock(data); data->interrupted = true; - screenshot_task_unlock(data); + task_unlock(data); - tt_thread_join(data->thread); + thread_join(data->thread); - screenshot_task_lock(data); - tt_thread_free(data->thread); - data->thread = NULL; - screenshot_task_unlock(data); + task_lock(data); + thread_free(data->thread); + data->thread = nullptr; + task_unlock(data); } -} \ No newline at end of file +} + +} // namespace diff --git a/tactility/src/services/screenshot/screenshot_task.h b/Tactility/Source/Services/Screenshot/ScreenshotTask.h similarity index 58% rename from tactility/src/services/screenshot/screenshot_task.h rename to Tactility/Source/Services/Screenshot/ScreenshotTask.h index 606f1f19..b396632d 100644 --- a/tactility/src/services/screenshot/screenshot_task.h +++ b/Tactility/Source/Services/Screenshot/ScreenshotTask.h @@ -1,16 +1,14 @@ #pragma once -#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::service::screenshot { typedef void ScreenshotTask; -ScreenshotTask* screenshot_task_alloc(); +ScreenshotTask* task_alloc(); -void screenshot_task_free(ScreenshotTask* task); +void task_free(ScreenshotTask* task); /** @brief Start taking screenshots after a certain delay * @param task the screenshot task @@ -18,19 +16,17 @@ void screenshot_task_free(ScreenshotTask* task); * @param delay_in_seconds the delay before starting (and between successive screenshots) * @param amount 0 = indefinite, >0 for a specific */ -void screenshot_task_start_timed(ScreenshotTask* task, const char* path, uint8_t delay_in_seconds, uint8_t amount); +void task_start_timed(ScreenshotTask* task, const char* path, uint8_t delay_in_seconds, uint8_t amount); /** @brief Start taking screenshot whenever an app is started * @param task the screenshot task * @param path the path to store the screenshots at */ -void screenshot_task_start_apps(ScreenshotTask* task, const char* path); +void task_start_apps(ScreenshotTask* task, const char* path); /** @brief Stop taking screenshots * @param task the screenshot task */ -void screenshot_task_stop(ScreenshotTask* task); +void task_stop(ScreenshotTask* task); -#ifdef __cplusplus } -#endif \ No newline at end of file diff --git a/tactility/src/services/statusbar_updater/statusbar_updater.c b/Tactility/Source/Services/Statusbar/Statusbar.cpp similarity index 55% rename from tactility/src/services/statusbar_updater/statusbar_updater.c rename to Tactility/Source/Services/Statusbar/Statusbar.cpp index 3634fc76..62fe79dd 100644 --- a/tactility/src/services/statusbar_updater/statusbar_updater.c +++ b/Tactility/Source/Services/Statusbar/Statusbar.cpp @@ -1,11 +1,13 @@ -#include "assets.h" -#include "mutex.h" -#include "power.h" -#include "sdcard.h" -#include "service.h" -#include "services/wifi/wifi.h" -#include "tactility.h" -#include "ui/statusbar.h" +#include "Assets.h" +#include "Hal/Power.h" +#include "Hal/Sdcard.h" +#include "Mutex.h" +#include "Service.h" +#include "Services/Wifi/Wifi.h" +#include "Tactility.h" +#include "Ui/Statusbar.h" + +namespace tt::service::statusbar { #define TAG "statusbar_service" @@ -23,7 +25,7 @@ typedef struct { // region wifi -const char* wifi_get_status_icon_for_rssi(int rssi, bool secured) { +const char* get_status_icon_for_rssi(int rssi, bool secured) { if (rssi > 0) { return TT_ASSETS_ICON_WIFI_CONNECTION_ISSUE; } else if (rssi >= -30) { @@ -39,30 +41,30 @@ const char* wifi_get_status_icon_for_rssi(int rssi, bool secured) { } } -static const char* wifi_get_status_icon(WifiRadioState state, bool secure) { +static const char* wifi_get_status_icon(wifi::WifiRadioState state, bool secure) { int rssi; switch (state) { - case WIFI_RADIO_ON_PENDING: - case WIFI_RADIO_ON: - case WIFI_RADIO_OFF_PENDING: - case WIFI_RADIO_OFF: + case wifi::WIFI_RADIO_ON_PENDING: + case wifi::WIFI_RADIO_ON: + case wifi::WIFI_RADIO_OFF_PENDING: + case wifi::WIFI_RADIO_OFF: return TT_ASSETS_ICON_WIFI_OFF; - case WIFI_RADIO_CONNECTION_PENDING: + case wifi::WIFI_RADIO_CONNECTION_PENDING: return TT_ASSETS_ICON_WIFI_FIND; - case WIFI_RADIO_CONNECTION_ACTIVE: - rssi = wifi_get_rssi(); - return wifi_get_status_icon_for_rssi(rssi, secure); + case wifi::WIFI_RADIO_CONNECTION_ACTIVE: + rssi = wifi::get_rssi(); + return get_status_icon_for_rssi(rssi, secure); default: - tt_crash_implementation("not implemented"); + tt_crash("not implemented"); } } static void update_wifi_icon(ServiceData* data) { - WifiRadioState radio_state = wifi_get_radio_state(); - bool is_secure = wifi_is_connection_secure(); + wifi::WifiRadioState radio_state = wifi::get_radio_state(); + bool is_secure = wifi::is_connection_secure(); const char* desired_icon = wifi_get_status_icon(radio_state, is_secure); if (data->wifi_last_icon != desired_icon) { - tt_statusbar_icon_set_image(data->wifi_icon_id, desired_icon); + lvgl::statusbar_icon_set_image(data->wifi_icon_id, desired_icon); data->wifi_last_icon = desired_icon; } } @@ -71,24 +73,24 @@ static void update_wifi_icon(ServiceData* data) { // region sdcard -static _Nullable const char* sdcard_get_status_icon(SdcardState state) { +static _Nullable const char* sdcard_get_status_icon(hal::sdcard::State state) { switch (state) { - case SdcardStateMounted: + case hal::sdcard::StateMounted: return TT_ASSETS_ICON_SDCARD; - case SdcardStateError: - case SdcardStateUnmounted: + case hal::sdcard::StateError: + case hal::sdcard::StateUnmounted: return TT_ASSETS_ICON_SDCARD_ALERT; default: - return NULL; + return nullptr; } } static void update_sdcard_icon(ServiceData* data) { - SdcardState state = tt_sdcard_get_state(); + hal::sdcard::State state = hal::sdcard::get_state(); const char* desired_icon = sdcard_get_status_icon(state); if (data->sdcard_last_icon != desired_icon) { - tt_statusbar_icon_set_image(data->sdcard_icon_id, desired_icon); - tt_statusbar_icon_set_visibility(data->sdcard_icon_id, desired_icon != NULL); + lvgl::statusbar_icon_set_image(data->sdcard_icon_id, desired_icon); + lvgl::statusbar_icon_set_visibility(data->sdcard_icon_id, desired_icon != nullptr); data->sdcard_last_icon = desired_icon; } } @@ -98,8 +100,8 @@ static void update_sdcard_icon(ServiceData* data) { // region power static _Nullable const char* power_get_status_icon() { - _Nullable const Power* power = tt_get_config()->hardware->power; - if (power != NULL) { + _Nullable const hal::Power* power = get_config()->hardware->power; + if (power != nullptr) { uint8_t charge = power->get_charge_level(); if (charge >= 230) { return TT_ASSETS_ICON_POWER_100; @@ -113,15 +115,15 @@ static _Nullable const char* power_get_status_icon() { return TT_ASSETS_ICON_POWER_020; } } else { - return NULL; + return nullptr; } } static void update_power_icon(ServiceData* data) { const char* desired_icon = power_get_status_icon(); if (data->power_last_icon != desired_icon) { - tt_statusbar_icon_set_image(data->power_icon_id, desired_icon); - tt_statusbar_icon_set_visibility(data->power_icon_id, desired_icon != NULL); + lvgl::statusbar_icon_set_image(data->power_icon_id, desired_icon); + lvgl::statusbar_icon_set_visibility(data->power_icon_id, desired_icon != nullptr); data->power_last_icon = desired_icon; } } @@ -131,20 +133,20 @@ static void update_power_icon(ServiceData* data) { // region service static ServiceData* service_data_alloc() { - ServiceData* data = malloc(sizeof(ServiceData)); + auto* data = static_cast(malloc(sizeof(ServiceData))); *data = (ServiceData) { .mutex = tt_mutex_alloc(MutexTypeNormal), - .thread = tt_thread_alloc(), + .thread = thread_alloc(), .service_interrupted = false, - .wifi_icon_id = tt_statusbar_icon_add(NULL), - .wifi_last_icon = NULL, - .sdcard_icon_id = tt_statusbar_icon_add(NULL), - .sdcard_last_icon = NULL, - .power_icon_id = tt_statusbar_icon_add(NULL), - .power_last_icon = NULL + .wifi_icon_id = lvgl::statusbar_icon_add(nullptr), + .wifi_last_icon = nullptr, + .sdcard_icon_id = lvgl::statusbar_icon_add(nullptr), + .sdcard_last_icon = nullptr, + .power_icon_id = lvgl::statusbar_icon_add(nullptr), + .power_last_icon = nullptr }; - tt_statusbar_icon_set_visibility(data->wifi_icon_id, true); + lvgl::statusbar_icon_set_visibility(data->wifi_icon_id, true); update_wifi_icon(data); update_sdcard_icon(data); // also updates visibility update_power_icon(data); @@ -154,10 +156,10 @@ static ServiceData* service_data_alloc() { static void service_data_free(ServiceData* data) { tt_mutex_free(data->mutex); - tt_thread_free(data->thread); - tt_statusbar_icon_remove(data->wifi_icon_id); - tt_statusbar_icon_remove(data->sdcard_icon_id); - tt_statusbar_icon_remove(data->power_icon_id); + thread_free(data->thread); + lvgl::statusbar_icon_remove(data->wifi_icon_id); + lvgl::statusbar_icon_remove(data->sdcard_icon_id); + lvgl::statusbar_icon_remove(data->power_icon_id); free(data); } @@ -171,46 +173,47 @@ static void service_data_unlock(ServiceData* data) { int32_t service_main(TT_UNUSED void* parameter) { TT_LOG_I(TAG, "Started main loop"); - ServiceData* data = (ServiceData*)parameter; - tt_check(data != NULL); + auto* data = (ServiceData*)parameter; + tt_assert(data != nullptr); while (!data->service_interrupted) { update_wifi_icon(data); update_sdcard_icon(data); update_power_icon(data); - tt_delay_ms(1000); + delay_ms(1000); } return 0; } -static void on_start(Service service) { +static void on_start(Service& service) { ServiceData* data = service_data_alloc(); + service.setData(data); - tt_service_set_data(service, data); - - tt_thread_set_callback(data->thread, service_main); - tt_thread_set_current_priority(ThreadPriorityLow); - tt_thread_set_stack_size(data->thread, 3000); - tt_thread_set_context(data->thread, data); - tt_thread_start(data->thread); + thread_set_callback(data->thread, service_main); + thread_set_current_priority(ThreadPriorityLow); + thread_set_stack_size(data->thread, 3000); + thread_set_context(data->thread, data); + thread_start(data->thread); } -static void on_stop(Service service) { - ServiceData* data = tt_service_get_data(service); +static void on_stop(Service& service) { + auto* data = static_cast(service.getData()); // Stop thread service_data_lock(data); data->service_interrupted = true; service_data_unlock(data); tt_mutex_release(data->mutex); - tt_thread_join(data->thread); + thread_join(data->thread); service_data_free(data); } -const ServiceManifest statusbar_updater_service = { - .id = "statusbar_updater", +extern const ServiceManifest manifest = { + .id = "Statusbar", .on_start = &on_start, .on_stop = &on_stop }; -// endregion service \ No newline at end of file +// endregion service + +} // namespace diff --git a/tactility/src/services/statusbar_updater/statusbar_updater.h b/Tactility/Source/Services/Statusbar/Statusbar.h similarity index 58% rename from tactility/src/services/statusbar_updater/statusbar_updater.h rename to Tactility/Source/Services/Statusbar/Statusbar.h index 3119e50d..60abf2d9 100644 --- a/tactility/src/services/statusbar_updater/statusbar_updater.h +++ b/Tactility/Source/Services/Statusbar/Statusbar.h @@ -1,10 +1,6 @@ #pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::service::statusbar { /** * Return the relevant icon asset from assets.h for the given inputs @@ -12,8 +8,6 @@ extern "C" { * @param secured whether the access point is a secured one (as in: not an open one) * @return */ -const char* wifi_get_status_icon_for_rssi(int rssi, bool secured); +const char* get_status_icon_for_rssi(int rssi, bool secured); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp new file mode 100644 index 00000000..f41f4863 --- /dev/null +++ b/Tactility/Source/Tactility.cpp @@ -0,0 +1,162 @@ +#include "Tactility.h" + +#include "AppManifestRegistry.h" +#include "LvglInit_i.h" +#include "ServiceRegistry.h" +#include "TactilityHeadless.h" +#include "Services/Loader/Loader.h" + +namespace tt { + +#define TAG "tactility" + +static const Configuration* config_instance = NULL; + +// region Default services + +namespace service { + namespace gui { extern const ServiceManifest manifest; } + namespace loader { extern const ServiceManifest manifest; } + namespace screenshot { extern const ServiceManifest manifest; } + namespace statusbar { extern const ServiceManifest manifest; } +} + +static const ServiceManifest* const system_services[] = { + &service::loader::manifest, + &service::gui::manifest, // depends on loader service +#ifndef ESP_PLATFORM // Screenshots don't work yet on ESP32 + &service::screenshot::manifest, +#endif + &service::statusbar::manifest +}; + +// endregion + +// region Default apps + +namespace app { + namespace desktop { extern const AppManifest manifest; } + namespace files { extern const AppManifest manifest; } + namespace gpio { extern const AppManifest manifest; } + namespace image_viewer { extern const AppManifest manifest; } + namespace screenshot { extern const AppManifest manifest; } + namespace settings { extern const AppManifest manifest; } + namespace settings::display { extern const AppManifest manifest; } + namespace settings::power { extern const AppManifest manifest; } + namespace system_info { extern const AppManifest manifest; } + namespace text_viewer { extern const AppManifest manifest; } + namespace wifi_connect { extern const AppManifest manifest; } + namespace wifi_manage { extern const AppManifest manifest; } +} + +#ifndef ESP_PLATFORM +extern const AppManifest screenshot_app; +#endif + +static const AppManifest* const system_apps[] = { + &app::desktop::manifest, + &app::files::manifest, + &app::gpio::manifest, + &app::image_viewer::manifest, + &app::settings::manifest, + &app::settings::display::manifest, + &app::system_info::manifest, + &app::text_viewer::manifest, + &app::wifi_connect::manifest, + &app::wifi_manage::manifest, +#ifndef ESP_PLATFORM + &app::screenshot::manifest, // Screenshots don't work yet on ESP32 +#endif +}; + +// endregion + +static void register_system_apps() { + TT_LOG_I(TAG, "Registering default apps"); + + int app_count = sizeof(system_apps) / sizeof(AppManifest*); + for (int i = 0; i < app_count; ++i) { + app_manifest_registry_add(system_apps[i]); + } + + if (get_config()->hardware->power != nullptr) { + app_manifest_registry_add(&app::settings::power::manifest); + } +} + +static void register_user_apps(const AppManifest* const apps[TT_CONFIG_APPS_LIMIT]) { + TT_LOG_I(TAG, "Registering user apps"); + for (size_t i = 0; i < TT_CONFIG_APPS_LIMIT; i++) { + const AppManifest* manifest = apps[i]; + if (manifest != nullptr) { + app_manifest_registry_add(manifest); + } else { + // reached end of list + break; + } + } +} + +static void register_and_start_system_services() { + TT_LOG_I(TAG, "Registering and starting system services"); + int app_count = sizeof(system_services) / sizeof(ServiceManifest*); + for (int i = 0; i < app_count; ++i) { + service_registry_add(system_services[i]); + tt_check(service_registry_start(system_services[i]->id)); + } +} + +static void register_and_start_user_services(const ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT]) { + TT_LOG_I(TAG, "Registering and starting user services"); + for (size_t i = 0; i < TT_CONFIG_SERVICES_LIMIT; i++) { + const ServiceManifest* manifest = services[i]; + if (manifest != nullptr) { + service_registry_add(manifest); + tt_check(service_registry_start(manifest->id)); + } else { + // reached end of list + break; + } + } +} + +void init(const Configuration* config) { + TT_LOG_I(TAG, "init started"); + + + // Assign early so starting services can use it + config_instance = config; + + headless_init(config->hardware); + + lvgl_init(config->hardware); + + app_manifest_registry_init(); + + // Note: the order of starting apps and services is critical! + // System services are registered first so the apps below can find them if needed + register_and_start_system_services(); + // Then we register system apps. They are not used/started yet. + register_system_apps(); + // Then we register and start user services. They are started after system app + // registration just in case they want to figure out which system apps are installed. + register_and_start_user_services(config->services); + // Now we register the user apps, as they might rely on the user services. + register_user_apps(config->apps); + + TT_LOG_I(TAG, "init starting desktop app"); + service::loader::start_app(app::desktop::manifest.id, true, Bundle()); + + if (config->auto_start_app_id) { + TT_LOG_I(TAG, "init auto-starting %s", config->auto_start_app_id); + service::loader::start_app(config->auto_start_app_id, true, Bundle()); + } + + TT_LOG_I(TAG, "init complete"); +} + +const Configuration* _Nullable get_config() { + return config_instance; +} + +} // namespace diff --git a/tactility/src/tactility.h b/Tactility/Source/Tactility.h similarity index 62% rename from tactility/src/tactility.h rename to Tactility/Source/Tactility.h index a66318bc..8e9f3d2d 100644 --- a/tactility/src/tactility.h +++ b/Tactility/Source/Tactility.h @@ -1,34 +1,30 @@ #pragma once -#include "app_manifest.h" -#include "hardware_config.h" -#include "service_manifest.h" -#include "tactility_config.h" +#include "AppManifest.h" +#include "Hal/Configuration.h" +#include "ServiceManifest.h" +#include "TactilityConfig.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { typedef struct { - const HardwareConfig* hardware; + const hal::Configuration* hardware; // List of user applications const AppManifest* const apps[TT_CONFIG_APPS_LIMIT]; const ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT]; const char* auto_start_app_id; -} Config; +} Configuration; /** * Attempts to initialize Tactility and all configured hardware. * @param config */ -void tt_init(const Config* config); +void init(const Configuration* config); /** * While technically nullable, this instance is always set if tt_init() succeeds. * @return the Configuration instance that was passed on to tt_init() if init is successful */ -const Config* _Nullable tt_get_config(); +const Configuration* _Nullable get_config(); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/Tactility/Source/TactilityConfig.h b/Tactility/Source/TactilityConfig.h new file mode 100644 index 00000000..65bce9d9 --- /dev/null +++ b/Tactility/Source/TactilityConfig.h @@ -0,0 +1,6 @@ +#pragma once + +#include "TactilityHeadlessConfig.h" + +#define TT_CONFIG_APPS_LIMIT 32 +#define TT_CONFIG_FORCE_ONSCREEN_KEYBOARD false \ No newline at end of file diff --git a/tactility/src/ui/label_utils.c b/Tactility/Source/Ui/LabelUtils.cpp similarity index 78% rename from tactility/src/ui/label_utils.c rename to Tactility/Source/Ui/LabelUtils.cpp index ca44d90e..850f2f91 100644 --- a/tactility/src/ui/label_utils.c +++ b/Tactility/Source/Ui/LabelUtils.cpp @@ -1,6 +1,7 @@ -#include "label_utils.h" -#include -#include +#include "LabelUtils.h" +#include "TactilityCore.h" + +namespace tt::lvgl { #define TAG "tt_lv_label" @@ -29,17 +30,17 @@ static long file_get_size(FILE* file) { static char* str_alloc_from_file(const char* filepath) { FILE* file = fopen(filepath, "rb"); - if (file == NULL) { + if (file == nullptr) { TT_LOG_E(TAG, "Failed to open %s", filepath); - return NULL; + return nullptr; } long content_length = file_get_size(file); - char* text_buffer = malloc(content_length + 1); - if (text_buffer == NULL) { + auto* text_buffer = static_cast(malloc(content_length + 1)); + if (text_buffer == nullptr) { TT_LOG_E(TAG, "Insufficient memory. Failed to allocate %ldl bytes.", content_length); - return NULL; + return nullptr; } int buffer; @@ -55,8 +56,10 @@ static char* str_alloc_from_file(const char* filepath) { return text_buffer; } -void tt_lv_label_set_text_file(lv_obj_t* label, const char* filepath) { +void label_set_text_file(lv_obj_t* label, const char* filepath) { char* text = str_alloc_from_file(filepath); lv_label_set_text(label, text); free(text); } + +} // namespace diff --git a/Tactility/Source/Ui/LabelUtils.h b/Tactility/Source/Ui/LabelUtils.h new file mode 100644 index 00000000..e4500231 --- /dev/null +++ b/Tactility/Source/Ui/LabelUtils.h @@ -0,0 +1,9 @@ +#pragma once + +#include "lvgl.h" + +namespace tt::lvgl { + +void label_set_text_file(lv_obj_t* label, const char* filepath); + +} // namespace diff --git a/tactility/src/ui/lvgl_keypad.c b/Tactility/Source/Ui/LvglKeypad.cpp similarity index 60% rename from tactility/src/ui/lvgl_keypad.c rename to Tactility/Source/Ui/LvglKeypad.cpp index 38d9d583..957a463e 100644 --- a/tactility/src/ui/lvgl_keypad.c +++ b/Tactility/Source/Ui/LvglKeypad.cpp @@ -1,23 +1,27 @@ -#include "lvgl_keypad.h" +#include "LvglKeypad.h" + +namespace tt::lvgl { static lv_indev_t* keyboard_device = NULL; -bool tt_lvgl_keypad_is_available() { +bool keypad_is_available() { return keyboard_device != NULL; } -void tt_lvgl_keypad_set_indev(lv_indev_t* device) { +void keypad_set_indev(lv_indev_t* device) { keyboard_device = device; } -void tt_lvgl_keypad_activate(lv_group_t* group) { +void keypad_activate(lv_group_t* group) { if (keyboard_device != NULL) { lv_indev_set_group(keyboard_device, group); } } -void tt_lvgl_keypad_deactivate() { +void keypad_deactivate() { if (keyboard_device != NULL) { lv_indev_set_group(keyboard_device, NULL); } } + +} // namespace diff --git a/tactility/src/ui/lvgl_keypad.h b/Tactility/Source/Ui/LvglKeypad.h similarity index 69% rename from tactility/src/ui/lvgl_keypad.h rename to Tactility/Source/Ui/LvglKeypad.h index 2bc33e70..5f43a7cf 100644 --- a/tactility/src/ui/lvgl_keypad.h +++ b/Tactility/Source/Ui/LvglKeypad.h @@ -5,34 +5,30 @@ #include "lvgl.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::lvgl { /** * @return true if LVGL is configured with a keypad */ -bool tt_lvgl_keypad_is_available(); +bool keypad_is_available(); /** * Set the keypad. * @param device the keypad device */ -void tt_lvgl_keypad_set_indev(lv_indev_t* device); +void keypad_set_indev(lv_indev_t* device); /** * Activate the keypad for a widget group. * @param group */ -void tt_lvgl_keypad_activate(lv_group_t* group); +void keypad_activate(lv_group_t* group); /** * Deactivate the keypad for the current widget group (if any). * You don't have to call this after calling _activate() because widget * cleanup automatically removes itself from the group it belongs to. */ -void tt_lvgl_keypad_deactivate(); +void keypad_deactivate(); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/Tactility/Source/Ui/LvglSync.cpp b/Tactility/Source/Ui/LvglSync.cpp new file mode 100644 index 00000000..a02b4700 --- /dev/null +++ b/Tactility/Source/Ui/LvglSync.cpp @@ -0,0 +1,27 @@ +#include "LvglSync.h" + +namespace tt::lvgl { + +static LvglLock lock_singleton = nullptr; +static LvglUnlock unlock_singleton = nullptr; + +void sync_set(LvglLock lock, LvglUnlock unlock) { + lock_singleton = lock; + unlock_singleton = unlock; +} + +bool lock(uint32_t timeout_ticks) { + if (lock_singleton) { + return lock_singleton(timeout_ticks); + } else { + return true; + } +} + +void unlock() { + if (unlock_singleton) { + unlock_singleton(); + } +} + +} // namespace diff --git a/Tactility/Source/Ui/LvglSync.h b/Tactility/Source/Ui/LvglSync.h new file mode 100644 index 00000000..daa7b315 --- /dev/null +++ b/Tactility/Source/Ui/LvglSync.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace tt::lvgl { + +typedef bool (*LvglLock)(uint32_t timeout_ticks); +typedef void (*LvglUnlock)(); + +void sync_set(LvglLock lock, LvglUnlock unlock); +bool lock(uint32_t timeout_ticks); +void unlock(); + +} // namespace diff --git a/Tactility/Source/Ui/Spacer.cpp b/Tactility/Source/Ui/Spacer.cpp new file mode 100644 index 00000000..ead7b476 --- /dev/null +++ b/Tactility/Source/Ui/Spacer.cpp @@ -0,0 +1,13 @@ +#include "Spacer.h" +#include "Style.h" + +namespace tt::lvgl { + +lv_obj_t* spacer_create(lv_obj_t* parent, int32_t width, int32_t height) { + lv_obj_t* spacer = lv_obj_create(parent); + lv_obj_set_size(spacer, width, height); + obj_set_style_bg_invisible(spacer); + return spacer; +} + +} // namespace diff --git a/Tactility/Source/Ui/Spacer.h b/Tactility/Source/Ui/Spacer.h new file mode 100644 index 00000000..4559b506 --- /dev/null +++ b/Tactility/Source/Ui/Spacer.h @@ -0,0 +1,9 @@ +#pragma once + +#include "lvgl.h" + +namespace tt::lvgl { + +lv_obj_t* spacer_create(lv_obj_t* parent, int32_t width, int32_t height); + +} // namespace diff --git a/tactility/src/ui/statusbar.c b/Tactility/Source/Ui/Statusbar.cpp similarity index 76% rename from tactility/src/ui/statusbar.c rename to Tactility/Source/Ui/Statusbar.cpp index 46ceb6ed..1821172b 100644 --- a/tactility/src/ui/statusbar.c +++ b/Tactility/Source/Ui/Statusbar.cpp @@ -1,15 +1,16 @@ #define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration -#include "statusbar.h" +#include "Statusbar.h" -#include "mutex.h" -#include "pubsub.h" -#include "tactility_core.h" -#include "ui/spacer.h" -#include "ui/style.h" +#include "Mutex.h" +#include "Pubsub.h" +#include "TactilityCore.h" +#include "Ui/Spacer.h" +#include "Ui/Style.h" +#include "LvglSync.h" #include "lvgl.h" -#include "lvgl_sync.h" -#include "tactility.h" + +namespace tt::lvgl { #define TAG "statusbar" @@ -26,7 +27,7 @@ typedef struct { } StatusbarData; static StatusbarData statusbar_data = { - .mutex = NULL, + .mutex = nullptr, .icons = {} }; @@ -41,24 +42,24 @@ static void statusbar_init() { statusbar_data.mutex = tt_mutex_alloc(MutexTypeRecursive); statusbar_data.pubsub = tt_pubsub_alloc(); for (int i = 0; i < STATUSBAR_ICON_LIMIT; i++) { - statusbar_data.icons[i].image = NULL; + statusbar_data.icons[i].image = nullptr; statusbar_data.icons[i].visible = false; statusbar_data.icons[i].claimed = false; } } static void statusbar_ensure_initialized() { - if (statusbar_data.mutex == NULL) { + if (statusbar_data.mutex == nullptr) { statusbar_init(); } } -void statusbar_lock() { +static void statusbar_lock() { statusbar_ensure_initialized(); tt_mutex_acquire(statusbar_data.mutex, TtWaitForever); } -void statusbar_unlock() { +static void statusbar_unlock() { tt_mutex_release(statusbar_data.mutex); } @@ -69,23 +70,23 @@ static void statusbar_event(const lv_obj_class_t* class_p, lv_event_t* event); static void update_main(Statusbar* statusbar); static const lv_obj_class_t statusbar_class = { + .base_class = &lv_obj_class, .constructor_cb = &statusbar_constructor, .destructor_cb = &statusbar_destructor, .event_cb = &statusbar_event, .width_def = LV_PCT(100), .height_def = STATUSBAR_HEIGHT, .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, - .instance_size = sizeof(Statusbar), - .base_class = &lv_obj_class + .instance_size = sizeof(Statusbar) }; static void statusbar_pubsub_event(TT_UNUSED const void* message, void* obj) { TT_LOG_I(TAG, "event"); - Statusbar* statusbar = (Statusbar*)obj; - if (tt_lvgl_lock(tt_ms_to_ticks(100))) { + auto* statusbar = static_cast(obj); + if (lock(ms_to_ticks(100))) { update_main(statusbar); lv_obj_invalidate(&statusbar->obj); - tt_lvgl_unlock(); + unlock(); } } @@ -94,18 +95,18 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) LV_TRACE_OBJ_CREATE("begin"); lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE); LV_TRACE_OBJ_CREATE("finished"); - Statusbar* statusbar = (Statusbar*)obj; + auto* statusbar = (Statusbar*)obj; statusbar_ensure_initialized(); statusbar->pubsub_subscription = tt_pubsub_subscribe(statusbar_data.pubsub, &statusbar_pubsub_event, statusbar); } -static void statusbar_destructor(const lv_obj_class_t* class_p, lv_obj_t* obj) { - Statusbar* statusbar = (Statusbar*)obj; +static void statusbar_destructor(TT_UNUSED const lv_obj_class_t* class_p, lv_obj_t* obj) { + auto* statusbar = (Statusbar*)obj; tt_pubsub_unsubscribe(statusbar_data.pubsub, statusbar->pubsub_subscription); } static void update_icon(lv_obj_t* image, const StatusbarIcon* icon) { - if (icon->image != NULL && icon->visible && icon->claimed) { + if (icon->image != nullptr && icon->visible && icon->claimed) { lv_obj_set_style_image_recolor(image, lv_color_white(), 0); lv_obj_set_style_image_recolor_opa(image, 255, 0); lv_image_set_src(image, icon->image); @@ -115,28 +116,28 @@ static void update_icon(lv_obj_t* image, const StatusbarIcon* icon) { } } -lv_obj_t* tt_statusbar_create(lv_obj_t* parent) { +lv_obj_t* statusbar_create(lv_obj_t* parent) { LV_LOG_INFO("begin"); lv_obj_t* obj = lv_obj_class_create_obj(&statusbar_class, parent); lv_obj_class_init_obj(obj); - Statusbar* statusbar = (Statusbar*)obj; + auto* statusbar = (Statusbar*)obj; lv_obj_set_width(obj, LV_PCT(100)); lv_obj_set_height(obj, STATUSBAR_HEIGHT); - tt_lv_obj_set_style_no_padding(obj); + obj_set_style_no_padding(obj); lv_obj_center(obj); lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW); - lv_obj_t* left_spacer = tt_lv_spacer_create(obj, 1, 1); + lv_obj_t* left_spacer = spacer_create(obj, 1, 1); lv_obj_set_flex_grow(left_spacer, 1); statusbar_lock(); for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { lv_obj_t* image = lv_image_create(obj); lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE); - tt_lv_obj_set_style_no_padding(image); - tt_lv_obj_set_style_bg_blacken(image); + obj_set_style_no_padding(image); + obj_set_style_bg_blacken(image); statusbar->icons[i] = image; update_icon(image, &(statusbar_data.icons[i])); @@ -162,7 +163,7 @@ static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t* } lv_event_code_t code = lv_event_get_code(event); - lv_obj_t* obj = lv_event_get_target(event); + auto* obj = static_cast(lv_event_get_target(event)); if (code == LV_EVENT_VALUE_CHANGED) { lv_obj_invalidate(obj); @@ -170,54 +171,56 @@ static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t* } } -int8_t tt_statusbar_icon_add(const char* _Nullable image) { +int8_t statusbar_icon_add(const char* _Nullable image) { statusbar_lock(); int8_t result = -1; for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { if (!statusbar_data.icons[i].claimed) { statusbar_data.icons[i].claimed = true; - statusbar_data.icons[i].visible = (image != NULL); + statusbar_data.icons[i].visible = (image != nullptr); statusbar_data.icons[i].image = image; result = i; TT_LOG_I(TAG, "id %d: added", i); break; } } - tt_pubsub_publish(statusbar_data.pubsub, NULL); + tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_unlock(); return result; } -void tt_statusbar_icon_remove(int8_t id) { +void statusbar_icon_remove(int8_t id) { TT_LOG_I(TAG, "id %d: remove", id); tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT); statusbar_lock(); StatusbarIcon* icon = &statusbar_data.icons[id]; icon->claimed = false; icon->visible = false; - icon->image = NULL; - tt_pubsub_publish(statusbar_data.pubsub, NULL); + icon->image = nullptr; + tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_unlock(); } -void tt_statusbar_icon_set_image(int8_t id, const char* image) { +void statusbar_icon_set_image(int8_t id, const char* image) { TT_LOG_I(TAG, "id %d: set image %s", id, image); tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT); statusbar_lock(); StatusbarIcon* icon = &statusbar_data.icons[id]; tt_check(icon->claimed); icon->image = image; - tt_pubsub_publish(statusbar_data.pubsub, NULL); + tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_unlock(); } -void tt_statusbar_icon_set_visibility(int8_t id, bool visible) { +void statusbar_icon_set_visibility(int8_t id, bool visible) { TT_LOG_I(TAG, "id %d: set visibility %d", id, visible); tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT); statusbar_lock(); StatusbarIcon* icon = &statusbar_data.icons[id]; tt_check(icon->claimed); icon->visible = visible; - tt_pubsub_publish(statusbar_data.pubsub, NULL); + tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_unlock(); } + +} // namespace diff --git a/Tactility/Source/Ui/Statusbar.h b/Tactility/Source/Ui/Statusbar.h new file mode 100644 index 00000000..d4a06390 --- /dev/null +++ b/Tactility/Source/Ui/Statusbar.h @@ -0,0 +1,18 @@ +#pragma once + +#include "lvgl.h" +#include "App.h" + +namespace tt::lvgl { + +#define STATUSBAR_ICON_LIMIT 8 +#define STATUSBAR_ICON_SIZE 20 +#define STATUSBAR_HEIGHT (STATUSBAR_ICON_SIZE + 4) // 4 extra pixels for border and outline + +lv_obj_t* statusbar_create(lv_obj_t* parent); +int8_t statusbar_icon_add(const char* _Nullable image); +void statusbar_icon_remove(int8_t id); +void statusbar_icon_set_image(int8_t id, const char* image); +void statusbar_icon_set_visibility(int8_t id, bool visible); + +} // namespace diff --git a/tactility/src/ui/style.c b/Tactility/Source/Ui/Style.cpp similarity index 66% rename from tactility/src/ui/style.c rename to Tactility/Source/Ui/Style.cpp index c2ed04a0..9acad8de 100644 --- a/tactility/src/ui/style.c +++ b/Tactility/Source/Ui/Style.cpp @@ -1,23 +1,27 @@ -#include "style.h" +#include "Style.h" -void tt_lv_obj_set_style_bg_blacken(lv_obj_t* obj) { +namespace tt::lvgl { + +void obj_set_style_bg_blacken(lv_obj_t* obj) { lv_obj_set_style_bg_color(obj, lv_color_black(), 0); lv_obj_set_style_border_color(obj, lv_color_black(), 0); } -void tt_lv_obj_set_style_bg_invisible(lv_obj_t* obj) { +void obj_set_style_bg_invisible(lv_obj_t* obj) { lv_obj_set_style_bg_opa(obj, 0, 0); lv_obj_set_style_border_width(obj, 0, 0); } -void tt_lv_obj_set_style_no_padding(lv_obj_t* obj) { +void obj_set_style_no_padding(lv_obj_t* obj) { lv_obj_set_style_pad_all(obj, LV_STATE_DEFAULT, 0); lv_obj_set_style_pad_gap(obj, LV_STATE_DEFAULT, 0); } -void tt_lv_obj_set_style_auto_padding(lv_obj_t* obj) { +void obj_set_style_auto_padding(lv_obj_t* obj) { lv_obj_set_style_pad_top(obj, 8, 0); lv_obj_set_style_pad_bottom(obj, 8, 0); lv_obj_set_style_pad_left(obj, 16, 0); lv_obj_set_style_pad_right(obj, 16, 0); } + +} // namespace diff --git a/Tactility/Source/Ui/Style.h b/Tactility/Source/Ui/Style.h new file mode 100644 index 00000000..9833b986 --- /dev/null +++ b/Tactility/Source/Ui/Style.h @@ -0,0 +1,20 @@ +#pragma once + +#include "lvgl.h" + +namespace tt::lvgl { + +void obj_set_style_bg_blacken(lv_obj_t* obj); +void obj_set_style_bg_invisible(lv_obj_t* obj); +void obj_set_style_no_padding(lv_obj_t* obj); + +/** + * This is to create automatic padding depending on the screen size. + * The larger the screen, the more padding it gets. + * TODO: It currently only applies a single basic padding, but will be improved later. + * + * @param obj + */ +void obj_set_style_auto_padding(lv_obj_t* obj); + +} // namespace diff --git a/tactility/src/ui/toolbar.c b/Tactility/Source/Ui/Toolbar.cpp similarity index 71% rename from tactility/src/ui/toolbar.c rename to Tactility/Source/Ui/Toolbar.cpp index 2df623e9..06d75259 100644 --- a/tactility/src/ui/toolbar.c +++ b/Tactility/Source/Ui/Toolbar.cpp @@ -1,9 +1,11 @@ #define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration -#include "toolbar.h" +#include "Toolbar.h" -#include "services/loader/loader.h" -#include "ui/spacer.h" -#include "ui/style.h" +#include "Services/Loader/Loader.h" +#include "Ui/Spacer.h" +#include "Ui/Style.h" + +namespace tt::lvgl { typedef struct { lv_obj_t obj; @@ -18,17 +20,17 @@ typedef struct { static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj); static const lv_obj_class_t toolbar_class = { + .base_class = &lv_obj_class, .constructor_cb = &toolbar_constructor, - .destructor_cb = NULL, + .destructor_cb = nullptr, .width_def = LV_PCT(100), .height_def = TOOLBAR_HEIGHT, .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, .instance_size = sizeof(Toolbar), - .base_class = &lv_obj_class }; static void stop_app(TT_UNUSED lv_event_t* event) { - loader_stop_app(); + service::loader::stop_app(); } static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) { @@ -39,14 +41,14 @@ static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) { LV_TRACE_OBJ_CREATE("finished"); } -lv_obj_t* tt_toolbar_create(lv_obj_t* parent, const char* title) { +lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) { LV_LOG_INFO("begin"); lv_obj_t* obj = lv_obj_class_create_obj(&toolbar_class, parent); lv_obj_class_init_obj(obj); - Toolbar* toolbar = (Toolbar*)obj; + auto* toolbar = (Toolbar*)obj; - tt_lv_obj_set_style_no_padding(obj); + obj_set_style_no_padding(obj); lv_obj_center(obj); lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW); @@ -55,15 +57,15 @@ lv_obj_t* tt_toolbar_create(lv_obj_t* parent, const char* title) { toolbar->close_button = lv_button_create(obj); lv_obj_set_size(toolbar->close_button, TOOLBAR_HEIGHT - 4, TOOLBAR_HEIGHT - 4); - tt_lv_obj_set_style_no_padding(toolbar->close_button); + obj_set_style_no_padding(toolbar->close_button); toolbar->close_button_image = lv_image_create(toolbar->close_button); lv_obj_align(toolbar->close_button_image, LV_ALIGN_CENTER, 0, 0); // Need spacer to avoid button press glitch animation - tt_lv_spacer_create(obj, title_offset_x, 1); + spacer_create(obj, title_offset_x, 1); lv_obj_t* label_container = lv_obj_create(obj); - tt_lv_obj_set_style_no_padding(label_container); + obj_set_style_no_padding(label_container); lv_obj_set_style_border_width(label_container, 0, 0); lv_obj_set_height(label_container, LV_PCT(100)); // 2% less due to 4px translate (it's not great, but it works) lv_obj_set_flex_grow(label_container, 1); @@ -71,7 +73,7 @@ lv_obj_t* tt_toolbar_create(lv_obj_t* parent, const char* title) { toolbar->title_label = lv_label_create(label_container); lv_obj_set_style_text_font(toolbar->title_label, &lv_font_montserrat_18, 0); // TODO replace with size 18 lv_obj_set_height(toolbar->title_label, TOOLBAR_TITLE_FONT_HEIGHT); - lv_label_set_text(toolbar->title_label, title); + lv_label_set_text(toolbar->title_label, title.c_str()); lv_obj_set_pos(toolbar->title_label, 0, title_offset_y); lv_obj_set_style_text_align(toolbar->title_label, LV_TEXT_ALIGN_LEFT, 0); @@ -83,33 +85,33 @@ lv_obj_t* tt_toolbar_create(lv_obj_t* parent, const char* title) { return obj; } -lv_obj_t* tt_toolbar_create_for_app(lv_obj_t* parent, App app) { - const AppManifest* manifest = tt_app_get_manifest(app); - lv_obj_t* toolbar = tt_toolbar_create(parent, manifest->name); - tt_toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &stop_app, NULL); +lv_obj_t* toolbar_create_for_app(lv_obj_t* parent, App app) { + const AppManifest& manifest = tt_app_get_manifest(app); + lv_obj_t* toolbar = toolbar_create(parent, manifest.name); + toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &stop_app, nullptr); return toolbar; } -void tt_toolbar_set_title(lv_obj_t* obj, const char* title) { - Toolbar* toolbar = (Toolbar*)obj; - lv_label_set_text(toolbar->title_label, title); +void toolbar_set_title(lv_obj_t* obj, const std::string& title) { + auto* toolbar = (Toolbar*)obj; + lv_label_set_text(toolbar->title_label, title.c_str()); } -void tt_toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data) { - Toolbar* toolbar = (Toolbar*)obj; +void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data) { + auto* toolbar = (Toolbar*)obj; lv_obj_add_event_cb(toolbar->close_button, callback, LV_EVENT_CLICKED, user_data); lv_image_set_src(toolbar->close_button_image, icon); // e.g. LV_SYMBOL_CLOSE } -uint8_t tt_toolbar_add_action(lv_obj_t* obj, const char* icon, const char* text, lv_event_cb_t callback, void* user_data) { - Toolbar* toolbar = (Toolbar*)obj; +uint8_t toolbar_add_action(lv_obj_t* obj, const char* icon, const char* text, lv_event_cb_t callback, void* user_data) { + auto* toolbar = (Toolbar*)obj; uint8_t id = toolbar->action_count; tt_check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached"); toolbar->action_count++; lv_obj_t* action_button = lv_button_create(toolbar->action_container); lv_obj_set_size(action_button, TOOLBAR_HEIGHT - 4, TOOLBAR_HEIGHT - 4); - tt_lv_obj_set_style_no_padding(action_button); + obj_set_style_no_padding(action_button); lv_obj_add_event_cb(action_button, callback, LV_EVENT_CLICKED, user_data); lv_obj_t* action_button_image = lv_image_create(action_button); lv_image_set_src(action_button_image, icon); @@ -117,3 +119,5 @@ uint8_t tt_toolbar_add_action(lv_obj_t* obj, const char* icon, const char* text, return id; } + +} // namespace diff --git a/Tactility/Source/Ui/Toolbar.h b/Tactility/Source/Ui/Toolbar.h new file mode 100644 index 00000000..86ee0344 --- /dev/null +++ b/Tactility/Source/Ui/Toolbar.h @@ -0,0 +1,27 @@ +#pragma once + +#include "lvgl.h" +#include "App.h" + +namespace tt::lvgl { + +#define TOOLBAR_HEIGHT 40 +#define TOOLBAR_ACTION_LIMIT 8 +#define TOOLBAR_TITLE_FONT_HEIGHT 18 + +typedef void(*ToolbarActionCallback)(void* _Nullable context); + +typedef struct { + const char* icon; + const char* text; + ToolbarActionCallback callback; + void* _Nullable callback_context; +} ToolbarAction; + +lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title); +lv_obj_t* toolbar_create_for_app(lv_obj_t* parent, tt::App app); +void toolbar_set_title(lv_obj_t* obj, const std::string& title); +void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data); +uint8_t toolbar_add_action(lv_obj_t* obj, const char* icon, const char* text, lv_event_cb_t callback, void* user_data); + +} // namespace diff --git a/tactility-core/CMakeLists.txt b/TactilityCore/CMakeLists.txt similarity index 52% rename from tactility-core/CMakeLists.txt rename to TactilityCore/CMakeLists.txt index c02a35ee..a67b2291 100644 --- a/tactility-core/CMakeLists.txt +++ b/TactilityCore/CMakeLists.txt @@ -1,35 +1,32 @@ cmake_minimum_required(VERSION 3.16) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) -file(GLOB SOURCES "src/*.c") -file(GLOB HEADERS "src/*.h") +file(GLOB SOURCES "Source/*.c*") +file(GLOB HEADERS "Source/*.h*") -add_library(tactility-core OBJECT) +add_library(TactilityCore OBJECT) -target_sources(tactility-core +target_sources(TactilityCore PRIVATE ${SOURCES} PUBLIC ${HEADERS} ) -target_include_directories(tactility-core SYSTEM - PRIVATE src/ - INTERFACE src/ + +target_include_directories(TactilityCore SYSTEM + PUBLIC Source/ ) if (DEFINED ENV{ESP_IDF_VERSION}) add_definitions(-DESP_PLATFORM) - target_link_libraries(tactility-core - PUBLIC mlib + target_link_libraries(TactilityCore PUBLIC idf::mbedtls PRIVATE idf::nvs_flash # ESP-IDF // for secure.c ) else() add_definitions(-D_Nullable=) add_definitions(-D_Nonnull=) - target_link_libraries(tactility-core - PUBLIC mlib + target_link_libraries(TactilityCore PUBLIC mbedtls PUBLIC freertos_kernel ) diff --git a/tactility-core/LICENSE.md b/TactilityCore/LICENSE.md similarity index 100% rename from tactility-core/LICENSE.md rename to TactilityCore/LICENSE.md diff --git a/tactility-core/README.md b/TactilityCore/README.md similarity index 100% rename from tactility-core/README.md rename to TactilityCore/README.md diff --git a/TactilityCore/Source/ApiLock.h b/TactilityCore/Source/ApiLock.h new file mode 100644 index 00000000..76a5bd72 --- /dev/null +++ b/TactilityCore/Source/ApiLock.h @@ -0,0 +1,20 @@ +#pragma once + +#include "EventFlag.h" + +typedef tt::EventFlag* ApiLock; + +#define TT_API_LOCK_EVENT (1U << 0) + +#define tt_api_lock_alloc_locked() tt::event_flag_alloc() + +#define tt_api_lock_wait_unlock(_lock) \ + tt::event_flag_wait(_lock, TT_API_LOCK_EVENT, tt::TtFlagWaitAny, tt::TtWaitForever) + +#define tt_api_lock_free(_lock) tt::event_flag_free(_lock) + +#define tt_api_lock_unlock(_lock) tt::event_flag_set(_lock, TT_API_LOCK_EVENT) + +#define tt_api_lock_wait_unlock_and_free(_lock) \ + tt_api_lock_wait_unlock(_lock); \ + tt_api_lock_free(_lock); diff --git a/TactilityCore/Source/Bundle.cpp b/TactilityCore/Source/Bundle.cpp new file mode 100644 index 00000000..061c9059 --- /dev/null +++ b/TactilityCore/Source/Bundle.cpp @@ -0,0 +1,83 @@ +#include "Bundle.h" + +namespace tt { + +bool Bundle::getBool(const std::string& key) const { + return this->entries.find(key)->second.value_bool; +} + +int32_t Bundle::getInt32(const std::string& key) const { + return this->entries.find(key)->second.value_int32; +} + +std::string Bundle::getString(const std::string& key) const { + return this->entries.find(key)->second.value_string; +} + +bool Bundle::hasBool(const std::string& key) const { + auto entry = this->entries.find(key); + return entry != std::end(this->entries) && entry->second.type == TypeBool; +} + +bool Bundle::hasInt32(const std::string& key) const { + auto entry = this->entries.find(key); + return entry != std::end(this->entries) && entry->second.type == TypeInt32; +} + +bool Bundle::hasString(const std::string& key) const { + auto entry = this->entries.find(key); + return entry != std::end(this->entries) && entry->second.type == TypeString; +} + +bool Bundle::optBool(const std::string& key, bool& out) const { + auto entry = this->entries.find(key); + if (entry != std::end(this->entries) && entry->second.type == TypeBool) { + out = entry->second.value_bool; + return true; + } else { + return false; + } +} + +bool Bundle::optInt32(const std::string& key, int32_t& out) const { + auto entry = this->entries.find(key); + if (entry != std::end(this->entries) && entry->second.type == TypeInt32) { + out = entry->second.value_int32; + return true; + } else { + return false; + } +} + +bool Bundle::optString(const std::string& key, std::string& out) const { + auto entry = this->entries.find(key); + if (entry != std::end(this->entries) && entry->second.type == TypeString) { + out = entry->second.value_string; + return true; + } else { + return false; + } +} + +void Bundle::putBool(const std::string& key, bool value) { + this->entries[key] = { + .type = TypeBool, + .value_bool = value + }; +} + +void Bundle::putInt32(const std::string& key, int32_t value) { + this->entries[key] = { + .type = TypeInt32, + .value_int32 = value + }; +} + +void Bundle::putString(const std::string& key, const std::string& value) { + this->entries[key] = { + .type = TypeString, + .value_string = value + }; +} + +} // namespace diff --git a/TactilityCore/Source/Bundle.h b/TactilityCore/Source/Bundle.h new file mode 100644 index 00000000..17fcbc24 --- /dev/null +++ b/TactilityCore/Source/Bundle.h @@ -0,0 +1,63 @@ +/** + * @brief key-value storage for general purpose. + * Maps strings on a fixed set of data types. + */ +#pragma once + +#include +#include +#include +#include + +namespace tt { + +class Bundle { + +private: + + typedef uint32_t Hash; + + typedef enum { + TypeBool, + TypeInt32, + TypeString, + } Type; + + typedef struct { + const char* key; + Type type; + union { + bool value_bool; + int32_t value_int32; + }; + std::string value_string; + } Value; + + std::unordered_map entries; + +public: + + Bundle() = default; + + Bundle(const Bundle& bundle) { + this->entries = bundle.entries; + } + + bool getBool(const std::string& key) const; + int32_t getInt32(const std::string& key) const; + std::string getString(const std::string& key) const; + + bool hasBool(const std::string& key) const; + bool hasInt32(const std::string& key) const; + bool hasString(const std::string& key) const; + + bool optBool(const std::string& key, bool& out) const; + bool optInt32(const std::string& key, int32_t& out) const; + bool optString(const std::string& key, std::string& out) const; + + void putBool(const std::string& key, bool value); + void putInt32(const std::string& key, int32_t value); + void putString(const std::string& key, const std::string& value); +}; + +} // namespace diff --git a/tactility-core/src/check.c b/TactilityCore/Source/Check.cpp similarity index 92% rename from tactility-core/src/check.c rename to TactilityCore/Source/Check.cpp index f15ee6c3..8f0f7473 100644 --- a/tactility-core/src/check.c +++ b/TactilityCore/Source/Check.cpp @@ -1,7 +1,7 @@ -#include "check.h" +#include "Check.h" -#include "core_defines.h" -#include "log.h" +#include "CoreDefines.h" +#include "Log.h" #ifdef ESP_TARGET #include "freertos/FreeRTOS.h" @@ -27,7 +27,7 @@ static void tt_print_memory_info() { } static void tt_print_task_info() { - const char* name = pcTaskGetName(NULL); + const char* name = pcTaskGetName(nullptr); const char* safe_name = name ? name : "main"; TT_LOG_E(TAG, "Task: %s", safe_name); TT_LOG_E(TAG, "Stack watermark: %u", uxTaskGetStackHighWaterMark(NULL) * 4); diff --git a/tactility-core/src/check.h b/TactilityCore/Source/Check.h similarity index 76% rename from tactility-core/src/check.h rename to TactilityCore/Source/Check.h index 796e862f..d9cc5fb2 100644 --- a/tactility-core/src/check.h +++ b/TactilityCore/Source/Check.h @@ -13,33 +13,21 @@ */ #pragma once -#include "log.h" -#include "m-core.h" +#include "Log.h" +#include -#ifdef __cplusplus -extern "C" { #define TT_NORETURN [[noreturn]] -#else -#include -#define TT_NORETURN noreturn -#endif /** Crash system */ TT_NORETURN void tt_crash_implementation(); /** Crash system with message. */ -#define __tt_crash(message) \ +#define tt_crash(message) \ do { \ TT_LOG_E("crash", "%s\n\tat %s:%d", ((message) ? (message) : ""), __FILE__, __LINE__); \ tt_crash_implementation(); \ } while (0) -/** Crash system - * - * @param optional message (const char*) - */ -#define tt_crash(...) M_APPLY(__tt_crash, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) - /** Halt system * * @param optional message (const char*) @@ -47,14 +35,14 @@ TT_NORETURN void tt_crash_implementation(); #define tt_halt(...) M_APPLY(__tt_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) /** Check condition and crash if check failed */ -#define __tt_check(__e, __m) \ +#define tt_check_internal(__e, __m) \ do { \ if (!(__e)) { \ TT_LOG_E("check", "%s", #__e); \ if (__m) { \ - __tt_crash(#__m); \ + tt_crash_internal(#__m); \ } else { \ - __tt_crash(""); \ + tt_crash_internal(""); \ } \ } \ } while (0) @@ -64,12 +52,16 @@ TT_NORETURN void tt_crash_implementation(); * @param condition to check * @param optional message (const char*) */ -#define tt_check(...) \ - M_APPLY(__tt_check, M_DEFAULT_ARGS(2, NULL, __VA_ARGS__)) + +#ifdef NDEBUG +#define tt_check(x, ...) if (!(x)) { TT_LOG_E("check", "check failed: %s", #x); } +#else +#define tt_check(x, ...) assert(x) +#endif /** Only in debug build: Assert condition and crash if assert failed */ #ifdef TT_DEBUG -#define __tt_assert(__e, __m) \ +#define tt_assert_internal(__e, __m) \ do { \ if (!(__e)) { \ TT_LOG_E("assert", "%s", #__e); \ @@ -95,9 +87,4 @@ TT_NORETURN void tt_crash_implementation(); * @param condition to check * @param optional message (const char*) */ -#define tt_assert(...) \ - M_APPLY(__tt_assert, M_DEFAULT_ARGS(2, NULL, __VA_ARGS__)) - -#ifdef __cplusplus -} -#endif +#define tt_assert(expression) assert(expression) diff --git a/tactility-core/src/core_defines.h b/TactilityCore/Source/CoreDefines.h similarity index 95% rename from tactility-core/src/core_defines.h rename to TactilityCore/Source/CoreDefines.h index 26725ee2..ee3f3423 100644 --- a/tactility-core/src/core_defines.h +++ b/TactilityCore/Source/CoreDefines.h @@ -1,6 +1,6 @@ #pragma once -#include "core_extra_defines.h" +#include "CoreExtraDefines.h" #ifdef ESP_PLATFORM #include "freertos/portmacro.h" diff --git a/tactility-core/src/core_extra_defines.h b/TactilityCore/Source/CoreExtraDefines.h similarity index 100% rename from tactility-core/src/core_extra_defines.h rename to TactilityCore/Source/CoreExtraDefines.h diff --git a/tactility-core/src/core_types.h b/TactilityCore/Source/CoreTypes.h similarity index 91% rename from tactility-core/src/core_types.h rename to TactilityCore/Source/CoreTypes.h index f9c5e550..0b9647ec 100644 --- a/tactility-core/src/core_types.h +++ b/TactilityCore/Source/CoreTypes.h @@ -1,12 +1,9 @@ #pragma once -#include -#include -#include "tactility_core_config.h" +#include +#include "TactilityCoreConfig.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { typedef enum { TtWaitForever = 0xFFFFFFFFU, @@ -39,6 +36,4 @@ typedef enum { TtStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization. } TtStatus; -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility-core/src/critical.c b/TactilityCore/Source/Critical.cpp similarity index 84% rename from tactility-core/src/critical.c rename to TactilityCore/Source/Critical.cpp index 6bbe2e84..3226be30 100644 --- a/tactility-core/src/critical.c +++ b/TactilityCore/Source/Critical.cpp @@ -1,6 +1,5 @@ -#include "critical.h" -#include "core_defines.h" - +#include "Critical.h" +#include "CoreDefines.h" #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -19,8 +18,10 @@ static portMUX_TYPE critical_mutex; #define TT_ENTER_CRITICAL() taskENTER_CRITICAL() #endif -__TtCriticalInfo __tt_critical_enter(void) { - __TtCriticalInfo info; +namespace tt::critical { + +TtCriticalInfo enter() { + TtCriticalInfo info; info.isrm = 0; info.from_isr = TT_IS_ISR(); @@ -37,7 +38,7 @@ __TtCriticalInfo __tt_critical_enter(void) { return info; } -void __tt_critical_exit(__TtCriticalInfo info) { +void exit(TtCriticalInfo info) { if (info.from_isr) { taskEXIT_CRITICAL_FROM_ISR(info.isrm); } else if (info.kernel_running) { @@ -46,3 +47,5 @@ void __tt_critical_exit(__TtCriticalInfo info) { portENABLE_INTERRUPTS(); } } + +} diff --git a/tactility-core/src/critical.h b/TactilityCore/Source/Critical.h similarity index 67% rename from tactility-core/src/critical.h rename to TactilityCore/Source/Critical.h index bc3d840a..fc404924 100644 --- a/tactility-core/src/critical.h +++ b/TactilityCore/Source/Critical.h @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #ifndef TT_CRITICAL_ENTER #define TT_CRITICAL_ENTER() __TtCriticalInfo __tt_critical_info = __tt_critical_enter(); @@ -11,12 +10,16 @@ #define TT_CRITICAL_EXIT() __tt_critical_exit(__tt_critical_info); #endif +namespace tt::critical { + typedef struct { uint32_t isrm; bool from_isr; bool kernel_running; -} __TtCriticalInfo; +} TtCriticalInfo; -__TtCriticalInfo __tt_critical_enter(void); +TtCriticalInfo enter(); -void __tt_critical_exit(__TtCriticalInfo info); +void exit(TtCriticalInfo info); + +} // namespace diff --git a/tactility-core/src/secure.c b/TactilityCore/Source/Crypt.cpp similarity index 85% rename from tactility-core/src/secure.c rename to TactilityCore/Source/Crypt.cpp index 2a094be1..18a5db37 100644 --- a/tactility-core/src/secure.c +++ b/TactilityCore/Source/Crypt.cpp @@ -1,9 +1,9 @@ -#include "secure.h" +#include "Crypt.h" -#include "check.h" -#include "log.h" +#include "Check.h" +#include "Log.h" #include "mbedtls/aes.h" -#include +#include #ifdef ESP_PLATFORM #include "esp_cpu.h" @@ -11,6 +11,8 @@ #include "nvs_flash.h" #endif +namespace tt::crypt { + #define TAG "secure" #define TT_NVS_NAMESPACE "tt_secure" @@ -63,7 +65,7 @@ static void get_nvs_key(uint8_t key[32]) { if (result != ESP_OK) { TT_LOG_E(TAG, "Failed to get key from NVS (%s)", esp_err_to_name(result)); - tt_crash(); + tt_crash("NVS error"); } size_t length = 32; @@ -71,10 +73,12 @@ static void get_nvs_key(uint8_t key[32]) { TT_LOG_I(TAG, "Fetched key from NVS (%d bytes)", length); tt_check(length == 32); } else { + // TODO: Improved randomness esp_cpu_cycle_count_t cycle_count = esp_cpu_get_cycle_count(); - unsigned seed = (unsigned)cycle_count; + auto seed = cycle_count; + srand(seed); for (int i = 0; i < 32; ++i) { - key[i] = (uint8_t)rand_r(&seed); + key[i] = (uint8_t)(rand()); } ESP_ERROR_CHECK(nvs_set_blob(handle, "key", key, 32)); TT_LOG_I(TAG, "Stored new key in NVS"); @@ -120,7 +124,7 @@ static void get_key(uint8_t key[32]) { #endif } -void tt_secure_get_iv_from_data(const void* data, size_t data_length, uint8_t iv[16]) { +void get_iv_from_data(const void* data, size_t data_length, uint8_t iv[16]) { memset((void*)iv, 0, 16); uint8_t* data_bytes = (uint8_t*)data; for (int i = 0; i < data_length; ++i) { @@ -129,11 +133,11 @@ void tt_secure_get_iv_from_data(const void* data, size_t data_length, uint8_t iv } } -void tt_secure_get_iv_from_string(const char* input, uint8_t iv[16]) { - tt_secure_get_iv_from_data((const void*)input, strlen(input), iv); +void get_iv_from_string(const char* input, uint8_t iv[16]) { + get_iv_from_data((const void*)input, strlen(input), iv); } -static int tt_aes256_crypt_cbc( +static int aes256_crypt_cbc( const uint8_t key[32], int mode, size_t length, @@ -159,7 +163,7 @@ static int tt_aes256_crypt_cbc( return result; } -int tt_secure_encrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length) { +int encrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length) { tt_check(length % 16 == 0, "Length is not a multiple of 16 bytes (for AES 256"); uint8_t key[32]; get_key(key); @@ -168,10 +172,10 @@ int tt_secure_encrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, uint8_t iv_copy[16]; memcpy(iv_copy, iv, sizeof(iv_copy)); - return tt_aes256_crypt_cbc(key, MBEDTLS_AES_ENCRYPT, length, iv_copy, in_data, out_data); + return aes256_crypt_cbc(key, MBEDTLS_AES_ENCRYPT, length, iv_copy, in_data, out_data); } -int tt_secure_decrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length) { +int decrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length) { tt_check(length % 16 == 0, "Length is not a multiple of 16 bytes (for AES 256"); uint8_t key[32]; get_key(key); @@ -180,5 +184,7 @@ int tt_secure_decrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, uint8_t iv_copy[16]; memcpy(iv_copy, iv, sizeof(iv_copy)); - return tt_aes256_crypt_cbc(key, MBEDTLS_AES_DECRYPT, length, iv_copy, in_data, out_data); + return aes256_crypt_cbc(key, MBEDTLS_AES_DECRYPT, length, iv_copy, in_data, out_data); } + +} // namespace diff --git a/tactility-core/src/secure.h b/TactilityCore/Source/Crypt.h similarity index 79% rename from tactility-core/src/secure.h rename to TactilityCore/Source/Crypt.h index a260239e..b2ae33f7 100644 --- a/tactility-core/src/secure.h +++ b/TactilityCore/Source/Crypt.h @@ -18,12 +18,10 @@ */ #pragma once -#include -#include +#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::crypt { /** * @brief Fills the IV with zeros and then creates an IV based on the input data. @@ -31,14 +29,14 @@ extern "C" { * @param data_length input data length * @param iv output IV */ -void tt_secure_get_iv_from_data(const void* data, size_t data_length, uint8_t iv[16]); +void get_iv_from_data(const void* data, size_t data_length, uint8_t iv[16]); /** * @brief Fills the IV with zeros and then creates an IV based on the input data. * @param input input text * @param iv output IV */ -void tt_secure_get_iv_from_string(const char* input, uint8_t iv[16]); +void get_iv_from_string(const char* input, uint8_t iv[16]); /** * @brief Encrypt data. @@ -52,7 +50,7 @@ void tt_secure_get_iv_from_string(const char* input, uint8_t iv[16]); * @param length data length, a multiple of 16 * @return the result of esp_aes_crypt_cbc() (MBEDTLS_ERR_*) */ -int tt_secure_encrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length); +int encrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length); /** * @brief Decrypt data. @@ -66,8 +64,6 @@ int tt_secure_encrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, * @param length data length, a multiple of 16 * @return the result of esp_aes_crypt_cbc() (MBEDTLS_ERR_*) */ -int tt_secure_decrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length); +int decrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/TactilityCore/Source/Dispatcher.cpp b/TactilityCore/Source/Dispatcher.cpp new file mode 100644 index 00000000..e06f0aff --- /dev/null +++ b/TactilityCore/Source/Dispatcher.cpp @@ -0,0 +1,39 @@ +#include "Dispatcher.h" + +namespace tt { + +Dispatcher::Dispatcher(size_t queueLimit) : + queue(queueLimit, sizeof(DispatcherMessage)), + mutex(MutexTypeNormal), + buffer({ .callback = nullptr, .context = nullptr }) { } + +Dispatcher::~Dispatcher() { + queue.reset(); + // Wait for Mutex usage + mutex.acquire(TtWaitForever); + mutex.release(); +} + +void Dispatcher::dispatch(Callback callback, void* context) { + DispatcherMessage message = { + .callback = callback, + .context = context + }; + mutex.acquire(TtWaitForever); + queue.put(&message, TtWaitForever); + mutex.release(); +} + +bool Dispatcher::consume(uint32_t timeout_ticks) { + mutex.acquire(TtWaitForever); + if (queue.get(&buffer, timeout_ticks) == TtStatusOk) { + buffer.callback(buffer.context); + mutex.release(); + return true; + } else { + mutex.release(); + return false; + } +} + +} // namespace diff --git a/TactilityCore/Source/Dispatcher.h b/TactilityCore/Source/Dispatcher.h new file mode 100644 index 00000000..dd0dd095 --- /dev/null +++ b/TactilityCore/Source/Dispatcher.h @@ -0,0 +1,35 @@ +/** +* @file Dispatcher.h +* +* Dispatcher is a thread-safe code execution queue. +*/ +#pragma once + +#include "MessageQueue.h" +#include "Mutex.h" + +namespace tt { + +typedef void (*Callback)(void* data); + +class Dispatcher { +private: + typedef struct { + Callback callback; + void* context; + } DispatcherMessage; + + MessageQueue queue; + Mutex mutex; + DispatcherMessage buffer; // Buffer for consuming a message + +public: + + explicit Dispatcher(size_t queueLimit = 8); + ~Dispatcher(); + + void dispatch(Callback callback, void* context); + bool consume(uint32_t timeout_ticks); +}; + +} // namespace diff --git a/tactility-core/src/event_flag.c b/TactilityCore/Source/EventFlag.cpp similarity index 83% rename from tactility-core/src/event_flag.c rename to TactilityCore/Source/EventFlag.cpp index 1227a74d..b1237ea6 100644 --- a/tactility-core/src/event_flag.c +++ b/TactilityCore/Source/EventFlag.cpp @@ -1,8 +1,7 @@ -#include "event_flag.h" - -#include "check.h" -#include "core_defines.h" +#include "EventFlag.h" +#include "Check.h" +#include "CoreDefines.h" #ifdef ESP_TARGET #include "freertos/FreeRTOS.h" @@ -17,25 +16,27 @@ #define TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U #define TT_EVENT_FLAG_INVALID_BITS (~((1UL << TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U)) -EventFlag* tt_event_flag_alloc() { +namespace tt { + +EventFlag* event_flag_alloc() { tt_assert(!TT_IS_IRQ_MODE()); EventGroupHandle_t handle = xEventGroupCreate(); tt_check(handle); - return ((EventFlag*)handle); + return static_cast(handle); } -void tt_event_flag_free(EventFlag* instance) { +void event_flag_free(EventFlag* instance) { tt_assert(!TT_IS_IRQ_MODE()); vEventGroupDelete((EventGroupHandle_t)instance); } -uint32_t tt_event_flag_set(EventFlag* instance, uint32_t flags) { +uint32_t event_flag_set(EventFlag* instance, uint32_t flags) { tt_assert(instance); tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U); - EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; + auto hEventGroup = static_cast(instance); uint32_t rflags; BaseType_t yield; @@ -55,11 +56,11 @@ uint32_t tt_event_flag_set(EventFlag* instance, uint32_t flags) { return (rflags); } -uint32_t tt_event_flag_clear(EventFlag* instance, uint32_t flags) { +uint32_t event_flag_clear(EventFlag* instance, uint32_t flags) { tt_assert(instance); tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U); - EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; + auto hEventGroup = static_cast(instance); uint32_t rflags; if (TT_IS_IRQ_MODE()) { @@ -81,10 +82,10 @@ uint32_t tt_event_flag_clear(EventFlag* instance, uint32_t flags) { return (rflags); } -uint32_t tt_event_flag_get(EventFlag* instance) { +uint32_t event_flag_get(EventFlag* instance) { tt_assert(instance); - EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; + auto hEventGroup = static_cast(instance); uint32_t rflags; if (TT_IS_IRQ_MODE()) { @@ -97,7 +98,7 @@ uint32_t tt_event_flag_get(EventFlag* instance) { return (rflags); } -uint32_t tt_event_flag_wait( +uint32_t event_flag_wait( EventFlag* instance, uint32_t flags, uint32_t options, @@ -107,7 +108,7 @@ uint32_t tt_event_flag_wait( tt_assert(instance); tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U); - EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; + auto hEventGroup = static_cast(instance); BaseType_t wait_all; BaseType_t exit_clr; uint32_t rflags; @@ -153,3 +154,5 @@ uint32_t tt_event_flag_wait( /* Return event flags before clearing */ return (rflags); } + +} // namespace diff --git a/tactility-core/src/event_flag.h b/TactilityCore/Source/EventFlag.h similarity index 71% rename from tactility-core/src/event_flag.h rename to TactilityCore/Source/EventFlag.h index 881c9614..c0a07eda 100644 --- a/tactility-core/src/event_flag.h +++ b/TactilityCore/Source/EventFlag.h @@ -1,10 +1,8 @@ #pragma once -#include "core_types.h" +#include "CoreTypes.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { typedef void EventFlag; @@ -12,13 +10,13 @@ typedef void EventFlag; * * @return pointer to EventFlag */ -EventFlag* tt_event_flag_alloc(); +EventFlag* event_flag_alloc(); /** Deallocate EventFlag * * @param instance pointer to EventFlag */ -void tt_event_flag_free(EventFlag* instance); +void event_flag_free(EventFlag* instance); /** Set flags * @@ -27,7 +25,7 @@ void tt_event_flag_free(EventFlag* instance); * * @return Resulting flags or error (TtStatus) */ -uint32_t tt_event_flag_set(EventFlag* instance, uint32_t flags); +uint32_t event_flag_set(EventFlag* instance, uint32_t flags); /** Clear flags * @@ -36,7 +34,7 @@ uint32_t tt_event_flag_set(EventFlag* instance, uint32_t flags); * * @return Resulting flags or error (TtStatus) */ -uint32_t tt_event_flag_clear(EventFlag* instance, uint32_t flags); +uint32_t event_flag_clear(EventFlag* instance, uint32_t flags); /** Get flags * @@ -44,7 +42,7 @@ uint32_t tt_event_flag_clear(EventFlag* instance, uint32_t flags); * * @return Resulting flags */ -uint32_t tt_event_flag_get(EventFlag* instance); +uint32_t event_flag_get(EventFlag* instance); /** Wait flags * @@ -55,13 +53,11 @@ uint32_t tt_event_flag_get(EventFlag* instance); * * @return Resulting flags or error (TtStatus) */ -uint32_t tt_event_flag_wait( +uint32_t event_flag_wait( EventFlag* instance, uint32_t flags, uint32_t options, uint32_t timeout ); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility-core/src/hash.c b/TactilityCore/Source/Hash.cpp similarity index 69% rename from tactility-core/src/hash.c rename to TactilityCore/Source/Hash.cpp index 2b66ae99..1ee5f8ad 100644 --- a/tactility-core/src/hash.c +++ b/TactilityCore/Source/Hash.cpp @@ -1,6 +1,8 @@ -#include "hash.h" +#include "Hash.h" -uint32_t tt_hash_string_djb2(const char* str) { +namespace tt::hash { + +uint32_t djb2(const char* str) { uint32_t hash = 5381; char c = (char)*str++; while (c != 0) { @@ -10,9 +12,9 @@ uint32_t tt_hash_string_djb2(const char* str) { return hash; } -uint32_t tt_hash_bytes_djb2(const void* data, size_t length) { +uint32_t djb2(const void* data, size_t length) { uint32_t hash = 5381; - uint8_t* data_bytes = (uint8_t*)data; + auto* data_bytes = static_cast(data); uint8_t c = *data_bytes++; size_t index = 0; while (index < length) { @@ -22,3 +24,5 @@ uint32_t tt_hash_bytes_djb2(const void* data, size_t length) { } return hash; } + +} // namespace diff --git a/tactility-core/src/hash.h b/TactilityCore/Source/Hash.h similarity index 56% rename from tactility-core/src/hash.h rename to TactilityCore/Source/Hash.h index b3edf7e8..83e5856e 100644 --- a/tactility-core/src/hash.h +++ b/TactilityCore/Source/Hash.h @@ -1,26 +1,22 @@ #pragma once -#include -#include +#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::hash { /** * Implementation of DJB2 hashing algorithm. * @param[in] str the string to calculate the hash for * @return the hash */ -uint32_t tt_hash_string_djb2(const char* str); +uint32_t djb2(const char* str); /** * Implementation of DJB2 hashing algorithm. * @param[in] data the bytes to calculate the hash for * @return the hash */ -uint32_t tt_hash_bytes_djb2(const void* data, size_t length); +uint32_t djb2(const void* data, size_t length); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/tactility-core/src/kernel.c b/TactilityCore/Source/Kernel.cpp similarity index 80% rename from tactility-core/src/kernel.c rename to TactilityCore/Source/Kernel.cpp index 91f4c1b9..c5614efc 100644 --- a/tactility-core/src/kernel.c +++ b/TactilityCore/Source/Kernel.cpp @@ -1,7 +1,7 @@ -#include "kernel.h" -#include "check.h" -#include "core_defines.h" -#include "core_types.h" +#include "Kernel.h" +#include "Check.h" +#include "CoreDefines.h" +#include "CoreTypes.h" #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -17,16 +17,18 @@ #include #endif -bool tt_kernel_is_irq() { +namespace tt { + +bool kernel_is_irq() { return TT_IS_IRQ_MODE(); } -bool tt_kernel_is_running() { +bool kernel_is_running() { return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING; } -int32_t tt_kernel_lock() { - tt_assert(!tt_kernel_is_irq()); +int32_t kernel_lock() { + tt_assert(!kernel_is_irq()); int32_t lock; @@ -50,8 +52,8 @@ int32_t tt_kernel_lock() { return (lock); } -int32_t tt_kernel_unlock() { - tt_assert(!tt_kernel_is_irq()); +int32_t kernel_unlock() { + tt_assert(!kernel_is_irq()); int32_t lock; @@ -80,8 +82,8 @@ int32_t tt_kernel_unlock() { return (lock); } -int32_t tt_kernel_restore_lock(int32_t lock) { - tt_assert(!tt_kernel_is_irq()); +int32_t kernel_restore_lock(int32_t lock) { + tt_assert(!kernel_is_irq()); switch (xTaskGetSchedulerState()) { case taskSCHEDULER_SUSPENDED: @@ -111,13 +113,13 @@ int32_t tt_kernel_restore_lock(int32_t lock) { return (lock); } -uint32_t tt_kernel_get_tick_frequency() { +uint32_t kernel_get_tick_frequency() { /* Return frequency in hertz */ return (configTICK_RATE_HZ); } -void tt_delay_tick(uint32_t ticks) { - tt_assert(!tt_kernel_is_irq()); +void delay_tick(uint32_t ticks) { + tt_assert(!kernel_is_irq()); if (ticks == 0U) { taskYIELD(); } else { @@ -125,8 +127,8 @@ void tt_delay_tick(uint32_t ticks) { } } -TtStatus tt_delay_until_tick(uint32_t tick) { - tt_assert(!tt_kernel_is_irq()); +TtStatus delay_until_tick(uint32_t tick) { + tt_assert(!kernel_is_irq()); TickType_t tcnt, delay; TtStatus stat; @@ -152,10 +154,10 @@ TtStatus tt_delay_until_tick(uint32_t tick) { return (stat); } -uint32_t tt_get_tick() { +uint32_t get_tick() { TickType_t ticks; - if (tt_kernel_is_irq() != 0U) { + if (kernel_is_irq() != 0U) { ticks = xTaskGetTickCountFromISR(); } else { ticks = xTaskGetTickCount(); @@ -164,7 +166,7 @@ uint32_t tt_get_tick() { return ticks; } -uint32_t tt_ms_to_ticks(uint32_t milliseconds) { +uint32_t ms_to_ticks(uint32_t milliseconds) { #if configTICK_RATE_HZ == 1000 return milliseconds; #else @@ -172,7 +174,7 @@ uint32_t tt_ms_to_ticks(uint32_t milliseconds) { #endif } -void tt_delay_ms(uint32_t milliseconds) { +void delay_ms(uint32_t milliseconds) { if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { if (milliseconds > 0 && milliseconds < portMAX_DELAY - 1) { milliseconds += 1; @@ -180,14 +182,14 @@ void tt_delay_ms(uint32_t milliseconds) { #if configTICK_RATE_HZ_RAW == 1000 tt_delay_tick(milliseconds); #else - tt_delay_tick(tt_ms_to_ticks(milliseconds)); + delay_tick(ms_to_ticks(milliseconds)); #endif } else if (milliseconds > 0) { - tt_delay_us(milliseconds * 1000); + delay_us(milliseconds * 1000); } } -void tt_delay_us(uint32_t microseconds) { +void delay_us(uint32_t microseconds) { #ifdef ESP_PLATFORM ets_delay_us(microseconds); #else @@ -195,10 +197,12 @@ void tt_delay_us(uint32_t microseconds) { #endif } -Platform tt_get_platform() { +Platform get_platform() { #ifdef ESP_PLATFORM return PlatformEsp; #else return PlatformPc; #endif } + +} // namespace diff --git a/tactility-core/src/kernel.h b/TactilityCore/Source/Kernel.h similarity index 83% rename from tactility-core/src/kernel.h rename to TactilityCore/Source/Kernel.h index 4fa9f9ad..218453ec 100644 --- a/tactility-core/src/kernel.h +++ b/TactilityCore/Source/Kernel.h @@ -1,10 +1,8 @@ #pragma once -#include "core_types.h" +#include "CoreTypes.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { typedef enum { PlatformEsp, @@ -26,13 +24,13 @@ typedef enum { * * @return true if CPU is in IRQ or kernel running and IRQ is masked */ -bool tt_kernel_is_irq(); +bool kernel_is_irq(); /** Check if kernel is running * * @return true if running, false otherwise */ -bool tt_kernel_is_running(); +bool kernel_is_running(); /** Lock kernel, pause process scheduling * @@ -40,7 +38,7 @@ bool tt_kernel_is_running(); * * @return previous lock state(0 - unlocked, 1 - locked) */ -int32_t tt_kernel_lock(); +int32_t kernel_lock(); /** Unlock kernel, resume process scheduling * @@ -48,7 +46,7 @@ int32_t tt_kernel_lock(); * * @return previous lock state(0 - unlocked, 1 - locked) */ -int32_t tt_kernel_unlock(); +int32_t kernel_unlock(); /** Restore kernel lock state * @@ -58,13 +56,13 @@ int32_t tt_kernel_unlock(); * * @return new lock state or error */ -int32_t tt_kernel_restore_lock(int32_t lock); +int32_t kernel_restore_lock(int32_t lock); /** Get kernel systick frequency * * @return systick counts per second */ -uint32_t tt_kernel_get_tick_frequency(); +uint32_t kernel_get_tick_frequency(); /** Delay execution * @@ -74,7 +72,7 @@ uint32_t tt_kernel_get_tick_frequency(); * * @param[in] ticks The ticks count to pause */ -void tt_delay_tick(uint32_t ticks); +void delay_tick(uint32_t ticks); /** Delay until tick * @@ -84,14 +82,14 @@ void tt_delay_tick(uint32_t ticks); * * @return The status. */ -TtStatus tt_delay_until_tick(uint32_t tick); +TtStatus delay_until_tick(uint32_t tick); /** Convert milliseconds to ticks * * @param[in] milliseconds time in milliseconds * @return time in ticks */ -uint32_t tt_ms_to_ticks(uint32_t milliseconds); +uint32_t ms_to_ticks(uint32_t milliseconds); /** Delay in milliseconds * @@ -104,7 +102,7 @@ uint32_t tt_ms_to_ticks(uint32_t milliseconds); * * @param[in] milliseconds milliseconds to wait */ -void tt_delay_ms(uint32_t milliseconds); +void delay_ms(uint32_t milliseconds); /** Delay in microseconds * @@ -112,10 +110,8 @@ void tt_delay_ms(uint32_t milliseconds); * * @param[in] microseconds microseconds to wait */ -void tt_delay_us(uint32_t microseconds); +void delay_us(uint32_t microseconds); -Platform tt_get_platform(); +Platform get_platform(); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility-core/src/log.c b/TactilityCore/Source/Log.cpp similarity index 77% rename from tactility-core/src/log.c rename to TactilityCore/Source/Log.cpp index 004937cb..43d00265 100644 --- a/tactility-core/src/log.c +++ b/TactilityCore/Source/Log.cpp @@ -1,17 +1,18 @@ #ifndef ESP_PLATFORM -#include "log.h" +#include "Log.h" #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" #include "freertos/task.h" #else -#include +#include #include #endif +namespace tt { -static char tt_loglevel_to_prefix(LogLevel level) { +static char loglevel_to_prefix(LogLevel level) { switch (level) { case LogLevelError: return 'E'; @@ -28,7 +29,7 @@ static char tt_loglevel_to_prefix(LogLevel level) { } } -static const char* tt_loglevel_to_colour(LogLevel level) { +static const char* loglevel_to_colour(LogLevel level) { switch (level) { case LogLevelError: return "\033[1;31m"; @@ -45,7 +46,7 @@ static const char* tt_loglevel_to_colour(LogLevel level) { } } -uint64_t tt_log_timestamp(void) { +uint64_t log_timestamp() { #ifdef ESP_PLATFORM if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) { return clock() / CLOCKS_PER_SEC * 1000; @@ -59,8 +60,8 @@ uint64_t tt_log_timestamp(void) { #else static uint64_t base = 0; - struct timeval time; - gettimeofday(&time, 0); + struct timeval time {}; + gettimeofday(&time, nullptr); uint64_t now = ((uint64_t)time.tv_sec * 1000) + (time.tv_usec / 1000); if (base == 0) { base = now; @@ -69,12 +70,12 @@ uint64_t tt_log_timestamp(void) { #endif } -void tt_log(LogLevel level, const char* tag, const char* format, ...) { +void log(LogLevel level, const char* tag, const char* format, ...) { printf( - "%s%c (%llu) %s: ", - tt_loglevel_to_colour(level), - tt_loglevel_to_prefix(level), - tt_log_timestamp(), + "%s%c (%lu) %s: ", + loglevel_to_colour(level), + loglevel_to_prefix(level), + log_timestamp(), tag ); @@ -86,4 +87,6 @@ void tt_log(LogLevel level, const char* tag, const char* format, ...) { printf("\033[0m\n"); } +} // namespace + #endif \ No newline at end of file diff --git a/tactility-core/src/log.h b/TactilityCore/Source/Log.h similarity index 64% rename from tactility-core/src/log.h rename to TactilityCore/Source/Log.h index 3f4714c0..3d2bd3d6 100644 --- a/tactility-core/src/log.h +++ b/TactilityCore/Source/Log.h @@ -3,12 +3,8 @@ #ifdef ESP_TARGET #include "esp_log.h" #else -#include -#include -#endif - -#ifdef __cplusplus -extern "C" { +#include +#include #endif #ifdef ESP_TARGET @@ -26,6 +22,8 @@ extern "C" { #else +namespace tt { + typedef enum { LogLevelError, LogLevelWarning, @@ -34,21 +32,19 @@ typedef enum { LogLevelTrace } LogLevel; -void tt_log(LogLevel level, const char* tag, const char* format, ...); +void log(LogLevel level, const char* tag, const char* format, ...); + +} // namespace #define TT_LOG_E(tag, format, ...) \ - tt_log(LogLevelError, tag, format, ##__VA_ARGS__) + tt::log(tt::LogLevelError, tag, format, ##__VA_ARGS__) #define TT_LOG_W(tag, format, ...) \ - tt_log(LogLevelWarning, tag, format, ##__VA_ARGS__) + tt::log(tt::LogLevelWarning, tag, format, ##__VA_ARGS__) #define TT_LOG_I(tag, format, ...) \ - tt_log(LogLevelInfo, tag, format, ##__VA_ARGS__) + tt::log(tt::LogLevelInfo, tag, format, ##__VA_ARGS__) #define TT_LOG_D(tag, format, ...) \ - tt_log(LogLevelDebug, tag, format, ##__VA_ARGS__) + tt::log(tt::LogLevelDebug, tag, format, ##__VA_ARGS__) #define TT_LOG_T(tag, format, ...) \ - tt_log(LOG_LEVEL_TRACE, tag, format, ##__VA_ARGS__) + tt::log(tt::LogLevelTrace, tag, format, ##__VA_ARGS__) #endif // ESP_TARGET - -#ifdef __cplusplus -} -#endif diff --git a/TactilityCore/Source/MessageQueue.cpp b/TactilityCore/Source/MessageQueue.cpp new file mode 100644 index 00000000..c0ceaf47 --- /dev/null +++ b/TactilityCore/Source/MessageQueue.cpp @@ -0,0 +1,173 @@ +#include "MessageQueue.h" +#include "Check.h" +#include "Kernel.h" + +namespace tt { + +MessageQueue::MessageQueue(uint32_t msg_count, uint32_t msg_size) { + tt_assert((kernel_is_irq() == 0U) && (msg_count > 0U) && (msg_size > 0U)); + queue_handle = xQueueCreate(msg_count, msg_size); + tt_check(queue_handle); +} + +MessageQueue::~MessageQueue() { + tt_assert(kernel_is_irq() == 0U); + vQueueDelete(queue_handle); +} + +TtStatus MessageQueue::put(const void* msg_ptr, uint32_t timeout) { + TtStatus stat; + BaseType_t yield; + + stat = TtStatusOk; + + if (kernel_is_irq() != 0U) { + if ((queue_handle == nullptr) || (msg_ptr == nullptr) || (timeout != 0U)) { + stat = TtStatusErrorParameter; + } else { + yield = pdFALSE; + + if (xQueueSendToBackFromISR(queue_handle, msg_ptr, &yield) != pdTRUE) { + stat = TtStatusErrorResource; + } else { + portYIELD_FROM_ISR(yield); + } + } + } else { + if ((queue_handle == nullptr) || (msg_ptr == nullptr)) { + stat = TtStatusErrorParameter; + } else { + if (xQueueSendToBack(queue_handle, msg_ptr, (TickType_t)timeout) != pdPASS) { + if (timeout != 0U) { + stat = TtStatusErrorTimeout; + } else { + stat = TtStatusErrorResource; + } + } + } + } + + /* Return execution status */ + return (stat); +} + +TtStatus MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) { + TtStatus stat; + BaseType_t yield; + + stat = TtStatusOk; + + if (kernel_is_irq() != 0U) { + if ((queue_handle == nullptr) || (msg_ptr == nullptr) || (timeout_ticks != 0U)) { + stat = TtStatusErrorParameter; + } else { + yield = pdFALSE; + + if (xQueueReceiveFromISR(queue_handle, msg_ptr, &yield) != pdPASS) { + stat = TtStatusErrorResource; + } else { + portYIELD_FROM_ISR(yield); + } + } + } else { + if ((queue_handle == nullptr) || (msg_ptr == nullptr)) { + stat = TtStatusErrorParameter; + } else { + if (xQueueReceive(queue_handle, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) { + if (timeout_ticks != 0U) { + stat = TtStatusErrorTimeout; + } else { + stat = TtStatusErrorResource; + } + } + } + } + + /* Return execution status */ + return (stat); +} + +uint32_t MessageQueue::getCapacity() const { + auto* mq = (StaticQueue_t*)(queue_handle); + uint32_t capacity; + + if (mq == nullptr) { + capacity = 0U; + } else { + /* capacity = pxQueue->uxLength */ + capacity = mq->uxDummy4[1]; + } + + /* Return maximum number of messages */ + return (capacity); +} + +uint32_t MessageQueue::getMessageSize() const { + auto* mq = (StaticQueue_t*)(queue_handle); + uint32_t size; + + if (mq == nullptr) { + size = 0U; + } else { + /* size = pxQueue->uxItemSize */ + size = mq->uxDummy4[2]; + } + + /* Return maximum message size */ + return (size); +} + +uint32_t MessageQueue::getCount() const { + UBaseType_t count; + + if (queue_handle == nullptr) { + count = 0U; + } else if (kernel_is_irq() != 0U) { + count = uxQueueMessagesWaitingFromISR(queue_handle); + } else { + count = uxQueueMessagesWaiting(queue_handle); + } + + /* Return number of queued messages */ + return ((uint32_t)count); +} + +uint32_t MessageQueue::getSpace() const { + auto* mq = (StaticQueue_t*)(queue_handle); + uint32_t space; + uint32_t isrm; + + if (mq == nullptr) { + space = 0U; + } else if (kernel_is_irq() != 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); +} + +TtStatus MessageQueue::reset() { + TtStatus stat; + + if (kernel_is_irq() != 0U) { + stat = TtStatusErrorISR; + } else if (queue_handle == nullptr) { + stat = TtStatusErrorParameter; + } else { + stat = TtStatusOk; + (void)xQueueReset(queue_handle); + } + + /* Return execution status */ + return (stat); +} + +} // namespace diff --git a/TactilityCore/Source/MessageQueue.h b/TactilityCore/Source/MessageQueue.h new file mode 100644 index 00000000..30af0f4b --- /dev/null +++ b/TactilityCore/Source/MessageQueue.h @@ -0,0 +1,99 @@ +/** + * @file MessageQueue.h + * + * MessageQueue is a wrapper for FreeRTOS xQueue functionality. + * There is no additional thread-safety on top of the xQueue functionality, + * so make sure you create a lock if needed. + */ +#pragma once + +#include "CoreTypes.h" + +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#else +#include "FreeRTOS.h" +#include "queue.h" +#endif + +namespace tt { + +class MessageQueue { +private: + QueueHandle_t queue_handle; + +public: + /** Allocate message queue + * + * @param[in] msg_count The message count + * @param[in] msg_size The message size + */ + MessageQueue(uint32_t msg_count, uint32_t msg_size); + + ~MessageQueue(); + + /** Put message into queue + * + * @param instance pointer to MessageQueue instance + * @param[in] msg_ptr The message pointer + * @param[in] timeout The timeout + * @param[in] msg_prio The message prio + * + * @return The status. + */ + TtStatus put(const void* msg_ptr, uint32_t timeout); + + /** Get message from queue + * + * @param instance pointer to MessageQueue instance + * @param msg_ptr The message pointer + * @param msg_prio The message prioority + * @param[in] timeout_ticks The timeout + * + * @return The status. + */ + TtStatus get(void* msg_ptr, uint32_t timeout_ticks); + + /** Get queue capacity + * + * @param instance pointer to MessageQueue instance + * + * @return capacity in object count + */ + uint32_t getCapacity() const; + + /** Get message size + * + * @param instance pointer to MessageQueue instance + * + * @return Message size in bytes + */ + uint32_t getMessageSize() const; + + /** Get message count in queue + * + * @param instance pointer to MessageQueue instance + * + * @return Message count + */ + uint32_t getCount() const; + + /** Get queue available space + * + * @param instance pointer to MessageQueue instance + * + * @return Message count + */ + uint32_t getSpace() const; + + /** Reset queue + * + * @param instance pointer to MessageQueue instance + * + * @return The status. + */ + TtStatus reset(); +}; + +} // namespace diff --git a/TactilityCore/Source/Mutex.cpp b/TactilityCore/Source/Mutex.cpp new file mode 100644 index 00000000..2ba00723 --- /dev/null +++ b/TactilityCore/Source/Mutex.cpp @@ -0,0 +1,135 @@ +#include "Mutex.h" + +#include "Check.h" +#include "CoreDefines.h" +#include "Log.h" + +namespace tt { + +#define MUTEX_DEBUGGING false + +#if MUTEX_DEBUGGING +#define TAG "mutex" + void tt_mutex_info(Mutex mutex, const char* label) { + MutexData* data = (MutexData*)mutex; + if (data == NULL) { + TT_LOG_I(TAG, "mutex %s: is NULL", label); + } else { + TT_LOG_I(TAG, "mutex %s: handle=%0X type=%d owner=%0x", label, data->handle, data->type, tt_mutex_get_owner(mutex)); + } + } +#else +#define tt_mutex_info(mutex, text) +#endif + +Mutex::Mutex(MutexType type) : type(type) { + tt_mutex_info(data, "alloc"); + switch (type) { + case MutexTypeNormal: + semaphore = xSemaphoreCreateMutex(); + break; + case MutexTypeRecursive: + semaphore = xSemaphoreCreateRecursiveMutex(); + break; + default: + tt_crash("Mutex type unknown/corrupted"); + } + + tt_check(semaphore != nullptr); + +} + +Mutex::~Mutex() { + tt_assert(!TT_IS_IRQ_MODE()); + vSemaphoreDelete(semaphore); + semaphore = nullptr; // If the mutex is used after release, this might help debugging +} + +TtStatus Mutex::acquire(uint32_t timeout) const { + tt_assert(!TT_IS_IRQ_MODE()); + tt_assert(semaphore); + TtStatus status = TtStatusOk; + + tt_mutex_info(mutex, "acquire"); + + switch (type) { + case MutexTypeNormal: + if (xSemaphoreTake(semaphore, timeout) != pdPASS) { + if (timeout != 0U) { + status = TtStatusErrorTimeout; + } else { + status = TtStatusErrorResource; + } + } + break; + case MutexTypeRecursive: + if (xSemaphoreTakeRecursive(semaphore, timeout) != pdPASS) { + if (timeout != 0U) { + status = TtStatusErrorTimeout; + } else { + status = TtStatusErrorResource; + } + } + break; + default: + tt_crash("mutex type unknown/corrupted"); + } + + return status; +} + +TtStatus Mutex::release() const { + assert(!TT_IS_IRQ_MODE()); + tt_assert(semaphore); + TtStatus status = TtStatusOk; + + tt_mutex_info(mutex, "release"); + + switch (type) { + case MutexTypeNormal: { + if (xSemaphoreGive(semaphore) != pdPASS) { + status = TtStatusErrorResource; + } + break; + } + case MutexTypeRecursive: + if (xSemaphoreGiveRecursive(semaphore) != pdPASS) { + status = TtStatusErrorResource; + } + break; + default: + tt_crash("mutex type unknown/corrupted"); + } + + return status; +} + +ThreadId Mutex::getOwner() const { + tt_assert(!TT_IS_IRQ_MODE()); + tt_assert(semaphore); + return (ThreadId)xSemaphoreGetMutexHolder(semaphore); +} + + +Mutex* tt_mutex_alloc(MutexType type) { + return new Mutex(type); +} + +void tt_mutex_free(Mutex* mutex) { + delete mutex; +} + +TtStatus tt_mutex_acquire(Mutex* mutex, uint32_t timeout) { + return mutex-> acquire(timeout); +} + +TtStatus tt_mutex_release(Mutex* mutex) { + return mutex->release(); + +} + +ThreadId tt_mutex_get_owner(Mutex* mutex) { + return mutex->getOwner(); +} + +} // namespace diff --git a/tactility-core/src/mutex.h b/TactilityCore/Source/Mutex.h similarity index 60% rename from tactility-core/src/mutex.h rename to TactilityCore/Source/Mutex.h index a7059c62..448f271d 100644 --- a/tactility-core/src/mutex.h +++ b/TactilityCore/Source/Mutex.h @@ -4,19 +4,36 @@ */ #pragma once -#include "core_types.h" -#include "thread.h" +#include "CoreTypes.h" +#include "Thread.h" -#ifdef __cplusplus -extern "C" { +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#else +#include "FreeRTOS.h" +#include "semphr.h" #endif +namespace tt { + typedef enum { MutexTypeNormal, MutexTypeRecursive, } MutexType; -typedef void Mutex; +class Mutex { +private: + SemaphoreHandle_t semaphore; + MutexType type; +public: + Mutex(MutexType type); + ~Mutex(); + + TtStatus acquire(uint32_t timeout) const; + TtStatus release() const; + ThreadId getOwner() const; +}; /** Allocate Mutex * @@ -24,12 +41,15 @@ typedef void Mutex; * * @return pointer to Mutex instance */ + +[[deprecated("use class")]] Mutex* tt_mutex_alloc(MutexType type); /** Free Mutex * * @param mutex The Mutex instance */ +[[deprecated("use class")]] void tt_mutex_free(Mutex* mutex); /** Acquire mutex @@ -39,6 +59,7 @@ void tt_mutex_free(Mutex* mutex); * * @return The status. */ +[[deprecated("use class")]] TtStatus tt_mutex_acquire(Mutex* mutex, uint32_t timeout); /** Release mutex @@ -47,6 +68,7 @@ TtStatus tt_mutex_acquire(Mutex* mutex, uint32_t timeout); * * @return The status. */ +[[deprecated("use class")]] TtStatus tt_mutex_release(Mutex* mutex); /** Get mutex owner thread id @@ -55,8 +77,7 @@ TtStatus tt_mutex_release(Mutex* mutex); * * @return The thread identifier. */ +[[deprecated("use class")]] ThreadId tt_mutex_get_owner(Mutex* mutex); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/TactilityCore/Source/Pubsub.cpp b/TactilityCore/Source/Pubsub.cpp new file mode 100644 index 00000000..220cde86 --- /dev/null +++ b/TactilityCore/Source/Pubsub.cpp @@ -0,0 +1,84 @@ +#include "Pubsub.h" +#include "Check.h" +#include "Mutex.h" +#include + +namespace tt { + +struct PubSubSubscription { + uint64_t id; + PubSubCallback callback; + void* callback_context; +}; + +typedef std::list Subscriptions; + +struct PubSub { + uint64_t last_id = 0; + Subscriptions items; + Mutex* mutex; +}; + +PubSub* tt_pubsub_alloc() { + auto* pubsub = new PubSub(); + + pubsub->mutex = tt_mutex_alloc(MutexTypeNormal); + tt_assert(pubsub->mutex); + + return pubsub; +} + +void tt_pubsub_free(PubSub* pubsub) { + tt_assert(pubsub); + tt_check(pubsub->items.empty()); + tt_mutex_free(pubsub->mutex); + delete pubsub; +} + +PubSubSubscription* tt_pubsub_subscribe(PubSub* pubsub, PubSubCallback callback, void* callback_context) { + tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk); + PubSubSubscription subscription = { + .id = (++pubsub->last_id), + .callback = callback, + .callback_context = callback_context + }; + pubsub->items.push_back( + subscription + ); + + tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk); + + return (PubSubSubscription*)pubsub->last_id; +} + +void tt_pubsub_unsubscribe(PubSub* pubsub, PubSubSubscription* pubsub_subscription) { + tt_assert(pubsub); + tt_assert(pubsub_subscription); + + tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk); + bool result = false; + auto id = (uint64_t)pubsub_subscription; + for (auto it = pubsub->items.begin(); it != pubsub->items.end(); it++) { + if (it->id == id) { + pubsub->items.erase(it); + result = true; + break; + } + } + + tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk); + tt_check(result); +} + +void tt_pubsub_publish(PubSub* pubsub, void* message) { + tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk); + + // Iterate over subscribers + for (auto& it : pubsub->items) { + it.callback(message, it.callback_context); + } + + tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk); +} + +} // namespace diff --git a/tactility-core/src/pubsub.h b/TactilityCore/Source/Pubsub.h similarity index 95% rename from tactility-core/src/pubsub.h rename to TactilityCore/Source/Pubsub.h index 15e58e1b..6db6bb36 100644 --- a/tactility-core/src/pubsub.h +++ b/TactilityCore/Source/Pubsub.h @@ -4,9 +4,7 @@ */ #pragma once -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { /** PubSub Callback type */ typedef void (*PubSubCallback)(const void* message, void* context); @@ -63,6 +61,4 @@ void tt_pubsub_unsubscribe(PubSub* pubsub, PubSubSubscription* pubsub_subscripti */ void tt_pubsub_publish(PubSub* pubsub, void* message); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility-core/src/semaphore.c b/TactilityCore/Source/Semaphore.cpp similarity index 92% rename from tactility-core/src/semaphore.c rename to TactilityCore/Source/Semaphore.cpp index 8c9d8726..ab2d5731 100644 --- a/tactility-core/src/semaphore.c +++ b/TactilityCore/Source/Semaphore.cpp @@ -1,6 +1,6 @@ -#include "semaphore.h" -#include "check.h" -#include "core_defines.h" +#include "Semaphore.h" +#include "Check.h" +#include "CoreDefines.h" #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -10,17 +10,19 @@ #include "semphr.h" #endif +namespace tt { + Semaphore* tt_semaphore_alloc(uint32_t max_count, uint32_t initial_count) { tt_assert(!TT_IS_IRQ_MODE()); tt_assert((max_count > 0U) && (initial_count <= max_count)); - SemaphoreHandle_t hSemaphore = NULL; + SemaphoreHandle_t hSemaphore = nullptr; if (max_count == 1U) { hSemaphore = xSemaphoreCreateBinary(); - if ((hSemaphore != NULL) && (initial_count != 0U)) { + if ((hSemaphore != nullptr) && (initial_count != 0U)) { if (xSemaphoreGive(hSemaphore) != pdPASS) { vSemaphoreDelete(hSemaphore); - hSemaphore = NULL; + hSemaphore = nullptr; } } } else { @@ -122,3 +124,5 @@ uint32_t tt_semaphore_get_count(Semaphore* instance) { /* Return number of tokens */ return (count); } + +} // namespace diff --git a/tactility-core/src/semaphore.h b/TactilityCore/Source/Semaphore.h similarity index 89% rename from tactility-core/src/semaphore.h rename to TactilityCore/Source/Semaphore.h index 0330fa5a..888cb272 100644 --- a/tactility-core/src/semaphore.h +++ b/TactilityCore/Source/Semaphore.h @@ -1,11 +1,9 @@ #pragma once -#include "core_types.h" -#include "thread.h" +#include "CoreTypes.h" +#include "Thread.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { typedef void Semaphore; @@ -49,6 +47,4 @@ TtStatus tt_semaphore_release(Semaphore* instance); */ uint32_t tt_semaphore_get_count(Semaphore* instance); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/TactilityCore/Source/StreamBuffer.cpp b/TactilityCore/Source/StreamBuffer.cpp new file mode 100644 index 00000000..34adbbc9 --- /dev/null +++ b/TactilityCore/Source/StreamBuffer.cpp @@ -0,0 +1,98 @@ +#include "StreamBuffer.h" + +#include "Check.h" +#include "CoreDefines.h" +#include "CoreTypes.h" + +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#include "freertos/stream_buffer.h" +#else +#include "FreeRTOS.h" +#include "stream_buffer.h" +#endif + +namespace tt { + +StreamBuffer* stream_buffer_alloc(size_t size, size_t trigger_level) { + tt_assert(size != 0); + + StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level); + tt_check(handle); + + return handle; +}; + +void stream_buffer_free(StreamBuffer* stream_buffer) { + tt_assert(stream_buffer); + vStreamBufferDelete((StreamBufferHandle_t)stream_buffer); +}; + +bool stream_set_trigger_level(StreamBuffer* stream_buffer, size_t trigger_level) { + tt_assert(stream_buffer); + return xStreamBufferSetTriggerLevel((StreamBufferHandle_t)stream_buffer, trigger_level) == pdTRUE; +}; + +size_t stream_buffer_send( + StreamBuffer* stream_buffer, + const void* data, + size_t length, + uint32_t timeout +) { + size_t ret; + + if (TT_IS_IRQ_MODE()) { + BaseType_t yield; + ret = xStreamBufferSendFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield); + portYIELD_FROM_ISR(yield); + } else { + ret = xStreamBufferSend((StreamBufferHandle_t)stream_buffer, data, length, timeout); + } + + return ret; +}; + +size_t stream_buffer_receive( + StreamBuffer* stream_buffer, + void* data, + size_t length, + uint32_t timeout +) { + size_t ret; + + if (TT_IS_IRQ_MODE()) { + BaseType_t yield; + ret = xStreamBufferReceiveFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield); + portYIELD_FROM_ISR(yield); + } else { + ret = xStreamBufferReceive((StreamBufferHandle_t)stream_buffer, data, length, timeout); + } + + return ret; +} + +size_t stream_buffer_bytes_available(StreamBuffer* stream_buffer) { + return xStreamBufferBytesAvailable((StreamBufferHandle_t)stream_buffer); +}; + +size_t stream_buffer_spaces_available(StreamBuffer* stream_buffer) { + return xStreamBufferSpacesAvailable((StreamBufferHandle_t)stream_buffer); +}; + +bool stream_buffer_is_full(StreamBuffer* stream_buffer) { + return xStreamBufferIsFull((StreamBufferHandle_t)stream_buffer) == pdTRUE; +}; + +bool stream_buffer_is_empty(StreamBuffer* stream_buffer) { + return (xStreamBufferIsEmpty((StreamBufferHandle_t)stream_buffer) == pdTRUE); +}; + +TtStatus stream_buffer_reset(StreamBuffer* stream_buffer) { + if (xStreamBufferReset((StreamBufferHandle_t)stream_buffer) == pdPASS) { + return TtStatusOk; + } else { + return TtStatusError; + } +} + +} // namespace diff --git a/tactility-core/src/tt_stream_buffer.h b/TactilityCore/Source/StreamBuffer.h similarity index 89% rename from tactility-core/src/tt_stream_buffer.h rename to TactilityCore/Source/StreamBuffer.h index 6036ae37..e3fc3af3 100644 --- a/tactility-core/src/tt_stream_buffer.h +++ b/TactilityCore/Source/StreamBuffer.h @@ -12,14 +12,12 @@ * interrupt that will read from the buffer (the reader). */ #pragma once -#include "core_types.h" -#include -#include -#include -#ifdef __cplusplus -extern "C" { -#endif +#include "CoreTypes.h" +#include +#include + +namespace tt { typedef void StreamBuffer; @@ -34,14 +32,14 @@ typedef void StreamBuffer; * 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. */ -StreamBuffer* tt_stream_buffer_alloc(size_t size, size_t trigger_level); +StreamBuffer* stream_buffer_alloc(size_t size, size_t trigger_level); /** * @brief Free stream buffer instance * * @param stream_buffer The stream buffer instance. */ -void tt_stream_buffer_free(StreamBuffer* stream_buffer); +void stream_buffer_free(StreamBuffer* stream_buffer); /** * @brief Set trigger level for stream buffer. @@ -54,7 +52,7 @@ void tt_stream_buffer_free(StreamBuffer* 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 tt_stream_set_trigger_level(StreamBuffer* stream_buffer, size_t trigger_level); +bool stream_set_trigger_level(StreamBuffer* stream_buffer, size_t trigger_level); /** * @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer. @@ -70,7 +68,7 @@ bool tt_stream_set_trigger_level(StreamBuffer* stream_buffer, size_t trigger_lev * Ignored if called from ISR. * @return The number of bytes actually written to the stream buffer. */ -size_t tt_stream_buffer_send( +size_t stream_buffer_send( StreamBuffer* stream_buffer, const void* data, size_t length, @@ -92,7 +90,7 @@ size_t tt_stream_buffer_send( * Ignored if called from ISR. * @return The number of bytes read from the stream buffer, if any. */ -size_t tt_stream_buffer_receive( +size_t stream_buffer_receive( StreamBuffer* stream_buffer, void* data, size_t length, @@ -108,7 +106,7 @@ size_t tt_stream_buffer_receive( * @return The number of bytes that can be read from the stream buffer before * the stream buffer would be empty. */ -size_t tt_stream_buffer_bytes_available(StreamBuffer* stream_buffer); +size_t stream_buffer_bytes_available(StreamBuffer* stream_buffer); /** * @brief Queries a stream buffer to see how much free space it contains, which is @@ -119,7 +117,7 @@ size_t tt_stream_buffer_bytes_available(StreamBuffer* stream_buffer); * @return The number of bytes that can be written to the stream buffer before * the stream buffer would be full. */ -size_t tt_stream_buffer_spaces_available(StreamBuffer* stream_buffer); +size_t stream_buffer_spaces_available(StreamBuffer* stream_buffer); /** * @brief Queries a stream buffer to see if it is full. @@ -128,7 +126,7 @@ size_t tt_stream_buffer_spaces_available(StreamBuffer* stream_buffer); * @return true if the stream buffer is full. * @return false if the stream buffer is not full. */ -bool tt_stream_buffer_is_full(StreamBuffer* stream_buffer); +bool stream_buffer_is_full(StreamBuffer* stream_buffer); /** * @brief Queries a stream buffer to see if it is empty. @@ -151,6 +149,4 @@ bool tt_stream_buffer_is_empty(StreamBuffer* stream_buffer); */ TtStatus tt_stream_buffer_reset(StreamBuffer* stream_buffer); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/tactility-core/src/string_utils.c b/TactilityCore/Source/StringUtils.cpp similarity index 58% rename from tactility-core/src/string_utils.c rename to TactilityCore/Source/StringUtils.cpp index 554452af..e2285d46 100644 --- a/tactility-core/src/string_utils.c +++ b/TactilityCore/Source/StringUtils.cpp @@ -1,7 +1,9 @@ -#include "string_utils.h" -#include +#include "StringUtils.h" +#include -int tt_string_find_last_index(const char* text, size_t from_index, char find) { +namespace tt { + +int string_find_last_index(const char* text, size_t from_index, char find) { for (size_t i = from_index; i >= 0; i--) { if (text[i] == find) { return (int)i; @@ -10,8 +12,8 @@ int tt_string_find_last_index(const char* text, size_t from_index, char find) { return -1; } -bool tt_string_get_path_parent(const char* path, char* output) { - int index = tt_string_find_last_index(path, strlen(path) - 1, '/'); +bool string_get_path_parent(const char* path, char* output) { + int index = string_find_last_index(path, strlen(path) - 1, '/'); if (index == -1) { return false; } else if (index == 0) { @@ -24,3 +26,5 @@ bool tt_string_get_path_parent(const char* path, char* output) { return true; } } + +} // namespace diff --git a/tactility-core/src/string_utils.h b/TactilityCore/Source/StringUtils.h similarity index 68% rename from tactility-core/src/string_utils.h rename to TactilityCore/Source/StringUtils.h index b3d32fd5..0818feef 100644 --- a/tactility-core/src/string_utils.h +++ b/TactilityCore/Source/StringUtils.h @@ -1,11 +1,8 @@ #pragma once -#include -#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { /** * Find the last occurrence of a character. @@ -14,7 +11,7 @@ extern "C" { * @param[in] find the character to search for * @return the index of the found character, or -1 if none found */ -int tt_string_find_last_index(const char* text, size_t from_index, char find); +int string_find_last_index(const char* text, size_t from_index, char find); /** * Given a filesystem path as input, try and get the parent path. @@ -22,8 +19,6 @@ int tt_string_find_last_index(const char* text, size_t from_index, char find); * @param[out] output an output buffer that is allocated to at least the size of "current" * @return true when successful */ -bool tt_string_get_path_parent(const char* path, char* output); +bool string_get_path_parent(const char* path, char* output); -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace diff --git a/TactilityCore/Source/TactilityCore.h b/TactilityCore/Source/TactilityCore.h new file mode 100644 index 00000000..56d7eb85 --- /dev/null +++ b/TactilityCore/Source/TactilityCore.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "Check.h" +#include "CoreDefines.h" +#include "CoreExtraDefines.h" +#include "CoreTypes.h" +#include "Critical.h" +#include "EventFlag.h" +#include "Kernel.h" +#include "Log.h" diff --git a/tactility-core/src/tactility_core_config.h b/TactilityCore/Source/TactilityCoreConfig.h similarity index 100% rename from tactility-core/src/tactility_core_config.h rename to TactilityCore/Source/TactilityCoreConfig.h diff --git a/tactility-core/src/thread.c b/TactilityCore/Source/Thread.cpp similarity index 69% rename from tactility-core/src/thread.c rename to TactilityCore/Source/Thread.cpp index 21c732b2..6675c1f6 100644 --- a/tactility-core/src/thread.c +++ b/TactilityCore/Source/Thread.cpp @@ -1,9 +1,11 @@ -#include "thread.h" +#include "Thread.h" +#include +#include -#include "check.h" -#include "core_defines.h" -#include "kernel.h" -#include "log.h" +#include "Check.h" +#include "CoreDefines.h" +#include "Kernel.h" +#include "Log.h" #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -13,6 +15,8 @@ #include "task.h" #endif +namespace tt { + #define TAG "Thread" #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers @@ -52,7 +56,7 @@ struct Thread { }; /** Catch threads that are trying to exit wrong way */ -__attribute__((__noreturn__)) void tt_thread_catch() { //-V1082 +__attribute__((__noreturn__)) void 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 @@ -60,7 +64,7 @@ __attribute__((__noreturn__)) void tt_thread_catch() { //-V1082 __builtin_unreachable(); } -static void tt_thread_set_state(Thread* thread, ThreadState state) { +static void thread_set_state(Thread* thread, ThreadState state) { tt_assert(thread); thread->state = state; if (thread->state_callback) { @@ -68,16 +72,16 @@ static void tt_thread_set_state(Thread* thread, ThreadState state) { } } -static void tt_thread_body(void* context) { +static void thread_body(void* context) { tt_assert(context); - Thread* thread = context; + auto* thread = static_cast(context); // Store thread instance to thread local storage - tt_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) == NULL); - vTaskSetThreadLocalStoragePointer(NULL, 0, thread); + tt_assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr); + vTaskSetThreadLocalStoragePointer(nullptr, 0, thread); tt_assert(thread->state == ThreadStateStarting); - tt_thread_set_state(thread, ThreadStateRunning); + thread_set_state(thread, ThreadStateRunning); thread->ret = thread->callback(thread->context); @@ -91,59 +95,59 @@ static void tt_thread_body(void* context) { ); } - tt_thread_set_state(thread, ThreadStateStopped); + thread_set_state(thread, ThreadStateStopped); - vTaskSetThreadLocalStoragePointer(NULL, 0, NULL); - thread->task_handle = NULL; + vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr); + thread->task_handle = nullptr; - vTaskDelete(NULL); - tt_thread_catch(); + vTaskDelete(nullptr); + thread_catch(); } -Thread* tt_thread_alloc() { - Thread* thread = malloc(sizeof(Thread)); +Thread* thread_alloc() { + auto* thread = static_cast(malloc(sizeof(Thread))); // TODO: create default struct instead of using memset() memset(thread, 0, sizeof(Thread)); thread->is_static = false; - Thread* parent = NULL; + Thread* parent = nullptr; if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { // TLS is not available, if we called not from thread context - parent = pvTaskGetThreadLocalStoragePointer(NULL, 0); + parent = (Thread*)pvTaskGetThreadLocalStoragePointer(nullptr, 0); if (parent && parent->appid) { - tt_thread_set_appid(thread, parent->appid); + thread_set_appid(thread, parent->appid); } else { - tt_thread_set_appid(thread, "unknown"); + thread_set_appid(thread, "unknown"); } } else { // If scheduler is not started, we are starting driver thread - tt_thread_set_appid(thread, "driver"); + thread_set_appid(thread, "driver"); } return thread; } -Thread* tt_thread_alloc_ex( +Thread* thread_alloc_ex( const char* name, uint32_t stack_size, ThreadCallback callback, void* context ) { - Thread* thread = tt_thread_alloc(); - tt_thread_set_name(thread, name); - tt_thread_set_stack_size(thread, stack_size); - tt_thread_set_callback(thread, callback); - tt_thread_set_context(thread, context); + Thread* thread = thread_alloc(); + thread_set_name(thread, name); + thread_set_stack_size(thread, stack_size); + thread_set_callback(thread, callback); + thread_set_context(thread, context); return thread; } -void tt_thread_free(Thread* thread) { +void thread_free(Thread* thread) { tt_assert(thread); // Ensure that use join before free tt_assert(thread->state == ThreadStateStopped); - tt_assert(thread->task_handle == NULL); + tt_assert(thread->task_handle == nullptr); if (thread->name) free(thread->name); if (thread->appid) free(thread->appid); @@ -151,115 +155,115 @@ void tt_thread_free(Thread* thread) { free(thread); } -void tt_thread_set_name(Thread* thread, const char* name) { +void thread_set_name(Thread* thread, const char* name) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); if (thread->name) free(thread->name); - thread->name = name ? strdup(name) : NULL; + thread->name = name ? strdup(name) : nullptr; } -void tt_thread_set_appid(Thread* thread, const char* appid) { +void thread_set_appid(Thread* thread, const char* appid) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); if (thread->appid) free(thread->appid); - thread->appid = appid ? strdup(appid) : NULL; + thread->appid = appid ? strdup(appid) : nullptr; } -void tt_thread_mark_as_static(Thread* thread) { +void thread_mark_as_static(Thread* thread) { thread->is_static = true; } -bool tt_thread_mark_is_static(ThreadId thread_id) { - TaskHandle_t hTask = (TaskHandle_t)thread_id; - assert(!TT_IS_IRQ_MODE() && (hTask != NULL)); - Thread* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); - assert(thread != NULL); +bool thread_mark_is_static(ThreadId thread_id) { + auto hTask = (TaskHandle_t)thread_id; + assert(!TT_IS_IRQ_MODE() && (hTask != nullptr)); + auto* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); + assert(thread != nullptr); return thread->is_static; } -void tt_thread_set_stack_size(Thread* thread, size_t stack_size) { +void thread_set_stack_size(Thread* thread, size_t stack_size) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); tt_assert(stack_size % 4 == 0); thread->stack_size = stack_size; } -void tt_thread_set_callback(Thread* thread, ThreadCallback callback) { +void thread_set_callback(Thread* thread, ThreadCallback callback) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); thread->callback = callback; } -void tt_thread_set_context(Thread* thread, void* context) { +void thread_set_context(Thread* thread, void* context) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); thread->context = context; } -void tt_thread_set_priority(Thread* thread, ThreadPriority priority) { +void thread_set_priority(Thread* thread, ThreadPriority priority) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); tt_assert(priority >= 0 && priority <= TT_CONFIG_THREAD_MAX_PRIORITIES); thread->priority = priority; } -void tt_thread_set_current_priority(ThreadPriority priority) { +void thread_set_current_priority(ThreadPriority priority) { UBaseType_t new_priority = priority ? priority : ThreadPriorityNormal; - vTaskPrioritySet(NULL, new_priority); + vTaskPrioritySet(nullptr, new_priority); } -ThreadPriority tt_thread_get_current_priority() { - return (ThreadPriority)uxTaskPriorityGet(NULL); +ThreadPriority thread_get_current_priority() { + return (ThreadPriority)uxTaskPriorityGet(nullptr); } -void tt_thread_set_state_callback(Thread* thread, ThreadStateCallback callback) { +void thread_set_state_callback(Thread* thread, ThreadStateCallback callback) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); thread->state_callback = callback; } -void tt_thread_set_state_context(Thread* thread, void* context) { +void thread_set_state_context(Thread* thread, void* context) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); thread->state_context = context; } -ThreadState tt_thread_get_state(Thread* thread) { +ThreadState thread_get_state(Thread* thread) { tt_assert(thread); return thread->state; } -void tt_thread_start(Thread* thread) { +void thread_start(Thread* thread) { tt_assert(thread); tt_assert(thread->callback); tt_assert(thread->state == ThreadStateStopped); tt_assert(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t))); - tt_thread_set_state(thread, ThreadStateStarting); + thread_set_state(thread, ThreadStateStarting); uint32_t stack = thread->stack_size / sizeof(StackType_t); UBaseType_t priority = thread->priority ? thread->priority : ThreadPriorityNormal; if (thread->is_static) { #if configSUPPORT_STATIC_ALLOCATION == 1 thread->task_handle = xTaskCreateStatic( - tt_thread_body, + thread_body, thread->name, stack, thread, priority, - malloc(sizeof(StackType_t) * stack), - malloc(sizeof(StaticTask_t)) + static_cast(malloc(sizeof(StackType_t) * stack)), + static_cast(malloc(sizeof(StaticTask_t))) ); #else TT_LOG_E(TAG, "static tasks are not supported by current FreeRTOS config/platform - creating regular one"); BaseType_t ret = xTaskCreate( - tt_thread_body, thread->name, stack, thread, priority, &(thread->task_handle) + thread_body, thread->name, stack, thread, priority, &(thread->task_handle) ); tt_check(ret == pdPASS); #endif } else { BaseType_t ret = xTaskCreate( - tt_thread_body, thread->name, stack, thread, priority, &(thread->task_handle) + thread_body, thread->name, stack, thread, priority, &(thread->task_handle) ); tt_check(ret == pdPASS); } @@ -267,53 +271,53 @@ void tt_thread_start(Thread* thread) { tt_check(thread->state == ThreadStateStopped || thread->task_handle); } -bool tt_thread_join(Thread* thread) { +bool thread_join(Thread* thread) { tt_assert(thread); - tt_check(tt_thread_get_current() != thread); + tt_check(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) { - tt_delay_ms(10); + delay_ms(10); } return true; } -ThreadId tt_thread_get_id(Thread* thread) { +ThreadId thread_get_id(Thread* thread) { tt_assert(thread); return thread->task_handle; } -int32_t tt_thread_get_return_code(Thread* thread) { +int32_t thread_get_return_code(Thread* thread) { tt_assert(thread); tt_assert(thread->state == ThreadStateStopped); return thread->ret; } -ThreadId tt_thread_get_current_id() { +ThreadId thread_get_current_id() { return xTaskGetCurrentTaskHandle(); } -Thread* tt_thread_get_current() { - Thread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0); +Thread* thread_get_current() { + auto* thread = static_cast(pvTaskGetThreadLocalStoragePointer(nullptr, 0)); return thread; } -void tt_thread_yield() { +void thread_yield() { tt_assert(!TT_IS_IRQ_MODE()); taskYIELD(); } -uint32_t tt_thread_flags_set(ThreadId thread_id, uint32_t flags) { - TaskHandle_t hTask = (TaskHandle_t)thread_id; +uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) { + auto hTask = (TaskHandle_t)thread_id; uint32_t rflags; BaseType_t yield; - if ((hTask == NULL) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) { + if ((hTask == nullptr) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) { rflags = (uint32_t)TtStatusErrorParameter; } else { rflags = (uint32_t)TtStatusError; @@ -323,7 +327,7 @@ uint32_t tt_thread_flags_set(ThreadId thread_id, uint32_t flags) { (void)xTaskNotifyIndexedFromISR(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits, &yield); (void)xTaskNotifyAndQueryIndexedFromISR( - hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, NULL + hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, nullptr ); portYIELD_FROM_ISR(yield); @@ -336,7 +340,7 @@ uint32_t tt_thread_flags_set(ThreadId thread_id, uint32_t flags) { return (rflags); } -uint32_t tt_thread_flags_clear(uint32_t flags) { +uint32_t thread_flags_clear(uint32_t flags) { TaskHandle_t hTask; uint32_t rflags, cflags; @@ -365,7 +369,7 @@ uint32_t tt_thread_flags_clear(uint32_t flags) { return (rflags); } -uint32_t tt_thread_flags_get(void) { +uint32_t thread_flags_get() { TaskHandle_t hTask; uint32_t rflags; @@ -383,7 +387,7 @@ uint32_t tt_thread_flags_get(void) { return (rflags); } -uint32_t tt_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { +uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { uint32_t rflags, nval; uint32_t clear; TickType_t t0, td, tout; @@ -453,12 +457,12 @@ uint32_t tt_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout return (rflags); } -const char* tt_thread_get_name(ThreadId thread_id) { - TaskHandle_t hTask = (TaskHandle_t)thread_id; +const char* thread_get_name(ThreadId thread_id) { + auto hTask = (TaskHandle_t)thread_id; const char* name; - if (TT_IS_IRQ_MODE() || (hTask == NULL)) { - name = NULL; + if (TT_IS_IRQ_MODE() || (hTask == nullptr)) { + name = nullptr; } else { name = pcTaskGetName(hTask); } @@ -466,12 +470,12 @@ const char* tt_thread_get_name(ThreadId thread_id) { return (name); } -const char* tt_thread_get_appid(ThreadId thread_id) { - TaskHandle_t hTask = (TaskHandle_t)thread_id; +const char* thread_get_appid(ThreadId thread_id) { + auto hTask = (TaskHandle_t)thread_id; const char* appid = "system"; - if (!TT_IS_IRQ_MODE() && (hTask != NULL)) { - Thread* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); + if (!TT_IS_IRQ_MODE() && (hTask != nullptr)) { + auto* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); if (thread) { appid = thread->appid; } @@ -480,11 +484,11 @@ const char* tt_thread_get_appid(ThreadId thread_id) { return (appid); } -uint32_t tt_thread_get_stack_space(ThreadId thread_id) { - TaskHandle_t hTask = (TaskHandle_t)thread_id; +uint32_t thread_get_stack_space(ThreadId thread_id) { + auto hTask = (TaskHandle_t)thread_id; uint32_t sz; - if (TT_IS_IRQ_MODE() || (hTask == NULL)) { + if (TT_IS_IRQ_MODE() || (hTask == nullptr)) { sz = 0U; } else { sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t)); @@ -493,13 +497,13 @@ uint32_t tt_thread_get_stack_space(ThreadId thread_id) { return (sz); } -void tt_thread_suspend(ThreadId thread_id) { - TaskHandle_t hTask = (TaskHandle_t)thread_id; +void thread_suspend(ThreadId thread_id) { + auto hTask = (TaskHandle_t)thread_id; vTaskSuspend(hTask); } -void tt_thread_resume(ThreadId thread_id) { - TaskHandle_t hTask = (TaskHandle_t)thread_id; +void thread_resume(ThreadId thread_id) { + auto hTask = (TaskHandle_t)thread_id; if (TT_IS_IRQ_MODE()) { xTaskResumeFromISR(hTask); } else { @@ -507,7 +511,9 @@ void tt_thread_resume(ThreadId thread_id) { } } -bool tt_thread_is_suspended(ThreadId thread_id) { - TaskHandle_t hTask = (TaskHandle_t)thread_id; +bool thread_is_suspended(ThreadId thread_id) { + auto hTask = (TaskHandle_t)thread_id; return eTaskGetState(hTask) == eSuspended; } + +} // namespace diff --git a/tactility-core/src/thread.h b/TactilityCore/Source/Thread.h similarity index 72% rename from tactility-core/src/thread.h rename to TactilityCore/Source/Thread.h index 707c6fe2..bf135d8c 100644 --- a/tactility-core/src/thread.h +++ b/TactilityCore/Source/Thread.h @@ -1,14 +1,12 @@ #pragma once -#include "core_defines.h" -#include "core_types.h" +#include "CoreDefines.h" +#include "CoreTypes.h" -#include -#include +#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { /** ThreadState */ typedef enum { @@ -61,7 +59,7 @@ typedef void (*ThreadStateCallback)(ThreadState state, void* context); * * @return Thread instance */ -Thread* tt_thread_alloc(); +Thread* thread_alloc(); /** Allocate Thread, shortcut version * @@ -71,7 +69,7 @@ Thread* tt_thread_alloc(); * @param context * @return Thread* */ -Thread* tt_thread_alloc_ex( +Thread* thread_alloc_ex( const char* name, uint32_t stack_size, ThreadCallback callback, @@ -84,14 +82,14 @@ Thread* tt_thread_alloc_ex( * * @param thread Thread instance */ -void tt_thread_free(Thread* thread); +void thread_free(Thread* thread); /** Set Thread name * * @param thread Thread instance * @param name string */ -void tt_thread_set_name(Thread* thread, const char* name); +void thread_set_name(Thread* thread, const char* name); /** * @brief Set Thread appid @@ -101,68 +99,68 @@ void tt_thread_set_name(Thread* thread, const char* name); * @param thread * @param appid */ -void tt_thread_set_appid(Thread* thread, const char* appid); +void thread_set_appid(Thread* 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 tt_thread_mark_as_static(Thread* thread); +void thread_mark_as_static(Thread* thread); /** Set Thread stack size * * @param thread Thread instance * @param stack_size stack size in bytes */ -void tt_thread_set_stack_size(Thread* thread, size_t stack_size); +void thread_set_stack_size(Thread* thread, size_t stack_size); /** Set Thread callback * * @param thread Thread instance * @param callback ThreadCallback, called upon thread run */ -void tt_thread_set_callback(Thread* thread, ThreadCallback callback); +void thread_set_callback(Thread* thread, ThreadCallback callback); /** Set Thread context * * @param thread Thread instance * @param context pointer to context for thread callback */ -void tt_thread_set_context(Thread* thread, void* context); +void thread_set_context(Thread* thread, void* context); /** Set Thread priority * * @param thread Thread instance * @param priority ThreadPriority value */ -void tt_thread_set_priority(Thread* thread, ThreadPriority priority); +void thread_set_priority(Thread* thread, ThreadPriority priority); /** Set current thread priority * * @param priority ThreadPriority value */ -void tt_thread_set_current_priority(ThreadPriority priority); +void thread_set_current_priority(ThreadPriority priority); /** Get current thread priority * * @return ThreadPriority value */ -ThreadPriority tt_thread_get_current_priority(); +ThreadPriority thread_get_current_priority(); /** Set Thread state change callback * * @param thread Thread instance * @param callback state change callback */ -void tt_thread_set_state_callback(Thread* thread, ThreadStateCallback callback); +void thread_set_state_callback(Thread* thread, ThreadStateCallback callback); /** Set Thread state change context * * @param thread Thread instance * @param context pointer to context */ -void tt_thread_set_state_context(Thread* thread, void* context); +void thread_set_state_context(Thread* thread, void* context); /** Get Thread state * @@ -170,13 +168,13 @@ void tt_thread_set_state_context(Thread* thread, void* context); * * @return thread state from ThreadState */ -ThreadState tt_thread_get_state(Thread* thread); +ThreadState thread_get_state(Thread* thread); /** Start Thread * * @param thread Thread instance */ -void tt_thread_start(Thread* thread); +void thread_start(Thread* thread); /** Join Thread * @@ -187,7 +185,7 @@ void tt_thread_start(Thread* thread); * * @return bool */ -bool tt_thread_join(Thread* thread); +bool thread_join(Thread* thread); /** Get FreeRTOS ThreadId for Thread instance * @@ -195,7 +193,7 @@ bool tt_thread_join(Thread* thread); * * @return ThreadId or NULL */ -ThreadId tt_thread_get_id(Thread* thread); +ThreadId thread_get_id(Thread* thread); /** Get thread return code * @@ -203,7 +201,7 @@ ThreadId tt_thread_get_id(Thread* thread); * * @return return code */ -int32_t tt_thread_get_return_code(Thread* thread); +int32_t thread_get_return_code(Thread* thread); /** Thread related methods that doesn't involve Thread directly */ @@ -213,24 +211,24 @@ int32_t tt_thread_get_return_code(Thread* thread); * * @return ThreadId or NULL */ -ThreadId tt_thread_get_current_id(); +ThreadId thread_get_current_id(); /** Get Thread instance for current thread * * @return pointer to Thread or NULL if this thread doesn't belongs to Tactility */ -Thread* tt_thread_get_current(); +Thread* thread_get_current(); /** Return control to scheduler */ -void tt_thread_yield(); +void thread_yield(); -uint32_t tt_thread_flags_set(ThreadId thread_id, uint32_t flags); +uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags); -uint32_t tt_thread_flags_clear(uint32_t flags); +uint32_t thread_flags_clear(uint32_t flags); -uint32_t tt_thread_flags_get(void); +uint32_t thread_flags_get(); -uint32_t tt_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout); +uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout); /** * @brief Get thread name @@ -238,7 +236,7 @@ uint32_t tt_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout * @param thread_id * @return const char* name or NULL */ -const char* tt_thread_get_name(ThreadId thread_id); +const char* thread_get_name(ThreadId thread_id); /** * @brief Get thread appid @@ -246,7 +244,7 @@ const char* tt_thread_get_name(ThreadId thread_id); * @param thread_id * @return const char* appid */ -const char* tt_thread_get_appid(ThreadId thread_id); +const char* thread_get_appid(ThreadId thread_id); /** * @brief Get thread stack watermark @@ -254,34 +252,32 @@ const char* tt_thread_get_appid(ThreadId thread_id); * @param thread_id * @return uint32_t */ -uint32_t tt_thread_get_stack_space(ThreadId thread_id); +uint32_t thread_get_stack_space(ThreadId thread_id); /** Suspend thread * * @param thread_id thread id */ -void tt_thread_suspend(ThreadId thread_id); +void thread_suspend(ThreadId thread_id); /** Resume thread * * @param thread_id thread id */ -void tt_thread_resume(ThreadId thread_id); +void thread_resume(ThreadId thread_id); /** Get thread suspended state * * @param thread_id thread id * @return true if thread is suspended */ -bool tt_thread_is_suspended(ThreadId thread_id); +bool thread_is_suspended(ThreadId thread_id); /** Check if the thread was created with static memory * * @param thread_id thread id * @return true if thread memory is static */ -bool tt_thread_mark_is_static(ThreadId thread_id); +bool thread_mark_is_static(ThreadId thread_id); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility-core/src/timer.c b/TactilityCore/Source/Timer.cpp similarity index 53% rename from tactility-core/src/timer.c rename to TactilityCore/Source/Timer.cpp index afaf5a71..8199502c 100644 --- a/tactility-core/src/timer.c +++ b/TactilityCore/Source/Timer.cpp @@ -1,6 +1,7 @@ -#include "timer.h" -#include "check.h" -#include "kernel.h" +#include "Timer.h" +#include "Check.h" +#include "Kernel.h" +#include #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -10,25 +11,27 @@ #include "timers.h" #endif +namespace tt { + typedef struct { TimerCallback func; void* context; } TimerCallback_t; static void timer_callback(TimerHandle_t hTimer) { - TimerCallback_t* callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); + auto* callback = static_cast(pvTimerGetTimerID(hTimer)); - if (callb != NULL) { - callb->func(callb->context); + if (callback != nullptr) { + callback->func(callback->context); } } -Timer* tt_timer_alloc(TimerCallback func, TimerType type, void* context) { - tt_assert((tt_kernel_is_irq() == 0U) && (func != NULL)); - TimerCallback_t* callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t)); +Timer* timer_alloc(TimerCallback func, TimerType type, void* context) { + tt_assert((kernel_is_irq() == 0U) && (func != nullptr)); + auto* callback = static_cast(malloc(sizeof(TimerCallback_t))); - callb->func = func; - callb->context = context; + callback->func = func; + callback->context = context; UBaseType_t reload; if (type == TimerTypeOnce) { @@ -40,34 +43,34 @@ Timer* tt_timer_alloc(TimerCallback func, TimerType type, void* context) { // TimerCallback function is always provided as a callback and is used to call application // specified function with its context both stored in structure callb. // TODO: should we use pointer to function or function directly as-is? - TimerHandle_t hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, timer_callback); - tt_check(hTimer); + TimerHandle_t hTimer = xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, callback, timer_callback); + tt_assert(hTimer); /* Return timer ID */ return (Timer*)hTimer; } -void tt_timer_free(Timer* instance) { - tt_assert(!tt_kernel_is_irq()); +void timer_free(Timer* instance) { + tt_assert(!kernel_is_irq()); tt_assert(instance); - TimerHandle_t hTimer = (TimerHandle_t)instance; - TimerCallback_t* callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); + auto hTimer = static_cast(instance); + auto* callback = static_cast(pvTimerGetTimerID(hTimer)); tt_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); - while (tt_timer_is_running(instance)) tt_delay_tick(2); + while (timer_is_running(instance)) delay_tick(2); /* Return allocated memory to dynamic pool */ - free(callb); + free(callback); } -TtStatus tt_timer_start(Timer* instance, uint32_t ticks) { - tt_assert(!tt_kernel_is_irq()); +TtStatus timer_start(Timer* instance, uint32_t ticks) { + tt_assert(!kernel_is_irq()); tt_assert(instance); tt_assert(ticks < portMAX_DELAY); - TimerHandle_t hTimer = (TimerHandle_t)instance; + auto hTimer = static_cast(instance); TtStatus stat; if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS) { @@ -80,12 +83,12 @@ TtStatus tt_timer_start(Timer* instance, uint32_t ticks) { return (stat); } -TtStatus tt_timer_restart(Timer* instance, uint32_t ticks) { - tt_assert(!tt_kernel_is_irq()); +TtStatus timer_restart(Timer* instance, uint32_t ticks) { + tt_assert(!kernel_is_irq()); tt_assert(instance); tt_assert(ticks < portMAX_DELAY); - TimerHandle_t hTimer = (TimerHandle_t)instance; + auto hTimer = static_cast(instance); TtStatus stat; if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS && @@ -99,57 +102,59 @@ TtStatus tt_timer_restart(Timer* instance, uint32_t ticks) { return (stat); } -TtStatus tt_timer_stop(Timer* instance) { - tt_assert(!tt_kernel_is_irq()); +TtStatus timer_stop(Timer* instance) { + tt_assert(!kernel_is_irq()); tt_assert(instance); - TimerHandle_t hTimer = (TimerHandle_t)instance; + auto hTimer = static_cast(instance); tt_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS); return TtStatusOk; } -uint32_t tt_timer_is_running(Timer* instance) { - tt_assert(!tt_kernel_is_irq()); +uint32_t timer_is_running(Timer* instance) { + tt_assert(!kernel_is_irq()); tt_assert(instance); - TimerHandle_t hTimer = (TimerHandle_t)instance; + auto hTimer = static_cast(instance); /* Return 0: not running, 1: running */ return (uint32_t)xTimerIsTimerActive(hTimer); } -uint32_t tt_timer_get_expire_time(Timer* instance) { - tt_assert(!tt_kernel_is_irq()); +uint32_t timer_get_expire_time(Timer* instance) { + tt_assert(!kernel_is_irq()); tt_assert(instance); - TimerHandle_t hTimer = (TimerHandle_t)instance; + auto hTimer = static_cast(instance); return (uint32_t)xTimerGetExpiryTime(hTimer); } -void tt_timer_pending_callback(TimerPendigCallback callback, void* context, uint32_t arg) { +void timer_pending_callback(TimerPendigCallback callback, void* context, uint32_t arg) { BaseType_t ret = pdFAIL; - if (tt_kernel_is_irq()) { - ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL); + if (kernel_is_irq()) { + ret = xTimerPendFunctionCallFromISR(callback, context, arg, nullptr); } else { ret = xTimerPendFunctionCall(callback, context, arg, TtWaitForever); } - tt_check(ret == pdPASS); + tt_assert(ret == pdPASS); } -void tt_timer_set_thread_priority(TimerThreadPriority priority) { - tt_assert(!tt_kernel_is_irq()); +void timer_set_thread_priority(TimerThreadPriority priority) { + tt_assert(!kernel_is_irq()); TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); - tt_check(task_handle); // Don't call this method before timer task start + tt_assert(task_handle); // Don't call this method before timer task start if (priority == TimerThreadPriorityNormal) { vTaskPrioritySet(task_handle, configTIMER_TASK_PRIORITY); } else if (priority == TimerThreadPriorityElevated) { vTaskPrioritySet(task_handle, configMAX_PRIORITIES - 1); } else { - tt_crash(); + tt_crash("Unsupported timer priority"); } -} \ No newline at end of file +} + +} // namespace diff --git a/tactility-core/src/timer.h b/TactilityCore/Source/Timer.h similarity index 77% rename from tactility-core/src/timer.h rename to TactilityCore/Source/Timer.h index 9e13e387..60f5aa4d 100644 --- a/tactility-core/src/timer.h +++ b/TactilityCore/Source/Timer.h @@ -1,10 +1,8 @@ #pragma once -#include "core_types.h" +#include "CoreTypes.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt { typedef void (*TimerCallback)(void* context); @@ -23,13 +21,13 @@ typedef void Timer; * * @return The pointer to Timer instance */ -Timer* tt_timer_alloc(TimerCallback func, TimerType type, void* context); +Timer* timer_alloc(TimerCallback func, TimerType type, void* context); /** Free timer * * @param instance The pointer to Timer instance */ -void tt_timer_free(Timer* instance); +void timer_free(Timer* instance); /** Start timer * @@ -41,7 +39,7 @@ void tt_timer_free(Timer* instance); * * @return The status. */ -TtStatus tt_timer_start(Timer* instance, uint32_t ticks); +TtStatus timer_start(Timer* instance, uint32_t ticks); /** Restart timer with previous timeout value * @@ -53,7 +51,7 @@ TtStatus tt_timer_start(Timer* instance, uint32_t ticks); * * @return The status. */ -TtStatus tt_timer_restart(Timer* instance, uint32_t ticks); +TtStatus timer_restart(Timer* instance, uint32_t ticks); /** Stop timer * @@ -64,7 +62,7 @@ TtStatus tt_timer_restart(Timer* instance, uint32_t ticks); * * @return The status. */ -TtStatus tt_timer_stop(Timer* instance); +TtStatus timer_stop(Timer* instance); /** Is timer running * @@ -76,7 +74,7 @@ TtStatus tt_timer_stop(Timer* instance); * * @return 0: not running, 1: running */ -uint32_t tt_timer_is_running(Timer* instance); +uint32_t timer_is_running(Timer* instance); /** Get timer expire time * @@ -84,11 +82,11 @@ uint32_t tt_timer_is_running(Timer* instance); * * @return expire tick */ -uint32_t tt_timer_get_expire_time(Timer* instance); +uint32_t timer_get_expire_time(Timer* instance); typedef void (*TimerPendigCallback)(void* context, uint32_t arg); -void tt_timer_pending_callback(TimerPendigCallback callback, void* context, uint32_t arg); +void timer_pending_callback(TimerPendigCallback callback, void* context, uint32_t arg); typedef enum { TimerThreadPriorityNormal, /**< Lower then other threads */ @@ -99,8 +97,6 @@ typedef enum { * * @param[in] priority The priority */ -void tt_timer_set_thread_priority(TimerThreadPriority priority); +void timer_set_thread_priority(TimerThreadPriority priority); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/TactilityHeadless/CMakeLists.txt b/TactilityHeadless/CMakeLists.txt new file mode 100644 index 00000000..644f9cfe --- /dev/null +++ b/TactilityHeadless/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.16) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (DEFINED ENV{ESP_IDF_VERSION}) + file(GLOB_RECURSE SOURCE_FILES Source/*.c*) + + idf_component_register( + SRCS ${SOURCE_FILES} + INCLUDE_DIRS "Source/" + PRIV_INCLUDE_DIRS "Private/" + REQUIRES esp_wifi nvs_flash spiffs + ) + + set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/assets") + spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT) + + set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/config") + spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT) + + target_link_libraries(${COMPONENT_LIB} + PUBLIC TactilityCore + ) + + add_definitions(-DESP_PLATFORM) +else() + file(GLOB_RECURSE SOURCES "Source/*.c*") + file(GLOB_RECURSE HEADERS "Source/*.h*") + + add_library(TactilityHeadless OBJECT) + target_sources(TactilityHeadless + PRIVATE ${SOURCES} + PUBLIC ${HEADERS} + ) + + include_directories( + Private/ + ) + + target_include_directories(TactilityHeadless + PUBLIC Source/ + ) + + add_definitions(-D_Nullable=) + add_definitions(-D_Nonnull=) + target_link_libraries(TactilityHeadless + PUBLIC TactilityCore + PUBLIC freertos_kernel + ) +endif() diff --git a/TactilityHeadless/Private/EspInit.h b/TactilityHeadless/Private/EspInit.h new file mode 100644 index 00000000..1f8a26da --- /dev/null +++ b/TactilityHeadless/Private/EspInit.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef ESP_TARGET + +namespace tt { + +void esp_init(); + +} // namespace + +#endif // ESP_TARGET \ No newline at end of file diff --git a/TactilityHeadless/Private/EspPartitions_i.h b/TactilityHeadless/Private/EspPartitions_i.h new file mode 100644 index 00000000..5cd5238d --- /dev/null +++ b/TactilityHeadless/Private/EspPartitions_i.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef ESP_TARGET + +#include "esp_err.h" + +namespace tt { + +esp_err_t esp_partitions_init(); + +} // namespace + +#endif // ESP_TARGET \ No newline at end of file diff --git a/TactilityHeadless/Private/Hal/Hal_i.h b/TactilityHeadless/Private/Hal/Hal_i.h new file mode 100644 index 00000000..19791151 --- /dev/null +++ b/TactilityHeadless/Private/Hal/Hal_i.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Hal/Configuration.h" + +namespace tt::hal { + +void init(const Configuration* configuration); + +} // namespace diff --git a/tactility-headless/src/assets.h b/TactilityHeadless/Source/Assets.h similarity index 100% rename from tactility-headless/src/assets.h rename to TactilityHeadless/Source/Assets.h diff --git a/tactility-headless/src/esp_init.c b/TactilityHeadless/Source/EspInit.cpp similarity index 64% rename from tactility-headless/src/esp_init.c rename to TactilityHeadless/Source/EspInit.cpp index 0a15f0a3..61e1368b 100644 --- a/tactility-headless/src/esp_init.c +++ b/TactilityHeadless/Source/EspInit.cpp @@ -1,18 +1,19 @@ -#include "tactility_core.h" +#include "TactilityCore.h" #ifdef ESP_TARGET -#include "esp_partitions.h" -#include "services/wifi/wifi_settings.h" +#include "EspPartitions_i.h" #include "esp_event.h" #include "esp_netif.h" #include "nvs_flash.h" +namespace tt { + #define TAG "tactility" // Initialize NVS -static void tt_esp_nvs_init() { +static void esp_nvs_init() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { TT_LOG_I(TAG, "nvs erasing"); @@ -23,18 +24,17 @@ static void tt_esp_nvs_init() { TT_LOG_I(TAG, "nvs initialized"); } -static void tt_esp_network_init() { +static void esp_network_init() { ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); } -void tt_esp_init() { - tt_esp_nvs_init(); - tt_esp_partitions_init(); - - tt_esp_network_init(); - - tt_wifi_settings_init(); +void esp_init() { + esp_nvs_init(); + esp_partitions_init(); + esp_network_init(); } -#endif \ No newline at end of file +} // namespace + +#endif diff --git a/tactility-headless/src/esp_partitions.c b/TactilityHeadless/Source/EspPartitions.cpp similarity index 93% rename from tactility-headless/src/esp_partitions.c rename to TactilityHeadless/Source/EspPartitions.cpp index 017d936f..5a70d5a2 100644 --- a/tactility-headless/src/esp_partitions.c +++ b/TactilityHeadless/Source/EspPartitions.cpp @@ -1,10 +1,14 @@ #ifdef ESP_TARGET -#include "esp_partitions.h" +#include "EspPartitions.h" +#include "EspPartitions_i.h" +#include "Log.h" + #include "esp_spiffs.h" -#include "log.h" #include "nvs_flash.h" +namespace tt { + static const char* TAG = "filesystem"; static esp_err_t nvs_flash_init_safely() { @@ -39,7 +43,7 @@ static esp_err_t spiffs_init(esp_vfs_spiffs_conf_t* conf) { return ESP_OK; } -esp_err_t tt_esp_partitions_init() { +esp_err_t esp_partitions_init() { ESP_ERROR_CHECK(nvs_flash_init_safely()); esp_vfs_spiffs_conf_t assets_spiffs = { @@ -67,4 +71,6 @@ esp_err_t tt_esp_partitions_init() { return ESP_OK; } +} // namespace + #endif // ESP_TARGET diff --git a/tactility-headless/src/esp_partitions.h b/TactilityHeadless/Source/EspPartitions.h similarity index 58% rename from tactility-headless/src/esp_partitions.h rename to TactilityHeadless/Source/EspPartitions.h index d3b259dd..6ef8562d 100644 --- a/tactility-headless/src/esp_partitions.h +++ b/TactilityHeadless/Source/EspPartitions.h @@ -2,11 +2,11 @@ #ifdef ESP_TARGET -#include "esp_err.h" +namespace tt { #define MOUNT_POINT_ASSETS "/assets" #define MOUNT_POINT_CONFIG "/config" -esp_err_t tt_esp_partitions_init(); +} // namespace #endif // ESP_TARGET \ No newline at end of file diff --git a/tactility-headless/src/hardware_config.h b/TactilityHeadless/Source/Hal/Configuration.h similarity index 86% rename from tactility-headless/src/hardware_config.h rename to TactilityHeadless/Source/Hal/Configuration.h index 3dc54296..4fc44875 100644 --- a/tactility-headless/src/hardware_config.h +++ b/TactilityHeadless/Source/Hal/Configuration.h @@ -1,8 +1,10 @@ #pragma once -#include "tactility_core.h" -#include "sdcard.h" -#include "power.h" +#include "TactilityCore.h" +#include "Sdcard.h" +#include "Power.h" + +namespace tt::hal { typedef bool (*Bootstrap)(); typedef bool (*InitGraphics)(); @@ -35,10 +37,12 @@ typedef struct { /** * An optional SD card interface. */ - const SdCard* _Nullable sdcard; + const sdcard::SdCard* _Nullable sdcard; /** * An optional power interface for battery or other power delivery. */ const Power* _Nullable power; -} HardwareConfig; +} Configuration; + +} // namespace diff --git a/TactilityHeadless/Source/Hal/Hal.cpp b/TactilityHeadless/Source/Hal/Hal.cpp new file mode 100644 index 00000000..e9f42f5a --- /dev/null +++ b/TactilityHeadless/Source/Hal/Hal.cpp @@ -0,0 +1,22 @@ +#include "Hal/Hal_i.h" + +#define TAG "hardware" + +namespace tt::hal { + +void init(const Configuration* configuration) { + if (configuration->bootstrap != nullptr) { + TT_LOG_I(TAG, "Bootstrapping"); + tt_check(configuration->bootstrap(), "bootstrap failed"); + } + + if (configuration->sdcard != nullptr) { + TT_LOG_I(TAG, "Mounting sdcard"); + sdcard::mount(configuration->sdcard); + } + + tt_check(configuration->init_graphics, "Graphics init not set"); + tt_check(configuration->init_graphics(), "Graphics init failed"); +} + +} // namespace diff --git a/tactility-headless/src/power.h b/TactilityHeadless/Source/Hal/Power.h similarity index 83% rename from tactility-headless/src/power.h rename to TactilityHeadless/Source/Hal/Power.h index 5fb9a383..6a687a45 100644 --- a/tactility-headless/src/power.h +++ b/TactilityHeadless/Source/Hal/Power.h @@ -1,11 +1,8 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif +#include -#include -#include +namespace tt::hal { typedef bool (*PowerIsCharging)(); typedef bool (*PowerIsChargingEnabled)(); @@ -21,6 +18,4 @@ typedef struct { PowerGetCurrent get_current; } Power; -#ifdef __cplusplus -} -#endif \ No newline at end of file +} // namespace tt diff --git a/TactilityHeadless/Source/Hal/Sdcard.cpp b/TactilityHeadless/Source/Hal/Sdcard.cpp new file mode 100644 index 00000000..35762173 --- /dev/null +++ b/TactilityHeadless/Source/Hal/Sdcard.cpp @@ -0,0 +1,85 @@ +#include "Sdcard.h" + +#include "Mutex.h" +#include "TactilityCore.h" + +namespace tt::hal::sdcard { + +#define TAG "sdcard" + +static Mutex mutex(MutexTypeRecursive); + +typedef struct { + const SdCard* sdcard; + void* context; +} MountData; + +static MountData data = { + .sdcard = nullptr, + .context = nullptr +}; + +static bool lock(uint32_t timeout_ticks) { + return mutex.acquire(timeout_ticks) == TtStatusOk; +} + +static void unlock() { + mutex.release(); +} + +bool mount(const SdCard* sdcard) { + TT_LOG_I(TAG, "Mounting"); + + if (data.sdcard != nullptr) { + TT_LOG_E(TAG, "Failed to mount: already mounted"); + return false; + } + + if (lock(100)) { + void* context = sdcard->mount(TT_SDCARD_MOUNT_POINT); + data = (MountData) { + .sdcard = sdcard, + .context = context + }; + unlock(); + return (data.context != nullptr); + } else { + TT_LOG_E(TAG, "Failed to lock"); + return false; + } +} + +State get_state() { + if (data.context == nullptr) { + return StateUnmounted; + } else if (data.sdcard->is_mounted(data.context)) { + return StateMounted; + } else { + return StateError; + } +} + +bool unmount(uint32_t timeout_ticks) { + TT_LOG_I(TAG, "Unmounting"); + bool result = false; + + if (lock(timeout_ticks)) { + if (data.sdcard != nullptr) { + data.sdcard->unmount(data.context); + data = (MountData) { + .sdcard = nullptr, + .context = nullptr + }; + result = true; + } else { + TT_LOG_E(TAG, "Can't unmount: nothing mounted"); + } + unlock(); + } else { + TT_LOG_E(TAG, "Failed to lock in %lu ticks", timeout_ticks); + } + + return result; +} + +} // namespace diff --git a/TactilityHeadless/Source/Hal/Sdcard.h b/TactilityHeadless/Source/Hal/Sdcard.h new file mode 100644 index 00000000..d3f69e49 --- /dev/null +++ b/TactilityHeadless/Source/Hal/Sdcard.h @@ -0,0 +1,35 @@ +#pragma once + +#include "TactilityCore.h" + +namespace tt::hal::sdcard { + +#define TT_SDCARD_MOUNT_POINT "/sdcard" + +typedef void* (*Mount)(const char* mount_path); +typedef void (*Unmount)(void* context); +typedef bool (*IsMounted)(void* context); + +typedef enum { + StateMounted, + StateUnmounted, + StateError, +} State; + +typedef enum { + MountBehaviourAtBoot, /** Only mount at boot */ + MountBehaviourAnytime /** Mount/dismount any time */ +} MountBehaviour; + +typedef struct { + Mount mount; + Unmount unmount; + IsMounted is_mounted; + MountBehaviour mount_behaviour; +} SdCard; + +bool mount(const SdCard* sdcard); +State get_state(); +bool unmount(uint32_t timeout_ticks); + +} // namespace diff --git a/TactilityHeadless/Source/Preferences.h b/TactilityHeadless/Source/Preferences.h new file mode 100644 index 00000000..bbc45473 --- /dev/null +++ b/TactilityHeadless/Source/Preferences.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace tt { + +class Preferences { +private: + const char* namespace_; + +public: + explicit Preferences(const char* namespace_) { + this->namespace_ = namespace_; + } + + bool hasBool(const std::string& key) const; + bool hasInt32(const std::string& key) const; + bool hasString(const std::string& key) const; + + bool optBool(const std::string& key, bool& out) const; + bool optInt32(const std::string& key, int32_t& out) const; + bool optString(const std::string& key, std::string& out) const; + + void putBool(const std::string& key, bool value); + void putInt32(const std::string& key, int32_t value); + void putString(const std::string& key, const std::string& value); +}; + +} // namespace diff --git a/TactilityHeadless/Source/PreferencesEsp.cpp b/TactilityHeadless/Source/PreferencesEsp.cpp new file mode 100644 index 00000000..a92d4168 --- /dev/null +++ b/TactilityHeadless/Source/PreferencesEsp.cpp @@ -0,0 +1,103 @@ +#ifdef ESP_PLATFORM + +#include "nvs_flash.h" +#include "Preferences.h" +#include "TactilityCore.h" + +#define TAG "preferences" + +namespace tt { + +bool Preferences::optBool(const std::string& key, bool& out) const { + nvs_handle_t handle; + if (nvs_open(namespace_, NVS_READWRITE, &handle) != ESP_OK) { + return false; + } else { + uint8_t out_number; + bool success = nvs_get_u8(handle, key.c_str(), &out_number) == ESP_OK; + nvs_close(handle); + if (success) { + out = (bool)out_number; + } + return success; + } +} + +bool Preferences::optInt32(const std::string& key, int32_t& out) const { + nvs_handle_t handle; + if (nvs_open(namespace_, NVS_READWRITE, &handle) != ESP_OK) { + return false; + } else { + bool success = nvs_get_i32(handle, key.c_str(), &out) == ESP_OK; + nvs_close(handle); + return success; + } +} + +bool Preferences::optString(const std::string& key, std::string& out) const { + nvs_handle_t handle; + if (nvs_open(namespace_, NVS_READWRITE, &handle) != ESP_OK) { + return false; + } else { + size_t out_size = 256; + char* out_data = static_cast(malloc(out_size)); + bool success = nvs_get_str(handle, key.c_str(), out_data, &out_size) == ESP_OK; + nvs_close(handle); + out = out_data; + free(out_data); + return success; + } +} + +bool Preferences::hasBool(const std::string& key) const { + bool temp; + return optBool(key, temp); +} + +bool Preferences::hasInt32(const std::string& key) const { + int32_t temp; + return optInt32(key, temp); +} + +bool Preferences::hasString(const std::string& key) const { + std::string temp; + return optString(key, temp); +} + +void Preferences::putBool(const std::string& key, bool value) { + nvs_handle_t handle; + if (nvs_open(namespace_, NVS_READWRITE, &handle) == ESP_OK) { + if (nvs_set_u8(handle, key.c_str(), (uint8_t)value) != ESP_OK) { + TT_LOG_E(TAG, "failed to write %s:%s", namespace_, key.c_str()); + } + nvs_close(handle); + } else { + TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace_); + } +} + +void Preferences::putInt32(const std::string& key, int32_t value) { + nvs_handle_t handle; + if (nvs_open(namespace_, NVS_READWRITE, &handle) == ESP_OK) { + if (nvs_set_i32(handle, key.c_str(), value) != ESP_OK) { + TT_LOG_E(TAG, "failed to write %s:%s", namespace_, key.c_str()); + } + nvs_close(handle); + } else { + TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace_); + } +} + +void Preferences::putString(const std::string& key, const std::string& text) { + nvs_handle_t handle; + if (nvs_open(namespace_, NVS_READWRITE, &handle) == ESP_OK) { + nvs_set_str(handle, key.c_str(), text.c_str()); + nvs_close(handle); + } else { + TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace_); + } +} + +} // namespace + +#endif \ No newline at end of file diff --git a/TactilityHeadless/Source/PreferencesMock.cpp b/TactilityHeadless/Source/PreferencesMock.cpp new file mode 100644 index 00000000..9334250e --- /dev/null +++ b/TactilityHeadless/Source/PreferencesMock.cpp @@ -0,0 +1,70 @@ +#ifndef ESP_PLATFOM + +#include "Bundle.h" +#include "Preferences.h" +#include "TactilityCore.h" + +namespace tt { + +static Bundle preferences; + +/** + * Creates a string that is effectively "namespace:key" so we can create a single map (bundle) + * to store all the key/value pairs. + * + * @param[in] namespace + * @param[in] key + * @param[out] out + */ +std::string get_bundle_key(const std::string& namespace_, const std::string& key) { + return namespace_ + ':' + key; +} + +bool Preferences::hasBool(const std::string& key) const { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.hasBool(bundle_key); +} + +bool Preferences::hasInt32(const std::string& key) const { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.hasInt32(bundle_key); +} + +bool Preferences::hasString(const std::string& key) const { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.hasString(bundle_key); +} + +bool Preferences::optBool(const std::string& key, bool& out) const { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.optBool(bundle_key, out); +} + +bool Preferences::optInt32(const std::string& key, int32_t& out) const { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.optInt32(bundle_key, out); +} + +bool Preferences::optString(const std::string& key, std::string& out) const { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.optString(bundle_key, out); +} + +void Preferences::putBool(const std::string& key, bool value) { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.putBool(bundle_key, value); +} + +void Preferences::putInt32(const std::string& key, int32_t value) { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.putInt32(bundle_key, value); +} + +void Preferences::putString(const std::string& key, const std::string& value) { + std::string bundle_key = get_bundle_key(namespace_, key); + return preferences.putString(bundle_key, value); +} + +#endif + +} // namespace diff --git a/TactilityHeadless/Source/Service.cpp b/TactilityHeadless/Source/Service.cpp new file mode 100644 index 00000000..24fcc5e4 --- /dev/null +++ b/TactilityHeadless/Source/Service.cpp @@ -0,0 +1,23 @@ +#include "Service.h" +#include "ServiceManifest.h" + +namespace tt { + +Service::Service(const ServiceManifest& manifest) : manifest(manifest) {} + +const ServiceManifest& Service::getManifest() const { return manifest; } + +void* Service::getData() const { + mutex.acquire(TtWaitForever); + void* data_copy = data; + mutex.release(); + return data_copy; +} + +void Service::setData(void* newData) { + mutex.acquire(TtWaitForever); + data = newData; + mutex.release(); +} + +} // namespace diff --git a/TactilityHeadless/Source/Service.h b/TactilityHeadless/Source/Service.h new file mode 100644 index 00000000..ba538ba1 --- /dev/null +++ b/TactilityHeadless/Source/Service.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Mutex.h" +#include "ServiceManifest.h" + +namespace tt { + +class Service { +private: + Mutex mutex = Mutex(MutexTypeNormal); + const ServiceManifest& manifest; + void* data = nullptr; + +public: + Service(const ServiceManifest& manifest); + + [[nodiscard]] const ServiceManifest& getManifest() const; + [[nodiscard]] void* getData() const; + void setData(void* newData); +}; + +} // namespace diff --git a/TactilityHeadless/Source/ServiceManifest.h b/TactilityHeadless/Source/ServiceManifest.h new file mode 100644 index 00000000..135ada17 --- /dev/null +++ b/TactilityHeadless/Source/ServiceManifest.h @@ -0,0 +1,31 @@ +#pragma once + +#include "TactilityCore.h" +#include + +namespace tt { + +class Service; + +typedef void (*ServiceOnStart)(Service& service); +typedef void (*ServiceOnStop)(Service& service); + +typedef struct ServiceManifest { + /** + * The identifier by which the app is launched by the system and other apps. + */ + std::string id {}; + + /** + * Non-blocking method to call when service is started. + */ + const ServiceOnStart on_start = nullptr; + + /** + * Non-blocking method to call when service is stopped. + */ + const ServiceOnStop on_stop = nullptr; + +} ServiceManifest; + +} // namespace diff --git a/TactilityHeadless/Source/ServiceRegistry.cpp b/TactilityHeadless/Source/ServiceRegistry.cpp new file mode 100644 index 00000000..e8a7301e --- /dev/null +++ b/TactilityHeadless/Source/ServiceRegistry.cpp @@ -0,0 +1,99 @@ +#include "ServiceRegistry.h" + +#include "Mutex.h" +#include "Service.h" +#include "ServiceManifest.h" +#include "TactilityCore.h" +#include +#include + +namespace tt { + +#define TAG "service_registry" + +typedef std::unordered_map ServiceManifestMap; +typedef std::unordered_map ServiceInstanceMap; + +static ServiceManifestMap service_manifest_map; +static ServiceInstanceMap service_instance_map; + +static Mutex manifest_mutex(MutexTypeNormal); +static Mutex instance_mutex(MutexTypeNormal); + +void service_registry_add(const ServiceManifest* manifest) { + TT_LOG_I(TAG, "adding %s", manifest->id.c_str()); + + manifest_mutex.acquire(TtWaitForever); + service_manifest_map[manifest->id] = manifest; + manifest_mutex.release(); +} + +const ServiceManifest* _Nullable service_registry_find_manifest_by_id(const std::string& id) { + manifest_mutex.acquire(TtWaitForever); + auto iterator = service_manifest_map.find(id); + _Nullable const ServiceManifest * manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr; + manifest_mutex.release(); + return manifest; +} + +static Service* _Nullable service_registry_find_instance_by_id(const std::string& id) { + manifest_mutex.acquire(TtWaitForever); + auto iterator = service_instance_map.find(id); + _Nullable Service* service = iterator != service_instance_map.end() ? iterator->second : nullptr; + manifest_mutex.release(); + return service; +} + +void service_registry_for_each_manifest(ServiceManifestCallback callback, void* _Nullable context) { + manifest_mutex.acquire(TtWaitForever); + for (auto& it : service_manifest_map) { + callback(it.second, context); + } + manifest_mutex.release(); +} + +// TODO: return proper error/status instead of BOOL +bool service_registry_start(const std::string& id) { + TT_LOG_I(TAG, "starting %s", id.c_str()); + const ServiceManifest* manifest = service_registry_find_manifest_by_id(id); + if (manifest == nullptr) { + TT_LOG_E(TAG, "manifest not found for service %s", id.c_str()); + return false; + } + + auto* service = new Service(*manifest); + manifest->on_start(*service); + + instance_mutex.acquire(TtWaitForever); + service_instance_map[manifest->id] = service; + instance_mutex.release(); + TT_LOG_I(TAG, "started %s", id.c_str()); + + return true; +} + +_Nullable Service* service_find(const std::string& service_id) { + return (Service*)service_registry_find_instance_by_id(service_id); +} + +bool service_registry_stop(const std::string& id) { + TT_LOG_I(TAG, "stopping %s", id.c_str()); + Service* service = service_registry_find_instance_by_id(id); + if (service == nullptr) { + TT_LOG_W(TAG, "service not running: %s", id.c_str()); + return false; + } + + service->getManifest().on_stop(*service); + delete service; + + instance_mutex.acquire(TtWaitForever); + service_instance_map.erase(id); + instance_mutex.release(); + + TT_LOG_I(TAG, "stopped %s", id.c_str()); + + return true; +} + +} // namespace diff --git a/TactilityHeadless/Source/ServiceRegistry.h b/TactilityHeadless/Source/ServiceRegistry.h new file mode 100644 index 00000000..30e34b84 --- /dev/null +++ b/TactilityHeadless/Source/ServiceRegistry.h @@ -0,0 +1,22 @@ +#pragma once + +#include "ServiceManifest.h" +#include "TactilityCore.h" + +namespace tt { + +typedef void (*ServiceManifestCallback)(const ServiceManifest*, void* context); + +void service_registry_init(); + +void service_registry_add(const ServiceManifest* manifest); +void service_registry_remove(const ServiceManifest* manifest); +_Nullable const ServiceManifest* service_registry_find_manifest_by_id(const std::string& id); +void service_registry_for_each_manifest(ServiceManifestCallback callback, void* _Nullable context); + +bool service_registry_start(const std::string& id); +bool service_registry_stop(const std::string& id); + +Service* _Nullable service_find(const std::string& id); + +} // namespace diff --git a/tactility-headless/src/services/sdcard/sdcard.c b/TactilityHeadless/Source/Services/Sdcard/Sdcard.cpp similarity index 60% rename from tactility-headless/src/services/sdcard/sdcard.c rename to TactilityHeadless/Source/Services/Sdcard/Sdcard.cpp index 9a364987..1f4f9527 100644 --- a/tactility-headless/src/services/sdcard/sdcard.c +++ b/TactilityHeadless/Source/Services/Sdcard/Sdcard.cpp @@ -1,41 +1,43 @@ -#include +#include -#include "mutex.h" -#include "service.h" -#include "tactility_core.h" -#include "tactility_headless.h" +#include "Mutex.h" +#include "Service.h" +#include "TactilityCore.h" +#include "TactilityHeadless.h" #define TAG "sdcard_service" +namespace tt::service::sdcard { + static int32_t sdcard_task(void* context); typedef struct { Mutex* mutex; Thread* thread; - SdcardState last_state; + hal::sdcard::State last_state; bool interrupted; } ServiceData; static ServiceData* service_data_alloc() { - ServiceData* data = malloc(sizeof(ServiceData)); + auto* data = static_cast(malloc(sizeof(ServiceData))); *data = (ServiceData) { .mutex = tt_mutex_alloc(MutexTypeNormal), - .thread = tt_thread_alloc_ex( + .thread = thread_alloc_ex( "sdcard", 3000, // Minimum is ~2800 @ ESP-IDF 5.1.2 when ejecting sdcard &sdcard_task, data ), - .last_state = -1, + .last_state = hal::sdcard::StateUnmounted, .interrupted = false }; - tt_thread_set_priority(data->thread, ThreadPriorityLow); + thread_set_priority(data->thread, ThreadPriorityLow); return data; } static void service_data_free(ServiceData* data) { tt_mutex_free(data->mutex); - tt_thread_free(data->thread); + thread_free(data->thread); } static void service_data_lock(ServiceData* data) { @@ -47,8 +49,7 @@ static void service_data_unlock(ServiceData* data) { } static int32_t sdcard_task(void* context) { - ServiceData* data = (ServiceData*)context; - + auto* data = (ServiceData*)context; bool interrupted = false; do { @@ -56,11 +57,11 @@ static int32_t sdcard_task(void* context) { interrupted = data->interrupted; - SdcardState new_state = tt_sdcard_get_state(); + hal::sdcard::State new_state = hal::sdcard::get_state(); - if (new_state == SdcardStateError) { + if (new_state == hal::sdcard::StateError) { TT_LOG_W(TAG, "Sdcard error - unmounting. Did you eject the card in an unsafe manner?"); - tt_sdcard_unmount(); + hal::sdcard::unmount(ms_to_ticks(1000)); } if (new_state != data->last_state) { @@ -68,37 +69,39 @@ static int32_t sdcard_task(void* context) { } service_data_unlock(data); - tt_delay_ms(2000); + delay_ms(2000); } while (!interrupted); return 0; } -static void on_start(Service service) { - if (tt_get_hardware_config()->sdcard != NULL) { +static void on_start(Service& service) { + if (get_hardware_config()->sdcard != nullptr) { ServiceData* data = service_data_alloc(); - tt_service_set_data(service, data); - tt_thread_start(data->thread); + service.setData(data); + thread_start(data->thread); } else { TT_LOG_I(TAG, "task not started due to config"); } } -static void on_stop(Service service) { - ServiceData* data = tt_service_get_data(service); - if (data != NULL) { +static void on_stop(Service& service) { + auto* data = static_cast(service.getData()); + if (data != nullptr) { service_data_lock(data); data->interrupted = true; service_data_unlock(data); - tt_thread_join(data->thread); + thread_join(data->thread); service_data_free(data); } } -const ServiceManifest sdcard_service = { +extern const ServiceManifest manifest = { .id = "sdcard", .on_start = &on_start, .on_stop = &on_stop }; + +} // namespace diff --git a/tactility-headless/src/services/wifi/wifi.h b/TactilityHeadless/Source/Services/Wifi/Wifi.h similarity index 83% rename from tactility-headless/src/services/wifi/wifi.h rename to TactilityHeadless/Source/Services/Wifi/Wifi.h index 78537b80..f1fbd377 100644 --- a/tactility-headless/src/services/wifi/wifi.h +++ b/TactilityHeadless/Source/Services/Wifi/Wifi.h @@ -1,20 +1,15 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - -#include "pubsub.h" -#include "wifi_globals.h" -#include "wifi_settings.h" -#include -#include +#include "Pubsub.h" +#include "WifiGlobals.h" +#include "WifiSettings.h" +#include #ifdef ESP_PLATFORM #include "esp_wifi.h" -#include "wifi_settings.h" +#include "WifiSettings.h" #else -#include +#include // From esp_wifi_types.h in ESP-IDF 5.2 typedef enum { WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */ @@ -35,6 +30,8 @@ typedef enum { } wifi_auth_mode_t; #endif +namespace tt::service::wifi { + typedef enum { /** Radio was turned on */ WifiEventTypeRadioStateOn, @@ -77,60 +74,58 @@ typedef struct { * @brief Get wifi pubsub * @return PubSub* */ -PubSub* wifi_get_pubsub(); +PubSub* get_pubsub(); -WifiRadioState wifi_get_radio_state(); +WifiRadioState get_radio_state(); /** * @brief Request scanning update. Returns immediately. Results are through pubsub. */ -void wifi_scan(); +void scan(); /** * @return true if wifi is actively scanning */ -bool wifi_is_scanning(); +bool is_scanning(); /** * @brief Returns the access points from the last scan (if any). It only contains public APs. * @param records the allocated buffer to store the records in * @param limit the maximum amount of records to store */ -void wifi_get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count); +void get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count); /** * @brief Overrides the default scan result size of 16. * @param records the record limit for the scan result (84 bytes per record!) */ -void wifi_set_scan_records(uint16_t records); +void set_scan_records(uint16_t records); /** * @brief Enable/disable the radio. Ignores input if desired state matches current state. * @param enabled */ -void wifi_set_enabled(bool enabled); +void set_enabled(bool enabled); /** * @brief Connect to a network. Disconnects any existing connection. * Returns immediately but runs in the background. Results are through pubsub. * @param ap */ -void wifi_connect(const WifiApSettings* ap, bool remember); +void connect(const settings::WifiApSettings* ap, bool remember); /** * @brief Disconnect from the access point. Doesn't have any effect when not connected. */ -void wifi_disconnect(); +void disconnect(); /** * Return true if the connection isn't unencrypted. */ -bool wifi_is_connection_secure(); +bool is_connection_secure(); /** * Returns the RSSI value (negative number) or return 1 when not connected */ -int wifi_get_rssi(); +int get_rssi(); -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility-headless/src/services/wifi/wifi_esp.c b/TactilityHeadless/Source/Services/Wifi/WifiEsp.cpp similarity index 56% rename from tactility-headless/src/services/wifi/wifi_esp.c rename to TactilityHeadless/Source/Services/Wifi/WifiEsp.cpp index 15f479af..c7a4fad9 100644 --- a/tactility-headless/src/services/wifi/wifi_esp.c +++ b/TactilityHeadless/Source/Services/Wifi/WifiEsp.cpp @@ -1,48 +1,26 @@ #ifdef ESP_TARGET -#include "wifi.h" +#include "Wifi.h" -#include "assets.h" -#include "check.h" +#include "MessageQueue.h" +#include "Mutex.h" +#include "Check.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" -#include "log.h" -#include "message_queue.h" -#include "mutex.h" -#include "pubsub.h" -#include "service.h" -#include "wifi_settings.h" +#include "Log.h" +#include "Pubsub.h" +#include "Service.h" +#include "WifiSettings.h" +#include +#include #include -#define TAG "wifi" +namespace tt::service::wifi { + +#define TAG "wifi_service" #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 -typedef struct { - /** @brief Locking mechanism for modifying the Wifi instance */ - Mutex* mutex; - /** @brief The public event bus */ - PubSub* pubsub; - /** @brief The internal message queue */ - MessageQueue* queue; - /** @brief The network interface when wifi is started */ - esp_netif_t* _Nullable netif; - /** @brief Scanning results */ - wifi_ap_record_t* _Nullable scan_list; - /** @brief The current item count in scan_list (-1 when scan_list is NULL) */ - uint16_t scan_list_count; - /** @brief Maximum amount of records to scan (value > 0) */ - uint16_t scan_list_limit; - bool scan_active; - bool secure_connection; - esp_event_handler_instance_t event_handler_any_id; - esp_event_handler_instance_t event_handler_got_ip; - EventGroupHandle_t event_group; - WifiRadioState radio_state; - WifiApSettings connection_target; - bool connection_target_remember; // Whether to store the connection_target on successful connection or not -} Wifi; - typedef enum { WifiMessageTypeRadioOn, WifiMessageTypeRadioOff, @@ -61,122 +39,133 @@ typedef struct { }; } WifiMessage; -static Wifi* wifi_singleton = NULL; +class Wifi { +public: + Wifi(); + ~Wifi(); -// Forward declarations -static void wifi_scan_list_free_safely(Wifi* wifi); -static void wifi_disconnect_internal(Wifi* wifi); -static void wifi_lock(Wifi* wifi); -static void wifi_unlock(Wifi* wifi); - -// region Alloc - -static Wifi* wifi_alloc() { - Wifi* instance = malloc(sizeof(Wifi)); - instance->mutex = tt_mutex_alloc(MutexTypeRecursive); - instance->pubsub = tt_pubsub_alloc(); + std::atomic radio_state; + /** @brief Locking mechanism for modifying the Wifi instance */ + Mutex mutex = Mutex(MutexTypeRecursive); + /** @brief The public event bus */ + PubSub* pubsub = nullptr; + /** @brief The internal message queue */ + MessageQueue queue = MessageQueue(1, sizeof(WifiMessage)); // TODO: Deal with messages that come in while an action is ongoing // for example: when scanning and you turn off the radio, the scan should probably stop or turning off // the radio should disable the on/off button in the app as it is pending. - instance->queue = tt_message_queue_alloc(1, sizeof(WifiMessage)); - instance->netif = NULL; - instance->scan_active = false; - instance->scan_list = NULL; - instance->scan_list_count = 0; - instance->scan_list_limit = TT_WIFI_SCAN_RECORD_LIMIT; - instance->event_handler_any_id = NULL; - instance->event_handler_got_ip = NULL; - instance->event_group = xEventGroupCreate(); - instance->radio_state = WIFI_RADIO_OFF; - instance->secure_connection = false; - instance->connection_target = (WifiApSettings) { + /** @brief The network interface when wifi is started */ + esp_netif_t* _Nullable netif = nullptr; + /** @brief Scanning results */ + wifi_ap_record_t* _Nullable scan_list = nullptr; + /** @brief The current item count in scan_list (-1 when scan_list is NULL) */ + uint16_t scan_list_count = 0; + /** @brief Maximum amount of records to scan (value > 0) */ + uint16_t scan_list_limit = TT_WIFI_SCAN_RECORD_LIMIT; + bool scan_active = false; + bool secure_connection = false; + esp_event_handler_instance_t event_handler_any_id = nullptr; + esp_event_handler_instance_t event_handler_got_ip = nullptr; + EventGroupHandle_t event_group; + settings::WifiApSettings connection_target = { .ssid = { 0 }, .password = { 0 }, .auto_connect = false }; - instance->connection_target_remember = false; - return instance; + bool connection_target_remember = false; // Whether to store the connection_target on successful connection or not +}; + +static Wifi* wifi_singleton = nullptr; + +// Forward declarations +static void scan_list_free_safely(Wifi* wifi); +static void disconnect_internal_but_keep_active(Wifi* wifi); +static void lock(Wifi* wifi); +static void unlock(Wifi* wifi); + +// region Alloc + +Wifi::Wifi() : radio_state(WIFI_RADIO_OFF) { + pubsub = tt_pubsub_alloc(); + event_group = xEventGroupCreate(); } -static void wifi_free(Wifi* instance) { - tt_mutex_free(instance->mutex); - tt_pubsub_free(instance->pubsub); - tt_message_queue_free(instance->queue); - free(instance); +Wifi::~Wifi() { + tt_pubsub_free(pubsub); } // endregion Alloc // region Public functions -PubSub* wifi_get_pubsub() { +PubSub* get_pubsub() { tt_assert(wifi_singleton); return wifi_singleton->pubsub; } -WifiRadioState wifi_get_radio_state() { +WifiRadioState get_radio_state() { tt_assert(wifi_singleton); - wifi_lock(wifi_singleton); + lock(wifi_singleton); WifiRadioState state = wifi_singleton->radio_state; - wifi_unlock(wifi_singleton); + unlock(wifi_singleton); return state; } -void wifi_scan() { +void scan() { tt_assert(wifi_singleton); - wifi_lock(wifi_singleton); + lock(wifi_singleton); WifiMessage message = {.type = WifiMessageTypeScan}; // No need to lock for queue - tt_message_queue_put(wifi_singleton->queue, &message, 100 / portTICK_PERIOD_MS); - wifi_unlock(wifi_singleton); + wifi_singleton->queue.put(&message, 100 / portTICK_PERIOD_MS); + unlock(wifi_singleton); } -bool wifi_is_scanning() { +bool is_scanning() { tt_assert(wifi_singleton); - wifi_lock(wifi_singleton); + lock(wifi_singleton); bool is_scanning = wifi_singleton->scan_active; - wifi_unlock(wifi_singleton); + unlock(wifi_singleton); return is_scanning; } -void wifi_connect(const WifiApSettings* ap, bool remember) { +void connect(const settings::WifiApSettings* ap, bool remember) { tt_assert(wifi_singleton); - wifi_lock(wifi_singleton); - memcpy(&wifi_singleton->connection_target, ap, sizeof(WifiApSettings)); + lock(wifi_singleton); + memcpy(&wifi_singleton->connection_target, ap, sizeof(settings::WifiApSettings)); wifi_singleton->connection_target_remember = remember; WifiMessage message = {.type = WifiMessageTypeConnect}; - tt_message_queue_put(wifi_singleton->queue, &message, 100 / portTICK_PERIOD_MS); - wifi_unlock(wifi_singleton); + wifi_singleton->queue.put(&message, 100 / portTICK_PERIOD_MS); + unlock(wifi_singleton); } -void wifi_disconnect() { +void disconnect() { tt_assert(wifi_singleton); - wifi_lock(wifi_singleton); - wifi_singleton->connection_target = (WifiApSettings) { + lock(wifi_singleton); + wifi_singleton->connection_target = (settings::WifiApSettings) { .ssid = { 0 }, .password = { 0 }, .auto_connect = false }; WifiMessage message = {.type = WifiMessageTypeDisconnect}; - tt_message_queue_put(wifi_singleton->queue, &message, 100 / portTICK_PERIOD_MS); - wifi_unlock(wifi_singleton); + wifi_singleton->queue.put(&message, 100 / portTICK_PERIOD_MS); + unlock(wifi_singleton); } -void wifi_set_scan_records(uint16_t records) { +void set_scan_records(uint16_t records) { tt_assert(wifi_singleton); - wifi_lock(wifi_singleton); + lock(wifi_singleton); if (records != wifi_singleton->scan_list_limit) { - wifi_scan_list_free_safely(wifi_singleton); + scan_list_free_safely(wifi_singleton); wifi_singleton->scan_list_limit = records; } - wifi_unlock(wifi_singleton); + unlock(wifi_singleton); } -void wifi_get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count) { - tt_check(wifi_singleton); - tt_check(result_count); +void get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count) { + tt_assert(wifi_singleton); + tt_assert(result_count); - wifi_lock(wifi_singleton); + lock(wifi_singleton); if (wifi_singleton->scan_list_count == 0) { *result_count = 0; } else { @@ -192,34 +181,34 @@ void wifi_get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* res // so it effectively became the list count: *result_count = i; } - wifi_unlock(wifi_singleton); + unlock(wifi_singleton); } -void wifi_set_enabled(bool enabled) { - tt_check(wifi_singleton); - wifi_lock(wifi_singleton); +void set_enabled(bool enabled) { + tt_assert(wifi_singleton); + lock(wifi_singleton); if (enabled) { WifiMessage message = {.type = WifiMessageTypeRadioOn}; // No need to lock for queue - tt_message_queue_put(wifi_singleton->queue, &message, 100 / portTICK_PERIOD_MS); + wifi_singleton->queue.put(&message, 100 / portTICK_PERIOD_MS); } else { WifiMessage message = {.type = WifiMessageTypeRadioOff}; // No need to lock for queue - tt_message_queue_put(wifi_singleton->queue, &message, 100 / portTICK_PERIOD_MS); + wifi_singleton->queue.put(&message, 100 / portTICK_PERIOD_MS); } - wifi_unlock(wifi_singleton); + unlock(wifi_singleton); } -bool wifi_is_connection_secure() { - tt_check(wifi_singleton); - wifi_lock(wifi_singleton); +bool is_connection_secure() { + tt_assert(wifi_singleton); + lock(wifi_singleton); bool is_secure = wifi_singleton->secure_connection; - wifi_unlock(wifi_singleton); + unlock(wifi_singleton); return is_secure; } -int wifi_get_rssi() { - tt_check(wifi_singleton); +int get_rssi() { + tt_assert(wifi_singleton); static int rssi = 0; if (esp_wifi_sta_get_rssi(&rssi) == ESP_OK) { return rssi; @@ -230,73 +219,77 @@ int wifi_get_rssi() { // endregion Public functions -static void wifi_lock(Wifi* wifi) { +static void lock(Wifi* wifi) { tt_assert(wifi); - tt_assert(wifi->mutex); - tt_mutex_acquire(wifi->mutex, tt_ms_to_ticks(100)); + wifi->mutex.acquire(ms_to_ticks(100)); } -static void wifi_unlock(Wifi* wifi) { +static void unlock(Wifi* wifi) { tt_assert(wifi); - tt_assert(wifi->mutex); - tt_mutex_release(wifi->mutex); + wifi->mutex.release(); } -static void wifi_scan_list_alloc(Wifi* wifi) { - tt_check(wifi->scan_list == NULL); - wifi->scan_list = malloc(sizeof(wifi_ap_record_t) * wifi->scan_list_limit); +static void scan_list_alloc(Wifi* wifi) { + tt_assert(wifi->scan_list == nullptr); + wifi->scan_list = static_cast(malloc(sizeof(wifi_ap_record_t) * wifi->scan_list_limit)); wifi->scan_list_count = 0; } -static void wifi_scan_list_alloc_safely(Wifi* wifi) { - if (wifi->scan_list == NULL) { - wifi_scan_list_alloc(wifi); +static void scan_list_alloc_safely(Wifi* wifi) { + if (wifi->scan_list == nullptr) { + scan_list_alloc(wifi); } } -static void wifi_scan_list_free(Wifi* wifi) { - tt_check(wifi->scan_list != NULL); +static void scan_list_free(Wifi* wifi) { + tt_assert(wifi->scan_list != nullptr); free(wifi->scan_list); - wifi->scan_list = NULL; + wifi->scan_list = nullptr; wifi->scan_list_count = 0; } -static void wifi_scan_list_free_safely(Wifi* wifi) { - if (wifi->scan_list != NULL) { - wifi_scan_list_free(wifi); +static void scan_list_free_safely(Wifi* wifi) { + if (wifi->scan_list != nullptr) { + scan_list_free(wifi); } } -static void wifi_publish_event_simple(Wifi* wifi, WifiEventType type) { +static void publish_event_simple(Wifi* wifi, WifiEventType type) { WifiEvent turning_on_event = {.type = type}; tt_pubsub_publish(wifi->pubsub, &turning_on_event); } -static void wifi_copy_scan_list(Wifi* wifi) { - // Create scan list if it does not exist - wifi_scan_list_alloc_safely(wifi); - wifi->scan_list_count = 0; - uint16_t record_count = wifi->scan_list_limit; +static bool copy_scan_list(Wifi* wifi) { + if ((wifi->radio_state == WIFI_RADIO_ON || wifi->radio_state == WIFI_RADIO_CONNECTION_ACTIVE) && wifi->scan_active) { + // Create scan list if it does not exist + scan_list_alloc_safely(wifi); + wifi->scan_list_count = 0; + uint16_t record_count = wifi->scan_list_limit; - ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&record_count, wifi->scan_list)); - uint16_t safe_record_count = TT_MIN(wifi->scan_list_limit, record_count); - wifi->scan_list_count = safe_record_count; - TT_LOG_I(TAG, "Scanned %u APs. Showing %u:", record_count, safe_record_count); - for (uint16_t i = 0; i < safe_record_count; i++) { - wifi_ap_record_t* record = &wifi->scan_list[i]; - TT_LOG_I(TAG, " - SSID %s (RSSI %d, channel %d)", record->ssid, record->rssi, record->primary); + ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&record_count, wifi->scan_list)); + uint16_t safe_record_count = TT_MIN(wifi->scan_list_limit, record_count); + wifi->scan_list_count = safe_record_count; + TT_LOG_I(TAG, "Scanned %u APs. Showing %u:", record_count, safe_record_count); + for (uint16_t i = 0; i < safe_record_count; i++) { + wifi_ap_record_t* record = &wifi->scan_list[i]; + TT_LOG_I(TAG, " - SSID %s (RSSI %d, channel %d)", record->ssid, record->rssi, record->primary); + } + return true; + } else { + return false; } } -static void wifi_auto_connect(Wifi* wifi) { +static void auto_connect(Wifi* wifi) { for (int i = 0; i < wifi->scan_list_count; ++i) { const char* ssid = (const char*)wifi->scan_list[i].ssid; - if (tt_wifi_settings_contains(ssid)) { + if (settings::contains(ssid)) { static_assert(sizeof(wifi->scan_list[i].ssid) == (TT_WIFI_SSID_LIMIT + 1), "SSID size mismatch"); - WifiApSettings ap_settings; - if (tt_wifi_settings_load(ssid, &ap_settings)) { + settings::WifiApSettings ap_settings; + if (settings::load(ssid, &ap_settings)) { if (ap_settings.auto_connect) { - wifi_connect(&ap_settings, false); + TT_LOG_I(TAG, "Auto-connecting to %s", ap_settings.ssid); + connect(&ap_settings, false); } } else { TT_LOG_E(TAG, "Failed to load credentials for ssid %s", ssid); @@ -307,55 +300,47 @@ static void wifi_auto_connect(Wifi* wifi) { } static void event_handler(TT_UNUSED void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { - wifi_lock(wifi_singleton); + lock(wifi_singleton); if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { TT_LOG_I(TAG, "event_handler: sta start"); if (wifi_singleton->radio_state == WIFI_RADIO_CONNECTION_PENDING) { esp_wifi_connect(); } } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { - xEventGroupSetBits(wifi_singleton->event_group, WIFI_FAIL_BIT); - TT_LOG_I(TAG, "event_handler: disconnected"); - wifi_singleton->radio_state = WIFI_RADIO_ON; - wifi_publish_event_simple(wifi_singleton, WifiEventTypeDisconnected); + if (wifi_singleton->radio_state != WIFI_RADIO_OFF_PENDING) { + xEventGroupSetBits(wifi_singleton->event_group, WIFI_FAIL_BIT); + TT_LOG_I(TAG, "event_handler: disconnected"); + wifi_singleton->radio_state = WIFI_RADIO_ON; + publish_event_simple(wifi_singleton, WifiEventTypeDisconnected); + } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { - ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data; + auto* event = static_cast(event_data); TT_LOG_I(TAG, "event_handler: got ip:" IPSTR, IP2STR(&event->ip_info.ip)); xEventGroupSetBits(wifi_singleton->event_group, WIFI_CONNECTED_BIT); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { - wifi_event_sta_scan_done_t* event = (wifi_event_sta_scan_done_t*)event_data; + auto* event = static_cast(event_data); TT_LOG_I(TAG, "event_handler: wifi scanning done (scan id %u)", event->scan_id); - bool copied_list; - if ( - wifi_singleton->radio_state == WIFI_RADIO_ON || - wifi_singleton->radio_state == WIFI_RADIO_CONNECTION_ACTIVE || - wifi_singleton->radio_state == WIFI_RADIO_CONNECTION_PENDING - ) { - wifi_copy_scan_list(wifi_singleton); - copied_list = true; - } else { - copied_list = false; - } + bool copied_list = copy_scan_list(wifi_singleton); if ( wifi_singleton->radio_state != WIFI_RADIO_OFF && wifi_singleton->radio_state != WIFI_RADIO_OFF_PENDING ) { + wifi_singleton->scan_active = false; esp_wifi_scan_stop(); } - wifi_publish_event_simple(wifi_singleton, WifiEventTypeScanFinished); - wifi_singleton->scan_active = false; + publish_event_simple(wifi_singleton, WifiEventTypeScanFinished); TT_LOG_I(TAG, "Finished scan"); - if (copied_list) { - wifi_auto_connect(wifi_singleton); + if (copied_list && wifi_singleton->radio_state == WIFI_RADIO_ON) { + auto_connect(wifi_singleton); } } - wifi_unlock(wifi_singleton); + unlock(wifi_singleton); } -static void wifi_enable(Wifi* wifi) { +static void enable(Wifi* wifi) { WifiRadioState state = wifi->radio_state; if ( state == WIFI_RADIO_ON || @@ -368,9 +353,9 @@ static void wifi_enable(Wifi* wifi) { TT_LOG_I(TAG, "Enabling"); wifi->radio_state = WIFI_RADIO_ON_PENDING; - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOnPending); + publish_event_simple(wifi, WifiEventTypeRadioStateOnPending); - if (wifi->netif != NULL) { + if (wifi->netif != nullptr) { esp_netif_destroy(wifi->netif); } wifi->netif = esp_netif_create_default_wifi_sta(); @@ -385,7 +370,7 @@ static void wifi_enable(Wifi* wifi) { TT_LOG_E(TAG, "Insufficient memory"); } wifi->radio_state = WIFI_RADIO_OFF; - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOff); + publish_event_simple(wifi, WifiEventTypeRadioStateOff); return; } @@ -396,7 +381,7 @@ static void wifi_enable(Wifi* wifi) { WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, - NULL, + nullptr, &wifi->event_handler_any_id )); @@ -405,7 +390,7 @@ static void wifi_enable(Wifi* wifi) { IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, - NULL, + nullptr, &wifi->event_handler_got_ip )); @@ -413,7 +398,7 @@ static void wifi_enable(Wifi* wifi) { TT_LOG_E(TAG, "Wifi mode setting failed"); wifi->radio_state = WIFI_RADIO_OFF; esp_wifi_deinit(); - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOff); + publish_event_simple(wifi, WifiEventTypeRadioStateOff); return; } @@ -426,16 +411,16 @@ static void wifi_enable(Wifi* wifi) { wifi->radio_state = WIFI_RADIO_OFF; esp_wifi_set_mode(WIFI_MODE_NULL); esp_wifi_deinit(); - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOff); + publish_event_simple(wifi, WifiEventTypeRadioStateOff); return; } wifi->radio_state = WIFI_RADIO_ON; - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOn); + publish_event_simple(wifi, WifiEventTypeRadioStateOn); TT_LOG_I(TAG, "Enabled"); } -static void wifi_disable(Wifi* wifi) { +static void disable(Wifi* wifi) { WifiRadioState state = wifi->radio_state; if ( state == WIFI_RADIO_OFF || @@ -448,15 +433,15 @@ static void wifi_disable(Wifi* wifi) { TT_LOG_I(TAG, "Disabling"); wifi->radio_state = WIFI_RADIO_OFF_PENDING; - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOffPending); + publish_event_simple(wifi, WifiEventTypeRadioStateOffPending); // Free up scan list memory - wifi_scan_list_free_safely(wifi_singleton); + scan_list_free_safely(wifi_singleton); if (esp_wifi_stop() != ESP_OK) { TT_LOG_E(TAG, "Failed to stop radio"); wifi->radio_state = WIFI_RADIO_ON; - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOn); + publish_event_simple(wifi, WifiEventTypeRadioStateOn); return; } @@ -484,16 +469,16 @@ static void wifi_disable(Wifi* wifi) { TT_LOG_E(TAG, "Failed to deinit"); } - tt_check(wifi->netif != NULL); + tt_assert(wifi->netif != nullptr); esp_netif_destroy(wifi->netif); - wifi->netif = NULL; - + wifi->netif = nullptr; + wifi->scan_active = false; wifi->radio_state = WIFI_RADIO_OFF; - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOff); + publish_event_simple(wifi, WifiEventTypeRadioStateOff); TT_LOG_I(TAG, "Disabled"); } -static void wifi_scan_internal(Wifi* wifi) { +static void scan_internal(Wifi* wifi) { WifiRadioState state = wifi->radio_state; if (state != WIFI_RADIO_ON && state != WIFI_RADIO_CONNECTION_ACTIVE && state != WIFI_RADIO_CONNECTION_PENDING) { TT_LOG_W(TAG, "Scan unavailable: wifi not enabled"); @@ -501,10 +486,10 @@ static void wifi_scan_internal(Wifi* wifi) { } if (!wifi->scan_active) { - if (esp_wifi_scan_start(NULL, false) == ESP_OK) { + if (esp_wifi_scan_start(nullptr, false) == ESP_OK) { TT_LOG_I(TAG, "Starting scan"); wifi->scan_active = true; - wifi_publish_event_simple(wifi, WifiEventTypeScanStarted); + publish_event_simple(wifi, WifiEventTypeScanStarted); } else { TT_LOG_I(TAG, "Can't start scan"); } @@ -513,13 +498,28 @@ static void wifi_scan_internal(Wifi* wifi) { } } -static void wifi_connect_internal(Wifi* wifi, WifiConnectMessage* connect_message) { - // TODO: only when connected! - wifi_disconnect_internal(wifi); +static void connect_internal(Wifi* wifi) { + TT_LOG_I(TAG, "Connecting to %s", wifi->connection_target.ssid); + + // Stop radio first, if needed + WifiRadioState radio_state = wifi->radio_state; + if ( + radio_state == WIFI_RADIO_ON || + radio_state == WIFI_RADIO_CONNECTION_ACTIVE || + radio_state == WIFI_RADIO_CONNECTION_PENDING + ) { + TT_LOG_I(TAG, "Connecting: Stopping radio first"); + esp_err_t stop_result = esp_wifi_stop(); + wifi->scan_active = false; + if (stop_result != ESP_OK) { + TT_LOG_E(TAG, "Connecting: Failed to disconnect (%s)", esp_err_to_name(stop_result)); + return; + } + } wifi->radio_state = WIFI_RADIO_CONNECTION_PENDING; - wifi_publish_event_simple(wifi, WifiEventTypeConnectionPending); + publish_event_simple(wifi, WifiEventTypeConnectionPending); wifi_config_t wifi_config = { .sta = { @@ -528,10 +528,43 @@ static void wifi_connect_internal(Wifi* wifi, WifiConnectMessage* connect_messag * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards. */ - .threshold.authmode = WIFI_AUTH_WPA2_WPA3_PSK, + .ssid = {0}, + .password = {0}, + .scan_method = WIFI_ALL_CHANNEL_SCAN, + .bssid_set = false, + .bssid = { 0 }, + .channel = 0, + .listen_interval = 0, + .sort_method = WIFI_CONNECT_AP_BY_SIGNAL, + .threshold = { + .rssi = 0, + .authmode = WIFI_AUTH_WPA2_WPA3_PSK, + }, + .pmf_cfg = { + .capable = false, + .required = false + }, + .rm_enabled = 0, + .btm_enabled = 0, + .mbo_enabled = 0, + .ft_enabled = 0, + .owe_enabled = 0, + .transition_disable = 0, + .reserved = 0, .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, + .sae_pk_mode = WPA3_SAE_PK_MODE_AUTOMATIC, + .failure_retry_cnt = 1, + .he_dcm_set = 0, + .he_dcm_max_constellation_tx = 0, + .he_dcm_max_constellation_rx = 0, + .he_mcs9_enabled = 0, + .he_su_beamformee_disabled = 0, + .he_trig_su_bmforming_feedback_disabled = 0, + .he_trig_mu_bmforming_partial_feedback_disabled = 0, + .he_trig_cqi_feedback_disabled = 0, + .he_reserved = 0, .sae_h2e_identifier = {0}, - }, + } }; static_assert(sizeof(wifi_config.sta.ssid) == (sizeof(wifi_singleton->connection_target.ssid)-1), "SSID size mismatch"); @@ -544,7 +577,7 @@ static void wifi_connect_internal(Wifi* wifi, WifiConnectMessage* connect_messag if (set_config_result != ESP_OK) { wifi->radio_state = WIFI_RADIO_ON; TT_LOG_E(TAG, "failed to set wifi config (%s)", esp_err_to_name(set_config_result)); - wifi_publish_event_simple(wifi, WifiEventTypeConnectionFailed); + publish_event_simple(wifi, WifiEventTypeConnectionFailed); return; } @@ -552,7 +585,7 @@ static void wifi_connect_internal(Wifi* wifi, WifiConnectMessage* connect_messag if (wifi_start_result != ESP_OK) { wifi->radio_state = WIFI_RADIO_ON; TT_LOG_E(TAG, "failed to start wifi to begin connecting (%s)", esp_err_to_name(wifi_start_result)); - wifi_publish_event_simple(wifi, WifiEventTypeConnectionFailed); + publish_event_simple(wifi, WifiEventTypeConnectionFailed); return; } @@ -569,10 +602,10 @@ static void wifi_connect_internal(Wifi* wifi, WifiConnectMessage* connect_messag if (bits & WIFI_CONNECTED_BIT) { wifi->radio_state = WIFI_RADIO_CONNECTION_ACTIVE; - wifi_publish_event_simple(wifi, WifiEventTypeConnectionSuccess); + publish_event_simple(wifi, WifiEventTypeConnectionSuccess); TT_LOG_I(TAG, "Connected to %s", wifi->connection_target.ssid); if (wifi->connection_target_remember) { - if (!tt_wifi_settings_save(&wifi->connection_target)) { + if (!settings::save(&wifi->connection_target)) { TT_LOG_E(TAG, "Failed to store credentials"); } else { TT_LOG_I(TAG, "Stored credentials"); @@ -580,30 +613,18 @@ static void wifi_connect_internal(Wifi* wifi, WifiConnectMessage* connect_messag } } else if (bits & WIFI_FAIL_BIT) { wifi->radio_state = WIFI_RADIO_ON; - wifi_publish_event_simple(wifi, WifiEventTypeConnectionFailed); + publish_event_simple(wifi, WifiEventTypeConnectionFailed); TT_LOG_I(TAG, "Failed to connect to %s", wifi->connection_target.ssid); } else { wifi->radio_state = WIFI_RADIO_ON; - wifi_publish_event_simple(wifi, WifiEventTypeConnectionFailed); + publish_event_simple(wifi, WifiEventTypeConnectionFailed); TT_LOG_E(TAG, "UNEXPECTED EVENT"); } xEventGroupClearBits(wifi_singleton->event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT); } -static void wifi_disconnect_internal(Wifi* wifi) { - esp_err_t stop_result = esp_wifi_stop(); - if (stop_result != ESP_OK) { - TT_LOG_E(TAG, "Failed to disconnect (%s)", esp_err_to_name(stop_result)); - } else { - wifi->radio_state = WIFI_RADIO_ON; - wifi->secure_connection = false; - wifi_publish_event_simple(wifi, WifiEventTypeDisconnected); - TT_LOG_I(TAG, "Disconnected"); - } -} - -static void wifi_disconnect_internal_but_keep_active(Wifi* wifi) { +static void disconnect_internal_but_keep_active(Wifi* wifi) { esp_err_t stop_result = esp_wifi_stop(); if (stop_result != ESP_OK) { TT_LOG_E(TAG, "Failed to disconnect (%s)", esp_err_to_name(stop_result)); @@ -614,7 +635,10 @@ static void wifi_disconnect_internal_but_keep_active(Wifi* wifi) { .sta = { .ssid = {0}, .password = {0}, - .threshold.authmode = WIFI_AUTH_OPEN, + .threshold = { + .rssi = 0, + .authmode = WIFI_AUTH_OPEN, + }, .sae_pwe_h2e = WPA3_SAE_PWE_UNSPECIFIED, .sae_h2e_identifier = {0}, }, @@ -625,7 +649,7 @@ static void wifi_disconnect_internal_but_keep_active(Wifi* wifi) { // TODO: disable radio, because radio state is in limbo between off and on wifi->radio_state = WIFI_RADIO_OFF; TT_LOG_E(TAG, "failed to set wifi config (%s)", esp_err_to_name(set_config_result)); - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOff); + publish_event_simple(wifi, WifiEventTypeRadioStateOff); return; } @@ -634,95 +658,98 @@ static void wifi_disconnect_internal_but_keep_active(Wifi* wifi) { // TODO: disable radio, because radio state is in limbo between off and on wifi->radio_state = WIFI_RADIO_OFF; TT_LOG_E(TAG, "failed to start wifi to begin connecting (%s)", esp_err_to_name(wifi_start_result)); - wifi_publish_event_simple(wifi, WifiEventTypeRadioStateOff); + publish_event_simple(wifi, WifiEventTypeRadioStateOff); return; } wifi->radio_state = WIFI_RADIO_ON; - wifi_publish_event_simple(wifi, WifiEventTypeDisconnected); + publish_event_simple(wifi, WifiEventTypeDisconnected); TT_LOG_I(TAG, "Disconnected"); } -// ESP wifi APIs need to run from the main task, so we can't just spawn a thread +// ESP Wi-Fi APIs need to run from the main task, so we can't just spawn a thread _Noreturn int32_t wifi_main(TT_UNUSED void* parameter) { TT_LOG_I(TAG, "Started main loop"); - tt_check(wifi_singleton != NULL); + tt_assert(wifi_singleton != nullptr); Wifi* wifi = wifi_singleton; - MessageQueue* queue = wifi->queue; + MessageQueue& queue = wifi->queue; if (TT_WIFI_AUTO_ENABLE) { - wifi_enable(wifi); - wifi_scan_internal(wifi); + enable(wifi); + scan_internal(wifi); } WifiMessage message; while (true) { - if (tt_message_queue_get(queue, &message, 10000 / portTICK_PERIOD_MS) == TtStatusOk) { + if (queue.get(&message, 10000 / portTICK_PERIOD_MS) == TtStatusOk) { TT_LOG_I(TAG, "Processing message of type %d", message.type); switch (message.type) { case WifiMessageTypeRadioOn: - wifi_lock(wifi); - wifi_enable(wifi); - wifi_unlock(wifi); + lock(wifi); + enable(wifi); + unlock(wifi); break; case WifiMessageTypeRadioOff: - wifi_lock(wifi); - wifi_disable(wifi); - wifi_unlock(wifi); + lock(wifi); + disable(wifi); + unlock(wifi); break; case WifiMessageTypeScan: - wifi_lock(wifi); - wifi_scan_internal(wifi); - wifi_unlock(wifi); + lock(wifi); + scan_internal(wifi); + unlock(wifi); break; case WifiMessageTypeConnect: - wifi_lock(wifi); - wifi_connect_internal(wifi, &message.connect_message); - wifi_unlock(wifi); + lock(wifi); + connect_internal(wifi); + unlock(wifi); break; case WifiMessageTypeDisconnect: - wifi_lock(wifi); - wifi_disconnect_internal_but_keep_active(wifi); - wifi_unlock(wifi); + lock(wifi); + disconnect_internal_but_keep_active(wifi); + unlock(wifi); break; } } // Automatic scanning is done so we can automatically connect to access points - wifi_lock(wifi); + lock(wifi); bool should_start_scan = wifi->radio_state == WIFI_RADIO_ON && !wifi->scan_active; - wifi_unlock(wifi); + unlock(wifi); if (should_start_scan) { - wifi_scan_internal(wifi); + scan_internal(wifi); } } } -static void wifi_service_start(TT_UNUSED Service service) { - tt_check(wifi_singleton == NULL); - wifi_singleton = wifi_alloc(); +static void service_start(Service& service) { + tt_assert(wifi_singleton == nullptr); + wifi_singleton = new Wifi(); + service.setData(wifi_singleton); } -static void wifi_service_stop(TT_UNUSED Service service) { - tt_check(wifi_singleton != NULL); +static void service_stop(Service& service) { + tt_assert(wifi_singleton != nullptr); WifiRadioState state = wifi_singleton->radio_state; if (state != WIFI_RADIO_OFF) { - wifi_disable(wifi_singleton); + disable(wifi_singleton); } - wifi_free(wifi_singleton); - wifi_singleton = NULL; + delete wifi_singleton; + wifi_singleton = nullptr; // wifi_main() cannot be stopped yet as it runs in the main task. // We could theoretically exit it, but then we wouldn't be able to restart the service. tt_crash("not fully implemented"); } -const ServiceManifest wifi_service = { +extern const ServiceManifest manifest = { .id = "wifi", - .on_start = &wifi_service_start, - .on_stop = &wifi_service_stop + .on_start = &service_start, + .on_stop = &service_stop }; +} // namespace + #endif // ESP_TARGET \ No newline at end of file diff --git a/tactility-headless/src/services/wifi/wifi_globals.h b/TactilityHeadless/Source/Services/Wifi/WifiGlobals.h similarity index 84% rename from tactility-headless/src/services/wifi/wifi_globals.h rename to TactilityHeadless/Source/Services/Wifi/WifiGlobals.h index 7e7c37e9..64c98fcd 100644 --- a/tactility-headless/src/services/wifi/wifi_globals.h +++ b/TactilityHeadless/Source/Services/Wifi/WifiGlobals.h @@ -1,9 +1,5 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #define TT_WIFI_AUTO_CONNECT true // Default setting for new Wi-Fi entries #define TT_WIFI_AUTO_ENABLE false @@ -11,7 +7,3 @@ extern "C" { #define TT_WIFI_SSID_LIMIT 32 // 32 characters/octets, according to IEEE 802.11-2020 spec #define TT_WIFI_CREDENTIALS_PASSWORD_LIMIT 64 // 64 characters/octets, according to IEEE 802.11-2020 spec - -#ifdef __cplusplus -} -#endif diff --git a/tactility-headless/src/services/wifi/wifi_mock.c b/TactilityHeadless/Source/Services/Wifi/WifiMock.cpp similarity index 53% rename from tactility-headless/src/services/wifi/wifi_mock.c rename to TactilityHeadless/Source/Services/Wifi/WifiMock.cpp index 78025114..be82d171 100644 --- a/tactility-headless/src/services/wifi/wifi_mock.c +++ b/TactilityHeadless/Source/Services/Wifi/WifiMock.cpp @@ -1,15 +1,17 @@ -#include "wifi.h" +#include "Wifi.h" #ifndef ESP_TARGET -#include "assets.h" -#include "check.h" -#include "log.h" -#include "message_queue.h" -#include "mutex.h" -#include "pubsub.h" -#include "service.h" -#include +#include "Check.h" +#include "Log.h" +#include "MessageQueue.h" +#include "Mutex.h" +#include "Pubsub.h" +#include "Service.h" +#include +#include + +namespace tt::service::wifi { #define TAG "wifi" #define WIFI_CONNECTED_BIT BIT0 @@ -21,14 +23,14 @@ typedef struct { /** @brief The public event bus */ PubSub* pubsub; /** @brief The internal message queue */ - MessageQueue* queue; + MessageQueue queue; bool scan_active; bool secure_connection; WifiRadioState radio_state; } Wifi; -static Wifi* wifi_singleton = NULL; +static Wifi* wifi = NULL; // Forward declarations static void wifi_lock(Wifi* wifi); @@ -36,7 +38,7 @@ static void wifi_unlock(Wifi* wifi); // region Static -static void wifi_publish_event_simple(Wifi* wifi, WifiEventType type) { +static void publish_event_simple(Wifi* wifi, WifiEventType type) { WifiEvent turning_on_event = {.type = type}; tt_pubsub_publish(wifi->pubsub, &turning_on_event); } @@ -46,7 +48,7 @@ static void wifi_publish_event_simple(Wifi* wifi, WifiEventType type) { // region Alloc static Wifi* wifi_alloc() { - Wifi* instance = malloc(sizeof(Wifi)); + auto* instance = static_cast(malloc(sizeof(Wifi))); instance->mutex = tt_mutex_alloc(MutexTypeRecursive); instance->pubsub = tt_pubsub_alloc(); instance->scan_active = false; @@ -58,7 +60,6 @@ static Wifi* wifi_alloc() { static void wifi_free(Wifi* instance) { tt_mutex_free(instance->mutex); tt_pubsub_free(instance->pubsub); - tt_message_queue_free(instance->queue); free(instance); } @@ -66,41 +67,41 @@ static void wifi_free(Wifi* instance) { // region Public functions -PubSub* wifi_get_pubsub() { - tt_assert(wifi_singleton); - return wifi_singleton->pubsub; +PubSub* get_pubsub() { + tt_assert(wifi); + return wifi->pubsub; } -WifiRadioState wifi_get_radio_state() { - return wifi_singleton->radio_state; +WifiRadioState get_radio_state() { + return wifi->radio_state; } -void wifi_scan() { - tt_assert(wifi_singleton); - wifi_singleton->scan_active = false; // TODO: enable and then later disable automatically +void scan() { + tt_assert(wifi); + wifi->scan_active = false; // TODO: enable and then later disable automatically } -bool wifi_is_scanning() { - tt_assert(wifi_singleton); - return wifi_singleton->scan_active; +bool is_scanning() { + tt_assert(wifi); + return wifi->scan_active; } -void wifi_connect(const WifiApSettings* ap, bool remember) { - tt_assert(wifi_singleton); +void connect(const settings::WifiApSettings* ap, bool remember) { + tt_assert(wifi); // TODO: implement } -void wifi_disconnect() { - tt_assert(wifi_singleton); +void disconnect() { + tt_assert(wifi); } -void wifi_set_scan_records(uint16_t records) { - tt_assert(wifi_singleton); +void set_scan_records(uint16_t records) { + tt_assert(wifi); // TODO: implement } -void wifi_get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count) { - tt_check(wifi_singleton); +void get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count) { + tt_check(wifi); tt_check(result_count); if (limit >= 5) { @@ -125,22 +126,22 @@ void wifi_get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* res } } -void wifi_set_enabled(bool enabled) { - tt_assert(wifi_singleton != NULL); +void set_enabled(bool enabled) { + tt_assert(wifi != NULL); if (enabled) { - wifi_singleton->radio_state = WIFI_RADIO_ON; - wifi_singleton->secure_connection = true; + wifi->radio_state = WIFI_RADIO_ON; + wifi->secure_connection = true; } else { - wifi_singleton->radio_state = WIFI_RADIO_OFF; + wifi->radio_state = WIFI_RADIO_OFF; } } -bool wifi_is_connection_secure() { - return wifi_singleton->secure_connection; +bool is_connection_secure() { + return wifi->secure_connection; } -int wifi_get_rssi() { - if (wifi_singleton->radio_state == WIFI_RADIO_CONNECTION_ACTIVE) { +int get_rssi() { + if (wifi->radio_state == WIFI_RADIO_CONNECTION_ACTIVE) { return -30; } else { return 0; @@ -149,36 +150,38 @@ int wifi_get_rssi() { // endregion Public functions -static void wifi_lock(Wifi* wifi) { +static void lock(Wifi* wifi) { tt_crash("this fails for now"); tt_assert(wifi); tt_assert(wifi->mutex); tt_mutex_acquire(wifi->mutex, 100); } -static void wifi_unlock(Wifi* wifi) { +static void unlock(Wifi* wifi) { tt_assert(wifi); tt_assert(wifi->mutex); tt_mutex_release(wifi->mutex); } -static void wifi_service_start(TT_UNUSED Service service) { - tt_check(wifi_singleton == NULL); - wifi_singleton = wifi_alloc(); +static void service_start(TT_UNUSED Service& service) { + tt_check(wifi == nullptr); + wifi = wifi_alloc(); } -static void wifi_service_stop(TT_UNUSED Service service) { - tt_check(wifi_singleton != NULL); +static void service_stop(TT_UNUSED Service& service) { + tt_check(wifi != nullptr); - wifi_free(wifi_singleton); - wifi_singleton = NULL; + wifi_free(wifi); + wifi = nullptr; } -const ServiceManifest wifi_service = { +extern const ServiceManifest manifest = { .id = "wifi", - .on_start = &wifi_service_start, - .on_stop = &wifi_service_stop + .on_start = &service_start, + .on_stop = &service_stop }; +} // namespace + #endif // ESP_TARGET \ No newline at end of file diff --git a/tactility-headless/src/services/wifi/wifi_settings.h b/TactilityHeadless/Source/Services/Wifi/WifiSettings.h similarity index 51% rename from tactility-headless/src/services/wifi/wifi_settings.h rename to TactilityHeadless/Source/Services/Wifi/WifiSettings.h index db2ba76e..b9a5b211 100644 --- a/tactility-headless/src/services/wifi/wifi_settings.h +++ b/TactilityHeadless/Source/Services/Wifi/WifiSettings.h @@ -1,11 +1,8 @@ #pragma once -#include -#include "wifi_globals.h" +#include "WifiGlobals.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace tt::service::wifi::settings { /** * This struct is stored as-is into NVS flash. @@ -19,16 +16,12 @@ typedef struct { bool auto_connect; } WifiApSettings; -void tt_wifi_settings_init(); +bool contains(const char* ssid); -bool tt_wifi_settings_contains(const char* ssid); +bool load(const char* ssid, WifiApSettings* settings); -bool tt_wifi_settings_load(const char* ssid, WifiApSettings* settings); +bool save(const WifiApSettings* settings); -bool tt_wifi_settings_save(const WifiApSettings* settings); +bool remove(const char* ssid); -bool tt_wifi_settings_remove(const char* ssid); - -#ifdef __cplusplus -} -#endif +} // namespace diff --git a/tactility-headless/src/services/wifi/wifi_settings_esp.c b/TactilityHeadless/Source/Services/Wifi/WifiSettingsEsp.cpp similarity index 70% rename from tactility-headless/src/services/wifi/wifi_settings_esp.c rename to TactilityHeadless/Source/Services/Wifi/WifiSettingsEsp.cpp index 1b6d62e2..515d163f 100644 --- a/tactility-headless/src/services/wifi/wifi_settings_esp.c +++ b/TactilityHeadless/Source/Services/Wifi/WifiSettingsEsp.cpp @@ -1,53 +1,50 @@ #ifdef ESP_TARGET -#include "wifi_globals.h" -#include "wifi_settings.h" +#include "WifiGlobals.h" +#include "WifiSettings.h" +#include #include "nvs_flash.h" -#include "log.h" -#include "hash.h" -#include "check.h" -#include "secure.h" +#include "Log.h" +#include "Hash.h" +#include "Check.h" +#include "Crypt.h" #define TAG "wifi_settings" #define TT_NVS_NAMESPACE "wifi_settings" // limited by NVS_KEY_NAME_MAX_SIZE // region Wi-Fi Credentials - static -static esp_err_t tt_wifi_credentials_nvs_open(nvs_handle_t* handle, nvs_open_mode_t mode) { +static esp_err_t credentials_nvs_open(nvs_handle_t* handle, nvs_open_mode_t mode) { return nvs_open(TT_NVS_NAMESPACE, NVS_READWRITE, handle); } -static void tt_wifi_credentials_nvs_close(nvs_handle_t handle) { +static void credentials_nvs_close(nvs_handle_t handle) { nvs_close(handle); } // endregion Wi-Fi Credentials - static // region Wi-Fi Credentials - public +namespace tt::service::wifi::settings { -bool tt_wifi_settings_contains(const char* ssid) { +bool contains(const char* ssid) { nvs_handle_t handle; - esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READONLY); + esp_err_t result = credentials_nvs_open(&handle, NVS_READONLY); if (result != ESP_OK) { TT_LOG_E(TAG, "Failed to open NVS handle: %s", esp_err_to_name(result)); return false; } bool key_exists = nvs_find_key(handle, ssid, NULL) == ESP_OK; - tt_wifi_credentials_nvs_close(handle); + credentials_nvs_close(handle); return key_exists; } -void tt_wifi_settings_init() { - TT_LOG_I(TAG, "init"); - static_assert(strlen(TT_NVS_NAMESPACE) <= NVS_KEY_NAME_MAX_SIZE, "Namespace name too long"); -} - -bool tt_wifi_settings_load(const char* ssid, WifiApSettings* settings) { +bool load(const char* ssid, WifiApSettings* settings) { nvs_handle_t handle; - esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READONLY); + esp_err_t result = credentials_nvs_open(&handle, NVS_READONLY); if (result != ESP_OK) { TT_LOG_E(TAG, "Failed to open NVS handle: %s", esp_err_to_name(result)); return false; @@ -58,8 +55,8 @@ bool tt_wifi_settings_load(const char* ssid, WifiApSettings* settings) { result = nvs_get_blob(handle, ssid, &encrypted_settings, &length); uint8_t iv[16]; - tt_secure_get_iv_from_string(ssid, iv); - int decrypt_result = tt_secure_decrypt( + crypt::get_iv_from_string(ssid, iv); + int decrypt_result = crypt::decrypt( iv, (uint8_t*)encrypted_settings.password, (uint8_t*)settings->password, @@ -77,7 +74,7 @@ bool tt_wifi_settings_load(const char* ssid, WifiApSettings* settings) { TT_LOG_E(TAG, "Failed to get credentials for \"%s\": %s", ssid, esp_err_to_name(result)); } - tt_wifi_credentials_nvs_close(handle); + credentials_nvs_close(handle); settings->auto_connect = encrypted_settings.auto_connect; strcpy((char*)settings->ssid, encrypted_settings.ssid); @@ -85,9 +82,9 @@ bool tt_wifi_settings_load(const char* ssid, WifiApSettings* settings) { return result == ESP_OK; } -bool tt_wifi_settings_save(const WifiApSettings* settings) { +bool save(const WifiApSettings* settings) { nvs_handle_t handle; - esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READWRITE); + esp_err_t result = credentials_nvs_open(&handle, NVS_READWRITE); if (result != ESP_OK) { TT_LOG_E(TAG, "Failed to open NVS handle: %s", esp_err_to_name(result)); return false; @@ -101,8 +98,8 @@ bool tt_wifi_settings_save(const WifiApSettings* settings) { encrypted_settings.password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT] = 0; uint8_t iv[16]; - tt_secure_get_iv_from_data(settings->ssid, strlen(settings->ssid), iv); - int encrypt_result = tt_secure_encrypt( + crypt::get_iv_from_data(settings->ssid, strlen(settings->ssid), iv); + int encrypt_result = crypt::encrypt( iv, (uint8_t*)settings->password, (uint8_t*)encrypted_settings.password, @@ -121,13 +118,13 @@ bool tt_wifi_settings_save(const WifiApSettings* settings) { } } - tt_wifi_credentials_nvs_close(handle); + credentials_nvs_close(handle); return result == ESP_OK; } -bool tt_wifi_settings_remove(const char* ssid) { +bool remove(const char* ssid) { nvs_handle_t handle; - esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READWRITE); + esp_err_t result = credentials_nvs_open(&handle, NVS_READWRITE); if (result != ESP_OK) { TT_LOG_E(TAG, "Failed to open NVS handle to store \"%s\": %s", ssid, esp_err_to_name(result)); return false; @@ -138,10 +135,12 @@ bool tt_wifi_settings_remove(const char* ssid) { TT_LOG_E(TAG, "Failed to erase credentials for \"%s\": %s", ssid, esp_err_to_name(result)); } - tt_wifi_credentials_nvs_close(handle); + credentials_nvs_close(handle); return result == ESP_OK; } // end region Wi-Fi Credentials - public +} // nemespace + #endif // ESP_TARGET \ No newline at end of file diff --git a/TactilityHeadless/Source/Services/Wifi/WifiSettingsMock.cpp b/TactilityHeadless/Source/Services/Wifi/WifiSettingsMock.cpp new file mode 100644 index 00000000..1325f505 --- /dev/null +++ b/TactilityHeadless/Source/Services/Wifi/WifiSettingsMock.cpp @@ -0,0 +1,28 @@ +#ifndef ESP_TARGET + +#include "WifiSettings.h" +#include "Log.h" + +namespace tt::service::wifi::settings { + +#define TAG "wifi_settings_mock" + +bool contains(const char* ssid) { + return false; +} + +bool load(const char* ssid, WifiApSettings* settings) { + return false; +} + +bool save(const WifiApSettings* settings) { + return false; +} + +bool remove(const char* ssid) { + return false; +} + +} // namespace + +#endif // ESP_TARGET \ No newline at end of file diff --git a/TactilityHeadless/Source/TactilityHeadless.cpp b/TactilityHeadless/Source/TactilityHeadless.cpp new file mode 100644 index 00000000..dde90e50 --- /dev/null +++ b/TactilityHeadless/Source/TactilityHeadless.cpp @@ -0,0 +1,47 @@ +#include "TactilityHeadless.h" +#include "Hal/Configuration.h" +#include "Hal/Hal_i.h" +#include "ServiceManifest.h" +#include "ServiceRegistry.h" + +#ifdef ESP_PLATFORM +#include "EspInit.h" +#endif + +namespace tt { + +#define TAG "tactility" + +namespace service::wifi { extern const ServiceManifest manifest; } +namespace service::sdcard { extern const ServiceManifest manifest; } + +static const ServiceManifest* const system_services[] = { + &service::sdcard::manifest, + &service::wifi::manifest +}; + +static const hal::Configuration* hardwareConfig = nullptr; + +static void register_and_start_system_services() { + TT_LOG_I(TAG, "Registering and starting system services"); + int app_count = sizeof(system_services) / sizeof(ServiceManifest*); + for (int i = 0; i < app_count; ++i) { + service_registry_add(system_services[i]); + tt_check(service_registry_start(system_services[i]->id)); + } +} + +void headless_init(const hal::Configuration* config) { +#ifdef ESP_PLATFORM + esp_init(); +#endif + hardwareConfig = config; + hal::init(config); + register_and_start_system_services(); +} + +const hal::Configuration* get_hardware_config() { + return hardwareConfig; +} + +} // namespace diff --git a/TactilityHeadless/Source/TactilityHeadless.h b/TactilityHeadless/Source/TactilityHeadless.h new file mode 100644 index 00000000..3ef41d5d --- /dev/null +++ b/TactilityHeadless/Source/TactilityHeadless.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Hal/Configuration.h" +#include "TactilityHeadlessConfig.h" + +namespace tt { + +void headless_init(const hal::Configuration* config); + +const hal::Configuration* get_hardware_config(); + +} // namespace diff --git a/tactility-headless/src/tactility_headless_config.h b/TactilityHeadless/Source/TactilityHeadlessConfig.h similarity index 100% rename from tactility-headless/src/tactility_headless_config.h rename to TactilityHeadless/Source/TactilityHeadlessConfig.h diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt new file mode 100644 index 00000000..d53c5025 --- /dev/null +++ b/Tests/CMakeLists.txt @@ -0,0 +1,9 @@ +project(tests) + +set(DOCTESTINC ${PROJECT_SOURCE_DIR}/Include) + +enable_testing() +add_subdirectory(TactilityCore) + +add_custom_target(build-tests) +add_dependencies(build-tests TactilityCoreTests) diff --git a/tests/include/doctest.h b/Tests/Include/doctest.h similarity index 100% rename from tests/include/doctest.h rename to Tests/Include/doctest.h diff --git a/Tests/TactilityCore/BundleTest.cpp b/Tests/TactilityCore/BundleTest.cpp new file mode 100644 index 00000000..e0eeae61 --- /dev/null +++ b/Tests/TactilityCore/BundleTest.cpp @@ -0,0 +1,49 @@ +#include "doctest.h" +#include "Bundle.h" + +using namespace tt; + +TEST_CASE("boolean can be stored and retrieved") { + Bundle bundle; + bundle.putBool("key", true); + CHECK(bundle.hasBool("key")); + CHECK(bundle.getBool("key")); + bool opt_result = false; + CHECK(bundle.optBool("key", opt_result)); + CHECK_EQ(opt_result, true); +} + +TEST_CASE("int32 can be stored and retrieved") { + Bundle bundle; + bundle.putInt32("key", true); + CHECK(bundle.hasInt32("key")); + CHECK(bundle.getInt32("key")); + int32_t opt_result = false; + CHECK(bundle.optInt32("key", opt_result)); + CHECK_EQ(opt_result, true); +} + +TEST_CASE("string can be stored and retrieved") { + Bundle bundle; + bundle.putString("key", "test"); + CHECK(bundle.hasString("key")); + CHECK_EQ(bundle.getString("key"), "test"); + std::string opt_result; + CHECK(bundle.optString("key", opt_result)); + CHECK_EQ(opt_result, "test"); +} + +TEST_CASE("bundle copy makes an actual copy") { + auto* original_ptr = new Bundle(); + Bundle& original = *original_ptr; + original.putBool("bool", true); + original.putInt32("int32", 123); + original.putString("string", "text"); + + Bundle copy = original; + delete original_ptr; + + CHECK_EQ(copy.getBool("bool"), true); + CHECK_EQ(copy.getInt32("int32"), 123); + CHECK_EQ(copy.getString("string"), "text"); +} diff --git a/Tests/TactilityCore/CMakeLists.txt b/Tests/TactilityCore/CMakeLists.txt new file mode 100644 index 00000000..a81fd687 --- /dev/null +++ b/Tests/TactilityCore/CMakeLists.txt @@ -0,0 +1,21 @@ +project(TactilityCoreTests) + +enable_language(C CXX ASM) + +set(CMAKE_CXX_COMPILER g++) + +file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/*.cpp) +add_executable(TactilityCoreTests EXCLUDE_FROM_ALL ${TEST_SOURCES}) + +target_include_directories(TactilityCoreTests PRIVATE + ${DOCTESTINC} +) + +add_test(NAME TactilityCoreTests + COMMAND TactilityCoreTests +) + +target_link_libraries(TactilityCoreTests + PUBLIC TactilityCore + freertos_kernel +) diff --git a/Tests/TactilityCore/DispatcherTest.cpp b/Tests/TactilityCore/DispatcherTest.cpp new file mode 100644 index 00000000..33597e51 --- /dev/null +++ b/Tests/TactilityCore/DispatcherTest.cpp @@ -0,0 +1,36 @@ +#include "doctest.h" +#include "TactilityCore.h" +#include "Dispatcher.h" + +using namespace tt; + +void increment_callback(void* context) { + auto* counter = (uint32_t*)context; + (*counter)++; +} + +TEST_CASE("dispatcher should not call callback if consume isn't called") { + Dispatcher dispatcher; + + uint32_t counter = 0; + dispatcher.dispatch(&increment_callback, &counter); + delay_tick(10); + + CHECK_EQ(counter, 0); +} + +TEST_CASE("dispatcher should be able to dealloc when message is not consumed") { + auto* dispatcher = new Dispatcher(); + uint32_t counter = 0; + dispatcher->dispatch(increment_callback, &counter); + delete dispatcher; +} + +TEST_CASE("dispatcher should call callback when consume is called") { + Dispatcher dispatcher; + + uint32_t counter = 0; + dispatcher.dispatch(increment_callback, &counter); + dispatcher.consume(100); + CHECK_EQ(counter, 1); +} diff --git a/tests/tactility-core/main.cpp b/Tests/TactilityCore/Main.cpp similarity index 96% rename from tests/tactility-core/main.cpp rename to Tests/TactilityCore/Main.cpp index 91b88422..c1dd29aa 100644 --- a/tests/tactility-core/main.cpp +++ b/Tests/TactilityCore/Main.cpp @@ -27,7 +27,7 @@ void test_task(void* parameter) { vTaskEndScheduler(); } - vTaskDelete(NULL); + vTaskDelete(nullptr); } int main(int argc, char** argv) { @@ -43,7 +43,7 @@ int main(int argc, char** argv) { 8192, &data, 1, - NULL + nullptr ); assert(task_result == pdPASS); diff --git a/Tests/TactilityCore/MessageQueueTest.cpp b/Tests/TactilityCore/MessageQueueTest.cpp new file mode 100644 index 00000000..5c3fc6fb --- /dev/null +++ b/Tests/TactilityCore/MessageQueueTest.cpp @@ -0,0 +1,86 @@ +#include "doctest.h" +#include "MessageQueue.h" + +using namespace tt; + +TEST_CASE("message queue capacity should be correct") { + MessageQueue queue(10, 1); + + uint32_t capacity = queue.getCapacity(); + CHECK_EQ(capacity, 10); +} + +TEST_CASE("message queue initial count should be 0") { + MessageQueue queue(10, 1); + + uint32_t count = queue.getCount(); + CHECK_EQ(count, 0); +} + +TEST_CASE("message queue message size should be correct") { + MessageQueue queue(1, 123); + + uint32_t message_size = queue.getMessageSize(); + CHECK_EQ(message_size, 123); +} + +TEST_CASE("message queue count should increase when message is added") { + MessageQueue queue(10, sizeof(uint32_t)); + + uint32_t message = 123; + queue.put(&message, 100); + uint32_t count = queue.getCount(); + CHECK_EQ(count, 1); +} + +TEST_CASE("message queue count should be 0 when message is added and queue is reset") { + MessageQueue queue(10, sizeof(uint32_t)); + + uint32_t message = 123; + + queue.put(&message, 100); + queue.reset(); + uint32_t count = queue.getCount(); + CHECK_EQ(count, 0); +} + +TEST_CASE("message queue consumption should work") { + MessageQueue queue(10, sizeof(uint32_t)); + + uint32_t out_message = 123; + queue.put(&out_message, 100); + + uint32_t in_message = 0; + queue.get(&in_message, 100); + CHECK_EQ(in_message, 123); +} + +TEST_CASE("message queue count should decrease when message is consumed") { + MessageQueue queue(10, sizeof(uint32_t)); + + uint32_t out_message = 123; + queue.put(&out_message, 100); + + uint32_t in_message = 0; + queue.get(&in_message, 100); + uint32_t count = queue.getCount(); + CHECK_EQ(count, 0); +} + +TEST_CASE("message queue should make copy of data") { + // Given a number that we can later delete + MessageQueue queue(1, sizeof(int32_t)); + const int32_t test_value = 123; + auto* number = new int32_t(); + *number = test_value; + + // When we put the number in the queue and then delete it + queue.put(number, 100); + delete number; + + // We want to verify that the value was copied into the queue and retrieved properly + int32_t queue_number = 0; + CHECK_EQ(queue.get(&queue_number, 100), TtStatusOk); + CHECK_EQ(queue_number, test_value); +} + diff --git a/Tests/TactilityCore/MutexTest.cpp b/Tests/TactilityCore/MutexTest.cpp new file mode 100644 index 00000000..9291e8b7 --- /dev/null +++ b/Tests/TactilityCore/MutexTest.cpp @@ -0,0 +1,36 @@ +#include "doctest.h" +#include "TactilityCore.h" +#include "Mutex.h" + +using namespace tt; + +static int thread_with_mutex_parameter(void* parameter) { + auto* mutex = (Mutex*)parameter; + tt_mutex_acquire(mutex, TtWaitForever); + return 0; +} + +TEST_CASE("a mutex can block a thread") { + auto* mutex = tt_mutex_alloc(MutexTypeNormal); + tt_mutex_acquire(mutex, TtWaitForever); + + Thread* thread = thread_alloc_ex( + "thread", + 1024, + &thread_with_mutex_parameter, + mutex + ); + thread_start(thread); + + delay_ms(5); + CHECK_EQ(thread_get_state(thread), ThreadStateRunning); + + tt_mutex_release(mutex); + + delay_ms(5); + CHECK_EQ(thread_get_state(thread), ThreadStateStopped); + + thread_join(thread); + thread_free(thread); + tt_mutex_free(mutex); +} diff --git a/tests/tactility-core/thread_test.cpp b/Tests/TactilityCore/ThreadTest.cpp similarity index 59% rename from tests/tactility-core/thread_test.cpp rename to Tests/TactilityCore/ThreadTest.cpp index 24d6fa5c..3847aa89 100644 --- a/tests/tactility-core/thread_test.cpp +++ b/Tests/TactilityCore/ThreadTest.cpp @@ -1,11 +1,13 @@ #include "doctest.h" -#include "tactility_core.h" -#include "thread.h" +#include "TactilityCore.h" +#include "Thread.h" + +using namespace tt; static int interruptable_thread(void* parameter) { bool* interrupted = (bool*)parameter; while (!*interrupted) { - tt_delay_ms(5); + delay_ms(5); } return 0; } @@ -23,79 +25,79 @@ static int thread_with_return_code(void* parameter) { TEST_CASE("when a thread is started then its callback should be called") { bool has_called = false; - Thread* thread = tt_thread_alloc_ex( + auto* thread = thread_alloc_ex( "immediate return task", 4096, &immediate_return_thread, &has_called ); CHECK(!has_called); - tt_thread_start(thread); - tt_thread_join(thread); - tt_thread_free(thread); + thread_start(thread); + thread_join(thread); + thread_free(thread); CHECK(has_called); } TEST_CASE("a thread can be started and stopped") { bool interrupted = false; - Thread* thread = tt_thread_alloc_ex( + auto* thread = thread_alloc_ex( "interruptable thread", 4096, &interruptable_thread, &interrupted ); CHECK(thread); - tt_thread_start(thread); + thread_start(thread); interrupted = true; - tt_thread_join(thread); - tt_thread_free(thread); + thread_join(thread); + thread_free(thread); } TEST_CASE("thread id should only be set at when thread is started") { bool interrupted = false; - Thread* thread = tt_thread_alloc_ex( + auto* thread = thread_alloc_ex( "interruptable thread", 4096, &interruptable_thread, &interrupted ); - CHECK(tt_thread_get_id(thread) == NULL); - tt_thread_start(thread); - CHECK(tt_thread_get_id(thread) != NULL); + CHECK_EQ(thread_get_id(thread), nullptr); + thread_start(thread); + CHECK_NE(thread_get_id(thread), nullptr); interrupted = true; - tt_thread_join(thread); - CHECK(tt_thread_get_id(thread) == NULL); - tt_thread_free(thread); + thread_join(thread); + CHECK_EQ(thread_get_id(thread), nullptr); + thread_free(thread); } TEST_CASE("thread state should be correct") { bool interrupted = false; - Thread* thread = tt_thread_alloc_ex( + auto* thread = thread_alloc_ex( "interruptable thread", 4096, &interruptable_thread, &interrupted ); - CHECK_EQ(tt_thread_get_state(thread), ThreadStateStopped); - tt_thread_start(thread); - ThreadState state = tt_thread_get_state(thread); + CHECK_EQ(thread_get_state(thread), ThreadStateStopped); + thread_start(thread); + ThreadState state = thread_get_state(thread); CHECK((state == ThreadStateStarting || state == ThreadStateRunning)); interrupted = true; - tt_thread_join(thread); - CHECK_EQ(tt_thread_get_state(thread), ThreadStateStopped); - tt_thread_free(thread); + thread_join(thread); + CHECK_EQ(thread_get_state(thread), ThreadStateStopped); + thread_free(thread); } TEST_CASE("thread id should only be set at when thread is started") { int code = 123; - Thread* thread = tt_thread_alloc_ex( + auto* thread = thread_alloc_ex( "return code", 4096, &thread_with_return_code, &code ); - tt_thread_start(thread); - tt_thread_join(thread); - CHECK_EQ(tt_thread_get_return_code(thread), code); - tt_thread_free(thread); + thread_start(thread); + thread_join(thread); + CHECK_EQ(thread_get_return_code(thread), code); + thread_free(thread); } diff --git a/Tests/TactilityCore/TimerTest.cpp b/Tests/TactilityCore/TimerTest.cpp new file mode 100644 index 00000000..58e9e335 --- /dev/null +++ b/Tests/TactilityCore/TimerTest.cpp @@ -0,0 +1,64 @@ +#include "doctest.h" +#include "TactilityCore.h" +#include "Timer.h" + +using namespace tt; + +void* timer_callback_context = NULL; +static void timer_callback_with_context(void* context) { + timer_callback_context = context; +} + +static void timer_callback_with_counter(void* context) { + int* int_ptr = (int*)context; + (*int_ptr)++; +} + +TEST_CASE("a timer passes the context correctly") { + int foo = 1; + auto* timer = timer_alloc(&timer_callback_with_context, TimerTypeOnce, &foo); + timer_start(timer, 1); + delay_tick(10); + timer_stop(timer); + timer_free(timer); + + CHECK_EQ(timer_callback_context, &foo); +} + +TEST_CASE("TimerTypePeriodic timers can be stopped and restarted") { + int counter = 0; + auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypePeriodic, &counter); + timer_start(timer, 1); + delay_tick(10); + timer_stop(timer); + delay_tick(10); + timer_stop(timer); + timer_free(timer); + + CHECK_GE(counter, 2); +} + +TEST_CASE("TimerTypePeriodic calls the callback periodically") { + int counter = 0; + int ticks_to_run = 10; + auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypePeriodic, &counter); + timer_start(timer, 1); + delay_tick(ticks_to_run); + timer_stop(timer); + timer_free(timer); + + CHECK_EQ(counter, ticks_to_run); +} + +TEST_CASE("restarting TimerTypeOnce timers has no effect") { + int counter = 0; + auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypeOnce, &counter); + timer_start(timer, 1); + delay_tick(10); + timer_stop(timer); + delay_tick(10); + timer_stop(timer); + timer_free(timer); + + CHECK_EQ(counter, 1); +} diff --git a/app-esp/src/hello_world/hello_world.c b/app-esp/src/hello_world/hello_world.c deleted file mode 100644 index 16813bc3..00000000 --- a/app-esp/src/hello_world/hello_world.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "hello_world.h" -#include "lvgl.h" -#include "ui/toolbar.h" - -static void app_show(App app, lv_obj_t* parent) { - lv_obj_t* toolbar = tt_toolbar_create_for_app(parent, app); - lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); - - lv_obj_t* label = lv_label_create(parent); - lv_label_set_text(label, "Hello, world!"); - lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); -} - -const AppManifest hello_world_app = { - .id = "helloworld", - .name = "Hello World", - .icon = NULL, - .type = AppTypeUser, - .on_start = NULL, - .on_stop = NULL, - .on_show = &app_show, - .on_hide = NULL -}; diff --git a/app-esp/src/hello_world/hello_world.h b/app-esp/src/hello_world/hello_world.h deleted file mode 100644 index f2563e22..00000000 --- a/app-esp/src/hello_world/hello_world.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "app_manifest.h" - -extern const AppManifest hello_world_app; diff --git a/app-esp/src/main.c b/app-esp/src/main.c deleted file mode 100644 index 767a3ebf..00000000 --- a/app-esp/src/main.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "board_config.h" - -// Apps -#include "hello_world/hello_world.h" -#include "tactility.h" - -extern void wifi_main(void*); - -void app_main(void) { - static const Config config = { - /** - * Auto-select a board based on the ./sdkconfig.board.* file - * that you copied to ./sdkconfig before you opened this project. - */ - .hardware = TT_BOARD_HARDWARE, - .apps = { - &hello_world_app, - }, - .services = {}, - .auto_start_app_id = NULL - }; - - tt_init(&config); - - wifi_main(NULL); -} diff --git a/app-sim/CMakeLists.txt b/app-sim/CMakeLists.txt deleted file mode 100644 index 49c24c92..00000000 --- a/app-sim/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -file(GLOB_RECURSE SOURCES "src/*.c") -add_executable(app-sim ${SOURCES}) -target_link_libraries(app-sim - PRIVATE tactility - PRIVATE tactility-core - PRIVATE tactility-headless - PRIVATE lvgl -) - -find_package(SDL2 REQUIRED CONFIG) -target_link_libraries(app-sim PRIVATE ${SDL2_LIBRARIES}) -include_directories(${SDL2_INCLUDE_DIRS}) - -add_definitions(-D_Nullable=) -add_definitions(-D_Nonnull=) diff --git a/app-sim/src/hello_world/hello_world.c b/app-sim/src/hello_world/hello_world.c deleted file mode 100644 index 0f057b08..00000000 --- a/app-sim/src/hello_world/hello_world.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "hello_world.h" -#include "services/loader/loader.h" -#include "ui/toolbar.h" - -static void app_show(App app, lv_obj_t* parent) { - lv_obj_t* toolbar = tt_toolbar_create_for_app(parent, app); - lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); - - lv_obj_t* label = lv_label_create(parent); - lv_label_set_text(label, "Hello, world!"); - lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); -} - -const AppManifest hello_world_app = { - .id = "helloworld", - .name = "Hello World", - .icon = NULL, - .type = AppTypeUser, - .on_start = NULL, - .on_stop = NULL, - .on_show = &app_show, - .on_hide = NULL -}; diff --git a/app-sim/src/hello_world/hello_world.h b/app-sim/src/hello_world/hello_world.h deleted file mode 100644 index f2563e22..00000000 --- a/app-sim/src/hello_world/hello_world.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "app_manifest.h" - -extern const AppManifest hello_world_app; diff --git a/app-sim/src/lvgl_hal.h b/app-sim/src/lvgl_hal.h deleted file mode 100644 index 50f21c33..00000000 --- a/app-sim/src/lvgl_hal.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -lv_display_t* lvgl_hal_init(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/app-sim/src/main.c b/app-sim/src/main.c deleted file mode 100644 index 273939a5..00000000 --- a/app-sim/src/main.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "hello_world/hello_world.h" -#include "tactility.h" - -extern const HardwareConfig sim_hardware; - -void app_main() { - static const Config config = { - .hardware = &sim_hardware, - .apps = { - &hello_world_app - }, - .services = {}, - .auto_start_app_id = NULL - }; - - tt_init(&config); -} diff --git a/boards/lilygo_tdeck/display_i.h b/boards/lilygo_tdeck/display_i.h deleted file mode 100644 index e753b153..00000000 --- a/boards/lilygo_tdeck/display_i.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "lvgl.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -lv_display_t* tdeck_display_init(); - -bool tdeck_backlight_init(); - -void tdeck_backlight_set(uint8_t duty); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/boards/lilygo_tdeck/lilygo_tdeck.h b/boards/lilygo_tdeck/lilygo_tdeck.h deleted file mode 100644 index f8b795c4..00000000 --- a/boards/lilygo_tdeck/lilygo_tdeck.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "hardware_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern const HardwareConfig lilygo_tdeck; - -#ifdef __cplusplus -} -#endif diff --git a/boards/m5stack_core2/CMakeLists.txt b/boards/m5stack_core2/CMakeLists.txt deleted file mode 100644 index 2dba219b..00000000 --- a/boards/m5stack_core2/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -idf_component_register( - SRC_DIRS "source" - INCLUDE_DIRS "include" - PRIV_INCLUDE_DIRS "private" - REQUIRES tactility m5stack_shared vfs fatfs M5Unified -) diff --git a/boards/m5stack_core2/include/m5stack_core2.h b/boards/m5stack_core2/include/m5stack_core2.h deleted file mode 100644 index dac57a84..00000000 --- a/boards/m5stack_core2/include/m5stack_core2.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "hardware_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern const HardwareConfig m5stack_core2; - -#ifdef __cplusplus -} -#endif diff --git a/boards/m5stack_core2/private/config.h b/boards/m5stack_core2/private/config.h deleted file mode 100644 index 2f5ce29f..00000000 --- a/boards/m5stack_core2/private/config.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "driver/spi_common.h" -#include "driver/gpio.h" - -// SD Card -#define CORE2_SDCARD_SPI_HOST SPI2_HOST -#define CORE2_SDCARD_PIN_CS GPIO_NUM_4 -#define CORE2_SDCARD_SPI_FREQUENCY 800000U -#define CORE2_SDCARD_FORMAT_ON_MOUNT_FAILED false -#define CORE2_SDCARD_MAX_OPEN_FILES 4 -#define CORE2_SDCARD_ALLOC_UNIT_SIZE (16 * 1024) -#define CORE2_SDCARD_STATUS_CHECK_ENABLED false diff --git a/boards/m5stack_cores3/CMakeLists.txt b/boards/m5stack_cores3/CMakeLists.txt deleted file mode 100644 index 2dba219b..00000000 --- a/boards/m5stack_cores3/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -idf_component_register( - SRC_DIRS "source" - INCLUDE_DIRS "include" - PRIV_INCLUDE_DIRS "private" - REQUIRES tactility m5stack_shared vfs fatfs M5Unified -) diff --git a/boards/m5stack_cores3/include/m5stack_cores3.h b/boards/m5stack_cores3/include/m5stack_cores3.h deleted file mode 100644 index d13058cb..00000000 --- a/boards/m5stack_cores3/include/m5stack_cores3.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "hardware_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern const HardwareConfig m5stack_cores3; - -#ifdef __cplusplus -} -#endif diff --git a/boards/m5stack_shared/CMakeLists.txt b/boards/m5stack_shared/CMakeLists.txt deleted file mode 100644 index 2822db28..00000000 --- a/boards/m5stack_shared/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "src" - INCLUDE_DIRS "src" - REQUIRES tactility esp_lvgl_port M5Unified -) diff --git a/boards/m5stack_shared/src/m5stack_shared.h b/boards/m5stack_shared/src/m5stack_shared.h deleted file mode 100644 index bde4aa29..00000000 --- a/boards/m5stack_shared/src/m5stack_shared.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "power.h" -#include "sdcard.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern bool m5stack_lvgl_init(); -extern bool m5stack_bootstrap(); - -extern Power m5stack_power; - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/boards/waveshare_s3_touch/bootstrap_i.h b/boards/waveshare_s3_touch/bootstrap_i.h deleted file mode 100644 index 248c45f5..00000000 --- a/boards/waveshare_s3_touch/bootstrap_i.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -bool ws3t_bootstrap(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/boards/waveshare_s3_touch/lvgl_i.h b/boards/waveshare_s3_touch/lvgl_i.h deleted file mode 100644 index a505a217..00000000 --- a/boards/waveshare_s3_touch/lvgl_i.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -bool ws3t_init_lvgl(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/boards/waveshare_s3_touch/touch_i.h b/boards/waveshare_s3_touch/touch_i.h deleted file mode 100644 index 0db1e98b..00000000 --- a/boards/waveshare_s3_touch/touch_i.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "lvgl.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void ws3t_touch_init(lv_display_t* display); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/boards/waveshare_s3_touch/waveshare_s3_touch.c b/boards/waveshare_s3_touch/waveshare_s3_touch.c deleted file mode 100644 index 15ae87e8..00000000 --- a/boards/waveshare_s3_touch/waveshare_s3_touch.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "waveshare_s3_touch.h" - -#include "lvgl_i.h" - -bool ws3t_bootstrap(); - -const HardwareConfig waveshare_s3_touch = { - .bootstrap = &ws3t_bootstrap, - .display = { - .set_backlight_duty = NULL // TODO: This requires implementing the CH422G IO expander - }, - .init_graphics = &ws3t_init_lvgl, - .sdcard = NULL, - .power = NULL -}; diff --git a/boards/waveshare_s3_touch/waveshare_s3_touch.h b/boards/waveshare_s3_touch/waveshare_s3_touch.h deleted file mode 100644 index 7dbddd67..00000000 --- a/boards/waveshare_s3_touch/waveshare_s3_touch.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "hardware_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Waveshare S3 Touch LCD 4.3 -extern const HardwareConfig waveshare_s3_touch; - -#ifdef __cplusplus -} -#endif diff --git a/boards/yellow_board/display_i.h b/boards/yellow_board/display_i.h deleted file mode 100644 index 1700d88a..00000000 --- a/boards/yellow_board/display_i.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -bool twodotfour_backlight_init(); -void twodotfour_backlight_set(uint8_t duty); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/boards/yellow_board/yellow_board.h b/boards/yellow_board/yellow_board.h deleted file mode 100644 index 88eb813d..00000000 --- a/boards/yellow_board/yellow_board.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "hardware_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Capacitive touch version of the 2.4" yellow board -extern const HardwareConfig yellow_board_24inch_cap; - -#ifdef __cplusplus -} -#endif diff --git a/libs/mlib/CMakeLists.txt b/libs/mlib/CMakeLists.txt deleted file mode 100644 index 3b17cd27..00000000 --- a/libs/mlib/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -add_library(mlib INTERFACE "") # No sources -target_include_directories(mlib INTERFACE .) - diff --git a/libs/mlib/LICENSE b/libs/mlib/LICENSE deleted file mode 100644 index 27766ef5..00000000 --- a/libs/mlib/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -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/libs/mlib/README.md b/libs/mlib/README.md deleted file mode 100644 index 85a39303..00000000 --- a/libs/mlib/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This folder is a partial git clone from: -https://github.com/P-p-H-d/mlib/commit/d9401371a6bc1c0f240161514549976bcdd98999 - diff --git a/libs/mlib/m-algo.h b/libs/mlib/m-algo.h deleted file mode 100644 index 46470bde..00000000 --- a/libs/mlib/m-algo.h +++ /dev/null @@ -1,1240 +0,0 @@ -/* - * 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/libs/mlib/m-array.h b/libs/mlib/m-array.h deleted file mode 100644 index 77bbc7f7..00000000 --- a/libs/mlib/m-array.h +++ /dev/null @@ -1,1132 +0,0 @@ -/* - * 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/libs/mlib/m-atomic.h b/libs/mlib/m-atomic.h deleted file mode 100644 index 54ceaa7c..00000000 --- a/libs/mlib/m-atomic.h +++ /dev/null @@ -1,342 +0,0 @@ -/* - * 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/libs/mlib/m-bitset.h b/libs/mlib/m-bitset.h deleted file mode 100644 index b1ec85c3..00000000 --- a/libs/mlib/m-bitset.h +++ /dev/null @@ -1,981 +0,0 @@ -/* - * 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/libs/mlib/m-bptree.h b/libs/mlib/m-bptree.h deleted file mode 100644 index bd823311..00000000 --- a/libs/mlib/m-bptree.h +++ /dev/null @@ -1,1499 +0,0 @@ -/* - * 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/libs/mlib/m-buffer.h b/libs/mlib/m-buffer.h deleted file mode 100644 index a98630fa..00000000 --- a/libs/mlib/m-buffer.h +++ /dev/null @@ -1,1350 +0,0 @@ -/* - * 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/libs/mlib/m-c-mempool.h b/libs/mlib/m-c-mempool.h deleted file mode 100644 index 6e496294..00000000 --- a/libs/mlib/m-c-mempool.h +++ /dev/null @@ -1,835 +0,0 @@ -/* - * 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/libs/mlib/m-concurrent.h b/libs/mlib/m-concurrent.h deleted file mode 100644 index 0360c39f..00000000 --- a/libs/mlib/m-concurrent.h +++ /dev/null @@ -1,925 +0,0 @@ -/* - * 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/libs/mlib/m-core.h b/libs/mlib/m-core.h deleted file mode 100644 index 2b3b0423..00000000 --- a/libs/mlib/m-core.h +++ /dev/null @@ -1,5297 +0,0 @@ -/* - * 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/libs/mlib/m-deque.h b/libs/mlib/m-deque.h deleted file mode 100644 index f8742e71..00000000 --- a/libs/mlib/m-deque.h +++ /dev/null @@ -1,1147 +0,0 @@ -/* - * 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/libs/mlib/m-dict.h b/libs/mlib/m-dict.h deleted file mode 100644 index 61f086b8..00000000 --- a/libs/mlib/m-dict.h +++ /dev/null @@ -1,1988 +0,0 @@ -/* - * 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/libs/mlib/m-funcobj.h b/libs/mlib/m-funcobj.h deleted file mode 100644 index 7b5401e8..00000000 --- a/libs/mlib/m-funcobj.h +++ /dev/null @@ -1,415 +0,0 @@ -/* - * 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/libs/mlib/m-genint.h b/libs/mlib/m-genint.h deleted file mode 100644 index b688f241..00000000 --- a/libs/mlib/m-genint.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * 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/libs/mlib/m-i-list.h b/libs/mlib/m-i-list.h deleted file mode 100644 index 9ce955d6..00000000 --- a/libs/mlib/m-i-list.h +++ /dev/null @@ -1,679 +0,0 @@ -/* - * 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/libs/mlib/m-i-shared.h b/libs/mlib/m-i-shared.h deleted file mode 100644 index f81fa555..00000000 --- a/libs/mlib/m-i-shared.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * 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/libs/mlib/m-list.h b/libs/mlib/m-list.h deleted file mode 100644 index 7a79b9a9..00000000 --- a/libs/mlib/m-list.h +++ /dev/null @@ -1,1527 +0,0 @@ -/* - * 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/libs/mlib/m-mempool.h b/libs/mlib/m-mempool.h deleted file mode 100644 index f36a152d..00000000 --- a/libs/mlib/m-mempool.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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/libs/mlib/m-mutex.h b/libs/mlib/m-mutex.h deleted file mode 100644 index 6fc34eba..00000000 --- a/libs/mlib/m-mutex.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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/libs/mlib/m-prioqueue.h b/libs/mlib/m-prioqueue.h deleted file mode 100644 index 4c79c0a6..00000000 --- a/libs/mlib/m-prioqueue.h +++ /dev/null @@ -1,520 +0,0 @@ -/* - * 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/libs/mlib/m-rbtree.h b/libs/mlib/m-rbtree.h deleted file mode 100644 index f5880f20..00000000 --- a/libs/mlib/m-rbtree.h +++ /dev/null @@ -1,1186 +0,0 @@ -/* - * 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/libs/mlib/m-serial-bin.h b/libs/mlib/m-serial-bin.h deleted file mode 100644 index 1bf3cea1..00000000 --- a/libs/mlib/m-serial-bin.h +++ /dev/null @@ -1,552 +0,0 @@ -/* - * 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/libs/mlib/m-serial-json.h b/libs/mlib/m-serial-json.h deleted file mode 100644 index 379b2004..00000000 --- a/libs/mlib/m-serial-json.h +++ /dev/null @@ -1,1199 +0,0 @@ -/* - * 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/libs/mlib/m-shared.h b/libs/mlib/m-shared.h deleted file mode 100644 index 8fba531d..00000000 --- a/libs/mlib/m-shared.h +++ /dev/null @@ -1,553 +0,0 @@ -/* - * 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/libs/mlib/m-snapshot.h b/libs/mlib/m-snapshot.h deleted file mode 100644 index 860ce57f..00000000 --- a/libs/mlib/m-snapshot.h +++ /dev/null @@ -1,814 +0,0 @@ -/* - * 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/libs/mlib/m-string.h b/libs/mlib/m-string.h deleted file mode 100644 index 501ac1bb..00000000 --- a/libs/mlib/m-string.h +++ /dev/null @@ -1,2787 +0,0 @@ -/* - * 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/libs/mlib/m-thread.h b/libs/mlib/m-thread.h deleted file mode 100644 index 4a7da750..00000000 --- a/libs/mlib/m-thread.h +++ /dev/null @@ -1,748 +0,0 @@ -/* - * 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/libs/mlib/m-tree.h b/libs/mlib/m-tree.h deleted file mode 100644 index 7b1d9c58..00000000 --- a/libs/mlib/m-tree.h +++ /dev/null @@ -1,1603 +0,0 @@ -/* - * 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/libs/mlib/m-try.h b/libs/mlib/m-try.h deleted file mode 100644 index 504f8d63..00000000 --- a/libs/mlib/m-try.h +++ /dev/null @@ -1,578 +0,0 @@ -/* - * 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/libs/mlib/m-tuple.h b/libs/mlib/m-tuple.h deleted file mode 100644 index 731dc2ab..00000000 --- a/libs/mlib/m-tuple.h +++ /dev/null @@ -1,784 +0,0 @@ -/* - * 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->id); \ - M_EMPLACE_CALL_FUNC(a, init_func, oplist, v->id, 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/libs/mlib/m-variant.h b/libs/mlib/m-variant.h deleted file mode 100644 index 1644bc19..00000000 --- a/libs/mlib/m-variant.h +++ /dev/null @@ -1,819 +0,0 @@ -/* - * 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/libs/mlib/m-worker.h b/libs/mlib/m-worker.h deleted file mode 100644 index 22a59c77..00000000 --- a/libs/mlib/m-worker.h +++ /dev/null @@ -1,698 +0,0 @@ -/* - * 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/run.sh b/run.sh index 640540d4..c73de400 100755 --- a/run.sh +++ b/run.sh @@ -6,7 +6,7 @@ else cmake -S ./ -B build-sim cmake --build build-sim -j 12 cd data - ../build-sim/app-sim/app-sim + ../build-sim/AppSim/AppSim cd - fi diff --git a/runtests.sh b/runtests.sh index e18fee1c..2e3caac9 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,4 +1,4 @@ cmake -S ./ -B build-sim cmake --build build-sim --target build-tests -j 14 -build-sim/tests/tactility-core/tactility-core-tests --exit +build-sim/Tests/TactilityCore/TactilityCoreTests --exit diff --git a/tactility-core/src/api_lock.h b/tactility-core/src/api_lock.h deleted file mode 100644 index abb53d54..00000000 --- a/tactility-core/src/api_lock.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "event_flag.h" - -typedef EventFlag* ApiLock; - -#define TT_API_LOCK_EVENT (1U << 0) - -#define tt_api_lock_alloc_locked() tt_event_flag_alloc() - -#define tt_api_lock_wait_unlock(_lock) \ - tt_event_flag_wait(_lock, TT_API_LOCK_EVENT, TtFlagWaitAny, TtWaitForever) - -#define tt_api_lock_free(_lock) tt_event_flag_free(_lock) - -#define tt_api_lock_unlock(_lock) tt_event_flag_set(_lock, TT_API_LOCK_EVENT) - -#define tt_api_lock_wait_unlock_and_free(_lock) \ - tt_api_lock_wait_unlock(_lock); \ - tt_api_lock_free(_lock); diff --git a/tactility-core/src/bundle.c b/tactility-core/src/bundle.c deleted file mode 100644 index 8ebfc900..00000000 --- a/tactility-core/src/bundle.c +++ /dev/null @@ -1,238 +0,0 @@ -#include "bundle.h" - -#include "m-dict.h" -#include "m_cstr_dup.h" -#include "check.h" - -// region BundleEntry - -typedef enum { - BundleEntryTypeBool, - BundleEntryTypeInt32, - BundleEntryTypeString, -} BundleEntryType; - -typedef struct { - BundleEntryType type; - union { - bool bool_value; - int32_t int32_value; - char* string_ptr; - }; -} BundleEntry; - -BundleEntry* bundle_entry_alloc_bool(bool value) { - BundleEntry* entry = malloc(sizeof(BundleEntry)); - entry->type = BundleEntryTypeBool; - entry->bool_value = value; - return entry; -} - -BundleEntry* bundle_entry_alloc_int32(int32_t value) { - BundleEntry* entry = malloc(sizeof(BundleEntry)); - entry->type = BundleEntryTypeInt32; - entry->int32_value = value; - return entry; -} - -BundleEntry* bundle_entry_alloc_string(const char* text) { - BundleEntry* entry = malloc(sizeof(BundleEntry)); - entry->type = BundleEntryTypeString; - entry->string_ptr = malloc(strlen(text) + 1); - strcpy(entry->string_ptr, text); - return entry; -} - -BundleEntry* bundle_entry_alloc_copy(BundleEntry* source) { - BundleEntry* entry = malloc(sizeof(BundleEntry)); - entry->type = source->type; - if (source->type == BundleEntryTypeString) { - entry->string_ptr = malloc(strlen(source->string_ptr) + 1); - strcpy(entry->string_ptr, source->string_ptr); - } else { - entry->int32_value = source->int32_value; - } - return entry; -} - -void bundle_entry_free(BundleEntry* entry) { - if (entry->type == BundleEntryTypeString) { - free(entry->string_ptr); - } - free(entry); -} - -// endregion BundleEntry - -// region Bundle - -DICT_DEF2(BundleDict, const char*, M_CSTR_DUP_OPLIST, BundleEntry*, M_PTR_OPLIST) - -typedef struct { - BundleDict_t dict; -} BundleData; - -Bundle tt_bundle_alloc() { - BundleData* bundle = malloc(sizeof(BundleData)); - BundleDict_init(bundle->dict); - return bundle; -} - -Bundle tt_bundle_alloc_copy(Bundle source) { - BundleData* source_data = (BundleData*)source; - BundleData* target_data = tt_bundle_alloc(); - - BundleDict_it_t it; - for (BundleDict_it(it, source_data->dict); !BundleDict_end_p(it); BundleDict_next(it)) { - const char* key = BundleDict_cref(it)->key; - BundleEntry* entry = BundleDict_cref(it)->value; - BundleEntry* entry_copy = bundle_entry_alloc_copy(entry); - BundleDict_set_at(target_data->dict, key, entry_copy); - } - - return target_data; -} - -void tt_bundle_free(Bundle bundle) { - BundleData* data = (BundleData*)bundle; - - BundleDict_it_t it; - for (BundleDict_it(it, data->dict); !BundleDict_end_p(it); BundleDict_next(it)) { - bundle_entry_free(BundleDict_cref(it)->value); - } - - BundleDict_clear(data->dict); - free(data); -} - -bool tt_bundle_get_bool(Bundle bundle, const char* key) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry = BundleDict_get(data->dict, key); - tt_check(entry != NULL); - return (*entry)->bool_value; -} - -int32_t tt_bundle_get_int32(Bundle bundle, const char* key) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry = BundleDict_get(data->dict, key); - tt_check(entry != NULL); - return (*entry)->int32_value; -} - -const char* tt_bundle_get_string(Bundle bundle, const char* key) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry = BundleDict_get(data->dict, key); - tt_check(entry != NULL); - return (*entry)->string_ptr; -} - -bool tt_bundle_has_bool(Bundle bundle, const char* key) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry = BundleDict_get(data->dict, key); - return (entry != NULL) && ((*entry)->type == BundleEntryTypeBool); -} - -bool tt_bundle_has_int32(Bundle bundle, const char* key) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry = BundleDict_get(data->dict, key); - return (entry != NULL) && ((*entry)->type == BundleEntryTypeInt32); -} - -bool tt_bundle_has_string(Bundle bundle, const char* key) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry = BundleDict_get(data->dict, key); - return (entry != NULL) && ((*entry)->type == BundleEntryTypeString); -} - -bool tt_bundle_opt_bool(Bundle bundle, const char* key, bool* out) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry_ptr = BundleDict_get(data->dict, key); - if (entry_ptr != NULL) { - BundleEntry* entry = *entry_ptr; - if (entry->type == BundleEntryTypeBool) { - *out = entry->bool_value; - return true; - } else { - return false; - } - } else { - return false; - } -} - -bool tt_bundle_opt_int32(Bundle bundle, const char* key, int32_t* out) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry_ptr = BundleDict_get(data->dict, key); - if (entry_ptr != NULL) { - BundleEntry* entry = *entry_ptr; - if (entry->type == BundleEntryTypeInt32) { - *out = entry->int32_value; - return true; - } else { - return false; - } - } else { - return false; - } -} - -bool tt_bundle_opt_string(Bundle bundle, const char* key, char** out) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry_ptr = BundleDict_get(data->dict, key); - if (entry_ptr != NULL) { - BundleEntry* entry = *entry_ptr; - if (entry->type == BundleEntryTypeString) { - *out = entry->string_ptr; - return true; - } else { - return false; - } - } else { - return false; - } -} - -void tt_bundle_put_bool(Bundle bundle, const char* key, bool value) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry_handle = BundleDict_get(data->dict, key); - if (entry_handle != NULL) { - BundleEntry* entry = *entry_handle; - tt_assert(entry->type == BundleEntryTypeBool); - entry->bool_value = value; - } else { - BundleEntry* entry = bundle_entry_alloc_bool(value); - BundleDict_set_at(data->dict, key, entry); - } -} - -void tt_bundle_put_int32(Bundle bundle, const char* key, int32_t value) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry_handle = BundleDict_get(data->dict, key); - if (entry_handle != NULL) { - BundleEntry* entry = *entry_handle; - tt_assert(entry->type == BundleEntryTypeInt32); - entry->int32_value = value; - } else { - BundleEntry* entry = bundle_entry_alloc_int32(value); - BundleDict_set_at(data->dict, key, entry); - } -} - -void tt_bundle_put_string(Bundle bundle, const char* key, const char* value) { - BundleData* data = (BundleData*)bundle; - BundleEntry** entry_handle = BundleDict_get(data->dict, key); - if (entry_handle != NULL) { - BundleEntry* entry = *entry_handle; - tt_assert(entry->type == BundleEntryTypeString); - if (entry->string_ptr != NULL) { - free(entry->string_ptr); - } - entry->string_ptr = malloc(strlen(value) + 1); - strcpy(entry->string_ptr, value); - } else { - BundleEntry* entry = bundle_entry_alloc_string(value); - BundleDict_set_at(data->dict, key, entry); - } -} - -// endregion Bundle \ No newline at end of file diff --git a/tactility-core/src/bundle.h b/tactility-core/src/bundle.h deleted file mode 100644 index cdcebc47..00000000 --- a/tactility-core/src/bundle.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @brief key-value storage for general purpose. - * Maps strings on a fixed set of data types. - */ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void* Bundle; - -Bundle tt_bundle_alloc(); -Bundle tt_bundle_alloc_copy(Bundle source); -void tt_bundle_free(Bundle bundle); - -bool tt_bundle_get_bool(Bundle bundle, const char* key); -int32_t tt_bundle_get_int32(Bundle bundle, const char* key); -const char* tt_bundle_get_string(Bundle bundle, const char* key); - -bool tt_bundle_has_bool(Bundle bundle, const char* key); -bool tt_bundle_has_int32(Bundle bundle, const char* key); -bool tt_bundle_has_string(Bundle bundle, const char* key); - -bool tt_bundle_opt_bool(Bundle bundle, const char* key, bool* out); -bool tt_bundle_opt_int32(Bundle bundle, const char* key, int32_t* out); -bool tt_bundle_opt_string(Bundle bundle, const char* key, char** out); - -void tt_bundle_put_bool(Bundle bundle, const char* key, bool value); -void tt_bundle_put_int32(Bundle bundle, const char* key, int32_t value); -void tt_bundle_put_string(Bundle bundle, const char* key, const char* value); - -#ifdef __cplusplus -} -#endif diff --git a/tactility-core/src/dispatcher.c b/tactility-core/src/dispatcher.c deleted file mode 100644 index 9d00fa81..00000000 --- a/tactility-core/src/dispatcher.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "dispatcher.h" - -#include "tactility_core.h" - -typedef struct { - Callback callback; - void* context; -} DispatcherMessage; - -typedef struct { - MessageQueue* queue; - Mutex* mutex; - DispatcherMessage buffer; // Buffer for consuming a message -} DispatcherData; - -Dispatcher* tt_dispatcher_alloc(uint32_t message_count) { - DispatcherData* data = malloc(sizeof(DispatcherData)); - *data = (DispatcherData) { - .queue = tt_message_queue_alloc(message_count, sizeof(DispatcherMessage)), - .mutex = tt_mutex_alloc(MutexTypeNormal), - .buffer = { - .callback = NULL, - .context = NULL - } - }; - return data; -} - -void tt_dispatcher_free(Dispatcher* dispatcher) { - DispatcherData* data = (DispatcherData*)dispatcher; - tt_mutex_acquire(data->mutex, TtWaitForever); - tt_message_queue_reset(data->queue); - tt_message_queue_free(data->queue); - tt_mutex_release(data->mutex); - tt_mutex_free(data->mutex); - free(data); -} - -void tt_dispatcher_dispatch(Dispatcher* dispatcher, Callback callback, void* context) { - DispatcherData* data = (DispatcherData*)dispatcher; - DispatcherMessage message = { - .callback = callback, - .context = context - }; - tt_mutex_acquire(data->mutex, TtWaitForever); - tt_message_queue_put(data->queue, &message, TtWaitForever); - tt_mutex_release(data->mutex); -} - -bool tt_dispatcher_consume(Dispatcher* dispatcher, uint32_t timeout_ticks) { - DispatcherData* data = (DispatcherData*)dispatcher; - tt_mutex_acquire(data->mutex, TtWaitForever); - if (tt_message_queue_get(data->queue, &(data->buffer), timeout_ticks) == TtStatusOk) { - DispatcherMessage* message = &(data->buffer); - message->callback(message->context); - tt_mutex_release(data->mutex); - return true; - } else { - tt_mutex_release(data->mutex); - return false; - } -} diff --git a/tactility-core/src/dispatcher.h b/tactility-core/src/dispatcher.h deleted file mode 100644 index 31191e58..00000000 --- a/tactility-core/src/dispatcher.h +++ /dev/null @@ -1,27 +0,0 @@ -/** -* @file dispatcher.h -* -* Dispatcher is a thread-safe code execution queue. -*/ -#pragma once - -#include "message_queue.h" -#include "mutex.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*Callback)(void* data); - -typedef void Dispatcher; - -Dispatcher* tt_dispatcher_alloc(uint32_t message_count); -void tt_dispatcher_free(Dispatcher* dispatcher); -void tt_dispatcher_dispatch(Dispatcher* data, Callback callback, void* context); -bool tt_dispatcher_consume(Dispatcher* data, uint32_t timeout_ticks); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility-core/src/m_cstr_dup.h b/tactility-core/src/m_cstr_dup.h deleted file mode 100644 index 3d257f8a..00000000 --- a/tactility-core/src/m_cstr_dup.h +++ /dev/null @@ -1,28 +0,0 @@ -/** @file:m_cstr_dup.h - * @brief Helpers for mlib - */ -#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/tactility-core/src/message_queue.c b/tactility-core/src/message_queue.c deleted file mode 100644 index 2bec05d7..00000000 --- a/tactility-core/src/message_queue.c +++ /dev/null @@ -1,186 +0,0 @@ -#include "message_queue.h" -#include "check.h" -#include "kernel.h" - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#else -#include "FreeRTOS.h" -#include "queue.h" -#endif - -MessageQueue* tt_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { - tt_assert((tt_kernel_is_irq() == 0U) && (msg_count > 0U) && (msg_size > 0U)); - - QueueHandle_t handle = xQueueCreate(msg_count, msg_size); - tt_check(handle); - - return ((MessageQueue*)handle); -} - -void tt_message_queue_free(MessageQueue* instance) { - tt_assert(tt_kernel_is_irq() == 0U); - tt_assert(instance); - - vQueueDelete((QueueHandle_t)instance); -} - -TtStatus tt_message_queue_put(MessageQueue* instance, const void* msg_ptr, uint32_t timeout) { - QueueHandle_t hQueue = (QueueHandle_t)instance; - TtStatus stat; - BaseType_t yield; - - stat = TtStatusOk; - - if (tt_kernel_is_irq() != 0U) { - if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { - stat = TtStatusErrorParameter; - } else { - yield = pdFALSE; - - if (xQueueSendToBackFromISR(hQueue, msg_ptr, &yield) != pdTRUE) { - stat = TtStatusErrorResource; - } else { - portYIELD_FROM_ISR(yield); - } - } - } else { - if ((hQueue == NULL) || (msg_ptr == NULL)) { - stat = TtStatusErrorParameter; - } else { - if (xQueueSendToBack(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) { - if (timeout != 0U) { - stat = TtStatusErrorTimeout; - } else { - stat = TtStatusErrorResource; - } - } - } - } - - /* Return execution status */ - return (stat); -} - -TtStatus tt_message_queue_get(MessageQueue* instance, void* msg_ptr, uint32_t timeout_ticks) { - QueueHandle_t hQueue = (QueueHandle_t)instance; - TtStatus stat; - BaseType_t yield; - - stat = TtStatusOk; - - if (tt_kernel_is_irq() != 0U) { - if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout_ticks != 0U)) { - stat = TtStatusErrorParameter; - } else { - yield = pdFALSE; - - if (xQueueReceiveFromISR(hQueue, msg_ptr, &yield) != pdPASS) { - stat = TtStatusErrorResource; - } else { - portYIELD_FROM_ISR(yield); - } - } - } else { - if ((hQueue == NULL) || (msg_ptr == NULL)) { - stat = TtStatusErrorParameter; - } else { - if (xQueueReceive(hQueue, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) { - if (timeout_ticks != 0U) { - stat = TtStatusErrorTimeout; - } else { - stat = TtStatusErrorResource; - } - } - } - } - - /* Return execution status */ - return (stat); -} - -uint32_t tt_message_queue_get_capacity(MessageQueue* 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 tt_message_queue_get_message_size(MessageQueue* 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 tt_message_queue_get_count(MessageQueue* instance) { - QueueHandle_t hQueue = (QueueHandle_t)instance; - UBaseType_t count; - - if (hQueue == NULL) { - count = 0U; - } else if (tt_kernel_is_irq() != 0U) { - count = uxQueueMessagesWaitingFromISR(hQueue); - } else { - count = uxQueueMessagesWaiting(hQueue); - } - - /* Return number of queued messages */ - return ((uint32_t)count); -} - -uint32_t tt_message_queue_get_space(MessageQueue* instance) { - StaticQueue_t* mq = (StaticQueue_t*)instance; - uint32_t space; - uint32_t isrm; - - if (mq == NULL) { - space = 0U; - } else if (tt_kernel_is_irq() != 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); -} - -TtStatus tt_message_queue_reset(MessageQueue* instance) { - QueueHandle_t hQueue = (QueueHandle_t)instance; - TtStatus stat; - - if (tt_kernel_is_irq() != 0U) { - stat = TtStatusErrorISR; - } else if (hQueue == NULL) { - stat = TtStatusErrorParameter; - } else { - stat = TtStatusOk; - (void)xQueueReset(hQueue); - } - - /* Return execution status */ - return (stat); -} diff --git a/tactility-core/src/message_queue.h b/tactility-core/src/message_queue.h deleted file mode 100644 index b9145627..00000000 --- a/tactility-core/src/message_queue.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * @file message_queue.h - * - * MessageQueue is a wrapper for FreeRTOS xQueue functionality. - * There is no additional thread-safety on top of the xQueue functionality, - * so make sure you create a lock if needed. - */ -#pragma once - -#include "core_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void MessageQueue; - -/** Allocate message queue - * - * @param[in] msg_count The message count - * @param[in] msg_size The message size - * - * @return pointer to MessageQueue instance - */ -MessageQueue* tt_message_queue_alloc(uint32_t msg_count, uint32_t msg_size); - -/** Free queue - * - * @param instance pointer to MessageQueue instance - */ -void tt_message_queue_free(MessageQueue* instance); - -/** Put message into queue - * - * @param instance pointer to MessageQueue instance - * @param[in] msg_ptr The message pointer - * @param[in] timeout The timeout - * @param[in] msg_prio The message prio - * - * @return The status. - */ -TtStatus tt_message_queue_put(MessageQueue* instance, const void* msg_ptr, uint32_t timeout); - -/** Get message from queue - * - * @param instance pointer to MessageQueue instance - * @param msg_ptr The message pointer - * @param msg_prio The message prioority - * @param[in] timeout_ticks The timeout - * - * @return The status. - */ -TtStatus tt_message_queue_get(MessageQueue* instance, void* msg_ptr, uint32_t timeout_ticks); - -/** Get queue capacity - * - * @param instance pointer to MessageQueue instance - * - * @return capacity in object count - */ -uint32_t tt_message_queue_get_capacity(MessageQueue* instance); - -/** Get message size - * - * @param instance pointer to MessageQueue instance - * - * @return Message size in bytes - */ -uint32_t tt_message_queue_get_message_size(MessageQueue* instance); - -/** Get message count in queue - * - * @param instance pointer to MessageQueue instance - * - * @return Message count - */ -uint32_t tt_message_queue_get_count(MessageQueue* instance); - -/** Get queue available space - * - * @param instance pointer to MessageQueue instance - * - * @return Message count - */ -uint32_t tt_message_queue_get_space(MessageQueue* instance); - -/** Reset queue - * - * @param instance pointer to MessageQueue instance - * - * @return The status. - */ -TtStatus tt_message_queue_reset(MessageQueue* instance); - -#ifdef __cplusplus -} -#endif diff --git a/tactility-core/src/mutex.c b/tactility-core/src/mutex.c deleted file mode 100644 index d2307edd..00000000 --- a/tactility-core/src/mutex.c +++ /dev/null @@ -1,138 +0,0 @@ -#include "mutex.h" - -#include "check.h" -#include "core_defines.h" -#include "log.h" - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#else -#include "FreeRTOS.h" -#include "semphr.h" -#endif - -typedef struct { - SemaphoreHandle_t handle; - MutexType type; -} MutexData; - -#define MUTEX_DEBUGGING false - -#if MUTEX_DEBUGGING -#define TAG "mutex" -void tt_mutex_info(Mutex mutex, const char* label) { - MutexData* data = (MutexData*)mutex; - if (data == NULL) { - TT_LOG_I(TAG, "mutex %s: is NULL", label); - } else { - TT_LOG_I(TAG, "mutex %s: handle=%0X type=%d owner=%0x", label, data->handle, data->type, tt_mutex_get_owner(mutex)); - } -} -#else -#define tt_mutex_info(mutex, text) -#endif - -Mutex* tt_mutex_alloc(MutexType type) { - tt_assert(!TT_IS_IRQ_MODE()); - MutexData* data = malloc(sizeof(MutexData)); - - data->type = type; - - switch (type) { - case MutexTypeNormal: - data->handle = xSemaphoreCreateMutex(); - break; - case MutexTypeRecursive: - data->handle = xSemaphoreCreateRecursiveMutex(); - break; - default: - tt_crash("mutex type unknown/corrupted"); - } - - tt_check(data->handle != NULL); - tt_mutex_info(data, "alloc "); - return (Mutex*)data; -} - -void tt_mutex_free(Mutex* mutex) { - tt_assert(!TT_IS_IRQ_MODE()); - tt_assert(mutex); - - MutexData* data = (MutexData*)mutex; - vSemaphoreDelete(data->handle); - data->handle = NULL; // If the mutex is used after release, this might help debugging - data->type = 0xBAADF00D; // Set to an invalid value - free(data); -} - -TtStatus tt_mutex_acquire(Mutex* mutex, uint32_t timeout) { - tt_assert(mutex); - tt_assert(!TT_IS_IRQ_MODE()); - MutexData* data = (MutexData*)mutex; - tt_assert(data->handle); - TtStatus status = TtStatusOk; - - tt_mutex_info(mutex, "acquire"); - - switch (data->type) { - case MutexTypeNormal: - if (xSemaphoreTake(data->handle, timeout) != pdPASS) { - if (timeout != 0U) { - status = TtStatusErrorTimeout; - } else { - status = TtStatusErrorResource; - } - } - break; - case MutexTypeRecursive: - if (xSemaphoreTakeRecursive(data->handle, timeout) != pdPASS) { - if (timeout != 0U) { - status = TtStatusErrorTimeout; - } else { - status = TtStatusErrorResource; - } - } - break; - default: - tt_crash("mutex type unknown/corrupted"); - } - - return status; -} - -TtStatus tt_mutex_release(Mutex* mutex) { - tt_assert(mutex); - assert(!TT_IS_IRQ_MODE()); - MutexData* data = (MutexData*)mutex; - tt_assert(data->handle); - TtStatus status = TtStatusOk; - - tt_mutex_info(mutex, "release"); - - switch (data->type) { - case MutexTypeNormal: { - if (xSemaphoreGive(data->handle) != pdPASS) { - status = TtStatusErrorResource; - } - break; - } - case MutexTypeRecursive: - if (xSemaphoreGiveRecursive(data->handle) != pdPASS) { - status = TtStatusErrorResource; - } - break; - default: - tt_crash("mutex type unknown/corrupted %d"); - } - - return status; -} - -ThreadId tt_mutex_get_owner(Mutex* mutex) { - tt_assert(mutex != NULL); - tt_assert(!TT_IS_IRQ_MODE()); - MutexData* data = (MutexData*)mutex; - tt_assert(data->handle); - return (ThreadId)xSemaphoreGetMutexHolder(data->handle); -} diff --git a/tactility-core/src/pubsub.c b/tactility-core/src/pubsub.c deleted file mode 100644 index 7bb65f7f..00000000 --- a/tactility-core/src/pubsub.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "pubsub.h" -#include "check.h" -#include "mutex.h" - -#include - -struct PubSubSubscription { - PubSubCallback callback; - void* callback_context; -}; - -LIST_DEF(PubSubSubscriptionList, PubSubSubscription, M_POD_OPLIST); - -struct PubSub { - PubSubSubscriptionList_t items; - Mutex* mutex; -}; - -PubSub* tt_pubsub_alloc() { - PubSub* pubsub = malloc(sizeof(PubSub)); - - pubsub->mutex = tt_mutex_alloc(MutexTypeNormal); - tt_assert(pubsub->mutex); - - PubSubSubscriptionList_init(pubsub->items); - - return pubsub; -} - -void tt_pubsub_free(PubSub* pubsub) { - tt_assert(pubsub); - - tt_check(PubSubSubscriptionList_size(pubsub->items) == 0); - - PubSubSubscriptionList_clear(pubsub->items); - - tt_mutex_free(pubsub->mutex); - - free(pubsub); -} - -PubSubSubscription* tt_pubsub_subscribe(PubSub* pubsub, PubSubCallback callback, void* callback_context) { - tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk); - // put uninitialized item to the list - PubSubSubscription* item = PubSubSubscriptionList_push_raw(pubsub->items); - - // initialize item - item->callback = callback; - item->callback_context = callback_context; - - tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk); - - return item; -} - -void tt_pubsub_unsubscribe(PubSub* pubsub, PubSubSubscription* pubsub_subscription) { - tt_assert(pubsub); - tt_assert(pubsub_subscription); - - tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk); - bool result = false; - - // iterate over items - PubSubSubscriptionList_it_t it; - for (PubSubSubscriptionList_it(it, pubsub->items); !PubSubSubscriptionList_end_p(it); - PubSubSubscriptionList_next(it)) { - const PubSubSubscription* item = PubSubSubscriptionList_cref(it); - - // if the iterator is equal to our element - if (item == pubsub_subscription) { - PubSubSubscriptionList_remove(pubsub->items, it); - result = true; - break; - } - } - - tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk); - tt_check(result); -} - -void tt_pubsub_publish(PubSub* pubsub, void* message) { - tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk); - - // iterate over subscribers - PubSubSubscriptionList_it_t it; - for (PubSubSubscriptionList_it(it, pubsub->items); !PubSubSubscriptionList_end_p(it); - PubSubSubscriptionList_next(it)) { - const PubSubSubscription* item = PubSubSubscriptionList_cref(it); - item->callback(message, item->callback_context); - } - - tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk); -} diff --git a/tactility-core/src/tactility_core.h b/tactility-core/src/tactility_core.h deleted file mode 100644 index ef68b474..00000000 --- a/tactility-core/src/tactility_core.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include - -#include "check.h" -#include "core_defines.h" -#include "core_extra_defines.h" -#include "core_types.h" -#include "critical.h" -#include "event_flag.h" -#include "kernel.h" -#include "log.h" diff --git a/tactility-core/src/tt_stream_buffer.c b/tactility-core/src/tt_stream_buffer.c deleted file mode 100644 index c44bfac8..00000000 --- a/tactility-core/src/tt_stream_buffer.c +++ /dev/null @@ -1,94 +0,0 @@ -#include "tt_stream_buffer.h" - -#include "check.h" -#include "core_defines.h" -#include "core_types.h" - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/stream_buffer.h" -#else -#include "FreeRTOS.h" -#include "stream_buffer.h" -#endif - -StreamBuffer* tt_stream_buffer_alloc(size_t size, size_t trigger_level) { - tt_assert(size != 0); - - StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level); - tt_check(handle); - - return handle; -}; - -void tt_stream_buffer_free(StreamBuffer* stream_buffer) { - tt_assert(stream_buffer); - vStreamBufferDelete(stream_buffer); -}; - -bool tt_stream_set_trigger_level(StreamBuffer* stream_buffer, size_t trigger_level) { - tt_assert(stream_buffer); - return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE; -}; - -size_t tt_stream_buffer_send( - StreamBuffer* stream_buffer, - const void* data, - size_t length, - uint32_t timeout -) { - size_t ret; - - if (TT_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 tt_stream_buffer_receive( - StreamBuffer* stream_buffer, - void* data, - size_t length, - uint32_t timeout -) { - size_t ret; - - if (TT_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 tt_stream_buffer_bytes_available(StreamBuffer* stream_buffer) { - return xStreamBufferBytesAvailable(stream_buffer); -}; - -size_t tt_stream_buffer_spaces_available(StreamBuffer* stream_buffer) { - return xStreamBufferSpacesAvailable(stream_buffer); -}; - -bool tt_stream_buffer_is_full(StreamBuffer* stream_buffer) { - return xStreamBufferIsFull(stream_buffer) == pdTRUE; -}; - -bool tt_stream_buffer_is_empty(StreamBuffer* stream_buffer) { - return (xStreamBufferIsEmpty(stream_buffer) == pdTRUE); -}; - -TtStatus tt_stream_buffer_reset(StreamBuffer* stream_buffer) { - if (xStreamBufferReset(stream_buffer) == pdPASS) { - return TtStatusOk; - } else { - return TtStatusError; - } -} \ No newline at end of file diff --git a/tactility-headless/CMakeLists.txt b/tactility-headless/CMakeLists.txt deleted file mode 100644 index 9279fe8b..00000000 --- a/tactility-headless/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -if (DEFINED ENV{ESP_IDF_VERSION}) - file(GLOB_RECURSE SOURCE_FILES src/*.c) - - idf_component_register( - SRCS ${SOURCE_FILES} - INCLUDE_DIRS "src/" - REQUIRES esp_wifi nvs_flash spiffs - ) - - set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../data/assets") - spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT) - - set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../data/config") - spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT) - - target_link_libraries(${COMPONENT_LIB} - PUBLIC tactility-core - ) - - add_definitions(-DESP_PLATFORM) -else() - file(GLOB_RECURSE SOURCES "src/*.c") - file(GLOB_RECURSE HEADERS "src/*.h") - - add_library(tactility-headless OBJECT) - target_sources(tactility-headless - PRIVATE ${SOURCES} - PUBLIC ${HEADERS} - ) - - target_include_directories(tactility-headless - PRIVATE src/ - INTERFACE src/ - ) - - add_definitions(-D_Nullable=) - add_definitions(-D_Nonnull=) - target_link_libraries(tactility-headless - PUBLIC tactility-core - PUBLIC freertos_kernel - ) -endif() diff --git a/tactility-headless/src/esp_init.h b/tactility-headless/src/esp_init.h deleted file mode 100644 index 2a7697c6..00000000 --- a/tactility-headless/src/esp_init.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#ifdef ESP_TARGET - -#ifdef __cplusplus -extern "C" { -#endif - -void tt_esp_init(); - -#ifdef __cplusplus -} -#endif - -#endif // ESP_TARGET \ No newline at end of file diff --git a/tactility-headless/src/hardware.c b/tactility-headless/src/hardware.c deleted file mode 100644 index 3b9b9311..00000000 --- a/tactility-headless/src/hardware.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "hardware_i.h" - -#include "preferences.h" -#include "sdcard_i.h" - -#define TAG "hardware" - -void tt_hardware_init(const HardwareConfig* config) { - if (config->bootstrap != NULL) { - TT_LOG_I(TAG, "Bootstrapping"); - tt_check(config->bootstrap(), "bootstrap failed"); - } - - tt_sdcard_init(); - if (config->sdcard != NULL) { - TT_LOG_I(TAG, "Mounting sdcard"); - tt_sdcard_mount(config->sdcard); - } - - tt_check(config->init_graphics, "lvlg init not set"); - tt_check(config->init_graphics(), "lvgl init failed"); -} diff --git a/tactility-headless/src/hardware_i.h b/tactility-headless/src/hardware_i.h deleted file mode 100644 index 49bda383..00000000 --- a/tactility-headless/src/hardware_i.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "hardware_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void tt_hardware_init(const HardwareConfig* config); - -#ifdef __cplusplus -} -#endif diff --git a/tactility-headless/src/power.c b/tactility-headless/src/power.c deleted file mode 100644 index 04d00718..00000000 --- a/tactility-headless/src/power.c +++ /dev/null @@ -1 +0,0 @@ -#include "power.h" diff --git a/tactility-headless/src/preferences.c b/tactility-headless/src/preferences.c deleted file mode 100644 index 103e87f9..00000000 --- a/tactility-headless/src/preferences.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "preferences.h" - -#ifdef ESP_PLATFORM -extern const Preferences preferences_esp; -#else -extern const Preferences preferences_memory; -#endif - -const Preferences* tt_preferences() { -#ifdef ESP_PLATFORM - return &preferences_esp; -#else - return &preferences_memory; -#endif -} \ No newline at end of file diff --git a/tactility-headless/src/preferences.h b/tactility-headless/src/preferences.h deleted file mode 100644 index fb16cf5b..00000000 --- a/tactility-headless/src/preferences.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef bool (*PreferencesHasBool)(const char* namespace, const char* key); -typedef bool (*PreferencesHasInt32)(const char* namespace, const char* key); -typedef bool (*PreferencesHasString)(const char* namespace, const char* key); - -typedef bool (*PreferencesOptBool)(const char* namespace, const char* key, bool* out); -typedef bool (*PreferencesOptInt32)(const char* namespace, const char* key, int32_t* out); -typedef bool (*PreferencesOptString)(const char* namespace, const char* key, char* out, size_t* size); - -typedef void (*PreferencesPutBool)(const char* namespace, const char* key, bool value); -typedef void (*PreferencesPutInt32)(const char* namespace, const char* key, int32_t value); -typedef void (*PreferencesPutString)(const char* namespace, const char* key, const char* value); - -typedef struct { - PreferencesHasBool has_bool; - PreferencesHasInt32 has_int32; - PreferencesHasString has_string; - - PreferencesOptBool opt_bool; - PreferencesOptInt32 opt_int32; - PreferencesOptString opt_string; - - PreferencesPutBool put_bool; - PreferencesPutInt32 put_int32; - PreferencesPutString put_string; -} Preferences; - -/** - * @return an instance of Preferences. - */ -const Preferences* tt_preferences(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility-headless/src/preferences_esp.c b/tactility-headless/src/preferences_esp.c deleted file mode 100644 index b9ef8de3..00000000 --- a/tactility-headless/src/preferences_esp.c +++ /dev/null @@ -1,108 +0,0 @@ -#ifdef ESP_PLATFORM - -#include "preferences.h" -#include "nvs_flash.h" -#include "tactility_core.h" - -#define TAG "preferences" - -static bool opt_bool(const char* namespace, const char* key, bool* out) { - nvs_handle_t handle; - if (nvs_open(namespace, NVS_READWRITE, &handle) != ESP_OK) { - return false; - } else { - uint8_t out_number; - bool success = nvs_get_u8(handle, key, &out_number) == ESP_OK; - nvs_close(handle); - if (success) { - *out = (bool)out_number; - } - return success; - } -} - -static bool opt_int32(const char* namespace, const char* key, int32_t* out) { - nvs_handle_t handle; - if (nvs_open(namespace, NVS_READWRITE, &handle) != ESP_OK) { - return false; - } else { - bool success = nvs_get_i32(handle, key, out) == ESP_OK; - nvs_close(handle); - return success; - } -} - -static bool opt_string(const char* namespace, const char* key, char* out, size_t* size) { - nvs_handle_t handle; - if (nvs_open(namespace, NVS_READWRITE, &handle) != ESP_OK) { - return false; - } else { - bool success = nvs_get_str(handle, key, out, size) == ESP_OK; - nvs_close(handle); - return success; - } -} - -static bool has_bool(const char* namespace, const char* key) { - bool temp; - return opt_bool(namespace, key, &temp); -} - -static bool has_int32(const char* namespace, const char* key) { - int32_t temp; - return opt_int32(namespace, key, &temp); -} - -static bool has_string(const char* namespace, const char* key) { - char temp[128]; - size_t temp_size = 128; - return opt_string(namespace, key, temp, &temp_size); -} - -static void put_bool(const char* namespace, const char* key, bool value) { - nvs_handle_t handle; - if (nvs_open(namespace, NVS_READWRITE, &handle) == ESP_OK) { - if (nvs_set_u8(handle, key, (uint8_t)value) != ESP_OK) { - TT_LOG_E(TAG, "failed to write %s:%s", namespace, key); - } - nvs_close(handle); - } else { - TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace); - } -} - -static void put_int32(const char* namespace, const char* key, int32_t value) { - nvs_handle_t handle; - if (nvs_open(namespace, NVS_READWRITE, &handle) == ESP_OK) { - if (nvs_set_i32(handle, key, value) != ESP_OK) { - TT_LOG_E(TAG, "failed to write %s:%s", namespace, key); - } - nvs_close(handle); - } else { - TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace); - } -} - -static void put_string(const char* namespace, const char* key, const char* text) { - nvs_handle_t handle; - if (nvs_open(namespace, NVS_READWRITE, &handle) == ESP_OK) { - nvs_set_str(handle, key, text); - nvs_close(handle); - } else { - TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace); - } -} - -const Preferences preferences_esp = { - .has_bool = &has_bool, - .has_int32 = &has_int32, - .has_string = &has_string, - .opt_bool = &opt_bool, - .opt_int32 = &opt_int32, - .opt_string = &opt_string, - .put_bool = &put_bool, - .put_int32 = &put_int32, - .put_string = &put_string -}; - -#endif \ No newline at end of file diff --git a/tactility-headless/src/preferences_memory.c b/tactility-headless/src/preferences_memory.c deleted file mode 100644 index dbc83869..00000000 --- a/tactility-headless/src/preferences_memory.c +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef ESP_PLATFOM - -#include "bundle.h" -#include "preferences.h" -#include -#include - -static Bundle preferences_bundle; - -static Bundle get_preferences_bundle() { - if (preferences_bundle == NULL) { - preferences_bundle = tt_bundle_alloc(); - } - return preferences_bundle; -} - -/** - * Creates a string that is effectively "namespace:key" so we can create a single map (bundle) - * to store all the key/value pairs. - * - * @param[in] namespace - * @param[in] key - * @param[out] out - */ -static void get_bundle_key(const char* namespace, const char* key, char* out) { - strcpy(out, namespace); - size_t namespace_len = strlen(namespace); - out[namespace_len] = ':'; - char* out_with_key_offset = &out[namespace_len + 1]; - strcpy(out_with_key_offset, key); -} - -static bool has_bool(const char* namespace, const char* key) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - return tt_bundle_has_bool(get_preferences_bundle(), bundle_key); -} - -static bool has_int32(const char* namespace, const char* key) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - return tt_bundle_has_int32(get_preferences_bundle(), bundle_key); -} - -static bool has_string(const char* namespace, const char* key) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - return tt_bundle_has_string(get_preferences_bundle(), bundle_key); -} - -static bool opt_bool(const char* namespace, const char* key, bool* out) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - return tt_bundle_opt_bool(get_preferences_bundle(), bundle_key, out); -} - -static bool opt_int32(const char* namespace, const char* key, int32_t* out) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - return tt_bundle_opt_int32(get_preferences_bundle(), bundle_key, out); -} - -static bool opt_string(const char* namespace, const char* key, char* out, size_t* size) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - char* bundle_out = NULL; - if (tt_bundle_opt_string(get_preferences_bundle(), bundle_key, &bundle_out)) { - tt_assert(bundle_out != NULL); - size_t found_length = strlen(bundle_out); - tt_check(found_length <= (*size + 1), "output buffer not large enough"); - *size = found_length; - strcpy(out, bundle_out); - return true; - } else { - return false; - } -} - -static void put_bool(const char* namespace, const char* key, bool value) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - return tt_bundle_put_bool(get_preferences_bundle(), bundle_key, value); -} - -static void put_int32(const char* namespace, const char* key, int32_t value) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - return tt_bundle_put_int32(get_preferences_bundle(), bundle_key, value); -} - -static void put_string(const char* namespace, const char* key, const char* text) { - char bundle_key[128]; - get_bundle_key(namespace, key, bundle_key); - return tt_bundle_put_string(get_preferences_bundle(), bundle_key, text); -} - -const Preferences preferences_memory = { - .has_bool = &has_bool, - .has_int32 = &has_int32, - .has_string = &has_string, - .opt_bool = &opt_bool, - .opt_int32 = &opt_int32, - .opt_string = &opt_string, - .put_bool = &put_bool, - .put_int32 = &put_int32, - .put_string = &put_string -}; - -#endif \ No newline at end of file diff --git a/tactility-headless/src/sdcard.c b/tactility-headless/src/sdcard.c deleted file mode 100644 index 9811998a..00000000 --- a/tactility-headless/src/sdcard.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "sdcard.h" - -#include "mutex.h" -#include "tactility_core.h" - -#define TAG "sdcard" - -static Mutex* mutex = NULL; - -typedef struct { - const SdCard* sdcard; - void* context; -} MountData; - -static MountData data = { - .sdcard = NULL, - .context = NULL -}; - -void tt_sdcard_init() { - if (mutex == NULL) { - mutex = tt_mutex_alloc(MutexTypeRecursive); - } -} - -static bool sdcard_lock(uint32_t timeout_ticks) { - return tt_mutex_acquire(mutex, timeout_ticks) == TtStatusOk; -} - -static void sdcard_unlock() { - tt_mutex_release(mutex); -} - -bool tt_sdcard_mount(const SdCard* sdcard) { - TT_LOG_I(TAG, "Mounting"); - - if (data.sdcard != NULL) { - TT_LOG_E(TAG, "Failed to mount: already mounted"); - return false; - } - - if (sdcard_lock(100)) { - void* context = sdcard->mount(TT_SDCARD_MOUNT_POINT); - data = (MountData) { - .context = context, - .sdcard = sdcard - }; - sdcard_unlock(); - return (data.context != NULL); - } else { - TT_LOG_E(TAG, "Failed to lock"); - return false; - } -} - -SdcardState tt_sdcard_get_state() { - if (data.context == NULL) { - return SdcardStateUnmounted; - } else if (data.sdcard->is_mounted(data.context)) { - return SdcardStateMounted; - } else { - return SdcardStateError; - } -} - -bool tt_sdcard_unmount(uint32_t timeout_ticks) { - TT_LOG_I(TAG, "Unmounting"); - bool result = false; - - if (sdcard_lock(timeout_ticks)) { - if (data.sdcard != NULL) { - data.sdcard->unmount(data.context); - data = (MountData) { - .context = NULL, - .sdcard = NULL - }; - result = true; - } else { - TT_LOG_E(TAG, "Can't unmount: nothing mounted"); - } - sdcard_unlock(); - } else { - TT_LOG_E(TAG, "Failed to lock in %lu ticks", timeout_ticks); - } - - return result; -} diff --git a/tactility-headless/src/sdcard.h b/tactility-headless/src/sdcard.h deleted file mode 100644 index 3966e088..00000000 --- a/tactility-headless/src/sdcard.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "tactility_core.h" - -#define TT_SDCARD_MOUNT_POINT "/sdcard" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void* (*SdcardMount)(const char* mount_path); -typedef void (*SdcardUnmount)(void* context); -typedef bool (*SdcardIsMounted)(void* context); - -typedef enum { - SdcardStateMounted, - SdcardStateUnmounted, - SdcardStateError, -} SdcardState; - -typedef enum { - SdcardMountBehaviourAtBoot, /** Only mount at boot */ - SdcardMountBehaviourAnytime /** Mount/dismount any time */ -} SdcardMountBehaviour; - -typedef struct { - SdcardMount mount; - SdcardUnmount unmount; - SdcardIsMounted is_mounted; - SdcardMountBehaviour mount_behaviour; -} SdCard; - -bool tt_sdcard_mount(const SdCard* sdcard); -SdcardState tt_sdcard_get_state(); -bool tt_sdcard_unmount(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility-headless/src/sdcard_i.h b/tactility-headless/src/sdcard_i.h deleted file mode 100644 index ce953c00..00000000 --- a/tactility-headless/src/sdcard_i.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -void tt_sdcard_init(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility-headless/src/service.c b/tactility-headless/src/service.c deleted file mode 100644 index 6c327654..00000000 --- a/tactility-headless/src/service.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "log.h" -#include "service_i.h" -#include "tactility_core.h" - -// region Alloc/free - -ServiceData* tt_service_alloc(const ServiceManifest* manifest) { - ServiceData* data = malloc(sizeof(ServiceData)); - *data = (ServiceData) { - .manifest = manifest, - .mutex = tt_mutex_alloc(MutexTypeNormal), - .data = NULL - }; - return data; -} - -void tt_service_free(ServiceData* data) { - tt_assert(data); - tt_mutex_free(data->mutex); - free(data); -} - -// endregion Alloc/free - -// region Internal - -static void tt_service_lock(ServiceData * data) { - tt_mutex_acquire(data->mutex, TtWaitForever); -} - -static void tt_service_unlock(ServiceData* data) { - tt_mutex_release(data->mutex); -} - -// endregion Internal - -// region Getters & Setters - -const ServiceManifest* tt_service_get_manifest(Service service) { - ServiceData* data = (ServiceData*)service; - tt_service_lock(data); - const ServiceManifest* manifest = data->manifest; - tt_service_unlock(data); - return manifest; -} - -void tt_service_set_data(Service service, void* value) { - ServiceData* data = (ServiceData*)service; - tt_service_lock(data); - data->data = value; - tt_service_unlock(data); -} - -void* _Nullable tt_service_get_data(Service service) { - ServiceData* data = (ServiceData*)service; - tt_service_lock(data); - void* value = data->data; - tt_service_unlock(data); - return value; -} - -// endregion Getters & Setters \ No newline at end of file diff --git a/tactility-headless/src/service.h b/tactility-headless/src/service.h deleted file mode 100644 index 9d3fba1f..00000000 --- a/tactility-headless/src/service.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "service_manifest.h" - -typedef void* Service; - -const ServiceManifest* tt_service_get_manifest(Service service); - -void tt_service_set_data(Service service, void* value); -void* _Nullable tt_service_get_data(Service service); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility-headless/src/service_i.h b/tactility-headless/src/service_i.h deleted file mode 100644 index 02fea3ca..00000000 --- a/tactility-headless/src/service_i.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "service.h" - -#include "mutex.h" -#include "service_manifest.h" - -typedef struct { - Mutex* mutex; - const ServiceManifest* manifest; - void* data; -} ServiceData; - -ServiceData* tt_service_alloc(const ServiceManifest* manifest); -void tt_service_free(ServiceData* service); diff --git a/tactility-headless/src/service_manifest.h b/tactility-headless/src/service_manifest.h deleted file mode 100644 index 62e4fe41..00000000 --- a/tactility-headless/src/service_manifest.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "tactility_core.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void* Service; - -typedef void (*ServiceOnStart)(Service service); -typedef void (*ServiceOnStop)(Service service); - -typedef struct { - /** - * The identifier by which the app is launched by the system and other apps. - */ - const char* id; - - /** - * Non-blocking method to call when service is started. - */ - const ServiceOnStart _Nullable on_start; - - /** - * Non-blocking method to call when service is stopped. - */ - const ServiceOnStop _Nullable on_stop; - -} ServiceManifest; - -#ifdef __cplusplus -} -#endif diff --git a/tactility-headless/src/service_registry.c b/tactility-headless/src/service_registry.c deleted file mode 100644 index 6ece86ab..00000000 --- a/tactility-headless/src/service_registry.c +++ /dev/null @@ -1,145 +0,0 @@ -#include "service_registry.h" - -#include "m-dict.h" -#include "m_cstr_dup.h" -#include "mutex.h" -#include "service_i.h" -#include "service_manifest.h" -#include "tactility_core.h" - -#define TAG "service_registry" - -DICT_DEF2(ServiceManifestDict, const char*, M_CSTR_DUP_OPLIST, const ServiceManifest*, M_PTR_OPLIST) -DICT_DEF2(ServiceInstanceDict, const char*, M_CSTR_DUP_OPLIST, const ServiceData*, M_PTR_OPLIST) - -#define APP_REGISTRY_FOR_EACH(manifest_var_name, code_to_execute) \ - { \ - service_registry_manifest_lock(); \ - ServiceManifestDict_it_t it; \ - for (ServiceManifestDict_it(it, service_manifest_dict); !ServiceManifestDict_end_p(it); ServiceManifestDict_next(it)) { \ - const ServiceManifest*(manifest_var_name) = ServiceManifestDict_cref(it)->value; \ - code_to_execute; \ - } \ - service_registry_manifest_unlock(); \ - } - -static ServiceManifestDict_t service_manifest_dict; -static ServiceInstanceDict_t service_instance_dict; -static Mutex* manifest_mutex = NULL; -static Mutex* instance_mutex = NULL; - -void tt_service_registry_init() { - tt_assert(manifest_mutex == NULL); - manifest_mutex = tt_mutex_alloc(MutexTypeNormal); - ServiceManifestDict_init(service_manifest_dict); - - tt_assert(instance_mutex == NULL); - instance_mutex = tt_mutex_alloc(MutexTypeNormal); - ServiceInstanceDict_init(service_instance_dict); -} - -void service_registry_instance_lock() { - tt_assert(instance_mutex != NULL); - tt_mutex_acquire(instance_mutex, TtWaitForever); -} - -void service_registry_instance_unlock() { - tt_assert(instance_mutex != NULL); - tt_mutex_release(instance_mutex); -} - -void service_registry_manifest_lock() { - tt_assert(manifest_mutex != NULL); - tt_mutex_acquire(manifest_mutex, TtWaitForever); -} - -void service_registry_manifest_unlock() { - tt_assert(manifest_mutex != NULL); - tt_mutex_release(manifest_mutex); -} -void tt_service_registry_add(const ServiceManifest* manifest) { - TT_LOG_I(TAG, "adding %s", manifest->id); - - service_registry_manifest_lock(); - ServiceManifestDict_set_at(service_manifest_dict, manifest->id, manifest); - service_registry_manifest_unlock(); -} - -void tt_service_registry_remove(const ServiceManifest* manifest) { - TT_LOG_I(TAG, "removing %s", manifest->id); - service_registry_manifest_lock(); - ServiceManifestDict_erase(service_manifest_dict, manifest->id); - service_registry_manifest_unlock(); -} - -const ServiceManifest* _Nullable tt_service_registry_find_manifest_by_id(const char* id) { - service_registry_manifest_lock(); - const ServiceManifest** _Nullable manifest = ServiceManifestDict_get(service_manifest_dict, id); - service_registry_manifest_unlock(); - return (manifest != NULL) ? *manifest : NULL; -} - -ServiceData* _Nullable service_registry_find_instance_by_id(const char* id) { - service_registry_instance_lock(); - const ServiceData** _Nullable service_ptr = ServiceInstanceDict_get(service_instance_dict, id); - if (service_ptr == NULL) { - return NULL; - } - ServiceData* service = (ServiceData*)*service_ptr; - service_registry_instance_unlock(); - return service; -} - -void tt_service_registry_for_each_manifest(ServiceManifestCallback callback, void* _Nullable context) { - APP_REGISTRY_FOR_EACH(manifest, { - callback(manifest, context); - }); -} - -// TODO: return proper error/status instead of BOOL -bool tt_service_registry_start(const char* service_id) { - TT_LOG_I(TAG, "starting %s", service_id); - const ServiceManifest* manifest = tt_service_registry_find_manifest_by_id(service_id); - if (manifest == NULL) { - TT_LOG_E(TAG, "manifest not found for service %s", service_id); - return false; - } - - Service service = tt_service_alloc(manifest); - manifest->on_start(service); - - service_registry_instance_lock(); - ServiceInstanceDict_set_at(service_instance_dict, manifest->id, service); - service_registry_instance_unlock(); - TT_LOG_I(TAG, "started %s", service_id); - - return true; -} - -Service _Nullable tt_service_find(const char* service_id) { - service_registry_instance_lock(); - const ServiceData** _Nullable service_ptr = ServiceInstanceDict_get(service_instance_dict, service_id); - const ServiceData* service = service_ptr ? *service_ptr : NULL; - service_registry_instance_unlock(); - return (Service)service; -} - -bool tt_service_registry_stop(const char* service_id) { - TT_LOG_I(TAG, "stopping %s", service_id); - ServiceData* service = service_registry_find_instance_by_id(service_id); - if (service == NULL) { - TT_LOG_W(TAG, "service not running: %s", service_id); - return false; - } - - service->manifest->on_stop(service); - tt_service_free(service); - - service_registry_instance_lock(); - ServiceInstanceDict_erase(service_instance_dict, service_id); - service_registry_instance_unlock(); - - TT_LOG_I(TAG, "stopped %s", service_id); - - return true; -} diff --git a/tactility-headless/src/service_registry.h b/tactility-headless/src/service_registry.h deleted file mode 100644 index d6f03ca6..00000000 --- a/tactility-headless/src/service_registry.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "service_manifest.h" -#include "tactility_core.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef void (*ServiceManifestCallback)(const ServiceManifest*, void* context); - -void tt_service_registry_init(); - -void tt_service_registry_add(const ServiceManifest* manifest); -void tt_service_registry_remove(const ServiceManifest* manifest); -const ServiceManifest _Nullable* tt_service_registry_find_manifest_by_id(const char* id); -void tt_service_registry_for_each_manifest(ServiceManifestCallback callback, void* _Nullable context); - -bool tt_service_registry_start(const char* service_id); -bool tt_service_registry_stop(const char* service_id); - -Service _Nullable tt_service_find(const char* service_id); - -#ifdef __cplusplus -} -#endif diff --git a/tactility-headless/src/services/sdcard/sdcard.h b/tactility-headless/src/services/sdcard/sdcard.h deleted file mode 100644 index af80ff83..00000000 --- a/tactility-headless/src/services/sdcard/sdcard.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility-headless/src/services/wifi/wifi_settings_mock.c b/tactility-headless/src/services/wifi/wifi_settings_mock.c deleted file mode 100644 index 7fe65018..00000000 --- a/tactility-headless/src/services/wifi/wifi_settings_mock.c +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ESP_TARGET - -#include "wifi_settings.h" -#include "log.h" - -#define TAG "wifi_settings_mock" - -bool tt_wifi_settings_contains(const char* ssid) { - return false; -} - -void tt_wifi_settings_init() { - TT_LOG_I(TAG, "init"); -} - -bool tt_wifi_settings_load(const char* ssid, WifiApSettings* settings) { - return false; -} - -bool tt_wifi_settings_save(const WifiApSettings* settings) { - return false; -} - -bool tt_wifi_settings_remove(const char* ssid) { - return false; -} - -#endif // ESP_TARGET \ No newline at end of file diff --git a/tactility-headless/src/tactility_headless.c b/tactility-headless/src/tactility_headless.c deleted file mode 100644 index 22c2061a..00000000 --- a/tactility-headless/src/tactility_headless.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "tactility_headless.h" -#include "hardware_config.h" -#include "hardware_i.h" -#include "service_manifest.h" -#include "service_registry.h" - -#ifdef ESP_PLATFORM -#include "esp_init.h" -#endif - -#define TAG "tactility" - -extern const ServiceManifest sdcard_service; -extern const ServiceManifest wifi_service; - -static const ServiceManifest* const system_services[] = { - &sdcard_service, - &wifi_service -}; - -static const HardwareConfig* hardwareConfig = NULL; - -static void register_and_start_system_services() { - TT_LOG_I(TAG, "Registering and starting system services"); - int app_count = sizeof(system_services) / sizeof(ServiceManifest*); - for (int i = 0; i < app_count; ++i) { - tt_service_registry_add(system_services[i]); - tt_check(tt_service_registry_start(system_services[i]->id)); - } -} - -void tt_headless_init(const HardwareConfig* config) { -#ifdef ESP_PLATFORM - tt_esp_init(); -#endif - hardwareConfig = config; - tt_service_registry_init(); - tt_hardware_init(config); - register_and_start_system_services(); -} - -const HardwareConfig* tt_get_hardware_config() { - return hardwareConfig; -} diff --git a/tactility-headless/src/tactility_headless.h b/tactility-headless/src/tactility_headless.h deleted file mode 100644 index 3e5d6168..00000000 --- a/tactility-headless/src/tactility_headless.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "hardware_config.h" -#include "tactility_headless_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void tt_headless_init(const HardwareConfig* config); - -const HardwareConfig* tt_get_hardware_config(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility/CMakeLists.txt b/tactility/CMakeLists.txt deleted file mode 100644 index 6a309371..00000000 --- a/tactility/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -if (DEFINED ENV{ESP_IDF_VERSION}) - file(GLOB_RECURSE SOURCE_FILES src/*.c) - - idf_component_register( - SRCS ${SOURCE_FILES} - INCLUDE_DIRS "src/" - REQUIRES tactility-headless lvgl driver - ) - - target_link_libraries(${COMPONENT_LIB} - PUBLIC lv_screenshot - ) - - add_definitions(-DESP_PLATFORM) -else() - file(GLOB_RECURSE SOURCES "src/*.c") - file(GLOB_RECURSE HEADERS "src/*.h") - - add_library(tactility OBJECT) - target_sources(tactility - PRIVATE ${SOURCES} - PUBLIC ${HEADERS} - ) - - target_include_directories(tactility - PRIVATE src/ - INTERFACE src/ - ) - - add_definitions(-D_Nullable=) - add_definitions(-D_Nonnull=) - target_link_libraries(tactility - PUBLIC tactility-headless - PUBLIC lvgl - PUBLIC freertos_kernel - PUBLIC lv_screenshot - ) -endif() - diff --git a/tactility/src/app.c b/tactility/src/app.c deleted file mode 100644 index e3bf6019..00000000 --- a/tactility/src/app.c +++ /dev/null @@ -1,143 +0,0 @@ -#include "app_i.h" - -#include "log.h" -#include - -#define TAG "app" - -static AppFlags tt_app_get_flags_default(AppType type); - -static const AppFlags DEFAULT_FLAGS = { - .show_statusbar = true -}; - -// region Alloc/free - -App tt_app_alloc(const AppManifest* manifest, Bundle _Nullable parameters) { -#ifdef ESP_PLATFORM - size_t memory_before = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); -#else - size_t memory_before = 0; -#endif - AppData* data = malloc(sizeof(AppData)); - *data = (AppData) { - .mutex = tt_mutex_alloc(MutexTypeNormal), - .state = AppStateInitial, - .flags = tt_app_get_flags_default(manifest->type), - .manifest = manifest, - .parameters = parameters, - .memory = memory_before, - .data = NULL - }; - return (App*)data; -} - -void tt_app_free(App app) { - AppData* data = (AppData*)app; - - size_t memory_before = data->memory; - - if (data->parameters) { - tt_bundle_free(data->parameters); - } - tt_mutex_free(data->mutex); - free(data); - -#ifdef ESP_PLATFORM - size_t memory_after = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); -#else - size_t memory_after = 0; -#endif - - if (memory_after < memory_before) { - TT_LOG_W(TAG, "Potential memory leak: gained %u bytes after closing app", memory_before - memory_after); - TT_LOG_W(TAG, "Note that WiFi service frees up memory asynchronously and that the leak can be caused by an app that was launched by this app."); - } -} - -// endregion - -// region Internal - -static void tt_app_lock(AppData* data) { - tt_mutex_acquire(data->mutex, TtWaitForever); -} - -static void tt_app_unlock(AppData* data) { - tt_mutex_release(data->mutex); -} - -static AppFlags tt_app_get_flags_default(AppType type) { - return DEFAULT_FLAGS; -} - -// endregion Internal - -// region Public getters & setters - -void tt_app_set_state(App app, AppState state) { - AppData* data = (AppData*)app; - tt_app_lock(data); - data->state = state; - tt_app_unlock(data); -} - -AppState tt_app_get_state(App app) { - AppData* data = (AppData*)app; - tt_app_lock(data); - AppState state = data->state; - tt_app_unlock(data); - return state; -} - -const AppManifest* tt_app_get_manifest(App app) { - AppData* data = (AppData*)app; - // No need to lock const data; - return data->manifest; -} - -AppFlags tt_app_get_flags(App app) { - AppData* data = (AppData*)app; - tt_app_lock(data); - AppFlags flags = data->flags; - tt_app_unlock(data); - return flags; -} - -void tt_app_set_flags(App app, AppFlags flags) { - AppData* data = (AppData*)app; - tt_app_lock(data); - data->flags = flags; - tt_app_unlock(data); -} - -void* tt_app_get_data(App app) { - AppData* data = (AppData*)app; - tt_app_lock(data); - void* value = data->data; - tt_app_unlock(data); - return value; -} - -void tt_app_set_data(App app, void* value) { - AppData* data = (AppData*)app; - tt_app_lock(data); - data->data = value; - tt_app_unlock(data); -} - -/** TODO: Make this thread-safe. - * In practice, the bundle is writeable, so someone could be writing to it - * while it is being accessed from another thread. - * Consider creating MutableBundle vs Bundle. - * Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it. - */ -Bundle _Nullable tt_app_get_parameters(App app) { - AppData* data = (AppData*)app; - tt_app_lock(data); - Bundle bundle = data->parameters; - tt_app_unlock(data); - return bundle; -} - -// endregion Public getters & setters diff --git a/tactility/src/app.h b/tactility/src/app.h deleted file mode 100644 index 25e049c8..00000000 --- a/tactility/src/app.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "app_manifest.h" -#include "bundle.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - AppStateInitial, // App is being activated in loader - AppStateStarted, // App is in memory - AppStateShowing, // App view is created - AppStateHiding, // App view is destroyed - AppStateStopped // App is not in memory -} AppState; - -typedef union { - struct { - bool show_statusbar : 1; - }; - unsigned char flags; -} AppFlags; - -typedef void* App; - -/** @brief Create an app - * @param manifest - * @param parameters optional bundle. memory ownership is transferred to App - * @return - */ -App tt_app_alloc(const AppManifest* manifest, Bundle _Nullable parameters); -void tt_app_free(App app); - -void tt_app_set_state(App app, AppState state); -AppState tt_app_get_state(App app); - -const AppManifest* tt_app_get_manifest(App app); - -AppFlags tt_app_get_flags(App app); -void tt_app_set_flags(App app, AppFlags flags); - -void* _Nullable tt_app_get_data(App app); -void tt_app_set_data(App app, void* data); - -Bundle _Nullable tt_app_get_parameters(App app); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/app_manifest_registry.c b/tactility/src/app_manifest_registry.c deleted file mode 100644 index d8c02990..00000000 --- a/tactility/src/app_manifest_registry.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "app_manifest_registry.h" - -#include "m-dict.h" -#include "m_cstr_dup.h" -#include "mutex.h" -#include "tactility_core.h" - -#define TAG "app_registry" - -DICT_DEF2(AppManifestDict, const char*, M_CSTR_DUP_OPLIST, const AppManifest*, M_PTR_OPLIST) - -#define APP_REGISTRY_FOR_EACH(manifest_var_name, code_to_execute) \ - { \ - app_registry_lock(); \ - AppManifestDict_it_t it; \ - for (AppManifestDict_it(it, app_manifest_dict); !AppManifestDict_end_p(it); AppManifestDict_next(it)) { \ - const AppManifest*(manifest_var_name) = AppManifestDict_cref(it)->value; \ - code_to_execute; \ - } \ - app_registry_unlock(); \ - } - -AppManifestDict_t app_manifest_dict; -Mutex* hash_mutex = NULL; - -void tt_app_manifest_registry_init() { - tt_assert(hash_mutex == NULL); - hash_mutex = tt_mutex_alloc(MutexTypeNormal); - AppManifestDict_init(app_manifest_dict); -} - -void app_registry_lock() { - tt_assert(hash_mutex != NULL); - tt_mutex_acquire(hash_mutex, TtWaitForever); -} - -void app_registry_unlock() { - tt_assert(hash_mutex != NULL); - tt_mutex_release(hash_mutex); -} - -void tt_app_manifest_registry_add(const AppManifest* manifest) { - TT_LOG_I(TAG, "adding %s", manifest->id); - - app_registry_lock(); - AppManifestDict_set_at(app_manifest_dict, manifest->id, manifest); - app_registry_unlock(); -} - -void tt_app_manifest_registry_remove(const AppManifest* manifest) { - TT_LOG_I(TAG, "removing %s", manifest->id); - app_registry_lock(); - AppManifestDict_erase(app_manifest_dict, manifest->id); - app_registry_unlock(); -} - -const AppManifest _Nullable* tt_app_manifest_registry_find_by_id(const char* id) { - app_registry_lock(); - const AppManifest _Nullable** manifest = AppManifestDict_get(app_manifest_dict, id); - app_registry_unlock(); - return (manifest != NULL) ? *manifest : NULL; -} - -void tt_app_manifest_registry_for_each_of_type(AppType type, void* _Nullable context, AppManifestCallback callback) { - APP_REGISTRY_FOR_EACH(manifest, { - if (manifest->type == type) { - callback(manifest, context); - } - }); -} - -void tt_app_manifest_registry_for_each(AppManifestCallback callback, void* _Nullable context) { - APP_REGISTRY_FOR_EACH(manifest, { - callback(manifest, context); - }); -} diff --git a/tactility/src/app_manifest_registry.h b/tactility/src/app_manifest_registry.h deleted file mode 100644 index ac4fa23b..00000000 --- a/tactility/src/app_manifest_registry.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "app_manifest.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*AppManifestCallback)(const AppManifest*, void* context); - -void tt_app_manifest_registry_init(); -void tt_app_manifest_registry_add(const AppManifest* manifest); -void tt_app_manifest_registry_remove(const AppManifest* manifest); -const AppManifest _Nullable* tt_app_manifest_registry_find_by_id(const char* id); -void tt_app_manifest_registry_for_each(AppManifestCallback callback, void* _Nullable context); -void tt_app_manifest_registry_for_each_of_type(AppType type, void* _Nullable context, AppManifestCallback callback); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/apps/files/files_data.h b/tactility/src/apps/files/files_data.h deleted file mode 100644 index 5be80cf2..00000000 --- a/tactility/src/apps/files/files_data.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include "lvgl.h" - -#define MAX_PATH_LENGTH 256 - -typedef struct { - char current_path[MAX_PATH_LENGTH]; - struct dirent** dir_entries; - int dir_entries_count; - lv_obj_t* list; -} FilesData; - -#ifdef __cplusplus -extern "C" { -#endif - -FilesData* files_data_alloc(); -void files_data_free(FilesData* data); -void files_data_free_entries(FilesData* data); -bool files_data_set_entries_for_child_path(FilesData* data, const char* child_path); -bool files_data_set_entries_for_path(FilesData* data, const char* path); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility/src/apps/image_viewer/image_viewer.c b/tactility/src/apps/image_viewer/image_viewer.c deleted file mode 100644 index ee883988..00000000 --- a/tactility/src/apps/image_viewer/image_viewer.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "image_viewer.h" -#include "log.h" -#include "lvgl.h" -#include "ui/style.h" -#include "ui/toolbar.h" -#include - -#define TAG "image_viewer" - -static void app_show(App app, lv_obj_t* parent) { - lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - tt_toolbar_create_for_app(parent, app); - - lv_obj_t* wrapper = lv_obj_create(parent); - lv_obj_set_width(wrapper, LV_PCT(100)); - lv_obj_set_flex_grow(wrapper, 1); - lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); - tt_lv_obj_set_style_no_padding(wrapper); - tt_lv_obj_set_style_bg_invisible(wrapper); - - lv_obj_t* image = lv_img_create(wrapper); - lv_obj_align(image, LV_ALIGN_CENTER, 0, 0); - Bundle bundle = tt_app_get_parameters(app); - if (tt_bundle_has_string(bundle, IMAGE_VIEWER_FILE_ARGUMENT)) { - const char* file = tt_bundle_get_string(bundle, IMAGE_VIEWER_FILE_ARGUMENT); - char* prefixed_path = malloc(strlen(file) + 3); - tt_assert(prefixed_path != NULL); - sprintf(prefixed_path, "A:%s", file); - TT_LOG_I(TAG, "Opening %s", prefixed_path); - lv_img_set_src(image, prefixed_path); - free(prefixed_path); - } -} - -const AppManifest image_viewer_app = { - .id = "image_viewer", - .name = "Image Viewer", - .icon = NULL, - .type = AppTypeHidden, - .on_start = NULL, - .on_stop = NULL, - .on_show = &app_show, - .on_hide = NULL -}; diff --git a/tactility/src/apps/image_viewer/image_viewer.h b/tactility/src/apps/image_viewer/image_viewer.h deleted file mode 100644 index 888770e5..00000000 --- a/tactility/src/apps/image_viewer/image_viewer.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#define IMAGE_VIEWER_FILE_ARGUMENT "file" - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility/src/apps/text_viewer/text_viewer.c b/tactility/src/apps/text_viewer/text_viewer.c deleted file mode 100644 index 4d900eea..00000000 --- a/tactility/src/apps/text_viewer/text_viewer.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "text_viewer.h" -#include "log.h" -#include "lvgl.h" -#include "ui/label_utils.h" -#include "ui/style.h" -#include "ui/toolbar.h" - -#define TAG "text_viewer" - -static void app_show(App app, lv_obj_t* parent) { - lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - tt_toolbar_create_for_app(parent, app); - - lv_obj_t* wrapper = lv_obj_create(parent); - lv_obj_set_width(wrapper, LV_PCT(100)); - lv_obj_set_flex_grow(wrapper, 1); - lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); - tt_lv_obj_set_style_no_padding(wrapper); - tt_lv_obj_set_style_bg_invisible(wrapper); - - lv_obj_t* text = lv_label_create(wrapper); - lv_obj_align(text, LV_ALIGN_CENTER, 0, 0); - Bundle bundle = tt_app_get_parameters(app); - if (tt_bundle_has_string(bundle, TEXT_VIEWER_FILE_ARGUMENT)) { - const char* filepath = tt_bundle_get_string(bundle, TEXT_VIEWER_FILE_ARGUMENT); - TT_LOG_I(TAG, "Opening %s", filepath); - tt_lv_label_set_text_file(text, filepath); - } -} - -const AppManifest text_viewer_app = { - .id = "text_viewer", - .name = "Text Viewer", - .icon = NULL, - .type = AppTypeHidden, - .on_start = NULL, - .on_stop = NULL, - .on_show = &app_show, - .on_hide = NULL -}; diff --git a/tactility/src/apps/text_viewer/text_viewer.h b/tactility/src/apps/text_viewer/text_viewer.h deleted file mode 100644 index 926f7407..00000000 --- a/tactility/src/apps/text_viewer/text_viewer.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#define TEXT_VIEWER_FILE_ARGUMENT "file" - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility/src/apps/wifi_connect/wifi_connect.c b/tactility/src/apps/wifi_connect/wifi_connect.c deleted file mode 100644 index 9630f7ca..00000000 --- a/tactility/src/apps/wifi_connect/wifi_connect.c +++ /dev/null @@ -1,141 +0,0 @@ -#include "wifi_connect.h" - -#include "app.h" -#include "services/loader/loader.h" -#include "services/wifi/wifi.h" -#include "tactility_core.h" -#include "ui/lvgl_sync.h" -#include "wifi_connect_state_updating.h" - -#define TAG "wifi_connect" - -// Forward declarations -static void wifi_connect_event_callback(const void* message, void* context); - -static void on_connect(const WifiApSettings* ap_settings, bool remember, TT_UNUSED void* parameter) { - WifiConnect* wifi = (WifiConnect*)parameter; - wifi_connect_state_set_ap_settings(wifi, ap_settings); - wifi_connect_state_set_connecting(wifi, true); - wifi_connect(ap_settings, remember); -} - -static WifiConnect* wifi_connect_alloc() { - WifiConnect* wifi = malloc(sizeof(WifiConnect)); - - PubSub* wifi_pubsub = wifi_get_pubsub(); - wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &wifi_connect_event_callback, wifi); - wifi->mutex = tt_mutex_alloc(MutexTypeNormal); - wifi->state = (WifiConnectState) { - .connection_error = false, - .settings = { - .auto_connect = false, - .ssid = { 0 }, - .password = { 0 } - } - }; - wifi->bindings = (WifiConnectBindings) { - .on_connect_ssid = &on_connect, - .on_connect_ssid_context = wifi, - }; - wifi->view_enabled = false; - - return wifi; -} - -static void wifi_connect_free(WifiConnect* wifi) { - PubSub* wifi_pubsub = wifi_get_pubsub(); - tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription); - tt_mutex_free(wifi->mutex); - - free(wifi); -} - -void wifi_connect_lock(WifiConnect* wifi) { - tt_assert(wifi); - tt_assert(wifi->mutex); - tt_mutex_acquire(wifi->mutex, TtWaitForever); -} - -void wifi_connect_unlock(WifiConnect* wifi) { - tt_assert(wifi); - tt_assert(wifi->mutex); - tt_mutex_release(wifi->mutex); -} - -void wifi_connect_request_view_update(WifiConnect* wifi) { - wifi_connect_lock(wifi); - if (wifi->view_enabled) { - if (tt_lvgl_lock(1000)) { - wifi_connect_view_update(&wifi->view, &wifi->bindings, &wifi->state); - tt_lvgl_unlock(); - } else { - TT_LOG_E(TAG, "Failed to lock lvgl"); - } - } - wifi_connect_unlock(wifi); -} - -static void wifi_connect_event_callback(const void* message, void* context) { - const WifiEvent* event = (const WifiEvent*)message; - WifiConnect* wifi = (WifiConnect*)context; - switch (event->type) { - case WifiEventTypeConnectionFailed: - if (wifi->state.is_connecting) { - wifi_connect_state_set_connecting(wifi, false); - wifi_connect_state_set_radio_error(wifi, true); - wifi_connect_request_view_update(wifi); - } - break; - case WifiEventTypeConnectionSuccess: - if (wifi->state.is_connecting) { - wifi_connect_state_set_connecting(wifi, false); - loader_stop_app(); - } - break; - default: - break; - } - wifi_connect_request_view_update(wifi); -} - -static void app_show(App app, lv_obj_t* parent) { - WifiConnect* wifi = (WifiConnect*)tt_app_get_data(app); - - wifi_connect_lock(wifi); - wifi->view_enabled = true; - wifi_connect_view_create(app, wifi, parent); - wifi_connect_view_update(&wifi->view, &wifi->bindings, &wifi->state); - wifi_connect_unlock(wifi); -} - -static void app_hide(App app) { - WifiConnect* wifi = (WifiConnect*)tt_app_get_data(app); - // No need to lock view, as this is called from within Gui's LVGL context - wifi_connect_view_destroy(&wifi->view); - wifi_connect_lock(wifi); - wifi->view_enabled = false; - wifi_connect_unlock(wifi); -} - -static void app_start(App app) { - WifiConnect* wifi_connect = wifi_connect_alloc(app); - tt_app_set_data(app, wifi_connect); -} - -static void app_stop(App app) { - WifiConnect* wifi = tt_app_get_data(app); - tt_assert(wifi != NULL); - wifi_connect_free(wifi); - tt_app_set_data(app, NULL); -} - -AppManifest wifi_connect_app = { - .id = "wifi_connect", - .name = "Wi-Fi Connect", - .icon = LV_SYMBOL_WIFI, - .type = AppTypeSettings, - .on_start = &app_start, - .on_stop = &app_stop, - .on_show = &app_show, - .on_hide = &app_hide -}; diff --git a/tactility/src/apps/wifi_connect/wifi_connect.h b/tactility/src/apps/wifi_connect/wifi_connect.h deleted file mode 100644 index c7f4da9a..00000000 --- a/tactility/src/apps/wifi_connect/wifi_connect.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "mutex.h" -#include "services/wifi/wifi.h" -#include "wifi_connect_bindings.h" -#include "wifi_connect_state.h" -#include "wifi_connect_view.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - PubSubSubscription* wifi_subscription; - Mutex* mutex; - WifiConnectState state; - WifiConnectView view; - bool view_enabled; - WifiConnectBindings bindings; -} WifiConnect; - -void wifi_connect_lock(WifiConnect* wifi); - -void wifi_connect_unlock(WifiConnect* wifi); - -void wifi_connect_request_view_update(WifiConnect* wifi); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/apps/wifi_connect/wifi_connect_bindings.h b/tactility/src/apps/wifi_connect/wifi_connect_bindings.h deleted file mode 100644 index 2d4b6586..00000000 --- a/tactility/src/apps/wifi_connect/wifi_connect_bindings.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "services/wifi/wifi_settings.h" -#include - -typedef void (*OnConnectSsid)(const WifiApSettings* settings, bool store, void* context); - -typedef struct { - OnConnectSsid on_connect_ssid; - void* on_connect_ssid_context; -} WifiConnectBindings; diff --git a/tactility/src/apps/wifi_connect/wifi_connect_state.h b/tactility/src/apps/wifi_connect/wifi_connect_state.h deleted file mode 100644 index a9e29aa0..00000000 --- a/tactility/src/apps/wifi_connect/wifi_connect_state.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "app.h" -#include "services/wifi/wifi.h" -#include "services/wifi/wifi_settings.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * View's state - */ -typedef struct { - WifiApSettings settings; - bool connection_error; - bool is_connecting; -} WifiConnectState; - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/apps/wifi_connect/wifi_connect_state_updating.c b/tactility/src/apps/wifi_connect/wifi_connect_state_updating.c deleted file mode 100644 index 770d065a..00000000 --- a/tactility/src/apps/wifi_connect/wifi_connect_state_updating.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "wifi_connect_state_updating.h" - -void wifi_connect_state_set_radio_error(WifiConnect* wifi, bool error) { - wifi_connect_lock(wifi); - wifi->state.connection_error = error; - wifi_connect_unlock(wifi); -} - -void wifi_connect_state_set_ap_settings(WifiConnect* wifi, const WifiApSettings* settings) { - wifi_connect_lock(wifi); - memcpy(&(wifi->state.settings), settings, sizeof(WifiApSettings)); - wifi_connect_unlock(wifi); -} - -void wifi_connect_state_set_connecting(WifiConnect* wifi, bool is_connecting) { - wifi_connect_lock(wifi); - wifi->state.is_connecting = is_connecting; - wifi_connect_unlock(wifi); -} diff --git a/tactility/src/apps/wifi_connect/wifi_connect_state_updating.h b/tactility/src/apps/wifi_connect/wifi_connect_state_updating.h deleted file mode 100644 index bc6baed9..00000000 --- a/tactility/src/apps/wifi_connect/wifi_connect_state_updating.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "wifi_connect.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void wifi_connect_state_set_radio_error(WifiConnect* wifi, bool error); -void wifi_connect_state_set_ap_settings(WifiConnect* wifi, const WifiApSettings* settings); -void wifi_connect_state_set_connecting(WifiConnect* wifi, bool is_connecting); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/apps/wifi_manage/wifi_manage.c b/tactility/src/apps/wifi_manage/wifi_manage.c deleted file mode 100644 index ee76ea6a..00000000 --- a/tactility/src/apps/wifi_manage/wifi_manage.c +++ /dev/null @@ -1,172 +0,0 @@ -#include "wifi_manage.h" - -#include "app.h" -#include "apps/wifi_connect/wifi_connect_bundle.h" -#include "services/loader/loader.h" -#include "services/wifi/wifi_settings.h" -#include "tactility_core.h" -#include "ui/lvgl_sync.h" -#include "wifi_manage_state_updating.h" -#include "wifi_manage_view.h" - -#define TAG "wifi_manage" - -// Forward declarations -static void wifi_manage_event_callback(const void* message, void* context); - -static void on_connect(const char* ssid) { - WifiApSettings settings; - if (tt_wifi_settings_load(ssid, &settings)) { - TT_LOG_I(TAG, "Connecting with known credentials"); - wifi_connect(&settings, false); - } else { - TT_LOG_I(TAG, "Starting connection dialog"); - Bundle bundle = tt_bundle_alloc(); - tt_bundle_put_string(bundle, WIFI_CONNECT_PARAM_SSID, ssid); - tt_bundle_put_string(bundle, WIFI_CONNECT_PARAM_PASSWORD, ""); - loader_start_app("wifi_connect", false, bundle); - } -} - -static void on_disconnect() { - wifi_disconnect(); -} - -static void on_wifi_toggled(bool enabled) { - wifi_set_enabled(enabled); -} - -static WifiManage* wifi_manage_alloc() { - WifiManage* wifi = malloc(sizeof(WifiManage)); - - wifi->wifi_subscription = NULL; - wifi->mutex = tt_mutex_alloc(MutexTypeNormal); - wifi->state = (WifiManageState) { - .scanning = wifi_is_scanning(), - .radio_state = wifi_get_radio_state() - }; - wifi->view_enabled = false; - wifi->bindings = (WifiManageBindings) { - .on_wifi_toggled = &on_wifi_toggled, - .on_connect_ssid = &on_connect, - .on_disconnect = &on_disconnect - }; - - return wifi; -} - -static void wifi_manage_free(WifiManage* wifi) { - tt_mutex_free(wifi->mutex); - - free(wifi); -} - -void wifi_manage_lock(WifiManage* wifi) { - tt_assert(wifi); - tt_assert(wifi->mutex); - tt_mutex_acquire(wifi->mutex, TtWaitForever); -} - -void wifi_manage_unlock(WifiManage* wifi) { - tt_assert(wifi); - tt_assert(wifi->mutex); - tt_mutex_release(wifi->mutex); -} - -void wifi_manage_request_view_update(WifiManage* wifi) { - wifi_manage_lock(wifi); - if (wifi->view_enabled) { - if (tt_lvgl_lock(1000)) { - wifi_manage_view_update(&wifi->view, &wifi->bindings, &wifi->state); - tt_lvgl_unlock(); - } else { - TT_LOG_E(TAG, "failed to lock lvgl"); - } - } - wifi_manage_unlock(wifi); -} - -static void wifi_manage_event_callback(const void* message, void* context) { - const WifiEvent* event = (const WifiEvent*)message; - WifiManage* wifi = (WifiManage*)context; - wifi_manage_state_set_radio_state(wifi, wifi_get_radio_state()); - switch (event->type) { - case WifiEventTypeScanStarted: - wifi_manage_state_set_scanning(wifi, true); - break; - case WifiEventTypeScanFinished: - wifi_manage_state_set_scanning(wifi, false); - wifi_manage_state_update_scanned_records(wifi); - break; - case WifiEventTypeRadioStateOn: - if (!wifi_is_scanning()) { - wifi_scan(); - } - break; - default: - break; - } - - wifi_manage_request_view_update(wifi); -} - -static void app_show(App app, lv_obj_t* parent) { - WifiManage* wifi = (WifiManage*)tt_app_get_data(app); - - PubSub* wifi_pubsub = wifi_get_pubsub(); - wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &wifi_manage_event_callback, wifi); - - // State update (it has its own locking) - wifi_manage_state_set_radio_state(wifi, wifi_get_radio_state()); - wifi_manage_state_set_scanning(wifi, wifi_is_scanning()); - wifi_manage_state_update_scanned_records(wifi); - - // View update - wifi_manage_lock(wifi); - wifi->view_enabled = true; - strcpy((char*)wifi->state.connect_ssid, "Connected"); // TODO update with proper SSID - wifi_manage_view_create(app, &wifi->view, &wifi->bindings, parent); - wifi_manage_view_update(&wifi->view, &wifi->bindings, &wifi->state); - wifi_manage_unlock(wifi); - - WifiRadioState radio_state = wifi_get_radio_state(); - bool can_scan = radio_state == WIFI_RADIO_ON || - radio_state == WIFI_RADIO_CONNECTION_PENDING || - radio_state == WIFI_RADIO_CONNECTION_ACTIVE; - if (can_scan && !wifi_is_scanning()) { - wifi_scan(); - } -} - -static void app_hide(App app) { - WifiManage* wifi = (WifiManage*)tt_app_get_data(app); - wifi_manage_lock(wifi); - PubSub* wifi_pubsub = wifi_get_pubsub(); - tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription); - wifi->wifi_subscription = NULL; - wifi->view_enabled = false; - wifi_manage_unlock(wifi); -} - -static void app_start(App app) { - WifiManage* wifi = wifi_manage_alloc(); - tt_app_set_data(app, wifi); -} - -static void app_stop(App app) { - WifiManage* wifi = (WifiManage*)tt_app_get_data(app); - tt_assert(wifi != NULL); - wifi_manage_free(wifi); - tt_app_set_data(app, NULL); -} - -AppManifest wifi_manage_app = { - .id = "wifi_manage", - .name = "Wi-Fi", - .icon = LV_SYMBOL_WIFI, - .type = AppTypeSettings, - .on_start = &app_start, - .on_stop = &app_stop, - .on_show = &app_show, - .on_hide = &app_hide -}; diff --git a/tactility/src/apps/wifi_manage/wifi_manage.h b/tactility/src/apps/wifi_manage/wifi_manage.h deleted file mode 100644 index bc7ab626..00000000 --- a/tactility/src/apps/wifi_manage/wifi_manage.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "mutex.h" -#include "services/wifi/wifi.h" -#include "wifi_manage_view.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - PubSubSubscription* wifi_subscription; - Mutex* mutex; - WifiManageState state; - WifiManageView view; - bool view_enabled; - WifiManageBindings bindings; -} WifiManage; - -void wifi_manage_lock(WifiManage* wifi); - -void wifi_manage_unlock(WifiManage* wifi); - -void wifi_manage_request_view_update(WifiManage* wifi); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/apps/wifi_manage/wifi_manage_state.h b/tactility/src/apps/wifi_manage/wifi_manage_state.h deleted file mode 100644 index e9d36a78..00000000 --- a/tactility/src/apps/wifi_manage/wifi_manage_state.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include "services/wifi/wifi.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define WIFI_SCAN_AP_RECORD_COUNT 16 - -/** - * View's state - */ -typedef struct { - bool scanning; - WifiRadioState radio_state; - uint8_t connect_ssid[33]; - WifiApRecord ap_records[WIFI_SCAN_AP_RECORD_COUNT]; - uint16_t ap_records_count; -} WifiManageState; - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/apps/wifi_manage/wifi_manage_state_updating.c b/tactility/src/apps/wifi_manage/wifi_manage_state_updating.c deleted file mode 100644 index 93be38ae..00000000 --- a/tactility/src/apps/wifi_manage/wifi_manage_state_updating.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "wifi_manage.h" - -void wifi_manage_state_set_scanning(WifiManage* wifi, bool is_scanning) { - wifi_manage_lock(wifi); - wifi->state.scanning = is_scanning; - wifi_manage_unlock(wifi); -} - -void wifi_manage_state_set_radio_state(WifiManage* wifi, WifiRadioState state) { - wifi_manage_lock(wifi); - wifi->state.radio_state = state; - wifi_manage_unlock(wifi); -} - -void wifi_manage_state_update_scanned_records(WifiManage* wifi) { - wifi_manage_lock(wifi); - wifi_get_scan_results( - wifi->state.ap_records, - WIFI_SCAN_AP_RECORD_COUNT, - &wifi->state.ap_records_count - ); - wifi_manage_unlock(wifi); -} \ No newline at end of file diff --git a/tactility/src/apps/wifi_manage/wifi_manage_state_updating.h b/tactility/src/apps/wifi_manage/wifi_manage_state_updating.h deleted file mode 100644 index e939979d..00000000 --- a/tactility/src/apps/wifi_manage/wifi_manage_state_updating.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "wifi_manage.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void wifi_manage_state_set_scanning(WifiManage* wifi, bool is_scanning); -void wifi_manage_state_set_radio_state(WifiManage* wifi, WifiRadioState state); -void wifi_manage_state_update_scanned_records(WifiManage* wifi); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/apps/wifi_manage/wifi_manage_view.h b/tactility/src/apps/wifi_manage/wifi_manage_view.h deleted file mode 100644 index 2d371ab3..00000000 --- a/tactility/src/apps/wifi_manage/wifi_manage_view.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "app.h" -#include "lvgl.h" -#include "wifi_manage_bindings.h" -#include "wifi_manage_state.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - lv_obj_t* root; - lv_obj_t* enable_switch; - lv_obj_t* scanning_spinner; - lv_obj_t* networks_label; - lv_obj_t* networks_list; - lv_obj_t* connected_ap_container; - lv_obj_t* connected_ap_label; -} WifiManageView; - -void wifi_manage_view_create(App app, WifiManageView* view, WifiManageBindings* bindings, lv_obj_t* parent); -void wifi_manage_view_update(WifiManageView* view, WifiManageBindings* bindings, WifiManageState* state); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/lvgl_init.c b/tactility/src/lvgl_init.c deleted file mode 100644 index f50ac0af..00000000 --- a/tactility/src/lvgl_init.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "lvgl_init_i.h" -#include "preferences.h" -#include "lvgl.h" - -void tt_lvgl_init(const HardwareConfig* config) { - SetBacklightDuty set_backlight_duty = config->display.set_backlight_duty; - if (set_backlight_duty != NULL) { - int32_t backlight_duty = 200; - if (!tt_preferences()->opt_int32("display", "backlight_duty", &backlight_duty)) { - tt_preferences()->put_int32("display", "backlight_duty", backlight_duty); - } - int32_t safe_backlight_duty = TT_MIN(backlight_duty, 255); - set_backlight_duty((uint8_t)safe_backlight_duty); - } - - int32_t rotation; - if (tt_preferences()->opt_int32("display", "rotation", &rotation)) { - if (rotation != LV_DISPLAY_ROTATION_0) { - lv_disp_set_rotation(lv_disp_get_default(), rotation); - } - } -} diff --git a/tactility/src/lvgl_init_i.h b/tactility/src/lvgl_init_i.h deleted file mode 100644 index a7509690..00000000 --- a/tactility/src/lvgl_init_i.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "hardware_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void tt_lvgl_init(const HardwareConfig* config); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility/src/services/gui/gui.c b/tactility/src/services/gui/gui.c deleted file mode 100644 index 548d55d9..00000000 --- a/tactility/src/services/gui/gui.c +++ /dev/null @@ -1,163 +0,0 @@ -#include "gui_i.h" - -#include "tactility.h" -#include "services/loader/loader.h" -#include "ui/lvgl_sync.h" -#include "ui/lvgl_keypad.h" - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#else -#include "FreeRTOS.h" -#endif - -#define TAG "gui" - -// Forward declarations -void gui_redraw(Gui*); -static int32_t gui_main(void*); - -Gui* gui = NULL; - -typedef void (*PubSubCallback)(const void* message, void* context); -void gui_loader_callback(const void* message, void* context) { - Gui* gui = (Gui*)context; - LoaderEvent* event = (LoaderEvent*)message; - if (event->type == LoaderEventTypeApplicationShowing) { - App* app = event->app_showing.app; - const AppManifest* app_manifest = tt_app_get_manifest(app); - gui_show_app(app, app_manifest->on_show, app_manifest->on_hide); - } else if (event->type == LoaderEventTypeApplicationHiding) { - gui_hide_app(); - } -} - -Gui* gui_alloc() { - Gui* instance = malloc(sizeof(Gui)); - memset(instance, 0, sizeof(Gui)); - tt_check(instance != NULL); - instance->thread = tt_thread_alloc_ex( - "gui", - 4096, // Last known minimum was 2800 for launching desktop - &gui_main, - NULL - ); - instance->mutex = tt_mutex_alloc(MutexTypeRecursive); - instance->keyboard = NULL; - instance->loader_pubsub_subscription = tt_pubsub_subscribe(loader_get_pubsub(), &gui_loader_callback, instance); - tt_check(tt_lvgl_lock(1000 / portTICK_PERIOD_MS)); - instance->keyboard_group = lv_group_create(); - instance->lvgl_parent = lv_scr_act(); - tt_lvgl_unlock(); - - return instance; -} - -void gui_free(Gui* instance) { - tt_assert(instance != NULL); - tt_thread_free(instance->thread); - tt_mutex_free(instance->mutex); - - tt_check(tt_lvgl_lock(1000 / portTICK_PERIOD_MS)); - lv_group_del(instance->keyboard_group); - tt_lvgl_unlock(); - - free(instance); -} - -void gui_lock() { - tt_assert(gui); - tt_assert(gui->mutex); - tt_check(tt_mutex_acquire(gui->mutex, configTICK_RATE_HZ) == TtStatusOk); -} - -void gui_unlock() { - tt_assert(gui); - tt_assert(gui->mutex); - tt_check(tt_mutex_release(gui->mutex) == TtStatusOk); -} - -void gui_request_draw() { - tt_assert(gui); - ThreadId thread_id = tt_thread_get_id(gui->thread); - tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW); -} - -void gui_show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) { - gui_lock(); - tt_check(gui->app_view_port == NULL); - gui->app_view_port = view_port_alloc(app, on_show, on_hide); - gui_unlock(); - gui_request_draw(); -} - -void gui_hide_app() { - gui_lock(); - ViewPort* view_port = gui->app_view_port; - tt_check(view_port != NULL); - - // We must lock the LVGL port, because the viewport hide callbacks - // might call LVGL APIs (e.g. to remove the keyboard from the screen root) - tt_check(tt_lvgl_lock(configTICK_RATE_HZ)); - view_port_hide(view_port); - tt_lvgl_unlock(); - - view_port_free(view_port); - gui->app_view_port = NULL; - gui_unlock(); -} - -static int32_t gui_main(TT_UNUSED void* p) { - tt_check(gui); - Gui* local_gui = gui; - - while (1) { - uint32_t flags = tt_thread_flags_wait( - GUI_THREAD_FLAG_ALL, - TtFlagWaitAny, - TtWaitForever - ); - // Process and dispatch draw call - if (flags & GUI_THREAD_FLAG_DRAW) { - tt_thread_flags_clear(GUI_THREAD_FLAG_DRAW); - gui_redraw(local_gui); - } - - if (flags & GUI_THREAD_FLAG_EXIT) { - tt_thread_flags_clear(GUI_THREAD_FLAG_EXIT); - break; - } - } - - return 0; -} - -// region AppManifest - -static void gui_start(TT_UNUSED Service service) { - gui = gui_alloc(); - - tt_thread_set_priority(gui->thread, THREAD_PRIORITY_SERVICE); - tt_thread_start(gui->thread); -} - -static void gui_stop(TT_UNUSED Service service) { - gui_lock(); - - ThreadId thread_id = tt_thread_get_id(gui->thread); - tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT); - tt_thread_join(gui->thread); - tt_thread_free(gui->thread); - - gui_unlock(); - - gui_free(gui); -} - -const ServiceManifest gui_service = { - .id = "gui", - .on_start = &gui_start, - .on_stop = &gui_stop -}; - -// endregion diff --git a/tactility/src/services/gui/icon.c b/tactility/src/services/gui/icon.c deleted file mode 100644 index 98f35b12..00000000 --- a/tactility/src/services/gui/icon.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "icon_i.h" - -uint8_t icon_get_width(const Icon* instance) { - return instance->width; -} - -uint8_t icon_get_height(const Icon* instance) { - return instance->height; -} - -const uint8_t* icon_get_data(const Icon* instance) { - return instance->frames[0]; -} diff --git a/tactility/src/services/gui/icon.h b/tactility/src/services/gui/icon.h deleted file mode 100644 index 2c182312..00000000 --- a/tactility/src/services/gui/icon.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -struct Icon { - const uint8_t width; - const uint8_t height; - const uint8_t frame_count; - const uint8_t frame_rate; - const uint8_t* const* frames; -}; diff --git a/tactility/src/services/gui/icon_i.h b/tactility/src/services/gui/icon_i.h deleted file mode 100644 index 565dde34..00000000 --- a/tactility/src/services/gui/icon_i.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include "icon.h" - -typedef struct { - const uint8_t width; - const uint8_t height; - const uint8_t frame_count; - const uint8_t frame_rate; - const uint8_t* const* frames; -} Icon; diff --git a/tactility/src/services/loader/loader_i.h b/tactility/src/services/loader/loader_i.h deleted file mode 100644 index 9f0cda6b..00000000 --- a/tactility/src/services/loader/loader_i.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "api_lock.h" -#include "app_manifest.h" -#include "loader.h" -#include "message_queue.h" -#include "pubsub.h" -#include "services/gui/view_port.h" -#include "thread.h" - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#else -#include "FreeRTOS.h" -#include "semphr.h" -#endif - -#define APP_STACK_SIZE 32 - -struct Loader { - Thread* thread; - PubSub* pubsub_internal; - PubSub* pubsub_external; - MessageQueue* queue; - Mutex* mutex; - int8_t app_stack_index; - App app_stack[APP_STACK_SIZE]; -}; - -typedef enum { - LoaderMessageTypeAppStart, - LoaderMessageTypeAppStop, - LoaderMessageTypeServiceStop, -} LoaderMessageType; - -typedef struct { - const char* id; - Bundle _Nullable bundle; -} LoaderMessageAppStart; - -typedef struct { - LoaderStatus value; -} LoaderMessageLoaderStatusResult; - -typedef struct { - bool value; -} LoaderMessageBoolResult; - -typedef struct { - // This lock blocks anyone from starting an app as long - // as an app is already running via loader_start() - ApiLock api_lock; - LoaderMessageType type; - - union { - LoaderMessageAppStart start; - }; - - union { - LoaderMessageLoaderStatusResult* status_value; - LoaderMessageBoolResult* bool_value; - }; -} LoaderMessage; diff --git a/tactility/src/services/screenshot/screenshot.c b/tactility/src/services/screenshot/screenshot.c deleted file mode 100644 index 7b5fe2c4..00000000 --- a/tactility/src/services/screenshot/screenshot.c +++ /dev/null @@ -1,133 +0,0 @@ -#include "screenshot.h" - -#include "mutex.h" -#include "screenshot_task.h" -#include "service.h" -#include "service_registry.h" -#include "tactility_core.h" - -#define TAG "sdcard_service" - -typedef struct { - Mutex* mutex; - ScreenshotTask* task; - ScreenshotMode mode; -} ServiceData; - -static ServiceData* service_data_alloc() { - ServiceData* data = malloc(sizeof(ServiceData)); - *data = (ServiceData) { - .mutex = tt_mutex_alloc(MutexTypeNormal), - .task = NULL - }; - return data; -} - -static void service_data_free(ServiceData* data) { - tt_mutex_free(data->mutex); -} - -static void service_data_lock(ServiceData* data) { - tt_check(tt_mutex_acquire(data->mutex, TtWaitForever) == TtStatusOk); -} - -static void service_data_unlock(ServiceData* data) { - tt_check(tt_mutex_release(data->mutex) == TtStatusOk); -} - -static void on_start(Service service) { - ServiceData* data = service_data_alloc(); - tt_service_set_data(service, data); -} - -static void on_stop(Service service) { - ServiceData* data = tt_service_get_data(service); - if (data->task) { - screenshot_task_free(data->task); - data->task = NULL; - } - tt_mutex_free(data->mutex); - service_data_free(data); -} - -const ServiceManifest screenshot_service = { - .id = "screenshot", - .on_start = &on_start, - .on_stop = &on_stop -}; - -void tt_screenshot_start_apps(const char* path) { - Service _Nullable service = tt_service_find(screenshot_service.id); - if (service == NULL) { - TT_LOG_E(TAG, "Service not found"); - return; - } - - ServiceData* data = tt_service_get_data(service); - service_data_lock(data); - if (data->task == NULL) { - data->task = screenshot_task_alloc(); - data->mode = ScreenshotModeApps; - screenshot_task_start_apps(data->task, path); - } else { - TT_LOG_E(TAG, "Screenshot task already running"); - } - service_data_unlock(data); -} - -void tt_screenshot_start_timed(const char* path, uint8_t delay_in_seconds, uint8_t amount) { - Service _Nullable service = tt_service_find(screenshot_service.id); - if (service == NULL) { - TT_LOG_E(TAG, "Service not found"); - return; - } - - ServiceData* data = tt_service_get_data(service); - service_data_lock(data); - if (data->task == NULL) { - data->task = screenshot_task_alloc(); - data->mode = ScreenshotModeTimed; - screenshot_task_start_timed(data->task, path, delay_in_seconds, amount); - } else { - TT_LOG_E(TAG, "Screenshot task already running"); - } - service_data_unlock(data); -} - -void tt_screenshot_stop() { - Service _Nullable service = tt_service_find(screenshot_service.id); - if (service == NULL) { - TT_LOG_E(TAG, "Service not found"); - return; - } - - ServiceData* data = tt_service_get_data(service); - service_data_lock(data); - if (data->task != NULL) { - screenshot_task_stop(data->task); - screenshot_task_free(data->task); - data->task = NULL; - data->mode = ScreenshotModeNone; - } else { - TT_LOG_E(TAG, "Screenshot task not running"); - } - service_data_unlock(data); -} - -ScreenshotMode tt_screenshot_get_mode() { - Service _Nullable service = tt_service_find(screenshot_service.id); - if (service == NULL) { - TT_LOG_E(TAG, "Service not found"); - return ScreenshotModeNone; - } else { - ServiceData* data = tt_service_get_data(service); - service_data_lock(data); - ScreenshotMode mode = data->mode; - service_data_unlock(data); - return mode; - } -} - -bool tt_screenshot_is_started() { - return tt_screenshot_get_mode() != ScreenshotModeNone; -} diff --git a/tactility/src/tactility.c b/tactility/src/tactility.c deleted file mode 100644 index 3e29adbd..00000000 --- a/tactility/src/tactility.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "tactility.h" - -#include "app_manifest_registry.h" -#include "lvgl_init_i.h" -#include "service_registry.h" -#include "services/loader/loader.h" -#include "tactility_headless.h" - -#define TAG "tactility" - -static const Config* config_instance = NULL; - -// region Default services - -extern const ServiceManifest gui_service; -extern const ServiceManifest loader_service; -extern const ServiceManifest screenshot_service; -extern const ServiceManifest sdcard_service; -extern const ServiceManifest wifi_service; -extern const ServiceManifest statusbar_updater_service; - -static const ServiceManifest* const system_services[] = { - &loader_service, - &gui_service, // depends on loader service -#ifndef ESP_PLATFORM // Screenshots don't work yet on ESP32 - &screenshot_service, -#endif - &statusbar_updater_service -}; - -// endregion - -// region Default apps - -extern const AppManifest desktop_app; -extern const AppManifest display_app; -extern const AppManifest files_app; -extern const AppManifest gpio_app; -extern const AppManifest image_viewer_app; -extern const AppManifest power_app; -extern const AppManifest settings_app; -extern const AppManifest system_info_app; -extern const AppManifest text_viewer_app; -extern const AppManifest wifi_connect_app; -extern const AppManifest wifi_manage_app; - -#ifndef ESP_PLATFORM -extern const AppManifest screenshot_app; -#endif - -static const AppManifest* const system_apps[] = { - &desktop_app, - &display_app, - &files_app, - &gpio_app, - &image_viewer_app, - &settings_app, - &system_info_app, - &text_viewer_app, - &wifi_connect_app, - &wifi_manage_app, -#ifndef ESP_PLATFORM - &screenshot_app, // Screenshots don't work yet on ESP32 -#endif -}; - -// endregion - -static void register_system_apps() { - TT_LOG_I(TAG, "Registering default apps"); - - int app_count = sizeof(system_apps) / sizeof(AppManifest*); - for (int i = 0; i < app_count; ++i) { - tt_app_manifest_registry_add(system_apps[i]); - } - - if (tt_get_config()->hardware->power != NULL) { - tt_app_manifest_registry_add(&power_app); - } -} - -static void register_user_apps(const AppManifest* const apps[TT_CONFIG_APPS_LIMIT]) { - TT_LOG_I(TAG, "Registering user apps"); - for (size_t i = 0; i < TT_CONFIG_APPS_LIMIT; i++) { - const AppManifest* manifest = apps[i]; - if (manifest != NULL) { - tt_app_manifest_registry_add(manifest); - } else { - // reached end of list - break; - } - } -} - -static void register_and_start_system_services() { - TT_LOG_I(TAG, "Registering and starting system services"); - int app_count = sizeof(system_services) / sizeof(ServiceManifest*); - for (int i = 0; i < app_count; ++i) { - tt_service_registry_add(system_services[i]); - tt_check(tt_service_registry_start(system_services[i]->id)); - } -} - -static void register_and_start_user_services(const ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT]) { - TT_LOG_I(TAG, "Registering and starting user services"); - for (size_t i = 0; i < TT_CONFIG_SERVICES_LIMIT; i++) { - const ServiceManifest* manifest = services[i]; - if (manifest != NULL) { - tt_service_registry_add(manifest); - tt_check(tt_service_registry_start(manifest->id)); - } else { - // reached end of list - break; - } - } -} -void tt_init(const Config* config) { - TT_LOG_I(TAG, "tt_init started"); - - - // Assign early so starting services can use it - config_instance = config; - - tt_headless_init(config->hardware); - - tt_lvgl_init(config->hardware); - - tt_app_manifest_registry_init(); - - // Note: the order of starting apps and services is critical! - // System services are registered first so the apps below can find them if needed - register_and_start_system_services(); - // Then we register system apps. They are not used/started yet. - register_system_apps(); - // Then we register and start user services. They are started after system app - // registration just in case they want to figure out which system apps are installed. - register_and_start_user_services(config->services); - // Now we register the user apps, as they might rely on the user services. - register_user_apps(config->apps); - - TT_LOG_I(TAG, "tt_init starting desktop app"); - loader_start_app(desktop_app.id, true, NULL); - - if (config->auto_start_app_id) { - TT_LOG_I(TAG, "tt_init auto-starting %s", config->auto_start_app_id); - loader_start_app(config->auto_start_app_id, true, NULL); - } - - TT_LOG_I(TAG, "tt_init complete"); -} - -const Config* _Nullable tt_get_config() { - return config_instance; -} diff --git a/tactility/src/tactility_config.h b/tactility/src/tactility_config.h deleted file mode 100644 index 9fd1e7ae..00000000 --- a/tactility/src/tactility_config.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "tactility_headless_config.h" - -#define TT_CONFIG_APPS_LIMIT 32 -#define TT_CONFIG_FORCE_ONSCREEN_KEYBOARD false \ No newline at end of file diff --git a/tactility/src/ui/label_utils.h b/tactility/src/ui/label_utils.h deleted file mode 100644 index 7627e5ef..00000000 --- a/tactility/src/ui/label_utils.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "lvgl.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void tt_lv_label_set_text_file(lv_obj_t* label, const char* filepath); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/tactility/src/ui/lvgl_sync.c b/tactility/src/ui/lvgl_sync.c deleted file mode 100644 index 9a87efda..00000000 --- a/tactility/src/ui/lvgl_sync.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "lvgl_sync.h" - -static LvglLock lock_singleton = NULL; -static LvglUnlock unlock_singleton = NULL; - -void tt_lvgl_sync_set(LvglLock lock, LvglUnlock unlock) { - lock_singleton = lock; - unlock_singleton = unlock; -} - -bool tt_lvgl_lock(uint32_t timeout_ticks) { - if (lock_singleton) { - return lock_singleton(timeout_ticks); - } else { - return true; - } -} - -void tt_lvgl_unlock() { - if (unlock_singleton) { - unlock_singleton(); - } -} diff --git a/tactility/src/ui/lvgl_sync.h b/tactility/src/ui/lvgl_sync.h deleted file mode 100644 index 47a4c409..00000000 --- a/tactility/src/ui/lvgl_sync.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef bool (*LvglLock)(uint32_t timeout_ticks); -typedef void (*LvglUnlock)(); - -void tt_lvgl_sync_set(LvglLock lock, LvglUnlock unlock); -bool tt_lvgl_lock(uint32_t timeout_ticks); -void tt_lvgl_unlock(); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/ui/spacer.c b/tactility/src/ui/spacer.c deleted file mode 100644 index 95a3cac3..00000000 --- a/tactility/src/ui/spacer.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "spacer.h" -#include "style.h" - -lv_obj_t* tt_lv_spacer_create(lv_obj_t* parent, int32_t width, int32_t height) { - lv_obj_t* spacer = lv_obj_create(parent); - lv_obj_set_size(spacer, width, height); - tt_lv_obj_set_style_bg_invisible(spacer); - return spacer; -} diff --git a/tactility/src/ui/spacer.h b/tactility/src/ui/spacer.h deleted file mode 100644 index c1ff417b..00000000 --- a/tactility/src/ui/spacer.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "lvgl.h" - -#ifdef __cplusplus -extern "C" { -#endif - -lv_obj_t* tt_lv_spacer_create(lv_obj_t* parent, int32_t width, int32_t height); - -#ifdef __cplusplus - } -#endif diff --git a/tactility/src/ui/statusbar.h b/tactility/src/ui/statusbar.h deleted file mode 100644 index 4b4f9a67..00000000 --- a/tactility/src/ui/statusbar.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "lvgl.h" -#include "app.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define STATUSBAR_ICON_LIMIT 8 -#define STATUSBAR_ICON_SIZE 20 -#define STATUSBAR_HEIGHT (STATUSBAR_ICON_SIZE + 4) // 4 extra pixels for border and outline - -lv_obj_t* tt_statusbar_create(lv_obj_t* parent); -int8_t tt_statusbar_icon_add(const char* _Nullable image); -void tt_statusbar_icon_remove(int8_t id); -void tt_statusbar_icon_set_image(int8_t id, const char* image); -void tt_statusbar_icon_set_visibility(int8_t id, bool visible); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/ui/style.h b/tactility/src/ui/style.h deleted file mode 100644 index 5816fa3d..00000000 --- a/tactility/src/ui/style.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "lvgl.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void tt_lv_obj_set_style_bg_blacken(lv_obj_t* obj); -void tt_lv_obj_set_style_bg_invisible(lv_obj_t* obj); -void tt_lv_obj_set_style_no_padding(lv_obj_t* obj); - -/** - * This is to create automatic padding depending on the screen size. - * The larger the screen, the more padding it gets. - * TODO: It currently only applies a single basic padding, but will be improved later. - * - * @param obj - */ -void tt_lv_obj_set_style_auto_padding(lv_obj_t* obj); - -#ifdef __cplusplus -} -#endif diff --git a/tactility/src/ui/toolbar.h b/tactility/src/ui/toolbar.h deleted file mode 100644 index bfb72583..00000000 --- a/tactility/src/ui/toolbar.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "lvgl.h" -#include "app.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define TOOLBAR_HEIGHT 40 -#define TOOLBAR_ACTION_LIMIT 8 -#define TOOLBAR_TITLE_FONT_HEIGHT 18 - -typedef void(*ToolbarActionCallback)(void* _Nullable context); - -typedef struct { - const char* icon; - const char* text; - ToolbarActionCallback callback; - void* _Nullable callback_context; -} ToolbarAction; - -lv_obj_t* tt_toolbar_create(lv_obj_t* parent, const char* title); -lv_obj_t* tt_toolbar_create_for_app(lv_obj_t* parent, App app); -void tt_toolbar_set_title(lv_obj_t* obj, const char* title); -void tt_toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data); -uint8_t tt_toolbar_add_action(lv_obj_t* obj, const char* icon, const char* text, lv_event_cb_t callback, void* user_data); - -#ifdef __cplusplus -} -#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index ac057c75..00000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -project(tests) - -set(DOCTESTINC ${PROJECT_SOURCE_DIR}/include) - -enable_testing() -add_subdirectory(tactility-core) - -add_custom_target(build-tests) -add_dependencies(build-tests tactility-core-tests) diff --git a/tests/tactility-core/CMakeLists.txt b/tests/tactility-core/CMakeLists.txt deleted file mode 100644 index 9af07fe3..00000000 --- a/tests/tactility-core/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -project(tactility-core-tests) - -enable_language(C CXX ASM) - -set(CMAKE_CXX_COMPILER g++) - -file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/*.cpp) -add_executable(tactility-core-tests EXCLUDE_FROM_ALL ${TEST_SOURCES}) - -target_include_directories(tactility-core-tests PRIVATE - ${DOCTESTINC} -) - -add_test(NAME tactility-core-tests - COMMAND tactility-core-tests -) - -target_link_libraries(tactility-core-tests - PUBLIC tactility-core - freertos_kernel -) diff --git a/tests/tactility-core/bundle_test.cpp b/tests/tactility-core/bundle_test.cpp deleted file mode 100644 index fd094fe7..00000000 --- a/tests/tactility-core/bundle_test.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "doctest.h" -#include "bundle.h" -#include - -TEST_CASE("boolean can be stored and retrieved") { - Bundle bundle = tt_bundle_alloc(); - tt_bundle_put_bool(bundle, "key", true); - CHECK(tt_bundle_get_bool(bundle, "key")); - bool opt_result = false; - CHECK(tt_bundle_opt_bool(bundle, "key", &opt_result)); - CHECK(tt_bundle_has_bool(bundle, "key")); - CHECK_EQ(opt_result, true); - tt_bundle_free(bundle); -} - -TEST_CASE("int32 can be stored and retrieved") { - Bundle bundle = tt_bundle_alloc(); - tt_bundle_put_int32(bundle, "key", 42); - CHECK(tt_bundle_get_int32(bundle, "key")); - int32_t opt_result = 0; - CHECK(tt_bundle_opt_int32(bundle, "key", &opt_result)); - CHECK(tt_bundle_has_int32(bundle, "key")); - CHECK_EQ(opt_result, 42); - tt_bundle_free(bundle); -} - -TEST_CASE("string can be stored and retrieved") { - Bundle bundle = tt_bundle_alloc(); - tt_bundle_put_string(bundle, "key", "value"); - const char* value_from_bundle = tt_bundle_get_string(bundle, "key"); - CHECK_EQ(strcmp(value_from_bundle, "value"), 0); - char* opt_result = NULL; - CHECK(tt_bundle_opt_string(bundle, "key", &opt_result)); - CHECK(tt_bundle_has_string(bundle, "key")); - CHECK(opt_result != NULL); - tt_bundle_free(bundle); -} - -TEST_CASE("bundle copy holds all copied values when original is freed") { - Bundle original = tt_bundle_alloc(); - tt_bundle_put_bool(original, "bool", true); - tt_bundle_put_int32(original, "int32", 123); - tt_bundle_put_string(original, "string", "text"); - - Bundle copy = tt_bundle_alloc_copy(original); - tt_bundle_free(original); - - CHECK(tt_bundle_get_bool(copy, "bool") == true); - CHECK_EQ(tt_bundle_get_int32(copy, "int32"), 123); - CHECK_EQ(strcmp(tt_bundle_get_string(copy, "string"), "text"), 0); - tt_bundle_free(copy); -} diff --git a/tests/tactility-core/dispatcher_test.cpp b/tests/tactility-core/dispatcher_test.cpp deleted file mode 100644 index d5f5ccd6..00000000 --- a/tests/tactility-core/dispatcher_test.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "doctest.h" -#include "tactility_core.h" -#include "dispatcher.h" - -void increment_callback(void* context) { - auto* counter = (uint32_t*)context; - (*counter)++; -} - -TEST_CASE("dispatcher should not call callback if consume isn't called") { - Dispatcher* dispatcher = tt_dispatcher_alloc(10); - - uint32_t counter = 0; - tt_dispatcher_dispatch(dispatcher, &increment_callback, &counter); - tt_delay_tick(10); - CHECK_EQ(counter, 0); - - tt_dispatcher_free(dispatcher); -} - - -TEST_CASE("dispatcher should be able to dealloc when message is not consumed") { - Dispatcher* dispatcher = tt_dispatcher_alloc(10); - uint32_t counter = 0; - tt_dispatcher_dispatch(dispatcher, increment_callback, &counter); - tt_dispatcher_free(dispatcher); -} - -TEST_CASE("dispatcher should call callback when consume is called") { - Dispatcher* dispatcher = tt_dispatcher_alloc(10); - - uint32_t counter = 0; - tt_dispatcher_dispatch(dispatcher, increment_callback, &counter); - tt_dispatcher_consume(dispatcher, 100); - CHECK_EQ(counter, 1); - - tt_dispatcher_free(dispatcher); -} diff --git a/tests/tactility-core/message_queue_test.cpp b/tests/tactility-core/message_queue_test.cpp deleted file mode 100644 index d31cdbf4..00000000 --- a/tests/tactility-core/message_queue_test.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "doctest.h" -#include "message_queue.h" - -TEST_CASE("message queue capacity should be correct") { - MessageQueue* queue = tt_message_queue_alloc(10, 1); - - uint32_t capacity = tt_message_queue_get_capacity(queue); - CHECK_EQ(capacity, 10); - - tt_message_queue_free(queue); -} - -TEST_CASE("message queue initial count should be 0") { - MessageQueue* queue = tt_message_queue_alloc(10, 1); - - uint32_t count = tt_message_queue_get_count(queue); - CHECK_EQ(count, 0); - - tt_message_queue_free(queue); -} - -TEST_CASE("message queue size should be correct") { - MessageQueue* queue = tt_message_queue_alloc(1, 123); - - uint32_t size = tt_message_queue_get_message_size(queue); - CHECK_EQ(size, 123); - - tt_message_queue_free(queue); -} - -TEST_CASE("message queue count should increase when message is added") { - MessageQueue* queue = tt_message_queue_alloc(10, sizeof(uint32_t)); - - uint32_t message = 123; - - tt_message_queue_put(queue, &message, 100); - uint32_t count = tt_message_queue_get_count(queue); - CHECK_EQ(count, 1); - - tt_message_queue_free(queue); -} - -TEST_CASE("message queue count should be 0 when message is added and queue is reset") { - MessageQueue* queue = tt_message_queue_alloc(10, sizeof(uint32_t)); - - uint32_t message = 123; - - tt_message_queue_put(queue, &message, 100); - tt_message_queue_reset(queue); - uint32_t count = tt_message_queue_get_count(queue); - CHECK_EQ(count, 0); - - tt_message_queue_free(queue); -} - -TEST_CASE("message queue consumption should work") { - MessageQueue* queue = tt_message_queue_alloc(10, sizeof(uint32_t)); - - uint32_t out_message = 123; - tt_message_queue_put(queue, &out_message, 100); - - uint32_t in_message = 0; - tt_message_queue_get(queue, &in_message, 100); - CHECK_EQ(in_message, 123); - - tt_message_queue_free(queue); -} - -TEST_CASE("message queue count should decrease when message is consumed") { - MessageQueue* queue = tt_message_queue_alloc(10, sizeof(uint32_t)); - - uint32_t out_message = 123; - tt_message_queue_put(queue, &out_message, 100); - - uint32_t in_message = 0; - tt_message_queue_get(queue, &in_message, 100); - uint32_t count = tt_message_queue_get_count(queue); - CHECK_EQ(count, 0); - - tt_message_queue_free(queue); -} diff --git a/tests/tactility-core/mutex_test.cpp b/tests/tactility-core/mutex_test.cpp deleted file mode 100644 index 829f1039..00000000 --- a/tests/tactility-core/mutex_test.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "doctest.h" -#include "tactility_core.h" -#include "mutex.h" - -static int thread_with_mutex_parameter(void* parameter) { - Mutex* mutex = (Mutex*)parameter; - tt_mutex_acquire(mutex, TtWaitForever); - return 0; -} - -TEST_CASE("a mutex can block a thread") { - Mutex* mutex = tt_mutex_alloc(MutexTypeNormal); - tt_mutex_acquire(mutex, TtWaitForever); - - Thread* thread = tt_thread_alloc_ex( - "thread", - 1024, - &thread_with_mutex_parameter, - mutex - ); - tt_thread_start(thread); - - tt_delay_ms(5); - CHECK_EQ(tt_thread_get_state(thread), ThreadStateRunning); - - tt_mutex_release(mutex); - - tt_delay_ms(5); - CHECK_EQ(tt_thread_get_state(thread), ThreadStateStopped); - - tt_thread_join(thread); - tt_thread_free(thread); - tt_mutex_free(mutex); -} diff --git a/tests/tactility-core/timer_test.cpp b/tests/tactility-core/timer_test.cpp deleted file mode 100644 index 140c1131..00000000 --- a/tests/tactility-core/timer_test.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "doctest.h" -#include "tactility_core.h" -#include "timer.h" - -void* timer_callback_context = NULL; -static void timer_callback_with_context(void* context) { - timer_callback_context = context; -} - -static void timer_callback_with_counter(void* context) { - int* int_ptr = (int*)context; - (*int_ptr)++; -} - -TEST_CASE("a timer passes the context correctly") { - int foo = 1; - Timer* timer = tt_timer_alloc(&timer_callback_with_context, TimerTypeOnce, &foo); - tt_timer_start(timer, 1); - tt_delay_tick(10); - tt_timer_stop(timer); - tt_timer_free(timer); - - CHECK_EQ(timer_callback_context, &foo); -} - -TEST_CASE("TimerTypePeriodic timers can be stopped and restarted") { - int counter = 0; - Timer* timer = tt_timer_alloc(&timer_callback_with_counter, TimerTypePeriodic, &counter); - tt_timer_start(timer, 1); - tt_delay_tick(10); - tt_timer_stop(timer); - tt_delay_tick(10); - tt_timer_stop(timer); - tt_timer_free(timer); - - CHECK_GE(counter, 2); -} - -TEST_CASE("TimerTypePeriodic calls the callback periodically") { - int counter = 0; - int ticks_to_run = 10; - Timer* timer = tt_timer_alloc(&timer_callback_with_counter, TimerTypePeriodic, &counter); - tt_timer_start(timer, 1); - tt_delay_tick(ticks_to_run); - tt_timer_stop(timer); - tt_timer_free(timer); - - CHECK_EQ(counter, ticks_to_run); -} - -TEST_CASE("restarting TimerTypeOnce timers has no effect") { - int counter = 0; - Timer* timer = tt_timer_alloc(&timer_callback_with_counter, TimerTypeOnce, &counter); - tt_timer_start(timer, 1); - tt_delay_tick(10); - tt_timer_stop(timer); - tt_delay_tick(10); - tt_timer_stop(timer); - tt_timer_free(timer); - - CHECK_EQ(counter, 1); -}