ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
numparse.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file numparse.h
6 * @brief Tiny no-stdlib base-10 number parsers (strtol/strtoul/strtof replacements).
7 *
8 * This library does not pull in `<stdlib.h>` - no heap, no locale, no bloat. These
9 * header-only inline helpers parse a base-10 integer / unsigned / float from a
10 * string, mirroring the `strtol`-family `endptr` contract: skip leading
11 * whitespace, accept an optional sign, consume digits, and (optionally) report
12 * where parsing stopped. If no digit is converted, `*end` is set to the original
13 * pointer (so callers can detect "no number") and the result is 0.
14 *
15 * @author Douglas Quigg (dstroy0)
16 * @date 2026
17 */
18
19#ifndef PROTOCORE_NUMPARSE_H
20#define PROTOCORE_NUMPARSE_H
21
22inline bool pc_np_ws(char c)
23{
24 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
25}
26inline bool pc_np_digit(char c)
27{
28 return c >= '0' && c <= '9';
29}
30
31/** @brief Parse a base-10 long; sets @p end past the digits (or to @p s if none). */
32inline long pc_strtol(const char *s, const char **end)
33{
34 const char *p = s;
35 while (pc_np_ws(*p))
36 {
37 p++;
38 }
39 bool neg = false;
40 if (*p == '+' || *p == '-')
41 {
42 neg = (*p++ == '-');
43 }
44 const char *ds = p;
45 unsigned long v = 0; // accumulate unsigned: signed overflow (a huge digit run) is UB
46 while (pc_np_digit(*p))
47 {
48 v = v * 10UL + (unsigned long)(*p++ - '0');
49 }
50 if (end)
51 {
52 *end = (p == ds) ? s : p;
53 }
54 return neg ? (long)(0UL - v) : (long)v; // two's-complement reinterpret, no negation UB
55}
56
57/** @brief Parse a base-10 unsigned long; sets @p end past the digits (or to @p s). */
58inline unsigned long pc_strtoul(const char *s, const char **end)
59{
60 const char *p = s;
61 while (pc_np_ws(*p))
62 {
63 p++;
64 }
65 if (*p == '+')
66 {
67 p++;
68 }
69 const char *ds = p;
70 unsigned long v = 0;
71 while (pc_np_digit(*p))
72 {
73 v = v * 10UL + (unsigned long)(*p++ - '0');
74 }
75 if (end)
76 {
77 *end = (p == ds) ? s : p;
78 }
79 return v;
80}
81
82// Parse the fractional part after a '.', advancing p and accumulating into val.
83inline void pc_strtod_frac(const char *&p, double &val, bool &any)
84{
85 p++; // consume '.'
86 double scale = 1.0;
87 while (pc_np_digit(*p))
88 {
89 scale *= 10.0;
90 val += (double)(*p++ - '0') / scale;
91 any = true;
92 }
93}
94
95// Apply a trailing exponent (e[+/-]NNN) to val, advancing p past it.
96inline void pc_strtod_exp(const char *&p, double &val)
97{
98 p++; // consume 'e'/'E'
99 bool eneg = false;
100 if (*p == '+' || *p == '-')
101 {
102 eneg = (*p++ == '-');
103 }
104 int ex = 0;
105 while (pc_np_digit(*p))
106 {
107 ex = (ex < 400) ? ex * 10 + (*p - '0') : ex; // clamp: 10^400 overflows the double to inf
108 p++;
109 }
110 double m = 1.0;
111 for (int k = 0; k < ex; k++)
112 {
113 m *= 10.0;
114 }
115 val = eneg ? val / m : val * m;
116}
117
118/** @brief Parse a double (integer[.frac][e[+/-]exp]); sets @p end (or to @p s if none). */
119inline double pc_strtod(const char *s, const char **end)
120{
121 const char *p = s;
122 while (pc_np_ws(*p))
123 {
124 p++;
125 }
126 bool neg = false;
127 if (*p == '+' || *p == '-')
128 {
129 neg = (*p++ == '-');
130 }
131 bool any = false;
132 double val = 0.0;
133 while (pc_np_digit(*p))
134 {
135 val = val * 10.0 + (*p++ - '0');
136 any = true;
137 }
138 if (*p == '.')
139 {
140 pc_strtod_frac(p, val, any);
141 }
142 if (any && (*p == 'e' || *p == 'E'))
143 {
144 pc_strtod_exp(p, val);
145 }
146 if (end)
147 {
148 *end = any ? p : s;
149 }
150 return neg ? -val : val;
151}
152
153/** @brief Parse a float (integer[.frac][e[+/-]exp]); sets @p end (or to @p s if none). */
154inline float pc_strtof(const char *s, const char **end)
155{
156 return (float)pc_strtod(s, end); // GGA lat/lon and other sub-meter values need pc_strtod's precision
157}
158
159#endif // PROTOCORE_NUMPARSE_H
bool pc_np_ws(char c)
Definition numparse.h:22
void pc_strtod_exp(const char *&p, double &val)
Definition numparse.h:96
unsigned long pc_strtoul(const char *s, const char **end)
Parse a base-10 unsigned long; sets end past the digits (or to s).
Definition numparse.h:58
long pc_strtol(const char *s, const char **end)
Parse a base-10 long; sets end past the digits (or to s if none).
Definition numparse.h:32
float pc_strtof(const char *s, const char **end)
Parse a float (integer[.frac][e[+/-]exp]); sets end (or to s if none).
Definition numparse.h:154
bool pc_np_digit(char c)
Definition numparse.h:26
double pc_strtod(const char *s, const char **end)
Parse a double (integer[.frac][e[+/-]exp]); sets end (or to s if none).
Definition numparse.h:119
void pc_strtod_frac(const char *&p, double &val, bool &any)
Definition numparse.h:83