• R/O
  • SSH
  • HTTPS

chibios: Commit


Commit MetaInfo

Revision13908 (tree)
Time2020-11-15 20:30:34
Authorgdisirio

Log Message

Added chscanf() and buffered streams, contributed by Alex Lewontin.

Change Summary

Incremental Difference

--- trunk/os/hal/include/hal_streams.h (revision 13907)
+++ trunk/os/hal/include/hal_streams.h (revision 13908)
@@ -151,6 +151,68 @@
151151 #define streamGet(ip) ((ip)->vmt->get(ip))
152152 /** @} */
153153
154+/**
155+ * @brief @p BaseBufferedStream specific methods.
156+ */
157+#define _base_buffered_stream_methods \
158+ _base_sequential_stream_methods \
159+ /* Channel unget method */ \
160+ msg_t (*unget)(void *instance, uint8_t b);
161+
162+/**
163+ * @brief @p BaseBufferedStream specific data.
164+ * @note It is empty because @p BaseBufferedStream is only an interface
165+ * without implementation.
166+ */
167+#define _base_buffered_stream_data \
168+ _base_sequential_stream_data
169+
170+/**
171+ * @extends BaseSequentialStreamVMT
172+ *
173+ * @brief @p BaseBufferedStream virtual methods table.
174+ */
175+struct BaseBufferedStreamVMT {
176+ _base_buffered_stream_methods
177+};
178+
179+/**
180+ * @extends BaseSequentialStream
181+ *
182+ * @brief Buffered stream class.
183+ * @details This class @p extends BaseSequentialStream to represent a generic
184+ * blocking buffered sequential data stream.
185+ */
186+typedef struct {
187+ /** @brief Virtual Methods Table. */
188+ const struct BaseBufferedStreamVMT *vmt;
189+ _base_buffered_stream_data
190+} BaseBufferedStream;
191+
192+/**
193+ * @name Macro Functions (BaseBufferedStream)
194+ * @{
195+ */
196+/**
197+ * @brief Buffered Stream unget.
198+ * @details This function replaces a byte value to a stream. streamUnget
199+ * only guarantees a single byte can be replaced, and multiple
200+ * calls without intervening calls to streamGet or streamRead may fail
201+ *
202+ * @param[in] ip pointer to a @p BaseBufferedStream or derived class
203+ * @param[in] b the byte value to be written to the channel
204+ *
205+ * @post
206+ *
207+ * @return The operation status.
208+ * @retval STM_OK if the operation succeeded.
209+ * @retval STM_RESET if the operation failed
210+ *
211+ * @api
212+ */
213+#define streamUnget(ip, b) ((ip)->vmt->unget(ip, b))
214+/** @} */
215+
154216 #endif /* HAL_STREAMS_H */
155217
156218 /** @} */
--- trunk/os/hal/lib/streams/bufstreams.c (nonexistent)
+++ trunk/os/hal/lib/streams/bufstreams.c (revision 13908)
@@ -0,0 +1,123 @@
1+/*
2+ ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+*/
16+
17+/*
18+ This file was contributed by Alex Lewontin.
19+ */
20+
21+/**
22+ * @file bufstreams.c
23+ * @brief Buffered streams code.
24+ *
25+ * @addtogroup HAL_BUFFERED_STREAMS
26+ * @details Wrapper for BaseSequentialStreams that allows some ungetting.
27+ * @{
28+ */
29+
30+#include <string.h>
31+
32+#include "hal.h"
33+#include "bufstreams.h"
34+
35+/*===========================================================================*/
36+/* Driver local definitions. */
37+/*===========================================================================*/
38+
39+/*===========================================================================*/
40+/* Driver exported variables. */
41+/*===========================================================================*/
42+
43+/*===========================================================================*/
44+/* Driver local variables. */
45+/*===========================================================================*/
46+
47+/*===========================================================================*/
48+/* Driver local functions. */
49+/*===========================================================================*/
50+
51+static size_t _writes(void *ip, const uint8_t *bp, size_t n) {
52+ BufferedStreamAdapter *bsap = ip;
53+ return bsap->bssp->vmt->write(bsap->bssp, bp, n);
54+}
55+
56+static size_t _reads(void *ip, uint8_t *bp, size_t n) {
57+ BufferedStreamAdapter* bsap = ip;
58+ size_t buffered;
59+
60+ if (n < (BUFSTREAM_BUFFER_SIZE - bsap->ndx)) {
61+ buffered = n;
62+ } else {
63+ buffered = BUFSTREAM_BUFFER_SIZE - bsap->ndx;
64+ }
65+
66+ memcpy(bp, bsap->buffer + (BUFSTREAM_BUFFER_SIZE - buffered), buffered);
67+ bsap->ndx += buffered;
68+
69+ return buffered + bsap->bssp->vmt->read(bsap->bssp, bp + buffered, n - buffered);
70+}
71+
72+static msg_t _put(void *ip, uint8_t b) {
73+ BufferedStreamAdapter* bsap = ip;
74+ return bsap->bssp->vmt->put(bsap->bssp, b);
75+}
76+
77+static msg_t _get(void *ip) {
78+ BufferedStreamAdapter* bsap = ip;
79+
80+ if (bsap->ndx == BUFSTREAM_BUFFER_SIZE) {
81+ return bsap->bssp->vmt->get(bsap->bssp);
82+ }
83+
84+ return bsap->buffer[bsap->ndx++];
85+}
86+
87+static msg_t _unget(void* ip, uint8_t b) {
88+ BufferedStreamAdapter* bsap = ip;
89+
90+ if (((int8_t)b == STM_RESET) || (bsap->ndx == 0)) {
91+ return STM_RESET;
92+ }
93+
94+ bsap->buffer[--(bsap->ndx)] = b;
95+
96+ return b;
97+}
98+
99+static const struct BufferedStreamAdapterVMT vmt = {
100+ (size_t)0, _writes, _reads, _put, _get, _unget
101+};
102+
103+/*===========================================================================*/
104+/* Driver exported functions. */
105+/*===========================================================================*/
106+
107+/**
108+ * @brief Buffered stream adapter object initialization.
109+ *
110+ * @param[out] bsap pointer to the @p BufferedStream object to be
111+ * initialized
112+ * @param[in] bssp pointer to a @p BaseSequentialStream fulfilling
113+ * object to be wrapped
114+ */
115+void bsaObjectInit(BufferedStreamAdapter *bsap, BaseSequentialStream* bssp) {
116+
117+ bsap->vmt = &vmt;
118+ bsap->bssp = bssp;
119+ memset(bsap->buffer, STM_RESET, BUFSTREAM_BUFFER_SIZE);
120+ bsap->ndx = BUFSTREAM_BUFFER_SIZE;
121+}
122+
123+/** @} */
--- trunk/os/hal/lib/streams/bufstreams.h (nonexistent)
+++ trunk/os/hal/lib/streams/bufstreams.h (revision 13908)
@@ -0,0 +1,103 @@
1+/*
2+ ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+*/
16+
17+/*
18+ This file was contributed by Alex Lewontin.
19+ */
20+
21+/**
22+ * @file bufstreams.h
23+ * @brief Buffered streams structures and macros.
24+
25+ * @addtogroup HAL_BUFFERED_STREAMS
26+ * @{
27+ */
28+
29+#ifndef BUFSTREAMS_H
30+#define BUFSTREAMS_H
31+
32+/*===========================================================================*/
33+/* Driver constants. */
34+/*===========================================================================*/
35+
36+/**
37+ * @brief Buffer size for unget.
38+ */
39+#if !defined(BUFSTREAM_BUFFER_SIZE) || defined(__DOXYGEN__)
40+#define BUFSTREAM_BUFFER_SIZE 1
41+#endif
42+
43+/*===========================================================================*/
44+/* Driver pre-compile time settings. */
45+/*===========================================================================*/
46+
47+/*===========================================================================*/
48+/* Derived constants and error checks. */
49+/*===========================================================================*/
50+
51+/*===========================================================================*/
52+/* Driver data structures and types. */
53+/*===========================================================================*/
54+
55+/**
56+ * @brief @p BufferedStreamAdapter specific data.
57+ */
58+#define _buffered_stream_adapter_data \
59+ _base_buffered_stream_data \
60+ /* Pointer to a wrapped BaseSequentialStream object */ \
61+ BaseSequentialStream* bssp; \
62+ /* Stream buffer */ \
63+ uint8_t buffer[BUFSTREAM_BUFFER_SIZE]; \
64+ /* Index in the stream buffer */ \
65+ size_t ndx;
66+
67+/**
68+ * @brief @p BufferedStreamAdapter virtual methods table.
69+ */
70+struct BufferedStreamAdapterVMT {
71+ _base_buffered_stream_methods
72+};
73+
74+/**
75+ * @extends BufferedStream
76+ *
77+ * @brief Buffered stream adapter object.
78+ */
79+typedef struct {
80+ /** @brief Virtual Methods Table.*/
81+ const struct BufferedStreamAdapterVMT *vmt;
82+ _buffered_stream_adapter_data
83+} BufferedStreamAdapter;
84+
85+/*===========================================================================*/
86+/* Driver macros. */
87+/*===========================================================================*/
88+
89+/*===========================================================================*/
90+/* External declarations. */
91+/*===========================================================================*/
92+
93+#ifdef __cplusplus
94+extern "C" {
95+#endif
96+ void bsaObjectInit(BufferedStreamAdapter *bsap, BaseSequentialStream* bssp);
97+#ifdef __cplusplus
98+}
99+#endif
100+
101+#endif /* MEMSTREAMS_H */
102+
103+/** @} */
--- trunk/os/hal/lib/streams/chscanf.c (nonexistent)
+++ trunk/os/hal/lib/streams/chscanf.c (revision 13908)
@@ -0,0 +1,795 @@
1+/*
2+ ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+*/
16+
17+/*
18+ This file was contributed by Alex Lewontin.
19+ */
20+
21+/**
22+ * @file chscanf.c
23+ * @brief Mini scanf-like functionality.
24+ *
25+ * @addtogroup HAL_CHSCANF
26+ * @details Mini scanf-like functionality.
27+ * @{
28+ */
29+
30+#include <ctype.h>
31+
32+#include "hal.h"
33+#include "chscanf.h"
34+#include "memstreams.h"
35+
36+static long sym_to_val(char sym, int base)
37+{
38+ sym = tolower(sym);
39+ if (sym <= '7' && sym >= '0') {
40+ return sym - '0';
41+ }
42+ switch (base) {
43+ case 16:
44+ if (sym <= 'f' && sym >= 'a') {
45+ return (sym - 'a' + 0xa);
46+ }
47+ /* fallthrough */
48+ case 10:
49+ if (sym == '8') {
50+ return 8;
51+ }
52+ if (sym == '9') {
53+ return 9;
54+ }
55+ /* fallthrough */
56+ default:
57+ return -1;
58+ }
59+}
60+
61+#if CHSCANF_USE_FLOAT
62+
63+/* Custom mixed-type power function. The internal promotion of the result to a double
64+ allows for a greater dynamic range than integral types. This function is mostly for
65+ simplicity, to allow us to do floating point math without either requiring any
66+ libc linkages, or actually having to write floating point algorithms ourselves */
67+static inline double ch_mpow(double x, unsigned long y)
68+{
69+ double res = 1;
70+
71+ do {
72+ if (y & 1) {
73+ res *= x;
74+ }
75+ x *= x;
76+ } while (y >>= 1);
77+
78+ return res;
79+}
80+
81+#endif
82+
83+/**
84+ * @brief System formatted input function.
85+ * @details This function implements a minimal @p vscanf()-like functionality
86+ * with input on a @p BaseSequentialStream.
87+ * The general parameters format is: %[*][width][l|L]p
88+ * The following parameter types (p) are supported:
89+ * - <b>x</b> hexadecimal integer.
90+ * - <b>X</b> hexadecimal long.
91+ * - <b>o</b> octal integer.
92+ * - <b>O</b> octal long.
93+ * - <b>d</b> decimal signed integer.
94+ * - <b>D</b> decimal signed long.
95+ * - <b>u</b> decimal unsigned integer.
96+ * - <b>U</b> decimal unsigned long.
97+ * - <b>c</b> character.
98+ * - <b>s</b> string.
99+ * .
100+ *
101+ * @param[in] chp pointer to a @p BufferedStream implementing object
102+ * @param[in] fmt formatting string
103+ * @param[in] ap list of parameters
104+ * @return The number parameters in ap that have been successfully
105+ * filled. This does not conform to the standard in that if
106+ * a failure (either matching or input) occurs before any
107+ * parameters are assigned, the function will return 0.
108+ *
109+ * @api
110+ */
111+int chvscanf(BaseBufferedStream *chp, const char *fmt, va_list ap)
112+{
113+ char f;
114+ msg_t c;
115+ int width, base, i;
116+ int n = 0;
117+ void* buf;
118+ bool is_long, is_signed, is_positive;
119+ long vall, digit;
120+#if CHSCANF_USE_FLOAT
121+ long exp;
122+ double valf;
123+ char exp_char;
124+ int exp_base;
125+ bool exp_is_positive, initial_digit;
126+ char* match;
127+ int fixed_point;
128+#endif
129+
130+ /* Peek the first character of the format string. If it is null,
131+ we don't even need to take any input, just return 0 */
132+ f = *fmt++;
133+ if (f == 0) {
134+ return n;
135+ }
136+
137+ /* Otherwise, get the first character from the input stream before we loop for the first time
138+ (no peek function for the stream means an extra character is taken out every iteration of the
139+ loop, so each loop iteration uses the value of c from the last one. However, the first iteration
140+ has no value to work with, so we initialize it here) */
141+ c = streamGet(chp);
142+
143+ while (c != STM_RESET && f != 0) {
144+
145+ /* There are 3 options for f:
146+ - whitespace (take and discard as much contiguous whitespace as possible)
147+ - a non-whitespace, non-control sequence character (must 1:1 match)
148+ - a %, which indicates the beginning of a control sequence
149+ */
150+
151+ if (isspace(f)) {
152+ while (isspace(c)) {
153+ c = streamGet(chp);
154+ }
155+ f = *fmt++;
156+ continue;
157+ }
158+
159+ if (f != '%') {
160+ if (f != c) {
161+ break;
162+ } else {
163+ c = streamGet(chp);
164+ f = *fmt++;
165+ continue;
166+ }
167+ }
168+
169+ /* So we have a formatting token... probably */
170+ f = *fmt++;
171+ /* Special case: a %% is equivalent to a '%' literal */
172+ if (f == '%') {
173+ if (f != c) {
174+ break;
175+ } else {
176+ c = streamGet(chp);
177+ f = *fmt++;
178+ continue;
179+ }
180+ }
181+
182+ if (f == '*') {
183+ buf = NULL;
184+ f = *fmt++;
185+ } else {
186+ buf = va_arg(ap, void*);
187+ }
188+
189+ /* Parse the optional width specifier */
190+ width = 0;
191+ while (isdigit(f)) {
192+ width = (width * 10) + (f - '0');
193+ f = *fmt++;
194+ }
195+
196+ if (!width) {
197+ width = -1;
198+ }
199+
200+ /* Parse the optional length specifier */
201+ if (f == 'l' || f == 'L') {
202+ is_long = true;
203+ f = *fmt++;
204+ } else {
205+ is_long = isupper(f);
206+ }
207+
208+ is_positive = true;
209+ is_signed = true;
210+ base = 10;
211+
212+ switch (f) {
213+
214+ case 'c':
215+ /* Not supporting wchar_t, is_long is just ignored */
216+ if (width == 0) {
217+ width = 1;
218+ }
219+ for (i = 0; i < width; ++i) {
220+ if (buf) {
221+ ((char*)buf)[i] = c;
222+ }
223+ c = streamGet(chp);
224+ if (c == STM_RESET) {
225+ return n;
226+ }
227+ }
228+ ++n;
229+ f = *fmt++;
230+ continue;
231+
232+ case 's':
233+ /* S specifier discards leading whitespace */
234+ while (isspace(c)) {
235+ c = streamGet(chp);
236+ if (c == STM_RESET) {
237+ return n;
238+ }
239+ }
240+ /* Not supporting wchar_t, is_long is just ignored */
241+ if (width == 0) {
242+ width = -1;
243+ }
244+ for (i = 0; i < width; ++i) {
245+
246+ if (isspace(c)) {
247+ if (buf) {
248+ ((char*)buf)[i] = 0;
249+ }
250+
251+ break;
252+ }
253+
254+ if (buf) {
255+ ((char*)buf)[i] = c;
256+ }
257+ c = streamGet(chp);
258+ if (c == STM_RESET) {
259+ return n;
260+ }
261+ }
262+
263+ if (width != -1) {
264+ if (buf) {
265+ ((char*)buf)[width] = 0;
266+ }
267+ }
268+ ++n;
269+ f = *fmt++;
270+ continue;
271+
272+#if CHSCANF_USE_FLOAT
273+ case 'f':
274+ valf = -1;
275+ exp_char = 'e';
276+ exp_base = 10;
277+ fixed_point = 0;
278+ initial_digit = false;
279+ while (isspace(c)) {
280+ c = streamGet(chp);
281+ }
282+
283+ if (c == '+') {
284+ if (--width == 0) {
285+ return n;
286+ }
287+ c = streamGet(chp);
288+
289+ } else if (c == '-') {
290+ if (--width == 0) {
291+ return n;
292+ }
293+ is_positive = false;
294+ c = streamGet(chp);
295+ }
296+
297+ /* Special cases: a float can be INF(INITY) or NAN. As a note about this behavior:
298+ this consumes "the longest sequence of input characters which does not exceed any
299+ specified field width and which is, or is a prefix of, a matching input sequence" (from
300+ the C99 standard). Therefore, if a '%f' format token gets the input 'INFINITxyx',
301+ it will consume the 'INFINIT', leaving 'xyz' in the stream. Similarly, if it gets
302+ 'NAxyz', it will consume the 'NA', leaving 'xyz' in the stream.
303+
304+ Given that it seems a little odd to accept a short version and a long version, but not
305+ a version in between that contains the short version but isn't long enough to be the
306+ long version, This implementation is fairly permissive, and will accept anything from
307+ 'INF' to 'INFINITY', case insensative, (e.g. 'INF', 'INfiN', 'INFit', or 'infinity')
308+ as a valid token meaning INF. It will not, however, accept less than 'INF' or 'NAN' as
309+ a valid token (so the above example 'NAxyz' would consume the 'NA', but not recognize it
310+ as signifying NaN)
311+ */
312+
313+ if (tolower(c) == 'n') {
314+ c = streamGet(chp);
315+
316+ match = "an";
317+ while (*match != 0) {
318+ if (*match != tolower(c)) {
319+ streamUnget(chp, c);
320+ return n;
321+ }
322+ if (--width == 0) {
323+ streamUnget(chp, c);
324+ return n;
325+ }
326+ ++match;
327+ c = streamGet(chp);
328+ }
329+
330+ valf = NAN;
331+ goto float_common;
332+ }
333+
334+ if (tolower(c) == 'i') {
335+ c = streamGet(chp);
336+
337+ match = "nf";
338+ while (*match != 0) {
339+ if (*match != tolower(c)) {
340+ streamUnget(chp, c);
341+ return n;
342+ }
343+ ++match;
344+ c = streamGet(chp);
345+ if (--width == 0) {
346+ streamUnget(chp, c);
347+ return n;
348+ }
349+ }
350+
351+ valf = INFINITY;
352+
353+ match = "inity";
354+ while (*match != 0) {
355+ if (*match != tolower(c)) {
356+ break;
357+ }
358+ ++match;
359+ if (--width == 0) {
360+ break;
361+ }
362+ c = streamGet(chp);
363+ }
364+
365+ goto float_common;
366+ }
367+
368+ if (c == '0') {
369+ c = streamGet(chp);
370+ if (--width == 0) {
371+ valf = 0;
372+ goto float_common;
373+ }
374+
375+ if (c == 'x' || c == 'X') {
376+ base = 16;
377+ exp_char = 'p';
378+ exp_base = 2;
379+ c = streamGet(chp);
380+ if (--width == 0) {
381+ streamUnget(chp, c);
382+ return n;
383+ }
384+ } else {
385+ valf = 0;
386+ }
387+ }
388+
389+ if (sym_to_val(c, base) != -1) {
390+ valf = 0;
391+ }
392+
393+ while (width--) {
394+ digit = sym_to_val(c, base);
395+ if (digit == -1) {
396+ break;
397+ }
398+ valf = (valf * base) + (double)digit;
399+ c = streamGet(chp);
400+ }
401+
402+ if (c == '.') {
403+ c = streamGet(chp);
404+
405+ while (width--) {
406+ digit = sym_to_val(c, base);
407+ if (digit == -1) {
408+ break;
409+ }
410+ if (valf == -1) {
411+ valf = 0;
412+ }
413+ valf = (valf * base) + (double)digit;
414+ ++fixed_point;
415+ c = streamGet(chp);
416+ }
417+ }
418+
419+ if (valf == -1.0) {
420+ streamUnget(chp, c);
421+ return n;
422+ }
423+
424+ valf = valf / ch_mpow(base, fixed_point);
425+
426+ if (tolower(c) == exp_char) {
427+ if (width-- == 0) {
428+ return n;
429+ }
430+ c = streamGet(chp);
431+ exp_is_positive = true;
432+ exp = 0;
433+
434+ if (c == '+') {
435+ if (width-- == 0) {
436+ return n;
437+ }
438+ c = streamGet(chp);
439+
440+ } else if (c == '-') {
441+ if (width-- == 0) {
442+ return n;
443+ }
444+ exp_is_positive = false;
445+ c = streamGet(chp);
446+ }
447+ /*
448+ "When parsing an incomplete floating-point value that ends in the exponent with no digits,
449+ such as parsing "100er" with the conversion specifier %f, the sequence "100e" (the longest
450+ prefix of a possibly valid floating-point number) is consumed, resulting in a matching
451+ error (the consumed sequence cannot be converted to a floating-point number), with "r"
452+ remaining." (https://en.cppreference.com/w/c/io/fscanf)
453+ */
454+ digit = sym_to_val(c, 10);
455+ if (digit == -1) {
456+ streamUnget(chp, c);
457+ return n;
458+ }
459+ while (width--) {
460+ /* Even if the significand was hex, the exponent is decimal */
461+ digit = sym_to_val(c, 10);
462+ if (digit == -1) {
463+ break;
464+ }
465+ exp = (exp * 10) + digit;
466+ c = streamGet(chp);
467+ }
468+ if (exp_is_positive) {
469+ valf = valf * (double)ch_mpow(exp_base, exp);
470+ } else {
471+ valf = valf / (double)ch_mpow(exp_base, exp);
472+ }
473+ }
474+
475+ float_common:
476+ if (!is_positive) {
477+ valf = -1 * valf;
478+ }
479+
480+ if (buf) {
481+ if (is_long) {
482+ *(double*)buf = valf;
483+ } else {
484+ *(float*)buf = valf;
485+ }
486+ }
487+
488+ ++n;
489+ f = *fmt++;
490+ continue;
491+#endif
492+
493+ case 'i':
494+ case 'I':
495+ /* I specifier discards leading whitespace */
496+ while (isspace(c)) {
497+ c = streamGet(chp);
498+ }
499+ /* The char might be +, might be -, might be 0, or might be something else */
500+ if (c == '+') {
501+ if (--width == 0) {
502+ return n;
503+ }
504+ c = streamGet(chp);
505+ } else if (c == '-') {
506+ if (--width == 0) {
507+ return n;
508+ }
509+ is_positive = false;
510+ c = streamGet(chp);
511+ }
512+
513+ if (c == '0') {
514+ if (--width == 0) {
515+ return ++n;
516+ }
517+ c = streamGet(chp);
518+ if (c == 'x' || c == 'X') {
519+ base = 16;
520+ if (--width == 0) {
521+ return n;
522+ }
523+ c = streamGet(chp);
524+
525+ } else {
526+ base = 8;
527+ }
528+ }
529+ break;
530+
531+ case 'd':
532+ case 'D':
533+ while (isspace(c)) {
534+ c = streamGet(chp);
535+ }
536+ if (c == '+') {
537+ if (--width == 0) {
538+ return n;
539+ }
540+ c = streamGet(chp);
541+
542+ } else if (c == '-') {
543+ if (--width == 0) {
544+ return n;
545+ }
546+ is_positive = false;
547+ c = streamGet(chp);
548+ }
549+ break;
550+ case 'X':
551+ case 'x':
552+ case 'P':
553+ case 'p':
554+ is_signed = false;
555+ base = 16;
556+ while (isspace(c)) {
557+ c = streamGet(chp);
558+ }
559+ if (c == '+') {
560+ if (--width == 0) {
561+ return n;
562+ }
563+ c = streamGet(chp);
564+ } else if (c == '-') {
565+ if (--width == 0) {
566+ return n;
567+ }
568+ is_positive = false;
569+ c = streamGet(chp);
570+ }
571+ if (c == '0') {
572+ if (--width == 0) {
573+ return ++n;
574+ }
575+ c = streamGet(chp);
576+ if (c == 'x' || c == 'X') {
577+ if (--width == 0) {
578+ return n;
579+ }
580+ c = streamGet(chp);
581+ }
582+ }
583+ break;
584+ case 'U':
585+ case 'u':
586+ is_signed = false;
587+ while (isspace(c)) {
588+ c = streamGet(chp);
589+ }
590+ if (c == '+') {
591+ if (--width == 0) {
592+ return n;
593+ }
594+ c = streamGet(chp);
595+ } else if (c == '-') {
596+ if (--width == 0) {
597+ return n;
598+ }
599+ is_positive = false;
600+ c = streamGet(chp);
601+ }
602+ break;
603+ case 'O':
604+ case 'o':
605+ is_signed = false;
606+ base = 8;
607+ while (isspace(c)) {
608+ c = streamGet(chp);
609+ }
610+
611+ if (c == '+') {
612+ if (--width == 0) {
613+ return n;
614+ }
615+ c = streamGet(chp);
616+ } else if (c == '-') {
617+ if (--width == 0) {
618+ return n;
619+ }
620+ is_positive = false;
621+ c = streamGet(chp);
622+ }
623+
624+ break;
625+ default:
626+ streamUnget(chp, c);
627+ return n;
628+ }
629+
630+ vall = 0UL;
631+
632+ /* If we don't have at least one additional eligible character, it's a matching failure */
633+ if (sym_to_val(c, base) == -1) {
634+ break;
635+ }
636+
637+ while (width--) {
638+ digit = sym_to_val(c, base);
639+ if (digit == -1) {
640+ break;
641+ }
642+ vall = (vall * base) + digit;
643+ c = streamGet(chp);
644+ }
645+
646+ if (!is_positive) {
647+ vall = -1 * vall;
648+ }
649+
650+ if (buf) {
651+ if (is_long && is_signed) {
652+ *((signed long*)buf) = vall;
653+ } else if (is_long && !is_signed) {
654+ *((unsigned long*)buf) = vall;
655+ } else if (!is_long && is_signed) {
656+ *((signed int*)buf) = vall;
657+ } else if (!is_long && !is_signed) {
658+ *((unsigned int*)buf) = vall;
659+ }
660+ }
661+ f = *fmt++;
662+ ++n;
663+ }
664+ streamUnget(chp, c);
665+ return n;
666+}
667+
668+/**
669+ * @brief System formatted input function.
670+ * @details This function implements a minimal @p scanf() like functionality
671+ * with input from a @p BufferedStream.
672+ * The general parameters format is: %[*][width][l|L]p
673+ * The following parameter types (p) are supported:
674+ * - <b>x</b> hexadecimal integer.
675+ * - <b>X</b> hexadecimal long.
676+ * - <b>o</b> octal integer.
677+ * - <b>O</b> octal long.
678+ * - <b>d</b> decimal signed integer.
679+ * - <b>D</b> decimal signed long.
680+ * - <b>u</b> decimal unsigned integer.
681+ * - <b>U</b> decimal unsigned long.
682+ * - <b>c</b> character.
683+ * - <b>s</b> string.
684+ * .
685+ *
686+ * @param[in] chp pointer to a @p BufferedStream implementing object
687+ * @param[in] fmt formatting string
688+ * @return The number parameters in ap that have been successfully
689+ * filled. This does not conform to the standard in that if
690+ * a failure (either matching or input) occurs before any
691+ * parameters are assigned, the function will return 0.
692+ *
693+ * @api
694+ */
695+int chscanf(BaseBufferedStream *chp, const char *fmt, ...)
696+{
697+ va_list ap;
698+ int retval;
699+
700+ va_start(ap, fmt);
701+ retval = chvscanf(chp, fmt, ap);
702+ va_end(ap);
703+
704+ return retval;
705+}
706+
707+/**
708+ * @brief System formatted input function.
709+ * @details This function implements a minimal @p snscanf()-like functionality.
710+ * The general parameters format is: %[*][width][l|L]p
711+ * The following parameter types (p) are supported:
712+ * - <b>x</b> hexadecimal integer.
713+ * - <b>X</b> hexadecimal long.
714+ * - <b>o</b> octal integer.
715+ * - <b>O</b> octal long.
716+ * - <b>d</b> decimal signed integer.
717+ * - <b>D</b> decimal signed long.
718+ * - <b>u</b> decimal unsigned integer.
719+ * - <b>U</b> decimal unsigned long.
720+ * - <b>c</b> character.
721+ * - <b>s</b> string.
722+ * .
723+ *
724+ * @param[in] str pointer to a buffer
725+ * @param[in] size size of the buffer
726+ * @param[in] fmt formatting string
727+ * @return The number parameters in ap that have been successfully
728+ * filled. This does not conform to the standard in that if
729+ * a failure (either matching or input) occurs before any
730+ * parameters are assigned, the function will return 0.
731+ *
732+ * @api
733+ */
734+int chsnscanf(char *str, size_t size, const char *fmt, ...)
735+{
736+ va_list ap;
737+ int retval;
738+
739+ /* Performing the scan operation.*/
740+ va_start(ap, fmt);
741+ retval = chvsnscanf(str, size, fmt, ap);
742+ va_end(ap);
743+
744+ /* Return number of receiving arguments successfully assigned.*/
745+ return retval;
746+}
747+
748+/**
749+ * @brief System formatted input function.
750+ * @details This function implements a minimal @p vsnscanf()-like functionality.
751+ * The general parameters format is: %[*][width][l|L]p
752+ * The following parameter types (p) are supported:
753+ * - <b>x</b> hexadecimal integer.
754+ * - <b>X</b> hexadecimal long.
755+ * - <b>o</b> octal integer.
756+ * - <b>O</b> octal long.
757+ * - <b>d</b> decimal signed integer.
758+ * - <b>D</b> decimal signed long.
759+ * - <b>u</b> decimal unsigned integer.
760+ * - <b>U</b> decimal unsigned long.
761+ * - <b>c</b> character.
762+ * - <b>s</b> string.
763+ * .
764+ *
765+ * @param[in] str pointer to a buffer
766+ * @param[in] size size of the buffer
767+ * @param[in] fmt formatting string
768+ * @param[in] ap list of parameters
769+ * @return The number parameters in ap that have been successfully
770+ * filled. This does not conform to the standard in that if
771+ * a failure (either matching or input) occurs before any
772+ * parameters are assigned, the function will return 0.
773+ *
774+ * @api
775+ */
776+int chvsnscanf(char *str, size_t size, const char *fmt, va_list ap)
777+{
778+ MemoryStream ms;
779+ size_t size_wo_nul;
780+
781+ if (size > 0)
782+ size_wo_nul = size - 1;
783+ else
784+ size_wo_nul = 0;
785+
786+ /* Memory stream object to be used as a string writer, reserving one
787+ byte for the final zero. */
788+ msObjectInit(&ms, (uint8_t*)str, size_wo_nul, 0);
789+
790+ /* Performing the scan operation using the common code and
791+ return number of receiving arguments successfully assigned. */
792+ return chvscanf((BaseBufferedStream *)&ms, fmt, ap);
793+}
794+
795+/** @} */
--- trunk/os/hal/lib/streams/chscanf.h (nonexistent)
+++ trunk/os/hal/lib/streams/chscanf.h (revision 13908)
@@ -0,0 +1,60 @@
1+/*
2+ ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+*/
16+
17+/*
18+ This file was contributed by Alex Lewontin.
19+ */
20+
21+/**
22+ * @file chscanf.h
23+ * @brief Mini scanf-like functionality.
24+ *
25+ * @addtogroup HAL_CHSCANF
26+ * @{
27+ */
28+
29+#ifndef CHSCANF_H
30+#define CHSCANF_H
31+
32+#include <stdarg.h>
33+
34+#include "bufstreams.h"
35+
36+/**
37+ * @brief Float type support.
38+ */
39+#if !defined(CHSCANF_USE_FLOAT) || defined(__DOXYGEN__)
40+#define CHSCANF_USE_FLOAT FALSE
41+#endif
42+
43+#if CHSCANF_USE_FLOAT
44+#include <math.h>
45+#endif
46+
47+#ifdef __cplusplus
48+extern "C" {
49+#endif
50+ int chvscanf(BaseBufferedStream *chp, const char *fmt, va_list ap);
51+ int chscanf(BaseBufferedStream *chp, const char *fmt, ...);
52+ int chsnscanf(char *str, size_t size, const char *fmt, ...);
53+ int chvsnscanf(char *str, size_t size, const char *fmt, va_list ap);
54+#ifdef __cplusplus
55+}
56+#endif
57+
58+#endif /* CHSCANF_H */
59+
60+/** @} */
--- trunk/os/hal/lib/streams/memstreams.c (revision 13907)
+++ trunk/os/hal/lib/streams/memstreams.c (revision 13908)
@@ -85,8 +85,20 @@
8585 return b;
8686 }
8787
88-static const struct MemStreamVMT vmt = {(size_t)0, _writes, _reads, _put, _get};
88+static msg_t _unget(void* ip, uint8_t b)
89+{
90+ MemoryStream* msp = ip;
8991
92+ if (msp->offset <= 0)
93+ return MSG_RESET;
94+ msp->offset -= 1;
95+ *(msp->buffer + msp->offset) = b;
96+
97+ return MSG_OK;
98+}
99+
100+static const struct MemStreamVMT vmt = {(size_t)0, _writes, _reads, _put, _get, _unget};
101+
90102 /*===========================================================================*/
91103 /* Driver exported functions. */
92104 /*===========================================================================*/
--- trunk/os/hal/lib/streams/memstreams.h (revision 13907)
+++ trunk/os/hal/lib/streams/memstreams.h (revision 13908)
@@ -45,7 +45,7 @@
4545 * @brief @p MemStream specific data.
4646 */
4747 #define _memory_stream_data \
48- _base_sequential_stream_data \
48+ _base_buffered_stream_data \
4949 /* Pointer to the stream buffer.*/ \
5050 uint8_t *buffer; \
5151 /* Size of the stream.*/ \
@@ -59,7 +59,7 @@
5959 * @brief @p MemStream virtual methods table.
6060 */
6161 struct MemStreamVMT {
62- _base_sequential_stream_methods
62+ _base_buffered_stream_methods
6363 };
6464
6565 /**
--- trunk/os/hal/lib/streams/nullstreams.c (revision 13907)
+++ trunk/os/hal/lib/streams/nullstreams.c (revision 13908)
@@ -74,8 +74,17 @@
7474 return 4;
7575 }
7676
77-static const struct NullStreamVMT vmt = {(size_t)0, writes, reads, put, get};
77+static msg_t unget(void* ip, uint8_t b)
78+{
7879
80+ (void)ip;
81+ (void)b;
82+
83+ return MSG_OK;
84+}
85+
86+static const struct NullStreamVMT vmt = {(size_t)0, writes, reads, put, get, unget};
87+
7988 /*===========================================================================*/
8089 /* Driver exported functions. */
8190 /*===========================================================================*/
--- trunk/os/hal/lib/streams/nullstreams.h (revision 13907)
+++ trunk/os/hal/lib/streams/nullstreams.h (revision 13908)
@@ -45,17 +45,17 @@
4545 * @brief @p NullStream specific data.
4646 */
4747 #define _null_stream_data \
48- _base_sequential_stream_data
48+ _base_buffered_stream_data
4949
5050 /**
5151 * @brief @p NullStream virtual methods table.
5252 */
5353 struct NullStreamVMT {
54- _base_sequential_stream_methods
54+ _base_buffered_stream_methods
5555 };
5656
5757 /**
58- * @extends BaseSequentialStream
58+ * @extends BaseBufferedStream
5959 *
6060 * @brief Null stream object.
6161 */
--- trunk/readme.txt (revision 13907)
+++ trunk/readme.txt (revision 13908)
@@ -74,6 +74,7 @@
7474 *****************************************************************************
7575
7676 *** Next ***
77+- NEW: Added chscanf() and buffered streams, contributed by Alex Lewontin.
7778 - NEW: Added option to LWIP bindings to use memory pools instead of heap
7879 allocator.
7980 - NEW: Added MACv2 driver for STM32H7xx.
Show on old repository browser