From 2911263a7d214827045a074427281a18820894d0 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Wed, 19 Aug 2026 18:52:08 +0000 Subject: [PATCH 01/23] Per METplus-Internal/#68, switch gmtime() calls to gmtime_r(). --- src/basic/vx_util/main.cc | 24 ++++++----- src/basic/vx_util/observation.h | 9 ++-- src/libcode/vx_summary/summary_obs.h | 42 ++++++++++--------- .../vx_time_series/compute_swinging_door.h | 13 +++--- src/tools/dev_utils/insitu_nc_to_ascii.cc | 21 ++++------ 5 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/basic/vx_util/main.cc b/src/basic/vx_util/main.cc index 9d59372f18..5824f238ec 100644 --- a/src/basic/vx_util/main.cc +++ b/src/basic/vx_util/main.cc @@ -139,14 +139,12 @@ void do_post_process() { //////////////////////////////////////////////////////////////////////// string get_current_time() { - time_t curr_time; - tm * curr_tm; - char date_string[MET_BUF_SIZE]; - - time(&curr_time); - curr_tm = gmtime (&curr_time); + time_t curr_time = time(nullptr); + struct tm curr_tm; + gmtime_r(&curr_time, &curr_tm); - strftime(date_string, MET_BUF_SIZE, "%Y-%m-%d %TZ", curr_tm); + char date_string[MET_BUF_SIZE]; + strftime(date_string, MET_BUF_SIZE, "%Y-%m-%d %TZ", &curr_tm); return string(date_string); } @@ -184,10 +182,14 @@ void set_handlers() { //////////////////////////////////////////////////////////////////////// void set_user_id() { - met_user_id = geteuid (); - struct passwd *pw; - pw = getpwuid (met_user_id); - if (pw) met_user_name = string(pw->pw_name); + met_user_id = geteuid(); + struct passwd pw; + struct passwd *result = nullptr; + std::vector buf(MET_BUF_SIZE+1); + int ret = getpwuid_r(met_user_id, &pw, buf.data(), buf.size(), &result); + if(ret == 0 && result != nullptr) { + met_user_name = string(pw.pw_name); + } } //////////////////////////////////////////////////////////////////////// diff --git a/src/basic/vx_util/observation.h b/src/basic/vx_util/observation.h index c3eba0bcae..a59bcda894 100644 --- a/src/basic/vx_util/observation.h +++ b/src/basic/vx_util/observation.h @@ -234,15 +234,16 @@ class Observation static std::string _getTimeString(const time_t &unix_time) { - struct tm *time_struct = gmtime(&unix_time); + struct tm time_struct; + gmtime_r(&unix_time, &time_struct); char time_string[tmp_buf_size]; snprintf(time_string, sizeof(time_string), "%04d%02d%02d_%02d%02d%02d", - time_struct->tm_year + 1900, time_struct->tm_mon + 1, - time_struct->tm_mday, time_struct->tm_hour, - time_struct->tm_min, time_struct->tm_sec); + time_struct.tm_year + 1900, time_struct.tm_mon + 1, + time_struct.tm_mday, time_struct.tm_hour, + time_struct.tm_min, time_struct.tm_sec); return std::string(time_string); } diff --git a/src/libcode/vx_summary/summary_obs.h b/src/libcode/vx_summary/summary_obs.h index b1b9431c09..317acde918 100644 --- a/src/libcode/vx_summary/summary_obs.h +++ b/src/libcode/vx_summary/summary_obs.h @@ -117,10 +117,11 @@ class SummaryObs static int unixtimeToSecs(const time_t unix_time) { - struct tm *time_struct = gmtime(&unix_time); + struct tm time_struct; + gmtime_r(&unix_time, &time_struct); - return (time_struct->tm_hour * 3600) + - (time_struct->tm_min * 60) + time_struct->tm_sec; + return (time_struct.tm_hour * 3600) + + (time_struct.tm_min * 60) + time_struct.tm_sec; } // Convert the number of seconds from the beginning of the day to a string @@ -150,7 +151,8 @@ class SummaryObs static time_t getFirstIntervalOfDay(const time_t test_time, const int begin_secs, const int end_secs, const int step) { - struct tm *time_struct = gmtime(&test_time); + struct tm time_struct; + gmtime_r(&test_time, &time_struct); int start_of_day_secs = 0; @@ -174,15 +176,15 @@ class SummaryObs start_of_day_secs = begin_secs % step; } - time_struct->tm_hour = start_of_day_secs / 3600; - start_of_day_secs -= time_struct->tm_hour * 3600; + time_struct.tm_hour = start_of_day_secs / 3600; + start_of_day_secs -= time_struct.tm_hour * 3600; - time_struct->tm_min = start_of_day_secs / 60; - start_of_day_secs -= time_struct->tm_min * 60; + time_struct.tm_min = start_of_day_secs / 60; + start_of_day_secs -= time_struct.tm_min * 60; - time_struct->tm_sec = start_of_day_secs; + time_struct.tm_sec = start_of_day_secs; - return timegm(time_struct); + return timegm(&time_struct); } // Get the interval time of the interval that contains the given data @@ -212,26 +214,28 @@ class SummaryObs static time_t getEndOfDay(const time_t unix_time) { - struct tm *time_struct = gmtime(&unix_time); + struct tm time_struct; + gmtime_r(&unix_time, &time_struct); - time_struct->tm_hour = 23; - time_struct->tm_min = 59; - time_struct->tm_sec = 59; + time_struct.tm_hour = 23; + time_struct.tm_min = 59; + time_struct.tm_sec = 59; - return timegm(time_struct); + return timegm(&time_struct); } static std::string _timeToString(const time_t unix_time) { - struct tm *time_struct = gmtime(&unix_time); + struct tm time_struct; + gmtime_r(&unix_time, &time_struct); char time_string[tmp_buf_size]; snprintf(time_string, sizeof(time_string), "%04d%02d%02d_%02d%02d%02d", - time_struct->tm_year + 1900, time_struct->tm_mon + 1, - time_struct->tm_mday, - time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec); + time_struct.tm_year + 1900, time_struct.tm_mon + 1, + time_struct.tm_mday, + time_struct.tm_hour, time_struct.tm_min, time_struct.tm_sec); return time_string; } diff --git a/src/libcode/vx_time_series/compute_swinging_door.h b/src/libcode/vx_time_series/compute_swinging_door.h index b41d226dcb..5665708bcc 100644 --- a/src/libcode/vx_time_series/compute_swinging_door.h +++ b/src/libcode/vx_time_series/compute_swinging_door.h @@ -113,18 +113,19 @@ class SDObservation return timegm(&time_struct); } - + static std::string _getTimeString(const time_t &unix_time) { - struct tm *time_struct = gmtime(&unix_time); + struct tm time_struct; + gmtime_r(&unix_time, &time_struct); char time_string[tmp_buf_size]; - snprintf(time_string, sizeof(time_string), + snprintf(time_string, sizeof(time_string), "%04d%02d%02d_%02d%02d%02d", - time_struct->tm_year + 1900, time_struct->tm_mon + 1, - time_struct->tm_mday, - time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec); + time_struct.tm_year + 1900, time_struct.tm_mon + 1, + time_struct.tm_mday, time_struct.tm_hour, + time_struct.tm_min, time_struct.tm_sec); return std::string(time_string); } diff --git a/src/tools/dev_utils/insitu_nc_to_ascii.cc b/src/tools/dev_utils/insitu_nc_to_ascii.cc index 3e291e4bb9..3e5f12dcd0 100644 --- a/src/tools/dev_utils/insitu_nc_to_ascii.cc +++ b/src/tools/dev_utils/insitu_nc_to_ascii.cc @@ -91,23 +91,16 @@ int met_main(int argc, char * argv []) { // Construct the time string - struct tm *time_struct = gmtime(&time_obs); - if (time_struct == 0) - { -// mlog << Error << "\n" << method_name << " -> " -// << "error converting time value to time structure" << endl; - cerr << "Error converting time value to time structure" << endl; - fclose(output_file); - exit(1); - } + struct tm time_struct; + gmtime_r(&time_obs, &time_struct); char time_obs_string[80]; - snprintf(time_obs_string, sizeof(time_obs_string), - "%04d%02d%02d_%02d%02d%02d", - time_struct->tm_year + 1900, time_struct->tm_mon + 1, - time_struct->tm_mday, - time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec); + snprintf(time_obs_string, sizeof(time_obs_string), + "%04d%02d%02d_%02d%02d%02d", + time_struct.tm_year + 1900, time_struct.tm_mon + 1, + time_struct.tm_mday, + time_struct.tm_hour, time_struct.tm_min, time_struct.tm_sec); // Write the observations. From aae59094efbf3601468b8f3812f48e6ba2aec544 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Tue, 25 Aug 2026 17:59:21 +0000 Subject: [PATCH 02/23] Per dtcenter/METplus-Internal#68, fix ConcatString, NumArray, and IntArray to be nothrow move constructible for SonarQube. --- src/basic/vx_log/concat_string.cc | 30 ++++++- src/basic/vx_log/concat_string.h | 2 + src/basic/vx_util/crc_array.h | 136 ++++++++++-------------------- src/basic/vx_util/num_array.cc | 30 +++++++ src/basic/vx_util/num_array.h | 2 + 5 files changed, 109 insertions(+), 91 deletions(-) diff --git a/src/basic/vx_log/concat_string.cc b/src/basic/vx_log/concat_string.cc index 04d3c2ce10..335fe87dbc 100644 --- a/src/basic/vx_log/concat_string.cc +++ b/src/basic/vx_log/concat_string.cc @@ -73,6 +73,18 @@ ConcatString::ConcatString(const ConcatString & c) //////////////////////////////////////////////////////////////////////// +ConcatString::ConcatString(ConcatString && c) noexcept + : Precision(c.Precision), + FloatFormat(move(c.FloatFormat)), + s(move(c.s)) +{ + init_from_scratch(); +} + + +//////////////////////////////////////////////////////////////////////// + + ConcatString::ConcatString(const std::string & Text) { init_from_scratch(); @@ -112,6 +124,22 @@ ConcatString & ConcatString::operator=(const ConcatString & c) //////////////////////////////////////////////////////////////////////// +ConcatString & ConcatString::operator=(ConcatString && c) noexcept +{ + if(this != &c) { + s = move(c.s); + FloatFormat = move(c.FloatFormat); + Precision = c.Precision; + c.Precision = concat_string_default_precision; + } + + return *this; +} + + +//////////////////////////////////////////////////////////////////////// + + ConcatString & ConcatString::operator=(const std::string & Text) { s.assign(Text); @@ -156,7 +184,7 @@ ConcatString & ConcatString::operator=(const char c) void ConcatString::init_from_scratch() { // MET #3253 Initialize to fix SonarQube reliability issue - Precision = 0; + Precision = concat_string_default_precision; set_precision(concat_string_default_precision); } diff --git a/src/basic/vx_log/concat_string.h b/src/basic/vx_log/concat_string.h index 1c44768f72..89fd349e5b 100644 --- a/src/basic/vx_log/concat_string.h +++ b/src/basic/vx_log/concat_string.h @@ -69,9 +69,11 @@ class ConcatString { ConcatString(); ~ConcatString(); ConcatString(const ConcatString &); + ConcatString(ConcatString &&) noexcept; ConcatString(const std::string &); ConcatString(const char *); ConcatString & operator=(const ConcatString &); + ConcatString & operator=(ConcatString &&) noexcept; ConcatString & operator=(const std::string &); ConcatString & operator=(const char *); ConcatString & operator=(const char); diff --git a/src/basic/vx_util/crc_array.h b/src/basic/vx_util/crc_array.h index de9afc93ab..1b997b8c71 100644 --- a/src/basic/vx_util/crc_array.h +++ b/src/basic/vx_util/crc_array.h @@ -31,12 +31,6 @@ //////////////////////////////////////////////////////////////////////// -static const int crc_array_alloc_inc = 25; - - -//////////////////////////////////////////////////////////////////////// - - template class CRC_Array { @@ -47,13 +41,8 @@ class CRC_Array { void assign(const CRC_Array &); - std::vector e; - int Nelements; - - int Nalloc; - public: CRC_Array() { init_from_scratch(); } @@ -62,6 +51,8 @@ class CRC_Array { CRC_Array(const CRC_Array & _a) { init_from_scratch(); assign(_a); } + CRC_Array(CRC_Array && _a) noexcept : e(move(_a.e)) {} + CRC_Array & operator=(const CRC_Array & _a) { if ( this == &_a ) return *this; @@ -72,13 +63,23 @@ class CRC_Array { } + CRC_Array & operator=(CRC_Array && _a) noexcept { + + if ( this == &_a ) return *this; + + e = move(_a.e); + + return *this; + + } + CRC_Array & operator=(const NumArray &); bool operator==(const CRC_Array &) const; void clear(); - void extend(int, bool exact = true); + void extend(int); void dump(std::ostream &, int depth = 0) const; @@ -93,8 +94,8 @@ class CRC_Array { // get stuff // - int n_elements() const { return Nelements; } - int n () const { return Nelements; } + int n_elements() const { return (int) e.size(); } + int n () const { return (int) e.size(); } T operator[] (int) const; @@ -159,9 +160,9 @@ bool CRC_Array::operator==(const CRC_Array & a) const { -if ( Nelements != a.Nelements ) return false; +if ( n() != a.n() ) return false; -for(int j=0; j::clear() e.clear(); -Nelements = Nalloc = 0; - return; } @@ -215,15 +214,12 @@ void CRC_Array::assign(const CRC_Array & a) clear(); -if ( a.Nelements == 0 ) return; +if ( a.n() == 0 ) return; -extend(a.Nelements); +extend(a.n()); e = a.e; -Nelements = a.Nelements; - - return; } @@ -234,28 +230,11 @@ return; template -void CRC_Array::extend(int len, bool exact) +void CRC_Array::extend(int len) { -if ( Nalloc >= len ) return; - -if ( ! exact ) { - - int k; - - k = len/crc_array_alloc_inc; - - if ( len%crc_array_alloc_inc ) ++k; - - len = k*crc_array_alloc_inc; - -} - -e.reserve( len ); - -Nalloc = len; - +e.reserve(len); return; @@ -273,13 +252,9 @@ void CRC_Array::dump(std::ostream & out, int depth) const Indent prefix(depth); +out << prefix << "Nelements = " << n() << "\n"; -out << prefix << "Nelements = " << Nelements << "\n"; -out << prefix << "Nalloc = " << Nalloc << "\n"; - -int j; - -for (j=0; j::dump_one_line(std::ostream & out, int depth) const int j; Indent prefix(depth); -out << prefix << '(' << Nelements << ") "; +out << prefix << '(' << n() << ") "; -for (j=0; j 0 ) out << ' '; @@ -355,12 +330,11 @@ template void CRC_Array::set(int ix, const T & elem) { - if ( (ix < 0) || (ix >= Nelements) ) { + if ( (ix < 0) || (ix >= n()) ) { mlog << Error << "\nCRC_Array::set(int, T) const -> " << "range check error ... index = " << ix - << ", Nelements = " << Nelements - << ", Nalloc = " << Nalloc + << ", Nelements = " << n() << "\n\n"; exit ( 1 ); @@ -379,12 +353,11 @@ T CRC_Array::operator[](int i) const { -if ( (i < 0) || (i >= Nelements) ) { +if ( (i < 0) || (i >= n()) ) { mlog << Error << "\nCRC_Array::operator[](int) const -> " << "range check error ... index = " << i - << ", Nelements = " << Nelements - << ", Nalloc = " << Nalloc + << ", Nelements = " << n() << "\n\n"; exit ( 1 ); @@ -405,10 +378,9 @@ bool CRC_Array::has(const T & k, bool forward) const { -int j; bool found = false; if (forward) { - for (j=0; j=0; --j) { + for (int j=n()-1; j>=0; --j) { if ( e[j] == k ) { found = true; break; @@ -438,18 +410,17 @@ bool CRC_Array::has(const T & k, int & index, bool forward) const { -int j; bool found = false; index = -1; if (forward) { - for (j=0; j=0; --j) { + for (int j=n()-1; j>=0; --j) { if ( e[j] == k ) { index = j; found = true; break; } } } @@ -468,12 +439,8 @@ void CRC_Array::add(const T & k) { -extend(Nelements + 1, false); - e.emplace_back(k); -Nelements++; - return; } @@ -488,16 +455,12 @@ void CRC_Array::add(const CRC_Array & a) { -extend(Nelements + a.Nelements); - -int j; +extend(n() + a.n()); -for (j=0; j<(a.Nelements); ++j) { +for (int j=0; j<(a.n()); ++j) { e.emplace_back(a.e[j]); - Nelements++; - } @@ -535,11 +498,11 @@ StringArray sa; sa.parse_css(text); -extend(Nelements + sa.n_elements()); +extend(n() + sa.n()); int j; -for (j=0; j<(sa.n_elements()); j++) { +for (j=0; j<(sa.n()); j++) { add(timestring_to_sec(sa[j].c_str())); @@ -559,7 +522,7 @@ void CRC_Array::sort_increasing() { -if ( Nelements <= 1 ) return; +if ( n() <= 1 ) return; std::sort(e.begin(), e.end()); @@ -577,12 +540,11 @@ T CRC_Array::sum() const { -int j, count; -T s; +T s = 0; -s = 0; +int count; -for(j=0, count=0; j::min() const { -if ( Nelements == 0 ) return bad_data_int; - -int j; +if ( n() == 0 ) return bad_data_int; T min_v = e[0]; -for(j=0; j::max() const { -if(Nelements == 0) return bad_data_int; - -int j; +if(n() == 0) return bad_data_int; T max_v = e[0]; -for(j=0; j::increment(const T & k) { -int j; - -for (j=0; j &a) : e(a) {}; NumArray & operator=(const NumArray &); + NumArray & operator=(NumArray &&) noexcept; bool operator==(const NumArray &) const; void clear(); From 797af5432de226a991a9c35da326dfc455658eb0 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Tue, 25 Aug 2026 18:34:20 +0000 Subject: [PATCH 03/23] Per dtcenter/METplus-Internal#68, reimplement TimeArray using vectors. --- src/basic/vx_cal/time_array.cc | 223 ++++++++++----------------------- src/basic/vx_cal/time_array.h | 21 ++-- 2 files changed, 75 insertions(+), 169 deletions(-) diff --git a/src/basic/vx_cal/time_array.cc b/src/basic/vx_cal/time_array.cc index 2dcd83e9db..970eafc45a 100644 --- a/src/basic/vx_cal/time_array.cc +++ b/src/basic/vx_cal/time_array.cc @@ -24,12 +24,6 @@ using namespace std; -//////////////////////////////////////////////////////////////////////// - - -static int compare_unixtime(const void *, const void *); - - //////////////////////////////////////////////////////////////////////// @@ -41,11 +35,11 @@ static int compare_unixtime(const void *, const void *); //////////////////////////////////////////////////////////////////////// -TimeArray::TimeArray() +TimeArray::~TimeArray() { -init_from_scratch(); +clear(); } @@ -53,11 +47,11 @@ init_from_scratch(); //////////////////////////////////////////////////////////////////////// -TimeArray::~TimeArray() +TimeArray::TimeArray(const TimeArray & a) { -clear(); +assign(a); } @@ -65,17 +59,14 @@ clear(); //////////////////////////////////////////////////////////////////////// -TimeArray::TimeArray(const TimeArray & a) - +TimeArray::TimeArray(TimeArray && a) noexcept + : e(move(a.e)), Sorted(a.Sorted) { -init_from_scratch(); - -assign(a); +a.Sorted = false; } - //////////////////////////////////////////////////////////////////////// @@ -95,23 +86,17 @@ return *this; //////////////////////////////////////////////////////////////////////// -bool TimeArray::operator==(const TimeArray & a) const +TimeArray & TimeArray::operator=(TimeArray && a) noexcept { -if ( Nelements != a.Nelements ) return false; - -bool status = true; - -for (int j=0; j= n ) return; - -if ( ! exact ) { - - int k; - - k = n/time_array_alloc_inc; - - if ( n%time_array_alloc_inc ) ++k; - - n = k*time_array_alloc_inc; - -} - -unixtime * u = (unixtime *) nullptr; - -u = new unixtime [n]; - -if ( !u ) { - - mlog << Error << "\nvoid TimeArray::extend(int) -> " - << "memory allocation error\n\n"; - - exit ( 1 ); - -} - -memset(u, 0, n*sizeof(unixtime)); - -if ( e ) { - - for (int j=0; j= Nelements) ) { +if ( (n < 0) || (n >= n_elements()) ) { mlog << Error << "\nTimeArray::operator[](int) const -> " << "range check error\n\n"; @@ -328,7 +263,7 @@ int TimeArray::index(unixtime u) const int match = -1; -for (int j=0; j= Nelements) ) { +if ( (n < 0) || (n >= n_elements()) ) { mlog << Error << "\nTimeArray::set(int, unixtime) -> " << "range check error\n\n"; @@ -461,12 +392,10 @@ unixtime TimeArray::min() const { -unixtime u; - -if(Nelements == 0) return bad_data_ll; +if(e.empty()) return bad_data_ll; -u = e[0]; -for(int j=0; j u) u = e[j]; } @@ -503,14 +430,16 @@ ConcatString TimeArray::serialize() const { - ConcatString s; +ConcatString s; - if(n_elements() == 0) return s; +if(e.empty()) return s; - s << e[0]; - for(int j=1; j= Nelements || - end < 0 || end >= Nelements || +if ( beg < 0 || beg >= n() || + end < 0 || end >= n() || end < beg ) { mlog << Error << "\nTimeArray::subset(int, int) -> " << "range check error\n\n"; @@ -605,26 +534,6 @@ return subset_ta; } -//////////////////////////////////////////////////////////////////////// - - -int compare_unixtime(const void *p1, const void *p2) - -{ - -const unixtime *a = (const unixtime *) p1; -const unixtime *b = (const unixtime *) p2; - - -if ( (*a) < (*b) ) return -1; - -if ( (*a) > (*b) ) return 1; - - -return 0; - -} - //////////////////////////////////////////////////////////////////////// diff --git a/src/basic/vx_cal/time_array.h b/src/basic/vx_cal/time_array.h index 4d9bf35d39..1447c48f8e 100644 --- a/src/basic/vx_cal/time_array.h +++ b/src/basic/vx_cal/time_array.h @@ -36,31 +36,28 @@ class TimeArray { private: - void init_from_scratch(); - void assign(const TimeArray &); - unixtime * e; - - int Nelements; + std::vector e; - int Nalloc; - - bool Sorted; + bool Sorted = false; public: - TimeArray(); + TimeArray() = default; ~TimeArray(); TimeArray(const TimeArray &); + TimeArray(TimeArray &&) noexcept; + TimeArray & operator=(const TimeArray &); + TimeArray & operator=(TimeArray &&) noexcept; bool operator==(const TimeArray &) const; void clear(); void erase(); - void extend(int, bool exact = true); + void extend(int); void dump(std::ostream &, int depth = 0) const; @@ -95,8 +92,8 @@ class TimeArray { //////////////////////////////////////////////////////////////////////// -inline int TimeArray::n_elements() const { return Nelements; } -inline int TimeArray::n() const { return Nelements; } +inline int TimeArray::n_elements() const { return (int) e.size(); } +inline int TimeArray::n() const { return (int) e.size(); } //////////////////////////////////////////////////////////////////////// From 5cd0ef0d6edb2359e34ebad410d538d2d520e1bf Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Tue, 25 Aug 2026 18:41:41 +0000 Subject: [PATCH 04/23] Per dtcenter/METplus-Internal#68, StringArray to be nothrow move constructible for SonarQube. --- src/basic/vx_log/string_array.cc | 34 ++++++++++++++++++++++++++++++++ src/basic/vx_log/string_array.h | 3 +++ 2 files changed, 37 insertions(+) diff --git a/src/basic/vx_log/string_array.cc b/src/basic/vx_log/string_array.cc index 21634be877..148aa033e2 100644 --- a/src/basic/vx_log/string_array.cc +++ b/src/basic/vx_log/string_array.cc @@ -87,6 +87,21 @@ assign(a); //////////////////////////////////////////////////////////////////////// +StringArray::StringArray(StringArray && a) noexcept + : s(move(a.s)), MaxLength(a.MaxLength), IgnoreCase(a.IgnoreCase), Sorted(a.Sorted) + +{ + +a.MaxLength = 0; +a.IgnoreCase = false; +a.Sorted = false; + +} + + +//////////////////////////////////////////////////////////////////////// + + StringArray::StringArray(const vector & a) : s(a) { @@ -116,6 +131,25 @@ return *this; //////////////////////////////////////////////////////////////////////// +StringArray & StringArray::operator=(StringArray && a) noexcept + +{ + +if ( this == &a ) return *this; + +s = move(a.s); +MaxLength = a.MaxLength; +IgnoreCase = a.IgnoreCase; +Sorted = a.Sorted; + +return *this; + +} + + +//////////////////////////////////////////////////////////////////////// + + bool StringArray::operator==(const StringArray & a) const { diff --git a/src/basic/vx_log/string_array.h b/src/basic/vx_log/string_array.h index 3b6cbd42a2..231731b15a 100644 --- a/src/basic/vx_log/string_array.h +++ b/src/basic/vx_log/string_array.h @@ -48,8 +48,11 @@ class StringArray { StringArray(); ~StringArray(); StringArray(const StringArray &); + StringArray(StringArray &&) noexcept; explicit StringArray(const std::vector &); + StringArray & operator=(const StringArray &); + StringArray & operator=(StringArray &&) noexcept; bool operator==(const StringArray &) const; void clear(); From 947777646c9018d109d969a96881592dd675a028 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Tue, 25 Aug 2026 20:47:56 +0000 Subject: [PATCH 05/23] Drive down SonarQube issues in num_array.cc --- src/basic/vx_util/num_array.cc | 114 +++++++++++++++------------------ 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/src/basic/vx_util/num_array.cc b/src/basic/vx_util/num_array.cc index 16c51dacd8..7e9c2720da 100644 --- a/src/basic/vx_util/num_array.cc +++ b/src/basic/vx_util/num_array.cc @@ -208,9 +208,7 @@ void NumArray::dump(ostream & out, int depth) const out << prefix << "Length = " << n_elements() << "\n"; out << prefix << "Sorted = " << (Sorted ? "true" : "false") << "\n"; - int j; - - for (j=0; j=0; --j) { + for (int j=n_elements()-1; j>=0; --j) { if ( is_eq(e[j], d) ) { found = 1; break; @@ -769,20 +766,18 @@ void NumArray::compute_mean_variance(double &mn, double &var) const { - int j, count; - double s, s_sq; - - if(n_elements() == 0) { + if(e.empty()) { mn = var = bad_data_double; return; } - s = s_sq = 0.0; - count = 0; + double s = 0.0; + double s_sq = 0.0; + int count = 0; - for(j=0; j max_n) || (is_eq(uniq_n[j], max_n) && max_j >= 0 && uniq_v[j] < uniq_v[max_j])) { @@ -890,6 +885,7 @@ double NumArray::mode() const } } + double v; if(max_j >= 0) v = uniq_v[max_j]; else v = bad_data_double; @@ -905,13 +901,11 @@ double NumArray::min() const { - if(n_elements() == 0) return bad_data_double; - - int j; + if(e.empty()) return bad_data_double; double min_v = e[0]; - for(j=0; j max_v) max_v = e[j]; } @@ -951,11 +943,10 @@ double NumArray::range() const { - double v, v1, v2; - - v1 = max(); - v2 = min(); - v = (is_bad_data(v1) || is_bad_data(v2) ? bad_data_double : v1 - v2); + double v1 = max(); + double v2 = min(); + double v = (is_bad_data(v1) || is_bad_data(v2) ? + bad_data_double : v1 - v2); return v; @@ -969,9 +960,8 @@ int NumArray::n_valid() const { - int j, n_vld; - - for(j=0, n_vld=0; j 1) { var = (s_sq - s*s/(double) count)/((double) (count - 1)); if(is_eq(var, 0.0)) var = 0.0; @@ -1290,13 +1278,12 @@ double NumArray::mean_abs_diff() const { - int i, j, count; - double sum, mad; - int n = n_elements(); - for(i=0, count=0, sum=0.0; i Date: Tue, 25 Aug 2026 20:49:20 +0000 Subject: [PATCH 06/23] Reimplement ThreshArray as a std::vector. --- src/basic/vx_util/thresh_array.cc | 156 ++++++++++-------------------- src/basic/vx_util/thresh_array.h | 19 ++-- 2 files changed, 60 insertions(+), 115 deletions(-) diff --git a/src/basic/vx_util/thresh_array.cc b/src/basic/vx_util/thresh_array.cc index 720dd9f0a7..304aa4b538 100644 --- a/src/basic/vx_util/thresh_array.cc +++ b/src/basic/vx_util/thresh_array.cc @@ -19,10 +19,6 @@ using namespace std; -//////////////////////////////////////////////////////////////////////// - -static const int thresharray_alloc_inc = 10; - //////////////////////////////////////////////////////////////////////// // // Code for class ThreshArray @@ -30,24 +26,24 @@ static const int thresharray_alloc_inc = 10; //////////////////////////////////////////////////////////////////////// ThreshArray::ThreshArray() { - - init_from_scratch(); } //////////////////////////////////////////////////////////////////////// ThreshArray::~ThreshArray() { - clear(); } //////////////////////////////////////////////////////////////////////// ThreshArray::ThreshArray(const ThreshArray & a) { + assign(a); +} - init_from_scratch(); +//////////////////////////////////////////////////////////////////////// - assign(a); +ThreshArray::ThreshArray(ThreshArray && a) noexcept + : t(move(a.t)) { } //////////////////////////////////////////////////////////////////////// @@ -63,22 +59,20 @@ ThreshArray & ThreshArray::operator=(const ThreshArray & a) { //////////////////////////////////////////////////////////////////////// -void ThreshArray::init_from_scratch() { +ThreshArray & ThreshArray::operator=(ThreshArray && a) noexcept { - t = (SingleThresh *) nullptr; + if(this == &a) return *this; - clear(); + t = move(a.t); - return; + return *this; } //////////////////////////////////////////////////////////////////////// void ThreshArray::clear() { - if(t) { delete [] t; t = (SingleThresh *) nullptr; } - - Nelements = Nalloc = 0; + t.clear(); return; } @@ -86,31 +80,22 @@ void ThreshArray::clear() { //////////////////////////////////////////////////////////////////////// void ThreshArray::assign(const ThreshArray & a) { - int j; clear(); - extend(a.Nelements); - - for(j=0; j<(a.Nelements); j++) add(a.t[j]); - - Nelements = a.Nelements; - - return; + t = a.t; } //////////////////////////////////////////////////////////////////////// void ThreshArray::dump(ostream & out, int depth) const { - int j; Indent prefix(depth); Indent prefix2(depth + 1); - out << prefix << "Nelements = " << Nelements << "\n"; - out << prefix << "Nalloc = " << Nalloc << "\n"; + out << prefix << "Nelements = " << n() << "\n"; - for(j=0; j= Nelements)) { + if((n < 0) || (n >= n_elements())) { mlog << Error << "\nThreshArray::operator[](int) const -> " << "range check error!\n\n"; exit(1); @@ -184,11 +146,7 @@ SingleThresh ThreshArray::operator[](int n) const { void ThreshArray::add(const SingleThresh &st) { - extend(Nelements + 1, false); - - t[Nelements] = st; - - Nelements++; + t.emplace_back(st); return; } @@ -200,11 +158,7 @@ void ThreshArray::add(const double val, const ThreshType type) { st.set(val, type); - extend(Nelements + 1, false); - - t[Nelements] = st; - - Nelements++; + add(st); return; } @@ -216,11 +170,7 @@ void ThreshArray::add(const char *thresh_str) { st.set(thresh_str); - extend(Nelements + 1, false); - - t[Nelements] = st; - - Nelements++; + add(st); return; } @@ -228,13 +178,12 @@ void ThreshArray::add(const char *thresh_str) { //////////////////////////////////////////////////////////////////////// void ThreshArray::add(const ThreshArray & a) { - int j; - if(a.n() == 0) return; + if(a.t.empty()) return; - extend(Nelements + a.n()); + extend(n() + a.n()); - for(j=0; j<(a.n()); j++) add(a[j]); + for(int j=0; j<(a.n()); j++) add(a[j]); return; } @@ -242,14 +191,13 @@ void ThreshArray::add(const ThreshArray & a) { //////////////////////////////////////////////////////////////////////// void ThreshArray::add_css(const char *text) { - int j; StringArray sa; sa.parse_css(text); - extend(Nelements + sa.n()); + extend(n() + sa.n()); - for(j=0; j t[i+1].get_value() || t[i].get_type() != t[i+1].get_type() || @@ -383,27 +331,27 @@ void ThreshArray::check_bin_thresh() const { //////////////////////////////////////////////////////////////////////// int ThreshArray::check_bins(double v, const ClimoPntInfo *cpi) const { - int i, bin; + int bin; // Check for bad data or no thresholds - if(is_bad_data(v) || Nelements == 0) return bad_data_int; + if(is_bad_data(v) || t.empty()) return bad_data_int; // For < and <=, check thresholds left to right if(t[0].get_type() == thresh_lt || t[0].get_type() == thresh_le) { - for(i=0, bin=-1; i and >=, check thresholds right to left else { - for(i=Nelements-1, bin=-1; i>=0; i--) { + for(int i=n()-1, bin=-1; i>=0; i--) { if(t[i].check(v, cpi)) { bin = i+1; break; @@ -424,7 +372,7 @@ bool ThreshArray::check_dbl(double v, const ClimoPntInfo *cpi) const { // // Check if the value satisifes all the thresholds in the array // - for(int i=0; i " @@ -473,14 +421,14 @@ void ThreshArray::set_perc(const NumArray *fptr, const NumArray *optr, exit(1); } - if(farr->n() != Nelements || - oarr->n() != Nelements) { + if(farr->n() != n() || + oarr->n() != n()) { mlog << Error << "\nThreshArray::set_perc() -> " << "not enough thresholds provided!\n\n"; exit(1); } - for(int i=0; ithresh()[i]), &(oarr->thresh()[i])); @@ -493,9 +441,9 @@ void ThreshArray::set_perc(const NumArray *fptr, const NumArray *optr, void ThreshArray::multiply_by(const double x) { - if(Nelements == 0) return; + if(t.empty()) return; - for(int i=0; i &v) { - if(Nelements == 0) return; + if(t.empty()) return; - for(int i=0; i &v) { bool ThreshArray::equal_bin_width(double &width) const { // Check number of elements - if(Nelements < 2) { + if(n() < 2) { width = bad_data_double; return false; } @@ -528,7 +476,7 @@ bool ThreshArray::equal_bin_width(double &width) const { bool is_equal = true; // Check for consistent widths, ignoring the last bin - for(int i=0; i<(Nelements-2); i++) { + for(int i=0; i<(n()-2); i++) { double cur_width = t[i+1].get_value() - t[i].get_value(); if(!is_eq(width, cur_width, loose_tol)) { width = bad_data_double; diff --git a/src/basic/vx_util/thresh_array.h b/src/basic/vx_util/thresh_array.h index f6468a3bd6..2f73a01719 100644 --- a/src/basic/vx_util/thresh_array.h +++ b/src/basic/vx_util/thresh_array.h @@ -24,24 +24,23 @@ class ThreshArray { public: - void init_from_scratch(); void assign(const ThreshArray &); - SingleThresh * t; - - int Nelements; - int Nalloc; + std::vector t; public: ThreshArray(); ~ThreshArray(); ThreshArray(const ThreshArray &); + ThreshArray(ThreshArray &&) noexcept; + ThreshArray & operator=(const ThreshArray &); + ThreshArray & operator=(ThreshArray &&) noexcept; void clear(); - void extend(int, bool exact = true); + void extend(int); void dump(std::ostream &, int depth = 0) const; @@ -49,7 +48,6 @@ class ThreshArray { SingleThresh operator[](int) const; const SingleThresh * thresh() const; - SingleThresh * buf() const; void add(const SingleThresh &); void add(const double, const ThreshType); @@ -93,10 +91,9 @@ class ThreshArray { //////////////////////////////////////////////////////////////////////// -inline int ThreshArray::n_elements() const { return Nelements; } -inline int ThreshArray::n() const { return Nelements; } -inline const SingleThresh * ThreshArray::thresh() const { return t; } -inline SingleThresh * ThreshArray::buf() const { return t; } +inline int ThreshArray::n_elements() const { return (int) t.size(); } +inline int ThreshArray::n() const { return (int) t.size(); } +inline const SingleThresh * ThreshArray::thresh() const { return t.data(); } //////////////////////////////////////////////////////////////////////// From 93662feb3bcbeea97030bf9b3463d36cf4d6a78c Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Tue, 25 Aug 2026 21:51:12 +0000 Subject: [PATCH 07/23] SonarQube updates. --- src/basic/vx_config/config_constants.h | 18 +++-- src/basic/vx_config/config_util.cc | 82 +++++++++++++++++++- src/libcode/vx_grid/grid_base.cc | 33 ++++++++ src/libcode/vx_grid/grid_base.h | 2 + src/libcode/vx_statistics/pair_data_point.cc | 16 ++++ src/libcode/vx_statistics/pair_data_point.h | 2 + src/tools/other/gen_ens_prod/gen_ens_prod.cc | 2 +- 7 files changed, 145 insertions(+), 10 deletions(-) diff --git a/src/basic/vx_config/config_constants.h b/src/basic/vx_config/config_constants.h index 4e3ee5b740..62ee76766c 100644 --- a/src/basic/vx_config/config_constants.h +++ b/src/basic/vx_config/config_constants.h @@ -270,8 +270,10 @@ struct BootInfo { BootInfo() { clear(); } ~BootInfo() { clear(); } - BootInfo(BootInfo const &i) { *this = i; } - BootInfo &operator=(const BootInfo &a) noexcept; // SonarQube findings + BootInfo(const BootInfo &); + BootInfo(BootInfo &&) noexcept; + BootInfo &operator=(const BootInfo &); + BootInfo &operator=(BootInfo &&) noexcept; void clear(); }; @@ -356,8 +358,10 @@ struct ClimoCDFInfo { ClimoCDFInfo() { clear(); } ~ClimoCDFInfo() { clear(); } - ClimoCDFInfo(ClimoCDFInfo const &i) { *this = i; } - ClimoCDFInfo &operator=(const ClimoCDFInfo &a) noexcept; // SonarQube findings + ClimoCDFInfo(const ClimoCDFInfo &); + ClimoCDFInfo(ClimoCDFInfo &&) noexcept; + ClimoCDFInfo &operator=(const ClimoCDFInfo &); + ClimoCDFInfo &operator=(ClimoCDFInfo &&) noexcept; void clear(); void set_cdf_ta(int, bool &); // Construct equally-likely thresholds }; @@ -456,8 +460,10 @@ struct MaskLatLon { MaskLatLon() { clear(); } ~MaskLatLon() { clear(); } - MaskLatLon(MaskLatLon const &i) { *this = i; } - MaskLatLon &operator=(const MaskLatLon &a) noexcept; + MaskLatLon(const MaskLatLon &); + MaskLatLon(MaskLatLon &&) noexcept; + MaskLatLon &operator=(const MaskLatLon &); + MaskLatLon &operator=(MaskLatLon &&) noexcept; void clear(); friend bool operator==(const MaskLatLon &lhs, const MaskLatLon &rhs) { diff --git a/src/basic/vx_config/config_util.cc b/src/basic/vx_config/config_util.cc index f4f17915a4..89572edb88 100644 --- a/src/basic/vx_config/config_util.cc +++ b/src/basic/vx_config/config_util.cc @@ -735,6 +735,19 @@ StringArray parse_sid_mask_as_list(const ConcatString &mask_sid_str) { // /////////////////////////////////////////////////////////////////////////////// +MaskLatLon::MaskLatLon(const MaskLatLon &a) { + name = a.name; + lat_thresh = a.lat_thresh; + lon_thresh = a.lon_thresh; +} + +/////////////////////////////////////////////////////////////////////////////// + +MaskLatLon::MaskLatLon(MaskLatLon &&a) noexcept + : name(a.name), lat_thresh(a.lat_thresh), lon_thresh(a.lon_thresh) { } + +/////////////////////////////////////////////////////////////////////////////// + void MaskLatLon::clear() { name.clear(); lat_thresh.clear(); @@ -743,7 +756,7 @@ void MaskLatLon::clear() { /////////////////////////////////////////////////////////////////////////////// -MaskLatLon &MaskLatLon::operator=(const MaskLatLon &a) noexcept { +MaskLatLon & MaskLatLon::operator=(const MaskLatLon &a) { if(this != &a) { name = a.name; lat_thresh = a.lat_thresh; @@ -754,6 +767,20 @@ MaskLatLon &MaskLatLon::operator=(const MaskLatLon &a) noexcept { /////////////////////////////////////////////////////////////////////////////// +MaskLatLon & MaskLatLon::operator=(MaskLatLon &&a) noexcept { + if(this != &a) { + name = a.name; + lat_thresh = a.lat_thresh; + lon_thresh = a.lon_thresh; + a.name.clear(); + a.lat_thresh.clear(); + a.lon_thresh.clear(); + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// + vector parse_conf_llpnt_mask(Dictionary *dict) { const DictionaryEntry *entry; Dictionary *llpnt_dict; @@ -1419,7 +1446,20 @@ void TimeSummaryInfo::clear() { /////////////////////////////////////////////////////////////////////////////// -BootInfo & BootInfo::operator=(const BootInfo &a) noexcept { +BootInfo & BootInfo::operator=(const BootInfo &a) { + if(this != &a) { + interval = a.interval; + rep_prop = a.rep_prop; + n_rep = a.n_rep; + rng = a.rng; + seed = a.seed; + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// + +BootInfo & BootInfo::operator=(BootInfo &&a) noexcept { if(this != &a) { interval = a.interval; rep_prop = a.rep_prop; @@ -1932,6 +1972,26 @@ InterpInfo parse_conf_interp(Dictionary *dict, const char *conf_key) { return info; } +/////////////////////////////////////////////////////////////////////////////// +// +// Code for class ClimoCDFInfo +// +/////////////////////////////////////////////////////////////////////////////// + +ClimoCDFInfo::ClimoCDFInfo(const ClimoCDFInfo &a) { + flag = a.flag; + n_bin = a.n_bin; + cdf_ta = a.cdf_ta; + write_bins = a.write_bins; + direct_prob = a.direct_prob; +} + +/////////////////////////////////////////////////////////////////////////////// + +ClimoCDFInfo::ClimoCDFInfo(ClimoCDFInfo &&a) noexcept + : flag(a.flag), n_bin(a.n_bin), cdf_ta(a.cdf_ta), + write_bins(a.write_bins), direct_prob(a.direct_prob) { } + /////////////////////////////////////////////////////////////////////////////// void ClimoCDFInfo::clear() { @@ -2002,7 +2062,7 @@ void ClimoCDFInfo::set_cdf_ta(int n_bin, bool ¢er) { /////////////////////////////////////////////////////////////////////////////// -ClimoCDFInfo &ClimoCDFInfo::operator=(const ClimoCDFInfo &a) noexcept { +ClimoCDFInfo &ClimoCDFInfo::operator=(const ClimoCDFInfo &a) { if(this != &a) { flag = a.flag; n_bin = a.n_bin; @@ -2013,6 +2073,22 @@ ClimoCDFInfo &ClimoCDFInfo::operator=(const ClimoCDFInfo &a) noexcept { return *this; } +/////////////////////////////////////////////////////////////////////////////// + +ClimoCDFInfo &ClimoCDFInfo::operator=(ClimoCDFInfo &&a) noexcept { + if(this != &a) { + flag = a.flag; + a.flag = false; + n_bin = a.n_bin; + a.n_bin = 0; + cdf_ta = move(a.cdf_ta); + write_bins = a.write_bins; + a.write_bins = false; + direct_prob = a.direct_prob; + a.direct_prob = false; + } + return *this; +} /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_grid/grid_base.cc b/src/libcode/vx_grid/grid_base.cc index 722b7d78bf..36b54f7f4d 100644 --- a/src/libcode/vx_grid/grid_base.cc +++ b/src/libcode/vx_grid/grid_base.cc @@ -985,6 +985,19 @@ assign(g); //////////////////////////////////////////////////////////////////////// +Grid::Grid(Grid && g) noexcept + : rep(move(g.rep)), swap_to_north(g.swap_to_north) + +{ + +g.swap_to_north = false; + +} + + +//////////////////////////////////////////////////////////////////////// + + Grid & Grid::operator=(const Grid & g) { @@ -999,6 +1012,26 @@ return *this; //////////////////////////////////////////////////////////////////////// +Grid & Grid::operator=(Grid && g) noexcept + +{ + +if ( this != &g ) { + + rep = move(g.rep); + swap_to_north = g.swap_to_north; + g.swap_to_north = false; + +} + +return *this; + +} + + +//////////////////////////////////////////////////////////////////////// + + Grid::Grid(const char * _name) { diff --git a/src/libcode/vx_grid/grid_base.h b/src/libcode/vx_grid/grid_base.h index 27efc82092..a50db26a04 100644 --- a/src/libcode/vx_grid/grid_base.h +++ b/src/libcode/vx_grid/grid_base.h @@ -223,7 +223,9 @@ class Grid : public GridInterface { #endif virtual ~Grid(); Grid(const Grid &); + Grid(Grid &&) noexcept; Grid & operator=(const Grid &); + Grid & operator=(Grid &&) noexcept; void clear(); diff --git a/src/libcode/vx_statistics/pair_data_point.cc b/src/libcode/vx_statistics/pair_data_point.cc index 76a25887aa..70dea9ecdd 100644 --- a/src/libcode/vx_statistics/pair_data_point.cc +++ b/src/libcode/vx_statistics/pair_data_point.cc @@ -449,6 +449,11 @@ VxPairDataPoint::VxPairDataPoint(const VxPairDataPoint &vx_pd) { //////////////////////////////////////////////////////////////////////// +VxPairDataPoint::VxPairDataPoint(VxPairDataPoint &&vx_pd) noexcept + : pd(move(vx_pd.pd)) { } + +//////////////////////////////////////////////////////////////////////// + VxPairDataPoint & VxPairDataPoint::operator=(const VxPairDataPoint &vx_pd) { if(this == &vx_pd) return *this; @@ -460,6 +465,17 @@ VxPairDataPoint & VxPairDataPoint::operator=(const VxPairDataPoint &vx_pd) { //////////////////////////////////////////////////////////////////////// +VxPairDataPoint & VxPairDataPoint::operator=(VxPairDataPoint &&vx_pd) noexcept { + + if(this == &vx_pd) return *this; + + pd = move(vx_pd.pd); + + return *this; +} + +//////////////////////////////////////////////////////////////////////// + void VxPairDataPoint::init_from_scratch() { fcst_info = (VarInfo *) nullptr; diff --git a/src/libcode/vx_statistics/pair_data_point.h b/src/libcode/vx_statistics/pair_data_point.h index 5d4dd74988..5566c5f34e 100644 --- a/src/libcode/vx_statistics/pair_data_point.h +++ b/src/libcode/vx_statistics/pair_data_point.h @@ -105,7 +105,9 @@ class VxPairDataPoint : public VxPairBase { VxPairDataPoint(); ~VxPairDataPoint(); VxPairDataPoint(const VxPairDataPoint &); + VxPairDataPoint(VxPairDataPoint &&) noexcept; VxPairDataPoint & operator=(const VxPairDataPoint &); + VxPairDataPoint & operator=(VxPairDataPoint &&) noexcept; ////////////////////////////////////////////////////////////////// // diff --git a/src/tools/other/gen_ens_prod/gen_ens_prod.cc b/src/tools/other/gen_ens_prod/gen_ens_prod.cc index ed0825988e..00c49adada 100644 --- a/src/tools/other/gen_ens_prod/gen_ens_prod.cc +++ b/src/tools/other/gen_ens_prod/gen_ens_prod.cc @@ -743,7 +743,7 @@ static void track_counts(const GenEnsProdVarInfo *ens_info, // Ensemble thresholds const int n_thr = ens_info->cat_ta.n(); - const SingleThresh *thr_buf = ens_info->cat_ta.buf(); + const SingleThresh *thr_buf = ens_info->cat_ta.thresh(); // Increment counts for each grid point for(int i=0; i Date: Tue, 25 Aug 2026 22:05:00 +0000 Subject: [PATCH 08/23] Add missing BootInfo definition. --- src/basic/vx_config/config_util.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/basic/vx_config/config_util.cc b/src/basic/vx_config/config_util.cc index 89572edb88..85d2a3d624 100644 --- a/src/basic/vx_config/config_util.cc +++ b/src/basic/vx_config/config_util.cc @@ -1444,6 +1444,16 @@ void TimeSummaryInfo::clear() { vld_freq = bad_data_int; } +/////////////////////////////////////////////////////////////////////////////// +// +// Code for class BootInfo +// +/////////////////////////////////////////////////////////////////////////////// + +BootInfo::BootInfo(BootInfo &&a) noexcept + : interval(a.interval), rep_prop(a.rep_prop), n_rep(a.n_rep), + rng(a.rng), seed(a.seed) { } + /////////////////////////////////////////////////////////////////////////////// BootInfo & BootInfo::operator=(const BootInfo &a) { From 41052cef5d9e4f4ff472c8fbfb12e03038a65f73 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Tue, 25 Aug 2026 22:20:13 +0000 Subject: [PATCH 09/23] Fix up Grid --- src/libcode/vx_grid/grid_base.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libcode/vx_grid/grid_base.cc b/src/libcode/vx_grid/grid_base.cc index 36b54f7f4d..4f50db89fe 100644 --- a/src/libcode/vx_grid/grid_base.cc +++ b/src/libcode/vx_grid/grid_base.cc @@ -986,11 +986,11 @@ assign(g); Grid::Grid(Grid && g) noexcept - : rep(move(g.rep)), swap_to_north(g.swap_to_north) + : rep(g.rep), swap_to_north(g.swap_to_north) { -g.swap_to_north = false; +g.rep = nullptr; } @@ -1018,9 +1018,10 @@ Grid & Grid::operator=(Grid && g) noexcept if ( this != &g ) { - rep = move(g.rep); + if(rep) delete rep; + rep = g.rep; swap_to_north = g.swap_to_north; - g.swap_to_north = false; + g.rep = nullptr; } From b7b3692f5466abf9e28dd378dfd6f3cd7dc377b3 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Tue, 25 Aug 2026 22:32:55 +0000 Subject: [PATCH 10/23] Correct VxPairDataPoint noexcept move/assignment functions. --- src/libcode/vx_statistics/pair_data_point.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcode/vx_statistics/pair_data_point.cc b/src/libcode/vx_statistics/pair_data_point.cc index 70dea9ecdd..307aa9c9b6 100644 --- a/src/libcode/vx_statistics/pair_data_point.cc +++ b/src/libcode/vx_statistics/pair_data_point.cc @@ -450,7 +450,7 @@ VxPairDataPoint::VxPairDataPoint(const VxPairDataPoint &vx_pd) { //////////////////////////////////////////////////////////////////////// VxPairDataPoint::VxPairDataPoint(VxPairDataPoint &&vx_pd) noexcept - : pd(move(vx_pd.pd)) { } + : VxPairBase(move(vx_pd)), pd(move(vx_pd.pd)) { } //////////////////////////////////////////////////////////////////////// @@ -469,6 +469,7 @@ VxPairDataPoint & VxPairDataPoint::operator=(VxPairDataPoint &&vx_pd) noexcept { if(this == &vx_pd) return *this; + VxPairBase::operator=(move(vx_pd)); pd = move(vx_pd.pd); return *this; From 1ff6bc86ad2630772276668924d3e818eda95a9f Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 15:20:16 +0000 Subject: [PATCH 11/23] Per dtcenter/METplus-Internal#68, saving latest state of development. --- src/basic/vx_util/data_plane.cc | 23 ++++ src/basic/vx_util/data_plane.h | 2 + src/basic/vx_util/thresh_array.cc | 8 +- src/basic/vx_util/thresh_array.h | 2 +- src/libcode/vx_statistics/pair_base.cc | 120 +++++++++++++++++++- src/libcode/vx_statistics/pair_base.h | 2 + src/tools/core/pair_stat/compile_scratch.sh | 1 + src/tools/core/pair_stat/scratch.cpp | 21 ++++ 8 files changed, 166 insertions(+), 13 deletions(-) create mode 100755 src/tools/core/pair_stat/compile_scratch.sh create mode 100644 src/tools/core/pair_stat/scratch.cpp diff --git a/src/basic/vx_util/data_plane.cc b/src/basic/vx_util/data_plane.cc index 67912013fc..f0b0cd5ff0 100644 --- a/src/basic/vx_util/data_plane.cc +++ b/src/basic/vx_util/data_plane.cc @@ -1117,6 +1117,16 @@ DataPlaneArray::DataPlaneArray(const DataPlaneArray & a) { /////////////////////////////////////////////////////////////////////////////// +DataPlaneArray::DataPlaneArray(DataPlaneArray &&a) noexcept + : Lower(move(a.Lower)), + Upper(move(a.Upper)), + Plane(move(a.Plane)), + Nplanes(a.Nplanes) { + a.Nplanes = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + DataPlaneArray & DataPlaneArray::operator=(const DataPlaneArray & a) { if(this == &a) return *this; assign(a); @@ -1125,6 +1135,19 @@ DataPlaneArray & DataPlaneArray::operator=(const DataPlaneArray & a) { /////////////////////////////////////////////////////////////////////////////// +DataPlaneArray & DataPlaneArray::operator=(DataPlaneArray &&a) noexcept { + if (this != &a) { + Lower = move(a.Lower); + Upper = move(a.Upper); + Plane = move(a.Plane); + Nplanes = a.Nplanes; + a.Nplanes = 0; + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// + DataPlaneArray & DataPlaneArray::operator+=(const DataPlaneArray &d) { const char *method_name = "DataPlaneArray::operator+=(const DataPlaneArray &) -> "; diff --git a/src/basic/vx_util/data_plane.h b/src/basic/vx_util/data_plane.h index 6b548a5b5d..fe5df34db6 100644 --- a/src/basic/vx_util/data_plane.h +++ b/src/basic/vx_util/data_plane.h @@ -191,7 +191,9 @@ class DataPlaneArray { DataPlaneArray(); ~DataPlaneArray(); DataPlaneArray(const DataPlaneArray &); + DataPlaneArray(DataPlaneArray &&) noexcept; DataPlaneArray & operator=(const DataPlaneArray &); + DataPlaneArray & operator=(DataPlaneArray &&) noexcept; DataPlaneArray & operator+=(const DataPlaneArray &); DataPlaneArray & operator/=(const double); diff --git a/src/basic/vx_util/thresh_array.cc b/src/basic/vx_util/thresh_array.cc index 304aa4b538..f01340d458 100644 --- a/src/basic/vx_util/thresh_array.cc +++ b/src/basic/vx_util/thresh_array.cc @@ -25,11 +25,6 @@ using namespace std; // //////////////////////////////////////////////////////////////////////// -ThreshArray::ThreshArray() { -} - -//////////////////////////////////////////////////////////////////////// - ThreshArray::~ThreshArray() { clear(); } @@ -43,8 +38,7 @@ ThreshArray::ThreshArray(const ThreshArray & a) { //////////////////////////////////////////////////////////////////////// ThreshArray::ThreshArray(ThreshArray && a) noexcept - : t(move(a.t)) { -} + : t(move(a.t)) { } //////////////////////////////////////////////////////////////////////// diff --git a/src/basic/vx_util/thresh_array.h b/src/basic/vx_util/thresh_array.h index 2f73a01719..47b96095b9 100644 --- a/src/basic/vx_util/thresh_array.h +++ b/src/basic/vx_util/thresh_array.h @@ -30,7 +30,7 @@ class ThreshArray { public: - ThreshArray(); + ThreshArray() = default; ~ThreshArray(); ThreshArray(const ThreshArray &); ThreshArray(ThreshArray &&) noexcept; diff --git a/src/libcode/vx_statistics/pair_base.cc b/src/libcode/vx_statistics/pair_base.cc index dc4199f367..637c604faa 100644 --- a/src/libcode/vx_statistics/pair_base.cc +++ b/src/libcode/vx_statistics/pair_base.cc @@ -913,21 +913,131 @@ VxPairBase::~VxPairBase() { //////////////////////////////////////////////////////////////////////// -VxPairBase::VxPairBase(const VxPairBase &vx_pb) { +VxPairBase::VxPairBase(const VxPairBase &v) { init_from_scratch(); - assign(vx_pb); + assign(v); +} + +//////////////////////////////////////////////////////////////////////// + +VxPairBase::VxPairBase(VxPairBase &&v) noexcept + : fcst_info(v.fcst_info), + obs_info(v.obs_info), + fclm_info(v.fclm_info), + oclm_info(v.oclm_info), + desc(move(v.desc)), + interp_thresh(v.interp_thresh), + fcst_dpa(move(v.fcst_dpa)), + fcmn_dpa(move(v.fcmn_dpa)), + fcsd_dpa(move(v.fcsd_dpa)), + ocmn_dpa(move(v.ocmn_dpa)), + ocsd_dpa(move(v.ocsd_dpa)), + fcst_ut(v.fcst_ut), + beg_ut(v.beg_ut), + end_ut(v.end_ut), + sid_inc_filt(move(v.sid_inc_filt)), + sid_exc_filt(move(v.sid_exc_filt)), + obs_qty_inc_filt(move(v.obs_qty_inc_filt)), + obs_qty_exc_filt(move(v.obs_qty_exc_filt)), + mpr_thr_inc_map(move(v.mpr_thr_inc_map)), + mpr_str_inc_map(move(v.mpr_str_inc_map)), + mpr_str_exc_map(move(v.mpr_str_exc_map)), + msg_typ_sfc(move(v.msg_typ_sfc)), + msg_typ_lnd(move(v.msg_typ_lnd)), + msg_typ_wtr(move(v.msg_typ_wtr)), + msg_typ_lapsert(move(v.msg_typ_lapsert)), + msg_typ_mslagl(move(v.msg_typ_mslagl)), + sfc_info(move(v.sfc_info)), + n_msg_typ(v.n_msg_typ), + n_mask(v.n_mask), + n_interp(v.n_interp), + n_vx(v.n_vx), + pb_ptr(move(v.pb_ptr)), + n_try(v.n_try), + rej_sid(v.rej_sid), rej_var(v.rej_var), rej_vld(v.rej_vld), + rej_obs(v.rej_obs), rej_grd(v.rej_grd), rej_topo(v.rej_topo), + rej_lvl(v.rej_lvl), rej_qty(v.rej_qty), + rej_typ(move(v.rej_typ)), rej_mask(move(v.rej_mask)), + rej_fcst(move(v.rej_fcst)), rej_cmn(move(v.rej_cmn)), + rej_csd(move(v.rej_csd)), rej_mpr(move(v.rej_mpr)), + rej_dup(move(v.rej_dup)) +{ + v.fcst_info = nullptr; + v.obs_info = nullptr; + v.fclm_info = nullptr; + v.oclm_info = nullptr; +} + +//////////////////////////////////////////////////////////////////////// + +VxPairBase & VxPairBase::operator=(const VxPairBase &v) { + + if(this == &v) return *this; + + assign(v); + + return *this; } //////////////////////////////////////////////////////////////////////// -VxPairBase & VxPairBase::operator=(const VxPairBase &vx_pb) { +VxPairBase & VxPairBase::operator=(VxPairBase &&v) noexcept { + if (this != &v) { + delete fcst_info; delete obs_info; + delete fclm_info; delete oclm_info; + + fcst_info = v.fcst_info; v.fcst_info = nullptr; + obs_info = v.obs_info; v.obs_info = nullptr; + fclm_info = v.fclm_info; v.fclm_info = nullptr; + oclm_info = v.oclm_info; v.oclm_info = nullptr; + + desc = move(v.desc); + interp_thresh = v.interp_thresh; + + fcst_dpa = move(v.fcst_dpa); + fcmn_dpa = move(v.fcmn_dpa); + fcsd_dpa = move(v.fcsd_dpa); + ocmn_dpa = move(v.ocmn_dpa); + ocsd_dpa = move(v.ocsd_dpa); + + fcst_ut = v.fcst_ut; + beg_ut = v.beg_ut; + end_ut = v.end_ut; - if(this == &vx_pb) return *this; + sid_inc_filt = move(v.sid_inc_filt); + sid_exc_filt = move(v.sid_exc_filt); + obs_qty_inc_filt = move(v.obs_qty_inc_filt); + obs_qty_exc_filt = move(v.obs_qty_exc_filt); - assign(vx_pb); + mpr_thr_inc_map = move(v.mpr_thr_inc_map); + mpr_str_inc_map = move(v.mpr_str_inc_map); + mpr_str_exc_map = move(v.mpr_str_exc_map); + msg_typ_sfc = move(v.msg_typ_sfc); + msg_typ_lnd = move(v.msg_typ_lnd); + msg_typ_wtr = move(v.msg_typ_wtr); + msg_typ_lapsert = move(v.msg_typ_lapsert); + msg_typ_mslagl = move(v.msg_typ_mslagl); + + sfc_info = move(v.sfc_info); + + n_msg_typ = v.n_msg_typ; n_mask = v.n_mask; + n_interp = v.n_interp; n_vx = v.n_vx; + + pb_ptr = move(v.pb_ptr); + + n_try = v.n_try; + rej_sid = v.rej_sid; rej_var = v.rej_var; rej_vld = v.rej_vld; + rej_obs = v.rej_obs; rej_grd = v.rej_grd; rej_topo = v.rej_topo; + rej_lvl = v.rej_lvl; rej_qty = v.rej_qty; + + rej_typ = move(v.rej_typ); rej_mask = move(v.rej_mask); + rej_fcst = move(v.rej_fcst); rej_cmn = move(v.rej_cmn); + rej_csd = move(v.rej_csd); rej_mpr = move(v.rej_mpr); + rej_dup = move(v.rej_dup); + } return *this; } diff --git a/src/libcode/vx_statistics/pair_base.h b/src/libcode/vx_statistics/pair_base.h index 26026faa88..ff19dc0787 100644 --- a/src/libcode/vx_statistics/pair_base.h +++ b/src/libcode/vx_statistics/pair_base.h @@ -239,7 +239,9 @@ class VxPairBase { VxPairBase(); ~VxPairBase(); VxPairBase(const VxPairBase &); + VxPairBase(VxPairBase &&) noexcept; VxPairBase & operator=(const VxPairBase &); + VxPairBase & operator=(VxPairBase &&) noexcept; ////////////////////////////////////////////////////////////////// // diff --git a/src/tools/core/pair_stat/compile_scratch.sh b/src/tools/core/pair_stat/compile_scratch.sh new file mode 100755 index 0000000000..5190cdb6e9 --- /dev/null +++ b/src/tools/core/pair_stat/compile_scratch.sh @@ -0,0 +1 @@ +g++ -DHAVE_CONFIG_H -I. -I../../../.. -I../../../../src/basic/vx_cal -I../../../../src/basic/vx_config -I../../../../src/basic/vx_log -I../../../../src/basic/vx_math -I../../../../src/basic/vx_util -I../../../../src/basic/vx_util_math -I../../../../src/libcode/vx_afm -I../../../../src/libcode/vx_analysis_util -I../../../../src/libcode/vx_color -I../../../../src/libcode/vx_data2d -I../../../../src/libcode/vx_data2d_factory -I../../../../src/libcode/vx_data2d_grib -I../../../../src/libcode/vx_data2d_grib2 -I../../../../src/libcode/vx_data2d_nc_met -I../../../../src/libcode/vx_data2d_nc_wrf -I../../../../src/libcode/vx_data2d_nc_cf -I../../../../src/libcode/vx_data2d_python -I../../../../src/libcode/vx_data2d_ugrid -I../../../../src/libcode/vx_python3_utils -I../../../../src/libcode/vx_geodesy -I../../../../src/libcode/vx_gis -I../../../../src/libcode/vx_gnomon -I../../../../src/libcode/vx_grid -I../../../../src/libcode/vx_gsl_prob -I../../../../src/libcode/vx_ioda -I../../../../src/libcode/vx_nav -I../../../../src/libcode/vx_nc_obs -I../../../../src/libcode/vx_nc_util -I../../../../src/libcode/vx_pb_util -I../../../../src/libcode/vx_plot_util -I../../../../src/libcode/vx_pointdata_python -I../../../../src/libcode/vx_ps -I../../../../src/libcode/vx_pxm -I../../../../src/libcode/vx_render -I../../../../src/libcode/vx_regrid -I../../../../src/libcode/vx_shapedata -I../../../../src/libcode/vx_solar -I../../../../src/libcode/vx_statistics -I../../../../src/libcode/vx_stat_out -I../../../../src/libcode/vx_bool_calc -I../../../../src/libcode/vx_summary -I../../../../src/libcode/vx_time_series -I../../../../src/libcode/vx_series_data -I../../../../src/libcode/vx_seeps -I../../../../src/libcode/vx_tc_util -fopenmp -I/nrit/ral/proj-9.2.1/include -I/nrit/ral/atlas-0.30.0/include -I/nrit/ral/eckit-1.20.2/include -I/nrit/ral/netcdf-4.9.2/gnu-12.2.0/include -I/nrit/ral/hdf5-1.14.2/include -I/nrit/ral/nceplibs-1.4.0/g2c-1.6.4/include -DMET_PYTHON_BIN_EXE="/nrit/ral/met-python3.12/bin/python3.12" -I../../basic/vx_log -I../../basic/vx_util -I/nrit/ral/met-python3.12/include/python3.12 -I/usr/include -I/nrit/ral/hdf4-4.2.16-2/include/hdf -I/nrit/ral/hdf-eos2-3.0/include scratch.cpp diff --git a/src/tools/core/pair_stat/scratch.cpp b/src/tools/core/pair_stat/scratch.cpp new file mode 100644 index 0000000000..1503ebd55f --- /dev/null +++ b/src/tools/core/pair_stat/scratch.cpp @@ -0,0 +1,21 @@ +#include +#include "pair_stat_conf_info.h" + +#define CHECK_NOTHROW_MOVE(Type) \ + static_assert(std::is_nothrow_move_constructible::value, \ + #Type " is NOT nothrow move constructible"); + +CHECK_NOTHROW_MOVE(ConcatString) +CHECK_NOTHROW_MOVE(NumArray) +CHECK_NOTHROW_MOVE(IntArray) +CHECK_NOTHROW_MOVE(TimeArray) +CHECK_NOTHROW_MOVE(StringArray) +CHECK_NOTHROW_MOVE(ThreshArray) +CHECK_NOTHROW_MOVE(Grid) + +CHECK_NOTHROW_MOVE(VxPairDataPoint) +CHECK_NOTHROW_MOVE(StatHdrInfo) +CHECK_NOTHROW_MOVE(SetLogic) +CHECK_NOTHROW_MOVE(MaskLatLon) +CHECK_NOTHROW_MOVE(ClimoCDFInfo) +CHECK_NOTHROW_MOVE(BootInfo) From e8d572d88b336b4657e96a4193c0779583d69a38 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 15:21:10 +0000 Subject: [PATCH 12/23] Back out accidentally committed files. --- src/tools/core/pair_stat/compile_scratch.sh | 1 - src/tools/core/pair_stat/scratch.cpp | 21 --------------------- 2 files changed, 22 deletions(-) delete mode 100755 src/tools/core/pair_stat/compile_scratch.sh delete mode 100644 src/tools/core/pair_stat/scratch.cpp diff --git a/src/tools/core/pair_stat/compile_scratch.sh b/src/tools/core/pair_stat/compile_scratch.sh deleted file mode 100755 index 5190cdb6e9..0000000000 --- a/src/tools/core/pair_stat/compile_scratch.sh +++ /dev/null @@ -1 +0,0 @@ -g++ -DHAVE_CONFIG_H -I. -I../../../.. -I../../../../src/basic/vx_cal -I../../../../src/basic/vx_config -I../../../../src/basic/vx_log -I../../../../src/basic/vx_math -I../../../../src/basic/vx_util -I../../../../src/basic/vx_util_math -I../../../../src/libcode/vx_afm -I../../../../src/libcode/vx_analysis_util -I../../../../src/libcode/vx_color -I../../../../src/libcode/vx_data2d -I../../../../src/libcode/vx_data2d_factory -I../../../../src/libcode/vx_data2d_grib -I../../../../src/libcode/vx_data2d_grib2 -I../../../../src/libcode/vx_data2d_nc_met -I../../../../src/libcode/vx_data2d_nc_wrf -I../../../../src/libcode/vx_data2d_nc_cf -I../../../../src/libcode/vx_data2d_python -I../../../../src/libcode/vx_data2d_ugrid -I../../../../src/libcode/vx_python3_utils -I../../../../src/libcode/vx_geodesy -I../../../../src/libcode/vx_gis -I../../../../src/libcode/vx_gnomon -I../../../../src/libcode/vx_grid -I../../../../src/libcode/vx_gsl_prob -I../../../../src/libcode/vx_ioda -I../../../../src/libcode/vx_nav -I../../../../src/libcode/vx_nc_obs -I../../../../src/libcode/vx_nc_util -I../../../../src/libcode/vx_pb_util -I../../../../src/libcode/vx_plot_util -I../../../../src/libcode/vx_pointdata_python -I../../../../src/libcode/vx_ps -I../../../../src/libcode/vx_pxm -I../../../../src/libcode/vx_render -I../../../../src/libcode/vx_regrid -I../../../../src/libcode/vx_shapedata -I../../../../src/libcode/vx_solar -I../../../../src/libcode/vx_statistics -I../../../../src/libcode/vx_stat_out -I../../../../src/libcode/vx_bool_calc -I../../../../src/libcode/vx_summary -I../../../../src/libcode/vx_time_series -I../../../../src/libcode/vx_series_data -I../../../../src/libcode/vx_seeps -I../../../../src/libcode/vx_tc_util -fopenmp -I/nrit/ral/proj-9.2.1/include -I/nrit/ral/atlas-0.30.0/include -I/nrit/ral/eckit-1.20.2/include -I/nrit/ral/netcdf-4.9.2/gnu-12.2.0/include -I/nrit/ral/hdf5-1.14.2/include -I/nrit/ral/nceplibs-1.4.0/g2c-1.6.4/include -DMET_PYTHON_BIN_EXE="/nrit/ral/met-python3.12/bin/python3.12" -I../../basic/vx_log -I../../basic/vx_util -I/nrit/ral/met-python3.12/include/python3.12 -I/usr/include -I/nrit/ral/hdf4-4.2.16-2/include/hdf -I/nrit/ral/hdf-eos2-3.0/include scratch.cpp diff --git a/src/tools/core/pair_stat/scratch.cpp b/src/tools/core/pair_stat/scratch.cpp deleted file mode 100644 index 1503ebd55f..0000000000 --- a/src/tools/core/pair_stat/scratch.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include "pair_stat_conf_info.h" - -#define CHECK_NOTHROW_MOVE(Type) \ - static_assert(std::is_nothrow_move_constructible::value, \ - #Type " is NOT nothrow move constructible"); - -CHECK_NOTHROW_MOVE(ConcatString) -CHECK_NOTHROW_MOVE(NumArray) -CHECK_NOTHROW_MOVE(IntArray) -CHECK_NOTHROW_MOVE(TimeArray) -CHECK_NOTHROW_MOVE(StringArray) -CHECK_NOTHROW_MOVE(ThreshArray) -CHECK_NOTHROW_MOVE(Grid) - -CHECK_NOTHROW_MOVE(VxPairDataPoint) -CHECK_NOTHROW_MOVE(StatHdrInfo) -CHECK_NOTHROW_MOVE(SetLogic) -CHECK_NOTHROW_MOVE(MaskLatLon) -CHECK_NOTHROW_MOVE(ClimoCDFInfo) -CHECK_NOTHROW_MOVE(BootInfo) From 617680b5c1c2f5e88016a6ccfc585d40ead60260 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 10:05:59 -0600 Subject: [PATCH 13/23] Per dtcenter/METplus-Internal#68, fix ThreshArray bin indexing bug and handle empty regex strings. --- src/basic/vx_util/get_filenames.cc | 4 +++- src/basic/vx_util/thresh_array.cc | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/basic/vx_util/get_filenames.cc b/src/basic/vx_util/get_filenames.cc index 1522844226..c1d93301f7 100644 --- a/src/basic/vx_util/get_filenames.cc +++ b/src/basic/vx_util/get_filenames.cc @@ -277,7 +277,9 @@ if ( prefix ) regex1 << "^" << prefix; if ( suffix ) regex2 << suffix << "$"; -return check_filename_regex(path, regex1.c_str(), regex2.c_str()); +return check_filename_regex(path, + prefix ? regex1.c_str() : nullptr, + suffix ? regex2.c_str() : nullptr); } diff --git a/src/basic/vx_util/thresh_array.cc b/src/basic/vx_util/thresh_array.cc index f01340d458..362bd0a890 100644 --- a/src/basic/vx_util/thresh_array.cc +++ b/src/basic/vx_util/thresh_array.cc @@ -334,7 +334,8 @@ int ThreshArray::check_bins(double v, const ClimoPntInfo *cpi) const { if(t[0].get_type() == thresh_lt || t[0].get_type() == thresh_le) { - for(int i=0, bin=-1; i and >=, check thresholds right to left else { - for(int i=n()-1, bin=-1; i>=0; i--) { + bin = -1; + for(int i=n()-1; i>=0; i--) { if(t[i].check(v, cpi)) { bin = i+1; break; From 6da31ca417234fe6451087c9e7a515c6e0adcc5c Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 10:40:47 -0600 Subject: [PATCH 14/23] Update MET's SonarQube scan job to copy the report-task.txt log file out to the GitHub action artifact. --- .github/jobs/build_sonarqube_image.sh | 4 ++++ internal/scripts/sonarqube/sonar-project.properties | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/jobs/build_sonarqube_image.sh b/.github/jobs/build_sonarqube_image.sh index 55d558624b..afe090bc08 100755 --- a/.github/jobs/build_sonarqube_image.sh +++ b/.github/jobs/build_sonarqube_image.sh @@ -43,3 +43,7 @@ id=$(docker create ${DOCKERHUB_TAG}) time_command mkdir -p /tmp/scannerwork time_command docker cp $id:/met/.scannerwork/report-task.txt /tmp/scannerwork/report-task.txt docker rm -v $id + +# Copy report-task.txt into the logs directory so it is included in the +# logs_sonarqube artifact uploaded by the workflow +cp /tmp/scannerwork/report-task.txt ${RUNNER_WORKSPACE}/logs/report-task.txt diff --git a/internal/scripts/sonarqube/sonar-project.properties b/internal/scripts/sonarqube/sonar-project.properties index 30ee495c7b..42618f9cf5 100644 --- a/internal/scripts/sonarqube/sonar-project.properties +++ b/internal/scripts/sonarqube/sonar-project.properties @@ -4,11 +4,11 @@ sonar.projectName=MET sonar.projectVersion=SONAR_PROJECT_VERSION sonar.branch.name=SONAR_BRANCH_NAME sonar.sources=src,scripts/python,data/wrappers -sonar.python.version=3.6.3 +sonar.python.version=3.14 sonar.sourceEncoding=UTF-8 -# The build-wrapper output dir -sonar.cfamily.build-wrapper-output=bw-outputs +# Path to the compile_commands.json generated by build-wrapper +sonar.cfamily.compile-commands=bw-outputs/compile_commands.json # SonarQube server sonar.host.url=SONAR_HOST_URL From 3465b88eb4d57d606dff0b9b4847ca703f0dddb8 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 11:23:37 -0600 Subject: [PATCH 15/23] Store a new shell script to export all existing SonarQube findings. --- .../sonarqube/fetch_sonarqube_findings.sh | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100755 internal/scripts/sonarqube/fetch_sonarqube_findings.sh diff --git a/internal/scripts/sonarqube/fetch_sonarqube_findings.sh b/internal/scripts/sonarqube/fetch_sonarqube_findings.sh new file mode 100755 index 0000000000..4909f45ecd --- /dev/null +++ b/internal/scripts/sonarqube/fetch_sonarqube_findings.sh @@ -0,0 +1,203 @@ +#!/bin/bash +# +# Fetch SonarQube findings (issues) for a branch via the Web API +#======================================================================= +# +# The sonar-scanner CLI only uploads an analysis report to the SonarQube +# server -- it does not print or return the resulting findings. The +# server processes that report asynchronously (the "Compute Engine" task) +# and only afterward are issues available, via the SonarQube dashboard or +# its Web API. +# +# This script pulls the findings for a branch via the Web API and writes +# them to local files for review. It is intended to be run locally by a +# developer with their own SONAR_TOKEN. Do NOT wire this into CI or +# publish its output as a build artifact -- unlike the dashboard (which +# requires a SonarQube login), CI artifacts on a public repo are visible +# to anyone who can view the workflow run. +# +# The /api/issues/search endpoint refuses to page past 10000 results +# (page * page_size > 10000 is rejected), regardless of how many issues +# actually match, because it is backed by Elasticsearch's default +# index.max_result_window. When a branch has more than 10000 matching +# issues, this script automatically splits the query by severity, and +# (if a single severity is still too large) further by type, fetching +# each slice separately and merging the results back together. +# +# Usage: fetch_sonarqube_findings.sh branch [outdir] +# where "branch" specifies the sonar.branch.name that was analyzed +# "outdir" specifies the output directory (default: .) +# +# Required Environment Variables: +# SONAR_HOST_URL +# SONAR_TOKEN +# +# Optional Environment Variables: +# SONAR_COMPONENT_KEY (default: MET) +# +# Requires: curl, jq +# +#======================================================================= + +function usage { + echo + echo "USAGE: $(basename $0) branch [outdir]" + echo " where \"branch\" specifies the sonar.branch.name that was analyzed" + echo " \"outdir\" specifies the output directory (default: .)" + echo +} + +# Check for arguments +if [[ $# -lt 1 ]]; then usage; exit 1; fi + +BRANCH=$1 +OUTDIR=${2:-.} +COMPONENT_KEY=${SONAR_COMPONENT_KEY:-MET} +PAGE_SIZE=500 +MAX_RESULT_WINDOW=10000 + +# SonarQube's fixed sets of severity and type values, used to split up +# queries that would otherwise exceed MAX_RESULT_WINDOW. +SEVERITIES=(BLOCKER CRITICAL MAJOR MINOR INFO) +TYPES=(BUG VULNERABILITY CODE_SMELL) + +# Check required environment variables +if [ -z "$SONAR_HOST_URL" ]; then + echo "ERROR: $(basename $0) -> \$SONAR_HOST_URL not defined!" + exit 1 +fi +if [ -z "$SONAR_TOKEN" ]; then + echo "ERROR: $(basename $0) -> \$SONAR_TOKEN not defined!" + exit 1 +fi +if ! command -v jq >/dev/null 2>&1; then + echo "ERROR: $(basename $0) -> jq is required but was not found in PATH" + exit 1 +fi + +mkdir -p ${OUTDIR} +SAFE_BRANCH=$(echo ${BRANCH} | sed 's%/%_%g') +JSON_FILE=${OUTDIR}/sonarqube_findings_${SAFE_BRANCH}.json +TXT_FILE=${OUTDIR}/sonarqube_findings_${SAFE_BRANCH}.txt +RAW_FILE=$(mktemp) +> ${RAW_FILE} + +BASE_QUERY="componentKeys=${COMPONENT_KEY}&branch=${BRANCH}&resolved=false" + +# api_get extra_params -> prints the JSON response on stdout, returns +# non-zero on failure (after printing the server's error body to stderr) +function api_get { + local extra="$1" + local url="${SONAR_HOST_URL}/api/issues/search?${BASE_QUERY}&${extra}" + local response + response=$(curl -s -f -u "${SONAR_TOKEN}:" "${url}") + if [ $? -ne 0 ]; then + echo "ERROR: $(basename $0) -> request failed: ${url}" >&2 + curl -s -u "${SONAR_TOKEN}:" "${url}" >&2 + return 1 + fi + echo "${response}" +} + +# get_total extra_params -> prints the total match count for that filter +function get_total { + local response + response=$(api_get "$1&ps=1&p=1") || return 1 + echo "${response}" | jq '.total' +} + +# fetch_all_pages extra_params label -> pages fully through a filter that +# is already known to match no more than MAX_RESULT_WINDOW issues, +# appending each page's raw JSON response to RAW_FILE +function fetch_all_pages { + local extra="$1" + local label="$2" + local page=1 + local fetched=0 + local total=-1 + local response n + + while :; do + response=$(api_get "${extra}&ps=${PAGE_SIZE}&p=${page}") || return 1 + + if [ ${total} -eq -1 ]; then + total=$(echo "${response}" | jq '.total') + fi + + echo "${response}" >> ${RAW_FILE} + + n=$(echo "${response}" | jq '.issues | length') + fetched=$(( fetched + n )) + echo " [${label}] page ${page}: ${n} issue(s), ${fetched}/${total}" + + if [ ${n} -eq 0 ] || [ ${fetched} -ge ${total} ]; then + break + fi + page=$(( page + 1 )) + done +} + +# fetch_partition extra_params label split_dim -> fetches a filter, +# recursively splitting by severity and then type if it exceeds +# MAX_RESULT_WINDOW; split_dim is the next dimension to split by if needed +function fetch_partition { + local extra="$1" + local label="$2" + local split_dim="$3" + local total sev typ + + total=$(get_total "${extra}") || return 1 + + if [ "${total}" -eq 0 ]; then + return 0 + fi + + if [ "${total}" -le ${MAX_RESULT_WINDOW} ]; then + echo "[${label}] ${total} issue(s)" + fetch_all_pages "${extra}" "${label}" + return $? + fi + + case "${split_dim}" in + severity) + echo "[${label}] ${total} issue(s) exceeds the ${MAX_RESULT_WINDOW} API limit; splitting by severity" + for sev in "${SEVERITIES[@]}"; do + fetch_partition "${extra}&severities=${sev}" "${label}/${sev}" "type" || return 1 + done + ;; + type) + echo "[${label}] ${total} issue(s) exceeds the ${MAX_RESULT_WINDOW} API limit; splitting by type" + for typ in "${TYPES[@]}"; do + fetch_partition "${extra}&types=${typ}" "${label}/${typ}" "none" || return 1 + done + ;; + none) + echo "WARNING: [${label}] has ${total} issue(s), still exceeding the ${MAX_RESULT_WINDOW} API limit after splitting by severity and type. Only the first ${MAX_RESULT_WINDOW} will be fetched -- some findings will be MISSING from the output." >&2 + fetch_all_pages "${extra}" "${label}" + ;; + esac +} + +echo "Fetching SonarQube findings for component '${COMPONENT_KEY}' branch '${BRANCH}' from ${SONAR_HOST_URL}" + +fetch_partition "" "all" "severity" +STATUS=$? +if [ ${STATUS} -ne 0 ]; then + rm -f ${RAW_FILE} + exit ${STATUS} +fi + +# Merge all of the pages into a single JSON file, de-duplicating issues +# by key since overlapping facets are not used but a retry could add +# a page twice +jq -s '{ issues: ([.[].issues[]] | unique_by(.key)) } | . + { total: (.issues | length) }' ${RAW_FILE} > ${JSON_FILE} +rm -f ${RAW_FILE} + +TOTAL_FETCHED=$(jq '.total' ${JSON_FILE}) +echo "Wrote ${TOTAL_FETCHED} issue(s) to ${JSON_FILE}" + +# Write a flat, sortable text summary: severity, rule, file, line, message +jq -r '.issues[] | [.severity, .rule, .component, (.line // "-" | tostring), .message] | @tsv' ${JSON_FILE} \ + | sort > ${TXT_FILE} + +echo "Wrote ${TXT_FILE}" From 4687828de60b93aaeb0a0ac691b5c81e3b70b242 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 11:32:28 -0600 Subject: [PATCH 16/23] Fix SonarQube blockers --- src/basic/vx_config/threshold.cc | 34 ++++++++++++++++++++++++++++ src/basic/vx_config/threshold.h | 2 ++ src/basic/vx_util/ncrr_array.h | 30 +++++++++++++++++++++++- src/libcode/vx_gis/shp_array.h | 28 +++++++++++++++++++++++ src/libcode/vx_tc_util/track_info.cc | 3 ++- 5 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/basic/vx_config/threshold.cc b/src/basic/vx_config/threshold.cc index 84c032b8b9..f2108c7b6f 100644 --- a/src/basic/vx_config/threshold.cc +++ b/src/basic/vx_config/threshold.cc @@ -1369,6 +1369,20 @@ assign(c); //////////////////////////////////////////////////////////////////////// +SingleThresh::SingleThresh(SingleThresh && c) noexcept + +{ + +node = c.node; + +c.node = nullptr; + +} + + +//////////////////////////////////////////////////////////////////////// + + SingleThresh::SingleThresh(const char * str) { @@ -1399,6 +1413,26 @@ return *this; //////////////////////////////////////////////////////////////////////// +SingleThresh & SingleThresh::operator=(SingleThresh && c) noexcept + +{ + +if ( this == &c ) return *this; + +clear(); + +node = c.node; + +c.node = nullptr; + +return *this; + +} + + +//////////////////////////////////////////////////////////////////////// + + bool SingleThresh::operator==(const SingleThresh &st) const { diff --git a/src/basic/vx_config/threshold.h b/src/basic/vx_config/threshold.h index 9ad748c0a5..3df044dd78 100644 --- a/src/basic/vx_config/threshold.h +++ b/src/basic/vx_config/threshold.h @@ -401,8 +401,10 @@ class SingleThresh { SingleThresh(); ~SingleThresh(); SingleThresh(const SingleThresh &); + SingleThresh(SingleThresh &&) noexcept; SingleThresh(const char *); SingleThresh & operator=(const SingleThresh &); + SingleThresh & operator=(SingleThresh &&) noexcept; void dump(std::ostream &, int = 0) const; diff --git a/src/basic/vx_util/ncrr_array.h b/src/basic/vx_util/ncrr_array.h index 82203bd29a..2da631ba8a 100644 --- a/src/basic/vx_util/ncrr_array.h +++ b/src/basic/vx_util/ncrr_array.h @@ -65,9 +65,18 @@ class NCRR_Array { NCRR_Array(const NCRR_Array & _a) { init_from_scratch(); assign(_a); } + NCRR_Array(NCRR_Array && _a) noexcept + : Nelements(_a.Nelements), Nalloc(_a.Nalloc), AllocInc(_a.AllocInc), e(_a.e) { + + _a.e = (T **) nullptr; + _a.Nelements = 0; + _a.Nalloc = 0; + + } + NCRR_Array & operator=(const NCRR_Array & _a) { - if ( this == _a ) return *this; + if ( this == &_a ) return *this; assign(_a); @@ -75,6 +84,25 @@ class NCRR_Array { } + NCRR_Array & operator=(NCRR_Array && _a) noexcept { + + if ( this == &_a ) return *this; + + clear(); + + Nelements = _a.Nelements; + Nalloc = _a.Nalloc; + AllocInc = _a.AllocInc; + e = _a.e; + + _a.e = (T **) nullptr; + _a.Nelements = 0; + _a.Nalloc = 0; + + return *this; + + } + void clear(); void dump(std::ostream &, int = 0) const; diff --git a/src/libcode/vx_gis/shp_array.h b/src/libcode/vx_gis/shp_array.h index 29fc4e10c2..29a9d8f91b 100644 --- a/src/libcode/vx_gis/shp_array.h +++ b/src/libcode/vx_gis/shp_array.h @@ -65,6 +65,15 @@ class Shp_Array { Shp_Array(const Shp_Array & _a) { init_from_scratch(); assign(_a); } + Shp_Array(Shp_Array && _a) noexcept + : Nelements(_a.Nelements), Nalloc(_a.Nalloc), AllocInc(_a.AllocInc), E(_a.E) { + + _a.E = (T *) nullptr; + _a.Nelements = 0; + _a.Nalloc = 0; + + } + Shp_Array & operator=(const Shp_Array & _a) { if ( this == &_a ) return *this; @@ -75,6 +84,25 @@ class Shp_Array { } + Shp_Array & operator=(Shp_Array && _a) noexcept { + + if ( this == &_a ) return *this; + + clear(); + + Nelements = _a.Nelements; + Nalloc = _a.Nalloc; + AllocInc = _a.AllocInc; + E = _a.E; + + _a.E = (T *) nullptr; + _a.Nelements = 0; + _a.Nalloc = 0; + + return *this; + + } + void clear(); diff --git a/src/libcode/vx_tc_util/track_info.cc b/src/libcode/vx_tc_util/track_info.cc index 8fadc2d919..9f12d83d00 100644 --- a/src/libcode/vx_tc_util/track_info.cc +++ b/src/libcode/vx_tc_util/track_info.cc @@ -426,7 +426,8 @@ StringArray TrackInfo::track_lines() const { void TrackInfo::add(const TrackPoint &p) { extend(NPoints + 1, false); - Point[NPoints++] = p; + Point[NPoints] = p; + NPoints++; // Check the valid time range if(MinValidTime == (unixtime) 0 || p.valid() < MinValidTime) From 6765e0d38b04c50455da403fec152511f22577e1 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 14:28:15 -0600 Subject: [PATCH 17/23] Per dtcenter/METplus-Internal#68, tell SonarQube to skip scanning bison output and flagging the GOTO statements they contain based on the cpp:S999 rule. --- internal/scripts/sonarqube/sonar-project.properties | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/scripts/sonarqube/sonar-project.properties b/internal/scripts/sonarqube/sonar-project.properties index 42618f9cf5..130a1c012e 100644 --- a/internal/scripts/sonarqube/sonar-project.properties +++ b/internal/scripts/sonarqube/sonar-project.properties @@ -7,6 +7,14 @@ sonar.sources=src,scripts/python,data/wrappers sonar.python.version=3.14 sonar.sourceEncoding=UTF-8 +# Exclude bison-generated parser code. The goto-based state machine in +# these files comes from bison's own skeleton templates (yacc.c/lalr1.cc) +# and is not something a grammar file or bison flag can change, so findings +# in it (e.g. cpp:S999) are not actionable. +sonar.exclusions=src/basic/vx_config/config.tab.cc,src/basic/vx_config/config.tab.h,\ +src/libcode/vx_color/color_parser_yacc.cc,src/libcode/vx_color/color_parser_yacc.h,\ +src/basic/enum_to_string/enum_parser.cc,src/basic/enum_to_string/enum_parser.h + # Path to the compile_commands.json generated by build-wrapper sonar.cfamily.compile-commands=bw-outputs/compile_commands.json From 941ac25067755b74aab3e86a9fa0f28f73256c21 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 16:46:49 -0600 Subject: [PATCH 18/23] Update fetch_sonarqube_findings.sh to pause between requests to reduce the load on the dtc-sca server. --- .../sonarqube/fetch_sonarqube_findings.sh | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/internal/scripts/sonarqube/fetch_sonarqube_findings.sh b/internal/scripts/sonarqube/fetch_sonarqube_findings.sh index 4909f45ecd..68ce05cdbd 100755 --- a/internal/scripts/sonarqube/fetch_sonarqube_findings.sh +++ b/internal/scripts/sonarqube/fetch_sonarqube_findings.sh @@ -24,6 +24,17 @@ # (if a single severity is still too large) further by type, fetching # each slice separately and merging the results back together. # +# Deep pagination against Elasticsearch (large "from" offsets) gets more +# expensive the deeper you page, and this endpoint is backed by an +# embedded, often memory-constrained Elasticsearch node. Hammering it +# with many such requests back-to-back has been observed to destabilize +# a SonarQube server. To keep this script a good citizen: +# - Requests are rate-limited (SONAR_REQUEST_DELAY between requests). +# - The page size defaults to a modest value, not the API's max of 500. +# - SONAR_SEVERITIES / SONAR_TYPES let you fetch only what you need +# right now (e.g. just BLOCKER) instead of every issue on the branch, +# which avoids deep pagination altogether for small slices. +# # Usage: fetch_sonarqube_findings.sh branch [outdir] # where "branch" specifies the sonar.branch.name that was analyzed # "outdir" specifies the output directory (default: .) @@ -34,6 +45,12 @@ # # Optional Environment Variables: # SONAR_COMPONENT_KEY (default: MET) +# SONAR_SEVERITIES Comma-separated subset of BLOCKER, CRITICAL, +# MAJOR, MINOR, INFO to fetch (default: all) +# SONAR_TYPES Comma-separated subset of BUG, VULNERABILITY, +# CODE_SMELL to fetch (default: all) +# SONAR_PAGE_SIZE Issues per request, max 500 (default: 100) +# SONAR_REQUEST_DELAY Seconds to sleep between requests (default: 1) # # Requires: curl, jq # @@ -53,11 +70,12 @@ if [[ $# -lt 1 ]]; then usage; exit 1; fi BRANCH=$1 OUTDIR=${2:-.} COMPONENT_KEY=${SONAR_COMPONENT_KEY:-MET} -PAGE_SIZE=500 +PAGE_SIZE=${SONAR_PAGE_SIZE:-100} +REQUEST_DELAY=${SONAR_REQUEST_DELAY:-1} MAX_RESULT_WINDOW=10000 -# SonarQube's fixed sets of severity and type values, used to split up -# queries that would otherwise exceed MAX_RESULT_WINDOW. +# SonarQube's fixed sets of severity and type values, used only to split +# up an *unfiltered* query that turns out to exceed MAX_RESULT_WINDOW. SEVERITIES=(BLOCKER CRITICAL MAJOR MINOR INFO) TYPES=(BUG VULNERABILITY CODE_SMELL) @@ -84,12 +102,30 @@ RAW_FILE=$(mktemp) BASE_QUERY="componentKeys=${COMPONENT_KEY}&branch=${BRANCH}&resolved=false" +# Fold a caller-requested severity/type filter directly into every +# request (the API accepts comma-separated values for both params). This +# both narrows the result set and, when it's small enough, sidesteps +# the deep-pagination splitting logic below entirely. +FILTERED=0 +if [ -n "${SONAR_SEVERITIES}" ]; then + BASE_QUERY="${BASE_QUERY}&severities=${SONAR_SEVERITIES}" + FILTERED=1 +fi +if [ -n "${SONAR_TYPES}" ]; then + BASE_QUERY="${BASE_QUERY}&types=${SONAR_TYPES}" + FILTERED=1 +fi + # api_get extra_params -> prints the JSON response on stdout, returns -# non-zero on failure (after printing the server's error body to stderr) +# non-zero on failure (after printing the server's error body to stderr). +# Rate-limited by SONAR_REQUEST_DELAY to avoid overloading the server. function api_get { local extra="$1" local url="${SONAR_HOST_URL}/api/issues/search?${BASE_QUERY}&${extra}" local response + + sleep ${REQUEST_DELAY} + response=$(curl -s -f -u "${SONAR_TOKEN}:" "${url}") if [ $? -ne 0 ]; then echo "ERROR: $(basename $0) -> request failed: ${url}" >&2 @@ -179,8 +215,20 @@ function fetch_partition { } echo "Fetching SonarQube findings for component '${COMPONENT_KEY}' branch '${BRANCH}' from ${SONAR_HOST_URL}" +if [ ${FILTERED} -eq 1 ]; then + echo "Filter: severities=[${SONAR_SEVERITIES:-all}] types=[${SONAR_TYPES:-all}]" +fi -fetch_partition "" "all" "severity" +# When the caller already narrowed the query with SONAR_SEVERITIES / +# SONAR_TYPES, don't also auto-split by severity/type -- that filter is +# already baked into BASE_QUERY, and appending another severities= or +# types= param on top of it would conflict. Just warn if it's still too +# big rather than fetching everything to find a further split. +if [ ${FILTERED} -eq 1 ]; then + fetch_partition "" "all" "none" +else + fetch_partition "" "all" "severity" +fi STATUS=$? if [ ${STATUS} -ne 0 ]; then rm -f ${RAW_FILE} From be8fb902b6651d96714a0b6b03901ace0c0a9a2b Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 10 Sep 2026 16:57:44 -0600 Subject: [PATCH 19/23] Per dtcenter/METplus-Internal#68, try a second time to squash a stubborn blocker issue. --- src/libcode/vx_tc_util/track_info.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libcode/vx_tc_util/track_info.cc b/src/libcode/vx_tc_util/track_info.cc index 9f12d83d00..a4f087dd21 100644 --- a/src/libcode/vx_tc_util/track_info.cc +++ b/src/libcode/vx_tc_util/track_info.cc @@ -426,6 +426,13 @@ StringArray TrackInfo::track_lines() const { void TrackInfo::add(const TrackPoint &p) { extend(NPoints + 1, false); + + if(NPoints < 0 || NPoints >= NAlloc) { + mlog << Error << "\nTrackInfo::add(const TrackPoint &) -> " + << "index out of range (" << NPoints << ")!\n\n"; + exit(1); + } + Point[NPoints] = p; NPoints++; From ca9a6dd1a58ea5acc4e6582bce39802d84185726 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 11 Sep 2026 09:47:30 -0600 Subject: [PATCH 20/23] Per dtcenter/METplus-Internal#68, address 6 SonarQube findings in crc_array.h, including a 'Reliability' one. --- src/basic/vx_util/crc_array.h | 45 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/src/basic/vx_util/crc_array.h b/src/basic/vx_util/crc_array.h index 1b997b8c71..9fc4684926 100644 --- a/src/basic/vx_util/crc_array.h +++ b/src/basic/vx_util/crc_array.h @@ -75,7 +75,17 @@ class CRC_Array { CRC_Array & operator=(const NumArray &); - bool operator==(const CRC_Array &) const; + friend bool operator==(const CRC_Array & a, const CRC_Array & b) { + + if ( a.n() != b.n() ) return false; + + for(int j=0; j - -bool CRC_Array::operator==(const CRC_Array & a) const - -{ - -if ( n() != a.n() ) return false; - -for(int j=0; j void CRC_Array::init_from_scratch() @@ -280,12 +270,11 @@ void CRC_Array::dump_one_line(std::ostream & out, int depth) const { -int j; Indent prefix(depth); out << prefix << '(' << n() << ") "; -for (j=0; j 0 ) out << ' '; @@ -500,9 +489,7 @@ sa.parse_css(text); extend(n() + sa.n()); -int j; - -for (j=0; j<(sa.n()); j++) { +for (int j=0; j<(sa.n()); j++) { add(timestring_to_sec(sa[j].c_str())); @@ -542,9 +529,9 @@ T CRC_Array::sum() const T s = 0; -int count; +int count = 0; -for(int j=0, count=0; j Date: Fri, 11 Sep 2026 11:10:53 -0600 Subject: [PATCH 21/23] Per dtcenter/METplus-Internal#68, address SonarQube findings in mm_engine.h/.cc, including a 'Reliability' one. --- src/tools/other/mode_time_domain/mm_engine.cc | 88 +++++++------------ src/tools/other/mode_time_domain/mm_engine.h | 5 +- 2 files changed, 33 insertions(+), 60 deletions(-) diff --git a/src/tools/other/mode_time_domain/mm_engine.cc b/src/tools/other/mode_time_domain/mm_engine.cc index cc76982089..c5a6130a94 100644 --- a/src/tools/other/mode_time_domain/mm_engine.cc +++ b/src/tools/other/mode_time_domain/mm_engine.cc @@ -89,8 +89,6 @@ void MM_Engine::init_from_scratch() { -comp_to_eq = (int *) nullptr; - clear(); return; @@ -107,7 +105,7 @@ void MM_Engine::clear() N_Composites = 0; -if ( comp_to_eq ) { delete [] comp_to_eq; comp_to_eq = 0; } +comp_to_eq.clear(); calc.clear(); @@ -132,14 +130,7 @@ clear(); N_Composites = e.N_Composites; -if ( e.comp_to_eq ) { - - comp_to_eq = new int [N_Composites]; - - memcpy(comp_to_eq, e.comp_to_eq, N_Composites*sizeof(int)); - -} - +comp_to_eq = e.comp_to_eq; calc = e.calc; @@ -165,9 +156,7 @@ graph.set_size(_n_fcst, _n_obs); // set up the initial partition // -int j; - -for (j=0; j<(graph.n_total()); ++j) { +for (int j=0; j<(graph.n_total()); ++j) { part.add_no_repeat(j); @@ -189,9 +178,7 @@ void MM_Engine::do_match_merge() { -int j, k; -int f_i, o_i; - +int j; // // fcst, obs @@ -199,11 +186,11 @@ int f_i, o_i; for (j=0; j<(graph.n_fcst()); ++j) { - f_i = graph.f_index(j); + int f_i = graph.f_index(j); - for (k=0; k<(graph.n_obs()); ++k) { + for (int k=0; k<(graph.n_obs()); ++k) { - o_i = graph.o_index(k); + int o_i = graph.o_index(k); if ( ! graph.has_fo_edge(j, k) ) continue; @@ -227,7 +214,7 @@ for (j=0; j<(graph.n_fcst()); ++j) { // -const EquivalenceClass * eq = 0; +const EquivalenceClass * eq = nullptr; N_Composites = 0; IntArray index_list; @@ -244,16 +231,11 @@ for (j=0; j<(part.n_elements()); ++j) { } // for j -if ( N_Composites > 0 ) { +comp_to_eq.clear(); - if ( comp_to_eq ) delete [] comp_to_eq; - comp_to_eq = new int [index_list.n()]; +for (j=0; j 5 ) { s << "Composites ...\n"; - for (j=0; jn_elements()); ++j) { +for (int j=0; j<(eq->n_elements()); ++j) { - k = eq->element(j); + int k = eq->element(j); if ( k < graph.n_fcst() ) a.add(k); @@ -395,13 +374,12 @@ IntArray MM_Engine::obs_composite(const int _composite_number) const { -int j, k; IntArray a; const EquivalenceClass * eq = part(comp_to_eq[_composite_number]); // this does range checking -for (j=0; j<(eq->n_elements()); ++j) { +for (int j=0; j<(eq->n_elements()); ++j) { - k = eq->element(j); + int k = eq->element(j); if ( k >= graph.n_fcst() ) a.add(k - graph.n_fcst()); @@ -419,14 +397,11 @@ int MM_Engine::map_fcst_id_to_composite(const int id) const // zero-based { -int j, k, m; +int k = id; +int j = part.which_class(k); -k = id; - -j = part.which_class(k); - -for (m=0; m Date: Fri, 11 Sep 2026 11:21:12 -0600 Subject: [PATCH 22/23] Per dtcenter/METplus-Internal#68, fix SonarQube 'Reliability' issue in point2grid.cc. --- src/tools/other/point2grid/point2grid.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/other/point2grid/point2grid.cc b/src/tools/other/point2grid/point2grid.cc index 470c6cb0f2..957be5dac9 100644 --- a/src/tools/other/point2grid/point2grid.cc +++ b/src/tools/other/point2grid/point2grid.cc @@ -1607,6 +1607,7 @@ static void regrid_nc_variable(NcFile *nc_in, Met2dDataFile *fr_mtddf, if (0 < cellArray.n()) { dataArray.clear(); dataArray.extend(cellArray.n()); + int single_from_index = bad_data_int; for (int dIdx=0; dIdx= 4) { if (from_min_value > data_value) from_min_value = data_value; @@ -1647,7 +1649,7 @@ static void regrid_nc_variable(NcFile *nc_in, Met2dDataFile *fr_mtddf, if (1 == data_cnt) mlog << Debug(9) << method_name << "value: " << to_value << " to (" << to_lon << ", " << to_lat - << ") from offset " << from_index << ".\n"; + << ") from offset " << single_from_index << ".\n"; else mlog << Debug(9) << method_name << "value: " << to_value From 3cd288ea1cc3f08ff2f6ce9506a17ffecb7731b6 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 11 Sep 2026 13:08:21 -0600 Subject: [PATCH 23/23] Per dtcenter/METplus-Internal#68, patch the new fetch_sonarqube_findings.sh script based on PR feedback from SonarCloud. --- .../sonarqube/fetch_sonarqube_findings.sh | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/internal/scripts/sonarqube/fetch_sonarqube_findings.sh b/internal/scripts/sonarqube/fetch_sonarqube_findings.sh index 68ce05cdbd..50e4bd85e3 100755 --- a/internal/scripts/sonarqube/fetch_sonarqube_findings.sh +++ b/internal/scripts/sonarqube/fetch_sonarqube_findings.sh @@ -62,6 +62,7 @@ function usage { echo " where \"branch\" specifies the sonar.branch.name that was analyzed" echo " \"outdir\" specifies the output directory (default: .)" echo + return 0 } # Check for arguments @@ -80,16 +81,16 @@ SEVERITIES=(BLOCKER CRITICAL MAJOR MINOR INFO) TYPES=(BUG VULNERABILITY CODE_SMELL) # Check required environment variables -if [ -z "$SONAR_HOST_URL" ]; then - echo "ERROR: $(basename $0) -> \$SONAR_HOST_URL not defined!" +if [[ -z "$SONAR_HOST_URL" ]]; then + echo "ERROR: $(basename $0) -> \$SONAR_HOST_URL not defined!" >&2 exit 1 fi -if [ -z "$SONAR_TOKEN" ]; then - echo "ERROR: $(basename $0) -> \$SONAR_TOKEN not defined!" +if [[ -z "$SONAR_TOKEN" ]]; then + echo "ERROR: $(basename $0) -> \$SONAR_TOKEN not defined!" >&2 exit 1 fi if ! command -v jq >/dev/null 2>&1; then - echo "ERROR: $(basename $0) -> jq is required but was not found in PATH" + echo "ERROR: $(basename $0) -> jq is required but was not found in PATH" >&2 exit 1 fi @@ -107,11 +108,11 @@ BASE_QUERY="componentKeys=${COMPONENT_KEY}&branch=${BRANCH}&resolved=false" # both narrows the result set and, when it's small enough, sidesteps # the deep-pagination splitting logic below entirely. FILTERED=0 -if [ -n "${SONAR_SEVERITIES}" ]; then +if [[ -n "${SONAR_SEVERITIES}" ]]; then BASE_QUERY="${BASE_QUERY}&severities=${SONAR_SEVERITIES}" FILTERED=1 fi -if [ -n "${SONAR_TYPES}" ]; then +if [[ -n "${SONAR_TYPES}" ]]; then BASE_QUERY="${BASE_QUERY}&types=${SONAR_TYPES}" FILTERED=1 fi @@ -127,7 +128,7 @@ function api_get { sleep ${REQUEST_DELAY} response=$(curl -s -f -u "${SONAR_TOKEN}:" "${url}") - if [ $? -ne 0 ]; then + if [[ $? -ne 0 ]]; then echo "ERROR: $(basename $0) -> request failed: ${url}" >&2 curl -s -u "${SONAR_TOKEN}:" "${url}" >&2 return 1 @@ -156,7 +157,7 @@ function fetch_all_pages { while :; do response=$(api_get "${extra}&ps=${PAGE_SIZE}&p=${page}") || return 1 - if [ ${total} -eq -1 ]; then + if [[ ${total} -eq -1 ]]; then total=$(echo "${response}" | jq '.total') fi @@ -166,7 +167,7 @@ function fetch_all_pages { fetched=$(( fetched + n )) echo " [${label}] page ${page}: ${n} issue(s), ${fetched}/${total}" - if [ ${n} -eq 0 ] || [ ${fetched} -ge ${total} ]; then + if [[ ${n} -eq 0 || ${fetched} -ge ${total} ]]; then break fi page=$(( page + 1 )) @@ -184,11 +185,11 @@ function fetch_partition { total=$(get_total "${extra}") || return 1 - if [ "${total}" -eq 0 ]; then + if [[ "${total}" -eq 0 ]]; then return 0 fi - if [ "${total}" -le ${MAX_RESULT_WINDOW} ]; then + if [[ "${total}" -le ${MAX_RESULT_WINDOW} ]]; then echo "[${label}] ${total} issue(s)" fetch_all_pages "${extra}" "${label}" return $? @@ -211,11 +212,15 @@ function fetch_partition { echo "WARNING: [${label}] has ${total} issue(s), still exceeding the ${MAX_RESULT_WINDOW} API limit after splitting by severity and type. Only the first ${MAX_RESULT_WINDOW} will be fetched -- some findings will be MISSING from the output." >&2 fetch_all_pages "${extra}" "${label}" ;; + *) + echo "ERROR: $(basename $0) -> fetch_partition() called with unknown split_dim: '${split_dim}'" >&2 + return 1 + ;; esac } echo "Fetching SonarQube findings for component '${COMPONENT_KEY}' branch '${BRANCH}' from ${SONAR_HOST_URL}" -if [ ${FILTERED} -eq 1 ]; then +if [[ ${FILTERED} -eq 1 ]]; then echo "Filter: severities=[${SONAR_SEVERITIES:-all}] types=[${SONAR_TYPES:-all}]" fi @@ -224,13 +229,13 @@ fi # already baked into BASE_QUERY, and appending another severities= or # types= param on top of it would conflict. Just warn if it's still too # big rather than fetching everything to find a further split. -if [ ${FILTERED} -eq 1 ]; then +if [[ ${FILTERED} -eq 1 ]]; then fetch_partition "" "all" "none" else fetch_partition "" "all" "severity" fi STATUS=$? -if [ ${STATUS} -ne 0 ]; then +if [[ ${STATUS} -ne 0 ]]; then rm -f ${RAW_FILE} exit ${STATUS} fi