[gcc(refs/users/aoliva/heads/testme)] [libstdc++] encode __time_get_state in tm

Alexandre Oliva aoliva@gcc.gnu.org
Fri Feb 17 05:00:35 GMT 2023


https://gcc.gnu.org/g:674ed3d302857f2ae3c669c1d36877bfde602953

commit 674ed3d302857f2ae3c669c1d36877bfde602953
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Feb 17 01:59:08 2023 -0300

    [libstdc++] encode __time_get_state in tm
    
    On platforms that fail the ptrtomemfn-cast-to-pfn hack, such as
    arm-*-vxworks*, time_get fails with %I and %p because the state is not
    preserved across do_get calls.
    
    This patch introduces an alternate hack, that encodes the state in
    unused bits of struct tm before calling do_get, extracts them in
    do_get, does the processing, and encodes it back, so that get extracts
    it.
    
    The finalizer is adjusted for idempotence, because both do_get and get
    may call it.
    
    
    for  libstdc++-v3/ChangeLog
    
            * include/bits/locale_facets_nonio.h (__time_get_state): Add
            _M_state_tm, _M_save_to and _M_restore_from.
            * include/bits/locale_facets_nonio.tcc (time_get::get): Drop
            do_get-overriding hack.  Use state unconditionally, and encode
            it in tm around do_get.
            (time_get::do_get): Extract state from tm, and encode it back,
            around parsing and finalizing.
            * src/c++98/locale_facets.cc
            (__time_get_state::_M_finalize_state): Make tm_hour and
            tm_year idempotent.
    
    
    Change-Id: I6fff27a23fb3da7921756069957d5672d25e6a6f
    TN: W127-053

Diff:
---
 libstdc++-v3/include/bits/locale_facets_nonio.h   | 80 +++++++++++++++++++++++
 libstdc++-v3/include/bits/locale_facets_nonio.tcc | 43 ++----------
 libstdc++-v3/src/c++98/locale_facets.cc           |  8 ++-
 3 files changed, 93 insertions(+), 38 deletions(-)

diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.h b/libstdc++-v3/include/bits/locale_facets_nonio.h
index 372cf042950..711bede1584 100644
--- a/libstdc++-v3/include/bits/locale_facets_nonio.h
+++ b/libstdc++-v3/include/bits/locale_facets_nonio.h
@@ -361,6 +361,86 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     void
     _M_finalize_state(tm* __tm);
 
+  private:
+    void
+    _M_state_tm(tm* __tm, bool __totm)
+    {
+      // Check we don't invade the in-range tm bits, even if int is
+      // 16-bits wide.
+#define _M_min_shift_tm_sec 6
+#define _M_min_shift_tm_min 6
+#define _M_min_shift_tm_hour 5
+#define _M_min_shift_tm_mday 5
+#define _M_min_shift_tm_mon 4
+#define _M_min_shift_tm_year 16 // 14, but signed, so avoid it.
+#define _M_min_shift_tm_wday 3
+#define _M_min_shift_tm_yday 9
+#define _M_min_shift_tm_isdst 1
+      // Represent __STF in __WDT bits of __TMF up to the __MSB bit.
+      // In __MSB, 0 stands for the most significant bit of __TMF,
+      // 1 the bit next to it, and so on.
+#define _M_time_get_state_bitfield_inout(__tmf, __msb, __wdt, __stf)	\
+  do									\
+  {									\
+    const unsigned __shift = (sizeof (__tm->__tmf) * __CHAR_BIT__	\
+			      - (__msb) - (__wdt));			\
+    static char __attribute__ ((__unused__))				\
+      __check_parms_##__tmf[(__msb) >= 0 && (__wdt) > 0			\
+			    && __shift >= (_M_min_shift_##__tmf		\
+					   + (sizeof (__tm->__tmf)	\
+					      * __CHAR_BIT__) - 16)	\
+			    ? 1 : -1];					\
+    const unsigned __mask = ((1 << (__wdt)) - 1) << __shift;		\
+    if (!__totm)							\
+      this->__stf = (__tm->__tmf & __mask) >> __shift;			\
+    __tm->__tmf &= ~__mask;						\
+    if (__totm)								\
+      __tm->__tmf |= ((unsigned)this->__stf << __shift) & __mask;	\
+    }									\
+  while (0)
+
+      _M_time_get_state_bitfield_inout (tm_hour,  0, 1, _M_have_I);
+      _M_time_get_state_bitfield_inout (tm_wday,  0, 1, _M_have_wday);
+      _M_time_get_state_bitfield_inout (tm_yday,  0, 1, _M_have_yday);
+      _M_time_get_state_bitfield_inout (tm_mon,   0, 1, _M_have_mon);
+      _M_time_get_state_bitfield_inout (tm_mday,  0, 1, _M_have_mday);
+      _M_time_get_state_bitfield_inout (tm_yday,  1, 1, _M_have_uweek);
+      _M_time_get_state_bitfield_inout (tm_yday,  2, 1, _M_have_wweek);
+      _M_time_get_state_bitfield_inout (tm_isdst, 0, 1, _M_have_century);
+      _M_time_get_state_bitfield_inout (tm_hour,  1, 1, _M_is_pm);
+      _M_time_get_state_bitfield_inout (tm_isdst, 1, 1, _M_want_century);
+      _M_time_get_state_bitfield_inout (tm_yday,  3, 1, _M_want_xday);
+      // _M_pad1
+      _M_time_get_state_bitfield_inout (tm_wday,  1, 6, _M_week_no);
+      // _M_pad2
+      _M_time_get_state_bitfield_inout (tm_mon,   1, 8, _M_century);
+      // _M_pad3
+
+#undef _M_min_shift_tm_hour
+#undef _M_min_shift_tm_sec
+#undef _M_min_shift_tm_min
+#undef _M_min_shift_tm_hour
+#undef _M_min_shift_tm_mday
+#undef _M_min_shift_tm_mon
+#undef _M_min_shift_tm_year
+#undef _M_min_shift_tm_wday
+#undef _M_min_shift_tm_yday
+#undef _M_min_shift_tm_isdst
+#undef _M_time_get_state_bitfield_inout
+    }
+  public:
+    // Encode *THIS into scratch bits of __TM.
+    void
+    _M_save_to(tm* __tm) {
+      _M_state_tm (__tm, true);
+    }
+
+    // Decode and zero out scratch bits of __TM back into *THIS.
+    void
+    _M_restore_from(tm* __tm) {
+      _M_state_tm (__tm, false);
+    }
+
     unsigned int _M_have_I : 1;
     unsigned int _M_have_wday : 1;
     unsigned int _M_have_yday : 1;
diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.tcc b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
index b19f4425300..47d243ce84d 100644
--- a/libstdc++-v3/include/bits/locale_facets_nonio.tcc
+++ b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
@@ -1464,21 +1464,8 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
       const locale& __loc = __io._M_getloc();
       ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);
       __err = ios_base::goodbit;
-      bool __use_state = false;
-#if __GNUC__ >= 5 && !defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpmf-conversions"
-      // Nasty hack.  The C++ standard mandates that get invokes the do_get
-      // virtual method, but unfortunately at least without an ABI change
-      // for the facets we can't keep state across the different do_get
-      // calls.  So e.g. if __fmt is "%p %I:%M:%S", we can't handle it
-      // properly, because we first handle the %p am/pm specifier and only
-      // later the 12-hour format specifier.
-      if ((void*)(this->*(&time_get::do_get)) == (void*)(&time_get::do_get))
-	__use_state = true;
-#pragma GCC diagnostic pop
-#endif
       __time_get_state __state = __time_get_state();
+      __state._M_save_to (__tm);
       while (__fmt != __fmtend &&
              __err == ios_base::goodbit)
         {
@@ -1510,26 +1497,8 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
                   __err = ios_base::failbit;
                   break;
                 }
-	      if (__use_state)
-		{
-		  char_type __new_fmt[4];
-		  __new_fmt[0] = __fmt_start[0];
-		  __new_fmt[1] = __fmt_start[1];
-		  if (__mod)
-		    {
-		      __new_fmt[2] = __fmt_start[2];
-		      __new_fmt[3] = char_type();
-		    }
-		  else
-		    __new_fmt[2] = char_type();
-		  __s = _M_extract_via_format(__s, __end, __io, __err, __tm,
-					      __new_fmt, __state);
-		  if (__s == __end)
-		    __err |= ios_base::eofbit;
-		}
-	      else
-		__s = this->do_get(__s, __end, __io, __err, __tm, __format,
-				   __mod);
+	      __s = this->do_get(__s, __end, __io, __err, __tm,
+				 __format, __mod);
               ++__fmt;
             }
           else if (__ctype.is(ctype_base::space, *__fmt))
@@ -1556,8 +1525,8 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
               break;
             }
         }
-      if (__use_state)
-	__state._M_finalize_state(__tm);
+      __state._M_restore_from (__tm);
+      __state._M_finalize_state(__tm);
       return __s;
     }
 
@@ -1588,9 +1557,11 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
         }
 
       __time_get_state __state = __time_get_state();
+      __state._M_restore_from (__tm);
       __beg = _M_extract_via_format(__beg, __end, __io, __err, __tm, __fmt,
 				    __state);
       __state._M_finalize_state(__tm);
+      __state._M_save_to (__tm);
       if (__beg == __end)
 	__err |= ios_base::eofbit;
       return __beg;
diff --git a/libstdc++-v3/src/c++98/locale_facets.cc b/libstdc++-v3/src/c++98/locale_facets.cc
index c0bb7fd181d..28c34b5f379 100644
--- a/libstdc++-v3/src/c++98/locale_facets.cc
+++ b/libstdc++-v3/src/c++98/locale_facets.cc
@@ -181,12 +181,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __time_get_state::
   _M_finalize_state(tm* tm)
   {
-    if (_M_have_I && _M_is_pm)
+    if (_M_have_I && _M_is_pm && (unsigned) tm->tm_hour < 12)
       tm->tm_hour += 12;
     if (_M_have_century)
       {
 	if (_M_want_century)
-	  tm->tm_year = tm->tm_year % 100;
+	  {
+	    tm->tm_year = tm->tm_year % 100;
+	    if (tm->tm_year < 0)
+	      tm->tm_year += 100;
+	  }
 	else
 	  tm->tm_year = 0;
 	tm->tm_year += (_M_century - 19) * 100;


More information about the Libstdc++-cvs mailing list