DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
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 DETERMINISTICESPASYNCWEBSERVER_DET_NUMPARSE_H
20#define DETERMINISTICESPASYNCWEBSERVER_DET_NUMPARSE_H
21
22inline bool det_np_ws(char c)
23{
24 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
25}
26inline bool det_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 det_strtol(const char *s, const char **end)
33{
34 const char *p = s;
35 while (det_np_ws(*p))
36 p++;
37 bool neg = false;
38 if (*p == '+' || *p == '-')
39 neg = (*p++ == '-');
40 const char *ds = p;
41 unsigned long v = 0; // accumulate unsigned: signed overflow (a huge digit run) is UB
42 while (det_np_digit(*p))
43 v = v * 10UL + (unsigned long)(*p++ - '0');
44 if (end)
45 *end = (p == ds) ? s : p;
46 return neg ? (long)(0UL - v) : (long)v; // two's-complement reinterpret, no negation UB
47}
48
49/** @brief Parse a base-10 unsigned long; sets @p end past the digits (or to @p s). */
50inline unsigned long det_strtoul(const char *s, const char **end)
51{
52 const char *p = s;
53 while (det_np_ws(*p))
54 p++;
55 if (*p == '+')
56 p++;
57 const char *ds = p;
58 unsigned long v = 0;
59 while (det_np_digit(*p))
60 v = v * 10UL + (unsigned long)(*p++ - '0');
61 if (end)
62 *end = (p == ds) ? s : p;
63 return v;
64}
65
66// Parse the fractional part after a '.', advancing p and accumulating into val.
67inline void det_strtod_frac(const char *&p, double &val, bool &any)
68{
69 p++; // consume '.'
70 double scale = 1.0;
71 while (det_np_digit(*p))
72 {
73 scale *= 10.0;
74 val += (double)(*p++ - '0') / scale;
75 any = true;
76 }
77}
78
79// Apply a trailing exponent (e[+/-]NNN) to val, advancing p past it.
80inline void det_strtod_exp(const char *&p, double &val)
81{
82 p++; // consume 'e'/'E'
83 bool eneg = false;
84 if (*p == '+' || *p == '-')
85 eneg = (*p++ == '-');
86 int ex = 0;
87 while (det_np_digit(*p))
88 {
89 ex = (ex < 400) ? ex * 10 + (*p - '0') : ex; // clamp: 10^400 overflows the double to inf
90 p++;
91 }
92 double m = 1.0;
93 for (int k = 0; k < ex; k++)
94 m *= 10.0;
95 val = eneg ? val / m : val * m;
96}
97
98/** @brief Parse a double (integer[.frac][e[+/-]exp]); sets @p end (or to @p s if none). */
99inline double det_strtod(const char *s, const char **end)
100{
101 const char *p = s;
102 while (det_np_ws(*p))
103 p++;
104 bool neg = false;
105 if (*p == '+' || *p == '-')
106 neg = (*p++ == '-');
107 bool any = false;
108 double val = 0.0;
109 while (det_np_digit(*p))
110 {
111 val = val * 10.0 + (*p++ - '0');
112 any = true;
113 }
114 if (*p == '.')
115 det_strtod_frac(p, val, any);
116 if (any && (*p == 'e' || *p == 'E'))
117 det_strtod_exp(p, val);
118 if (end)
119 *end = any ? p : s;
120 return neg ? -val : val;
121}
122
123/** @brief Parse a float (integer[.frac][e[+/-]exp]); sets @p end (or to @p s if none). */
124inline float det_strtof(const char *s, const char **end)
125{
126 return (float)det_strtod(s, end); // GGA lat/lon and other sub-meter values need det_strtod's precision
127}
128
129#endif // DETERMINISTICESPASYNCWEBSERVER_DET_NUMPARSE_H
float det_strtof(const char *s, const char **end)
Parse a float (integer[.frac][e[+/-]exp]); sets end (or to s if none).
Definition numparse.h:124
bool det_np_digit(char c)
Definition numparse.h:26
bool det_np_ws(char c)
Definition numparse.h:22
long det_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
void det_strtod_exp(const char *&p, double &val)
Definition numparse.h:80
double det_strtod(const char *s, const char **end)
Parse a double (integer[.frac][e[+/-]exp]); sets end (or to s if none).
Definition numparse.h:99
unsigned long det_strtoul(const char *s, const char **end)
Parse a base-10 unsigned long; sets end past the digits (or to s).
Definition numparse.h:50
void det_strtod_frac(const char *&p, double &val, bool &any)
Definition numparse.h:67