From 3578ec94b22f50140f4ef6f02b5d54bc2890654c Mon Sep 17 00:00:00 2001 From: Thomas Maier-Komor Date: Sun, 28 Feb 2021 00:52:51 +0100 Subject: [PATCH] release R2102 --- .hg_archival.txt | 4 +- ChangeLog | 13 + examples/ascii_output/ip4_output.cpp | 5 +- examples/datatypes/Makefile | 2 +- examples/syscfg/Makefile | 13 +- examples/syscfg/ip4ascii.h | 21 + examples/syscfg/main_stream.cpp | 60 ++- examples/syscfg/syscfg.wfc | 9 +- include/bytes.h | 62 ++- include/fstring.h | 8 +- share/parse_ascii.cct | 155 ++++--- share/write_mem.cc | 32 +- src/CodeGenerator.cc | 4 +- src/CodeTemplate.cc | 3 +- src/CppGenerator.cc | 254 ++++++++--- src/Field.cc | 26 +- src/Field.h | 3 + src/Generator.cc | 2 +- src/Message.cc | 4 +- src/Options.cc | 3 +- src/codeid.h | 3 +- src/wfc.cc | 2 +- src/xproto.y | 2 +- testsuite/genwfc.mk | 10 +- testsuite/ip4funcs.h | 20 + testsuite/testcases/binformats.wfc | 640 +++++++++++++++++++++++++++ testsuite/testcases/byname.wfc | 7 + testsuite/testcases/bynametest.cpp | 14 + testsuite/testcases/reference.wfc | 4 + testsuite/testcases/validbits2.wfc | 5 +- 30 files changed, 1213 insertions(+), 177 deletions(-) create mode 100644 examples/syscfg/ip4ascii.h create mode 100644 testsuite/ip4funcs.h create mode 100644 testsuite/testcases/binformats.wfc diff --git a/.hg_archival.txt b/.hg_archival.txt index fbea688..18f075c 100644 --- a/.hg_archival.txt +++ b/.hg_archival.txt @@ -1,4 +1,4 @@ repo: 74e97a8352b0714d95edee6a4e88879188f84695 -node: e4ffcdf1b68d181b4fd4053628f0039dd190aba7 +node: 8c6fd17949c867af77d3cb932e5fb19ac040984c branch: default -tag: R2101 +tag: R2102 diff --git a/ChangeLog b/ChangeLog index 77d0d7d..c3670c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Release R2102: +============== +- fix: potential bus error with write_u64 on little endian +- fix: do not set valid flag when parsing ascii fails +- fix: stringtype handling may fallback to C type +- fix: multiple options handling +- fix: return type must be ssize_t if function returns <0 +- fix: build fixes for examples +- enhancement: support for setting bytes with hex encoded data +- enhancement: updated syscfg example to demo setByName +- enhancement: added doxygen comments to the generated files +- enhancement: opimization for use of empty string as invalid value + Release R2101: ============== - enhancement: support option used for messages diff --git a/examples/ascii_output/ip4_output.cpp b/examples/ascii_output/ip4_output.cpp index 6a7e904..970d87a 100644 --- a/examples/ascii_output/ip4_output.cpp +++ b/examples/ascii_output/ip4_output.cpp @@ -75,15 +75,18 @@ int main() // print ASCII representation nc.toASCII(cout); + cout << endl; // modification of contents can also be done by setting by name nc.setByName("hostname","myhost"); nc.setByName("degC","19.8"); nc.toASCII(cout); + cout << endl; + cout << endl; // binary output: cout << "encoded binary has " << nc.calcSize() << " bytes\n" - "binary output:\n\n"; + "binary output:\n"; uint8_t buf[nc.calcSize()]; nc.toMemory(buf,sizeof(buf)); // serialze to buffer diff --git a/examples/datatypes/Makefile b/examples/datatypes/Makefile index dc9fed6..ed45982 100644 --- a/examples/datatypes/Makefile +++ b/examples/datatypes/Makefile @@ -2,7 +2,7 @@ WFC = ../../bin/wfc CXX = g++ CXXFLAGS= -I../../include -Os -CXXSRC = datatypes.cpp test_dt.cpp ../../lib/wfc_support.cpp +CXXSRC = datatypes.cpp test_dt.cpp CXXOBJ = $(CXXSRC:%.cpp=%.o) test_dt: $(CXXOBJ) diff --git a/examples/syscfg/Makefile b/examples/syscfg/Makefile index 30e5632..5d203b0 100644 --- a/examples/syscfg/Makefile +++ b/examples/syscfg/Makefile @@ -1,9 +1,13 @@ CXXFLAGS = -Os -g -I../../include CXXSRCS = main_stream.cpp syscfg_efix.cpp syscfg_edyn.cpp CXXOBJS = $(CXXSRCS:%.cpp=%.o) +DEPS = $(CXXSRCS:%.cpp=.%.d) all: pc edyn efix +.%.d: %.cpp + $(CXX) $(CXXFLAGS) -MG -MT$(@:.%.d=%.o) -MM $< > $@ + syscfg_pc.cpp: syscfg.wfc ../../bin/wfc -tpc -o syscfg_pc syscfg.wfc @@ -22,7 +26,7 @@ main_efix.o: main_posix.cpp syscfg_efix.cpp pc: syscfg_pc.o main_stream.o - $(CXX) $(CXXFLAGS) -I../../include main_stream.o syscfg_pc.o ../../lib/wfc_support.cpp -o $@ + $(CXX) $(CXXFLAGS) -I../../include main_stream.o syscfg_pc.o -o $@ edyn: syscfg_edyn.o main_edyn.o $(CXX) $(CXXFLAGS) -I../../include main_edyn.o syscfg_edyn.o -o $@ @@ -30,3 +34,10 @@ edyn: syscfg_edyn.o main_edyn.o efix: syscfg_efix.o main_efix.o $(CXX) $(CXXFLAGS) -I../../include main_efix.o syscfg_efix.o -o $@ +clean: + rm -f $(CXXOBJS) syscfg_pc.* syscfg_edy.* syscfg_efix.* + +.depend: syscfg_pc.cpp syscfg_edyn.cpp syscfg_efix.cpp | $(DEPS) + cat $(DEPS) > .depend + +include .depend diff --git a/examples/syscfg/ip4ascii.h b/examples/syscfg/ip4ascii.h new file mode 100644 index 0000000..0b93752 --- /dev/null +++ b/examples/syscfg/ip4ascii.h @@ -0,0 +1,21 @@ +#include +#include + +inline void ip4_ascii(std::ostream &str, uint32_t ip) +{ + str << (ip & 0xff) << '.' << ((ip >> 8) & 0xff) << '.' << ((ip >> 16) & 0xff) << '.' << ((ip >> 24 ) & 0xff); +} + + +static int parse_ipv4(uint32_t *ip, const char *str) +{ + uint8_t b[4]; + int n = 0; + int r = sscanf(str,"%hhu.%hhu.%hhu.%hhu%n",b+0,b+1,b+2,b+3,&n); + if (4 == r) { + *ip = ((uint32_t)b[0]) | ((uint32_t)b[1]<<8) | ((uint32_t)b[2]<<16) | ((uint32_t)b[3]<<24); + return n; + } + std::cerr << "parser error " << n << "\n"; + return -1; +} diff --git a/examples/syscfg/main_stream.cpp b/examples/syscfg/main_stream.cpp index 5f3cb20..cd51822 100644 --- a/examples/syscfg/main_stream.cpp +++ b/examples/syscfg/main_stream.cpp @@ -29,9 +29,9 @@ void saveConfig(const Config &cfg) out.open(CfgName); // open file out << str; // write to file if (out.good()) - cout << "writing config successful\n"; + cout << "\nwriting config successful\n"; else - cerr << "error writing config\n"; + cerr << "\nerror writing config\n"; } @@ -56,6 +56,18 @@ int readConfig(Config &cfg) } +static uint32_t str_to_ip(const char *str) +{ + uint8_t b[4]; + int n = sscanf(str,"%hhu.%hhu.%hhu.%hhu",b+0,b+1,b+2,b+3); + if (4 == n) { + uint32_t ip = ((uint32_t)b[0]<<24) | ((uint32_t)b[1]<<16) | ((uint32_t)b[2]<<8) | (uint32_t)b[3]; + return ip; + } + return 0; +} + + int main() { Config cfg; @@ -69,7 +81,51 @@ int main() // do some work here // e.g. produce ASCII output cfg.toASCII(std::cout); + + cout << "\nNow continue interactively, by setting gateway and netmask.\n" + "Therefore, enter parameter name and its argument separated by a singel space.\n" + "e.g. type 'netmask 24'\n"; + char line[80]; + do { + cin.getline(line,sizeof(line)); + char *sp = strchr(line,' '); + if (sp) { + *sp = 0; + if (0 > cfg.setByName(line,sp+1)) + cerr << "error parsing '" << line << "' = '" << sp+1 << "'\n"; + } + } while (line[0] != 0); + // without setByName + /* + char line[80]; + do { + + // this code misses necessary checks to be more readable as an example + if (!memcmp(line,"gateway ",8)) { + uint32_t ip = str_to_ip(line+8); + if (ip) + cfg.set_gateway(ip); + else + cerr << "invalid IP address\n"; + } else if (!memcmp(line,"ipv4 ",5)) { + uint32_t ip = str_to_ip(line+5); + if (ip) + cfg.set_ipv4(ip); + else + cerr << "invalid IP address '" << line+4 << "'\n"; + } else if (!memcmp(line,"netmask ",8)) { + long v = strtol(line+8,0,0); + cfg.set_netmask(v); + } else if (line[0] != 0) { + cerr << "invalid parameter\n"; + } + // ... + } while (line[0] != 0); + */ + + cfg.toASCII(std::cout); + cout << "\n"; // save the config before terminating saveConfig(cfg); return 0; diff --git a/examples/syscfg/syscfg.wfc b/examples/syscfg/syscfg.wfc index 9f63e27..b72558d 100644 --- a/examples/syscfg/syscfg.wfc +++ b/examples/syscfg/syscfg.wfc @@ -21,15 +21,18 @@ option embedded_dyn : embedded { option embedded_fix : embedded { header=; - /Config/hostname : stringtype = "FString<16>"; - /Config/wifi_ssid: stringtype = "FString<32>"; - /Config/wifi_pass: stringtype = "FString<32>"; + /Config/hostname : stringtype = "FString<16>"; + /Config/wifi_ssid: stringtype = "FString<32>"; + /Config/wifi_pass: stringtype = "FString<32>"; } option pc { toASCII = toASCII; endian = little; optimize = speed; + header="ip4ascii.h"; + /Config/ipv4: to_ascii = ip4_ascii, parse_ascii = parse_ipv4; + /Config/gateway: to_ascii = ip4_ascii, parse_ascii = parse_ipv4; } message Config diff --git a/include/bytes.h b/include/bytes.h index 71edf9b..bf77c6b 100644 --- a/include/bytes.h +++ b/include/bytes.h @@ -21,6 +21,7 @@ #define _BYTES_H #include +#include struct Bytes { @@ -29,14 +30,43 @@ struct Bytes , len(0) { } + explicit Bytes(const char *d) + : len(strlen(d)) + { + bytes = (char *)malloc(len); + memcpy(bytes,d,len); + } + Bytes(const char *d, size_t l) - : bytes(d) + : bytes((char *)malloc(l)) , len(l) - { } + { memcpy(bytes,d,l); } + + Bytes(const Bytes &b) + : bytes((char *)malloc(b.len)) + , len(b.len) + { memcpy(bytes,b.bytes,len); } + + ~Bytes() + { free(bytes); } + Bytes &operator = (const Bytes &b) + { + assign(b.bytes,b.len); + return *this; + } + + Bytes &operator = (const char *cstr) + { + size_t l = strlen(cstr); + assign(cstr,l); + return *this; + } + void assign(const char *d, size_t l) { - bytes = d; + bytes = (char*)realloc((void*)bytes,l); + memcpy(bytes,d,l); len = l; } @@ -51,15 +81,39 @@ struct Bytes void clear() { + free((void*)bytes); bytes = 0; len = 0; } + void push_back(char c) + { + size_t at = len; + ++len; + bytes = (char *) realloc((void*)bytes,len); + bytes[at] = c; + } + bool operator != (const Bytes &r) const { return (len != r.len) || (0 != memcmp(bytes,r.bytes,len)); } + bool operator == (const Bytes &r) const + { return (len == r.len) && (0 == memcmp(bytes,r.bytes,len)); } + + bool operator == (const char *cstr) const + { + size_t l = strlen(cstr); + return (l == len) && (0 == memcmp(bytes,cstr,l)); + } + + bool operator != (const char *cstr) const + { + size_t l = strlen(cstr); + return (l != len) || (0 != memcmp(bytes,cstr,l)); + } + private: - const char *bytes; + char *bytes; size_t len; }; diff --git a/include/fstring.h b/include/fstring.h index 082657b..82215e8 100644 --- a/include/fstring.h +++ b/include/fstring.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020, Thomas Maier-Komor + * Copyright (C) 2020-2021, Thomas Maier-Komor * * This source file belongs to Wire-Format-Compiler. * @@ -44,13 +44,11 @@ class FString l = len-1; memcpy(str,s,l); str[l] = 0; - str[len-1] = 0; } FString(const FString &a) { strcpy(str,a.str); - str[len-1] = 0; } FString &operator = (const FString &a) @@ -61,8 +59,8 @@ class FString FString &operator += (const FString &a) { - strncat(str,a.str,len-1); - str[len-1] = 0; + size_t l = strlen(str); + strncat(str,a.str,len-l-1); return *this; } diff --git a/share/parse_ascii.cct b/share/parse_ascii.cct index d203d25..04c5898 100644 --- a/share/parse_ascii.cct +++ b/share/parse_ascii.cct @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2018, Thomas Maier-Komor + * Copyright (C) 2017-2021, Thomas Maier-Komor * * This source file belongs to Wire-Format-Compiler. * @@ -27,14 +27,12 @@ int parse_ascii_u8(uint8_t *v, const char *ascii) { char *eptr; long d = strtol(ascii,&eptr,0); - if ((d < 0) || (d > UINT8_MAX)) { + if (eptr == ascii) $handle_error; - } - if (eptr != ascii) { - *v = (uint8_t) d; - return eptr-ascii; - } - $handle_error; + if ((d < 0) || (d > UINT8_MAX)) + $handle_error; + *v = (uint8_t) d; + return eptr-ascii; } @@ -46,14 +44,12 @@ int parse_ascii_u16(uint16_t *v, const char *ascii) { char *eptr; long d = strtol(ascii,&eptr,0); - if ((d < 0) || (d > UINT16_MAX)) { + if (eptr == ascii) $handle_error; - } - if (eptr != ascii) { - *v = (uint16_t) d; - return eptr-ascii; - } - $handle_error; + if ((d < 0) || (d > UINT16_MAX)) + $handle_error; + *v = (uint16_t) d; + return eptr-ascii; } @@ -65,13 +61,12 @@ int parse_ascii_u32(uint32_t *v, const char *ascii) { char *eptr; long long d = strtoll(ascii,&eptr,0); + if (eptr == ascii) + $handle_error; if ((d < 0) || (d > UINT32_MAX)) $handle_error; - if (eptr != ascii) { - *v = (uint32_t) d; - return eptr-ascii; - } - $handle_error; + *v = (uint32_t) d; + return eptr-ascii; } @@ -83,11 +78,10 @@ int parse_ascii_u64(uint64_t *v, const char *ascii) { char *eptr; unsigned long long d = strtoull(ascii,&eptr,0); - if (eptr != ascii) { - *v = (uint64_t) d; - return eptr-ascii; - } - $handle_error; + if (eptr == ascii) + $handle_error; + *v = (uint64_t) d; + return eptr-ascii; } @@ -99,14 +93,12 @@ int parse_ascii_s8(int8_t *v, const char *ascii) { char *eptr; long d = strtol(ascii,&eptr,0); - if ((d < INT8_MIN) || (d > INT8_MAX)) { + if (eptr == ascii) $handle_error; - } - if (eptr != ascii) { - *v = (uint8_t) d; - return eptr-ascii; - } - $handle_error; + if ((d < INT8_MIN) || (d > INT8_MAX)) + $handle_error; + *v = (uint8_t) d; + return eptr-ascii; } @@ -118,14 +110,12 @@ int parse_ascii_s16(int16_t *v, const char *ascii) { char *eptr; long d = strtol(ascii,&eptr,0); - if ((d < INT16_MIN) || (d > INT16_MAX)) { + if (eptr == ascii) $handle_error; - } - if (eptr != ascii) { - *v = (uint16_t) d; - return eptr-ascii; - } - $handle_error; + if ((d < INT16_MIN) || (d > INT16_MAX)) + $handle_error; + *v = (uint16_t) d; + return eptr-ascii; } @@ -137,14 +127,12 @@ int parse_ascii_s32(int32_t *v, const char *ascii) { char *eptr; long long d = strtoll(ascii,&eptr,0); - if ((d < INT32_MIN) || (d > INT32_MAX)) { + if (eptr == ascii) $handle_error; - } - if (eptr != ascii) { - *v = (uint32_t) d; - return eptr-ascii; - } - $handle_error; + if ((d < INT32_MIN) || (d > INT32_MAX)) + $handle_error; + *v = (uint32_t) d; + return eptr-ascii; } @@ -156,11 +144,10 @@ int parse_ascii_s64(int64_t *v, const char *ascii) { char *eptr; unsigned long long d = strtoull(ascii,&eptr,0); - if (eptr != ascii) { - *v = (int64_t) d; - return eptr-ascii; - } - $handle_error; + if (eptr == ascii) + $handle_error; + *v = (int64_t) d; + return eptr-ascii; } @@ -172,11 +159,10 @@ int parse_ascii_flt(float *v, const char *ascii) { char *eptr; float f = strtof(ascii,&eptr); - if (eptr != ascii) { - *v = f; - return eptr-ascii; - } - $handle_error; + if (eptr == ascii) + $handle_error; + *v = f; + return eptr-ascii; } @@ -188,11 +174,10 @@ int parse_ascii_dbl(double *v, const char *ascii) { char *eptr; double d = strtod(ascii,&eptr); - if (eptr != ascii) { - *v = d; - return eptr-ascii; - } - $handle_error; + if (eptr == ascii) + $handle_error; + *v = d; + return eptr-ascii; } @@ -269,11 +254,9 @@ int parse_ascii_bool(bool *v, const char *ascii) */ int parse_enum(varint_t *v, const char *n) { - if ((n[0] >= '0') && (n[0] <= '9')) { - char *e; - long ll = strtoll(n,&e,0); - if ((ll == 0) && (e == n)) - $handle_error; + char *e; + long ll = strtoll(n,&e,0); + if (e != n) { *v = ll; return e-n; } @@ -283,3 +266,43 @@ int parse_enum(varint_t *v, const char *n) *v = i->second; return strlen(n); } + +/* wfc-template: + * function: parse_ascii_bytes + * sysinclude: map + */ +template +int parse_ascii_bytes(T &b, const char *ascii) +{ + const char *at = ascii; + while (*at) { + char c0 = *at++; + while ((c0 == ' ') || (c0 == '\t') || (c0 == '\n') || (c0 == '\r')) + c0 = *at++; + char c1 = *at++; + while ((c1 == ' ') || (c1 == '\t') || (c1 == '\n') || (c1 == '\r')) + c1 = *at++; + if (c1 == 0) + $handle_error; + uint8_t v; + if ((c0 >= '0') && (c0 <= '9')) + v = c0-'0'; + else if ((c0 >= 'a') && (c0 <= 'f')) + v = c0-'a'+10; + else if ((c0 >= 'A') && (c0 <= 'F')) + v = c0-'A'+10; + else + return at-ascii-1; + v <<= 4; + if ((c1 >= '0') && (c1 <= '9')) + v |= c1-'0'; + else if ((c1 >= 'a') && (c1 <= 'f')) + v |= c1-'a'+10; + else if ((c1 >= 'A') && (c1 <= 'F')) + v |= c1-'A'+10; + else + $handle_error; + b.push_back(v); + } + return at-ascii; +} diff --git a/share/write_mem.cc b/share/write_mem.cc index cbbd91b..1cc611e 100644 --- a/share/write_mem.cc +++ b/share/write_mem.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2018, Thomas Maier-Komor + * Copyright (C) 2017-2021, Thomas Maier-Komor * * This source file belongs to Wire-Format-Compiler. * @@ -36,14 +36,25 @@ union mangle_double }; +/* wfc-template: + * function: write_u64 + * endian: little + * Optimize: speed + */ +#define write_u64_le(w,v) (*(uint64_t*)w) = v + + /* wfc-template: * function: write_u64 */ void write_u64_generic(uint8_t *wire, uint64_t v) { #if defined __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - *(uint64_t*)wire = v; -#else + if (((off_t)wire & 0x7) == 0) { + *(uint64_t*)wire = v; + return; + } +#endif *wire++ = v & 0xff; v >>= 8; *wire++ = v & 0xff; @@ -59,7 +70,6 @@ void write_u64_generic(uint8_t *wire, uint64_t v) *wire++ = v & 0xff; v >>= 8; *wire++ = v; -#endif } @@ -139,14 +149,6 @@ void write_u64_Os2(uint8_t *wire, uint64_t v) #define write_u32_le(w,v) (*(uint32_t*)w) = v -/* wfc-template: - * function: write_u64 - * endian: little - * Optimize: speed - */ -#define write_u64_le(w,v) (*(uint64_t*)w) = v - - /* wfc-template: * function: write_u64 * endian: little @@ -162,6 +164,12 @@ void write_u64_le_fun(uint8_t *wire, uint64_t v) */ void write_u32_0(uint8_t *wire, uint32_t v) { +#if defined __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + if (((off_t)wire & 0x3) == 0) { + *(uint32_t*)wire = v; + return; + } +#endif *wire++ = v & 0xff; v >>= 8; *wire++ = v & 0xff; diff --git a/src/CodeGenerator.cc b/src/CodeGenerator.cc index 074e888..c007c6a 100644 --- a/src/CodeGenerator.cc +++ b/src/CodeGenerator.cc @@ -56,7 +56,9 @@ CodeGenerator::CodeGenerator(PBFile *p, Options *o) impl = new XmlGenerator(p,o); } else abort(); - License = "\nCode generated with Wire-Format-Compiler " VERSION "\n\n"; + License = "\nCode generated by Wire-Format-Compiler (WFC)\n" + "WFC Version: " VERSION "\n" + "WFC is Copyright 2015-2021, Thomas Maier-Komor\n\n"; if (p) { License += "Source Information:\n===================\n"; License += "Filename : "; diff --git a/src/CodeTemplate.cc b/src/CodeTemplate.cc index a9c86c3..37d2c27 100644 --- a/src/CodeTemplate.cc +++ b/src/CodeTemplate.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2020, Thomas Maier-Komor + * Copyright (C) 2017-2021, Thomas Maier-Komor * * This source file belongs to Wire-Format-Compiler. * @@ -44,6 +44,7 @@ const char *Functions[] = { "decode_early", "decode_union", "parse_ascii_bool", + "parse_ascii_bytes", "parse_ascii_dbl", "parse_ascii_flt", "parse_ascii_s16", diff --git a/src/CppGenerator.cc b/src/CppGenerator.cc index 20b34a5..867064d 100644 --- a/src/CppGenerator.cc +++ b/src/CppGenerator.cc @@ -221,7 +221,8 @@ void CppGenerator::setTarget(const char *t) do { const map &nodeopts = no->getNodeOptions(); for (map::const_iterator i(nodeopts.begin()),e(nodeopts.end()); i != e; ++i) { - applyNodeOption(i->first.c_str(),i->second); + KVPair *p = i->second; + applyNodeOption(i->first.c_str(),p); } no = no->getParent(); } while (no); @@ -233,40 +234,52 @@ void CppGenerator::setTarget(const char *t) void CppGenerator::applyNodeOption(const char *nodepath, KVPair *kvp) { - diag("appyNodeOption(%s,[%s,%s])",nodepath,kvp->getKey().c_str(),kvp->getValue().c_str()); + diag("appyNodeOption(%s,[%s,%s,...])",nodepath,kvp->getKey().c_str(),kvp->getValue().c_str()); if (nodepath[0] != '/') error("invalid node path '%s'",nodepath); const char *node = nodepath+1; - const char *slash = strchr(node,'/'); Message *m = 0; // needed for '/message' case do { - string msgname(node,slash?slash-node:strlen(node)); + char *slash = strchr((char*)node,'/'); + string msgname(node,slash ? slash-node : strlen(node)); diag("appyNodeOption: msg %s",msgname.c_str()); - if (m) { - m = m->getMessage(msgname.c_str()); - } else { + if (m == 0) { m = file->getMessage(msgname.c_str()); + } else if (Message *sm = m->getMessage(msgname.c_str())) { + m = sm; + } else { + // this is a field or a enum + break; } if (m == 0) { warn("Ignoring unknwon message '%s' in nodepath '%s'",msgname.c_str(),nodepath); return; } - if (slash) { + if (slash == 0) + node = 0; + else node = slash+1; - slash = strchr(slash+1,'/'); - } - } while (slash); + } while (node); if (Enum *e = m ? m->getEnum(node) : file->getEnum(node)) { diag("appyNodeOption: enum %s",node); - e->setOption(kvp->getKey(),kvp->getValue()); + do { + e->setOption(kvp->getKey(),kvp->getValue()); + kvp = kvp->getNext(); + } while (kvp); } else if (m == 0) { warn("Unable to resolve node path %s. Ignoring option '%s'.",nodepath,kvp->getKey().c_str()); } else if (Field *f = m->getField(node)) { - diag("appyNodeOption: field %s",node); - f->setOption(kvp->getKey(),kvp->getValue()); + do { + diag("appyNodeOption: field %s, key %s",node,kvp->getKey().c_str()); + f->setOption(kvp->getKey(),kvp->getValue()); + kvp = kvp->getNext(); + } while (kvp); } else { - m->setOption(kvp->getKey().c_str(),kvp->getValue().c_str()); + do { + m->setOption(kvp->getKey().c_str(),kvp->getValue().c_str()); + kvp = kvp->getNext(); + } while (kvp); } } @@ -290,13 +303,17 @@ string CppGenerator::getValid(Field *f, bool invalid) } int vbit = f->getValidBit(); if (const char *invValue = f->getInvalidValue()) { - assert(vbit < 0); - ret += "m_$(fname)"; - if (invalid) - ret += " == "; - else - ret += " != "; - ret += invValue; + assert(vbit < 0); + if ((type == ft_string) && (!strcmp(invValue,"\"\""))) { + ret += "!m_$(fname).empty()"; + } else { + ret += "m_$(fname)"; + if (invalid) + ret += " == "; + else + ret += " != "; + ret += invValue; + } return ret; } assert(vbit >= 0); @@ -475,6 +492,8 @@ void CppGenerator::init() void CppGenerator::scanRequirements(Message *m) { + if (!m->getGenerate()) + return; if (optmode == optreview) hasVarInt = true; // tags are then always generated as varint for (auto i : m->getFields()) { @@ -685,6 +704,7 @@ static void allHelpers(vector &funcs, bool VarIntBits64) funcs.push_back(ct_to_dblstr); funcs.push_back(ct_parse_ascii_bool); + funcs.push_back(ct_parse_ascii_bytes); funcs.push_back(ct_parse_ascii_dbl); funcs.push_back(ct_parse_ascii_flt); funcs.push_back(ct_parse_ascii_s8); @@ -1252,32 +1272,67 @@ void CppGenerator::writeClass(Generator &G, Message *m) G << "bool operator != (const $(msg_name) &r) const;\n"; if (target->getFlag("withEqual")) G << "bool operator == (const $(msg_name) &r) const;\n"; - G << "void $(msg_clear)();\n" + G << "\n" + "//! function for resetting all members to their default values\n" + "void $(msg_clear)();\n" + "\n" + "//! Calculate the required number of bytes for serializing this object.\n" + "//! If the data of the object change, the number ob bytes needed for\n" + "//! serialization, may change, too.\n" "size_t $calcSize() const;\n"; if (G.hasValue("fromMemory")) - G << "ssize_t $(fromMemory)(const void *b, ssize_t s);\n"; + G << "\n" + "//! Function for parsing memory with serialized data to append and update this object.\n" + "//! @param b buffer of serialized data\n" + "//! @param s number of bytes available in the buffer\n" + "//! @return number of bytes parsed or negative value if an error occured\n" + "ssize_t $(fromMemory)(const void *b, ssize_t s);\n"; if (G.hasValue("toMemory")) - G << "size_t $(toMemory)(uint8_t *, ssize_t) const;\n"; + G << "\n" + "\t//! Serialize the object to memory.\n" + "\t//! param b buffer the data should be written to\n" + "\t//! param s number of bytes available in the buffer\n" + "\t//! return number of bytes written\n" + "ssize_t $(toMemory)(uint8_t *, ssize_t) const;\n"; if (G.hasValue("toWire")) { G.setMode(gen_wire); - G << "void $(toWire)($putparam) const;\n"; + G << "\n" + "//! Serialize the object using a function for transmitting individual bytes.\n" + "//! @param put function to put individual bytes for transmission on the wire\n" + "void $(toWire)($putparam) const;\n"; } if (G.hasValue("toSink")) { G.setMode(gen_sink); - G << "$(sink_template)void $(toSink)($putparam) const;\n"; + G << "\n" + "//! Function for serializing the object using the Sink class.\n" + "$(sink_template)void $(toSink)($putparam) const;\n"; } if (G.hasValue("toString")) { G.setMode(gen_string); - G << "void $(toString)($putparam) const;\n"; + G << "\n" + "//! Function for serializing the object to a string.\n" + "void $(toString)($putparam) const;\n"; } if (G.hasValue("toJSON")) - G << "void $(toJSON)($(streamtype) &json, unsigned indLvl = 0) const;\n"; + G << "\n" + "//! Function for writing the JSON representation of this object to a stream.\n" + "void $(toJSON)($(streamtype) &json, unsigned indLvl = 0) const;\n"; if (PrintOut) - G << "void $(toASCII)($(streamtype) &o, size_t indent = 0) const;\n"; + G << "\n" + "//! Function for writing an ASCII representation of this object to a stream.\n" + "void $(toASCII)($(streamtype) &o, size_t indent = 0) const;\n"; if (G.hasValue("getMaxSize")) - G << "static size_t $getMaxSize();\n"; + G << "\n" + "//! Function for determining the maximum size that the object may need for\n" + "//! its serialized representation\n" + "static size_t $getMaxSize();\n"; if (target->getOption("SetByName") != "") - G << "int $(set_by_name)(const char *, const char *);\n"; + G << "\n" + "//! Function for setting a parameter by its ASCII name using an ASCII representation of value.\n" + "//! @param param parameter name\n" + "//! @param value ASCII representation of the value\n" + "//! @return number of bytes parsed from value or negative value if an error occurs\n" + "int $(set_by_name)(const char *param, const char *value);\n"; G << '\n'; if (SubClasses) { for (unsigned i = 0, e = m->numEnums(); i != e; ++i) { @@ -1502,13 +1557,24 @@ void CppGenerator::writeHeader(const string &bn) "{\n" " public:\n" " virtual ~" << bcn << "() = 0;\n" + "\n" + " //! calculate the number ob bytes needed for a serialized representation\n" " virtual size_t $calcSize() const = 0;\n"; if (G.hasValue("toString")) - G << "\tvirtual void $toString($(stringtype) &) const = 0;\n"; + G << "\n" + "\t//! serialize the object to a string\n" + "\tvirtual void $toString($(stringtype) &) const = 0;\n"; if (G.hasValue("toSink") && !SinkToTemplate) - G << "\tvirtual void $toSink(Sink &) const = 0;\n"; + G << "\n" + "\t//! serialize the object to an object derived from class Sink\n" + "\tvirtual void $toSink(Sink &) const = 0;\n"; if (G.hasValue("toMemory")) - G << "\tvirtual size_t $toMemory(uint8_t *, ssize_t) const = 0;\n"; + G << "\n" + "\t//! serialize the object to memory\n" + "\t//! @param b buffer the data should be written to\n" + "\t//! @param s number of bytes available in the buffer\n" + "\t//! @return number of bytes written\n" + "\tvirtual ssize_t $toMemory(uint8_t *, ssize_t) const = 0;\n"; G << "};\n\n"; } for (unsigned i = 0, n = file->numEnums(); i != n; ++i) { @@ -1756,7 +1822,7 @@ void CppGenerator::writeSetByNameR(Generator &G, Field *f) "}\n"; return; } - if (!f->getOption("parse_ascii").empty()) { + if (!f->getParseAsciiFunction().empty()) { G << "return $parse_ascii(&m_$fname[x],value);\n" "}\n" "}\n"; @@ -1857,7 +1923,14 @@ void CppGenerator::writeSetByName(Generator &G, Message *m) { if (target->getOption("SetByName").empty()) return; - G << "int $(prefix)$(msg_name)::$(set_by_name)(const char *name, const char *value)\n" + G << "/*\n" + " * Function for setting an element in dot notation with an ASCII value.\n" + " * It will call the specified parse_ascii function for parsing the value.\n" + " *\n" + " * @return number of bytes successfully parsed or negative value indicating\n" + " * an error.\n" + " */\n" + "int $(prefix)$(msg_name)::$(set_by_name)(const char *name, const char *value)\n" "{\n"; unsigned n = 0; for (auto i : m->getFields()) { @@ -1865,15 +1938,18 @@ void CppGenerator::writeSetByName(Generator &G, Message *m) if ((f == 0) || !f->isUsed() || f->isObsolete()) continue; uint32_t t = f->getType(); - if ((t == ft_bytes) || (t == ft_cptr)) + if (t == ft_cptr) continue; - G.setField(f); ++n; if (q_repeated == f->getQuantifier()) { + if (t == ft_bytes) + continue; + G.setField(f); writeSetByNameR(G,f); G.setField(0); continue; } + G.setField(f); if ((t & ft_filter) == ft_msg) { if (f->getValidBit() >= 0) { G << "if (0 == memcmp(name,\"$(fname)\",$fnamelen)) {\n" @@ -1903,9 +1979,11 @@ void CppGenerator::writeSetByName(Generator &G, Message *m) "return 0;\n" "}\n"; } - if (!f->getOption("parse_ascii").empty()) { + if (!f->getParseAsciiFunction().empty()) { + G << "int r = $parse_ascii(&m_$fname,value);\n" + "if (r > 0)\n"; writeSetValid(G,f->getValidBit()); - G << "return $parse_ascii(&m_$fname,value);\n" + G << "return r;\n" "}\n"; G.setField(0); continue; @@ -1932,62 +2010,67 @@ void CppGenerator::writeSetByName(Generator &G, Message *m) G.setField(0); continue; } - writeSetValid(G,f->getValidBit()); switch (t) { case ft_string: G << "m_$fname = value;\n" - "return m_$(fname).size();\n"; + "int r = m_$(fname).size();\n"; + break; + case ft_bytes: + G << "int r = parse_ascii_bytes(m_$(fname),value);\n"; break; case ft_float: - G << "return parse_ascii_flt(&m_$(fname),value);\n"; + G << "int r = parse_ascii_flt(&m_$(fname),value);\n"; break; case ft_double: - G << "return parse_ascii_dbl(&m_$(fname),value);\n"; + G << "int r = parse_ascii_dbl(&m_$(fname),value);\n"; break; case ft_int8: case ft_sint8: case ft_sfixed8: - G << "return parse_ascii_s8(&m_$(fname),value);\n"; + G << "int r = parse_ascii_s8(&m_$(fname),value);\n"; break; case ft_int16: case ft_sint16: case ft_sfixed16: - G << "return parse_ascii_s16(&m_$(fname),value);\n"; + G << "int r = parse_ascii_s16(&m_$(fname),value);\n"; break; case ft_int32: case ft_sint32: case ft_sfixed32: - G << "return parse_ascii_s32(&m_$(fname),value);\n"; + G << "int r = parse_ascii_s32(&m_$(fname),value);\n"; break; case ft_int64: case ft_sint64: case ft_sfixed64: - G << "return parse_ascii_s64(&m_$(fname),value);\n"; + G << "int r = parse_ascii_s64(&m_$(fname),value);\n"; break; case ft_fixed8: case ft_uint8: - G << "return parse_ascii_u8(&m_$(fname),value);\n"; + G << "int r = parse_ascii_u8(&m_$(fname),value);\n"; break; case ft_uint16: case ft_fixed16: - G << "return parse_ascii_u16(&m_$(fname),value);\n"; + G << "int r = parse_ascii_u16(&m_$(fname),value);\n"; break; case ft_uint32: case ft_fixed32: - G << "return parse_ascii_u32(&m_$(fname),value);\n"; + G << "int r = parse_ascii_u32(&m_$(fname),value);\n"; break; case ft_uint64: case ft_fixed64: - G << "return parse_ascii_u64(&m_$(fname),value);\n"; + G << "int r = parse_ascii_u64(&m_$(fname),value);\n"; break; case ft_bool: - G << "return parse_ascii_bool(&m_$(fname),value);\n"; + G << "int r = parse_ascii_bool(&m_$(fname),value);\n"; break; case ft_signed: case ft_unsigned: default: abort(); } + G << "if (r > 0)\n"; + writeSetValid(G,f->getValidBit()); + G << "return r;\n"; G.setField(0); G << "}\n"; } @@ -2000,8 +2083,12 @@ void CppGenerator::writeClear(Generator &G, Field *f) uint8_t q = f->getQuantifier(); if (q == q_required) return; - G << "$(inline)void $(prefix)$(msg_name)::$(field_clear)()\n" - "{\n"; + G << "/*!\n" + " * Function for clearing the associated member variable.\n" + " * It will reset the value to the default value.\n" + " */\n" + "$(inline)void $(prefix)$(msg_name)::$(field_clear)()\n" + "{\n"; uint32_t type = f->getType(); int vbit = f->getValidBit(); if (vbit >= 0) { @@ -2284,8 +2371,13 @@ void CppGenerator::writeCalcSize(Generator &G, Field *f) void CppGenerator::writeCalcSize(Generator &G, Message *m) { - G << "size_t $(prefix)$(msg_name)::$calcSize() const\n" - << "{\n"; + G << "/*!\n" + " * Function for calculating the number of bytes needed for serializing\n" + " * the related object with its associated data.\n" + " * Changing the data may change the size of the serialized object.\n" + " */\n" + "size_t $(prefix)$(msg_name)::$calcSize() const\n" + "{\n"; size_t n = 0; for (auto i : m->getFields()) { Field *f = i.second; @@ -3229,7 +3321,14 @@ void CppGenerator::writeToX(Generator &G, Field *f) void CppGenerator::writeFromMemory(Generator &G, Message *m) { - G << "ssize_t $(prefix)$(msg_name)::$(fromMemory)(const void *b, ssize_t s)\n" + G << "/*!\n" + " * Function for parsing an object from serialized data.\n" + " * @param b buffer containg the data to be parsed\n" + " * @param s number of bytes in buffer\n" + " * @return number of bytes successfully parsed (can be < s)\n" + " * or a negative value indicating the error encountered\n" + " */\n" + "ssize_t $(prefix)$(msg_name)::$(fromMemory)(const void *b, ssize_t s)\n" "{\n" "const uint8_t *a = (const uint8_t *)b;\n" "const uint8_t *e = a + s;\n"; @@ -3328,7 +3427,13 @@ void CppGenerator::writeFromMemory(Generator &G, Message *m) void CppGenerator::writeToMemory(Generator &G, Message *m) { - G << "size_t $(prefix)$(msg_name)::$toMemory(uint8_t *b, ssize_t s) const\n" + G << "/*!\n" + " * Function for serializing the object to memory.\n" + " * @param b buffer to serialize the object to\n" + " * @param s number of bytes available in the buffer\n" + " * @return number of bytes successfully serialized\n" + " */\n" + "ssize_t $(prefix)$(msg_name)::$toMemory(uint8_t *b, ssize_t s) const\n" "{\n"; if (Debug) G << "std::cout << \"$(prefix)$(msg_name)::$toMemory(\" << (void*)b << \", \" << s << \")\\n\";\n"; @@ -3546,7 +3651,12 @@ void CppGenerator::writeToJson(Generator &G, Message *m) break; } G.addVariable("json_indent",resolve_esc(target->getOption("json_indent"))); - G << "void $(prefix)$(msg_name)::$(toJSON)($(streamtype) &json, unsigned indLvl) const\n" + G << "/*!\n" + " * Function for writing a JSON representation of the object to a stream.\n" + " * @param json stream object the data should be serialized to\n" + " * @param indLvl the indention level that should be used\n" + " */\n" + "void $(prefix)$(msg_name)::$(toJSON)($(streamtype) &json, unsigned indLvl) const\n" "{\n"; if (fsep == 0) G << "char fsep = '{';\n"; @@ -3604,7 +3714,10 @@ void CppGenerator::writeToX(Generator &G, Message *m) void CppGenerator::writeClear(Generator &G, Message *m) { - G << "void $(prefix)$(msg_name)::$(msg_clear)()\n" + G << "/*!\n" + " * Function for resetting all members of this object to their default values.\n" + " */\n" + "void $(prefix)$(msg_name)::$(msg_clear)()\n" "{\n"; for (auto i : m->getFields()) { Field *f = i.second; @@ -3818,7 +3931,6 @@ void CppGenerator::writePrint(Generator &G, Field *f) } else G << "o << $(field_value) << ';';\n"; } else if (!f->getAsciiFunction().empty()) { - G << ";\n"; G << f->getAsciiFunction() << "(o,$(field_value));\n"; G << "o << ';';\n"; } else switch (type) { @@ -3972,15 +4084,29 @@ void CppGenerator::writeFunctions(Generator &G, Message *m) if (G.hasValue("toWire")) { G.setMode(gen_wire); G.setVariable("toX",G.getVariable("toWire")); + G << "/*!\n" + " * Function for serializing the object directly to a transmission stream.\n" + " * @param put function for putting a byte on the transmitting wire\n" + " */\n"; writeToX(G,m); } if (G.hasValue("toSink") && !SinkToTemplate) { G.setMode(gen_sink); + G << "/*!\n" + " * Function for serializing the object using a Sink object.\n" + " * Derive a class from the Sink base class to provide custom methods\n" + " * for sending the objects' data.\n" + " * @param s object of the class derived from Sink\n" + " */\n"; G.setVariable("toX",G.getVariable("toSink")); writeToX(G,m); } if (G.hasValue("toString")) { G.setMode(gen_string); + G << "/*!\n" + " * Function for serializing the object to a string.\n" + " * @param s string object the data should be written to\n" + " */\n"; G.setVariable("toX",G.getVariable("toString")); writeToX(G,m); } @@ -4066,6 +4192,8 @@ void CppGenerator::writeHelpers(vector &funcs) funcs.push_back(ct_parse_ascii_s16); if (hasS8) funcs.push_back(ct_parse_ascii_s8); + if (hasBytes) + funcs.push_back(ct_parse_ascii_bytes); if (hasBool) funcs.push_back(ct_parse_ascii_bool); } diff --git a/src/Field.cc b/src/Field.cc index a86a5ad..37e9c7f 100644 --- a/src/Field.cc +++ b/src/Field.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2020, Thomas Maier-Komor + * Copyright (C) 2017-2021, Thomas Maier-Komor * * This source file belongs to Wire-Format-Compiler. * @@ -935,18 +935,23 @@ static bool is_xid(const string &v) else if (c == '_'); else return false; + unsigned nest = 0; while (*s) { c = *s++; if ((c >= 'a') && (c <= 'z')); else if ((c >= 'A') && (c <= 'Z')); else if ((c >= '0') && (c <= '9')); else if (c == '_'); - else if (c == '>'); - else if (c == '<'); + else if (c == '>') + --nest; + else if (c == '<') + ++nest; + else if (c == ','); + else if (c == ':'); else return false; } - return true; + return nest == 0; } @@ -1004,6 +1009,19 @@ void Field::setOption(const string &option, const string &value) } else { stringtype = st_class; type = ft_string; + size_t n = value.size(); + const char *d = value.data(); + string v; + if (d[0] == '"') { + assert(d[n-1] == '"'); // must be true due to lexical analysis + v.assign(d+1,n-2); + } else { + v = value; + } + + if (!is_xid(v)) + error("invalid argument to stringtype: %s",value.c_str()); + options->setOption("stringtype",v.c_str()); } } else error("invalid option stringtype for field '%s' of type '%s'",name.c_str(),getTypeName()); diff --git a/src/Field.h b/src/Field.h index dbe9f1c..1fdb8b4 100644 --- a/src/Field.h +++ b/src/Field.h @@ -195,6 +195,9 @@ class Field const std::string &getJsonFunction() const { return json_value_func; } + const std::string &getParseAsciiFunction() const + { return parse_ascii_func; } + const char *getInvalidValue() const { return invvalue.empty() ? 0 : invvalue.c_str(); } diff --git a/src/Generator.cc b/src/Generator.cc index bd676db..00b627f 100644 --- a/src/Generator.cc +++ b/src/Generator.cc @@ -577,7 +577,7 @@ void Generator::setField(const Field *f) addVariable("ptype",ptype); sprintf(tmp,"%u",f->getTagSize()); addVariable("tagsize",tmp); - addVariable("parse_ascii",f->getOption("parse_ascii")); + addVariable("parse_ascii",f->getParseAsciiFunction()); if (Enum *e = f->toEnum()) setEnum(e); } else { diff --git a/src/Message.cc b/src/Message.cc index 8143e9c..9972f1b 100644 --- a/src/Message.cc +++ b/src/Message.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2020, Thomas Maier-Komor + * Copyright (C) 2017-2021, Thomas Maier-Komor * * This source file belongs to Wire-Format-Compiler. * @@ -107,6 +107,8 @@ void Message::addReservation(unsigned lb, unsigned ub) Field *Message::getField(const char *n) const { + if (n == 0) + return 0; for (auto i(m_fields.begin()), e(m_fields.end()); i != e; ++i) { Field *f = i->second; if ((f != 0) && (0 == strcmp(n, f->getName()))) diff --git a/src/Options.cc b/src/Options.cc index bb10238..5c94ae9 100644 --- a/src/Options.cc +++ b/src/Options.cc @@ -1080,8 +1080,7 @@ KVPair::~KVPair() void KVPair::setNext(KVPair *n) { - assert(n->next == 0); - n->next = next; + assert(next == 0); next = n; } diff --git a/src/codeid.h b/src/codeid.h index c14801b..63ba7ec 100644 --- a/src/codeid.h +++ b/src/codeid.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2020, Thomas Maier-Komor + * Copyright (C) 2017-2021, Thomas Maier-Komor * * This source file belongs to Wire-Format-Compiler. * @@ -35,6 +35,7 @@ typedef enum { ct_decode_early, ct_decode_union, ct_parse_ascii_bool, + ct_parse_ascii_bytes, ct_parse_ascii_dbl, ct_parse_ascii_flt, ct_parse_ascii_s16, diff --git a/src/wfc.cc b/src/wfc.cc index 518f829..67372c9 100644 --- a/src/wfc.cc +++ b/src/wfc.cc @@ -50,7 +50,7 @@ const char *InstallDir = 0; void printVersion(ostream &out) { out << "Wire Format Compiler (WFC), Version " VERSION "\n" - "Copyright 2015-2020, Thomas Maier-Komor\n" + "Copyright 2015-2021, Thomas Maier-Komor\n" "License: GPLv3 (see file LICENSE for details)\n"; } diff --git a/src/xproto.y b/src/xproto.y index 9556f32..a2b474c 100644 --- a/src/xproto.y +++ b/src/xproto.y @@ -153,7 +153,7 @@ OptionList_P : Option_P { $$ = $1; } | OptionList_P COMMA Option_P - { $1->setNext($3); $$ = $1; } + { $3->setNext($1); $$ = $3; } ; OptionArg_P diff --git a/testsuite/genwfc.mk b/testsuite/genwfc.mk index f7a7ce4..ac8a686 100644 --- a/testsuite/genwfc.mk +++ b/testsuite/genwfc.mk @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2020, Thomas Maier-Komor +# Copyright (C) 2019-2021, Thomas Maier-Komor # # This file belongs to Wire-Format-Compiler. # @@ -25,7 +25,7 @@ WFCSRCS = testcases/empty_elements.wfc testcases/enumtest.wfc testcases/validbit testcases/reference.wfc testcases/validbits2.wfc testcases/tt.wfc \ testcases/unused.wfc testcases/fixed_only.wfc testcases/novarint.wfc \ testcases/packed.wfc testcases/virtual.wfc testcases/byname.wfc \ - testcases/inv_def.wfc testcases/arraycheck.wfc + testcases/inv_def.wfc testcases/arraycheck.wfc testcases/binformats.wfc CXXSRCS = $(WFCSRCS:testcases/%.wfc=$(ODIR)/%.cpp) $(ODIR)/referencev2.cpp @@ -58,3 +58,9 @@ $(ODIR)/comp_v2.cpp: testcases/compatibility.wfc $(ODIR)/referencev2.cpp: testcases/reference.wfc "$(WFC)" $(WFCFLAGS) -trev2 -o $(ODIR)/referencev2 $^ + +$(ODIR)/binformats.cpp: testcases/binformats.wfc + "$(WFC)" $(WFCFLAGS) -tpc -o $(ODIR)/binformats_pc $^ + "$(WFC)" $(WFCFLAGS) -tesp8266 -o $(ODIR)/binformats_esp8266 $^ + "$(WFC)" $(WFCFLAGS) -tesp32 -o $(ODIR)/binformats_esp32 $^ + diff --git a/testsuite/ip4funcs.h b/testsuite/ip4funcs.h new file mode 100644 index 0000000..3294dfe --- /dev/null +++ b/testsuite/ip4funcs.h @@ -0,0 +1,20 @@ +#include +#include + +inline void ip4_ascii(std::ostream &str, uint32_t ip) +{ + str << (ip & 0xff) << '.' << ((ip >> 8) & 0xff) << '.' << ((ip >> 16) & 0xff) << '.' << ((ip >> 24 ) & 0xff); +} + + +static int parse_ipv4(uint32_t *ip, const char *str) +{ + uint8_t b[4]; + int n = sscanf(str,"%hhu.%hhu.%hhu.%hhu",b+0,b+1,b+2,b+3); + if (4 == n) { + *ip = ((uint32_t)b[0]) | ((uint32_t)b[1]<<8) | ((uint32_t)b[2]<<16) | ((uint32_t)b[3]<<24); + return 1; + } + std::cerr << "parser error " << n << "\n"; + return -1; +} diff --git a/testsuite/testcases/binformats.wfc b/testsuite/testcases/binformats.wfc new file mode 100644 index 0000000..3ceaf06 --- /dev/null +++ b/testsuite/testcases/binformats.wfc @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2018-2021, Thomas Maier-Komor + * Atrium Firmware Package for ESP + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +option common { + id0=true; + header="support.h"; + header="stream.h"; + header="estring.h"; + streamtype=stream; + stringtype=estring; + bytestype=estring; +} + +option pc : common { + toSink=""; + toASCII=toASCII; + optimize=speed; +} + +option esp : common { + withEqual=true; + toString=""; + toSink=""; + toWire=""; + toASCII=toASCII; + intsize=32; + varintbits=32; + //header="astring.h"; + //stringtype=AString; + //ascii_indent=null_indent; +} + +option esp32 : esp { + optimize=speed; + endian=little; // not for esp8266, does not allow unaligned access + withUnequal=true; + /AdcConfig/mode: used = false; + /AdcConfig/clk_div: used = false; +} + +option esp8266 : esp { + ssize_t=_ssize_t; + optimize=size; + withUnequal=false; + /TouchpadConfig: used = false; + /TouchChannelConfig: used = false; + /AdcChannel: used = false; + /HardwareConfig/uart: used = false; + /HardwareConfig/touchpad: used = false; + /HardwareConfig/tp_channel: used = false; + /Bme280Config/freq: used = false; + /UartConfig/tx_gpio: used = false; + /UartConfig/rx_gpio: used = false; + /UartConfig/cts_gpio: used = false; + /UartConfig/rts_gpio: used = false; + /AdcConfig/adc1_bits: used = false; + /AdcConfig/adc2_bits: used = false; + /AdcConfig/hall_name: used = false; + /AdcConfig/channels: used = false; + /Ws2812bConfig/ch: used = false; +} + +option esp8285 : esp8266 { + //header="astring.h"; + //stringtype=AString; + /OwDeviceConfig: used = false; + /NodeConfig/httpd: used = false; + /NodeConfig/app_params: used = false; + /NodeConfig/signals: used = false; + /NodeConfig/functions: used = false; + /NodeConfig/owdevices: used = false; + /HardwareConfig/max7219: used = false; + /HardwareConfig/tlc5947: used = false; + /HardwareConfig/ws2812b: used = false; + /HardwareConfig/hcsr04: used = false; + /HardwareConfig/onewire: used = false; +} + +option s20 : esp8285 { + /HardwareConfig/dht: used = false; + /HardwareConfig/terminal: used = false; +} + +option dht_term : esp8285 { + /NodeConfig/holidays: used = false; + /NodeConfig/at_actions: used = false; +} + +option d1_lite : esp8266 { + withUnequal=false; +} + +option termserv : d1_lite { + /NodeConfig/holidays: used = false; + /NodeConfig/at_actions: used = false; +} + + +enum rstrsn_t { + unknown = 0; + powerup = 1; + external = 2; + software = 3; + panic = 4; + internal_wdt = 5; + task_wdt = 6; + watchdog = 7; + deepsleep = 8; + brownout = 9; + sdio = 10; + option toString=strReset; +} + + +message WifiConfig +{ + string ssid = 1 [ unset = "" ]; + string pass = 2 [ unset = "" ]; + bytes mac = 3; + required bool activate = 4 [ unset = false ]; + fixed32 addr4 = 5 [ to_ascii=ip4_to_ascii, to_json=ip4_to_ascii, parse_ascii=parse_ipv4 ]; + fixed8 netmask4 = 6; + fixed32 gateway4 = 7 [ to_ascii=ip4_to_ascii, to_json=ip4_to_ascii, parse_ascii=parse_ipv4 ]; +} + + +message MQTT +{ + string uri = 1 [ unset = "" ]; + bool enable = 2 [ unset = false ]; + string username = 3 [ unset = "" ]; + string password = 4 [ unset = "" ]; + repeated string subscribtions = 5; // qos0 subscribptions +} + + +message Date +{ + fixed8 day = 1 [ unset = 0 ]; + fixed8 month = 2 [ unset = 0 ]; + fixed16 year = 3 [ unset = 0 ]; + fixed8 endday = 4 [ unset = 0 ]; + fixed8 endmonth = 5 [ unset = 0 ]; + fixed8 endyear = 6 [ unset = 0 ]; +} + + +enum WeekDay +{ + Sunday = 0; + Monday = 1; + Tuesday = 2; + Wednesday = 3; + Thursday = 4; + Friday = 5; + Saturday = 6; + WorkDay = 7; + WeekEnd = 8; + EveryDay = 9; + Holiday = 10; +} + + +message AtAction +{ + optional WeekDay day = 1; + unsigned min_of_day = 2 [ to_json = min_of_day_to_ascii ]; + string action = 3; + required bool enable = 4 [ default = true ]; +} + + +message Influx +{ + string hostname = 1 [ unset = "" ]; + fixed16 port = 2 [ unset = 0 ]; + string measurement = 3 [ unset = "" ]; + unsigned interval = 4 [ default = 60000, usage = deprecated ]; +} + + +message UartSettings +{ + uint8 port = 1; + unsigned baudrate = 2; + // bit0,1: uart_word_length_t [ 5..8 ] + // bit2,3: uart_stop_bits_t [ 1=1, 2=1.5, 3=2 ] + // bit4,5: uart_hw_flowcontrol_t [ 0=none, 1=rts, 2=cts, 3=cts+rts ] + // bit6,7: uart_parity_t [ 0=none, 2=even, 3=odd ] + // bit8,9: reserved for future uart_parity_t extension + // bit10: ref_tick + // bit11-15: reserved + fixed16 config = 3 [ unset = 0 ]; + fixed8 rx_thresh = 4 [ unset = 0 ]; + unsigned tx_bufsize = 6; + unsigned rx_bufsize = 7; +} + + +message FtpdConfig +{ + fixed16 port = 1 [ unset = 0 ]; + bool start = 2; + string root = 3 [ unset = "" ]; +} + + +message HttpdConfig +{ + fixed16 port = 1 [ default = 80 ]; + bool start = 2 [ default = true ]; + string root = 3 [ unset = "" ]; + string uploaddir = 4 [ unset = "" ]; +} + + +message TerminalConfig +{ + sint8 uart_rx = 1 [ unset = -1 ]; + sint8 uart_tx = 2 [ unset = -1 ]; + string name = 3; +} + + +message Trigger +{ + string event = 1; + string action = 2; +} + + +message AppParam +{ + string key = 1; + unsigned uValue = 2; + string sValue = 3; + signed dValue = 4; + double fValue = 5; +} + + +message EventTimer +{ + string name = 1 [ unset = "" ]; + unsigned time = 2; // in msec + // bit0: repeat, bit1: autostart + unsigned config = 3 [ unset = 0 ]; +} + + +message FunctionConfig +{ + string name = 1; + string func = 2; + repeated string params = 3; +} + + +enum sigtype_t +{ + st_invalid = 0; + st_int = 1; + st_float = 2; + st_string = 3; +} + + +message SignalConfig +{ + string name = 1 [ unset = "" ]; + sigtype_t type = 2 [ unset = st_invalid ]; + string iv = 3 [ unset = "" ]; + // TODO + //bool persistent = 4 [ unset = false ]; // store every update to NVS +} + + +message OwDeviceConfig +{ + fixed64 id = 1 [ to_ascii = id64_to_ascii ]; + string name = 2; +} + + +message NodeConfig +{ + fixed32 magic = 0; // should always be set to 0xAE54EDC0 + string nodename = 1 [ unset = "" ]; + bytes pass_hash = 2; + unsigned cpu_freq = 3; + WifiConfig station = 4; + WifiConfig softap = 5; + string dns_server = 6 [ unset = "" ]; + string syslog_host = 7 [ unset = "" ]; + string sntp_server = 8 [ unset = "" ]; + string timezone = 9 [ unset = "" ]; + MQTT mqtt = 10; + fixed16 dmesg_size = 11 [ default = 512 ]; + Influx influx = 12; + unsigned station2ap_time = 13; // failover time from station to ap mode [s] + string domainname = 15 [ unset = "" ]; + + // action timers + repeated Date holidays = 16; + repeated AtAction at_actions = 17; + // bit0: enable, bit1: enable factory_reset + unsigned actions_enable = 18 [ default = 1 ]; + repeated Trigger triggers = 19; + + repeated UartSettings uart = 20; + repeated TerminalConfig terminal = 21; + + // set udp_ctrl_port to 0 to disable this feature + fixed16 udp_ctrl_port = 22 [ default = 12719 ]; + + repeated string debugs = 23; + + // TODO + //FtpdConfig ftpd = 24; + HttpdConfig httpd = 25; + + // event timer/timefuse + repeated EventTimer timefuses = 30; + repeated SignalConfig signals = 31; + repeated FunctionConfig functions = 32; + + // relay control (e.g. s20) + unsigned max_on_time = 34 [ unset = 0, usage=deprecated ]; // in minutes + + // application: light sensor controlled LED + unsigned threshold_off = 35; + unsigned threshold_on = 36; + unsigned dim_step = 37; // ms per 1% step + bool lightctrl = 38; // true=auto, false=manual + unsigned pwm_freq = 39; // base PWM frequenzy in Hz - shared for all channels + + repeated AppParam app_params = 40; + + repeated OwDeviceConfig owdevices = 50; +} + + +message SystemConfig +{ + string manufacturer = 1 [ unset = ""]; + string board_name = 2 [ unset = ""]; + string board_rev = 3 [ unset = ""]; + sint8 diag_uart = 4 [ unset = 0 ]; + sint8 console_rx = 5 [ default = 0 ]; + sint8 console_tx = 6 [ default = 0 ]; + string model_name = 7 [ unset = ""]; + string model_number = 8 [ unset = ""]; +} + + +message TouchpadConfig +{ + bool fsm_mode = 1 [ unset = false ]; + sint8 lvolt = 2 [ unset = -1 ]; + sint8 hvolt = 3 [ unset = -1 ]; + sint8 atten = 4 [ unset = -1 ]; + unsigned interval = 7; +} + + +message TouchChannelConfig +{ + string name = 1; + sint8 channel = 2 [ unset = -1 ]; + fixed16 threshold = 3; + uint8 slope = 4; + uint8 tieopt = 5; +} + + +enum pull_mode_t +{ + option allow_alias = true; + pull_none = 0; + pull_en = 1; + pull_dir = 2; + pull_down = 1; + pull_up = 3; +} + + +message ButtonConfig +{ + string name = 1 [ unset = "" ]; + sint8 gpio = 2 [ unset = -1 ]; + bool presslvl = 3 [ unset = 0 ]; + pull_mode_t pull_mode = 4 [ unset = pull_none ]; +} + + +enum relay_cfg_t +{ + rc_active_low = 0; + rc_active_high = 1; + rc_init_on = 2; + rc_persistent = 4; // store state in NVS and restore state on boot + rc_open_drain = 8; + rc_mqtt = 16; +} + + +message RelayConfig +{ + string name = 1 [ unset = "" ]; + sint8 gpio = 2 [ unset = -1 ]; + relay_cfg_t config = 3 [ unset = rc_active_low ]; + unsigned min_itv = 4 [ default = 0 ]; // minimum interval + sint8 interlock = 5 [ unset = -1 ]; // index of relay to interlock with +} + + +message Max7219Config +{ + sint8 clk = 1 [ unset = -1 ]; + sint8 dout = 2 [ unset = -1 ]; + sint8 cs = 3 [ unset = -1 ]; + bool odrain = 4 [ unset = false ]; + uint8 digits = 5 [ unset = 0 ]; +} + + +message Tlc5947Config +{ + sint8 sin = 1 [ unset = -1 ]; + sint8 sclk = 2 [ unset = -1 ]; + sint8 xlat = 3 [ unset = -1 ]; + sint8 blank = 4 [ unset = -1 ]; + uint8 ntlc = 5 [ unset = 0 ]; +} + + +message Ws2812bConfig +{ + sint8 gpio = 1 [ unset = -1 ]; + sint8 ch = 2 [ unset = -1 ]; + uint8 nleds = 3 [ unset = 0 ]; +} + + +enum dht_model_t +{ + DHT_NONE = 0; + RHT03 = 3; + DHT11 = 11; + DHT21 = 21; + DHT22 = 22; + AM2301 = 2301; + AM2302 = 2302; +} + + +message DhtConfig +{ + dht_model_t model = 1 [ unset = DHT_NONE ]; + sint8 gpio = 2 [ unset = -1 ]; +} + + +message Bme280Config +{ + uint8 port = 1 [ default = 0 ]; + sint8 sda = 2 [ unset = -1 ]; + sint8 scl = 3 [ unset = -1 ]; + unsigned freq = 4 [ default = 100000 ]; +} + + +message HcSr04Config +{ + sint8 trigger = 1 [ unset = -1 ]; + sint8 echo = 2 [ unset = -1 ]; +} + + +message LedConfig +{ + sint8 gpio = 1 [ unset = -1 ]; + // bit 0: active high + // bit 1: open drain + uint8 config = 2; + string name = 3 [ unset = "" ]; + sint8 pwm_ch = 4 [ unset = -1 ]; +} + + +message OneWireConfig +{ + sint8 gpio = 1 [ unset = -1 ]; + bool pullup = 2 [ unset = false ]; // pull-up the gpio + sint8 power = 3 [ unset = -1 ]; +} + + +message UartConfig +{ + sint8 port = 1; + sint8 tx_gpio = 2; + sint8 rx_gpio = 3; + sint8 cts_gpio = 4; + sint8 rts_gpio = 5; +} + + +message AdcChannel +{ + string name = 1 [ unset = "" ]; + + // ADC1 or ADC2 - ADC2 cannot be used with WiFi started + uint8 unit = 2 [ unset = 0 ]; + + // valid channels: 0-9 + sint8 ch = 3 [ unset = -1 ]; + + // adc_atten_t: 0 = 0dB, 1=2,5dB, 2=6dB, 3=11dB + uint8 atten = 4 [ default = 0 ]; +} + + +message AdcConfig +{ + // esp8266: set name to enable + // esp32: not used + string adc_name = 1 [ unset = "" ]; + + // adc_width_t: 0=9bit .. 3=12bit, adc is disabled if not set + uint8 adc1_bits = 2 [ default = 0 ]; + uint8 adc2_bits = 3 [ default = 0 ]; + + uint8 mode = 4; // esp8266 only (adc_mode_t: TOUT_MODE=0, VDD_MODE=1) + uint8 clk_div = 5; // esp8266 only + + // name of hall sensor + string hall_name = 6 [ unset = "" ]; + + repeated AdcChannel channels = 7; +} + + +message GpioConfig +{ + string name = 1; + sint8 gpio = 2 [ unset = -1 ]; + // bit 1,0: gpio_mode_t: disable, input, output, output_od + // bit 4..2: gpio_int_type_t + // {0=none, 1=pos dge, 2=neg edge, 3=both edge + // , 4=lolevel, 5=hilevel} + // bit 5: set initial level + // bit 6: init level + // bit 7: pull-up enable + // bit 8: pull-down enable + unsigned config = 3; +} + + +message HardwareConfig +{ + fixed32 magic = 0; // should always be set to 0xAE54EDCB + // core hardware + SystemConfig system = 1; + repeated UartConfig uart = 3; + AdcConfig adc = 4; + TouchpadConfig touchpad = 5; + repeated TouchChannelConfig tp_channel = 6; + repeated GpioConfig gpio = 7; + + // direct gpio drivers + repeated ButtonConfig button = 16; + repeated RelayConfig relay = 17; + repeated LedConfig led = 18; + + // high level applicaton drivers + Max7219Config max7219 = 32; // for 7-segment clock + Tlc5947Config tlc5947 = 33; // for nightsky LEDs + Ws2812bConfig ws2812b = 34; // for ws2812b LED strips + DhtConfig dht = 35; + Bme280Config bme280 = 36; + HcSr04Config hcsr04 = 37; + OneWireConfig onewire = 38; +} + + +/* +// draft/idea - maybe better direct structs for mmap access +enum ledaction_t +{ + la_nop = 0; // no operation + la_set = 1; // set specific led + la_mask = 2; // set color mask + la_setall = 3; // set all leds to value + la_delay = 4; // delay ms + la_dim = 5; // r/g/b delta as int8 + la_jump = 6; // index of next action to execute + la_random = 7; // generate random arg +} + +message LedAction +{ + ledaction_t action = 1; + // 0xff000000 : led mask + // 0x00ff0000 : red mask + // 0x0000ff00 : green mask + // 0x000000ff : blue mask + unsigned arg = 2; +} + + +message LedStripMode +{ + string name = 1; + repeated LedAction actions = 2; +} + + +message LedStripData +{ + fixed32 magic = 0; + repeated LedStripMode modes = 1; + +} +*/ diff --git a/testsuite/testcases/byname.wfc b/testsuite/testcases/byname.wfc index 6834759..e807e98 100644 --- a/testsuite/testcases/byname.wfc +++ b/testsuite/testcases/byname.wfc @@ -1,4 +1,5 @@ //option toASCII=toASCII; +option header = "ip4funcs.h"; message Pair { @@ -32,4 +33,10 @@ message M bool B = 11; repeated bool RB = 12; + + bytes data = 13; + + fixed32 ipv4 = 14 [ to_ascii = ip4_ascii, parse_ascii = parse_ipv4 ]; + + repeated bytes sectors = 15; } diff --git a/testsuite/testcases/bynametest.cpp b/testsuite/testcases/bynametest.cpp index bb73048..1a8fae9 100644 --- a/testsuite/testcases/bynametest.cpp +++ b/testsuite/testcases/bynametest.cpp @@ -30,6 +30,18 @@ int main() assert(x > 0); assert(tb.kvpair1().key() == "key"); runcheck(tb); + x = tb.setByName("BYTESO","deadbeef feed\raa\n00\tcc"); + assert(x > 0); + uint8_t data[] = {0xde,0xad,0xbe,0xef,0xfe,0xed,0xaa,0x00,0xcc}; + assert(tb.BYTESO().size() == sizeof(data)); + assert(0 == memcmp(tb.BYTESO().data(),data,sizeof(data))); + runcheck(tb); + x = tb.setByName("ipv4","10.158.66.3"); + assert(x > 0); + runcheck(tb); + stringstream ss; + ip4_ascii(ss,tb.ipv4()); + assert(ss.str() == "10.158.66.3"); // invalid settings x = tb.setByName("SI8","-129"); @@ -45,6 +57,8 @@ int main() assert(x > 0); assert(tb.PackedMsg().Bool() == false); runcheck(tb); + x = tb.setByName("BYTESO","deanbeeffeedaa00cc"); + assert(x < 0); // arrays x = tb.setByName("kvpairs[+]",""); diff --git a/testsuite/testcases/reference.wfc b/testsuite/testcases/reference.wfc index b9aedd4..88cc163 100644 --- a/testsuite/testcases/reference.wfc +++ b/testsuite/testcases/reference.wfc @@ -5,6 +5,7 @@ option SortMembers=type; option toString="toString"; option toJSON="toJSON"; option header="cstring.h"; +option header="ip4funcs.h"; option rev2 { withEqual=true; @@ -14,6 +15,7 @@ option rev2 { toString=toString; toJSON=toJSON; header="cstring.h"; + header="ip4funcs.h"; /TestBench/VI32: usage = obsolete; /TestBench/STR: usage = obsolete; /TestBench/STRV: usage = obsolete; @@ -149,4 +151,6 @@ message TestBench sint16 SI16 = 0x75; sint32 SI32 = 0x76; sint64 SI64 = 0x77; + + fixed32 ipv4 = 0x80 [ to_ascii = ip4_ascii, parse_ascii = parse_ipv4 ]; } diff --git a/testsuite/testcases/validbits2.wfc b/testsuite/testcases/validbits2.wfc index 9a674d3..d96110f 100644 --- a/testsuite/testcases/validbits2.wfc +++ b/testsuite/testcases/validbits2.wfc @@ -3,6 +3,7 @@ option header="math.h"; option header="cstring.h"; +option header="bytes.h"; option withEqual=true; option withUnequal=true; @@ -25,8 +26,8 @@ message M string s3i = 6 [ stringtype=CString, unset="" ]; bytes b1e = 11 [ bytestype=std::string ]; bytes b1i = 12 [ bytestype=std::string, unset="" ]; - bytes b2e = 13 [ bytestype=CString]; - bytes b2i = 14 [ bytestype=CString, unset="" ]; + bytes b2e = 13 [ bytestype=Bytes]; + bytes b2i = 14 [ bytestype=Bytes, unset="" ]; finger_t e1e = 20; finger_t e1i = 21 [ unset=nofinger ]; float f1e = 30;