diff --git a/src/utils/EntryModel.cxxtest b/src/utils/EntryModel.cxxtest index 033853e46..48b5ad03e 100644 --- a/src/utils/EntryModel.cxxtest +++ b/src/utils/EntryModel.cxxtest @@ -166,16 +166,19 @@ TEST(EntryModelTest, InitValuePopBack) // unsigned 0 value with leading zeros uem.init(4, 10, 0); - EXPECT_EQ(4U, em.max_size()); + EXPECT_EQ(4U, uem.max_size()); uem.push_back_char('0'); + EXPECT_EQ(0U, uem.get_value()); EXPECT_EQ("0", uem.get_string()); EXPECT_EQ(" 0", uem.get_string(true)); EXPECT_FALSE(uem.has_leading_zeros()); uem.push_back(0); + EXPECT_EQ(0U, uem.get_value()); EXPECT_EQ("00", uem.get_string()); EXPECT_EQ(" 00", uem.get_string(true)); EXPECT_TRUE(uem.has_leading_zeros()); uem.pop_back(); + EXPECT_EQ(0U, uem.get_value()); EXPECT_EQ("0", uem.get_string()); EXPECT_EQ(" 0", uem.get_string(true)); EXPECT_FALSE(uem.has_leading_zeros()); @@ -183,6 +186,75 @@ TEST(EntryModelTest, InitValuePopBack) EXPECT_EQ("", uem.get_string()); EXPECT_EQ(" ", uem.get_string(true)); EXPECT_FALSE(uem.has_leading_zeros()); + EXPECT_TRUE(uem.empty()); + + // unsigned 0 value with leading zeros, non-zero result + uem.init(4, 10, 0); + + EXPECT_EQ(4U, uem.max_size()); + uem.push_back_char('0'); + EXPECT_EQ(0U, uem.get_value()); + EXPECT_EQ("0", uem.get_string()); + EXPECT_EQ(" 0", uem.get_string(true)); + EXPECT_FALSE(uem.has_leading_zeros()); + uem.push_back(0); + EXPECT_EQ(0U, uem.get_value()); + EXPECT_EQ("00", uem.get_string()); + EXPECT_EQ(" 00", uem.get_string(true)); + EXPECT_TRUE(uem.has_leading_zeros()); + uem.push_back(5); + EXPECT_EQ(5U, uem.get_value()); + EXPECT_EQ("005", uem.get_string()); + EXPECT_EQ(" 005", uem.get_string(true)); + EXPECT_TRUE(uem.has_leading_zeros()); + uem.pop_back(); + EXPECT_EQ(0U, uem.get_value()); + EXPECT_EQ("00", uem.get_string()); + EXPECT_EQ(" 00", uem.get_string(true)); + EXPECT_TRUE(uem.has_leading_zeros()); + uem.pop_back(); + EXPECT_EQ(0U, uem.get_value()); + EXPECT_EQ("0", uem.get_string()); + EXPECT_EQ(" 0", uem.get_string(true)); + EXPECT_FALSE(uem.has_leading_zeros()); +} + +TEST(EntryModelTest, SetBaseConvert) +{ + EntryModel em; + EntryModel uem; + + // signed + em.init(4, 10, -43); + EXPECT_EQ(-43, em.get_value()); + EXPECT_EQ("-43", em.get_string()); + EXPECT_EQ(" -43", em.get_string(true)); + + em.set_base(16, true); + EXPECT_EQ(-0x43, em.get_value()); + EXPECT_EQ("-43", em.get_string()); + EXPECT_EQ(" -43", em.get_string(true)); + + em.set_base(10, true); + EXPECT_EQ(-43, em.get_value()); + EXPECT_EQ("-43", em.get_string()); + EXPECT_EQ(" -43", em.get_string(true)); + + // unsigned + uem.init(4, 10, 34); + EXPECT_EQ(34U, uem.get_value()); + EXPECT_EQ("34", uem.get_string()); + EXPECT_EQ(" 34", uem.get_string(true)); + + uem.set_base(16, true); + EXPECT_EQ(0x34U, uem.get_value()); + EXPECT_EQ("34", uem.get_string()); + EXPECT_EQ(" 34", uem.get_string(true)); + + uem.set_base(10, true); + EXPECT_EQ(34U, uem.get_value()); + EXPECT_EQ("34", uem.get_string()); + EXPECT_EQ(" 34", uem.get_string(true)); } TEST(EntryModelTest, PushBackAndAppend) @@ -501,3 +573,50 @@ TEST(EntryModelBoundedTest, SetMinMax) EXPECT_FALSE(em.is_at_initial_value()); EXPECT_FALSE(em.empty()); } + +TEST(EntryModelBoundedTest, LeadingZerosNonZeroMin) +{ + EntryModelBounded uem; + + // unsigned 0 value with leading zeros + uem.init(4, 10, 5, 5, 9999, 5); + uem.clear(); + + EXPECT_EQ(4U, uem.max_size()); + uem.push_back_char('0'); + EXPECT_EQ("0", uem.get_string()); + EXPECT_EQ(" 0", uem.get_string(true)); + EXPECT_EQ(1U, uem.size()); + EXPECT_FALSE(uem.has_leading_zeros()); + uem.push_back(0); + EXPECT_EQ("00", uem.get_string()); + EXPECT_EQ(" 00", uem.get_string(true)); + EXPECT_TRUE(uem.has_leading_zeros()); + uem.push_back(0); + EXPECT_EQ("000", uem.get_string()); + EXPECT_EQ(" 000", uem.get_string(true)); + EXPECT_TRUE(uem.has_leading_zeros()); + uem.push_back(0); + EXPECT_EQ("0005", uem.get_string()); + EXPECT_EQ("0005", uem.get_string(true)); + EXPECT_TRUE(uem.has_leading_zeros()); + + uem.pop_back(); + EXPECT_EQ("000", uem.get_string()); + EXPECT_EQ(" 000", uem.get_string(true)); + EXPECT_TRUE(uem.has_leading_zeros()); + uem.pop_back(); + EXPECT_EQ("00", uem.get_string()); + EXPECT_EQ(" 00", uem.get_string(true)); + EXPECT_TRUE(uem.has_leading_zeros()); + uem.pop_back(); + EXPECT_EQ("0", uem.get_string()); + EXPECT_EQ(" 0", uem.get_string(true)); + EXPECT_FALSE(uem.empty()); + EXPECT_FALSE(uem.has_leading_zeros()); + uem.pop_back(); + EXPECT_EQ("", uem.get_string()); + EXPECT_EQ(" ", uem.get_string(true)); + EXPECT_TRUE(uem.empty()); + EXPECT_FALSE(uem.has_leading_zeros()); +} diff --git a/src/utils/EntryModel.hxx b/src/utils/EntryModel.hxx index 1c873a937..ece89ca40 100644 --- a/src/utils/EntryModel.hxx +++ b/src/utils/EntryModel.hxx @@ -79,8 +79,7 @@ public: { maxSize_ = max_size; clear(); - set_base(base); - set_boundaries(); + set_base(base); // this will call set_boundaries() } /// Initialize with a value. @@ -111,6 +110,13 @@ public: // clear entry before inserting character clear(); } + if (value_ == 0 && val != 0 && size_) + { + // This is a special case where we transition from a user having + // entered all 0 (as a first digits) and then enters a non-zero + // (as a next digit). + ++numLeadingZeros_; + } value_ *= base_; if (value_ < 0) { @@ -139,13 +145,10 @@ public: switch (base_) { case 10: - HASSERT(c >= '0' && c <= '9'); push_back(c - '0'); break; case 16: c = toupper(c); - HASSERT((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'F')); push_back(c <= '9' ? c - '0' : c - 'A' + 10); break; } @@ -172,14 +175,11 @@ public: /// Removes (deletes) a character off the end. void pop_back() { + value_ /= base_; if (value_ == 0 && numLeadingZeros_) { --numLeadingZeros_; } - else - { - value_ /= base_; - } if (size_) { --size_; @@ -205,6 +205,30 @@ public: { HASSERT(base == 10 || base == 16); base_ = base; + set_boundaries(); + } + + /// Set the radix base. + /// @param base new radix base to set. + /// @param convert convert the current value, as a string, to the new base. + void set_base(int base, bool convert) + { + if (base != base_) + { + if (convert) + { + string str = get_string(); + if (std::is_signed::value) + { + value_ = strtoll(str.c_str(), nullptr, base); + } + else + { + value_ = strtoull(str.c_str(), nullptr, base); + } + } + set_base(base); + } } /// Set the value, keep the max number of digits and base the same. @@ -314,14 +338,10 @@ public: break; } } - // Assert that we do not have more leading zeros than space allows. - // The logic of push_back should never allow it. - HASSERT(numLeadingZeros_ == 0 || - (str.size() + numLeadingZeros_) <= maxSize_); if (numLeadingZeros_) { - str.insert(0, numLeadingZeros_, '0'); + str.insert(value_ < 0 ? 1 : 0, numLeadingZeros_, '0'); } if (right_justify && str.size() < maxSize_) { @@ -357,14 +377,21 @@ public: clamp(); } - /// Clamp the value at the min or max. + /// Clamp the value at the min or max. Clamping will not occur if the value + /// is zero and there is space for more leading zeros. /// @param force Normally, clamping doesn't occur if the entry is "empty". /// However, if force is set to true, we will clamp anyways. + /// force also applies if the value is zero yet there is space + /// for more leading zeros. virtual void clamp(bool force = false) { + if (value_ == 0 && size_ < maxSize_ && !force) + { + // skip clamping if we have space for more leading zeros + return; + } if (force || !empty_) { - // purposely do not reset the numLeadingZeros_ empty_ = false; if (value_ < valueMin_) { @@ -424,11 +451,14 @@ protected: void calculate_size() { // calculate new size_ - size_ = value_ < 0 ? numLeadingZeros_ + 1 : numLeadingZeros_; + size_ = value_ < 0 ? 1 : 0; for (T tmp = value_ < 0 ? -value_ : value_; tmp != 0; tmp /= base_) { ++size_; } + numLeadingZeros_ = std::min(static_cast(numLeadingZeros_), + static_cast(maxSize_ - size_)); + size_ += numLeadingZeros_; } T value_; ///< present value held @@ -481,7 +511,8 @@ public: } private: - /// Clamp the value at the min or max. + /// Clamp the value at the min or max. Clamping will not occur if the value + /// is zero and there is space for more leading zeros. /// @param force Normally, clamping doesn't occur if the entry is "empty". /// However, if force is set to true, we will clamp anyways. void clamp(bool force = false) override