mirror of
https://github.com/adafruit/Adafruit_GPS.git
synced 2026-10-26 22:10:17 +00:00
Merge pull request #185 from ladyada-eagleclaw/main
Add bounded NMEA field iteration
This commit is contained in:
3 files changed
+140
No files matched your search
@@ -0,0 +1,97 @@
|
||||
/* Host regression for bounded field iteration. From the repository root:
|
||||
g++ -std=c++11 -Wall -Wextra -Werror -fsanitize=address,undefined -Isrc \
|
||||
src/Adafruit_NMEA.cpp extras/tests/nmea_fields/nmea_fields.cpp \
|
||||
-o /tmp/nmea_fields
|
||||
/tmp/nmea_fields
|
||||
*/
|
||||
|
||||
#include "Adafruit_NMEA.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static void expectField(nmea_span_t &remaining, const char *expected);
|
||||
static void expectEnd(nmea_span_t &remaining);
|
||||
|
||||
int main() {
|
||||
// No NUL terminator: AddressSanitizer catches reading beyond this array.
|
||||
char text[] = {'A', ',', ',', 'B', ','};
|
||||
nmea_span_t remaining = {text, sizeof(text)};
|
||||
expectField(remaining, "A");
|
||||
expectField(remaining, "");
|
||||
expectField(remaining, "B");
|
||||
expectField(remaining, "");
|
||||
expectEnd(remaining);
|
||||
expectEnd(remaining);
|
||||
assert(memcmp(text, "A,,B,", sizeof(text)) == 0);
|
||||
puts("PASS: nonterminated input, empty fields, and repeated end-of-fields");
|
||||
|
||||
const char commas[] = {',', ','};
|
||||
remaining = {commas, sizeof(commas)};
|
||||
expectField(remaining, "");
|
||||
expectField(remaining, "");
|
||||
expectField(remaining, "");
|
||||
expectEnd(remaining);
|
||||
puts("PASS: leading, consecutive, and trailing empty fields");
|
||||
|
||||
nmea_sentence_t sentence =
|
||||
Adafruit_NMEA::validate("$PQTMVERNO*58", sizeof("$PQTMVERNO*58") - 1);
|
||||
assert(sentence.status == NMEA_FRAME_VALID);
|
||||
remaining = sentence.fields;
|
||||
expectEnd(remaining);
|
||||
sentence =
|
||||
Adafruit_NMEA::validate("$PQTMVERNO,*74", sizeof("$PQTMVERNO,*74") - 1);
|
||||
assert(sentence.status == NMEA_FRAME_VALID);
|
||||
remaining = sentence.fields;
|
||||
expectField(remaining, "");
|
||||
expectEnd(remaining);
|
||||
puts("PASS: validated zero-field and one-empty-field messages differ");
|
||||
|
||||
const char outside[] = "AB,CD";
|
||||
remaining = {outside, 2};
|
||||
expectField(remaining, "AB");
|
||||
expectEnd(remaining);
|
||||
remaining = {outside, 0};
|
||||
expectField(remaining, "");
|
||||
expectEnd(remaining);
|
||||
remaining = {NULL, 100};
|
||||
expectEnd(remaining);
|
||||
puts("PASS: declared boundaries and absent cursors are respected");
|
||||
|
||||
char longText[300];
|
||||
memset(longText, 'A', sizeof(longText));
|
||||
remaining = {longText, sizeof(longText)};
|
||||
nmea_span_t field = Adafruit_NMEA::nextField(remaining);
|
||||
assert(field.data == longText && field.length == sizeof(longText));
|
||||
expectEnd(remaining);
|
||||
puts("PASS: fields longer than 255 bytes keep their full length");
|
||||
|
||||
nmea_span_t first = {"A,B", 3};
|
||||
nmea_span_t second = {"1,,2", 4};
|
||||
nmea_span_t saved = Adafruit_NMEA::nextField(first);
|
||||
expectField(second, "1");
|
||||
expectField(first, "B");
|
||||
expectField(second, "");
|
||||
expectEnd(first);
|
||||
expectField(second, "2");
|
||||
expectEnd(second);
|
||||
assert(saved.length == 1 && saved.data[0] == 'A');
|
||||
puts("PASS: interleaved cursors and previously returned views stay "
|
||||
"independent");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void expectField(nmea_span_t &remaining, const char *expected) {
|
||||
const char *start = remaining.data;
|
||||
nmea_span_t field = Adafruit_NMEA::nextField(remaining);
|
||||
assert(field.data != NULL && field.data == start);
|
||||
assert(field.length == strlen(expected));
|
||||
if (field.length)
|
||||
assert(memcmp(field.data, expected, field.length) == 0);
|
||||
}
|
||||
|
||||
static void expectEnd(nmea_span_t &remaining) {
|
||||
nmea_span_t field = Adafruit_NMEA::nextField(remaining);
|
||||
assert(field.data == NULL && field.length == 0);
|
||||
assert(remaining.data == NULL && remaining.length == 0);
|
||||
}
|
||||
@@ -109,3 +109,45 @@ nmea_sentence_t Adafruit_NMEA::validate(const char *data, size_t length) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Return the next field and advance a bounded field cursor.
|
||||
@param remaining Start with a copy of a VALID sentence's fields span.
|
||||
Updated in place to refer to the fields after the next comma, or to an
|
||||
absent span after the last field. Its input storage must remain readable.
|
||||
@return Borrowed field text, excluding the comma. Non-NULL data with zero
|
||||
length means an empty field; NULL data means there are no fields left.
|
||||
|
||||
Leading, consecutive, and trailing commas preserve empty fields. An absent
|
||||
cursor stays absent on subsequent calls. No text is copied or modified,
|
||||
and no NUL terminator is required. Only the supplied length is inspected.
|
||||
Iterate each cursor in order for one forward scan of its fields.
|
||||
|
||||
To mark a cursor exhausted, set remaining.data to NULL. Setting only
|
||||
remaining.length to zero still yields one empty field if data is non-NULL.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
nmea_span_t Adafruit_NMEA::nextField(nmea_span_t &remaining) {
|
||||
nmea_span_t field = {NULL, 0};
|
||||
if (!remaining.data) {
|
||||
remaining.length = 0;
|
||||
return field;
|
||||
}
|
||||
|
||||
field.data = remaining.data;
|
||||
while (field.length < remaining.length &&
|
||||
remaining.data[field.length] != ',') {
|
||||
field.length++;
|
||||
}
|
||||
|
||||
if (field.length < remaining.length) {
|
||||
// Keep a non-NULL cursor after a trailing comma: one empty field remains.
|
||||
remaining.data += field.length + 1;
|
||||
remaining.length -= field.length + 1;
|
||||
} else {
|
||||
remaining.data = NULL;
|
||||
remaining.length = 0;
|
||||
}
|
||||
return field;
|
||||
}
|
||||
@@ -47,6 +47,7 @@ typedef struct {
|
||||
class Adafruit_NMEA {
|
||||
public:
|
||||
static nmea_sentence_t validate(const char *data, size_t length);
|
||||
static nmea_span_t nextField(nmea_span_t &remaining);
|
||||
};
|
||||
|
||||
#endif // ADAFRUIT_NMEA_H
|
||||
Reference in new issue
Block a user