This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
[libstdc++, 3.3?] bitset fixes
- From: Phil Edwards <phil at jaj dot com>
- To: libstdc++ at gcc dot gnu dot org, gcc-patches at gcc dot gnu dot org
- Date: Thu, 26 Dec 2002 19:02:56 -0500
- Subject: [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;
}