Compare commits

...

5 Commits

Author SHA1 Message Date
Lamdonn
bf21a64ab2 Add the coroutine readme
Add feature to bind CoTask and CoTimer to the specified CoScheduler
Improve functional safety
TODO:
1. Compatibility with coroutine API highly intensive IO, sockets, etc
2. Fix running fail on arm64
2025-11-20 00:58:03 +08:00
Lamdonn
f2b0a31bb3 1. Fix test_floatl.c compile fail issue
2. Update muti-core compile run.sh
2025-11-19 22:31:52 +08:00
Lamdonn
49025692ca Add the initial version coroutine module
TODO:
1. Add feature to bind CoTask and CoTimer to the specified CoScheduler
2. Compatibility with coroutine API highly intensive IO, sockets, etc
3. Improve functional safety
2025-11-11 22:42:25 +08:00
Lamdonn
70d1741bab 1. Fix set and map iterate looping error issue
2. Fix the list pop back macro definition
2025-10-10 20:25:47 +08:00
Lamdonn
a15641c42e Add the initial version yaml parser 2025-05-10 21:28:42 +08:00
26 changed files with 13015 additions and 34 deletions

View File

@ -2,7 +2,7 @@
![logo](/image/logo.png) ![logo](/image/logo.png)
[![Version](https://img.shields.io/badge/version-0.3.3-blue.svg)](https://gitee.com/Lamdonn/varch) [![Version](https://img.shields.io/badge/version-0.3.5-blue.svg)](https://gitee.com/Lamdonn/varch)
[![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE) [![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE)
[![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com) [![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com)
![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg) ![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg)
@ -11,14 +11,14 @@
[中文版](README.md) [中文版](README.md)
varch (we architecture, meaning our framework library) is a commonly used code module library for embedded C language, including the commonly used embedded algorithm library (search, check, filter, hash, sort, etc.), data structure/container library (general queue, stack, heap, list, dynamic array, string, set, dictionary, map, graph, etc.), parser library (csv, ini, json, xml, etc.), mathematical library (large number operation, mathematical operation), independent C language st d library, utility library, etc. varch (we architecture, meaning our framework library) is a commonly used code module library for embedded C language, including the commonly used embedded algorithm library (search, check, filter, hash, sort, etc.), data structure/container library (general queue, stack, heap, list, dynamic array, string, set, dictionary, map, graph, etc.), parser library (csv, ini, json, xml, etc.), mathematical library (large number operation, mathematical operation), C language coroutine library, independent C language st d library, utility library, etc.
It has the characteristics of **simplicity, universality, and efficiency**, with the aim of **learning** and **using it immediately** in development, improving development efficiency and code reliability and stability. It has the characteristics of **simplicity, universality, and efficiency**, with the aim of **learning** and **using it immediately** in development, improving development efficiency and code reliability and stability.
## Content ## Content
| module | version | usage | path | describe | | module | version | usage | path | describe |
|:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------| |:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------|
| overall | 00.03.03 | [link](README.en.md) | [path](./) | Overall | overall | 00.03.05 | [link](README.en.md) | [path](./) | Overall
| init | 01.00.00 | [link](/doc/init.en.md) | [path](./source/00_application) | Initialize export module | init | 01.00.00 | [link](/doc/init.en.md) | [path](./source/00_application) | Initialize export module
| console | 01.00.01 | [link](/doc/console.en.md) | [path](./source/00_application/console) | Console command input, combined with the 'command' module, parsing commands entered in the console | console | 01.00.01 | [link](/doc/console.en.md) | [path](./source/00_application/console) | Console command input, combined with the 'command' module, parsing commands entered in the console
| arg | 01.00.00 | [link](/doc/arg.en.md) | [path](./source/01_general) | Indefinite parameters, obtain the number of indefinite parameters and specified parameters | arg | 01.00.00 | [link](/doc/arg.en.md) | [path](./source/01_general) | Indefinite parameters, obtain the number of indefinite parameters and specified parameters
@ -45,13 +45,13 @@ It has the characteristics of **simplicity, universality, and efficiency**, with
| queue | 01.00.00 | [link](/doc/queue.en.md) | [path](./source/03_container) | Universal queue container | queue | 01.00.00 | [link](/doc/queue.en.md) | [path](./source/03_container) | Universal queue container
| stack | 01.00.00 | [link](/doc/stack.en.md) | [path](./source/03_container) | Universal stack container | stack | 01.00.00 | [link](/doc/stack.en.md) | [path](./source/03_container) | Universal stack container
| deque | 01.00.00 | [link](/doc/deque.en.md) | [path](./source/03_container) | Universal double-end queue container | deque | 01.00.00 | [link](/doc/deque.en.md) | [path](./source/03_container) | Universal double-end queue container
| list | 01.00.00 | [link](/doc/list.en.md) | [path](./source/03_container) | Universal list container, single-link and internal iteration | list | 01.00.01 | [link](/doc/list.en.md) | [path](./source/03_container) | Universal list container, single-link and internal iteration
| vector | 01.00.00 | [link](/doc/vector.en.md) | [path](./source/03_container) | Universal vector(array) container | vector | 01.00.00 | [link](/doc/vector.en.md) | [path](./source/03_container) | Universal vector(array) container
| str | 01.00.00 | [link](/doc/str.en.md) | [path](./source/03_container) | String class | str | 01.00.00 | [link](/doc/str.en.md) | [path](./source/03_container) | String class
| dict | 01.00.00 | [link](/doc/dict.en.md) | [path](./source/03_container) | Universal dictionarie container, implementation based on hash table | dict | 01.00.00 | [link](/doc/dict.en.md) | [path](./source/03_container) | Universal dictionarie container, implementation based on hash table
| heap | 01.00.00 | [link](/doc/heap.en.md) | [path](./source/03_container) | Universal heap container | heap | 01.00.00 | [link](/doc/heap.en.md) | [path](./source/03_container) | Universal heap container
| set | 01.00.00 | [link](/doc/set.en.md) | [path](./source/03_container) | Universal set container, implementation based on RB-tree | set | 01.00.01 | [link](/doc/set.en.md) | [path](./source/03_container) | Universal set container, implementation based on RB-tree
| map | 01.00.00 | [link](/doc/map.en.md) | [path](./source/03_container) | Universal map container, implementation based on RB-tree | map | 01.00.01 | [link](/doc/map.en.md) | [path](./source/03_container) | Universal map container, implementation based on RB-tree
| tree | 01.00.00 | [link](/doc/tree.en.md) | [path](./source/03_container) | Universal tree container | tree | 01.00.00 | [link](/doc/tree.en.md) | [path](./source/03_container) | Universal tree container
| graph | 01.00.00 | [link](/doc/graph.en.md) | [path](./source/03_container) | Universal graph container | graph | 01.00.00 | [link](/doc/graph.en.md) | [path](./source/03_container) | Universal graph container
| check | 01.00.00 | [link](/doc/check.en.md) | [path](./source/04_algorithm) | Verification algorithm, sum check, parity check, XOR check, LRC check | check | 01.00.00 | [link](/doc/check.en.md) | [path](./source/04_algorithm) | Verification algorithm, sum check, parity check, XOR check, LRC check
@ -67,6 +67,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with
| json | 01.00.00 | [link](/doc/json.en.md) | [path](./source/05_parser) | JSON file parsing generator | json | 01.00.00 | [link](/doc/json.en.md) | [path](./source/05_parser) | JSON file parsing generator
| txls | 01.01.00 | [link](/doc/txls.en.md) | [path](./source/05_parser) | TXLS file parsing generator | txls | 01.01.00 | [link](/doc/txls.en.md) | [path](./source/05_parser) | TXLS file parsing generator
| xml | 01.00.00 | [link](/doc/xml.en.md) | [path](./source/05_parser) | XML file parsing generator | xml | 01.00.00 | [link](/doc/xml.en.md) | [path](./source/05_parser) | XML file parsing generator
| yaml | 00.01.00 | [link](/doc/yaml.en.md) | [path](./source/05_parser) | YAML file parsing generator
| ramt | 01.00.00 | [link](/doc/ramt.en.md) | [path](./source/06_performance) | RAM test module | ramt | 01.00.00 | [link](/doc/ramt.en.md) | [path](./source/06_performance) | RAM test module
| romt | 01.00.00 | [link](/doc/romt.en.md) | [path](./source/06_performance) | ROM test module | romt | 01.00.00 | [link](/doc/romt.en.md) | [path](./source/06_performance) | ROM test module
| cant | 00.01.00 | [link](/doc/cant.en.md) | [path](./source/06_performance) | CAN test module | cant | 00.01.00 | [link](/doc/cant.en.md) | [path](./source/06_performance) | CAN test module
@ -76,6 +77,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with
| intl | 01.01.00 | [link](/doc/intl.en.md) | [path](./source/07_math) | Large integer arithmetic module | intl | 01.01.00 | [link](/doc/intl.en.md) | [path](./source/07_math) | Large integer arithmetic module
| floatl | 01.01.01 | [link](/doc/floatl.en.md) | [path](./source/07_math) | Large floating-point arithmetic module | floatl | 01.01.01 | [link](/doc/floatl.en.md) | [path](./source/07_math) | Large floating-point arithmetic module
| flmath | 01.00.00 | [link](/doc/flmath.en.md) | [path](./source/07_math) | Large floating-point arithmetic math module | flmath | 01.00.00 | [link](/doc/flmath.en.md) | [path](./source/07_math) | Large floating-point arithmetic math module
| coroutine | 00.02.00 | [link](/doc/coroutine.en.md) | [path](./source/08_coroutine) | Coroutine module
## Usage ## Usage

View File

@ -2,7 +2,7 @@
![logo](/image/logo.png) ![logo](/image/logo.png)
[![Version](https://img.shields.io/badge/version-0.3.3-blue.svg)](https://gitee.com/Lamdonn/varch) [![Version](https://img.shields.io/badge/version-0.3.5-blue.svg)](https://gitee.com/Lamdonn/varch)
[![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE) [![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE)
[![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com) [![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com)
![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg) ![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg)
@ -11,14 +11,14 @@
[English version](README.en.md) [English version](README.en.md)
varchwe-architecture意为我们的框架库是嵌入式C语言常用代码模块库包含了嵌入式中常用的算法库查找、校验、过滤、哈希、排序等、数据结构/容器库通用队列、栈、堆、列表、动态数组、字符串、集合、字典、映射、图等、解析器库csv、ini、json、xml等、数学库大数运算、数学运算、独立C语言std库、工具库等等。 varchwe-architecture意为我们的框架库是嵌入式C语言常用代码模块库包含了嵌入式中常用的算法库查找、校验、过滤、哈希、排序等、数据结构/容器库通用队列、栈、堆、列表、动态数组、字符串、集合、字典、映射、图等、解析器库csv、ini、json、xml等、数学库大数运算、数学运算C语言协程库、独立C语言std库、工具库等等。
具有**简单, 通用, 高效**的特点,目的为了**学习**以及在开发中**拿来就用**,提高开发效率以及代码可靠稳定性。 具有**简单, 通用, 高效**的特点,目的为了**学习**以及在开发中**拿来就用**,提高开发效率以及代码可靠稳定性。
## 内容 ## 内容
| module | version | usage | path | describe | | module | version | usage | path | describe |
|:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------| |:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------|
| overall | 00.03.03 | [link](README.md) | [path](./) | 整体 | overall | 00.03.05 | [link](README.md) | [path](./) | 整体
| init | 01.00.00 | [link](/doc/init.md) | [path](./source/00_application) | 初始化导出模块 | init | 01.00.00 | [link](/doc/init.md) | [path](./source/00_application) | 初始化导出模块
| console | 01.00.01 | [link](/doc/console.md) | [path](./source/00_application/console) | 控制台命令输入,结合 `command` 模块,解析在控制台中输入的命令 | console | 01.00.01 | [link](/doc/console.md) | [path](./source/00_application/console) | 控制台命令输入,结合 `command` 模块,解析在控制台中输入的命令
| arg | 01.00.00 | [link](/doc/arg.md) | [path](./source/01_general) | 不定参数,获取不定参数和指定参数的个数 | arg | 01.00.00 | [link](/doc/arg.md) | [path](./source/01_general) | 不定参数,获取不定参数和指定参数的个数
@ -45,13 +45,13 @@ varchwe-architecture意为我们的框架库是嵌入式C语言常用
| queue | 01.00.00 | [link](/doc/queue.md) | [path](./source/03_container) | 通用队列容器 | queue | 01.00.00 | [link](/doc/queue.md) | [path](./source/03_container) | 通用队列容器
| stack | 01.00.00 | [link](/doc/stack.md) | [path](./source/03_container) | 通用栈式容器 | stack | 01.00.00 | [link](/doc/stack.md) | [path](./source/03_container) | 通用栈式容器
| deque | 01.00.00 | [link](/doc/deque.md) | [path](./source/03_container) | 通用双端队列容器 | deque | 01.00.00 | [link](/doc/deque.md) | [path](./source/03_container) | 通用双端队列容器
| list | 01.00.00 | [link](/doc/list.md) | [path](./source/03_container) | 通用列表容器,单链接和支持内部迭代器 | list | 01.00.01 | [link](/doc/list.md) | [path](./source/03_container) | 通用列表容器,单链接和支持内部迭代器
| vector | 01.00.00 | [link](/doc/vector.md) | [path](./source/03_container) | 通用向量(数组)容器 | vector | 01.00.00 | [link](/doc/vector.md) | [path](./source/03_container) | 通用向量(数组)容器
| str | 01.00.00 | [link](/doc/str.md) | [path](./source/03_container) | 字符串类 | str | 01.00.00 | [link](/doc/str.md) | [path](./source/03_container) | 字符串类
| dict | 01.00.00 | [link](/doc/dict.md) | [path](./source/03_container) | 通用字典容器,基于哈希表实现 | dict | 01.00.00 | [link](/doc/dict.md) | [path](./source/03_container) | 通用字典容器,基于哈希表实现
| heap | 01.00.00 | [link](/doc/heap.md) | [path](./source/03_container) | 通用堆容器 | heap | 01.00.00 | [link](/doc/heap.md) | [path](./source/03_container) | 通用堆容器
| set | 01.00.00 | [link](/doc/set.md) | [path](./source/03_container) | 通用集合容器基于RB-tree实现 | set | 01.00.01 | [link](/doc/set.md) | [path](./source/03_container) | 通用集合容器基于RB-tree实现
| map | 01.00.00 | [link](/doc/map.md) | [path](./source/03_container) | 通用映射容器基于RB-tree实现 | map | 01.00.01 | [link](/doc/map.md) | [path](./source/03_container) | 通用映射容器基于RB-tree实现
| tree | 01.00.00 | [link](/doc/tree.md) | [path](./source/03_container) | 通用树容器 | tree | 01.00.00 | [link](/doc/tree.md) | [path](./source/03_container) | 通用树容器
| graph | 01.00.00 | [link](/doc/graph.md) | [path](./source/03_container) | 通用图容器 | graph | 01.00.00 | [link](/doc/graph.md) | [path](./source/03_container) | 通用图容器
| check | 01.00.00 | [link](/doc/check.md) | [path](./source/04_algorithm) | 校验算法求和校验奇偶校验异或校验LRC校验 | check | 01.00.00 | [link](/doc/check.md) | [path](./source/04_algorithm) | 校验算法求和校验奇偶校验异或校验LRC校验
@ -67,6 +67,7 @@ varchwe-architecture意为我们的框架库是嵌入式C语言常用
| json | 01.00.00 | [link](/doc/json.md) | [path](./source/05_parser) | JSON文件解析生成器 | json | 01.00.00 | [link](/doc/json.md) | [path](./source/05_parser) | JSON文件解析生成器
| txls | 01.01.00 | [link](/doc/txls.md) | [path](./source/05_parser) | TXLS文件解析生成器 | txls | 01.01.00 | [link](/doc/txls.md) | [path](./source/05_parser) | TXLS文件解析生成器
| xml | 01.00.00 | [link](/doc/xml.md) | [path](./source/05_parser) | XML文件解析生成器 | xml | 01.00.00 | [link](/doc/xml.md) | [path](./source/05_parser) | XML文件解析生成器
| yaml | 00.01.00 | [link](/doc/yaml.md) | [path](./source/05_parser) | YAML文件解析生成器
| ramt | 01.00.00 | [link](/doc/ramt.md) | [path](./source/06_performance) | RAM测试模块 | ramt | 01.00.00 | [link](/doc/ramt.md) | [path](./source/06_performance) | RAM测试模块
| romt | 01.00.00 | [link](/doc/romt.md) | [path](./source/06_performance) | ROM测试模块 | romt | 01.00.00 | [link](/doc/romt.md) | [path](./source/06_performance) | ROM测试模块
| cant | 00.01.00 | [link](/doc/cant.md) | [path](./source/06_performance) | CAN测试模块 | cant | 00.01.00 | [link](/doc/cant.md) | [path](./source/06_performance) | CAN测试模块
@ -76,6 +77,7 @@ varchwe-architecture意为我们的框架库是嵌入式C语言常用
| intl | 01.01.00 | [link](/doc/intl.md) | [path](./source/07_math) | 大型整数运算模块 | intl | 01.01.00 | [link](/doc/intl.md) | [path](./source/07_math) | 大型整数运算模块
| floatl | 01.01.01 | [link](/doc/floatl.md) | [path](./source/07_math) | 大型浮点数运算模块 | floatl | 01.01.01 | [link](/doc/floatl.md) | [path](./source/07_math) | 大型浮点数运算模块
| flmath | 01.00.00 | [link](/doc/flmath.md) | [path](./source/07_math) | 大型浮点数数学运算模块 | flmath | 01.00.00 | [link](/doc/flmath.md) | [path](./source/07_math) | 大型浮点数数学运算模块
| coroutine | 00.02.00 | [link](/doc/coroutine.md) | [path](./source/08_coroutine) | 协程模块
## 使用说明 ## 使用说明

653
doc/coroutine.en.md Normal file
View File

@ -0,0 +1,653 @@
# Table of Contents
- [Overview](#overview)
- [Core Features](#core-features)
- [Architecture Design](#architecture-design)
- [API Interface Details](#api-interface-details)
- [Configuration Options](#configuration-options)
- [Usage Guide](#usage-guide)
- [Advanced Features](#advanced-features)
- [Error Handling](#error-handling)
- [Performance Optimization](#performance-optimization)
- [Platform Support](#platform-support)
- [Application Scenarios](#application-scenarios)
## Overview
The coroutine module is a lightweight user-mode thread management mechanism that plays an important role in program development. It is mainly used in scenarios requiring efficient concurrency, such as resource-constrained embedded devices, network programming, asynchronous I/O, and real-time data processing.
Compared to traditional threads, it has lower resource consumption (no kernel-mode switching required) and faster context switching speed. Through the coroutine module, developers can achieve efficient task switching and concurrent execution, thereby improving program performance and response speed.
**coroutine** is a high-performance, lightweight C language coroutine library that uses an asymmetric stackful coroutine design. This library provides complete coroutine lifecycle management, supports multi-task scheduling, timer management, and event synchronization mechanisms. It offers concise APIs for creation, scheduling, and synchronization, and can be seamlessly integrated into various environments such as embedded systems and server programs. It is also relatively simple to port, requiring only compilation and linking of the module on the target platform (mainstream chip kernel architectures are already supported).
### Design Philosophy
- **Simple and Easy to Use**: Provides intuitive API interfaces
- **Easy to Port**: Requires few resource dependencies for porting
- **High Efficiency**: Low-overhead context switching
- **Stable and Reliable**: Error handling and resource management
## Core Features
### 1. Architecture Features
- **Asymmetric Stackful Coroutines**: Each coroutine has its own independent stack space
- **Multiple Scheduler Support**: Configurable with multiple independent coroutine schedulers
- **Hybrid Memory Management**: Supports both static pre-allocation and dynamic memory management
- **Cross-Platform Compatibility**: Supports various CPU architectures such as x86, x64, ARM, MIPS (not fully tested yet)
### 2. Coroutine State Management
#### Coroutine Scheduler State Definitions
#define COSCHEDULER_STATE_INTI (0) // Initialization state
#define COSCHEDULER_STATE_EXIT (1) // Exit state
#define COSCHEDULER_STATE_START (2) // Start state
#define COSCHEDULER_STATE_RUNNING (3) // Running state
#define COSCHEDULER_STATE_SCHEDULE (4) // Scheduling state
#### Coroutine Task State Definitions
#define COTASK_STATE_INTI (0) // Initialization state
#define COTASK_STATE_READY (1) // Ready state
#define COTASK_STATE_RUNNING (2) // Running state
#define COTASK_STATE_SUSPEND (3) // Suspended state
#define COTASK_STATE_DELETED (4) // Deleted state
### 3. Memory Management Configuration
#### Static Resource Limit Configuration
#define COROUTINE_SCHEDULER_NUMBER 1 // Number of schedulers
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // Maximum number of static tasks
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // Maximum number of static timers
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // Maximum number of static stacks
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // Default stack size
## Architecture Design
### 1. Overall Architecture
+----------------------------------------------------+
| Architecture |
+------------------------------------+---------------+
| | +---------+ |
| +--------+ +--------+ | | CoTimer | |
| | | | | | +---------+ |
| | | CoEvent | | CoEvent +---------+ |
| | CoTask |<=======>| CoTask |<=======>| CoTimer | |
| | | | | | +---------+ |
| | | | | | +---------+ |
| +--------+ +--------+ | | CoTimer | |
+------------------------------------+ +---------+ |
| CoScheduler |
+----------------------------------------------------+
### 2. Coroutine Context Switching Mechanism
- Uses setjmp/longjmp to achieve fast context saving and restoration
- Each coroutine maintains independent stack space and environment
- Supports coroutine suspension, resumption, and deletion operations
## API Interface Details
### 1. Scheduler Management Interface
#### CoScheduler_Init - Initialize Coroutine Scheduler
**Function Prototype:**
#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...)
**Function Description:**
Initializes the specified coroutine scheduler, setting the clock function and clock interval. This function must be executed before calling any other coroutine functions.
It wraps the int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara); function, providing more initialization parameter configuration.
**Parameter Details:**
- CoSchedulerId **[Required]**: Scheduler ID, range 0 to (COROUTINE_SCHEDULER_NUMBER-1)
- tick **[Required]**: Clock function pointer, used to get the current timestamp
- tickInterval **[Required]**: Clock interval, representing the number of nanoseconds corresponding to each tick
- lock **[Optional]**: Lock function pointer, used for resource protection in multi-threaded environments
- unlock **[Optional]**: Unlock function pointer, used for resource release in multi-threaded environments
- malloc **[Optional]**: Memory allocation function pointer
- free **[Optional]**: Memory release function pointer
**Return Value:**
- COROUTINE_E_OK (0): Initialization successful
- COROUTINE_E_INVALID_PARAMETER (-1): Invalid parameter
- COROUTINE_E_INVALID_TICK (-2): Invalid clock function
- COROUTINE_E_INVALID_TICK_INTERVAL (-3): Invalid clock interval
- COROUTINE_E_INVALID_LOCK (-4): Invalid lock function
- COROUTINE_E_INVALID_MHOOK (-5): Invalid memory hook function
**Usage Example:**
// Basic usage: use microsecond clock, 1000us interval
CoScheduler_Init(0, GetTimerUsec, 1000);
// Multi-thread support: with lock functions
CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
// Complete configuration: with memory management functions
CoScheduler_Init(0, GetTimerUsec, 1000,
.lock=thread_lock,
.unlock=thread_unlock,
.malloc=malloc,
.free=free
);
#### CoScheduler_Start - Start Coroutine Scheduler
**Function Prototype:**
int CoScheduler_Start(uint32_t CoSchedulerId);
**Function Description:**
Starts the specified coroutine scheduler and begins coroutine task scheduling.
**Parameters:**
- CoSchedulerId: Scheduler ID
**Return Value:**
- 0: Start successful
- Error code: Start failed
**Usage Process:**
// Complete scheduler startup process
CoScheduler_Init(0, GetTimerUsec, 1000);
testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
CoScheduler_Start(0);
#### CoScheduler_Exit - Exit Coroutine Scheduler
**Function Prototype:**
int CoScheduler_Exit(uint32_t CoSchedulerId);
**Function Description:**
Exits the specified coroutine scheduler, stops all coroutine tasks, and cleans up resources.
**Parameters:**
- CoSchedulerId: Scheduler ID
**Return Value:**
- COROUTINE_E_OK: Exit successful
### 2. Coroutine Task Management Interface
#### CoTask_Create - Create Coroutine Task
**Function Prototype:**
#define CoTask_Create(entry, ...)
**Function Description:**
Creates a new coroutine task, allowing specification of stack space, parameters, and scheduler.
It wraps the CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara); function, providing more initialization parameter configuration.
**Parameter Details:**
- entry **[Required]**: Coroutine entry function pointer, type void (*)(void*)
- pStack **[Optional]**: Stack space address, automatically allocated if NULL
- stackSize **[Optional]**: Stack size, uses default value if not specified
- arg **[Optional]**: Parameter pointer passed to the coroutine
- schedulerId **[Optional]**: Scheduler ID, negative value means automatic allocation
**Return Value:**
- CoTask_t: Successfully created coroutine task handle
- NULL: Creation failed
**Usage Example:**
// Create coroutine with default parameters
CoTask_t testCoroutine = CoTask_Create(test);
// Create coroutine with specified stack space
CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
// Create coroutine with specified stack size
CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
// Create coroutine with parameters
int arg = 4096;
CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
#### CoTask_Delete - Delete Coroutine Task
**Function Prototype:**
int CoTask_Delete(CoTask_t CoTask);
**Function Description:**
Deletes the specified coroutine task and releases related resources.
**Parameters:**
- CoTask: Coroutine task handle to be deleted
**Return Value:**
- 0: Deletion successful
- COROUTINE_E_INVALID_PARAMETER: Invalid parameter
#### CoTask_Self - Get Current Coroutine Task
**Function Prototype:**
CoTask_t CoTask_Self(void);
**Function Description:**
Gets the handle of the currently running coroutine task.
**Return Value:**
- CoTask_t: Current coroutine task handle
- NULL: Not in coroutine context
#### CoTask_SchedulerId - Get Coroutine Scheduler ID
**Function Prototype:**
int CoTask_SchedulerId(void);
**Function Description:**
Gets the scheduler ID to which the current coroutine task belongs.
**Return Value:**
- Scheduler ID: 0 to (COROUTINE_SCHEDULER_NUMBER-1)
### 3. Coroutine Waiting Mechanism Interface
#### CoTask_Wait - Coroutine Wait Function
**Function Prototype:**
#define CoTask_Wait(...)
**Function Description:**
Causes the current coroutine task to wait for a specified event or timeout.
It wraps the uint32_t CoTask_WaitP(CoTaskWaitPara *pPara); function, providing more waiting parameter configuration.
**Parameter Details:**
- pEvent **[Optional]**: Event pointer to wait for
- ms **[Optional]**: Number of milliseconds to wait
- tick **[Optional]**: Number of ticks to wait
**Return Value:**
- uint32_t: Triggered event flag bits
**Usage Example:**
void *test(void *arg) {
while (1) {
// Give up scheduler access right, similar to yield
CoTask_Wait();
// Wait for 1000ms
CoTask_Wait(.ms=1000);
// Wait for event to occur
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
printf("event 0x01 triggered\n");
}
// Combined wait: event + timeout
evs = CoTask_Wait(.pEvent=&g_Event, .ms=5000);
}
}
Wrapping CoTask_Wait can form the following commonly used methods:
#define CoTask_WaitMs(m) CoTask_Wait(.ms=m)
#define CoTask_WaitTick(t) CoTask_Wait(.tick=t)
#define CoTask_WaitEvent(e) CoTask_Wait(.pEvent=e)
#define CoTask_WaitEventMs(e, m) CoTask_Wait(.pEvent=e, .ms=m)
#define CoTask_WaitEventTick(e, t) CoTask_Wait(.pEvent=e, .tick=t)
### 4. Timer Management Interface
#### CoTimer_Create - Create Coroutine Timer
**Function Prototype:**
#define CoTimer_Create(entry, ...)
**Function Description:**
Creates a periodic coroutine timer.
It wraps the CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara); function, providing more initialization parameter configuration.
**Parameter Details:**
- entry **[Required]**: Timer entry function
- ms **[Optional]**: Millisecond interval
- tick **[Optional]**: Tick interval
**Return Value:**
- CoTimer_t: Successfully created timer handle
- NULL: Creation failed
**Usage Example:**
// Create timer with 100ms interval
CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100);
#### CoTimer_Delete - Delete Coroutine Timer
**Function Prototype:**
void CoTimer_Delete(CoTimer_t Timer);
**Function Description:**
Deletes the specified coroutine timer.
**Parameters:**
- Timer: Timer handle to be deleted
#### CoTimer_Self - Get Current Timer
**Function Prototype:**
CoTimer_t CoTimer_Self(void);
**Function Description:**
Gets the handle of the currently running coroutine timer.
**Return Value:**
- CoTimer_t: Current timer handle
- NULL: Not in timer context
### 5. Event Synchronization Interface
#### CoEvent_Init - Initialize Event
**Function Prototype:**
void CoEvent_Init(CoEvent *pEvent);
**Function Description:**
Initializes a coroutine event, preparing it for inter-coroutine synchronization.
**Parameters:**
- pEvent: Event pointer
**Usage Example:**
CoEvent_Init(&g_Event);
This method is called at runtime, while static initialization can directly assign COEVENT_STATIC_VALUE.
CoEvent g_Event = COEVENT_STATIC_VALUE;
#### CoEvent_Notify - Notify Event
**Function Prototype:**
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
**Function Description:**
Notifies the specified event, waking up coroutine tasks waiting for that event.
**Parameter Details:**
- pEvent: Event pointer
- evs: Event flag bits, supports simultaneous notification of multiple events
**Usage Example:**
// Notify single event
CoEvent_Notify(&g_Event, 0x01);
// Notify multiple events simultaneously
CoEvent_Notify(&g_Event, 0x01 | 0x02 | 0x04);
### 6. Monitoring Statistics Interface
#### CoTask_StackMaxUsed - Get Coroutine Stack Maximum Usage
**Function Prototype:**
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
**Function Description:**
Gets the maximum stack usage of the specified coroutine task.
**Parameters:**
- CoTask: Coroutine task handle
**Return Value:**
- size_t: Maximum stack usage (bytes)
#### CoTask_StackCurUsed - Get Coroutine Stack Current Usage
**Function Prototype:**
size_t CoTask_StackCurUsed(CoTask_t CoTask);
**Function Description:**
Gets the current stack usage of the specified coroutine task.
**Parameters:**
- CoTask: Coroutine task handle
**Return Value:**
- size_t: Current stack usage (bytes)
#### CoScheduler_CurLoad - Get Current Load
**Function Prototype:**
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
**Function Description:**
Gets the current load of the specified scheduler.
**Return Value:**
- uint16_t: Current load percentage (0-10000, representing 0.00%-100.00%)
#### CoScheduler_MaxLoad - Get Maximum Historical Load
**Function Prototype:**
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
**Function Description:**
Gets the historical maximum load of the specified scheduler.
**Return Value:**
- uint16_t: Maximum load percentage
### 7. Statistical Counting Interface
#### CoScheduler_TaskCount - Get Coroutine Task Count
**Function Prototype:**
int CoScheduler_TaskCount(uint32_t CoSchedulerId);
**Function Description:**
Gets the number of coroutine tasks in the specified scheduler.
**Parameters:**
- CoSchedulerId: Scheduler ID
**Return Value:**
- int: Number of coroutine tasks
#### CoScheduler_TimerCount - Get Timer Count
**Function Prototype:**
int CoScheduler_TimerCount(uint32_t CoSchedulerId);
**Function Description:**
Gets the number of coroutine timers in the specified scheduler.
**Parameters:**
- CoSchedulerId: Scheduler ID
**Return Value:**
- int: Number of timers
## Configuration Options
### Compile-time Configuration (coroutine_cfg.h)
**Basic Configuration:**
// Scheduler configuration
#define COROUTINE_SCHEDULER_NUMBER 1 // Number of schedulers
// Static resource limits
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // Maximum number of static tasks
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // Maximum number of static timers
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // Maximum number of static stacks
// Stack configuration
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // Default stack size
**Feature Switches:**
// Stack usage calculation
#define COROUTINE_ENABLE_STACK_CALCULATE 1 // Enable stack usage calculation
// Load calculation
#define COROUTINE_ENABLE_LOADING_CALCULATE 1 // Enable load calculation
// Debug support
#define COROUTINE_ENABLE_DEBUG 1 // Enable debug function
## Usage Guide
### 1. Basic Usage Process
#### Initialization Phase
#include "coroutine.h"
// Define clock function
uint64_t GetTimerUsec(void) {
// Return microsecond-level timestamp
return ...;
}
int main(void) {
// Initialize scheduler 0, use microsecond clock, 1000us interval
CoScheduler_Init(0, GetTimerUsec, 1000);
}
#### Coroutine Definition
// Coroutine task function
void *test_task(void *arg) {
int *p_count = (int*)arg;
while (1) {
// Coroutine task logic
(*p_count)++;
// Voluntarily yield CPU
CoTask_Wait();
}
return NULL;
}
#### Creation and Startup
// Create coroutine task
int count = 0;
CoTask_t testCoroutine = CoTask_Create(test_task, .arg=&count);
}
### 2. Advanced Usage Patterns
#### Event-Driven Pattern
void *event_driven_task(void *arg) {
while (1) {
// Wait for event
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
// Handle event 0x01
handle_event_01();
}
if (evs & 0x02) {
// Handle event 0x02
handle_event_02();
}
}
}
#### Timer Pattern
void *timer_task(void *arg) {
while (1) {
// Execute every 100ms
CoTask_Wait(.ms=100);
// Timer logic
periodic_work();
}
}
## Advanced Features
### 1. Stack Usage Calculation
After enabling COROUTINE_ENABLE_STACK_CALCULATE, you can monitor coroutine stack usage:
// Get coroutine stack maximum usage
size_t maxUsed = CoTask_StackMaxUsed(CoTask);
// Get coroutine stack current usage
size_t curUsed = CoTask_StackCurUsed(CoTask);
}
### 2. Load Calculation
After enabling COROUTINE_ENABLE_LOADING_CALCULATE, you can calculate the scheduler's load:
// Get current load
uint16_t curLoad = CoScheduler_CurLoad(CoSchedulerId);
// Get maximum historical load
uint16_t maxLoad = CoScheduler_MaxLoad(CoSchedulerId);
}
### 3. Performance Monitoring
// Monitor coroutine scheduling performance
void monitor_scheduler_performance(void) {
uint16_t curLoad = CoScheduler_CurLoad(0);
uint16_t maxLoad = CoScheduler_MaxLoad(0);
if (curLoad > 8000) { // 80% load warning
printf("Warning: Scheduler load is high: %d.%02d%%\n",
curLoad/100, curLoad%100);
}
}
## Error Handling
### Error Code Definitions
#define COROUTINE_E_OK (0) // Success
#define COROUTINE_E_INVALID_PARAMETER (-1) // Invalid parameter
#define COROUTINE_E_INVALID_TICK (-2) // Invalid clock function
#define COROUTINE_E_INVALID_TICK_INTERVAL (-3) // Invalid clock interval
#define COROUTINE_E_INVALID_LOCK (-4) // Invalid lock function
#define COROUTINE_E_INVALID_MHOOK (-5) // Invalid memory hook function
## Performance Optimization
### 1. Context Switching Optimization
- Uses setjmp/longjmp instead of complete context saving
- Minimizes register save and restore operations
### 2. Memory Management Optimization
- Static pre-allocation reduces dynamic memory allocation
- Stack space reuse mechanism
## Platform Support
This library implements cross-platform stack pointer operations through inline assembly:
- **x86/x64**: Uses esp/rsp registers
- **ARM**: Uses sp register
- **MIPS**: Uses sp register
- **RISC-V**: Uses sp register
- **PowerPC**: Uses r1 register
### Platform-Specific Implementation
// x86 architecture implementation
#if defined(__i386__)
#define COROUTINE_GET_STACK_POINTER(p) \
__asm__ __volatile__("movl %%esp, %0" : "=r"(p))
}
## Application Scenarios
### 1. Embedded Systems
- Real-time task scheduling
- Low-power device management
- Sensor data processing
### 2. Network Programming
- Asynchronous I/O processing
- Connection pool management
- Protocol stack implementation
### 3. Game Development
- Game object updates
- AI behavior scheduling
- Animation system management
### 4. IoT Applications
- Device communication protocols
- Data processing pipelines
- Remote control interfaces
## Summary
The **coroutine** coroutine library is a powerful, high-performance C language coroutine solution. It provides:
1. **Complete coroutine lifecycle management**
2. **Efficient multi-task scheduling mechanism**
3. **Flexible timer and event management**
4. **Comprehensive error handling and monitoring functions**

734
doc/coroutine.md Normal file
View File

@ -0,0 +1,734 @@
## 目录
- [概述](#概述)
- [核心特性](#核心特性)
- [架构设计](#架构设计)
- [API接口详解](#api接口详解)
- [配置选项](#配置选项)
- [使用指南](#使用指南)
- [高级功能](#高级功能)
- [错误处理](#错误处理)
- [性能优化](#性能优化)
- [平台支持](#平台支持)
- [应用场景](#应用场景)
## 概述
协程模块是一种轻量级的用户态线程管理机制在程序开发中扮演着重要角色主要用于资源受限的嵌入式设备、网络编程、异步IO、实时数据处理等需要高效并发的场景。
相比传统线程具有更低的资源消耗(无需内核态切换)和更快的上下文切换速度,通过协程模块,开发者能够实现高效的任务切换和并发执行,从而提升程序的性能和响应速度。
**coroutine** 是一个高性能、轻量级的C语言协程库采用非对称有栈协程设计。该库提供了完整的协程生命周期管理支持多任务调度、定时器管理和事件同步机制提供简洁的创建、调度与同步API接口可无缝集成到嵌入式系统、服务器程序等多种环境中。并且移植起来也比较简单只需要在目标平台上编译并链接该模块即可已支持主流的芯片内核架构
### 设计理念
- **简单易用**提供直观的API接口
- **移植方便**:移植所需资源依赖少
- **高效性能**:低开销的上下文切换
- **稳定可靠**:错误处理和资源管理
## 核心特性
### 1. 架构特性
- **非对称有栈协程**:每个协程拥有独立的栈空间
- **多调度器支持**:可配置多个独立的协程调度器
- **混合内存管理**:支持静态预分配和动态内存管理
- **跨平台兼容**支持x86、x64、ARM、MIPS等多种CPU架构尚未完全测试完
### 2. 协程状态管理
#### 协程调度器状态定义
```c
#define COSCHEDULER_STATE_INTI (0) // 初始化状态
#define COSCHEDULER_STATE_EXIT (1) // 退出状态
#define COSCHEDULER_STATE_START (2) // 启动状态
#define COSCHEDULER_STATE_RUNNING (3) // 运行状态
#define COSCHEDULER_STATE_SCHEDULE (4) // 调度状态
```
#### 协程任务状态定义
```c
#define COTASK_STATE_INTI (0) // 初始化状态
#define COTASK_STATE_READY (1) // 就绪状态
#define COTASK_STATE_RUNNING (2) // 运行状态
#define COTASK_STATE_SUSPEND (3) // 挂起状态
#define COTASK_STATE_DELETED (4) // 删除状态
```
### 3. 内存管理配置
#### 静态资源限制配置
```c
#define COROUTINE_SCHEDULER_NUMBER 1 // 调度器数量
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // 静态任务最大数量
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // 静态定时器最大数量
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // 静态栈最大数量
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // 默认栈大小
```
## 架构设计
### 1. 整体架构
```
+----------------------------------------------------+
| Architecture |
+------------------------------------+---------------+
| | +---------+ |
| +--------+ +--------+ | | CoTimer | |
| | | | | | +---------+ |
| | | CoEvent | | CoEvent +---------+ |
| | CoTask |<=======>| CoTask |<=======>| CoTimer | |
| | | | | | +---------+ |
| | | | | | +---------+ |
| +--------+ +--------+ | | CoTimer | |
+------------------------------------+ +---------+ |
| CoScheduler |
+----------------------------------------------------+
```
### 2. 协程上下文切换机制
- 使用setjmp/longjmp实现快速上下文保存和恢复
- 每个协程维护独立的栈空间和环境
- 支持协程挂起、恢复和删除操作
## API接口详解
### 1. 调度器管理接口
#### CoScheduler_Init - 初始化协程调度器
**函数原型:**
```c
#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...)
```
**函数说明:**
初始化指定的协程调度器,设置时钟函数和时钟间隔。此函数必须在调用任何其他协程函数之前执行。
套用 `int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara);` 函数,提供更多的初始化参数配置。
**参数详解:**
- `CoSchedulerId` **[必需]**调度器ID范围0到(COROUTINE_SCHEDULER_NUMBER-1)
- `tick` **[必需]**:时钟函数指针,用于获取当前时间戳
- `tickInterval` **[必需]**时钟间隔表示每个tick对应的纳秒数
- `lock` **[可选]**:锁函数指针,用于多线程环境下的资源保护
- `unlock` **[可选]**:解锁函数指针,用于多线程环境下的资源释放
- `malloc` **[可选]**:内存分配函数指针
- `free` **[可选]**:内存释放函数指针
**返回值:**
- `COROUTINE_E_OK` (0):初始化成功
- `COROUTINE_E_INVALID_PARAMETER` (-1):无效参数
- `COROUTINE_E_INVALID_TICK` (-2):无效时钟函数
- `COROUTINE_E_INVALID_TICK_INTERVAL` (-3):无效时钟间隔
- `COROUTINE_E_INVALID_LOCK` (-4):无效锁函数
- `COROUTINE_E_INVALID_MHOOK` (-5):无效内存钩子函数
**使用示例:**
```c
// 基本使用使用微秒时钟1000us间隔
CoScheduler_Init(0, GetTimerUsec, 1000);
// 多线程支持:带锁函数
CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
// 完整配置:带内存管理函数
CoScheduler_Init(0, GetTimerUsec, 1000,
.lock=thread_lock,
.unlock=thread_unlock,
.malloc=malloc,
.free=free
);
```
#### CoScheduler_Start - 启动协程调度器
**函数原型:**
```c
int CoScheduler_Start(uint32_t CoSchedulerId);
```
**函数说明:**
启动指定的协程调度器,开始执行协程任务调度。
**参数:**
- `CoSchedulerId`调度器ID
**返回值:**
- 0启动成功
- 错误码:启动失败
**使用流程:**
```c
// 完整的调度器启动流程
CoScheduler_Init(0, GetTimerUsec, 1000);
testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
CoScheduler_Start(0);
```
#### CoScheduler_Exit - 退出协程调度器
**函数原型:**
```c
int CoScheduler_Exit(uint32_t CoSchedulerId);
```
**函数说明:**
退出指定的协程调度器,停止所有协程任务并清理资源。
**参数:**
- `CoSchedulerId`调度器ID
**返回值:**
- `COROUTINE_E_OK`:退出成功
### 2. 协程任务管理接口
#### CoTask_Create - 创建协程任务
**函数原型:**
```c
#define CoTask_Create(entry, ...)
```
**函数说明:**
创建一个新的协程任务,可以指定栈空间、参数和调度器。
套用 `CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara);` 函数,提供更多的初始化参数配置。
**参数详解:**
- `entry` **[必需]**:协程入口函数指针,类型为`void (*)(void*)`
- `pStack` **[可选]**栈空间地址如果为NULL则自动分配
- `stackSize` **[可选]**:栈大小,未指定时使用默认值
- `arg` **[可选]**:传递给协程的参数指针
- `schedulerId` **[可选]**调度器ID负值表示自动分配
**返回值:**
- `CoTask_t`:成功创建的协程任务句柄
- `NULL`:创建失败
**使用示例:**
```c
// 使用默认参数创建协程
CoTask_t testCoroutine = CoTask_Create(test);
// 指定栈空间创建协程
CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
// 指定栈大小创建协程
CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
// 带参数创建协程
int arg = 4096;
CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
```
#### CoTask_Delete - 删除协程任务
**函数原型:**
```c
int CoTask_Delete(CoTask_t CoTask);
```
**函数说明:**
删除指定的协程任务,释放相关资源。
**参数:**
- `CoTask`:要删除的协程任务句柄
**返回值:**
- 0删除成功
- `COROUTINE_E_INVALID_PARAMETER`:无效参数
#### CoTask_Self - 获取当前协程任务
**函数原型:**
```c
CoTask_t CoTask_Self(void);
```
**函数说明:**
获取当前正在运行的协程任务句柄。
**返回值:**
- `CoTask_t`:当前协程任务句柄
- `NULL`:不在协程上下文中
#### CoTask_SchedulerId - 获取协程调度器ID
**函数原型:**
```c
int CoTask_SchedulerId(void);
```
**函数说明:**
获取当前协程任务所属的调度器ID。
**返回值:**
- 调度器ID0到(COROUTINE_SCHEDULER_NUMBER-1)
### 3. 协程等待机制接口
#### CoTask_Wait - 协程等待函数
**函数原型:**
```c
#define CoTask_Wait(...)
```
**函数说明:**
使当前协程任务等待指定的事件或超时。
套用 `uint32_t CoTask_WaitP(CoTaskWaitPara *pPara);` 函数,提供更多的等待参数配置。
**参数详解:**
- `pEvent` **[可选]**:等待的事件指针
- `ms` **[可选]**:等待的毫秒数
- `tick` **[可选]**等待的tick数
**返回值:**
- `uint32_t`:触发的事件标志位
**使用示例:**
```c
void *test(void *arg) {
while (1) {
// 放弃调度器访问权类似yield
CoTask_Wait();
// 等待1000ms
CoTask_Wait(.ms=1000);
// 等待事件发生
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
printf("event 0x01 triggered\n");
}
// 组合等待:事件+超时
evs = CoTask_Wait(.pEvent=&g_Event, .ms=5000);
}
}
```
套用 `CoTask_Wait` 可以形成以下常用方法:
```c
#define CoTask_WaitMs(m) CoTask_Wait(.ms=m)
#define CoTask_WaitTick(t) CoTask_Wait(.tick=t)
#define CoTask_WaitEvent(e) CoTask_Wait(.pEvent=e)
#define CoTask_WaitEventMs(e, m) CoTask_Wait(.pEvent=e, .ms=m)
#define CoTask_WaitEventTick(e, t) CoTask_Wait(.pEvent=e, .tick=t)
```
### 4. 定时器管理接口
#### CoTimer_Create - 创建协程定时器
**函数原型:**
```c
#define CoTimer_Create(entry, ...)
```
**函数说明:**
创建一个周期性的协程定时器。
套用 `CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara);` 函数,提供更多的初始化参数配置。
**参数详解:**
- `entry` **[必需]**:定时器入口函数
- `ms` **[可选]**:毫秒间隔
- `tick` **[可选]**tick间隔
**返回值:**
- `CoTimer_t`:成功创建的定时器句柄
- `NULL`:创建失败
**使用示例:**
```c
// 创建100ms间隔的定时器
CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100);
```
#### CoTimer_Delete - 删除协程定时器
**函数原型:**
```c
void CoTimer_Delete(CoTimer_t Timer);
```
**函数说明:**
删除指定的协程定时器。
**参数:**
- `Timer`:要删除的定时器句柄
#### CoTimer_Self - 获取当前定时器
**函数原型:**
```c
CoTimer_t CoTimer_Self(void);
```
**函数说明:**
获取当前正在运行的协程定时器句柄。
**返回值:**
- `CoTimer_t`:当前定时器句柄
- `NULL`:不在定时器上下文中
### 5. 事件同步接口
#### CoEvent_Init - 初始化事件
**函数原型:**
```c
void CoEvent_Init(CoEvent *pEvent);
```
**函数说明:**
初始化协程事件,准备用于协程间同步。
**参数:**
- `pEvent`:事件指针
**使用示例:**
```c
CoEvent_Init(&g_Event);
```
此方法为运行时调用,静态初始化可以直接赋值为 `COEVENT_STATIC_VALUE`
```c
CoEvent g_Event = COEVENT_STATIC_VALUE;
```
#### CoEvent_Notify - 通知事件
**函数原型:**
```c
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
```
**函数说明:**
通知指定的事件,唤醒等待该事件的协程任务。
**参数详解:**
- `pEvent`:事件指针
- `evs`:事件标志位,支持多事件同时通知
**使用示例:**
```c
// 通知单个事件
CoEvent_Notify(&g_Event, 0x01);
// 同时通知多个事件
CoEvent_Notify(&g_Event, 0x01 | 0x02 | 0x04);
```
### 6. 监控统计接口
#### CoTask_StackMaxUsed - 获取协程栈最大使用量
**函数原型:**
```c
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
```
**函数说明:**
获取指定协程任务栈的最大使用量。
**参数:**
- `CoTask`:协程任务句柄
**返回值:**
- `size_t`:栈最大使用量(字节)
#### CoTask_StackCurUsed - 获取协程栈当前使用量
**函数原型:**
```c
size_t CoTask_StackCurUsed(CoTask_t CoTask);
```
**函数说明:**
获取指定协程任务栈的当前使用量。
**参数:**
- `CoTask`:协程任务句柄
**返回值:**
- `size_t`:栈当前使用量(字节)
#### CoScheduler_CurLoad - 获取当前负载
**函数原型:**
```c
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
```
**函数说明:**
获取指定调度器的当前负载。
**返回值:**
- `uint16_t`当前负载百分比0-10000表示0.00%-100.00%
#### CoScheduler_MaxLoad - 获取最大历史负载
**函数原型:**
```c
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
```
**函数说明:**
获取指定调度器的历史最大负载。
**返回值:**
- `uint16_t`:最大负载百分比
### 7. 统计计数接口
#### CoScheduler_TaskCount - 获取协程任务数量
**函数原型:**
```c
int CoScheduler_TaskCount(uint32_t CoSchedulerId);
```
**函数说明:**
获取指定调度器中的协程任务数量。
**参数:**
- `CoSchedulerId`调度器ID
**返回值:**
- `int`:协程任务数量
#### CoScheduler_TimerCount - 获取定时器数量
**函数原型:**
```c
int CoScheduler_TimerCount(uint32_t CoSchedulerId);
```
**函数说明:**
获取指定调度器中的协程定时器数量。
**参数:**
- `CoSchedulerId`调度器ID
**返回值:**
- `int`:定时器数量
## 配置选项
### 编译时配置 (coroutine_cfg.h)
**基本配置:**
```c
// 调度器配置
#define COROUTINE_SCHEDULER_NUMBER 1 // 调度器数量
// 静态资源限制
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // 静态任务最大数量
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // 静态定时器最大数量
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // 静态栈最大数量
// 栈配置
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // 默认栈大小
```
**功能开关:**
```c
// 栈使用量计算
#define COROUTINE_ENABLE_STACK_CALCULATE 1 // 启用栈使用量计算
// 负载计算
#define COROUTINE_ENABLE_LOADING_CALCULATE 1 // 启用负载计算
// 调试支持
#define COROUTINE_ENABLE_DEBUG 1 // 启用调试功能
```
## 使用指南
### 1. 基本使用流程
#### 初始化阶段
```c
#include "coroutine.h"
// 定义时钟函数
uint64_t GetTimerUsec(void) {
// 返回微秒级时间戳
return ...;
}
int main(void) {
// 初始化调度器0使用微秒时钟1000us间隔
CoScheduler_Init(0, GetTimerUsec, 1000);
```
#### 协程定义
```c
// 协程任务函数
void *test_task(void *arg) {
int *p_count = (int*)arg;
while (1) {
// 协程任务逻辑
(*p_count)++;
// 主动让出CPU
CoTask_Wait();
}
return NULL;
}
```
#### 创建和启动
```c
// 创建协程任务
int count = 0;
CoTask_t testCoroutine = CoTask_Create(test_task, .arg=&count);
```
### 2. 高级使用模式
#### 事件驱动模式
```c
void *event_driven_task(void *arg) {
while (1) {
// 等待事件
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
// 处理事件0x01
handle_event_01();
}
if (evs & 0x02) {
// 处理事件0x02
handle_event_02();
}
}
}
```
#### 定时器模式
```c
void *timer_task(void *arg) {
while (1) {
// 每100ms执行一次
CoTask_Wait(.ms=100);
// 定时器逻辑
periodic_work();
}
}
```
## 高级功能
### 1. 栈使用量计算
启用 `COROUTINE_ENABLE_STACK_CALCULATE` 后,可以监控协程栈的使用情况:
```c
// 获取协程栈最大使用量
size_t maxUsed = CoTask_StackMaxUsed(CoTask);
// 获取协程栈当前使用量
size_t curUsed = CoTask_StackCurUsed(CoTask);
```
### 2. 负载计算
启用 `COROUTINE_ENABLE_LOADING_CALCULATE` 后,可以计算调度器的负载:
```c
// 获取当前负载
uint16_t curLoad = CoScheduler_CurLoad(CoSchedulerId);
// 获取最大历史负载
uint16_t maxLoad = CoScheduler_MaxLoad(CoSchedulerId);
```
### 3. 性能监控
```c
// 监控协程调度性能
void monitor_scheduler_performance(void) {
uint16_t curLoad = CoScheduler_CurLoad(0);
uint16_t maxLoad = CoScheduler_MaxLoad(0);
if (curLoad > 8000) { // 80%负载警告
printf("Warning: Scheduler load is high: %d.%02d%%\n",
curLoad/100, curLoad%100);
}
```
## 错误处理
### 错误码定义
```c
#define COROUTINE_E_OK (0) // 成功
#define COROUTINE_E_INVALID_PARAMETER (-1) // 无效参数
#define COROUTINE_E_INVALID_TICK (-2) // 无效时钟函数
#define COROUTINE_E_INVALID_TICK_INTERVAL (-3) // 无效时钟间隔
#define COROUTINE_E_INVALID_LOCK (-4) // 无效锁函数
#define COROUTINE_E_INVALID_MHOOK (-5) // 无效内存钩子函数
```
## 性能优化
### 1. 上下文切换优化
- 使用setjmp/longjmp替代完整的上下文保存
- 最小化寄存器保存和恢复操作
### 2. 内存管理优化
- 静态预分配减少动态内存分配
- 栈空间复用机制
## 平台支持
该库通过内联汇编实现了跨平台的栈指针操作:
- **x86/x64**使用esp/rsp寄存器
- **ARM**使用sp寄存器
- **MIPS**使用sp寄存器
- **RISC-V**使用sp寄存器
- **PowerPC**使用r1寄存器
### 平台特定实现
```c
// x86架构实现
#if defined(__i386__)
#define COROUTINE_GET_STACK_POINTER(p) \
__asm__ __volatile__("movl %%esp, %0" : "=r"(p))
```
## 应用场景
### 1. 嵌入式系统
- 实时任务调度
- 低功耗设备管理
- 传感器数据处理
### 2. 网络编程
- 异步I/O处理
- 连接池管理
- 协议栈实现
### 3. 游戏开发
- 游戏对象更新
- AI行为调度
- 动画系统管理
### 4. 物联网应用
- 设备通信协议
- 数据处理流水线
- 远程控制接口
## 总结
**coroutine** 协程库是一个功能强大、性能优异的C语言协程解决方案。它提供了
1. **完整的协程生命周期管理**
2. **高效的多任务调度机制**
3. **灵活的定时器和事件管理**
4. **完善的错误处理和监控功能**

436
doc/yaml.en.md Normal file
View File

@ -0,0 +1,436 @@
## Introduction
### What is a YAML file?
YAML (YAML Ain't Markup Language) is a **human-readable data serialization format** that focuses on concisely expressing structured data. It is widely used in configuration files, data exchange, and the description of complex data structures (such as the configurations of tools like Kubernetes and Ansible), rather than for storing traditional spreadsheet-like tabular data.
### Characteristics of YAML files
- **High readability**:
It uses indentation and symbols (such as `-` and `:`) to represent hierarchical relationships, with a format similar to natural language, making it easier to read than JSON/XML.
```yaml
user:
name: Alice
age: 30
hobbies:
- reading
- hiking
```
- **Supports complex data structures**:
It can represent scalars (strings, numbers), lists, dictionaries, etc., and supports nesting and multi-line text (using `|` or `>`).
- **Cross-platform compatibility**:
It is a plain text format that can be parsed by all operating systems and programming languages.
- **Seamless integration with programming languages**:
Most languages (Python, Java, Go, etc.) provide native or third-party libraries (such as PyYAML) to support YAML parsing.
### Uses of YAML
1. **Configuration files** (core use)
- Software configuration (such as Docker Compose, GitLab CI/CD)
- Cloud-native tool configuration (Kubernetes manifests)
```yaml
# Kubernetes Deployment example
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
```
2. **Data serialization**
- Replacing JSON/XML to transfer complex data
- Structured data description for API requests/responses
3. **Data exchange**
- Transferring structured information between different systems (such as microservice configuration synchronization)
### How to create and edit YAML files?
1. **Text editors**
- VS Code (it is recommended to install the YAML plugin for syntax highlighting and validation)
- Sublime Text / Vim, etc.
2. **Specialized tools**
- **Online YAML Validator**: Validates the syntax validity.
- **yq**: A command-line tool (similar to jq for processing YAML).
### Precautions
1. **Indentation sensitivity**
- Spaces must be used (usually 2 or 4 spaces, and tabs are prohibited).
- Incorrect indentation will cause parsing to fail.
2. **Key-value pair format**
- A space is required after the colon: `key: value` (not `key:value`).
3. **Special character handling**
- When a string contains symbols such as `:` and `#`, it is recommended to use quotes:
```yaml
message: "Hello:World"
comment: "This is a # symbol"
```
4. **Multi-line text**
- Preserve line breaks: `|`
```yaml
description: |
This is a
multi-line
text.
```
- Fold line breaks: `>`
```yaml
summary: >
This will fold
into a single line.
```
5. **Data type marking**
- Force specifying the type:
```yaml
boolean: !!bool "true"
timestamp: !!timestamp 2023-07-20T15:30:00Z
```
### Common error examples
```yaml
# Error: Mixing spaces and tabs in indentation
user:
name: Bob
age: 25 # Inconsistent indentation
# Error: Missing space in key-value pair
key1:value1 # Should be changed to key1: value1
# Error: Unescaped special characters
message: Line 1
Line 2 # Missing multi-line text identifier
```
### C language version of the YAML library
The YAML library provided by varch is simple and easy to use, capable of performing most basic operations on YAML files, including loading and saving YAML, as well as adding, deleting, modifying, and querying operations.
## Interfaces
### Creating and deleting YAML objects
```c
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
```
Here, **yaml_t** is the YAML structure. The creation method generates an empty YAML object, and the deletion method deletes the specified YAML object.
### Loading YAML objects
```c
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
```
YAML objects can be loaded from string text or from files. If the loading is successful, a YAML object will be returned; otherwise, NULL will be returned.
The flag is some flag for the operation function, defined as follows:
```
#define YAML_F_NONE (0)
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
```
When the YAML object fails to load, you can call the `int yaml_error_info(int* line, int* column);` function to locate the error.
The error types include:
```
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* date error */
```
### Dumping YAML objects
```c
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
```
First, the **yaml_dumps** method dumps the YAML object into a string according to the format. `*len` is the length of the converted string. When `NULL` is passed, the length is not obtained. The return value is the converted string, which is allocated by the function and **needs to be `free`d after use**.
The **yaml_file_dump** method dumps the YAML object into a file based on the **yaml_dumps** method. `filename` is the file name, and the return value is the length of the dump. A negative value indicates a dump failure.
### Adding child objects to a YAML object
```c
#define yaml_seq_add_null(yaml)
#define yaml_seq_add_int(yaml, num)
#define yaml_seq_add_float(yaml, num)
#define yaml_seq_add_string(yaml, string)
#define yaml_seq_add_sequence(yaml, sequence)
#define yaml_seq_add_mapping(yaml, mapping)
#define yaml_map_add_null(yaml, key)
#define yaml_map_add_int(yaml, key, num)
#define yaml_map_add_float(yaml, key, num)
#define yaml_map_add_string(yaml, key, string)
#define yaml_map_add_sequence(yaml, key, sequence)
#define yaml_map_add_mapping(yaml, key, mapping)
```
The above methods add scalars to sequences and mappings respectively. In fact, they are implemented by applying `insert`-type methods:
```c
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
```
These `insert`-type methods are used to insert child objects of different types at specified positions in a `yaml` object. `key` is the key of the inserted object, and `index` is the insertion position. The return value is the `yaml` object after the insertion operation. If the insertion fails, `NULL` is returned. Specifically:
- `yaml_insert_null`: Inserts a null-type child object.
- `yaml_insert_bool`: Inserts a boolean-type child object, where `b` is the boolean value (`YAML_FALSE` or `YAML_TRUE`).
- `yaml_insert_int`: Inserts an integer-type child object, where `num` is the integer value.
- `yaml_insert_float`: Inserts a floating-point-type child object, where `num` is the floating-point value.
- `yaml_insert_string`: Inserts a string-type child object, where `string` is the string value.
- `yaml_insert_sequence`: Inserts a sequence-type child object, where `sequence` is the sequence object.
- `yaml_insert_mapping`: Inserts a mapping-type child object, where `mapping` is the mapping object.
- `yaml_insert_document`: Inserts a document-type child object, where `document` is the document object.
- `yaml_insert_reference`: Inserts a reference-type child object, where `anchor` is the reference anchor, and `doc` is the referenced document object.
### Removing child objects from a YAML object
```c
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
```
Removes the `index`-th child object with the specified key `key` from the `yaml` object. If the removal is successful, `YAML_E_OK` is returned; otherwise, the corresponding error code is returned.
### YAML object attribute operations
```c
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
```
- `yaml_type`: Gets the type of the `yaml` object. The return value is one of the `YAML_TYPE_*` series of macro definitions, used to determine whether the object is of type `NULL`, boolean, integer, floating-point, string, sequence, mapping, document, reference, or complex key.
- `yaml_size`: Gets the size of the `yaml` object. For sequence or mapping-type objects, it returns the number of elements; for other types of objects, the meaning of the return value may vary depending on the specific implementation.
### YAML object comparison and copying
```c
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
```
- `yaml_compare`: Compares two `yaml` objects. `flag` is the comparison flag, used to specify the comparison method. The return value is the comparison result, and its specific meaning is determined by the implementation. Usually, 0 indicates equality, and non-0 indicates inequality.
- `yaml_copy`: Copies a `yaml` object. `flag` is the copy flag, used to specify the copy method. The return value is the copied `yaml` object. If the copy fails, `NULL` is returned.
### YAML object key operations
```c
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
```
- `yaml_set_key`: Sets a simple key for the `yaml` object, where `key` is the string value of the key. The return value is the `yaml` object after setting the key.
- `yaml_set_key_complex`: Sets a complex key for the `yaml` object, where `key` is the `yaml` object of the complex key. The return value is the `yaml` object after setting the key.
- `yaml_key`: Gets the simple key of the `yaml` object. The return value is the string value of the key.
- `yaml_key_complex`: Gets the complex key of the `yaml` object. The return value is the `yaml` object of the complex key.
### YAML object value setting
```c
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
```
These methods are used to set the value of a `yaml` object, and the return value is the `yaml` object after setting the value. Specifically:
- `yaml_set_null`: Sets the value of the `yaml` object to the null type.
- `yaml_set_bool`: Sets the value of the `yaml` object to the boolean type, where `b` is the boolean value (`YAML_FALSE` or `YAML_TRUE`).
- `yaml_set_int`: Sets the value of the `yaml` object to the integer type, where `num` is the integer value.
- `yaml_set_float`: Sets the value of the `yaml` object to the floating-point type, where `num` is the floating-point value.
- `yaml_set_string`: Sets the value of the `yaml` object to the string type, where `string` is the string value.
- `yaml_set_date`: Sets the value of the `yaml` object to the date type, where `year` is the year, `month` is the month, and `day` is the date.
- `yaml_set_time`: Sets the value of the `yaml` object to the time type, where `hour` is the hour, `minute` is the minute, `second` is the second, and `msec` is the millisecond.
- `yaml_set_utc`: Sets the value of the `yaml` object to the UTC time offset type, where `hour` is the hour offset and `minute` is the minute offset.
- `yaml_set_sequence`: Sets the value of the `yaml` object to the sequence type, where `sequence` is the sequence object.
- `yaml_set_mapping`: Sets the value of the `yaml` object to the mapping type, where `mapping` is the mapping object.
- `yaml_set_document`: Sets the value of the `yaml` object to the document type, where `document` is the document object.
### YAML object value retrieval
```c
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
```
These methods are used to retrieve the value of a `yaml` object, and the return value is the retrieved value. Specifically:
- `yaml_value_bool`: Retrieves the boolean value of the `yaml` object.
- `yaml_value_int`: Retrieves the integer value of the `yaml` object.
- `yaml_value_float`: Retrieves the floating-point value of the `yaml` object.
- `yaml_value_string`: Retrieves the string value of the `yaml` object.
- `yaml_value_sequence`: Retrieves the sequence value of the `yaml` object and returns the sequence object.
- `yaml_value_mapping`: Retrieves the mapping value of the `yaml` object and returns the mapping object.
- `yaml_value_document`: Retrieves the document value of the `yaml` object and returns the document object.
### YAML object child element operations
```c
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
```
- `yaml_attach`: Attaches a `yaml` child object `attach` at the specified position `index` in the `yaml` object. The return value is the `yaml` object after the operation. If the operation fails, `NULL` is returned.
- `yaml_dettach`: Detaches a child object from the specified position `index` in the `yaml` object. The return value is the detached child object. If the operation fails, `NULL` is returned.
### YAML object indexing and child object retrieval
```c
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
```
- `yaml_get_index`: Gets the index of the `index`-th child object with the key `key` in the `yaml` object. The return value is the index of the child object. If not found, `YAML_INV_INDEX` is returned.
- `yaml_get_index_complex`: Gets the index of the child object with the complex key `key` in the `yaml` object. The return value is the index of the child object. If not found, `YAML_INV_INDEX` is returned.
- `yaml_get_child`: Gets the `index`-th child object with the key `key` in the `yaml` object. The return value is the `yaml` object of the child object. If not found, `NULL` is returned.
- `yaml_get_child_complex`: Gets the child object with the complex key `key` in the `yaml` object. The return value is the `yaml` object of the child object. If not found, `NULL` is returned.
### YAML object anchor and alias operations
```c
const char* yaml_get_alias(yaml_t yaml);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
unsigned int yaml_anchor_size(yaml_t yaml);
```
- `yaml_get_alias`: Gets the alias of the `yaml` object. The return value is the string value of the alias.
- `yaml_get_anchor`: Gets the anchor object at the specified index `index` in the `yaml` object. The return value is the `yaml` object of the anchor.
- `yaml_set_alias`: Sets an alias for the `yaml` object, where `alias` is the string value of the alias and `doc` is the associated document object. The return value is the `yaml` object after setting the alias.
- `yaml_set_anchor`: Sets an anchor for the `yaml` object, where `anchor` is the string value of the anchor and `doc` is the associated document object. The return value is the `yaml` object after setting the anchor.
- `yaml_anchor_size`: Gets the number of anchors in the `yaml` object. The return value is the number of anchors.
## Reference examples
### Generating a YAML file
```c
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
```
The dumped file **write.yaml**
```yaml
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```
### Loading a YAML file
Similarly, loading a YAML file
```c
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
```
The running result:
```
load success!
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```

432
doc/yaml.md Normal file
View File

@ -0,0 +1,432 @@
## 介绍
### 什么是YAML文件
YAMLYAML Ain't Markup Language是一种**人类可读的数据序列化格式**专注于简洁表达结构化数据。它广泛用于配置文件、数据交换和复杂数据结构的描述如Kubernetes、Ansible等工具的配置而非传统电子表格类的表格数据存储。
### YAML文件的特点
- **可读性高**
使用缩进和符号(如`-`、`:`表示层级关系类似自然语言的格式比JSON/XML更易阅读。
```yaml
user:
name: Alice
age: 30
hobbies:
- reading
- hiking
```
- **支持复杂数据结构**
可表示标量(字符串、数字)、列表、字典等类型,支持嵌套和多行文本(通过`|`或`>`)。
- **跨平台兼容**
纯文本格式,所有操作系统和编程语言均可解析。
- **与编程语言无缝集成**
大多数语言Python、Java、Go等提供原生或第三方库如PyYAML支持YAML解析。
### YAML的用途
1. **配置文件**(核心用途)
- 软件配置如Docker Compose、GitLab CI/CD
- 云原生工具配置Kubernetes manifests
```yaml
# Kubernetes Deployment示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
```
2. **数据序列化**
- 替代JSON/XML传输复杂数据
- API请求/响应的结构化数据描述
3. **数据交换**
- 不同系统间传递结构化信息(如微服务配置同步)
### 如何创建和编辑YAML文件
1. **文本编辑器**
- VS Code推荐安装YAML插件实现语法高亮和校验
- Sublime Text / Vim等
2. **专用工具**
- **Online YAML Validator**:校验语法有效性
- **yq**命令行工具类似jq用于处理YAML
### 注意事项
1. **缩进敏感**
- 必须使用空格通常2或4空格禁止使用Tab
- 缩进错误会导致解析失败
2. **键值对格式**
- 冒号后需有空格:`key: value`(而非`key:value`
3. **特殊字符处理**
- 字符串包含`:`、`#`等符号时建议使用引号:
```yaml
message: "Hello:World"
comment: "This is a # symbol"
```
4. **多行文本**
- 保留换行符:`|`
```yaml
description: |
This is a
multi-line
text.
```
- 折叠换行符:`>`
```yaml
summary: >
This will fold
into a single line.
```
5. **数据类型标记**
- 强制指定类型:
```yaml
boolean: !!bool "true"
timestamp: !!timestamp 2023-07-20T15:30:00Z
```
### 常见错误示例
```yaml
# 错误缩进混用空格和Tab
user:
name: Bob
age: 25 # 缩进不一致
# 错误:键值对缺少空格
key1:value1 # 应改为 key1: value1
# 错误:未转义特殊字符
message: Line 1
Line 2 # 缺少多行文本标识符
```
### C语言版YAML库
varch提供的YAML库简便易用能完成大部分对于YAML文件的基础操作包含对yaml的加载和保存增删改查等操作。
## 接口
### 创建和删除yaml对象
```c
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
```
其中**yaml_t**为yaml的结构体创建方法会生成一个空yaml对象。删除方法则删除指定的yaml对象。
### yaml对象加载
```c
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
```
yaml对象可以从字符串文本中加载也可以从文件中加载。加载成功则会返回一个yaml对象失败则返回NULL。
flag为操作函数的一些flag定义如下
```
#define YAML_F_NONE (0)
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
```
当yaml对象加载失败的时候可以调用`int yaml_error_info(int* line, int* column);`函数进行定位错误。
错误类型包含
```
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* date error */
```
### yaml对象转储
```c
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
```
首先**yaml_dumps**方法将yaml对象按格式转储为字符串。`*len`则是转换出来的字符串长度,传入`NULL`时候就是不获取长度。返回值则是转换出来的字符串,这个字符串是函数分配的,**在结束使用需要`free`掉**。
**yaml_file_dump**方法则是在**yaml_dumps**的基础上将yaml转储到文件当中`filename`传入文件名,返回值为转储的长度,负值表示转储失败。
### yaml添加子对象
```c
#define yaml_seq_add_null(yaml)
#define yaml_seq_add_int(yaml, num)
#define yaml_seq_add_float(yaml, num)
#define yaml_seq_add_string(yaml, string)
#define yaml_seq_add_sequence(yaml, sequence)
#define yaml_seq_add_mapping(yaml, mapping)
#define yaml_map_add_null(yaml, key)
#define yaml_map_add_int(yaml, key, num)
#define yaml_map_add_float(yaml, key, num)
#define yaml_map_add_string(yaml, key, string)
#define yaml_map_add_sequence(yaml, key, sequence)
#define yaml_map_add_mapping(yaml, key, mapping)
```
以上方法分别在序列中和在映射中添加标量,其实就是套用 `insert` 类方法实现的
```c
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
```
这些`insert`类方法用于在`yaml`对象的指定位置插入不同类型的子对象。`key`为插入对象的键,`index`为插入的位置。返回值为插入操作后的`yaml`对象,若插入失败则返回`NULL`。具体如下:
- `yaml_insert_null`:插入一个空类型的子对象。
- `yaml_insert_bool`:插入一个布尔类型的子对象,`b`为布尔值(`YAML_FALSE` 或 `YAML_TRUE`)。
- `yaml_insert_int`:插入一个整数类型的子对象,`num`为整数值。
- `yaml_insert_float`:插入一个浮点数类型的子对象,`num`为浮点数值。
- `yaml_insert_string`:插入一个字符串类型的子对象,`string`为字符串值。
- `yaml_insert_sequence`:插入一个序列类型的子对象,`sequence`为序列对象。
- `yaml_insert_mapping`:插入一个映射类型的子对象,`mapping`为映射对象。
- `yaml_insert_document`:插入一个文档类型的子对象,`document`为文档对象。
- `yaml_insert_reference`:插入一个引用类型的子对象,`anchor`为引用的锚点,`doc`为引用的文档对象。
### yaml移除子对象
```c
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
```
移除`yaml`对象中特定的键为`key`的第`index`个子对象。若移除成功,返回`YAML_E_OK`;若移除失败,返回相应的错误码。
### yaml对象属性操作
```c
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
```
- `yaml_type`:获取`yaml`对象的类型,返回值为`YAML_TYPE_*`系列宏定义的值,用于判断对象是`NULL`、布尔、整数、浮点数、字符串、序列、映射、文档、引用或复杂键等类型。
- `yaml_size`:获取`yaml`对象的大小。对于序列或映射类型的对象,返回其元素的数量;对于其他类型的对象,返回值的含义可能因具体实现而异。
### yaml对象比较和复制
```c
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
```
- `yaml_compare`:比较两个`yaml`对象。`flag`为比较的标志位,用于指定比较的方式。返回值为比较结果,具体含义由实现决定,通常 0 表示相等,非 0 表示不相等。
- `yaml_copy`:复制一个`yaml`对象。`flag`为复制的标志位,用于指定复制的方式。返回值为复制后的`yaml`对象,若复制失败则返回`NULL`。
### yaml对象键操作
```c
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
```
- `yaml_set_key`:为`yaml`对象设置一个简单键,`key`为键的字符串值。返回值为设置键后的`yaml`对象。
- `yaml_set_key_complex`:为`yaml`对象设置一个复杂键,`key`为复杂键的`yaml`对象。返回值为设置键后的`yaml`对象。
- `yaml_key`:获取`yaml`对象的简单键,返回值为键的字符串值。
- `yaml_key_complex`:获取`yaml`对象的复杂键,返回值为复杂键的`yaml`对象。
### yaml对象值设置
```c
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
```
这些方法用于设置`yaml`对象的值,返回值为设置值后的`yaml`对象。具体如下:
- `yaml_set_null`:将`yaml`对象的值设置为空类型。
- `yaml_set_bool`:将`yaml`对象的值设置为布尔类型,`b`为布尔值(`YAML_FALSE` 或 `YAML_TRUE`)。
- `yaml_set_int`:将`yaml`对象的值设置为整数类型,`num`为整数值。
- `yaml_set_float`:将`yaml`对象的值设置为浮点数类型,`num`为浮点数值。
- `yaml_set_string`:将`yaml`对象的值设置为字符串类型,`string`为字符串值。
- `yaml_set_date`:将`yaml`对象的值设置为日期类型,`year`为年份,`month`为月份,`day`为日期。
- `yaml_set_time`:将`yaml`对象的值设置为时间类型,`hour`为小时,`minute`为分钟,`second`为秒,`msec`为毫秒。
- `yaml_set_utc`:将`yaml`对象的值设置为 UTC 时间偏移类型,`hour`为小时偏移,`minute`为分钟偏移。
- `yaml_set_sequence`:将`yaml`对象的值设置为序列类型,`sequence`为序列对象。
- `yaml_set_mapping`:将`yaml`对象的值设置为映射类型,`mapping`为映射对象。
- `yaml_set_document`:将`yaml`对象的值设置为文档类型,`document`为文档对象。
### yaml对象值获取
```c
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
```
这些方法用于获取`yaml`对象的值,返回值为获取到的值。具体如下:
- `yaml_value_bool`:获取`yaml`对象的布尔值。
- `yaml_value_int`:获取`yaml`对象的整数值。
- `yaml_value_float`:获取`yaml`对象的浮点数值。
- `yaml_value_string`:获取`yaml`对象的字符串值。
- `yaml_value_sequence`:获取`yaml`对象的序列值,返回序列对象。
- `yaml_value_mapping`:获取`yaml`对象的映射值,返回映射对象。
- `yaml_value_document`:获取`yaml`对象的文档值,返回文档对象。
### yaml对象子元素操作
```c
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
```
- `yaml_attach`:在`yaml`对象的指定位置`index`附加一个`yaml`子对象`attach`。返回值为操作后的`yaml`对象,若操作失败则返回`NULL`。
- `yaml_dettach`:从`yaml`对象的指定位置`index`分离一个子对象。返回值为分离出的子对象,若操作失败则返回`NULL`。
### yaml对象索引和子对象获取
```c
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
```
- `yaml_get_index`:获取`yaml`对象中键为`key`的第`index`个子对象的索引。返回值为子对象的索引,若未找到则返回`YAML_INV_INDEX`。
- `yaml_get_index_complex`:获取`yaml`对象中复杂键为`key`的子对象的索引。返回值为子对象的索引,若未找到则返回`YAML_INV_INDEX`。
- `yaml_get_child`:获取`yaml`对象中键为`key`的第`index`个子对象。返回值为子对象的`yaml`对象,若未找到则返回`NULL`。
- `yaml_get_child_complex`:获取`yaml`对象中复杂键为`key`的子对象。返回值为子对象的`yaml`对象,若未找到则返回`NULL`。
### yaml对象锚点和别名操作
```c
const char* yaml_get_alias(yaml_t yaml);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
unsigned int yaml_anchor_size(yaml_t yaml);
```
- `yaml_get_alias`:获取`yaml`对象的别名,返回值为别名的字符串值。
- `yaml_get_anchor`:获取`yaml`对象中指定索引`index`的锚点对象,返回值为锚点的`yaml`对象。
- `yaml_set_alias`:为`yaml`对象设置别名,`alias`为别名的字符串值,`doc`为关联的文档对象。返回值为设置别名后的`yaml`对象。
- `yaml_set_anchor`:为`yaml`对象设置锚点,`anchor`为锚点的字符串值,`doc`为关联的文档对象。返回值为设置锚点后的`yaml`对象。
- `yaml_anchor_size`:获取`yaml`对象中锚点的数量。返回值为锚点的数量。
## 参考例子
### 生成yaml文件
```c
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
```
转储的文件 **write.yaml**
```yaml
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```
### 加载yaml文件
同样加载yaml文件
```c
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
```
运行结果:
```
load success!
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```

View File

@ -21,6 +21,7 @@ ALGORITHM_PATH = $(WORKSPACE)/04_algorithm
PARSER_PATH = $(WORKSPACE)/05_parser PARSER_PATH = $(WORKSPACE)/05_parser
PERFORMANCE_PATH = $(WORKSPACE)/06_performance PERFORMANCE_PATH = $(WORKSPACE)/06_performance
MATH_PATH = $(WORKSPACE)/07_math MATH_PATH = $(WORKSPACE)/07_math
COROUTINE_PATH = $(WORKSPACE)/08_coroutine
################################################################################## ##################################################################################
### sources, libaries and head path ### sources, libaries and head path
@ -33,6 +34,7 @@ INCLUDE += $(ALGORITHM_PATH)
INCLUDE += $(PARSER_PATH) INCLUDE += $(PARSER_PATH)
INCLUDE += $(PERFORMANCE_PATH) INCLUDE += $(PERFORMANCE_PATH)
INCLUDE += $(MATH_PATH) INCLUDE += $(MATH_PATH)
INCLUDE += $(COROUTINE_PATH)
LIBSRCS += $(APPLICATION_PATH)/init.c LIBSRCS += $(APPLICATION_PATH)/init.c
LIBSRCS += $(wildcard $(APPLICATION_PATH)/console/*.c) LIBSRCS += $(wildcard $(APPLICATION_PATH)/console/*.c)
@ -43,6 +45,7 @@ LIBSRCS += $(wildcard $(ALGORITHM_PATH)/*.c)
LIBSRCS += $(wildcard $(PARSER_PATH)/*.c) LIBSRCS += $(wildcard $(PARSER_PATH)/*.c)
LIBSRCS += $(wildcard $(PERFORMANCE_PATH)/*.c) LIBSRCS += $(wildcard $(PERFORMANCE_PATH)/*.c)
LIBSRCS += $(wildcard $(MATH_PATH)/*.c) LIBSRCS += $(wildcard $(MATH_PATH)/*.c)
LIBSRCS += $(wildcard $(COROUTINE_PATH)/*.c)
LIBLIST += m LIBLIST += m
LIBLIST += pthread LIBLIST += pthread

View File

@ -1,3 +1,19 @@
version 0.3.5
date 2025.11.20
changes
1. Add the initial version coroutine
---------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------
version 0.3.4
date 2025.05.10
changes
1. Add the initial version yaml parser
---------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------
version 0.3.3 version 0.3.3
date 2025.03.18 date 2025.03.18
changes changes

4
run.sh
View File

@ -10,9 +10,9 @@ fi
echo "compilling..." echo "compilling..."
if [ "$1" == "debug" ]; then if [ "$1" == "debug" ]; then
make CFLAG=-g make -j CFLAG=-g
else else
make make -j
fi fi
if [ -f $exe_file ]; then if [ -f $exe_file ]; then

View File

@ -6,7 +6,7 @@
* \unit list * \unit list
* \brief This is a C language singly linked list with built-in iterators, simple, reliable, fast, small space * \brief This is a C language singly linked list with built-in iterators, simple, reliable, fast, small space
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/

View File

@ -6,7 +6,7 @@
* \unit list * \unit list
* \brief This is a C language singly linked list with built-in iterators, simple, reliable, fast, small space * \brief This is a C language singly linked list with built-in iterators, simple, reliable, fast, small space
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -19,7 +19,7 @@
#define LIST_V_MAJOR 1 #define LIST_V_MAJOR 1
#define LIST_V_MINOR 0 #define LIST_V_MINOR 0
#define LIST_V_PATCH 0 #define LIST_V_PATCH 1
/* list type definition, hiding structural members, not for external use */ /* list type definition, hiding structural members, not for external use */
@ -123,7 +123,7 @@ int list_dsize(list_t list);
* \param[out] data: the address of data * \param[out] data: the address of data
* \return 1 success or 0 fail * \return 1 success or 0 fail
*/ */
#define list_pop_back(list) list_erase((list), list_size(list), 1) #define list_pop_back(list) list_erase((list), list_size(list) - 1, 1)
/** /**
* \brief clear list. * \brief clear list.

View File

@ -6,7 +6,7 @@
* \unit map * \unit map
* \brief This is a general-purpose C language map module, with common data structure * \brief This is a general-purpose C language map module, with common data structure
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -694,8 +694,13 @@ static NODE* node_next(map_t map, NODE* node)
} }
else else
{ {
if (node == node->parent->left) node = node->parent; NODE *parent = node->parent;
else node = node->parent->parent; while (parent != map->nil && node == parent->right)
{
node = parent;
parent = parent->parent;
}
return parent;
} }
return node; return node;
} }
@ -709,8 +714,13 @@ static NODE* node_prev(map_t map, NODE* node)
} }
else else
{ {
if (node == node->parent->right) node = node->parent; NODE *parent = node->parent;
else node = node->parent->parent; while (parent != map->nil && node == parent->left)
{
node = parent;
parent = parent->parent;
}
return parent;
} }
return node; return node;
} }

View File

@ -6,7 +6,7 @@
* \unit map * \unit map
* \brief This is a general-purpose C language map module, with common data structure * \brief This is a general-purpose C language map module, with common data structure
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -20,7 +20,7 @@
#define MAP_V_MAJOR 1 #define MAP_V_MAJOR 1
#define MAP_V_MINOR 0 #define MAP_V_MINOR 0
#define MAP_V_PATCH 0 #define MAP_V_PATCH 1
/* map type definition, hiding structural members, not for external use */ /* map type definition, hiding structural members, not for external use */

View File

@ -6,7 +6,7 @@
* \unit set * \unit set
* \brief This is a general-purpose C language set module, with common data structure * \brief This is a general-purpose C language set module, with common data structure
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -592,8 +592,13 @@ static NODE* node_next(set_t set, NODE* node)
} }
else else
{ {
if (node == node->parent->left) node = node->parent; NODE *parent = node->parent;
else node = node->parent->parent; while (parent != set->nil && node == parent->right)
{
node = parent;
parent = parent->parent;
}
return parent;
} }
return node; return node;
} }
@ -607,8 +612,13 @@ static NODE* node_prev(set_t set, NODE* node)
} }
else else
{ {
if (node == node->parent->right) node = node->parent; NODE *parent = node->parent;
else node = node->parent->parent; while (parent != set->nil && node == parent->left)
{
node = parent;
parent = parent->parent;
}
return parent;
} }
return node; return node;
} }

View File

@ -6,7 +6,7 @@
* \unit set * \unit set
* \brief This is a general-purpose C language set module, with common data structure * \brief This is a general-purpose C language set module, with common data structure
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -19,7 +19,7 @@
#define SET_V_MAJOR 1 #define SET_V_MAJOR 1
#define SET_V_MINOR 0 #define SET_V_MINOR 0
#define SET_V_PATCH 0 #define SET_V_PATCH 1
/* set type definition, hiding structural members, not for external use */ /* set type definition, hiding structural members, not for external use */

7073
source/05_parser/yaml.c Normal file

File diff suppressed because it is too large Load Diff

278
source/05_parser/yaml.h Normal file
View File

@ -0,0 +1,278 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file yaml.h
* \unit yaml
* \brief This is a C language version of yaml streamlined parser
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#ifndef __yaml_H
#define __yaml_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdarg.h>
#include <limits.h>
#include <stdlib.h>
/* version infomation */
#define YAML_V_MAJOR 0
#define YAML_V_MINOR 1
#define YAML_V_PATCH 0
/* yaml type definition, hiding structural members, not for external use */
/**
* \brief Opaque handle to YAML node structure
*
* This typedef provides an opaque pointer to the internal YAML node structure.
* The actual structure definition is hidden from external code to maintain encapsulation.
*/
typedef struct YAML* yaml_t;
/* yaml normal types define */
/**
* \brief Enumeration of YAML data types
*
* These constants represent the different data types supported by the YAML parser.
* They are used to identify the type of value stored in a YAML node.
*/
#define YAML_TYPE_NULL (0) /* null type */
#define YAML_TYPE_BOOL (1) /* bool type */
#define YAML_TYPE_INT (2) /* number int type */
#define YAML_TYPE_FLOAT (3) /* number float type */
#define YAML_TYPE_STRING (4) /* string type */
#define YAML_TYPE_DATE (5) /* date type */
#define YAML_TYPE_SEQUENCE (6) /* sequence type */
#define YAML_TYPE_MAPPING (7) /* mapping type */
#define YAML_TYPE_DOCUMENT (8) /* document type */
#define YAML_TYPE_REFERENCE (9) /* reference type */
#define YAML_TYPE_COMPLEX_KEY (10) /* complex key */
/* bool define */
/**
* \brief Boolean values
*
* These constants represent boolean true and false values in the YAML parser.
*/
#define YAML_FALSE (0) /* bool false */
#define YAML_TRUE (1) /* bool true */
/* error type define */
/**
* \brief Error codes returned by the YAML parser
*
* These constants represent various error conditions that can occur during YAML parsing.
* They are used to indicate the nature of parsing errors to the calling code.
*/
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* target error */
/* dump flags define */
/**
* \brief Flags used to control YAML dumping and loading behavior
*
* These flags can be combined using bitwise OR to modify the behavior
* of functions that dump or load YAML documents.
*/
#define YAML_F_NONE (0) /* none flag */
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
/**
* \brief Represents an invalid index value
*
* This constant is used to indicate an invalid or non-existent index
* in functions that operate on indexed YAML structures.
*/
#define YAML_INV_INDEX ((unsigned int)(-1))
// Functions for creating and deleting YAML nodes.
// These functions are used to initialize and free the memory resources of YAML nodes.
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
// Function for getting YAML parsing error information.
// It is used to obtain error line number and column number during YAML parsing.
int yaml_error_info(int* line, int* column);
// Functions for getting the type and size of a YAML node.
// Used to determine the data type of a YAML node and get the number of child elements of a container node.
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
// Functions for comparing and copying YAML nodes.
// Used to check if two YAML nodes are equal and to create a copy of a YAML node.
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
// Functions for setting the key of a YAML node.
// Used to set a simple key or a complex key for a YAML node.
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
// Functions for getting the key of a YAML node.
// Used to retrieve the simple key or complex key of a YAML node.
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
// Functions for setting the value of a YAML node.
// Used to set a YAML node to different types of values such as null, boolean, integer, float, string, date, time, sequence, mapping, document, etc.
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
// Functions for getting the value of a YAML node.
// Used to retrieve the boolean value, integer value, float value, string value, sequence value, mapping value, document value, etc. of a YAML node.
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
// Functions for attaching and detaching YAML nodes.
// Used to attach a YAML node to another node or detach a child node from a node.
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
// Functions for inserting values into a YAML node.
// Used to insert different types of values such as null, boolean, integer, float, string, sequence, mapping, document, reference, etc. into a YAML node.
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
// Function for removing a value from a YAML node.
// Used to remove a value from a YAML node according to the key and index.
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
// Functions for getting the index of a YAML node.
// Used to obtain the index of a value in a YAML node, supporting the index acquisition of simple keys and complex keys.
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
// Functions for getting child nodes of a YAML node.
// Used to get the child nodes of a YAML node according to the key and index, supporting the retrieval of child nodes of simple keys and complex keys.
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
// Functions for setting and getting the alias of a YAML node.
// Used to set an alias for a YAML node and retrieve the alias of a YAML node.
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
const char* yaml_get_alias(yaml_t yaml);
// Functions for setting and getting the anchor of a YAML node.
// Used to set an anchor for a YAML node, retrieve the anchor, and get the size of the anchor.
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
unsigned int yaml_anchor_size(yaml_t yaml);
// YAML serialization functions.
// Used to serialize a YAML node into a string (yaml_dumps) or write it to a file (yaml_file_dump).
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
// YAML deserialization functions.
// Used to load YAML data from a string (yaml_loads) or a file (yaml_file_dump) and construct a YAML node.
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
// Macros for judging the type of a YAML node.
// Used to determine whether a YAML node is of a specific type (null, bool, int, float, string, sequence, mapping, document).
#define yaml_isnull(yaml) (yaml_type(yaml)==YAML_TYPE_NULL)
#define yaml_isbool(yaml) (yaml_type(yaml)==YAML_TYPE_BOOL)
#define yaml_isint(yaml) (yaml_type(yaml)==YAML_TYPE_INT)
#define yaml_isfloat(yaml) (yaml_type(yaml)==YAML_TYPE_FLOAT)
#define yaml_isstring(yaml) (yaml_type(yaml)==YAML_TYPE_STRING)
#define yaml_issequence(yaml) (yaml_type(yaml)==YAML_TYPE_SEQUENCE)
#define yaml_ismapping(yaml) (yaml_type(yaml)==YAML_TYPE_MAPPING)
#define yaml_isdocument(yaml) (yaml_type(yaml)==YAML_TYPE_DOCUMENT)
// Macros for adding values to a YAML node.
// Used to quickly add different types of values to a YAML sequence or mapping node.
#define yaml_seq_add_null(yaml) yaml_insert_null((yaml), 0, yaml_size((yaml)))
#define yaml_seq_add_bool(yaml, b) yaml_insert_bool((yaml), 0, yaml_size((yaml)), (b))
#define yaml_seq_add_int(yaml, num) yaml_insert_int((yaml), 0, yaml_size((yaml)), (num))
#define yaml_seq_add_float(yaml, num) yaml_insert_float((yaml), 0, yaml_size((yaml)), (num))
#define yaml_seq_add_string(yaml, string) yaml_insert_string((yaml), 0, yaml_size((yaml)), (string))
#define yaml_seq_add_sequence(yaml, sequence) yaml_insert_sequence((yaml), 0, yaml_size((yaml)), (sequence))
#define yaml_seq_add_mapping(yaml, mapping) yaml_insert_mapping((yaml), 0, yaml_size((yaml)), (mapping))
#define yaml_map_add_null(yaml, key) yaml_insert_null((yaml), (key), yaml_size((yaml)))
#define yaml_map_add_bool(yaml, key, b) yaml_insert_bool((yaml), (key), yaml_size((yaml)), (b))
#define yaml_map_add_int(yaml, key, num) yaml_insert_int((yaml), (key), yaml_size((yaml)), (num))
#define yaml_map_add_float(yaml, key, num) yaml_insert_float((yaml), (key), yaml_size((yaml)), (num))
#define yaml_map_add_string(yaml, key, string) yaml_insert_string((yaml), (key), yaml_size((yaml)), (string))
#define yaml_map_add_sequence(yaml, key, sequence) yaml_insert_sequence((yaml), (key), yaml_size((yaml)), (sequence))
#define yaml_map_add_mapping(yaml, key, mapping) yaml_insert_mapping((yaml), (key), yaml_size((yaml)), (mapping))
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,532 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file coroutine.h
* \unit coroutine
* \brief This is a C language coroutine library
* \author Lamdonn
* \version v0.2.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#ifndef __coroutine_H
#define __coroutine_H
#ifdef __cplusplus
extern "C" {
#endif
/* Version infomation */
#define COROUTINE_V_MAJOR 0
#define COROUTINE_V_MINOR 2
#define COROUTINE_V_PATCH 0
#include "coroutine_cfg.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include <setjmp.h>
/**
* \brief: Coroutine task
*/
typedef struct CoTask* CoTask_t;
/**
* \brief: Coroutine timer
*/
typedef struct CoTimer* CoTimer_t;
/**
* \brief: Coroutine task entry function
* \param arg: Task argument address
* \return: Task return value address
*/
typedef void *(*CoTaskEntry_t)(void *arg);
/**
* \brief: Coroutine timer entry function
*/
typedef void (*CoTimerEntry_t)(void);
/**
* \brief: Coroutine lock function
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, lock
*/
typedef void (*CoLock_t)(void);
/**
* \brief: Coroutine unlock function
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, unlock
*/
typedef void (*CoUnlock_t)(void);
/**
* \brief: Coroutine malloc function
* \param size: Allocate memory size
* \return: Allocate memory address
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, malloc
*/
typedef void *(*CoMalloc_t)(size_t size);
/**
* \brief: Coroutine free function
* \param block: Free memory address
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, free
*/
typedef void (*CoFree_t)(void *block);
/**
* \brief: Coroutine tick function
* \return: Current tick value
* \note: The time base on which the coroutine schedule depends, such as system tick.
*/
typedef uint64_t (*CoTick_t)(void);
/**
* \brief: Coroutine event
* \note: Used for synchronization between coroutines
*/
typedef struct
{
uint32_t flag; /**< Event flag, 32 bit storage, each bit can individually represent an event */
} CoEvent;
/**
* \brief: Coroutine scheduler initialize parameter
* \note: Coroutine scheduler initialize parameter is used to initialize the coroutine scheduler.
*/
typedef struct CoSchedulerInitPara
{
CoLock_t lock; /**< Coroutine scheduler lock function */
CoUnlock_t unlock; /**< Coroutine scheduler unlock function */
CoMalloc_t malloc; /**< Coroutine scheduler malloc function */
CoFree_t free; /**< Coroutine scheduler free function */
} CoSchedulerInitPara;
/**
* \brief: Coroutine task create parameter
* \note: Coroutine task create parameter is used to create a coroutine task.
*/
typedef struct CoTaskCreatePara
{
void *pStack; /**< Coroutine task stack address */
size_t stackSize; /**< Coroutine task stack size */
void *arg; /**< Coroutine task argument */
int schedulerId; /**< Coroutine task scheduler id */
} CoTaskCreatePara;
/**
* \brief: Coroutine timer create parameter
* \note: Coroutine timer create parameter is used to create a coroutine timer.
*/
typedef struct CoTimerCreatePara
{
uint64_t ms; /**< Coroutine timer ms, indicates the ms interval */
uint64_t tick; /**< Coroutine timer tick, indicates the tick interval */
int schedulerId; /**< Coroutine timer scheduler id */
} CoTimerCreatePara;
/**
* \brief: Coroutine task wait parameter
* \note: Coroutine task wait parameter is used to blocking task waiting for event or timeout.
*/
typedef struct CoTaskWaitPara
{
CoEvent *pEvent; /**< Coroutine task wait event pointer */
uint64_t ms; /**< Coroutine task wait timeout in ms */
uint64_t tick; /**< Coroutine task wait timeout in tick */
} CoTaskWaitPara;
/**
* \brief: Coroutine task create parameter default value
* \note: Coroutine task create parameter default value is used to create a coroutine task.
*/
#define COROUTINE_E_OK (0) /**< Coroutine task create parameter default value, indicates success */
#define COROUTINE_E_INVALID_PARAMETER (-1) /**< Coroutine task create parameter default value, indicates invalid parameter */
#define COROUTINE_E_INVALID_TICK (-2) /**< Coroutine task create parameter default value, indicates invalid tick function */
#define COROUTINE_E_INVALID_TICK_INTERVAL (-3) /**< Coroutine task create parameter default value, indicates invalid tick interval */
#define COROUTINE_E_INVALID_LOCK (-4) /**< Coroutine task create parameter default value, indicates invalid lock function */
#define COROUTINE_E_INVALID_MHOOK (-5) /**< Coroutine task create parameter default value, indicates invalid memory hook function */
#define COROUTINE_E_TCB_MEM_STACK_FAIL (-6) /**< Coroutine task create parameter default value, indicates memory stack fail */
/**
* \brief: Coroutine event default value
* \note: Coroutine event default value is used to initialize the coroutine event.
*/
#define COEVENT_STATIC_VALUE ((CoEvent){.flag=0})
/**
* \brief: Coroutine scheduler initialize function
* \param pScheduler: [Non-default parameter] Coroutine scheduler pointer
* \param tick: [Non-default parameter] Coroutine scheduler tick function, must be specified
* \param tickInterval: [Non-default parameter] Coroutine scheduler tick interval, indicates how many ns each tick is, must be specified
* \param lock: [Default parameter] Coroutine scheduler lock function,
* for use on multiple threads, you must specify as follows .lock=thread_lock
* \param unlock: [Default parameter] Coroutine scheduler unlock function,
* for use on multiple threads, you must specify as follows .unlock=thread_unlock
* \param malloc: [Default parameter] Coroutine scheduler malloc function,
* if you need to allocate memory dynamically (CoTask, CoTimer, stack, etc),
* you must specify as follows .malloc=mallloc
* \param free: [Default parameter] Coroutine scheduler free function,
* if you need to free memory dynamically (CoTask, CoTimer, stack, etc),
* you must specify as follows .free=free
* \return: Coroutine task create parameter default value, indicates success
* \note: Coroutine scheduler initialize function is used to initialize the coroutine scheduler.
* It must be called once before any other coroutine scheduler function.
* Only one initialization is allowed within a single thread
* It will initialize the coroutine scheduler with the given parameter.
* The coroutine scheduler will use the given tick function to measure the load of each task.
* The tick function must be called periodically with the given tick interval.
* The coroutine scheduler will use the given lock function to protect the shared resources.
* The coroutine scheduler will use the given malloc function to allocate memory for the tasks.
* The coroutine scheduler will use the given free function to free memory of the tasks.
* \warning: The coroutine scheduler will not check the validity of the given parameter.
* You must ensure that the given parameter is valid.
* \example:
* CoScheduler_Init(0, GetTimerMs, 1000000);
* \example:
* CoScheduler_Init(0, GetTimerUsec, 1000);
* \example:
* CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
* \example:
* CoScheduler_Init(0, GetTimerUsec, 1000, .malloc=mallloc, .free=free);
*/
#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...) CoScheduler_InitP(CoSchedulerId, tick, tickInterval, (CoSchedulerInitPara[1]){[0]={.lock=NULL,.unlock=NULL,.malloc=NULL,.free=NULL,__VA_ARGS__}})
/**
* \brief: Coroutine scheduler initialize function
* \param pScheduler: Coroutine scheduler pointer
* \param tick: Coroutine scheduler tick function
* \param tickInterval: Coroutine scheduler tick interval, indicates how many ns each tick is
* \param pPara: Coroutine scheduler initialize parameter pointer
* \return: Coroutine task create parameter default value, indicates success
* \note: Coroutine scheduler initialize function is used to initialize the coroutine scheduler.
* It must be called once before any other coroutine scheduler function.
* Only one initialization is allowed within a single thread
* It will initialize the coroutine scheduler with the given parameter.
* The coroutine scheduler will use the given tick function to measure the load of each task.
* The tick function must be called periodically with the given tick interval.
* The coroutine scheduler will use the given lock function to protect the shared resources.
* The coroutine scheduler will use the given malloc function to allocate memory for the tasks.
* The coroutine scheduler will use the given free function to free memory of the tasks.
*/
int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara);
/**
* \brief: Coroutine scheduler start function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine task create parameter default value, indicates success
* \note: Coroutine scheduler start function is used to start the coroutine scheduler.
* It must be called once after the coroutine scheduler is initialized.
* It will start the coroutine scheduler and run the tasks.
* \example:
* CoScheduler_Init(0, GetTimerUsec, 1000);
* testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest), 0);
* CoScheduler_Start(0);
*/
int CoScheduler_Start(uint32_t CoSchedulerId);
/**
* \brief: Coroutine scheduler exit function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine task create parameter default value, indicates success
* \note: Coroutine scheduler exit function is used to exit the coroutine scheduler.
* It must be called once after the coroutine scheduler is started.
* It will exit the coroutine scheduler and stop the tasks.
*/
int CoScheduler_Exit(uint32_t CoSchedulerId);
/**
* \brief: Coroutine scheduler task count function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine scheduler task count, indicates the number of tasks in the coroutine scheduler
* \note: Coroutine scheduler task count function is used to get the number of tasks in the coroutine scheduler.
*/
int CoScheduler_TaskCount(uint32_t CoSchedulerId);
/**
* \brief: Coroutine scheduler timer count function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine scheduler timer count, indicates the number of timers in the coroutine scheduler
* \note: Coroutine scheduler timer count function is used to get the number of timers in the coroutine scheduler.
*/
int CoScheduler_TimerCount(uint32_t CoSchedulerId);
/**
* \brief: Coroutine scheduler current load function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine scheduler current load, indicates the load of the coroutine scheduler
* \note: Coroutine scheduler current load function is used to get the current load of the coroutine scheduler.
* The load is the number of ticks that the coroutine scheduler has run.
* The load is updated periodically with the given tick interval.
*/
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
#endif
/**
* \brief: Coroutine scheduler maximum load function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine scheduler maximum load, indicates the maximum load of the coroutine scheduler
* \note: Coroutine scheduler maximum load function is used to get the maximum load of the coroutine scheduler.
* The load is the number of ticks that the coroutine scheduler has run.
* The load is updated periodically with the given tick interval.
*/
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
#endif
/**
* \brief: Coroutine task create function
* \param entry: [Non-default parameter] Coroutine task entry function, indicates the entry function of the coroutine task, must be specified
* \param stack: [Default parameter] Coroutine task stack, indicates the stack of the coroutine task.
* If not specified, it is allocated via the scheduler's malloc function.
* \param stackSize: [Default parameter] Coroutine task stack size, indicates the size of the coroutine task stack
* If not specified, it is the default stack size `COROUTINE_STACK_DEFAULT_SIZE`.
* \param arg: [Default parameter] Coroutine task argument, indicates the argument of the coroutine task
* \param schedulerId: [Default parameter] Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
* Negative value means the auto distribution.
* \return: Coroutine task handle, indicates the created coroutine task
* \note: Coroutine task create function is used to create a coroutine task.
* It will create a coroutine task with the given parameter.
* The coroutine task will run in the coroutine scheduler.
* \example:
* CoTask_t testCoroutine = CoTask_Create(test);
* \example:
* CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
* \example:
* CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
* \example:
* int arg = 4096;
* CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
*/
#define CoTask_Create(entry, ...) CoTask_CreateP(entry, (CoTaskCreatePara[1]){[0]={.pStack=NULL,.stackSize=0,.arg=NULL,.schedulerId=-1,__VA_ARGS__}})
/**
* \brief: Coroutine task create function, it is recommended to use @ref `CoTask_Create` instead
* \param pPara: Coroutine task create parameter pointer
* \return: Coroutine task handle, indicates the created coroutine task
* \note: Coroutine task create function is used to create a coroutine task.
* It will create a coroutine task with the given parameter.
* The coroutine task will run in the coroutine scheduler.
*/
CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara);
/**
* \brief: Coroutine task delete function
* \param CoTask: Coroutine task handle, indicates the coroutine task to be deleted
* \return: Coroutine task delete parameter default value, indicates success
* \note: Coroutine task delete function is used to delete a coroutine task.
* It will delete the given coroutine task.
* The coroutine task must be in the deleted state.
*/
int CoTask_Delete(CoTask_t CoTask);
/**
* \brief: Coroutine task self function
* \return: Coroutine task handle, indicates the current coroutine task
* \note: Coroutine task self function is used to get the current coroutine task.
* It will return the handle of the current coroutine task.
*/
CoTask_t CoTask_Self(void);
/**
* \brief: Coroutine task scheduler id function
* \return: Coroutine task scheduler id, indicates the scheduler id of the coroutine task
* \note: Coroutine task scheduler id function is used to get the scheduler id of the coroutine task.
*/
int CoTask_SchedulerId(void);
/**
* \brief: Coroutine task stack max used function
* \param CoTask: Coroutine task handle, indicates the coroutine task to get the stack max used
* \return: Coroutine task stack max used, indicates the maximum stack used of the coroutine task
* \note: Coroutine task stack max used function is used to get the maximum stack used of the coroutine task.
* The stack used is the maximum stack used of the coroutine task.
* The stack used is updated periodically with the given tick interval.
*/
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
#endif
/**
* \brief: Coroutine task stack current used function
* \param CoTask: Coroutine task handle, indicates the coroutine task to get the stack current used
* \return: Coroutine task stack current used, indicates the current stack used of the coroutine task
* \note: Coroutine task stack current used function is used to get the current stack used of the coroutine task.
* The stack used is the current stack used of the coroutine task.
* The stack used is updated periodically with the given tick interval.
*/
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t CoTask_StackCurUsed(CoTask_t CoTask);
#endif
/**
* \brief: Coroutine task wait function, blocking task waiting for event or timeout.
* \param pEvent: [Default parameter] Coroutine event pointer, indicates the event to wait
* \param ms: [Default parameter] Coroutine task wait ms, indicates the ms to wait
* \param tick: [Default parameter] Coroutine task wait tick, indicates the tick to wait
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
* \note: Coroutine task wait event function is used to wait for the given event.
* @ref `CoEvent_Init` must first be called to initialize the event.
* @ref `CoEvent_Notify` notifies the occurrence of the event
* It will block the current coroutine task.
* The event value is a bit mask, each bit represents an event.
* If multiple events are triggered, the corresponding bits will be set.
* \note: Coroutine task wait ms function is used to wait for the given ms.
* It will block the current coroutine task.
* \note: Coroutine task wait tick function is used to wait for the given tick.
* It will block the current coroutine task.
* \example:
* void *test(void *arg)
* {
* while (1)
* {
* // Give up access to the scheduler without waiting, similar to yeild
* CoTask_Wait();
*
* // Wait for 1000 ms
* CoTask_Wait(.ms=1000);
*
* // Wait for 1000000 tick
* CoTask_Wait(.tick=1000000);
*
* // Wait for 1000 ms or event, if no any events occur within 1000 ms, and will timeout blocking
* CoTask_Wait(.pEvent=&g_Event, .ms=1000);
*
* // Always wait for the event, and judge every events
* uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
* if (evs & 0x01)
* {
* printf("event 0x01 triggered\n");
* }
* if (evs & 0x02)
* {
* printf("event 0x02 triggered\n");
* }
* }
* }
*/
#define CoTask_Wait(...) CoTask_WaitP((CoTaskWaitPara[1]){[0]={.pEvent=NULL,.ms=0,.tick=0,__VA_ARGS__}})
/**
* \brief: Coroutine task wait function, it is recommended to use @ref `CoTask_Wait` instead
* \param pPara: Coroutine task wait parameter pointer
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
* \note: Coroutine task wait function is used to wait for the given event or timeout.
* It will block the current coroutine task.
* The event value is a bit mask, each bit represents an event.
* If multiple events are triggered, the corresponding bits will be set.
*/
uint32_t CoTask_WaitP(CoTaskWaitPara *pPara);
/**
* \brief: Coroutine task wait functions, the version of the macro definition used is often used
* \param m: Coroutine task wait ms, indicates the ms to wait
* \param t: Coroutine task wait tick, indicates the tick to wait
* \param e: Coroutine event pointer, indicates the event to wait
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
*/
#define CoTask_WaitMs(m) CoTask_Wait(.ms=m)
#define CoTask_WaitTick(t) CoTask_Wait(.tick=t)
#define CoTask_WaitEvent(e) CoTask_Wait(.pEvent=e)
#define CoTask_WaitEventMs(e, m) CoTask_Wait(.pEvent=e, .ms=m)
#define CoTask_WaitEventTick(e, t) CoTask_Wait(.pEvent=e, .tick=t)
/**
* \brief: Coroutine timer create function
* \param entry: [Non-default parameter] Coroutine timer entry, indicates the timer entry function
* \param ms: [Default parameter] Coroutine timer ms, indicates the ms interval
* \param tick: [Default parameter] Coroutine timer tick, indicates the tick interval
* \param schedulerId: [Default parameter] Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
* Negative value means the auto distribution.
* \return: Coroutine timer handle, indicates the created timer
* \note: One of the ms and tick parameters must be set
* \note: Coroutine timer create function is used to create a timer with the given ms or tick interval.
* The timer will call the given entry function periodically with the given ms or tick interval.
* \example:
* CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100); // create a timer with 100ms interval
*/
#define CoTimer_Create(entry, ...) CoTimer_CreateP(entry, (CoTimerCreatePara[1]){[0]={.ms=0,.tick=0,.schedulerId=-1,__VA_ARGS__}})
/**
* \brief: Coroutine timer create function, it is recommended to use @ref `CoTimer_Create` instead
* \param pPara: Coroutine timer create parameter pointer
* \return: Coroutine timer handle, indicates the created timer
* \note: Coroutine timer create function is used to create a timer with the given ms or tick interval.
* The timer will call the given entry function periodically with the given ms or tick interval.
*/
CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara);
/**
* \brief: Coroutine timer create ms function, it is recommended to use @ref `CoTimer_CreateMs` instead
* \param entry: Coroutine timer entry, indicates the timer entry function
* \param m: Coroutine timer ms, indicates the ms interval
* \param t: Coroutine timer tick, indicates the tick interval
* \return: Coroutine timer handle, indicates the created timer
* \note: Coroutine timer create ms function is used to create a timer with the given ms interval.
* The timer will call the given entry function periodically with the given ms interval.
* \example:
* CoTimer_t timer = CoTimer_CreateMs(timer_entry, 100); // create a timer with 100ms interval
*/
#define CoTimer_CreateMs(entry, m) CoTimer_Create(entry, .ms=m)
#define CoTimer_CreateTick(entry, t) CoTimer_Create(entry, .tick=t)
/**
* \brief: Coroutine timer delete function
* \param Timer: Coroutine timer handle, indicates the timer to delete
* \note: Coroutine timer delete function is used to delete the given timer.
* It must be called after @ref `CoTimer_CreateTick` or @ref `CoTimer_CreateMs`.
* \example:
* CoTimer_Delete(timer); // delete the timer
*/
void CoTimer_Delete(CoTimer_t Timer);
/**
* \brief: Coroutine timer self function
* \return: Coroutine timer handle, indicates the current timer
* \note: Coroutine timer self function is used to get the handle of the current timer.
* \example:
* CoTimer_t timer = CoTimer_Self(); // get the current timer
*/
CoTimer_t CoTimer_Self(void);
/**
* \brief: Coroutine timer scheduler id function
* \return: Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
* \note: Coroutine timer scheduler id function is used to get the scheduler id of the coroutine timer.
*/
int CoTimer_SchedulerId(void);
/**
* \brief: Coroutine event initialize function
* \param pEvent: Coroutine event pointer, indicates the event to initialize
* \note: Coroutine event initialize function is used to initialize the given event.
* It must be called before @ref `CoTask_WaitEvent`.
* \example:
* CoEvent_Init(&g_Event);
*/
void CoEvent_Init(CoEvent *pEvent);
/**
* \brief: Coroutine event notify function
* \param pEvent: Coroutine event pointer, indicates the event to notify
* \param evs: Coroutine event value, indicates the event value to notify
* \note: Coroutine event notify function is used to notify the occurrence of the event.
* It will set the corresponding bits in the event value.
* \example:
* CoEvent_Notify(&g_Event, 0x01); // notify event 0x01
* \example:
* CoEvent_Notify(&g_Event, 0x01 | 0x02); // notify event 0x01 and 0x02
*/
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
#ifdef __cplusplus
}
#endif
#endif // !__coroutine_H

View File

@ -0,0 +1,172 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file coroutine_cfg.h
* \unit coroutine
* \brief This is a C language coroutine library
* \author Lamdonn
* \version v0.2.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#ifndef __coroutine_cfg_H
#define __coroutine_cfg_H
/**
* \brief Coroutine scheduler number
* \note This macro defines the number of coroutine schedulers
*/
#define COROUTINE_SCHEDULER_NUMBER 1
/**
* \brief Coroutine static task max number
* \note This macro defines the maximum number of coroutine tasks that can be created statically
*/
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8
/**
* \brief Coroutine static timer max number
* \note This macro defines the maximum number of coroutine timers that can be created statically
*/
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8
/**
* \brief Coroutine static stack max number
* \note This macro defines the maximum number of coroutine stacks that can be created statically
*/
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8
/**
* \brief Coroutine stack default size
* \note This macro defines the default size of the coroutine stack
* \note If the coroutine stack size is not specified, this value will be used
*/
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // 4096 //
/**
* \brief Coroutine enable stack calculate
* \note This macro defines whether to enable the stack calculate feature
* \note If enabled, the coroutine stack using will be calculated automatically
*/
#define COROUTINE_ENABLE_STACK_CALCULATE 1
/**
* \brief Coroutine enable loading calculate
* \note This macro defines whether to enable the loading calculate feature
* \note If enabled, the coroutine loading will be calculated automatically
*/
#define COROUTINE_ENABLE_LOADING_CALCULATE 1
/**
* \brief Coroutine loading queue size
* \note This macro defines the size of the coroutine loading queue
*/
#define COROUTINE_LOADING_CALCULATE_QSIZE 10
/**
* \brief Coroutine loading calculate period
* \note This macro defines the period of the coroutine loading calculate
* \note The unit is ms
*/
#define COROUTINE_LOADING_CALCULATE_PERIOD 100
/**
* \brief Stack get and set Pointers depending on the platform definition
* \param p Pointer to the stack
* \return None
* \note This macro is used to get and set the stack pointer depending on the platform definition
* \note For x86_32, the stack pointer is esp; for x86_64, the stack pointer is rsp; for arm, the stack pointer is sp
* \note Common platforms have been implemented, not implemented platforms need to be implemented by users
*/
#if defined(__i386__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%esp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%esp" : : "r" (p) : "memory")
#elif defined(__x86_64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%rsp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%rsp" : : "r" (p) : "memory")
#elif defined(__arm__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__aarch64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__mips__)
#define COROUTINE_GET_SP(p) __asm__ volatile(".set noreorder\n\t" "move %0, $sp\n\t" ".set reorder" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile(".set noreorder\n\t" "move $sp, %0\n\t" ".set reorder" : : "r" (p) : "memory")
#elif defined(__riscv__)
#if __riscv_xlen == 32
#define COROUTINE_GET_SP(p) __asm__ volatile("mv %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mv sp, %0" : : "r" (p) : "memory")
#else
#define COROUTINE_GET_SP(p) __asm__ volatile("mv %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mv sp, %0" : : "r" (p) : "memory")
#endif
#elif defined(__powerpc__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__powerpc64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__s390__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
#elif defined(__s390x__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
#elif defined(__sparc__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
#elif defined(__sparcv9__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
#elif defined(__tile__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
#elif defined(__tilegx__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
#elif defined(__hppa__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__hppa64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__ia64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0=sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp=%0" : : "r" (p) : "memory")
#elif defined(__loongarch__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__m68k__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__mips64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__parisc__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__parisc64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__s390__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
#elif defined(__s390x__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
#elif defined(__sh__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__sh64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__xtensa__)
#define COROUTINE_GET_SP(p) __asm__ volatile("rsr.a1 %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("wsr.a1 %0" : : "r" (p) : "memory")
#else
#error "Unsupported platform, please implement `COROUTINE_GET_SP` and `COROUTINE_SET_SP`"
#endif
#endif // !__coroutine_cfg_H

108
test/file/read.yaml Normal file
View File

@ -0,0 +1,108 @@
mapping:
open: Yes
string null: string null
string null n: string null
n
string sigle: 'string sigle'
string sigle n: 'string
sigle'
string double: "string double"
string double n: "string
double"
string scalar: |
string
scalar
string scalar n: |2
string
scalar
string scalar -: |-
string
scalar
string scalar +: |+
string
scalar
string fold: >
string
fold
string fold n: >2
string
fold
string fold -: >-
string
fold
string fold +: >+
string
fold
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
number dec: 123456
number hex: 0x123456
number float: 012.3456
number e: 1.23e-4
number nan: .nan
number inf: .inf
number inf n: -.inf
Anchors:
people: &people
name: people
age: 18
height: 178
cat: &cat
name: cat
age: 2
string: &string
This is string
sequence: &sequence
- 1
- 2
Zhang San: &default
color: blue
size: medium
property: *people
Inherit:
<<: *people
# shape: circle
square brackets: [1,2,3,4]
brace brackets: {a: 1, b: 2, c: 3}
bin: -0b1010_0111_0100_1010_1110
iso8601: 2025-03-29T23:59:43.89-05:00
date: 2025-03-29
time: 2025-03-29T23:59:43
utc: 2025-03-29T23:59:43+08:00
int_22: !!int 123456
muti:
a: "AABCDEFG"
?
- a
- b
: fruit

11
test/file/write.yaml Normal file
View File

@ -0,0 +1,11 @@
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg

View File

@ -9,6 +9,7 @@ TEST_LIST += txls
TEST_LIST += xml TEST_LIST += xml
TEST_LIST += csv TEST_LIST += csv
TEST_LIST += json TEST_LIST += json
TEST_LIST += yaml
TEST_LIST += vector TEST_LIST += vector
TEST_LIST += list TEST_LIST += list
TEST_LIST += str TEST_LIST += str
@ -46,3 +47,4 @@ TEST_LIST += slup
# TEST_LIST += cpul # TEST_LIST += cpul
TEST_LIST += date TEST_LIST += date
TEST_LIST += unitt TEST_LIST += unitt
TEST_LIST += coroutine

320
test/test_coroutine.c Normal file
View File

@ -0,0 +1,320 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#if defined(TEST_TARGET_coroutine)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/coroutine.h>
#else
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "coroutine.h"
#endif
#define MUTI_THREAD_NUM 2
#if (MUTI_THREAD_NUM <= 0)
#error "MUTI_THREAD_NUM must be greater than 0"
#endif
#ifdef _WIN32
#include <windows.h>
uint64_t GetTimerUsec(void)
{
LARGE_INTEGER li;
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&li);
return (uint64_t)li.QuadPart * 1000000 / frequency.QuadPart;
}
#else
#include <sys/time.h>
uint64_t GetTimerUsec(void)
{
struct timeval mstime;
uint64_t us = 0;
gettimeofday(&mstime, NULL);
us = mstime.tv_sec * 1000000 + mstime.tv_usec;
return us;
}
#endif
/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/
// #define EXIT_TEST
extern uint64_t unitt_clock(void);
static int test_0(void)
{
for (int i = 0; i < 100; i++)
{
if (0)
{
#if defined (EXIT_TEST)
exit(0);
#endif
return UNITT_E_FAIL;
}
}
return UNITT_E_OK;
}
static void unitt_task(void)
{
static UNITT_TCASE rand_tests[] = {
UNITT_TCASE(test_0),
// UNITT_TCASE(test_1),
// UNITT_TCASE(test_2),
};
static UNITT suites[] = {
{ "coroutine suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
};
UNITT_EXE(suites);
}
/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/
static CoEvent gEvent = COEVENT_STATIC_VALUE;
static CoTask_t gTickCoroutine = NULL;
static CoTask_t gTockCoroutine = NULL;
static uint8_t gStackTick[1024 * 10];
static uint8_t gStackTock[1024 * 10];
static CoTimer_t gTimer0Handle = NULL;
static CoTimer_t gTimer1Handle = NULL;
static void *tick(void *arg)
{
while (1)
{
CoTask_Wait(.ms=1000);
printf("tick-running...\n");
CoEvent_Notify(&gEvent, 0x02);
}
}
static void *tock(void *arg)
{
while (1)
{
uint32_t evs = CoTask_Wait(.pEvent=&gEvent);
if (evs & 0x01)
{
printf("event 0x01 triggered...\n");
}
if (evs & 0x02)
{
printf("event 0x02 triggered...\n");
}
}
}
static void timer0(void)
{
printf("timer0-running...\n");
}
static void timer1(void)
{
printf("timer1-running... %d\n", CoScheduler_CurLoad(CoTimer_SchedulerId()));
// CoEvent_Notify(&gEvent, 0x01 | 0x02);
}
/* Testing begins here */
void *createTask(void *arg)
{
// CoTask_Create(tick, g_StackTick, sizeof(g_StackTick), 0);
// CoTask_Create(tock, g_StackTock, sizeof(g_StackTock), 0);
CoTask_Create(tick);
CoTask_Create(tock);
// timerHandle0 = CoTimer_CreateMs(timer0_entry, 100);
// timerHandle1 = CoTimer_CreateMs(timer1_entry, 1000);
}
#if (MUTI_THREAD_NUM == 1)
void test_singleThread(void)
{
CoScheduler_Init(0, GetTimerUsec, 1000);
createTask(NULL);
CoScheduler_Start(0);
}
#else
static pthread_t threads[MUTI_THREAD_NUM];
static pthread_t testThread;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static void thread_lock(void)
{
pthread_mutex_lock(&mutex);
}
static void thread_unlock(void)
{
pthread_mutex_unlock(&mutex);
}
static void *muti_thread_entry(void *arg)
{
CoScheduler_Start(*(int *)arg);
}
void test_mutiThread(void)
{
int threadId[MUTI_THREAD_NUM];
for (int i = 0; i < MUTI_THREAD_NUM; i++)
{
CoScheduler_Init(i, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
}
createTask(NULL);
for (int i = 0; i < MUTI_THREAD_NUM; i++)
{
threadId[i] = i;
pthread_create(&threads[i], NULL, muti_thread_entry, &threadId[i]);
/* You can bind thread to specific core */
/* So that each CoScheduler can run on different core */
}
for (int i = 0; i < MUTI_THREAD_NUM; i++)
{
pthread_join(threads[i], NULL);
}
}
#endif
static void test_base(void)
{
#if (MUTI_THREAD_NUM == 1)
test_singleThread();
#else
test_mutiThread();
#endif
}
/************************************************************************************/
/************************************* Command ************************************/
/************************************************************************************/
static void usage(void)
{
printf(
"Usage: coroutine [opt] [arg] ...\n"
"\n"
"options:\n"
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
" <base> Test base function\n"
" <ut> Unit test\n"
" ...\n"
" -h Print help\n"
" -v Print version\n"
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
"\n"
);
}
static int test(int argc, char *argv[])
{
char *execute = NULL;
int ut_period = 1000;
/* reset getopt */
command_opt_init();
while (1)
{
int opt = command_getopt(argc, argv, "e:hvu::");
if (opt == -1) break;
switch (opt)
{
// Add others opt here
case 'u' :
if (command_optarg) ut_period = atoi(command_optarg);
break;
case 'e' :
execute = command_optarg;
break;
case 'v' :
printf("coroutine version %d.%d.%d\r\n", COROUTINE_V_MAJOR, COROUTINE_V_MINOR, COROUTINE_V_PATCH);
return 0;
case '?':
printf("Unknown option `%c`\r\n", command_optopt);
return -1;
case 'h' :
default:
usage();
return 0;
}
}
if (execute)
{
if (!strcmp(execute, "base"))
{
test_base();
}
else if (!strcmp(execute, "ut"))
{
#if defined(TEST_TARGET_coroutine)
while (1)
{
unitt_task();
usleep(1000 * ut_period);
}
#else
printf("create task %d\r\n", task_create(ut_period, unitt_task));
#endif
}
}
else
{
test_base();
}
#if 0
if (1 == command_optind) // no opt
{
}
for (int index = command_optind; index < argc; index++)
{
if (!strcmp(argv[index], "base"))
{
test_base();
}
}
if (1 == argc)
{
test_base();
}
#endif
return 0;
}
/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/
#if defined(TEST_TARGET_coroutine)
int main(int argc, char *argv[])
{
return test(argc, argv);
}
#else
void test_coroutine(void)
{
command_export("coroutine", test);
}
init_export_app(test_coroutine);
#endif

View File

@ -4,6 +4,7 @@
#include <time.h> #include <time.h>
#include <math.h> #include <math.h>
#include <float.h> #include <float.h>
#include <stdint.h>
#if defined(TEST_TARGET_floatl) #if defined(TEST_TARGET_floatl)
#include <varch/command.h> #include <varch/command.h>
#include <varch/unitt.h> #include <varch/unitt.h>
@ -295,8 +296,8 @@ float double_to_float(double d)
} }
// 将 double 的二进制表示转换为 uint64_t // 将 double 的二进制表示转换为 uint64_t
typedef unsigned long long uint64_t; // typedef unsigned long long uint64_t;
typedef long long int64_t; // typedef long long int64_t;
uint64_t double_to_bits(double x) { uint64_t double_to_bits(double x) {
return *((uint64_t*)&x); return *((uint64_t*)&x);

579
test/test_yaml.c Normal file
View File

@ -0,0 +1,579 @@
#include <stdio.h>
#include <string.h>
#if defined(TEST_TARGET_yaml)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/yaml.h>
#else
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "yaml.h"
#endif
/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/
// #define EXIT_TEST
extern uint64_t unitt_clock(void);
static int test_0(void)
{
for (int i = 0; i < 100; i++)
{
if (0)
{
#if defined (EXIT_TEST)
exit(0);
#endif
return UNITT_E_FAIL;
}
}
return UNITT_E_OK;
}
static void unitt_task(void)
{
static UNITT_TCASE rand_tests[] = {
UNITT_TCASE(test_0),
// UNITT_TCASE(test_1),
// UNITT_TCASE(test_2),
};
static UNITT suites[] = {
{ "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
};
UNITT_EXE(suites);
}
/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/
#define READ_FILE "test/file/read.yaml"
#define WRITE_FILE "test/file/write.yaml"
static void yaml_preview(yaml_t yaml)
{
char *s = yaml_dumps(yaml, 0, 0, YAML_F_NONE);
if (s)
{
printf("%s\r\n", s);
free(s);
}
}
static void test_create(void)
{
yaml_t yaml = yaml_create();
if (yaml)
{
printf("yaml_create success!!! %p\r\n", yaml);
}
else
{
printf("yaml_create fail!!!\r\n");
}
yaml_delete(yaml);
}
static void test_add(void)
{
yaml_t yaml = NULL, info = NULL, hobby = NULL, key = NULL, complex = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "Lamdonn", yaml);
info = yaml_map_add_mapping(yaml, "Zhangsan", NULL);
yaml_map_add_int(info, "age", 29);
yaml_map_add_float(info, "height", 175);
yaml_map_add_string(info, "email", "Zhangsan@163.com");
hobby = yaml_map_add_sequence(info, "hobby", NULL);
yaml_seq_add_string(hobby, "Playing basketball");
yaml_seq_add_string(hobby, "Playing football");
yaml_seq_add_string(hobby, "Playing mobile game");
yaml_t kk = yaml_seq_add_mapping(hobby, NULL);
yaml_map_add_int(kk, "year", 29);
yaml_map_add_float(kk, "month", 6);
info = yaml_map_add_mapping(yaml, "Lisi", NULL);
complex = yaml_map_add_int(info, "1", 29);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_insert(void)
{
yaml_t root = NULL, doc = NULL, t = NULL;
root = yaml_create();
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
yaml_preview(root);
yaml_delete(root);
}
static void test_remove(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_remove(info, "email", 0);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_set(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_null(info);
yaml_preview(yaml);
yaml_set_bool(info, YAML_TRUE);
yaml_preview(yaml);
yaml_set_int(info, 123);
yaml_preview(yaml);
yaml_set_float(info, 3.14159);
yaml_preview(yaml);
yaml_set_string(info, "Hello yaml");
yaml_preview(yaml);
yaml_set_date(info, 2025, 01, 01);
yaml_set_time(info, 16, 30, 03, 200);
yaml_set_utc(info, +8, 30);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_get(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_t bool_ = yaml_map_add_bool(info, "open source", YAML_TRUE);
printf("bool_: %d\r\n", yaml_value_bool(bool_));
yaml_t int_ = yaml_map_add_int(info, "age", 26);
printf("int_: %d\r\n", yaml_value_int(int_));
yaml_t float_ = yaml_map_add_float(info, "height", 178.5);
printf("float_: %f\r\n", yaml_value_float(float_));
yaml_t string_ = yaml_map_add_string(info, "email", "Lamdonn@163.com");
printf("string_: %s\r\n", yaml_value_string(string_));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_key(void)
{
yaml_t yaml = NULL, key = NULL;
yaml = yaml_create();
yaml_set_key(yaml, "StringKey");
printf("key: %s\r\n", yaml_key(yaml));
key = yaml_create();
yaml_map_add_int(key, "a", 12);
yaml_map_add_int(key, "b", 13);
yaml_set_key_complex(yaml, key);
printf("complex key: \r\n");
yaml_preview(yaml_key_complex(yaml));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_child(void)
{
yaml_t yaml = NULL, info = NULL, key = NULL, child = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
key = yaml_create();
yaml_map_add_int(key, "a", 12);
yaml_map_add_int(key, "b", 13);
yaml_set_key_complex(yaml_map_add_string(info, "0", "This is a complex key"), key);
child = yaml_get_child(info, "age", 0);
printf("child['age']: %d\r\n", yaml_value_int(child));
child = yaml_get_child(info, NULL, 1);
printf("child[1]: %f\r\n", yaml_value_float(child));
child = yaml_get_child_complex(info, key);
printf("child[?]: %s\r\n", yaml_value_string(child));
printf("child['email'] index = %u\r\n", yaml_get_index(info, "email", 0));
printf("child[?] index = %u\r\n", yaml_get_index_complex(info, key));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_alias(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "AuthorInfo", yaml);
printf("alias: %s\r\n", yaml_get_alias(info));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_anchor(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "AuthorInfo", yaml);
for (int i = 0; i < yaml_anchor_size(yaml); i++)
{
yaml_t t = yaml_get_anchor(yaml, i);
printf("anchor[%d]: %s\r\n", i, yaml_get_alias(t));
}
temp = yaml_map_add_mapping(yaml, "Something", NULL);
yaml_set_anchor(temp, "AuthorInfo", yaml);
temp = yaml_map_add_mapping(yaml, "Extended", NULL);
yaml_insert_reference(temp, NULL, 0, "AuthorInfo", yaml);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_copy(void)
{
yaml_t yaml = NULL, info = NULL, copy = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
printf("SOURCE yaml: \r\n");
yaml_preview(yaml);
printf("COPY yaml: \r\n");
copy = yaml_copy(yaml, YAML_F_RECURSE);
yaml_preview(copy);
yaml_delete(copy);
yaml_delete(copy);
}
static void test_compare(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL, copy = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
copy = yaml_copy(yaml, YAML_F_RECURSE);
printf("yaml_compare %d\r\n", yaml_compare(yaml, copy, YAML_F_RECURSE));
yaml_delete(copy);
yaml_delete(copy);
}
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
static void test_base(void)
{
// test_dump();
test_load();
}
/************************************************************************************/
/************************************* Command ************************************/
/************************************************************************************/
static void usage(void)
{
printf(
"Usage: yaml [opt] [arg] ...\n"
"\n"
"options:\n"
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
" <base> Test base function\n"
" <ut> Unit test\n"
" <create> Test create and delete functions\n"
" <add> Test add category functions\n"
" <insert> Test insert category functions\n"
" <remove> Test remove category functions\n"
" <set> Test set category functions\n"
" <get> Test get category functions\n"
" <key> Test key category functions\n"
" <child> Test child category functions\n"
" <alias> Test alias category functions\n"
" <anchor> Test anchor category functions\n"
" <copy> Test copy function\n"
" <compare> Test compare function\n"
" <dump> Test dump functions\n"
" <load> Test load functions\n"
" -h Print help\n"
" -v Print version\n"
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
"\n"
);
}
static int test(int argc, char *argv[])
{
char *execute = NULL;
int ut_period = 1000;
/* reset getopt */
command_opt_init();
while (1)
{
int opt = command_getopt(argc, argv, "e:hvu::");
if (opt == -1) break;
switch (opt)
{
case 'u' :
if (command_optarg) ut_period = atoi(command_optarg);
break;
case 'e' :
execute = command_optarg;
break;
case 'v' :
printf("yaml version %d.%d.%d\r\n", YAML_V_MAJOR, YAML_V_MINOR, YAML_V_PATCH);
return 0;
case '?':
printf("Unknown option `%c`\r\n", command_optopt);
return -1;
case 'h' :
default:
usage();
return 0;
}
}
if (execute)
{
if (!strcmp(execute, "base"))
{
test_base();
}
else if (!strcmp(execute, "ut"))
{
#if defined(TEST_TARGET_yaml)
while (1)
{
unitt_task();
usleep(1000 * ut_period);
}
#else
printf("create task %d\r\n", task_create(ut_period, unitt_task));
#endif
}
else if (!strcmp(execute, "add"))
{
test_add();
}
else if (!strcmp(execute, "insert"))
{
test_insert();
}
else if (!strcmp(execute, "remove"))
{
test_remove();
}
else if (!strcmp(execute, "set"))
{
test_set();
}
else if (!strcmp(execute, "get"))
{
test_get();
}
else if (!strcmp(execute, "key"))
{
test_key();
}
else if (!strcmp(execute, "child"))
{
test_child();
}
else if (!strcmp(execute, "alias"))
{
test_alias();
}
else if (!strcmp(execute, "anchor"))
{
test_anchor();
}
else if (!strcmp(execute, "copy"))
{
test_copy();
}
else if (!strcmp(execute, "compare"))
{
test_compare();
}
else if (!strcmp(execute, "dump"))
{
test_dump();
}
else if (!strcmp(execute, "load"))
{
test_load();
}
}
else
{
test_base();
}
return 0;
}
/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/
#if defined(TEST_TARGET_yaml)
int main(int argc, char *argv[])
{
return test(argc, argv);
}
#else
void test_yaml(void)
{
command_export("yaml", test);
}
init_export_app(test_yaml);
#endif