├── .github └── workflows │ └── cmake-single-platform.yml ├── LICENSE ├── README.md ├── byte_queue.c ├── byte_queue.h ├── gen_pack.sh └── kk.byte_queue.pdsc /.github/workflows/cmake-single-platform.yml: -------------------------------------------------------------------------------- 1 | name: Build documentation and pack 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ main ] 7 | pull_request: 8 | branches: [ main ] 9 | release: 10 | types: [published] 11 | 12 | jobs: 13 | pack: 14 | name: Generate pack 15 | runs-on: ubuntu-20.04 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v3 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Fetch tags 23 | if: ${{ github.event_name == 'release' }} 24 | run: | 25 | git fetch --tags --force 26 | 27 | - name: Make gen_pack.sh executable 28 | run: chmod +x ./gen_pack.sh 29 | 30 | - name: Generate Pack 31 | uses: Open-CMSIS-Pack/gen-pack-action@main 32 | with: 33 | doxygen-version: 1.9.2 34 | packchk-version: 1.3.96 35 | gen-pack-script: ./gen_pack.sh 36 | gen-pack-output: ./output 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 探索字节队列的魔法:多类型支持、函数重载与线程安全 2 | 3 | 一个C语言编写的支持任意类型的环形队列. 4 | 5 | ## 特性: 6 | 7 | - 基于面向对象,支持多实例 8 | - 支持线程安全 9 | - 支持循环覆盖 10 | 11 | --- 12 | # 一、引言 13 | 在嵌入式系统和实时应用中,数据的传输和处理是至关重要的。字节队列(Byte Queue)是一种重要的数据结构,它能够高效地存储和管理数据流。通过使用字节队列,我们可以灵活地处理不同类型的数据、确保数据的完整性,并在多线程环境中安全地进行操作。本文将深入探讨字节队列的概念、作用及其实现中的多类型支持、函数重载与线程安全机制。 14 | ## 1.1 队列的基本概念 15 | 队列是一种先进先出(FIFO,First In First Out)的数据结构。数据通过“入队”(enqueue)操作添加到队列的尾部,并通过“出队”(dequeue)操作从队列的头部移除。在嵌入式系统中,队列常用于: 16 | 17 | - 数据缓冲:在数据产生和消费速率不匹配的情况下,队列可以暂存数据,平衡输入和输出之间的差异。 18 | - 任务调度:任务或事件的管理可以通过队列来实现,确保它们按照特定顺序被处理。 19 | - 通信:队列可以在不同模块或线程之间传递信息,从而实现模块间的解耦和同步。 20 | ## 1.2 字节队列的不足 21 | 尽管字节队列在嵌入式系统中提供了基本的数据存储与管理能力,但其在实际应用中也存在一些明显的不足: 22 | - 缺乏多类型支持:传统的字节队列往往只能处理单一类型的数据,例如,使用固定的字节数组存储数据,导致不同数据类型之间缺乏灵活性。为了支持不同类型的数据,开发者通常需要创建多个队列,从而增加了代码的复杂性和维护成本。 23 | - 没有函数重载:在C语言中,函数重载是通过不同的函数名称来实现的,缺乏类似C++的灵活性。这使得在队列操作中无法方便地处理不同数量和类型的参数,导致代码冗长且不易维护。 24 | - 线程安全机制不足:在多线程环境中,若多个线程同时访问字节队列而没有适当的同步机制,可能会导致数据损坏或不一致。传统的字节队列实现往往没有内置的线程安全支持,增加了并发编程的难度。 25 | 26 | # 二、字节队列的改进 27 | ## 2.1 多类型支持的实现原理 28 | **问题:** C语言中的数组或缓冲区往往只能存储单一类型的数据。例如,你可以定义一个uint8_t数组来存储字节数据,或者一个int32_t数组来存储整型数据。然而,在嵌入式系统中,我们常常需要处理各种类型的数据——8位、16位、32位的整数、浮点数等等。为了避免为每种类型单独创建队列,我们希望有一个灵活的队列,可以自动支持多种数据类型。 29 | 30 | **解决方案:** 我们使用C语言的宏来解决这个问题。通过宏,队列可以自动根据传入的数据类型来计算所需的存储空间。核心思想是:我们不关心具体的数据类型,而是通过宏和类型推导,计算每个数据需要的字节数,并按照字节的形式将数据存入队列中。 31 | 32 | **使用typeof推断数据类型:** 33 | 34 | C语言的typeof关键字可以根据表达式自动推断出数据类型,并可以通过该类型确定数据的大小。在我们的实现中,队列的操作宏会通过sizeof来获取传入数据的字节大小。 35 | **示例:** 36 | 37 | ```c 38 | #define enqueue(queue, data) enqueue_bytes(queue, &data, sizeof(typeof(data))) 39 | ``` 40 | 在这个宏中: 41 | 42 | - typeof(data) 会推断出data的类型,然后通过sizeof(typeof(data))确定该类型占用的字节数。 43 | - 通过将数据的地址传递给底层的enqueue_bytes函数,我们可以统一将所有类型的数据作为字节流处理。 44 | 45 | 通过这种方式,我们的队列可以支持任意类型的数据,比如8位字节、16位整数、32位浮点数,甚至自定义的数据结构,只要知道它们的大小即可。 46 | 47 | ## 2.2 函数重载的实现原理 48 | **问题:** 在C++等语言中,函数重载允许你定义多个同名的函数,但参数类型或数量不同。然而,C语言并不原生支持函数重载。这意味着如果我们想实现同名函数,处理不同类型或数量的参数,就需要想出另一种方法。 49 | 50 | **解决方案:** 我们可以通过C语言的宏来“模拟”函数重载。宏的灵活性使得我们可以根据不同的参数数量或类型,选择不同的底层函数进行处理。结合__VA_ARGS__等可变参数宏的特性,我们可以轻松实现这种重载行为。 51 | 52 | **使用宏实现参数数量的重载:** 53 | 宏可以根据传递的参数数量,调用不同的函数。我们使用__VA_ARGS__(可变参数)来处理不同数量的参数。 54 | 55 | ```c 56 | #define __CONNECT3(__A, __B, __C) __A##__B##__C 57 | #define __CONNECT2(__A, __B) __A##__B 58 | 59 | #define CONNECT3(__A, __B, __C) __CONNECT3(__A, __B, __C) 60 | #define CONNECT2(__A, __B) __CONNECT2(__A, __B) 61 | #define SAFE_NAME(__NAME) CONNECT3(__,__NAME,__LINE__) 62 | 63 | #define __ENQUEUE_0( __QUEUE, __VALUE) \ 64 | ({typeof((__VALUE)) SAFE_NAME(value) = __VALUE; \ 65 | enqueue_bytes((__QUEUE), &(SAFE_NAME(value)), (sizeof(__VALUE)));}) 66 | 67 | #define __ENQUEUE_1( __QUEUE, __ADDR, __ITEM_COUNT) \ 68 | enqueue_bytes((__QUEUE), (__ADDR), __ITEM_COUNT*(sizeof(typeof((__ADDR[0]))))) 69 | 70 | #define __ENQUEUE_2( __QUEUE, __ADDR, __TYPE, __ITEM_COUNT) \ 71 | enqueue_bytes((__QUEUE), (__ADDR), (__ITEM_COUNT * sizeof(__TYPE))) 72 | 73 | #define enqueue(__queue, __addr,...) \ 74 | CONNECT2(__ENQUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 75 | (__queue,(__addr),##__VA_ARGS__) 76 | ``` 77 | **工作原理:** 78 | - 如果没有参数,调用__ENQUEUE_0;如果传入了一个参数,调用__ENQUEUE_1;如果传入了二个参数,调用__ENQUEUE_2。 79 | 80 | ### 2.2.1 函数重载的秘密 ——“__PLOOC_VA_NUM_ARGS”宏的深度剖析 81 | __PLOOC_VA_NUM_ARGS宏的代码如下: 82 | ```c 83 | #define __PLOOC_VA_NUM_ARGS_IMPL( _0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12, \ 84 | _13,_14,_15,_16,__N,...) __N 85 | #define __PLOOC_VA_NUM_ARGS(...) \ 86 | __PLOOC_VA_NUM_ARGS_IMPL( 0,##__VA_ARGS__,16,15,14,13,12,11,10,9, \ 87 | 8,7,6,5,4,3,2,1,0) 88 | ``` 89 | 90 | 1. __PLOOC_VA_NUM_ARGS宏的作用是它可以告诉我们用户实际传递了多少个参数 91 | 92 | 这里,首先构造了一个特殊的参数宏,__PLOOC_VA_NUM_ARGS_IMPL(): 93 | - 在涉及"..."之前,它要用户至少传递18个参数; 94 | - 这个宏的返回值就是第十八个参数的内容; 95 | - 多出来的部分会被"..."吸收掉,不会产生任何后果 96 | 97 | __PLOOC_VA_NUM_ARGS() 的巧妙在于,它把\_\_VA_ARGS__放在了参数列表的最前面,并随后传递了 "16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0" 这样的序号: 98 | 99 | > 当__VA_ARGS__里有1个参数时,“1”对应第十八个参数__N,所以返回值是1 100 | > 当__VA_ARGS__里有2个参数时,“2”对应第十八个参数__N,所以返回值是2 101 | > ... 102 | > 当__VA_ARGS__里有9个参数时,"9"对应第十八个参数__N,所以返回值是9 103 | 104 | 举个例子: 105 | 106 | ```c 107 | __PLOOC_VA_NUM_ARGS(0x, D, E, A, D) 108 | ``` 109 | 展开为: 110 | 111 | ```c 112 | __PLOOC_VA_NUM_ARGS_IMPL(0,0x, D, E, A, D,16,15,14,13,12,11,10,9, \ 113 | 8,7,6,5,4,3,2,1,0) 114 | ``` 115 | __PLOOC_VA_NUM_ARGS的返回值是5,从左往右数,第十八个参数,正好是“5”。 116 | 117 | 2. 宏连接符##的作用 118 | 119 | ```c 120 | #define __CONNECT2(__A, __B) __A##__B 121 | #define CONNECT2(__A, __B) __CONNECT2(__A, __B) 122 | ``` 123 | 124 | 宏连接符 ## 的主要作用就是连接两个字符串,我们在宏定义中可以使用 ## 来连接两个字符。预处理器在预处理阶段对宏展开时,会将## 两边的字符合并,并删除 ## 这两个字符。 125 | 使用宏连接符 ##要注意一下两条结论: 126 | - 第一条:任何使用到胶水运算“##”对形参进行粘合的参数宏,一定需要额外的再套一层 127 | - 第二条:其余情况下,如果要用到胶水运算,一定要在内部借助参数宏来完成粘合过程 128 | 129 | 为了理解这一“结论”,我们不妨举一个例子:比如定义一个用于自动关闭中断并在完成指定操作后自动恢复原来状态的宏: 130 | 131 | ```c 132 | #define SAFE_ATOM_CODE(...) \ 133 | { \ 134 | uint32_t wTemp = __disable_irq(); \ 135 | __VA_ARGS__; \ 136 | __set_PRIMASK(wTemp); \ 137 | } 138 | ``` 139 | 由于这里定义了一个变量wTemp,而如果用户插入的代码中也使用了同名的变量,就会产生很多问题:轻则编译错误(重复定义);重则出现局部变量wTemp强行取代了用户自定义的静态变量的情况,从而直接导致系统运行出现随机性的故障(比如随机性的中断被关闭后不再恢复,或是原本应该被关闭的全局中断处于打开状态等等)。为了避免这一问题,我们往往会想自动给这个变量一个不会重复的名字,比如借助 `__LINE__` 宏给这一变量加入一个后缀: 140 | 141 | ```c 142 | #define SAFE_ATOM_CODE(...) \ 143 | { \ 144 | uint32_t wTemp##__LINE__ = __disable_irq(); \ 145 | __VA_ARGS__; \ 146 | __set_PRIMASK(wTemp); \ 147 | } 148 | ``` 149 | 假设这里 SAFE_ATOM_CODE 所在行的行号是 123,那么我们期待的代码展开是这个样子的(我重新缩进过了): 150 | 151 | ```c 152 | 153 | ... 154 | { 155 | uint32_t wTemp123 = __disable_irq(); 156 | __VA_ARGS__; 157 | __set_PRIMASK(wTemp); 158 | } 159 | ... 160 | ``` 161 | 然而,实际展开后的内容是这样的: 162 | 163 | ```c 164 | 165 | ... 166 | { 167 | uint32_t wTemp__LINE__ = __disable_irq(); 168 | __VA_ARGS__; 169 | __set_PRIMASK(wTemp); 170 | } 171 | ... 172 | ``` 173 | 这里,`__LINE__`似乎并没有被正确替换为123,而是以原样的形式与wTemp粘贴到了一起——这就是很多人经常抱怨的 `__LINE__` 宏不稳定的问题。实际上,这是因为上述宏的构建没有遵守前面所列举的两条结论导致的。 174 | 175 | 从内容上看,SAFE_ATOM_CODE() 要粘合的对象并不是形参,根据结论第二条,需要借助另外一个参数宏来帮忙完成这一过程。为此,我们需要引入一个专门的宏: 176 | 177 | ```c 178 | 179 | #define CONNECT2(__A, __B) __A##__B 180 | ``` 181 | 注意到,这个参数宏要对形参进行胶水运算,根据结论第一条,需要在宏的外面再套一层,因此,修改代码得到: 182 | 183 | ```c 184 | 185 | #define __CONNECT2(__A, __B) __A##__B 186 | #define CONNECT2(__A, __B) __CONNECT2(__A, __B) 187 | ``` 188 | 修改前面的定义得到: 189 | 190 | ```c 191 | 192 | #define SAFE_ATOM_CODE(...) \ 193 | { \ 194 | uint32_t CONNECT2(wTemp,__LINE__) = \ 195 | __disable_irq(); \ 196 | __VA_ARGS__; \ 197 | __set_PRIMASK(wTemp); \ 198 | } 199 | ``` 200 | 201 | 3. 再回头看对enqueue的封装,是不是很巧妙 202 | 203 | ```c 204 | #define __CONNECT3(__A, __B, __C) __A##__B##__C 205 | #define __CONNECT2(__A, __B) __A##__B 206 | 207 | #define CONNECT3(__A, __B, __C) __CONNECT3(__A, __B, __C) 208 | #define CONNECT2(__A, __B) __CONNECT2(__A, __B) 209 | #define SAFE_NAME(__NAME) CONNECT3(__,__NAME,__LINE__) 210 | 211 | #define __ENQUEUE_0( __QUEUE, __VALUE) \ 212 | ({typeof((__VALUE)) SAFE_NAME(value) = __VALUE; \ 213 | enqueue_bytes((__QUEUE), &(SAFE_NAME(value)), (sizeof(__VALUE)));}) 214 | 215 | #define __ENQUEUE_1( __QUEUE, __ADDR, __ITEM_COUNT) \ 216 | enqueue_bytes((__QUEUE), (__ADDR), __ITEM_COUNT*(sizeof(typeof((__ADDR[0]))))) 217 | 218 | #define __ENQUEUE_2( __QUEUE, __ADDR, __TYPE, __ITEM_COUNT) \ 219 | enqueue_bytes((__QUEUE), (__ADDR), (__ITEM_COUNT * sizeof(__TYPE))) 220 | 221 | #define enqueue(__queue, __addr,...) \ 222 | CONNECT2(__ENQUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 223 | (__queue,(__addr),##__VA_ARGS__) 224 | ``` 225 | 对enqueue展开后: 226 | ```c 227 | static byte_queue_t my_queue; 228 | uint8_t data1 = 0XAA; 229 | enqueue(&my_queue,data1);//__ENQUEUE_0(&my_queue,data1) 230 | enqueue(&my_queue,&data1,1);//__ENQUEUE_1(&my_queue,&data1,1) 231 | enqueue(&my_queue,&data1,uint8_t ,1);//__ENQUEUE_2(&my_queue,&data1,uint8_t ,1) 232 | ``` 233 | 234 | ## 2.3 线程安全的实现原理 235 | **问题:** 在多线程环境下,如果多个线程同时对同一个队列进行操作,可能会引发数据竞争问题,导致数据损坏或不一致。为了避免这种情况,我们需要保证每次对队列的操作是原子的,即不可打断的。 236 | 237 | **解决方案:** 在嵌入式系统中,常用的方法是通过禁用中断或使用锁机制来保证数据的一致性。在我们的实现中,我们使用禁用中断的方式来确保线程安全。这是一种非常常见的技术,尤其是在实时系统中。 238 | 239 | 为了尽量降低关中断对实时性的影响,我们只对操作队列指针的操作进行关中断保护,相对耗时间的数据拷贝不进行关中断。 240 | 函数伪代码如下: 241 | ```c 242 | bool enqueue(...) 243 | { 244 | bool bEarlyReturn = false; 245 | safe_atom_code(){ 246 | if (!this.bMutex){ 247 | this.bMutex= true; 248 | } else { 249 | bEarlyReturn = true; 250 | } 251 | } 252 | if (bEarlyReturn){ 253 | return false; 254 | } 255 | safe_atom_code(){ 256 | /*队列指针操作 */ 257 | ... 258 | } 259 | /* 数据操作*/ 260 | memcpy(...); 261 | ... 262 | this.bMutex = false; 263 | return true; 264 | } 265 | ``` 266 | 267 | 268 | **原子宏safe_atom_code()的实现:** 269 | 前边的例子中,我们实现了一个SAFE_ATOM_CODE的原子宏,唯一的问题是,这样的写法,在调试时完全没法在用户代码处添加断点(编译器会认为宏内所有的内容都写在了同一行),这是大多数人不喜欢使用宏来封装代码结构的最大原因。 270 | 接下来我们用另一种实现方式来解决这个问题,代码如下: 271 | ```c 272 | 273 | #define __CONNECT3(__A, __B, __C) __A##__B##__C 274 | #define __CONNECT2(__A, __B) __A##__B 275 | #define CONNECT3(__A, __B, __C) __CONNECT3(__A, __B, __C) 276 | #define CONNECT2(__A, __B) __CONNECT2(__A, __B) 277 | #define SAFE_NAME(__NAME) CONNECT3(__,__NAME,__LINE__) 278 | #include "cmsis_compiler.h" 279 | #define safe_atom_code() \ 280 | for( uint32_t SAFE_NAME(temp) = \ 281 | ({uint32_t SAFE_NAME(temp2)=__get_PRIMASK(); \ 282 | __disable_irq(); \ 283 | SAFE_NAME(temp2);}),*SAFE_NAME(temp3) = NULL; \ 284 | SAFE_NAME(temp3)++ == NULL; \ 285 | __set_PRIMASK(SAFE_NAME(temp))) 286 | #endif 287 | ``` 288 | **工作原理:** 289 | safe_atom_code()通过一个循环结构确保在队列操作期间,中断被禁用。循环结束后自动恢复中断。 290 | ### 2.3.1 for循环的妙用 291 | 首先构造一个只执行一次的for循环结构: 292 | 293 | ```c 294 | for (int i = 1; i > 0; i--) { 295 | ... 296 | } 297 | ``` 298 | 对于这样的for循环结构,几个关键部分就有了新的意义: 299 | - 在执行用户代码之前(灰色部分),有能力进行一定的“准备工作”(Before部分); 300 | 301 | - 在执行用户代码之后,有能力执行一定的“收尾工作”(After部分) 302 | 303 | - 在init_clause阶段有能力定义一个“仅仅只覆盖” for 循环的,并且只对 User Code可见的局部变量——换句话说,这些局部变量是不会污染 for 循环以外的地方的。 304 | 305 | 306 | 307 | ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c055f999602d43c3a2426fbcfe0a651c.jpeg) 308 | 309 | 利用这样的结构,我们很容易就能构造出一个可以通过花括号的形式来包裹用户代码的原子操作safe_atom_code(),在执行用户代码之前关闭中断,在执行完用户代码之后打开中断,还不影响在用户代码中添加断点,单步执行。 310 | 311 | **需要注意的是,如果需要中途退出循环,需要使用`continue`退出原子操作,不能使用`break`。** 312 | ## 2.4 总结 313 | 通过上述的多类型支持、函数重载和线程安全的实现,我们大大增强了字节队列的灵活性和实用性: 314 | 315 | 1. **多类型支持:** 自动推断数据类型和大小,支持不同类型数据的队列操作。 316 | 2. **函数重载:** 通过宏模拟C语言的函数重载,灵活处理不同数量和类型的参数。 317 | 3. **线程安全:** 通过禁用中断机制确保队列操作在多线程环境中的原子性,避免数据竞争问题。 318 | 319 | 这些改进使得我们的字节队列不仅可以在单线程环境中高效运行,还能在复杂的多线程系统中保持数据的一致性与安全性。 320 | # 三、API 接口 321 | 322 | ```c 323 | #define queue_init(__queue,__buffer,__size ,... ) \ 324 | __PLOOC_EVAL(__QUEUE_INIT_,##__VA_ARGS__) \ 325 | (__queue,(__buffer),(__size),##__VA_ARGS__) 326 | 327 | #define dequeue(__queue,__addr,...) \ 328 | __PLOOC_EVAL(__DEQUEUE_,##__VA_ARGS__) \ 329 | (__queue,(__addr),##__VA_ARGS__) 330 | 331 | #define enqueue(__queue, __addr,...) \ 332 | __PLOOC_EVAL(__ENQUEUE_,##__VA_ARGS__) \ 333 | (__queue,(__addr),##__VA_ARGS__) 334 | 335 | #define peek_queue(__queue, __addr,...) \ 336 | __PLOOC_EVAL(__PEEK_QUEUE_,##__VA_ARGS__) \ 337 | (__queue,(__addr),##__VA_ARGS__) 338 | 339 | extern 340 | byte_queue_t * queue_init_byte(byte_queue_t *ptObj, void *pBuffer, uint16_t hwItemSize,bool bIsCover); 341 | 342 | extern 343 | bool reset_queue(byte_queue_t *ptObj); 344 | 345 | extern 346 | uint16_t enqueue_bytes(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength); 347 | 348 | extern 349 | uint16_t dequeue_bytes(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength); 350 | 351 | extern 352 | bool is_queue_empty(byte_queue_t *ptQueue); 353 | 354 | extern 355 | bool is_peek_empty(byte_queue_t *ptObj); 356 | 357 | extern 358 | uint16_t peek_bytes_queue(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength); 359 | 360 | extern 361 | void reset_peek(byte_queue_t *ptQueue); 362 | 363 | extern 364 | void get_all_peeked(byte_queue_t *ptQueue); 365 | 366 | extern 367 | uint16_t get_peek_status(byte_queue_t *ptQueue); 368 | 369 | extern 370 | void restore_peek_status(byte_queue_t *ptQueue, uint16_t hwCount); 371 | 372 | extern 373 | uint16_t get_queue_count(byte_queue_t *ptObj); 374 | 375 | extern 376 | uint16_t get_queue_available_count(byte_queue_t *ptObj); 377 | 378 | ``` 379 | 380 | # 四、API 说明 381 | 382 | 383 | ## 1. 初始化队列 384 | 385 | ```c 386 | queue_init(__queue,__buffer,__size ,... ) 387 | ``` 388 | 参数说明: 389 | | 参数名 | 描述 | 390 | | ------------- | ---------------- | 391 | | __QUEUE | 队列的地址 | 392 | | __BUFFER | 队列缓存的首地址 | 393 | | __BUFFER_SIZE | 队列长度 | 394 | | 可变参数 | 是否覆盖,默认否 | 395 | 396 | 初始化队列之前首先需要通过byte_queue_t 结构体定义一个队列对象,和缓冲区的buf。 397 | 参考代码: 398 | 399 | ```c 400 | uint8_t s_cFIFOinBuffer[1024]; 401 | static byte_queue_t s_tFIFOin; 402 | queue_init(&s_tFIFOin, s_cFIFOinBuffer, sizeof(s_cFIFOinBuffer)); 403 | ``` 404 | 405 | ## 2. 入队 406 | 407 | ```c 408 | #define enqueue(__queue, __addr,...) 409 | ``` 410 | 参数说明: 411 | | 参数名 | 描述 | 412 | | ------- | ------------------------------------------------------------ | 413 | | __QUEUE | 队列的地址 | 414 | | __ADDR | 待入队的数据或者数据的地址 | 415 | | ... | 可变参数,需要入队的数据个数,或者数据类型和个数,如果为空,则只入队一个数据 | 416 | 417 | 参考代码: 418 | 419 | ```c 420 | typedef struct data_t{ 421 | uint32_t a; 422 | uint32_t b; 423 | uint32_t c; 424 | }data_t; 425 | 426 | uint8_t data1 = 0XAA; 427 | uint16_t data2 = 0X55AA; 428 | uint32_t data3 = 0X55AAAA55; 429 | uint16_t data4[] = {0x1234,0x5678}; 430 | 431 | data_t data5 = { 432 | .a = 0X11223344, 433 | .b = 0X55667788, 434 | .c = 0X99AABBCC, 435 | }; 436 | // 根据变量的类型,自动计算对象的大小 437 | enqueue(&s_tFIFOin,data1); 438 | enqueue(&s_tFIFOin,data2); 439 | enqueue(&s_tFIFOin,data3); 440 | // 一下三种方式都可以正确存储数组 441 | enqueue(&s_tFIFOin,data4,2);//可以不指名数据类型 442 | enqueue(&s_tFIFOin,data4,uint16_t,2);//也可以指名数据类型 443 | enqueue(&s_tFIFOin,data4,uint8_t,sizeof(data4));//或者用字节类型 444 | 445 | //一下两种方式都可以正确存储结构体类型 446 | enqueue(&s_tFIFOin,data5);//根据结构体的类型,自动计算结构体的大小 447 | enqueue(&s_tFIFOin,&data5,uint8_t,sizeof(data5));//也可以以数组方式存储 448 | 449 | enqueue(&s_tFIFOin,(uint8_t)0X11); //常量默认为int型,需要强制转换数据类型 450 | enqueue(&s_tFIFOin,(uint16_t)0X2233); //常量默认为int型,需要强制转换数据类型 451 | enqueue(&s_tFIFOin,0X44556677); 452 | enqueue(&s_tFIFOin,(char)'a');//单个字符也需要强制转换数据类型 453 | enqueue(&s_tFIFOin,"bc");//字符串默认会存储空字符\0 454 | enqueue(&s_tFIFOin,"def");//字符串默认会存储空字符\0 455 | 456 | ``` 457 | 458 | ## 3. 出队 459 | ```c 460 | #define dequeue(__queue,__addr,...) 461 | ``` 462 | 参数说明: 463 | | 参数名 | 描述 | 464 | | ------- | ------------------------------------------------------------ | 465 | | __QUEUE | 队列的地址 | 466 | | __ADDR | 用于保存出队数据变量的地址 | 467 | | ... | 可变参数,需要出队的数据个数,或者数据类型和个数,如果为空,则只出队一个数据 | 468 | 469 | 参考代码: 470 | 471 | ```c 472 | uint8_t data[100]; 473 | uint16_t data1; 474 | uint32_t data2; 475 | // 读出全部数据 476 | dequeue(&s_tFIFOin,data,GET_QUEUE_COUNT(&my_queue)); 477 | dequeue(&s_tFIFOin,data,uint8_t,GET_QUEUE_COUNT(&my_queue)) 478 | // 读一个数据,长度为uint16_t类型 479 | dequeue(&s_tFIFOin,&data1); 480 | // 读一个数据,长度为uint32_t类型 481 | dequeue(&s_tFIFOin,&data2); 482 | ``` 483 | 484 | ## 4. 查看 485 | ```c 486 | #define peek_queue(__queue, __addr,...) 487 | ``` 488 | 参数说明: 489 | | 参数名 | 描述 | 490 | | ------- | ------------------------------------------------------------ | 491 | | __QUEUE | 队列的地址 | 492 | | __ADDR | 用于保存查看数据变量的地址 | 493 | | ... | 可变参数,数据类型和需要查看的数据个数,如果为空,则只查看一个数据 | 494 | 495 | 参考代码: 496 | 497 | ```c 498 | uint8_t data[32]; 499 | uint16_t data1; 500 | uint32_t data2; 501 | // 查看多个数据 502 | peek_queue(&s_tFIFOin,&data,sizeof(data)); 503 | // 查看一个数据,长度为uint16_t类型 504 | peek_queue(&s_tFIFOin,&data1); 505 | // 查看一个数据,长度为uint32_t类型 506 | peek_queue(&s_tFIFOin,&data2); 507 | ``` 508 | # 五、快速使用 509 | 代码开源地址:[https://github.com/Aladdin-Wang/wl_queue](https://github.com/Aladdin-Wang/wl_queue) 510 | ```c 511 | #include "ring_queue.h" 512 | 513 | uint8_t data1 = 0XAA; 514 | uint16_t data2 = 0X55AA; 515 | uint32_t data3 = 0X55AAAA55; 516 | uint16_t data4[] = {0x1234,0x5678}; 517 | typedef struct data_t{ 518 | uint32_t a; 519 | uint32_t b; 520 | uint32_t c; 521 | }data_t; 522 | data_t data5 = { 523 | .a = 0X11223344, 524 | .b = 0X55667788, 525 | .c = 0X99AABBCC, 526 | }; 527 | 528 | uint8_t data[100]; 529 | static uint8_t s_hwQueueBuffer[100]; 530 | static byte_queue_t my_queue; 531 | 532 | queue_init(&my_queue,s_hwQueueBuffer,sizeof(s_hwQueueBuffer)); 533 | // 根据变量的类型,自动计算对象的大小 534 | enqueue(&my_queue,data1); 535 | enqueue(&my_queue,data2); 536 | enqueue(&my_queue,data3); 537 | 538 | // 一下三种方式都可以正确存储数组 539 | enqueue(&my_queue,data4,2);//可以不指名数据类型 540 | enqueue(&my_queue,data4,uint16_t,2);//也可以指名数据类型 541 | enqueue(&my_queue,data4,uint8_t,sizeof(data4));//或者用其他类型 542 | 543 | //一下两种方式都可以正确存储结构体类型 544 | enqueue(&my_queue,data5);//根据结构体的类型,自动计算对象的大小 545 | enqueue(&my_queue,&data5,uint8_t,sizeof(data5));//也可以以数组方式存储 546 | 547 | enqueue(&my_queue,(uint8_t)0X11); //常量默认为int型,需要强制转换数据类型 548 | enqueue(&my_queue,(uint16_t)0X2233); //常量默认为int型,需要强制转换数据类型 549 | enqueue(&my_queue,0X44556677); 550 | enqueue(&my_queue,(char)'a');//单个字符也需要强制转换数据类型 551 | enqueue(&my_queue,"bc");//字符串默认会存储空字符\0 552 | enqueue(&my_queue,"def"); 553 | 554 | // 读出全部数据 555 | dequeue(&my_queue,data,get_queue_count(&my_queue)); 556 | 557 | ``` 558 | -------------------------------------------------------------------------------- /byte_queue.c: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright 2022 KK (https://github.com/WALI-KANG) * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | * * 16 | ****************************************************************************/ 17 | #include "byte_queue.h" 18 | #undef this 19 | #define this (*ptThis) 20 | 21 | /**************************************************************************** 22 | * Function: queue_init_byte * 23 | * Description: Initializes a byte queue object. * 24 | * Parameters: * 25 | * - ptObj: Pointer to the byte_queue_t object to be initialized. * 26 | * - pBuffer: Pointer to the buffer for storing data. * 27 | * - hwItemSize: Size of each item in the buffer. * 28 | * - bIsCover: Indicates whether the queue should overwrite when full. * 29 | * Returns: Pointer to the initialized byte_queue_t object or NULL. * 30 | ****************************************************************************/ 31 | byte_queue_t *queue_init_byte(byte_queue_t *ptObj, void *pBuffer, uint16_t hwItemSize, bool bIsCover) 32 | { 33 | assert(NULL != ptObj); 34 | /* initialise "this" (i.e. ptThis) to access class members */ 35 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 36 | 37 | if (pBuffer == NULL || hwItemSize == 0) { 38 | return NULL; 39 | } 40 | 41 | safe_atom_code() { 42 | this.pchBuffer = pBuffer; 43 | this.hwSize = hwItemSize; 44 | this.hwHead = 0; 45 | this.hwTail = 0; 46 | this.hwLength = 0; 47 | this.hwPeek = this.hwHead; 48 | this.hwPeekLength = 0; 49 | this.bIsCover = bIsCover; 50 | } 51 | return ptObj; 52 | } 53 | 54 | /**************************************************************************** 55 | * Function: reset_queue * 56 | * Description: Resets the byte queue to its initial state. * 57 | * Parameters: * 58 | * - ptObj: Pointer to the byte_queue_t object to be reset. * 59 | * Returns: True if the reset is successful, false otherwise. * 60 | ****************************************************************************/ 61 | bool reset_queue(byte_queue_t *ptObj) 62 | { 63 | assert(NULL != ptObj); 64 | /* initialise "this" (i.e. ptThis) to access class members */ 65 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 66 | safe_atom_code() { 67 | this.hwHead = 0; 68 | this.hwTail = 0; 69 | this.hwLength = 0; 70 | this.hwPeek = this.hwHead; 71 | this.hwPeekLength = 0; 72 | } 73 | return true; 74 | } 75 | 76 | 77 | 78 | /**************************************************************************** 79 | * Function: enqueue_bytes * 80 | * Description: Enqueues multiple bytes into the byte queue. * 81 | * Parameters: * 82 | * - ptObj: Pointer to the byte_queue_t object. * 83 | * - pDate: Pointer to the data to be enqueued. * 84 | * - hwLength: Number of bytes to enqueue. * 85 | * Returns: Number of bytes actually enqueued. * 86 | ****************************************************************************/ 87 | 88 | uint16_t enqueue_bytes(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength) 89 | { 90 | assert(NULL != ptObj); // Ensure ptObj is not NULL 91 | assert(NULL != pDate); // Ensure pDate is not NULL 92 | /* initialise "this" (i.e. ptThis) to access class members */ 93 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 94 | bool bEarlyReturn = false; // Initialize early return flag 95 | safe_atom_code() { // Start atomic section for thread safety 96 | if(this.hwHead == this.hwTail && 0 != this.hwLength) { // Check if queue is full 97 | if(this.bIsCover == false) { // If not allowed to overwrite 98 | bEarlyReturn = true; 99 | continue; // Exit atomic block 100 | } 101 | } 102 | if(!this.bMutex) { // Check if mutex is free 103 | this.bMutex = true; // Lock the queue for thread safety 104 | } else { 105 | bEarlyReturn = true; // Another thread is modifying the queue 106 | } 107 | } 108 | if(bEarlyReturn) { 109 | return 0; // Return 0 if queue is full or accessed by another thread 110 | } 111 | uint8_t *pchByte = pDate; // Cast data pointer to byte pointer 112 | uint16_t hwTail = this.hwTail; // Store current tail index 113 | safe_atom_code() { // Start atomic section for thread safety 114 | if(hwDataLength > this.hwSize) { // If data length exceeds queue size 115 | hwDataLength = this.hwSize; // Limit data length to queue size 116 | } 117 | if(hwDataLength > (this.hwSize - this.hwLength)) { // If not enough space 118 | if(this.bIsCover == false) { // If not allowed to overwrite 119 | hwDataLength = this.hwSize - this.hwLength; // Adjust data length 120 | } else { // If overwriting is allowed 121 | uint16_t hwOverLength = hwDataLength - (this.hwSize - this.hwLength); // Calculate overwrite length 122 | if(hwOverLength < (this.hwSize - this.hwHead)) { 123 | this.hwHead += hwOverLength; // Move head forward 124 | } else { 125 | this.hwHead = hwDataLength - (this.hwSize - this.hwHead); // Wrap around 126 | } 127 | this.hwLength -= hwOverLength; // Decrease length 128 | this.hwPeek = this.hwHead; // Update peek index 129 | this.hwPeekLength = this.hwLength; // Update peek length 130 | } 131 | } 132 | if(hwDataLength < (this.hwSize - this.hwTail)) { 133 | this.hwTail += hwDataLength; // Move tail forward 134 | } else { 135 | this.hwTail = hwDataLength - (this.hwSize - this.hwTail); // Wrap around 136 | } 137 | this.hwLength += hwDataLength; // Increase queue length 138 | this.hwPeekLength += hwDataLength; // Increase peek length 139 | } 140 | if(hwDataLength <= (this.hwSize - hwTail)) { 141 | memcpy(&this.pchBuffer[hwTail], pchByte, hwDataLength); // Copy data to buffer 142 | } else { 143 | memcpy(&this.pchBuffer[hwTail], &pchByte[0], this.hwSize - hwTail); // Copy first part 144 | memcpy(&this.pchBuffer[0], &pchByte[this.hwSize - hwTail], hwDataLength - (this.hwSize - hwTail)); // Copy second part 145 | } 146 | this.bMutex = false; // Unlock the queue 147 | return hwDataLength; // Return number of bytes enqueued 148 | } 149 | 150 | 151 | /**************************************************************************** 152 | * Function: dequeue_bytes * 153 | * Description: Dequeues multiple bytes from the byte queue. * 154 | * Parameters: * 155 | * - ptObj: Pointer to the byte_queue_t object. * 156 | * - pDate: Pointer to store the dequeued data. * 157 | * - hwLength: Number of bytes to dequeue. * 158 | * Returns: Number of bytes actually dequeued. * 159 | ****************************************************************************/ 160 | 161 | uint16_t dequeue_bytes(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength) 162 | { 163 | assert(NULL != ptObj); // Ensure ptObj is not NULL 164 | assert(NULL != pDate); // Ensure pDate is not NULL 165 | 166 | /* initialise "this" (i.e. ptThis) to access class members */ 167 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 168 | bool bEarlyReturn = false; // Initialize early return flag 169 | safe_atom_code() { // Start atomic section for thread safety 170 | if(this.hwHead == this.hwTail && 0 == this.hwLength) { // Check if queue is empty 171 | bEarlyReturn = true; // Set early return flag 172 | continue; // Exit atomic block 173 | } 174 | if(!this.bMutex) { // Check if mutex is free 175 | this.bMutex = true; // Lock the queue for thread safety 176 | } else { 177 | bEarlyReturn = true; // Another thread is modifying the queue 178 | } 179 | } 180 | if(bEarlyReturn) { 181 | return 0; // Return 0 if queue is empty or accessed by another thread 182 | } 183 | uint8_t *pchByte = pDate; // Cast data pointer to byte pointer 184 | uint16_t hwHead = this.hwHead; // Store current head index 185 | safe_atom_code() { // Start atomic section for thread safety 186 | if(hwDataLength > this.hwLength) { // If requested length exceeds available data 187 | hwDataLength = this.hwLength; // Adjust data length 188 | } 189 | if(hwDataLength < (this.hwSize - this.hwHead)) { 190 | this.hwHead += hwDataLength; // Move head forward 191 | } else { 192 | this.hwHead = hwDataLength - (this.hwSize - this.hwHead); // Wrap around 193 | } 194 | this.hwLength -= hwDataLength; // Decrease queue length 195 | this.hwPeek = this.hwHead; // Update peek index 196 | this.hwPeekLength = this.hwLength; // Update peek length 197 | } 198 | if(hwDataLength <= (this.hwSize - hwHead)) { 199 | memcpy(pchByte, &this.pchBuffer[hwHead], hwDataLength); // Copy data from buffer 200 | } else { 201 | memcpy(&pchByte[0], &this.pchBuffer[hwHead], this.hwSize - hwHead); // Copy first part 202 | memcpy(&pchByte[this.hwSize - hwHead], &this.pchBuffer[0], hwDataLength - (this.hwSize - hwHead)); // Copy second part 203 | } 204 | this.bMutex = false; // Unlock the queue 205 | return hwDataLength; // Return number of bytes dequeued 206 | } 207 | 208 | /**************************************************************************** 209 | * Function: is_queue_empty * 210 | * Description: Checks if the byte queue is empty. * 211 | * Parameters: * 212 | * - ptObj: Pointer to the byte_queue_t object. * 213 | * Returns: True if the queue is empty, false otherwise. * 214 | ****************************************************************************/ 215 | 216 | bool is_queue_empty(byte_queue_t *ptObj) 217 | { 218 | assert(NULL != ptObj); 219 | /* initialise "this" (i.e. ptThis) to access class members */ 220 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 221 | 222 | if (this.hwHead == this.hwTail && 223 | 0 == this.hwLength ) { 224 | return true; 225 | } 226 | 227 | return false; 228 | } 229 | 230 | /**************************************************************************** 231 | * Function: get_queue_count * 232 | * Description: Gets the current number of elements in the byte queue. * 233 | * Parameters: * 234 | * - ptObj: Pointer to the byte_queue_t object. * 235 | * Returns: Number of elements in the queue. * 236 | ****************************************************************************/ 237 | 238 | uint16_t get_queue_count(byte_queue_t *ptObj) 239 | { 240 | assert(NULL != ptObj); 241 | /* initialise "this" (i.e. ptThis) to access class members */ 242 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 243 | return (this.hwLength); 244 | } 245 | /**************************************************************************** 246 | * Function: get_queue_available_count * 247 | * Description: Gets the available space in the byte queue. * 248 | * Parameters: * 249 | * - ptObj: Pointer to the byte_queue_t object. * 250 | * Returns: Available space in the queue. * 251 | ****************************************************************************/ 252 | 253 | uint16_t get_queue_available_count(byte_queue_t *ptObj) 254 | { 255 | assert(NULL != ptObj); 256 | /* initialise "this" (i.e. ptThis) to access class members */ 257 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 258 | return (this.hwSize - this.hwLength); 259 | } 260 | 261 | /**************************************************************************** 262 | * Function: is_peek_empty * 263 | * Description: Checks if the peek buffer is empty. * 264 | * Parameters: * 265 | * - ptObj: Pointer to the byte_queue_t object. * 266 | * Returns: True if the peek buffer is empty, false otherwise. * 267 | ****************************************************************************/ 268 | 269 | bool is_peek_empty(byte_queue_t *ptObj) 270 | { 271 | assert(NULL != ptObj); 272 | /* initialise "this" (i.e. ptThis) to access class members */ 273 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 274 | 275 | if (this.hwPeek == this.hwTail && 276 | 0 == this.hwPeekLength ) { 277 | return true; 278 | } 279 | 280 | return false; 281 | } 282 | 283 | 284 | /**************************************************************************** 285 | * Function: peek_bytes_queue * 286 | * Description: Peeks multiple bytes from the byte queue without dequeuing.* 287 | * Parameters: * 288 | * - ptObj: Pointer to the byte_queue_t object. * 289 | * - pDate: Pointer to store the peeked data. * 290 | * - hwLength: Number of bytes to peek. * 291 | * Returns: Number of bytes actually peeked. * 292 | ****************************************************************************/ 293 | 294 | uint16_t peek_bytes_queue(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength) 295 | { 296 | assert(NULL != ptObj); // Ensure ptObj is not NULL 297 | assert(NULL != pDate); // Ensure pDate is not NULL 298 | 299 | /* initialise "this" (i.e. ptThis) to access class members */ 300 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 301 | 302 | bool bEarlyReturn = false; // Initialize early return flag 303 | safe_atom_code() { // Start atomic section for thread safety 304 | if(this.hwPeek == this.hwTail && 0 == this.hwPeekLength) { // Check if peek buffer is empty 305 | bEarlyReturn = true; // Set early return flag 306 | continue; // Exit atomic block 307 | } 308 | if(!this.bMutex) { // Check if mutex is free 309 | this.bMutex = true; // Lock the queue for thread safety 310 | } else { 311 | bEarlyReturn = true; // Another thread is modifying the queue 312 | } 313 | } 314 | if(bEarlyReturn) { 315 | return 0; // Return 0 if peek buffer is empty or accessed by another thread 316 | } 317 | uint8_t *pchByte = pDate; // Cast data pointer to byte pointer 318 | uint16_t hwPeek = this.hwPeek; // Store current peek index 319 | safe_atom_code() { // Start atomic section for thread safety 320 | if(hwDataLength > this.hwPeekLength) { // If requested length exceeds available data 321 | hwDataLength = this.hwPeekLength; // Adjust data length 322 | } 323 | if(hwDataLength < (this.hwSize - this.hwPeek)) { 324 | this.hwPeek += hwDataLength; // Move peek index forward 325 | } else { 326 | this.hwPeek = hwDataLength - (this.hwSize - this.hwPeek); // Wrap around 327 | } 328 | this.hwPeekLength -= hwDataLength; // Decrease peek length 329 | } 330 | if(hwDataLength <= (this.hwSize - hwPeek)) { 331 | memcpy(pchByte, &this.pchBuffer[hwPeek], hwDataLength); // Copy data from buffer 332 | } else { 333 | memcpy(&pchByte[0], &this.pchBuffer[hwPeek], this.hwSize - hwPeek); // Copy first part 334 | memcpy(&pchByte[this.hwSize - hwPeek], &this.pchBuffer[0], hwDataLength - (this.hwSize - hwPeek)); // Copy second part 335 | } 336 | this.bMutex = false; // Unlock the queue 337 | return hwDataLength; // Return number of bytes peeked 338 | } 339 | 340 | /**************************************************************************** 341 | * Function: reset_peek * 342 | * Description: Resets the peek buffer to its initial state. * 343 | * Parameters: * 344 | * - ptObj: Pointer to the byte_queue_t object. * 345 | * Returns: True if the reset is successful, false otherwise. * 346 | ****************************************************************************/ 347 | 348 | bool reset_peek(byte_queue_t *ptObj) 349 | { 350 | assert(NULL != ptObj); 351 | /* initialise "this" (i.e. ptThis) to access class members */ 352 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 353 | safe_atom_code() { 354 | this.hwPeek = this.hwHead; 355 | this.hwPeekLength = this.hwLength; 356 | } 357 | return true; 358 | } 359 | 360 | /**************************************************************************** 361 | * Function: get_all_peeked * 362 | * Description: Moves all peeked elements back to the queue. * 363 | * Parameters: * 364 | * - ptObj: Pointer to the byte_queue_t object. * 365 | * Returns: True if successful, false otherwise. * 366 | ****************************************************************************/ 367 | 368 | bool get_all_peeked(byte_queue_t *ptObj) 369 | { 370 | assert(NULL != ptObj); 371 | /* initialise "this" (i.e. ptThis) to access class members */ 372 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 373 | safe_atom_code() { 374 | this.hwHead = this.hwPeek; 375 | this.hwLength = this.hwPeekLength; 376 | } 377 | return true; 378 | } 379 | 380 | /**************************************************************************** 381 | * Function: get_peek_status * 382 | * Description: Gets the current status of the peek buffer. * 383 | * Parameters: * 384 | * - ptObj: Pointer to the byte_queue_t object. * 385 | * Returns: Current number of elements in the peek buffer. * 386 | ****************************************************************************/ 387 | 388 | uint16_t get_peek_status(byte_queue_t *ptObj) 389 | { 390 | assert(NULL != ptObj); 391 | /* initialise "this" (i.e. ptThis) to access class members */ 392 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 393 | uint16_t hwCount; 394 | safe_atom_code() { 395 | if (this.hwPeek >= this.hwHead) { 396 | hwCount = this.hwPeek - this.hwHead; 397 | } else { 398 | hwCount = this.hwSize - this.hwHead + this.hwPeek; 399 | } 400 | } 401 | return hwCount; 402 | } 403 | 404 | /**************************************************************************** 405 | * Function: restore_peek_status * 406 | * Description: Restores the peek buffer status to a previous count. * 407 | * Parameters: * 408 | * - ptObj: Pointer to the byte_queue_t object. * 409 | * - hwCount: Number of elements to restore in the peek buffer. * 410 | * Returns: True if successful, false otherwise. * 411 | ****************************************************************************/ 412 | 413 | bool restore_peek_status(byte_queue_t *ptObj, uint16_t hwCount) 414 | { 415 | assert(NULL != ptObj); 416 | /* initialise "this" (i.e. ptThis) to access class members */ 417 | byte_queue_t *ptThis = (byte_queue_t *)ptObj; 418 | safe_atom_code() { 419 | if (this.hwHead + hwCount < this.hwSize) { 420 | this.hwPeek = this.hwHead + hwCount; 421 | } else { 422 | this.hwPeek = hwCount - (this.hwSize - this.hwHead); 423 | } 424 | 425 | this.hwPeekLength = this.hwPeekLength - hwCount; 426 | } 427 | return true; 428 | } 429 | -------------------------------------------------------------------------------- /byte_queue.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright 2022 KK (https://github.com/Aladdin-Wang) * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | * * 16 | ****************************************************************************/ 17 | 18 | #ifndef QUEUE_QUEUE_H_ 19 | #define QUEUE_QUEUE_H_ 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #undef __CONNECT2 26 | #undef CONNECT2 27 | #undef __CONNECT3 28 | #undef CONNECT3 29 | 30 | #define __CONNECT3(__A, __B, __C) __A##__B##__C 31 | #define __CONNECT2(__A, __B) __A##__B 32 | 33 | #define CONNECT3(__A, __B, __C) __CONNECT3(__A, __B, __C) 34 | #define CONNECT2(__A, __B) __CONNECT2(__A, __B) 35 | 36 | #ifndef SAFE_NAME 37 | #define SAFE_NAME(__NAME) CONNECT3(__,__NAME,__LINE__) 38 | #endif 39 | 40 | #ifndef __PLOOC_VA_NUM_ARGS_IMPL 41 | # define __PLOOC_VA_NUM_ARGS_IMPL( _0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11, \ 42 | _12,_13,_14,_15,_16,__N,...) __N 43 | #endif 44 | 45 | #ifndef __PLOOC_VA_NUM_ARGS 46 | #define __PLOOC_VA_NUM_ARGS(...) \ 47 | __PLOOC_VA_NUM_ARGS_IMPL( 0,##__VA_ARGS__,16,15,14,13,12,11,10,9, \ 48 | 8,7,6,5,4,3,2,1,0) 49 | #endif 50 | 51 | #ifndef safe_atom_code 52 | #include "cmsis_compiler.h" 53 | #define safe_atom_code() \ 54 | for( uint32_t SAFE_NAME(temp) = \ 55 | ({uint32_t SAFE_NAME(temp2)=__get_PRIMASK(); \ 56 | __disable_irq(); \ 57 | SAFE_NAME(temp2);}),*SAFE_NAME(temp3) = NULL; \ 58 | SAFE_NAME(temp3)++ == NULL; \ 59 | __set_PRIMASK(SAFE_NAME(temp))) 60 | #endif 61 | 62 | 63 | #define __DEQUEUE_0( __QUEUE, __ADDR) \ 64 | dequeue_bytes((__QUEUE), (__ADDR),(sizeof(typeof(*(__ADDR))))) 65 | 66 | #define __DEQUEUE_1( __QUEUE, __ADDR, __ITEM_COUNT) \ 67 | dequeue_bytes((__QUEUE), (__ADDR), __ITEM_COUNT*(sizeof(typeof((__ADDR[0]))))) 68 | 69 | #define __DEQUEUE_2( __QUEUE, __ADDR, __TYPE,__ITEM_COUNT) \ 70 | dequeue_bytes((__QUEUE), (__ADDR), (__ITEM_COUNT * sizeof(__TYPE))) 71 | 72 | 73 | #define __ENQUEUE_0( __QUEUE, __VALUE) \ 74 | ({typeof((__VALUE)) SAFE_NAME(value) = __VALUE; \ 75 | enqueue_bytes((__QUEUE), &(SAFE_NAME(value)), (sizeof(__VALUE)));}) 76 | 77 | #define __ENQUEUE_1( __QUEUE, __ADDR, __ITEM_COUNT) \ 78 | enqueue_bytes((__QUEUE), (__ADDR), __ITEM_COUNT*(sizeof(typeof((__ADDR[0]))))) 79 | 80 | #define __ENQUEUE_2( __QUEUE, __ADDR, __TYPE, __ITEM_COUNT) \ 81 | enqueue_bytes((__QUEUE), (__ADDR), (__ITEM_COUNT * sizeof(__TYPE))) 82 | 83 | 84 | #define __PEEK_QUEUE_0( __QUEUE, __ADDR) \ 85 | peek_bytes_queue((__QUEUE), (__ADDR), (sizeof(typeof(*(__ADDR))))) 86 | 87 | #define __PEEK_QUEUE_1( __QUEUE, __ADDR, __ITEM_COUNT) \ 88 | peek_bytes_queue((__QUEUE), (__ADDR), __ITEM_COUNT*(sizeof(typeof((__ADDR[0]))))) 89 | 90 | #define __PEEK_QUEUE_2( __QUEUE, __ADDR, __TYPE, __ITEM_COUNT) \ 91 | peek_bytes_queue((__QUEUE), (__ADDR), (__ITEM_COUNT * sizeof(__TYPE))) 92 | 93 | 94 | #define __QUEUE_INIT_0(__QUEUE, __BUFFER, __SIZE ) \ 95 | queue_init_byte(__QUEUE, __BUFFER, __SIZE, false ) 96 | 97 | #define __QUEUE_INIT_1(__QUEUE, __BUFFER, __SIZE, __COVER ) \ 98 | queue_init_byte(__QUEUE, __BUFFER, __SIZE, __COVER ) 99 | 100 | /*! 101 | * \brief Initialize the queue object. 102 | * 103 | * \param[in] __queue pointer to the queue object. 104 | * \param[in] __buffer address of ring buffer var 105 | * \param[in] __size size of the ring buffer in bytes. 106 | * 107 | * \return the address of queue item 108 | * 109 | * \details Here is an example: 110 | E.g. 111 | \code 112 | static uint8_t s_hwQueueBuffer[100]; 113 | static byte_queue_t my_queue; 114 | queue_init(&my_queue,s_hwQueueBuffer,sizeof(s_hwQueueBuffer)); 115 | \endcode 116 | */ 117 | 118 | #define queue_init(__queue,__buffer,__size ,... ) \ 119 | CONNECT2(__QUEUE_INIT_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 120 | (__queue,(__buffer),(__size),##__VA_ARGS__) 121 | 122 | #define QUEUE_INIT(__QUEUE, __BUFFER, __SIZE ,... ) \ 123 | CONNECT2(__QUEUE_INIT_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 124 | (__QUEUE,(__BUFFER),(__SIZE),##__VA_ARGS__) 125 | 126 | 127 | /*! 128 | * \brief Get data from the ring buffer. 129 | * 130 | * \param[in] __queue pointer to the queue object. 131 | * \param[in] __addr address to the data buffer 132 | * \param[in] ... Optional parameters,You can add data types and data quantities 133 | * 134 | * \return Return the data size we read from the ring buffer. 135 | * 136 | * \details Here is an example: 137 | E.g. The size is automatically calculated based on the variable type 138 | \code 139 | uint8_t data1; 140 | uint16_t data2; 141 | uint32_t data3; 142 | dequeue(&my_queue,&data1); 143 | dequeue(&my_queue,&data2); 144 | dequeue(&my_queue,&data3); 145 | \endcode 146 | E.g. Read out all data of ring buffer 147 | \code 148 | uint8_t data[100]; 149 | dequeue(&my_queue,data,get_queue_count(&my_queue)); 150 | dequeue(&my_queue,data,uint8_t,get_queue_count(&my_queue)); 151 | \endcode 152 | */ 153 | #define dequeue(__queue,__addr,...) \ 154 | CONNECT2(__DEQUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 155 | (__queue,(__addr),##__VA_ARGS__) 156 | 157 | #define DEQUEUE(__QUEUE, __ADDR,...) \ 158 | CONNECT2(__DEQUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 159 | (__QUEUE,(__ADDR),##__VA_ARGS__) 160 | 161 | 162 | /*! 163 | * \brief Put a block of data into the ring buffer. If the capacity of ring buffer is insufficient, it will discard out-of-range data. 164 | * 165 | * \param[in] __queue pointer to the queue object. 166 | * \param[in] __addr address to the data buffer 167 | * \param[in] ... Optional parameters,You can add data types and data quantities 168 | * 169 | * \return Return the data size we put into the ring buffer. 170 | * 171 | * \details Here is an example: 172 | E.g. The size is automatically calculated based on the variable type 173 | \code 174 | uint8_t data1 = 0XAA; 175 | uint16_t data2 = 0X55AA; 176 | uint32_t data3 = 0X55AAAA55; 177 | enqueue(&my_queue,data1); 178 | enqueue(&my_queue,data2); 179 | enqueue(&my_queue,data3); 180 | \endcode 181 | E.g. Put a block of data into the ring buffer 182 | \code 183 | uint32_t data[100]; 184 | enqueue(&my_queue,data,100); 185 | enqueue(&my_queue,data,uint32_t,100); 186 | \endcode 187 | */ 188 | 189 | #define enqueue(__queue, __addr,...) \ 190 | CONNECT2(__ENQUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 191 | (__queue,(__addr),##__VA_ARGS__) 192 | 193 | #define ENQUEUE(__QUEUE, __ADDR,...) \ 194 | CONNECT2(__ENQUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 195 | (__QUEUE,(__ADDR),##__VA_ARGS__) 196 | 197 | 198 | /*! 199 | * \brief Peek data from the ring buffer. 200 | * 201 | * \param[in] __queue pointer to the queue object. 202 | * \param[in] __addr address to the data buffer 203 | * \param[in] ... Optional parameters,You can add data types and data quantities 204 | * 205 | * \return Return the data size we peek from the ring buffer. 206 | * 207 | * \details Here is an example: 208 | E.g. The size is automatically calculated based on the variable type 209 | \code 210 | uint8_t data1; 211 | uint16_t data2; 212 | uint32_t data3; 213 | peek_queue(&my_queue,&data1); 214 | peek_queue(&my_queue,&data2); 215 | peek_queue(&my_queue,&data3); 216 | \endcode 217 | */ 218 | 219 | #define peek_queue(__queue, __addr,...) \ 220 | CONNECT2(__PEEK_QUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 221 | (__queue,(__addr),##__VA_ARGS__) 222 | 223 | #define PEEK_QUEUE(__QUEUE, __ADDR,...) \ 224 | CONNECT2(__PEEK_QUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \ 225 | (__QUEUE,(__ADDR),##__VA_ARGS__) 226 | 227 | #ifdef assert 228 | #undef assert 229 | #endif 230 | #define assert(EXPR) \ 231 | if (!(EXPR)) \ 232 | { \ 233 | printf("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \ 234 | while (1); \ 235 | } 236 | 237 | typedef struct byte_queue_t { 238 | uint8_t *pchBuffer; 239 | uint16_t hwSize; 240 | uint16_t hwHead; 241 | uint16_t hwTail; 242 | uint16_t hwLength; 243 | uint16_t hwPeek; 244 | uint16_t hwPeekLength; 245 | bool bMutex; 246 | bool bIsCover; 247 | } byte_queue_t; 248 | 249 | extern 250 | byte_queue_t *queue_init_byte(byte_queue_t *ptObj, void *pBuffer, uint16_t hwItemSize, bool bIsCover); 251 | 252 | extern 253 | bool reset_queue(byte_queue_t *ptObj); 254 | 255 | extern 256 | uint16_t enqueue_bytes(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength); 257 | 258 | extern 259 | uint16_t dequeue_bytes(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength); 260 | 261 | extern 262 | bool is_queue_empty(byte_queue_t *ptQueue); 263 | 264 | extern 265 | bool is_peek_empty(byte_queue_t *ptObj); 266 | 267 | extern 268 | uint16_t peek_bytes_queue(byte_queue_t *ptObj, void *pDate, uint16_t hwDataLength); 269 | 270 | extern 271 | bool reset_peek(byte_queue_t *ptQueue); 272 | 273 | extern 274 | bool get_all_peeked(byte_queue_t *ptQueue); 275 | 276 | extern 277 | uint16_t get_peek_status(byte_queue_t *ptQueue); 278 | 279 | extern 280 | bool restore_peek_status(byte_queue_t *ptQueue, uint16_t hwCount); 281 | 282 | extern 283 | uint16_t get_queue_count(byte_queue_t *ptObj); 284 | 285 | extern 286 | uint16_t get_queue_available_count(byte_queue_t *ptObj); 287 | 288 | #endif /* QUEUE_QUEUE_H_ */ 289 | -------------------------------------------------------------------------------- /gen_pack.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Version: 2.7 3 | # Date: 2023-05-22 4 | # This bash script generates a CMSIS Software Pack: 5 | # 6 | 7 | set -o pipefail 8 | 9 | # Set version of gen pack library 10 | # For available versions see https://github.com/Open-CMSIS-Pack/gen-pack/tags. 11 | # Use the tag name without the prefix "v", e.g., 0.7.0 12 | REQUIRED_GEN_PACK_LIB="0.8.4" 13 | 14 | # Set default command line arguments 15 | DEFAULT_ARGS=(-c "v") 16 | 17 | # Pack warehouse directory - destination 18 | # Default: ./output 19 | # 20 | # PACK_OUTPUT=./output 21 | 22 | # Temporary pack build directory, 23 | # Default: ./build 24 | # 25 | # PACK_BUILD=./build 26 | 27 | # Specify directory names to be added to pack base directory 28 | # An empty list defaults to all folders next to this script. 29 | # Default: empty (all folders) 30 | # 31 | #PACK_DIRS=" 32 | # 33 | #" 34 | 35 | # Specify file names to be added to pack base directory 36 | # Default: empty 37 | # 38 | PACK_BASE_FILES=" 39 | LICENSE 40 | byte_queue.c 41 | byte_queue.h 42 | README.md 43 | " 44 | 45 | # Specify file names to be deleted from pack build directory 46 | # Default: empty 47 | # 48 | # PACK_DELETE_FILES=" 49 | # " 50 | 51 | # Specify patches to be applied 52 | # Default: empty 53 | # 54 | # PACK_PATCH_FILES=" 55 | # 56 | # " 57 | 58 | # Specify addition argument to packchk 59 | # Default: empty 60 | # 61 | # PACKCHK_ARGS=() 62 | 63 | # Specify additional dependencies for packchk 64 | # Default: empty 65 | # 66 | # PACKCHK_DEPS=" 67 | # 68 | # " 69 | 70 | # Optional: restrict fallback modes for changelog generation 71 | # Default: full 72 | # Values: 73 | # - full Tag annotations, release descriptions, or commit messages (in order) 74 | # - release Tag annotations, or release descriptions (in order) 75 | # - tag Tag annotations only 76 | # 77 | # PACK_CHANGELOG_MODE="" 78 | 79 | # 80 | # custom pre-processing steps 81 | # 82 | # usage: preprocess 83 | # The build folder 84 | # 85 | function preprocess() { 86 | # add custom steps here to be executed 87 | # before populating the pack build folder 88 | return 0 89 | } 90 | 91 | # 92 | # custom post-processing steps 93 | # 94 | # usage: postprocess 95 | # The build folder 96 | # 97 | function postprocess() { 98 | # add custom steps here to be executed 99 | # after populating the pack build folder 100 | # but before archiving the pack into output folder 101 | return 0 102 | } 103 | 104 | ############ DO NOT EDIT BELOW ########### 105 | 106 | function install_lib() { 107 | local URL="https://github.com/Open-CMSIS-Pack/gen-pack/archive/refs/tags/v$1.tar.gz" 108 | local STATUS=$(curl -sLI "${URL}" | grep "^HTTP" | tail -n 1 | cut -d' ' -f2 || echo "$((600+$?))") 109 | if [[ $STATUS -ge 400 ]]; then 110 | echo "Wrong/unavailable gen-pack lib version '$1'!" >&2 111 | echo "Check REQUIRED_GEN_PACK_LIB variable." >&2 112 | echo "For available versions see https://github.com/Open-CMSIS-Pack/gen-pack/tags." >&2 113 | exit 1 114 | fi 115 | echo "Downloading gen-pack lib version '$1' to '$2' ..." 116 | mkdir -p "$2" 117 | curl -L "${URL}" -s | tar -xzf - --strip-components 1 -C "$2" || exit 1 118 | } 119 | 120 | function load_lib() { 121 | if [[ -d ${GEN_PACK_LIB} ]]; then 122 | . "${GEN_PACK_LIB}/gen-pack" 123 | return 0 124 | fi 125 | local GLOBAL_LIB="/usr/local/share/gen-pack/${REQUIRED_GEN_PACK_LIB}" 126 | local USER_LIB="${HOME}/.local/share/gen-pack/${REQUIRED_GEN_PACK_LIB}" 127 | if [[ ! -d "${GLOBAL_LIB}" && ! -d "${USER_LIB}" ]]; then 128 | echo "Required gen_pack lib not found!" >&2 129 | install_lib "${REQUIRED_GEN_PACK_LIB}" "${USER_LIB}" 130 | fi 131 | 132 | if [[ -d "${GLOBAL_LIB}" ]]; then 133 | . "${GLOBAL_LIB}/gen-pack" 134 | elif [[ -d "${USER_LIB}" ]]; then 135 | . "${USER_LIB}/gen-pack" 136 | else 137 | echo "Required gen-pack lib is not installed!" >&2 138 | exit 1 139 | fi 140 | } 141 | 142 | load_lib 143 | gen_pack "${DEFAULT_ARGS[@]}" "$@" 144 | 145 | exit 0 -------------------------------------------------------------------------------- /kk.byte_queue.pdsc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | kk 5 | byte_queue 6 | A circular queue library written in C that supports arbitrary types. 7 | https://github.com/Aladdin-Wang/byte_queue/ 8 | https://github.com/Aladdin-Wang/byte_queue/issues 9 | LICENSE 10 | 11 | 12 | 13 | 14 | https://github.com/Aladdin-Wang/byte_queue.git 15 | 16 | 17 | 18 | 19 | Active development ... 20 | 21 | 22 | 23 | Delete PLOOC. 24 | 25 | 26 | Delete PLOOC. 27 | 28 | 29 | Initial release of byte_queue. 30 | 31 | 32 | Initial release of byte_queue. 33 | 34 | 35 | 36 | 37 | 38 | Circular Queue 39 | C Language 40 | Data Structures 41 | 42 | 43 | 44 | 45 | 46 | A circular queue library written in C that supports arbitrary types. 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | A circular queue library written in C that supports arbitrary types. 58 | 59 | 60 | --------------------------------------------------------------------------------