Add C-language port of the MassData API with a Visual Studio 2026 test project#40
Conversation
…project Agent-Logs-Url: https://github.com/NEVSTOP-LAB/CSM-MassData-Parameter-Support/sessions/b7acbb10-bea4-49e9-9339-4a7786edba72 Co-authored-by: nevstop <8196752+nevstop@users.noreply.github.com>
nevstop
left a comment
There was a problem hiding this comment.
- 所有的注释改为中文注释
- 工程修改为 vs2026
- 测试放到 c/_test/vs/.. 目录下
|
@copilot 根据之前上的comments修改 |
… to c/_test/vs Agent-Logs-Url: https://github.com/NEVSTOP-LAB/CSM-MassData-Parameter-Support/sessions/3ca10741-4f42-4106-938e-77fb5c7ab9a7 Co-authored-by: nevstop <8196752+nevstop@users.noreply.github.com>
已在 5eb1974 中处理:1) 头文件、实现与测试程序中的所有注释(含 Doxygen)改为中文;2) VS 工程升级到 VS2026( |
Agent-Logs-Url: https://github.com/NEVSTOP-LAB/CSM-MassData-Parameter-Support/sessions/49ec99ef-0b8a-43e5-93ea-4ccb0872a44d Co-authored-by: nevstop <8196752+nevstop@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a C-language implementation of the MassData Parameter Support API (matching the LabVIEW VI surface), plus a standalone Visual Studio test project and repo hygiene updates so C-only changes don’t trigger the LabVIEW CI workflows.
Changes:
- Added
csm_massdataC API implementation + public header with Doxygen docs (Chinese). - Added a Visual Studio solution/project with a console-based test runner covering the exported API.
- Updated
.gitignoreand adjusted LabVIEW GitHub Actions workflows to ignore changes underc/**.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
c/src/csm_massdata.c |
Implements the global ring-buffer, encoder/decoder, datatype parsing, and status API. |
c/include/csm_massdata.h |
Public C API + documentation mirroring LabVIEW VI naming/semantics. |
c/_test/vs/test_main.c |
Console test suite validating correct behavior and major error cases. |
c/_test/vs/csm_massdata_test.vcxproj |
VS project configuration to build the C implementation and tests. |
c/_test/vs/csm_massdata_test.vcxproj.filters |
VS filters for source/header organization. |
c/_test/vs/csm_massdata_test.sln |
VS solution wrapper for the test project. |
c/README.md |
C port overview + build/test instructions. |
.gitignore |
Ignores Visual Studio build artifacts under c/. |
.github/workflows/Check_Broken_VIs.yml |
Adds c/** to paths-ignore so C-only changes don’t trigger. |
.github/workflows/Build_VIPM_Library.yml |
Adds c/** to paths-ignore so C-only changes don’t trigger. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (strncmp(p, "Start:", 6) != 0) { | ||
| return CSM_MASSDATA_ERR_PARSE; | ||
| } | ||
| p += 6; | ||
| tmp = strtoull(p, &end, 10); | ||
| if (end == p || *end != ';') { | ||
| return CSM_MASSDATA_ERR_PARSE; | ||
| } | ||
| *start_out = (uint64_t)tmp; | ||
| p = end + 1; | ||
|
|
||
| if (strncmp(p, "Size:", 5) != 0) { | ||
| return CSM_MASSDATA_ERR_PARSE; | ||
| } | ||
| p += 5; | ||
| tmp = strtoull(p, &end, 10); | ||
| if (end == p) { | ||
| return CSM_MASSDATA_ERR_PARSE; | ||
| } | ||
| *size_out = (uint64_t)tmp; | ||
| p = end; |
There was a problem hiding this comment.
csm_massdata_parse() uses strtoull without checking errno/ULLONG_MAX overflow, so extremely large Start/Size values can be accepted and then cast into uint64_t silently. Add errno checking (and optionally an explicit upper bound check) to reject out-of-range values with CSM_MASSDATA_ERR_PARSE.
| /* 可选的 ;DataType:<T> 后缀。 */ | ||
| if (data_type != NULL && data_type_cap > 0u) { | ||
| data_type[0] = '\0'; | ||
| } | ||
| if (*p == '\0') { | ||
| return CSM_MASSDATA_OK; | ||
| } | ||
| if (*p != ';') { | ||
| return CSM_MASSDATA_ERR_PARSE; | ||
| } | ||
| p++; | ||
| if (strncmp(p, "DataType:", 9) != 0) { | ||
| return CSM_MASSDATA_ERR_PARSE; | ||
| } | ||
| p += 9; | ||
| if (data_type != NULL && data_type_cap > 0u) { | ||
| size_t len = strlen(p); | ||
| if (len + 1u > data_type_cap) { | ||
| return CSM_MASSDATA_ERR_BUFFER_TOO_SMALL; | ||
| } | ||
| memcpy(data_type, p, len + 1u); | ||
| } | ||
| return CSM_MASSDATA_OK; |
There was a problem hiding this comment.
csm_massdata_parse() accepts any trailing characters after DataType: (and when data_type == NULL, it also accepts trailing characters without validating termination). Given the documented format <MassData>Start:<N>;Size:<N>[;DataType:<T>], the parser should ensure the string ends after the optional DataType value (and ideally reject DataType values containing ;, <, > to match the encoder’s constraints).
| static void csm_massdata_lazy_init(void) | ||
| { | ||
| if (g_state.initialized) { | ||
| return; | ||
| } | ||
| /* 第一次调用本模块的 API 时负责分配默认缓冲区。 | ||
| * 一旦初始化完成,该标志将由互斥量保护;但首次分配本身 | ||
| * 在多线程程序中存在竞态,因此关心线程安全的调用方应当 | ||
| * 在程序启动时主动调用 CSM_ConfigMassDataParameterCacheSize()。 */ | ||
| g_state.buffer = (uint8_t *)malloc(CSM_MASSDATA_DEFAULT_CACHE_SIZE); | ||
| g_state.capacity = (g_state.buffer != NULL) ? CSM_MASSDATA_DEFAULT_CACHE_SIZE : 0u; | ||
| g_state.write_total = 0u; | ||
| memset(&g_state.last_read, 0, sizeof(g_state.last_read)); | ||
| memset(&g_state.last_write, 0, sizeof(g_state.last_write)); | ||
| CSM_MUTEX_INIT(&g_state.mutex); | ||
| g_state.initialized = 1; | ||
| } |
There was a problem hiding this comment.
csm_massdata_lazy_init() is not thread-safe: multiple threads can concurrently allocate g_state.buffer and initialize g_state.mutex, leading to leaks and undefined behavior (double init of mutex / data race on initialized). Since public APIs call this without external synchronization, initialization should be made thread-safe (e.g., pthread_once / InitOnceExecuteOnce / a separate static init lock) rather than relying on the caller to serialize startup.
| * 本头文件中所有公开函数均为线程安全。多个线程的并发调用 | ||
| * 会通过内部互斥量串行化执行。 |
There was a problem hiding this comment.
The header states all public functions are thread-safe, but the implementation’s lazy initialization has a documented race on first use. Either make initialization thread-safe or adjust the API docs to reflect that callers must initialize/configure early to avoid concurrent first-call races.
| * 本头文件中所有公开函数均为线程安全。多个线程的并发调用 | |
| * 会通过内部互斥量串行化执行。 | |
| * 在内部状态完成初始化后,公开函数的并发调用会通过内部互斥量 | |
| * 串行化执行。首次使用可能触发延迟初始化;为避免首次调用时的 | |
| * 并发竞争,调用方应在进入多线程阶段前,于单线程上下文中先完成 | |
| * 一次初始化/配置调用,再让多个线程并发访问本 API。 |
| uint64_t end = start + size; | ||
| uint64_t oldest = (g_state.write_total > (uint64_t)g_state.capacity) | ||
| ? g_state.write_total - (uint64_t)g_state.capacity | ||
| : 0u; | ||
| if (start < oldest || end > g_state.write_total) { | ||
| CSM_MUTEX_UNLOCK(&g_state.mutex); | ||
| return CSM_MASSDATA_ERR_OVERWRITTEN; | ||
| } |
There was a problem hiding this comment.
end = start + size can overflow uint64_t, which can incorrectly pass the residency check and return unrelated bytes instead of CSM_MASSDATA_ERR_OVERWRITTEN for crafted arguments (e.g., Start near UINT64_MAX). Add an overflow check before the addition (e.g., if (size > 0 && start > UINT64_MAX - size) ...) and validate start <= write_total and end <= write_total using non-overflowing comparisons.
| uint64_t end = start + size; | |
| uint64_t oldest = (g_state.write_total > (uint64_t)g_state.capacity) | |
| ? g_state.write_total - (uint64_t)g_state.capacity | |
| : 0u; | |
| if (start < oldest || end > g_state.write_total) { | |
| CSM_MUTEX_UNLOCK(&g_state.mutex); | |
| return CSM_MASSDATA_ERR_OVERWRITTEN; | |
| } | |
| uint64_t oldest = (g_state.write_total > (uint64_t)g_state.capacity) | |
| ? g_state.write_total - (uint64_t)g_state.capacity | |
| : 0u; | |
| uint64_t end; | |
| if (size > 0u && start > UINT64_MAX - size) { | |
| CSM_MUTEX_UNLOCK(&g_state.mutex); | |
| return CSM_MASSDATA_ERR_OVERWRITTEN; | |
| } | |
| if (start < oldest || start > g_state.write_total) { | |
| CSM_MUTEX_UNLOCK(&g_state.mutex); | |
| return CSM_MASSDATA_ERR_OVERWRITTEN; | |
| } | |
| end = start + size; | |
| if (end > g_state.write_total) { | |
| CSM_MUTEX_UNLOCK(&g_state.mutex); | |
| return CSM_MASSDATA_ERR_OVERWRITTEN; | |
| } |
| csm_massdata_lazy_init(); | ||
|
|
||
| *data_size_out = (size_t)size; | ||
|
|
||
| if (size > 0u && data == NULL) { | ||
| return CSM_MASSDATA_ERR_INVALID_ARG; | ||
| } | ||
| if (size > (uint64_t)data_cap) { | ||
| return CSM_MASSDATA_ERR_BUFFER_TOO_SMALL; | ||
| } |
There was a problem hiding this comment.
*data_size_out = (size_t)size; is assigned before range validation and can truncate on 32-bit platforms (or when size > SIZE_MAX), potentially returning a misleading required size even when the function returns an error. Consider validating size <= SIZE_MAX first and only then populating data_size_out (or define how values larger than size_t are reported).
|
@copilot apply changes based on the comments in this thread |
Agent-Logs-Url: https://github.com/NEVSTOP-LAB/CSM-MassData-Parameter-Support/sessions/10ae5596-56b1-49ad-8159-dd1e35703e5e Co-authored-by: nevstop <8196752+nevstop@users.noreply.github.com>
已在 cd7227c 中完成所有六处修复:
|
c/_test/vs/c/**topaths-ignorein both LabVIEW workflows so changes underc/no longer trigger them