-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfc_trans.c
More file actions
469 lines (420 loc) · 14.2 KB
/
fc_trans.c
File metadata and controls
469 lines (420 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
/**
* @file fc_trans.c
* @author fool_cat (2696652257@qq.com)
* @brief 默认"\033[?;" "<num>" "m"作为分页信息,长度定为10个字符其能支持的端口号范围为4位数字(0 ~ 9999)
* @version 1.0
* @date 2025-04-24
*
* @copyright Copyright (c) 2025
*
*/
#include <string.h>
#include <stdlib.h>
#include "fc_trans.h"
#include "fc_helper.h"
#include "fc_port.h"
// 运行时断言
#ifndef fc_assert
#define fc_assert(x) ((void)(0))
#endif
// 自定义的分页信息
#ifndef FC_DIVISION_NUM_MAX_LEN
#define FC_DIVISION_NUM_MAX_LEN 4 /* 分页信息中端口号的最大长度("9999") */
#endif
#ifndef FC_DIVISION_NUM_MAX
#define FC_DIVISION_NUM_MAX (9999) /* 分页信息中端口号的最大值,即4位数字的最大值 */
#endif
#define FC_DIVISION_HEAD "\033[?;"
#define FC_DIVISION_TAIL "m"
#define FC_DIVISION_DEFAULT "\033[?;0m" // 默认窗口0
#define FC_DIVISION_POSITION (sizeof(FC_DIVISION_HEAD) - 1) // 分页信息的索引位置
#define FC_DIVISION_MIN_BUF_LEN (sizeof(FC_DIVISION_DEFAULT)) // 分页信息最小长度,包括前缀和后缀个一个字节的端口号
#define FC_DIVISION_MAX_BUF_LEN (FC_DIVISION_MIN_BUF_LEN + (FC_DIVISION_NUM_MAX_LEN - 1)) // 包含'\0'的分页信息最大长度,即"\033[?;9999m"的长度,方便使用字符串API
#ifndef EOF
#define EOF (-1)
#endif
/**
* @brief 判断字符数组是否全为数字
*
* @param arr
* @param length
* @return true
* @return false
*/
static bool is_all_digits(const char *arr, int length)
{
if (length <= 0)
{
return false;
}
for (int i = 0; i < length; i++)
{
if (arr[i] < '0' || arr[i] > '9')
{
return false;
}
}
return true;
}
//+********************************* receiver **********************************/
/**
* @brief 初始化接收器
*
* @param receiver 接收器对象
* @param fifo 一般建议是输入方向的fifo,不做强制
*/
void fc_receiver_init(fc_receiver_t *receiver, fc_fifo_t *fifo)
{
fc_assert(NULL != receiver);
fc_assert(NULL != fifo);
memset(receiver, 0, sizeof(fc_receiver_t));
receiver->fifo = fifo;
receiver->index = 0; // 默认窗口(页)0
}
/**
* @brief 绑定接收器的分发函数和结束函数
*
* @param receiver 接收器对象
* @param out 分发函数
* @param end 结束函数,返回true表示本次结束,返回false表示还在接收
*/
void fc_receiver_catch(fc_receiver_t *receiver, fc_receiver_out_t out, fc_receiver_end_t end)
{
fc_assert(NULL != receiver);
fc_assert(NULL != out); // 必须绑定分发函数
receiver->out = out; // 分发函数
receiver->end = end; // 结束函数,可以为NULL
}
/**
* @brief 接收器监控函数,需要保证线程安全
*
* @param receiver
*/
void fc_receiver_monitor(fc_receiver_t *receiver)
{
fc_assert(NULL != receiver->fifo);
fc_assert(NULL != receiver->out); // 先绑定了分发函数才能调用
fc_fifo_t *rb = receiver->fifo; // 环形缓冲区
size_t len_total = fc_fifo_get_used(rb);
if (len_total <= 0)
{
return; // 没有数据直接返回
}
size_t len = 0;
char *p_start = NULL;
char *p_end = NULL;
const char *head = FC_DIVISION_HEAD; // 分页信息头部
do
{
len_total = fc_fifo_get_used(rb); // 更新现存数据量
p_start = (char *)fc_fifo_linear_read_setup(rb, &len);
p_end = (char *)memchr(p_start, head[0], len); // 查找是否可能存在分页信息
if (NULL == p_end) // 不存在分页信息
{
// 分页信息不存在,直接分发数据
receiver->out(receiver->index, p_start, len);
fc_fifo_linear_read_done(rb, len);
}
else // 可能存在分页信息
{
// 先把分页信息之前的数据分发
if (p_end != p_start)
{
receiver->out(receiver->index, p_start, p_end - p_start);
}
fc_fifo_linear_read_done(rb, p_end - p_start);
len_total = fc_fifo_get_used(rb); // 更新现存数据量
char buff[FC_DIVISION_MAX_BUF_LEN] = {0}; // 多一个'\0'字符,方便字符串处理
len = fc_fifo_peek(rb, buff, (sizeof(buff) - 1) > len_total ? len_total : (sizeof(buff) - 1));
p_start = &buff[0]; // 分页信息开始位置
if (0 != memcmp(p_start, head, FC_DIVISION_POSITION)) // 分页信息头部
{
receiver->out(receiver->index, buff, 1); // 将这个可能为头的字符分发出去
fc_fifo_drop(rb, 1); // 弹出这个字符
continue; // 继续查找分页信息
}
p_end = strstr(buff + FC_DIVISION_POSITION, FC_DIVISION_TAIL); // 找到分页尾部
if (p_end) // 找到分页信息头部和尾部
{
if ((p_end - p_start) <= FC_DIVISION_POSITION)
{
// 根本没有端口号信息,直接当做数据分发
receiver->out(receiver->index, p_start, p_end - p_start + (sizeof(FC_DIVISION_TAIL) - 1));
fc_fifo_drop(rb, p_end - p_start + (sizeof(FC_DIVISION_TAIL) - 1)); // 弹出数据
continue; // 继续查找分页信息
}
// 判断索引开始到尾部中间的所有字符是否全为数字
if (is_all_digits(p_start + FC_DIVISION_POSITION, p_end - p_start - FC_DIVISION_POSITION))
{
// 更新当前端口号
*p_end = '\0'; // 将分页信息结束符替换为'\0'方便后续字符串处理
receiver->index = atoi(p_start + FC_DIVISION_POSITION);
}
else
{ // 不完全符合分页格式,当数据分发
receiver->out(receiver->index, p_start, p_end - p_start + (sizeof(FC_DIVISION_TAIL) - 1));
}
fc_fifo_drop(rb, p_end - p_start + (sizeof(FC_DIVISION_TAIL) - 1)); // 不论分页码信息解析是否成功,都要弹出帧尾(包含)之前的数据
}
else if (len < (FC_DIVISION_MAX_BUF_LEN - 1)) // 可能数据不完整导致的未找到分页信息尾部
{
if (receiver->end)
{
if (receiver->end(receiver))
{
// 已经完成数据接收,表明仅仅是数据刚好存在分页信息头
receiver->out(receiver->index, p_start, len); // 当做数据直接分发
fc_fifo_drop(rb, len); // 弹出数据
}
else
{
break; // 数据还没接收完,退出这次分发
}
}
else
{
// 没有绑定结束函数当数据已经接收完成,没有匹配到结束信息,直接当做数据分发
receiver->out(receiver->index, p_start, len);
fc_fifo_drop(rb, len);
}
}
else // 数据长度足够,但是没有匹配到结束信息,直接当数据分发
{
receiver->out(receiver->index, p_start, len);
fc_fifo_drop(rb, len);
}
}
} while (fc_fifo_get_used(rb) > 0); // 继续处理数据
}
//+********************************* sender **********************************/
/**
* @brief 初始化发送器
*
* @param sender
* @param fifo // 一般建议是输出方向的fifo,不做强制
*/
void fc_sender_init(fc_sender_t *sender, fc_fifo_t *fifo)
{
fc_assert(NULL != sender);
fc_assert(NULL != fifo);
const char *default_division = FC_DIVISION_DEFAULT;
memset(sender, 0, sizeof(fc_sender_t));
sender->fifo = fifo;
sender->index = 0; // 默认窗口0
// fc_fifo_write(sender->fifo, (void *)default_division, sizeof(FC_DIVISION_DEFAULT) - 1); // 写入默认窗口
}
/**
* @brief 切换窗口
*
* @param sender
* @param index 窗口号
* @return true
* @return false
*/
bool fc_sender_switch(fc_sender_t *sender, size_t index)
{
fc_assert(NULL != sender);
fc_assert(NULL != sender->fifo);
fc_assert(index <= FC_DIVISION_NUM_MAX); // 确保索引在范围内
if (sender->index != index)
{
size_t num = index;
char buff[FC_DIVISION_MAX_BUF_LEN] = {0};
int pos = FC_DIVISION_POSITION;
// 1. 拷贝前缀
for (int i = 0; i < FC_DIVISION_POSITION; i++)
{
buff[i] = FC_DIVISION_HEAD[i];
}
// 2. 计算数字位数并写入数字部分
if (num < 10)
{
buff[pos++] = '0' + num; // 只有一位数字
}
else
{
// 从最高位开始计算,避免前导零
size_t divisor = 10;
// 计算最大除数
for (size_t i = 0; i < (FC_DIVISION_NUM_MAX_LEN - 1); i++)
{
// if (num >= divisor && num < (divisor * 10))
if (num < (divisor * 10)) // 优化条件
{
break;
}
divisor *= 10;
}
int digit = 0;
// 逐位写入数字
do
{
digit = (size_t)num / (size_t)divisor; // 整数除法,丢掉余数部分
buff[pos++] = '0' + digit;
num -= digit * divisor;
if (num < 10)
{
while (divisor > 10)
{
divisor /= 10; // 除数逐渐减小,直到只剩下个位
buff[pos++] = '0'; // 写入中间0
}
buff[pos++] = '0' + num; // 写入最后一位数字
break;
}
divisor /= 10;
} while (divisor > 0);
}
// 3. 拷贝后缀
for (int i = 0; i < (sizeof(FC_DIVISION_TAIL) - 1); i++)
{
buff[pos++] = FC_DIVISION_TAIL[i];
}
if (fc_fifo_get_free(sender->fifo) < (size_t)pos)
{
return false; // 不够写入完整的分页信息
}
if (pos == fc_fifo_write(sender->fifo, buff, pos))
{
sender->index = index; // 成功切换窗口(页)
}
else
{
fc_assert(false);
return false; // 分页信息写入失败,理论上这里不可能进入
}
}
return true;
}
/**
* @brief 发送字符
*
* @param sender
* @param index
* @param ch
* @return int 发送成功返回ch,失败返回负值
*/
int fc_sender_putc(fc_sender_t *sender, size_t index, int ch)
{
if (fc_sender_switch(sender, index))
{
if (fc_fifo_write_byte(sender->fifo, (uint8_t)ch))
{
return ch; // 成功返回写入的字符
}
}
return EOF; // 返回失败
}
/**
* @brief 发送字符串
*
* @param sender
* @param index
* @param str
* @return int 发送成功返回发送的字节数,失败返回负值
*/
int fc_sender_puts(fc_sender_t *sender, size_t index, const char *str)
{
size_t len = strlen(str);
if (fc_sender_switch(sender, index))
{
if (fc_fifo_get_free(sender->fifo) >= len)
{
len = fc_fifo_write(sender->fifo, (void *)str, len);
return (int)len;
}
}
return EOF;
}
/**
* @brief 从指定地址发送指定长度的数据
*
* @param sender
* @param index
* @param buf
* @param len
* @return size_t 发送成功返回发送的字节数,失败返回负值
*/
int fc_sender_write(fc_sender_t *sender, size_t index, const void *buf, size_t len)
{
if (fc_sender_switch(sender, index))
{
if (fc_fifo_get_free(sender->fifo) >= len)
{
len = fc_fifo_write(sender->fifo, (void *)buf, len);
return (int)len;
}
}
return EOF;
}
/**
* @brief 发送格式化字符串
*
* @param sender
* @param index
* @param fmt
* @param ...
* @return int 发送成功返回发送的字节数,失败返回负值
*/
int fc_sender_printf(fc_sender_t *sender, size_t index, const char *fmt, ...)
{
int ret = EOF;
if (fc_sender_switch(sender, index))
{
va_list arp;
va_start(arp, fmt);
ret = fc_fifo_vprintf(sender->fifo, fmt, arp);
va_end(arp);
}
return ret;
}
/**
* @brief
*
* @param sender
* @param index
* @param fmt
* @param arp
* @return int
*/
int fc_sender_vprintf(fc_sender_t *sender, size_t index, const char *fmt, va_list arp)
{
int ret = EOF;
if (fc_sender_switch(sender, index))
{
ret = fc_fifo_vprintf(sender->fifo, fmt, arp);
}
return ret;
}
//+********************************* 默认对象 **********************************/
fc_receiver_t fc_receiver; // 接收器对象
fc_sender_t fc_sender; // 发送器对象
//+********************************* 提供一份对接log组件的写API **********************************/
// > C/C++兼容性宏定义
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief 默认log对象的write函数,写入到fc_trans的0端口
*
* @param buf
* @param len
* @return int
*/
fc_weak int log_write_trans(const char *buf, int len)
{
return fc_sender_write(&fc_sender, 0, buf, len);
}
#ifdef __cplusplus
}
#endif //\ __cplusplus
//+********************************* 注册到ENV段中 **********************************/
#include "fc_auto_init.h"
static void _fc_trans_auto_init(void)
{
fc_receiver_init(&fc_receiver, fc_stdin.rb[0]); // 初始化接收器
fc_sender_init(&fc_sender, fc_stdout.rb[0]); // 初始化发送器
}
// 等级比默认的1000优先级更高,但是低于port层的默认初始化,纯数据结构无外部依赖
INIT_EXPORT_ENV(_fc_trans_auto_init, FC_TRANS_INIT_ORDER);