Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ jobs:
cmake --build build --config Release
'

- name: Test
if: matrix.name == 'linux-x64'
uses: tj-actions/docker-run@v2
with:
image: cskburn-${{ matrix.name }}:latest
name: ${{ matrix.name }}-test
options: >-
--platform ${{ matrix.platform }}
--volume ${{ github.workspace }}:/cskburn
--workdir /cskburn
args: |
bash -c '
ctest --test-dir build --output-on-failure
'

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.10)

project(CSKBurnKit)
enable_testing()
include(CTest)

# 未显式指定时默认 Release:Ninja 等单配置生成器下 --config 无效,
# 否则发布产物会是无优化、带 assert 的默认构建
Expand Down
10 changes: 10 additions & 0 deletions cskburn/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,13 @@ target_compile_options(
)

install(TARGETS ${PROJECT_NAME} DESTINATION bin)

if(BUILD_TESTING)
add_executable(
cskburn_utils_test
tests/test_utils.c
src/utils.c
)
target_include_directories(cskburn_utils_test PRIVATE src)
add_test(NAME cskburn_utils COMMAND cskburn_utils_test)
endif()
86 changes: 64 additions & 22 deletions cskburn/src/utils.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "utils.h"

#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
Expand All @@ -19,61 +20,102 @@ read_file(const char *path, uint8_t *buf, uint32_t limit)
return (uint32_t)len;
}

bool
scan_int(const char *str, uint32_t *out)
static bool
scan_int_prefix(const char *str, uint32_t *out, const char **end)
{
if (str == NULL || str[0] == '\0' || str[0] == '-') {
if (str == NULL || out == NULL || end == NULL) {
return false;
}

// base 0 自动识别 0x/0X 十六进制前缀,其余按十进制解析
char *end = NULL;
const char *first = str;
while (isspace((unsigned char)*first)) {
first++;
}
if (*first == '\0' || *first == '-') {
return false;
}

const char *digits = *first == '+' ? first + 1 : first;
int base = digits[0] == '0' && (digits[1] == 'x' || digits[1] == 'X') ? 16 : 10;

// 仅 0x/0X 前缀按十六进制解析,其余按十进制解析
char *parsed_end = NULL;
errno = 0;
unsigned long val = strtoul(str, &end, 0);
if (errno != 0 || end == str || *end != '\0' || val > UINT32_MAX) {
unsigned long val = strtoul(str, &parsed_end, base);
if (errno != 0 || parsed_end == str || val > UINT32_MAX) {
return false;
}

*out = (uint32_t)val;
*end = parsed_end;
return true;
}

bool
scan_addr_size(const char *str, uint32_t *addr, uint32_t *size)
scan_int(const char *str, uint32_t *out)
{
char *split = strstr(str, ":");
if (split == NULL) {
if (out == NULL) {
return false;
}

const char *str_addr = str;
const char *str_size = split + 1;
uint32_t value;
const char *end;
if (!scan_int_prefix(str, &value, &end) || *end != '\0') {
return false;
}

return scan_int(str_addr, addr) && scan_int(str_size, size);
*out = value;
return true;
}

bool
scan_addr_size_name(const char *str, uint32_t *addr, uint32_t *size, const char **name)
scan_addr_size(const char *str, uint32_t *addr, uint32_t *size)
{
char *split;
if (str == NULL || addr == NULL || size == NULL) {
return false;
}

if (name == NULL) {
uint32_t parsed_addr;
uint32_t parsed_size;
const char *end;

if (!scan_int_prefix(str, &parsed_addr, &end) || *end != ':') {
return false;
}
if (!scan_int(end + 1, &parsed_size)) {
return false;
}

if ((split = strstr(str, ":")) == NULL) {
*addr = parsed_addr;
*size = parsed_size;
return true;
}

bool
scan_addr_size_name(const char *str,
uint32_t *addr,
uint32_t *size,
const char **name)
{
if (str == NULL || addr == NULL || size == NULL || name == NULL) {
return false;
}

const char *str_addr = str;
const char *str_size = split + 1;
uint32_t parsed_addr;
uint32_t parsed_size;
const char *end;

if ((split = strstr(str_size, ":")) == NULL) {
if (!scan_int_prefix(str, &parsed_addr, &end) || *end != ':') {
return false;
}
if (!scan_int_prefix(end + 1, &parsed_size, &end) || *end != ':' || end[1] == '\0') {
return false;
}

*name = split + 1;
return scan_int(str_addr, addr) && scan_int(str_size, size);
*addr = parsed_addr;
*size = parsed_size;
*name = end + 1;
return true;
}

void
Expand Down
126 changes: 126 additions & 0 deletions cskburn/tests/test_utils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "utils.h"

#define CHECK(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #expr); \
return false; \
} \
} while (0)

static bool
test_scan_int(void)
{
uint32_t value = 0;

CHECK(scan_int("0", &value) && value == 0);
CHECK(scan_int("4294967295", &value) && value == UINT32_MAX);
CHECK(scan_int("0xabcdef", &value) && value == 0xABCDEF);
CHECK(scan_int("0X100000", &value) && value == 0x100000);
CHECK(scan_int("010", &value) && value == 10);
CHECK(scan_int(" 123", &value) && value == 123);

CHECK(!scan_int(NULL, &value));
CHECK(!scan_int("1", NULL));
CHECK(!scan_int("", &value));
CHECK(!scan_int("-1", &value));
CHECK(!scan_int(" -1", &value));
CHECK(!scan_int("4294967296", &value));
CHECK(!scan_int("0x", &value));
CHECK(!scan_int("123junk", &value));
CHECK(!scan_int("123:456", &value));

return true;
}

static bool
test_scan_addr_size(void)
{
uint32_t addr = 0;
uint32_t size = 0;

CHECK(scan_addr_size("0x0:4194304", &addr, &size));
CHECK(addr == 0 && size == 4194304);
CHECK(scan_addr_size("0X100000:0x100000", &addr, &size));
CHECK(addr == 0x100000 && size == 0x100000);
CHECK(scan_addr_size("4096:8192", &addr, &size));
CHECK(addr == 4096 && size == 8192);
CHECK(scan_addr_size("000010:000020", &addr, &size));
CHECK(addr == 10 && size == 20);

CHECK(!scan_addr_size(NULL, &addr, &size));
CHECK(!scan_addr_size("1:2", NULL, &size));
CHECK(!scan_addr_size("1:2", &addr, NULL));
CHECK(!scan_addr_size("1", &addr, &size));
CHECK(!scan_addr_size(":1", &addr, &size));
CHECK(!scan_addr_size("1:", &addr, &size));
CHECK(!scan_addr_size("1:2:3", &addr, &size));
CHECK(!scan_addr_size("1:-2", &addr, &size));
CHECK(!scan_addr_size("4294967296:1", &addr, &size));
CHECK(!scan_addr_size("1:4294967296", &addr, &size));

addr = 11;
size = 22;
CHECK(!scan_addr_size("1:invalid", &addr, &size));
CHECK(addr == 11 && size == 22);

return true;
}

static bool
test_scan_addr_size_name(void)
{
uint32_t addr = 0;
uint32_t size = 0;
const char *name = NULL;

CHECK(scan_addr_size_name("0x0:4194304:factory.bin", &addr, &size, &name));
CHECK(addr == 0 && size == 4194304 && strcmp(name, "factory.bin") == 0);
CHECK(scan_addr_size_name("0X100000:0x6EC00:C:\\firmware\\dsp.bin", &addr, &size,
&name));
CHECK(addr == 0x100000 && size == 0x6EC00);
CHECK(strcmp(name, "C:\\firmware\\dsp.bin") == 0);
CHECK(scan_addr_size_name("1:2:output:part.bin", &addr, &size, &name));
CHECK(strcmp(name, "output:part.bin") == 0);

char long_arg[700] = "0:1:";
memset(long_arg + 4, 'a', sizeof(long_arg) - 5);
long_arg[sizeof(long_arg) - 1] = '\0';
CHECK(scan_addr_size_name(long_arg, &addr, &size, &name));
CHECK(name == long_arg + 4);
CHECK(strlen(name) == sizeof(long_arg) - 5);

CHECK(!scan_addr_size_name(NULL, &addr, &size, &name));
CHECK(!scan_addr_size_name("1:2:file", NULL, &size, &name));
CHECK(!scan_addr_size_name("1:2:file", &addr, NULL, &name));
CHECK(!scan_addr_size_name("1:2:file", &addr, &size, NULL));
CHECK(!scan_addr_size_name("1:2", &addr, &size, &name));
CHECK(!scan_addr_size_name(":2:file", &addr, &size, &name));
CHECK(!scan_addr_size_name("1::file", &addr, &size, &name));
CHECK(!scan_addr_size_name("1:2:", &addr, &size, &name));
CHECK(!scan_addr_size_name("1:invalid:file", &addr, &size, &name));
CHECK(!scan_addr_size_name("4294967296:1:file", &addr, &size, &name));

addr = 11;
size = 22;
name = "unchanged";
CHECK(!scan_addr_size_name("1:invalid:file", &addr, &size, &name));
CHECK(addr == 11 && size == 22 && strcmp(name, "unchanged") == 0);

return true;
}

int
main(void)
{
if (!test_scan_int() || !test_scan_addr_size() || !test_scan_addr_size_name()) {
return 1;
}
puts("utils parser tests passed");
return 0;
}
Loading