Skip to content

Commit 5b37b12

Browse files
committed
Lib: Util: String conversion API
Adds string conversion API. Implements basic string to parameter convertion functions. Signed-off-by: Anders Storrø <[email protected]>
1 parent ca217fe commit 5b37b12

File tree

8 files changed

+473
-0
lines changed

8 files changed

+473
-0
lines changed

include/sys/string_conv.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) 2022 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_SYS_STRING_CONV_H__
8+
#define ZEPHYR_INCLUDE_SYS_STRING_CONV_H__
9+
10+
#include <stdint.h>
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
/**
17+
* @brief Convert string to long param.
18+
*
19+
* @note On failure the passed value reference will not be altered.
20+
*
21+
* @param str Input string
22+
* @param val Converted value
23+
*
24+
* @return 0 on success.
25+
* @return -EINVAL on invalid string input.
26+
* @return -ERANGE if numeric string input is to large to convert.
27+
*/
28+
int string_conv_str2long(const char *str, long *val);
29+
30+
/**
31+
* @brief Convert string to unsigned long param.
32+
*
33+
* @note On failure the passed value reference will not be altered.
34+
*
35+
* @param str Input string
36+
* @param val Converted value
37+
*
38+
* @return 0 on success.
39+
* @return -EINVAL on invalid string input.
40+
* @return -ERANGE if numeric string input is to large to convert.
41+
*/
42+
int string_conv_str2ulong(const char *str, unsigned long *val);
43+
44+
/**
45+
* @brief Convert string to double param.
46+
*
47+
* @note On failure the passed value reference will not be altered.
48+
*
49+
* @param str Input string
50+
* @param val Converted value
51+
*
52+
* @return 0 on success.
53+
* @return -EINVAL on invalid string input.
54+
* @return -ERANGE if numeric string input is to large to convert.
55+
*/
56+
int string_conv_str2dbl(const char *str, double *val);
57+
58+
#ifdef __cplusplus
59+
}
60+
#endif
61+
62+
#endif /* ZEPHYR_INCLUDE_SYS_STRING_CONV_H__ */

lib/util/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# SPDX-License-Identifier: Apache-2.0
22

33
add_subdirectory_ifdef(CONFIG_FNMATCH fnmatch)
4+
add_subdirectory(string_conv)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) 2022 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
# #
5+
6+
zephyr_sources(string_conv.c)

lib/util/string_conv/string_conv.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright (c) 2022 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <ctype.h>
8+
#include <string.h>
9+
#include <stdlib.h>
10+
#include <errno.h>
11+
12+
static size_t whitespace_trim(char *out, size_t len, const char *str)
13+
{
14+
if (len == 0) {
15+
return 0;
16+
}
17+
18+
const char *end;
19+
size_t out_size;
20+
21+
while (str[0] == ' ') {
22+
str++;
23+
}
24+
25+
if (*str == 0) {
26+
*out = 0;
27+
return 1;
28+
}
29+
30+
end = str + strlen(str) - 1;
31+
while (end > str && (end[0] == ' ')) {
32+
end--;
33+
}
34+
end++;
35+
36+
out_size = (end - str) + 1;
37+
38+
if (out_size > len) {
39+
return 0;
40+
}
41+
42+
memcpy(out, str, out_size - 1);
43+
out[out_size - 1] = 0;
44+
45+
return out_size;
46+
}
47+
48+
int string_conv_str2long(char *str, long *val)
49+
{
50+
char trimmed_buf[12] = { 0 };
51+
size_t len = whitespace_trim(trimmed_buf, sizeof(trimmed_buf), str);
52+
53+
if (len < 2) {
54+
return -EINVAL;
55+
}
56+
57+
long temp_val;
58+
int idx = 0;
59+
60+
if ((trimmed_buf[0] == '-') || (trimmed_buf[0] == '+')) {
61+
idx++;
62+
}
63+
64+
for (int i = idx; i < (len - 1); i++) {
65+
if (!isdigit((int)trimmed_buf[i])) {
66+
return -EINVAL;
67+
}
68+
}
69+
70+
errno = 0;
71+
temp_val = strtol(trimmed_buf, NULL, 10);
72+
73+
if (errno == ERANGE) {
74+
return -ERANGE;
75+
}
76+
77+
*val = temp_val;
78+
return 0;
79+
}
80+
81+
int string_conv_str2ulong(char *str, unsigned long *val)
82+
{
83+
char trimmed_buf[12] = { 0 };
84+
size_t len = whitespace_trim(trimmed_buf, sizeof(trimmed_buf), str);
85+
86+
if (len < 2) {
87+
return -EINVAL;
88+
}
89+
90+
unsigned long temp_val;
91+
int idx = 0;
92+
93+
if (trimmed_buf[0] == '+') {
94+
idx++;
95+
}
96+
97+
for (int i = idx; i < (len - 1); i++) {
98+
if (!isdigit((int)trimmed_buf[i])) {
99+
return -EINVAL;
100+
}
101+
}
102+
103+
errno = 0;
104+
temp_val = strtoul(trimmed_buf, NULL, 10);
105+
106+
if (errno == ERANGE) {
107+
return -ERANGE;
108+
}
109+
110+
*val = temp_val;
111+
return 0;
112+
}
113+
114+
int string_conv_str2dbl(const char *str, double *val)
115+
{
116+
char trimmed_buf[22] = { 0 };
117+
long decimal;
118+
unsigned long frac;
119+
double frac_dbl;
120+
int err = 0;
121+
122+
size_t len = whitespace_trim(trimmed_buf, sizeof(trimmed_buf), str);
123+
124+
if (len < 2) {
125+
return -EINVAL;
126+
}
127+
128+
int comma_idx = strcspn(trimmed_buf, ".");
129+
int frac_len = strlen(trimmed_buf + comma_idx + 1);
130+
131+
/* Covers corner case "." input */
132+
if (strlen(trimmed_buf) < 2 && trimmed_buf[comma_idx] != 0) {
133+
return -EINVAL;
134+
}
135+
136+
trimmed_buf[comma_idx] = 0;
137+
138+
/* Avoid fractional overflow by losing one precision point */
139+
if (frac_len > 9) {
140+
trimmed_buf[comma_idx + 10] = 0;
141+
frac_len = 9;
142+
}
143+
144+
/* Avoid doing str2long if decimal part is empty */
145+
if (trimmed_buf[0] == '\0') {
146+
decimal = 0;
147+
} else {
148+
err = string_conv_str2long(trimmed_buf, &decimal);
149+
150+
if (err) {
151+
return err;
152+
}
153+
}
154+
155+
/* Avoid doing str2ulong if fractional part is empty */
156+
if ((trimmed_buf + comma_idx + 1)[0] == '\0') {
157+
frac = 0;
158+
} else {
159+
err = string_conv_str2ulong(trimmed_buf + comma_idx + 1, &frac);
160+
161+
if (err) {
162+
return err;
163+
}
164+
}
165+
166+
frac_dbl = (double)frac;
167+
168+
for (int i = 0; i < frac_len; i++) {
169+
frac_dbl /= 10;
170+
}
171+
172+
*val = (trimmed_buf[0] == '-') ? ((double)decimal - frac_dbl) :
173+
((double)decimal + frac_dbl);
174+
175+
return err;
176+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(string_conv)
6+
7+
FILE(GLOB app_sources src/*.c)
8+
target_sources(app PRIVATE ${app_sources})

tests/lib/string_conv/prj.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CONFIG_ZTEST=y

0 commit comments

Comments
 (0)