├── .github
└── workflows
│ └── cmake-single-platform.yml
├── kk.byte_queue.pdsc
├── gen_pack.sh
├── byte_queue.h
├── LICENSE
├── README.md
└── byte_queue.c
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | 
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 |
--------------------------------------------------------------------------------