This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[libstdc++, 3.3?] bitset fixes


Gennaro Prota pointed out a couple of problems in all SGI-derived
implementations of bitset.

1)  Extracting bits from an input stream can fail to do any actual
    work if we (for example) reach EOF on the stream before extracting
    the expected number of bits.  Users could have set an exception mask
    on the stream, so when we do something like

    - loop { extract bits into a holding area }
    - hit EOF unexpectedly
    - set EOF flag in stream
    - store bits from holding area into bitset

    an exception could be thrown at step 3.  Easily fixed.

2)  Shifting more bits than the size of the bitset causes a crash.
    It would be truly pedantic to argue "do as the ints do" in this case,
    since

    a) ints don't segfault when you shift them, and
    b) the standard doesn't leave much wiggle room

    The extra checking should be minimal overhead, thanks to the wonders
    of __builtin_expect.  Namely, we expect a shift size to be >0 and <N
    but we don't walk all over memory when it isn't.


Tested on i686/linux.  No additional regressions.  Applied to trunk.
I will move this to the 3.3 branch tomorrow, after additional testing and
a waiting period for comments.


2002-12-26  Phil Edwards  <pme@gcc.gnu.org>

	* include/std/std_bitset.h (_Base_bitset::_M_do_left_shift,
	_Base_bitset::_M_do_right_shift): Expect a non-zero shift.
	(bitset::operator<<=, bitset::operator>>=):  When shifting more bits
	than are in the bitset, zero memory rather than segfault.
	(operator>>(basic_istream,bitset):  Only call setstate once, after
	all work has been done.

	* testsuite/23_containers/bitset_members.cc (test03):  New test.
	* testsuite/23_containers/bitset_shift.cc (test02):  New test.


Index: include/std/std_bitset.h
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/include/std/std_bitset.h,v
retrieving revision 1.9
diff -u -3 -p -r1.9 std_bitset.h
--- include/std/std_bitset.h	16 Dec 2002 18:22:59 -0000	1.9
+++ include/std/std_bitset.h	26 Dec 2002 23:48:07 -0000
@@ -219,7 +219,7 @@ namespace std
     void
     _Base_bitset<_Nw>::_M_do_left_shift(size_t __shift)
     {
-      if (__shift != 0)
+      if (__builtin_expect(__shift != 0, 1))
 	{
 	  const size_t __wshift = __shift / _GLIBCPP_BITSET_BITS_PER_WORD;
 	  const size_t __offset = __shift % _GLIBCPP_BITSET_BITS_PER_WORD;
@@ -244,7 +244,7 @@ namespace std
     void
     _Base_bitset<_Nw>::_M_do_right_shift(size_t __shift)
     {
-      if (__shift != 0)
+      if (__builtin_expect(__shift != 0, 1))
 	{
 	  const size_t __wshift = __shift / _GLIBCPP_BITSET_BITS_PER_WORD;
 	  const size_t __offset = __shift % _GLIBCPP_BITSET_BITS_PER_WORD;
@@ -581,9 +581,11 @@ namespace std
    *  The template argument, @a _Nb, may be any non-negative number of type
    *  size_t.
    *
-   *  A %bitset of size N has N % (sizeof(unsigned long) * CHAR_BIT) unused
-   *  bits.  (They are the high-order bits in the highest word.)  It is
-   *  a class invariant that those unused bits are always zero.
+   *  A %bitset of size N uses U bits, where
+   *  U = (N % (sizeof(unsigned long) * CHAR_BIT)).
+   *  Thus, N - U bits are unused.  (They are the high-order bits in the
+   *  highest word.)  It is a class invariant that those unused bits are
+   *  always zero.
    *
    *  If you think of %bitset as "a simple array of bits," be aware that
    *  your mental picture is reversed:  a %bitset behaves the same way as
@@ -805,16 +807,26 @@ namespace std
     bitset<_Nb>&
     operator<<=(size_t __pos)
     {
-      this->_M_do_left_shift(__pos);
-      this->_M_do_sanitize();
+      if (__builtin_expect(__pos < _Nb, 1))
+        {
+          this->_M_do_left_shift(__pos);
+          this->_M_do_sanitize();
+        }
+      else
+	this->_M_do_reset();
       return *this;
     }
 
     bitset<_Nb>&
     operator>>=(size_t __pos)
     {
-      this->_M_do_right_shift(__pos);
-      this->_M_do_sanitize();
+      if (__builtin_expect(__pos < _Nb, 1))
+        {
+          this->_M_do_right_shift(__pos);
+          this->_M_do_sanitize();
+        }
+      else
+	this->_M_do_reset();
       return *this;
     }
     //@}
@@ -1183,6 +1195,7 @@ namespace std
       typename basic_istream<_CharT, _Traits>::sentry __sentry(__is);
       if (__sentry)
 	{
+	  ios_base::iostate  __state = ios_base::goodbit;
 	  basic_streambuf<_CharT, _Traits>* __buf = __is.rdbuf();
 	  for (size_t __i = 0; __i < _Nb; ++__i)
 	    {
@@ -1191,7 +1204,7 @@ namespace std
 	      typename _Traits::int_type __c1 = __buf->sbumpc();
 	      if (_Traits::eq_int_type(__c1, __eof))
 		{
-		  __is.setstate(ios_base::eofbit);
+		  __state |= ios_base::eofbit;
 		  break;
 		}
 	      else
@@ -1201,19 +1214,21 @@ namespace std
 
 		  if (__c == '0' || __c == '1')
 		    __tmp.push_back(__c);
-		  else if (_Traits::eq_int_type(__buf->sputbackc(__c2),
-						__eof))
+		  else if (_Traits::eq_int_type(__buf->sputbackc(__c2), __eof))
 		    {
-		      __is.setstate(ios_base::failbit);
+		      __state |= ios_base::failbit;
 		      break;
 		    }
 		}
 	    }
 
 	  if (__tmp.empty() && !_Nb)
-	    __is.setstate(ios_base::failbit);
+	    __state |= ios_base::failbit;
 	  else
 	    __x._M_copy_from_string(__tmp, static_cast<size_t>(0), _Nb);
+
+	  if (__state != ios_base::goodbit)
+	    __is.setstate(__state);    // may throw an exception
 	}
 
       return __is;
Index: testsuite/23_containers/bitset_members.cc
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/testsuite/23_containers/bitset_members.cc,v
retrieving revision 1.5
diff -u -3 -p -r1.5 bitset_members.cc
--- testsuite/23_containers/bitset_members.cc	9 Apr 2002 07:35:31 -0000	1.5
+++ testsuite/23_containers/bitset_members.cc	26 Dec 2002 23:48:07 -0000
@@ -22,6 +22,7 @@
 
 #include <bitset>
 #include <stdexcept>
+#include <sstream>
 #include <testsuite_hooks.h>
 
 void 
@@ -52,9 +53,26 @@ void test02()
   bs.count();
 }
 
+void test03()
+{
+  std::bitset<5>      b;
+  std::stringstream   ss ("101");
+
+  ss.exceptions(std::ios_base::eofbit);
+
+  try
+  {
+    ss >> b;
+  }
+  catch (std::exception&) {}
+
+  VERIFY( b.to_ulong() == 5 );
+}
+
 int main()
 {
   test01();
   test02();
+  test03();
   return 0;
 }
Index: testsuite/23_containers/bitset_shift.cc
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/testsuite/23_containers/bitset_shift.cc,v
retrieving revision 1.3
diff -u -3 -p -r1.3 bitset_shift.cc
--- testsuite/23_containers/bitset_shift.cc	7 Aug 2001 03:38:30 -0000	1.3
+++ testsuite/23_containers/bitset_shift.cc	26 Dec 2002 23:48:07 -0000
@@ -106,9 +106,25 @@ test01() {
   return test;
 }
 
+bool
+test02()
+{
+  bool test = true;
+
+  std::bitset<66>  b;
+  b <<= 400;
+  VERIFY( b.count() == 0 );
+
+#ifdef DEBUG_ASSERT
+  assert(test);
+#endif
+  return test;
+}
+
 int
 main() {
   test01();
+  test02();
 
   return 0;
 }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]