From 0e3e1b3ee142cc9b0591135a43710ed86af0288b Mon Sep 17 00:00:00 2001 From: Jonathan Hanks <jonathan.hanks@ligo.org> Date: Fri, 15 Apr 2022 15:58:24 -0700 Subject: [PATCH] EPICS Sequencer - Rework BURT parseLine in terms of fixed vectors and strings. * Remove the possibility to have variable string lengths passed in. * Work with an out parameter that knows its size and can assert it to the caller. --- src/epics/seq/burt_file.hh | 214 ++++++++++--------- src/epics/seq/fixed_size_string.hh | 13 ++ src/epics/seq/main.cc | 64 +++--- src/epics/seq/test/test_burtfile.cc | 28 +-- src/epics/seq/test/test_fixed_size_string.cc | 14 ++ 5 files changed, 175 insertions(+), 158 deletions(-) diff --git a/src/epics/seq/burt_file.hh b/src/epics/seq/burt_file.hh index 5cf6b8d92..f28ba6d06 100644 --- a/src/epics/seq/burt_file.hh +++ b/src/epics/seq/burt_file.hh @@ -6,8 +6,15 @@ #define DAQD_TRUNK_BURT_FILE_HH #include "fixed_size_string.hh" +#include "fixed_size_vector.hh" -/** +namespace BURT +{ + + using parsed_word = embedded::fixed_string< 128 >; + using parsed_line = embedded::fixed_size_vector< parsed_word, 4 >; + + /** * @brief Encode a string for output into a BURT file. * @note BURT encoding of strings does not escape quotes, * and lists empty strings as \0 (backslash and 0) @@ -16,126 +23,127 @@ * @tparam DEST_SIZE Must be at least 2 greater than SRC_SIZE * @param src Input string to encode. * @param dest Destination for an encoded version of src. - */ -template < std::size_t SRC_SIZE, std::size_t DEST_SIZE > -void -encodeBURTString( const embedded::fixed_string< SRC_SIZE >& src, - embedded::fixed_string< DEST_SIZE >& dest ) -{ - static_assert( DEST_SIZE >= ( SRC_SIZE + 2 ), - "The destination buffer must be at last bytes larger than " - "the source to account for quoting" ); - static_assert( DEST_SIZE >= 2, - "The destination buffer must be long enough to burt encode " - "a NULL string" ); - - if ( src.empty( ) ) + */ + template < std::size_t SRC_SIZE, std::size_t DEST_SIZE > + void + encodeBURTString( const embedded::fixed_string< SRC_SIZE >& src, + embedded::fixed_string< DEST_SIZE >& dest ) { - dest = "\\0"; - return; - } + static_assert( + DEST_SIZE >= ( SRC_SIZE + 2 ), + "The destination buffer must be at last bytes larger than " + "the source to account for quoting" ); + static_assert( + DEST_SIZE >= 2, + "The destination buffer must be long enough to burt encode " + "a NULL string" ); - bool expand = false; + if ( src.empty( ) ) + { + dest = "\\0"; + return; + } - for ( char ch : src ) - { - if ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '"' ) + bool expand = false; + + for ( char ch : src ) { - expand = true; - break; + if ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '"' ) + { + expand = true; + break; + } + } + // we already bounds check this above + if ( expand ) + { + dest.printf( "\"%s\"", src.c_str( ) ); + } + else + { + dest = src; } } - // we already bounds check this above - if ( expand ) - { - dest.printf( "\"%s\"", src.c_str( ) ); - } - else - { - dest = src; - } -} -/// Common routine to parse lines read from BURT/SDF files.. -/// @param[in] *s Pointer to line of chars to parse. -/// @param[in] bufSize Size in characters of each output buffer str1 thru str6 -/// @param[out] str1 thru str6 String pointers to return individual words from line. -/// @return wc Number of words in the line. -static inline -int parseLine(const char *s, int bufSize, char *str1, char *str2, char *str3, char *str4, char *str5,char *str6) -{ - int wc = 0; - int ii = 0; - int cursize = 0; - int lastwasspace = 1; + /// Common routine to parse lines read from BURT/SDF files.. + /// @param[in] *s Pointer to line of chars to parse. + /// @param[out] Output containing all the identified words in the line + /// @return wc Number of words in the line. + template <std::size_t N_WORDS, std::size_t WORD_LEN> + static inline int + parseLine( const char* s, + embedded::fixed_size_vector< + embedded::fixed_string<WORD_LEN>, + N_WORDS>& out ) + { + int lastwasspace = 1; - const char *lastquote = nullptr; - const char *qch = nullptr; - char *out[7]; + const char* lastquote = nullptr; + const char* qch = nullptr; - out[0]=str1; - out[1]=str2; - out[2]=str3; - out[3]=str4; - out[4]=str5; - out[5]=str6; - out[6]=nullptr; - for (ii = 0; out[ii]; ++ii) { *(out[ii]) = '\0'; } + out.clear(); + embedded::fixed_string<WORD_LEN> cur_word{}; - while (*s != 0 && *s != '\n') { - if (*s == ' ' || *s == '\t') { - // force the null termination of the output buffer - out[wc][bufSize-1] = '\0'; - if (!lastwasspace) { - ++wc; - // check for the end of the output buffers - if (!out[wc]) break; + while ( *s != 0 && *s != '\n' && out.capacity() > out.size() ) + { + if ( *s == ' ' || *s == '\t' ) + { + if ( !lastwasspace ) + { + out.push_back( cur_word ); + cur_word.clear(); + } + lastwasspace = 1; } - lastwasspace = 1; - cursize = 0; - } else if (*s != '"' || !lastwasspace) { - // regular character - if (cursize < bufSize) { - out[wc][cursize] = *s; - cursize++; + else if ( *s != '"' || !lastwasspace ) + { + cur_word += *s; + lastwasspace = 0; } - lastwasspace = 0; - } else { - // quote - // burt does not escape quotes, you have to look for the last quote and just take it. - lastquote = nullptr; - qch = s+1; + else + { + // quote + // burt does not escape quotes, you have to look for the last quote and just take it. + lastquote = nullptr; + qch = s + 1; - while (*qch && *qch !='\n') { - if (*qch == '"') lastquote = qch; - ++qch; - } - if (!lastquote) lastquote = qch; - ++s; - // copy from (s,qch) then set lastwasspace - while (s < lastquote) { - if (cursize < bufSize) { - out[wc][cursize] = *s; - cursize++; + while ( *qch && *qch != '\n' ) + { + if ( *qch == '"' ) + lastquote = qch; + ++qch; } + if ( !lastquote ) + lastquote = qch; ++s; + // copy from (s,qch) then set lastwasspace + while ( s < lastquote ) + { + cur_word += *s; + ++s; + } + out.push_back( cur_word ); + cur_word.clear(); + lastwasspace = 1; + if ( !( *s ) || *s == '\n' || out.size() == out.capacity() ) + break; } - out[wc][bufSize-1] = '\0'; - ++wc; - cursize = 0; - lastwasspace = 1; - if (!(*s) || *s == '\n' || !out[wc]) break; + ++s; } - ++s; - } - if (out[wc] && out[wc][0]) { - out[wc][bufSize-1]='\0'; - ++wc; - } - for (ii = 0; out[ii]; ++ii) { - if (strcmp(out[ii], "\\0") == 0) strcpy(out[ii], ""); + if ( !cur_word.empty( ) ) + { + out.push_back( cur_word ); + } + for ( auto& word:out ) + { + if ( word == "\\0" ) + { + word = ""; + } + } + return out.size(); } - return wc; + } #endif // DAQD_TRUNK_BURT_FILE_HH diff --git a/src/epics/seq/fixed_size_string.hh b/src/epics/seq/fixed_size_string.hh index e647760d7..6d88eb325 100644 --- a/src/epics/seq/fixed_size_string.hh +++ b/src/epics/seq/fixed_size_string.hh @@ -103,6 +103,19 @@ namespace embedded return *this; } + fixed_string& + operator+=( char in ) noexcept + { + if ( in && ( remaining() > 0 ) ) + { + size_type cur_size = size( ); + data_[ cur_size ] = in; + data_[ cur_size + 1 ] = 0; + } + return *this; + } + + fixed_string& operator+=( const char* in ) noexcept { diff --git a/src/epics/seq/main.cc b/src/epics/seq/main.cc index a3dc2d222..96d075ad4 100644 --- a/src/epics/seq/main.cc +++ b/src/epics/seq/main.cc @@ -1066,7 +1066,7 @@ bool writeTable2File(const char *burtdir, if(myTable[ii].datatype == SDF_NUM) { fprintf(csFile,"%s %d %.*e %s %d\n",entry.chname,1,precision,entry.data.chval,monitorstring.c_str(),entry.initialized); } else { - encodeBURTString(entry.data.strval, burtString); + BURT::encodeBURTString(entry.data.strval, burtString); fprintf(csFile,"%s %d %s %s %d\n",entry.chname,1,burtString.c_str(),monitorstring.c_str(),entry.initialized); } break; @@ -1075,7 +1075,7 @@ bool writeTable2File(const char *burtdir, if(entry.datatype == SDF_NUM) { fprintf(csFile,"%s %d %.*e %s\n",entry.chname,1,precision,entry.data.chval,monitorstring.c_str()); } else { - encodeBURTString(entry.data.strval, burtString ); + BURT::encodeBURTString(entry.data.strval, burtString ); fprintf(csFile,"%s %d %s %s\n",entry.chname,1,burtString.c_str(),monitorstring.c_str()); } } @@ -1084,7 +1084,7 @@ bool writeTable2File(const char *burtdir, if(entry.datatype == SDF_NUM) { fprintf(csFile,"%s %d %.*e\n",entry.chname,1,precision,entry.data.chval); } else { - encodeBURTString(entry.data.strval, burtString ); + BURT::encodeBURTString(entry.data.strval, burtString ); fprintf(csFile,"%s %d %s \n",entry.chname,1,burtString.c_str()); } break; @@ -1935,7 +1935,6 @@ int readConfig( const char *pref, ///< EPICS channel prefix from EPICS environm char c=0; int ii=0; int lock=0; - char s1[128],s2[128],s3[128],s4[128],s5[128],s6[128]; char ls[6][64]; ADDRESS paddr; dbAddr reloadDbAddr; @@ -1962,9 +1961,6 @@ int readConfig( const char *pref, ///< EPICS channel prefix from EPICS environm const static char* header_start = "--- Start"; const static char* header_end = "--- End"; - - - s1[0]=s2[0]=s3[0]=s4[0]=s5[0]=s6[0]='\0'; line[0]='\0'; ifo[0]='\0'; fname[0]=errMsg[0]='\0'; @@ -2001,9 +1997,6 @@ int readConfig( const char *pref, ///< EPICS channel prefix from EPICS environm } chNumP = 0; alarmCnt = 0; - // Put dummy in s4 as this column may or may not exist. - strcpy(s4,"x"); - bzero(s3,sizeof(s3)); strncpy(ifo,pref,3); chNotFound = 0; while(fgets(line,sizeof line,cdf) != nullptr) @@ -2026,22 +2019,22 @@ int readConfig( const char *pref, ///< EPICS channel prefix from EPICS environm isalarm = 0; lineCnt ++; - strcpy(s4,"x"); - argcount = parseLine(line,sizeof(s1),s1,s2,s3,s4,s5,s6); - if (strcmp(s4, "") == 0) strcpy(s4, "0"); - if(argcount == -1) { - readErrTable[rderror].chname = s1; - readErrTable[rderror].burtset = "Improper quotations "; - readErrTable[rderror].liveset.printf("Line # %d", lineCnt); - readErrTable[rderror].liveval = 0.0; - readErrTable[rderror].diff = sdfile; - readErrTable[rderror].timeset = timestring; - rderror ++; - printf("Read error --- %s\n",s1); - continue; - } - // Only 3 = no monit flag - // >=4 count be monit flag or string with quotes + BURT::parsed_line words{}; + argcount = BURT::parseLine(line, words); + // Only 3 = no monit flag + // >=4 count be monit flag or string with quotes + if ( argcount < 3 ) + { + continue; + } + if ( argcount < 4 ) + { + words.push_back(BURT::parsed_word("0")); + } + const char* s1 = words[0].c_str(); + const char* s2 = words[1].c_str(); + const char* s3 = words[2].c_str(); + const char* s4 = words[3].c_str(); // If 1st three chars match IFO ie checking this this line is not BURT header or channel marked RO if( // Don't allow load of SWSTAT or SWMASK, which are set by this program. @@ -2656,7 +2649,7 @@ void connectCallback(struct connection_handler_args args) { } /// Routine to register a channel -void registerPV(char *PVname) +void registerPV(const char *PVname) { long status=0; chid chid1; @@ -2712,16 +2705,17 @@ int daqToSDFDataType(int daqtype) { void parseChannelListReq(char *fname) { FILE *f = 0; char line[128]; - char s1[128],s2[128],s3[128],s4[128],s5[128],s6[128]; int argcount = 0; int in_header = 0; const static char* header_start = "--- Start"; const static char* header_end = "--- End"; - line[0]=s1[0]=s2[0]=s3[0]=s4[0]=s5[0]=s6[0]='\0'; + line[0]='\0'; f = fopen(fname, "r"); if (!f) return; + embedded::fixed_size_vector<embedded::fixed_string<128>, 1> words; + while (fgets(line, sizeof(line), f) != NULL) { if (in_header) { @@ -2736,13 +2730,13 @@ void parseChannelListReq(char *fname) { in_header = 1; continue; } - argcount = parseLine(line, sizeof(s1), s1, s2, s3, s4, s5, s6); + argcount = BURT::parseLine(line, words); if (argcount < 1) continue; - if (strstr(s1,"_SWMASK") != NULL || - strstr(s1,"_SDF_NAME") != NULL || - strstr(s1,"_SWREQ") != NULL) continue; - if (isAlarmChannelRaw(s1)) continue; - registerPV(s1); + if (strstr(words.front().c_str(),"_SWMASK") != NULL || + strstr(words.front().c_str(),"_SDF_NAME") != NULL || + strstr(words.front().c_str(),"_SWREQ") != NULL) continue; + if (isAlarmChannelRaw(words.front().c_str())) continue; + registerPV(words.front().c_str()); } fclose(f); } diff --git a/src/epics/seq/test/test_burtfile.cc b/src/epics/seq/test/test_burtfile.cc index 8f49b6a16..be9f24231 100644 --- a/src/epics/seq/test/test_burtfile.cc +++ b/src/epics/seq/test/test_burtfile.cc @@ -27,11 +27,11 @@ TEST_CASE( "Test encodeBURTString" ) { embedded::fixed_string< 18 > src( test_case.input ); embedded::fixed_string< 20 > dest; - encodeBURTString( src, dest ); + BURT::encodeBURTString( src, dest ); REQUIRE( strcmp( test_case.expected_output, dest.c_str( ) ) == 0 ); } } - +#include <iostream> TEST_CASE( "Test parseLine" ) { struct TestCase @@ -65,27 +65,15 @@ TEST_CASE( "Test parseLine" ) }; for ( const auto& test_case: test_cases) { - std::array<char[16], 6> out{}; - std::vector<char> input(test_case.input, test_case.input + strlen(test_case.input) + 1); - /* Canary goes at the end of each output to check for overflow */ - for (auto& out_entry:out) - { - out_entry[sizeof(out[0])-1] = 0x7f; - } - int wc = parseLine(input.data(), sizeof(out[0])-1, &out[0][0], &out[1][0], &out[2][0], &out[3][0], &out[4][0],&out[5][0]); + embedded::fixed_size_vector<embedded::fixed_string<15>, 6> out{}; + + std::cout << test_case.input << "\n"; + int wc = BURT::parseLine(test_case.input, out); REQUIRE(wc == test_case.expected_output.size()); + REQUIRE(wc == out.size()); for (auto i = 0; i < wc; ++i) { - REQUIRE(test_case.expected_output[i] == out[i]); - } - std::string empty{}; - for (auto i = wc; i < out.size(); ++i) - { - REQUIRE( empty == out[i] ); - } - for (auto& out_entry:out) - { - REQUIRE(out_entry[sizeof(out[0])-1] == 0x7f); + REQUIRE(out[i] == test_case.expected_output[i].c_str()); } } } \ No newline at end of file diff --git a/src/epics/seq/test/test_fixed_size_string.cc b/src/epics/seq/test/test_fixed_size_string.cc index 8792c5317..08920e23b 100644 --- a/src/epics/seq/test/test_fixed_size_string.cc +++ b/src/epics/seq/test/test_fixed_size_string.cc @@ -199,6 +199,20 @@ TEST_CASE( "You can append to a fixed size string" ) embedded::fixed_string< 64 > s; embedded::fixed_string< 6 > single( "0" ); embedded::fixed_string< 6 > five( "00000" ); + + for ( int i = 1; i <= 100; ++i ) + { + s += '0'; + std::size_t expected_zeros = + ( i < s.capacity( ) ? i : s.capacity( ) - 1 ); + REQUIRE( s.size( ) == expected_zeros ); + std::size_t j = 0; + for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j ) + { + } + REQUIRE( j == expected_zeros ); + } + s.clear(); for ( int i = 1; i <= 100; ++i ) { s += "0"; -- GitLab