From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from EUR04-DB3-obe.outbound.protection.outlook.com (mail-eopbgr60055.outbound.protection.outlook.com [40.107.6.55]) by sourceware.org (Postfix) with ESMTPS id 74EA33858C53; Thu, 14 Apr 2022 12:37:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 74EA33858C53 Received: from AM5PR0402CA0024.eurprd04.prod.outlook.com (2603:10a6:203:90::34) by AM8PR08MB5857.eurprd08.prod.outlook.com (2603:10a6:20b:1d2::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5144.29; Thu, 14 Apr 2022 12:37:34 +0000 Received: from VE1EUR03FT023.eop-EUR03.prod.protection.outlook.com (2603:10a6:203:90:cafe::2b) by AM5PR0402CA0024.outlook.office365.com (2603:10a6:203:90::34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5164.20 via Frontend Transport; Thu, 14 Apr 2022 12:37:34 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 63.35.35.123) smtp.mailfrom=arm.com; dkim=pass (signature was verified) header.d=armh.onmicrosoft.com;dmarc=pass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 63.35.35.123 as permitted sender) receiver=protection.outlook.com; client-ip=63.35.35.123; helo=64aa7808-outbound-1.mta.getcheckrecipient.com; Received: from 64aa7808-outbound-1.mta.getcheckrecipient.com (63.35.35.123) by VE1EUR03FT023.mail.protection.outlook.com (10.152.18.133) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5164.19 via Frontend Transport; Thu, 14 Apr 2022 12:37:34 +0000 Received: ("Tessian outbound ab7864ef57f2:v118"); Thu, 14 Apr 2022 12:37:33 +0000 X-CR-MTA-TID: 64aa7808 Received: from 84caf3361c4a.1 by 64aa7808-outbound-1.mta.getcheckrecipient.com id 6C618149-5740-41A0-92DF-D28471F5D869.1; Thu, 14 Apr 2022 12:37:28 +0000 Received: from EUR05-VI1-obe.outbound.protection.outlook.com by 64aa7808-outbound-1.mta.getcheckrecipient.com with ESMTPS id 84caf3361c4a.1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384); Thu, 14 Apr 2022 12:37:28 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=SL7ykTw1ELgUU2F5lx3yhK1sR/xllPLHafpwTrbMUsig4JPkozpZ4KGrb4C2ynLbeVrpx/0D1kDQF1G+UmlEBy8P2/MvagKykPtKabAIgUWIrdCwbSLHqDbh7k7M2B7psTCaeCU9H1RphXRYY6qaOBB1emL3DFMlz2L+oIxljCWFTcmFJ7s+JjUIAh9fbrDWpUiZalwKtRF0dBE8V6Vep/Hgid/PqhJjsjOrHXb1x/uVWysJ816PvWR1uRXgG8cWnYyJ8bA1LPFx+jVvGcUgM2iHy2cVSQE9XQbK/9F8jj9kp/DTmVRzgWpYhH0mHk1BqcAp+fzcYUzUr2Qcr0QQpw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=oYAFqVs+4z/UYtUv840fhMbfLgvKXVSMSj1iXCz8gmY=; b=QbokX82PZjlzplRZdAKhYyDS7qCyVweEaW/kJBFtoaVYwTqFyvD4G4b+HIRImrla2lkD9r4qmLjqzRm9+6+VW3jaQQghjbi7kieZ+9BfC+O4zgtP7u28dSe0ctW/nOB1d6zPK5yEKyUvBrMdilXTilB2l4v7C8ILco3AotwVuPx0TwCVRi+QQX4L4nbsHR+jJhkHcZcZmUo/yWB8685yXvpDGOnv4HrOJwPozSKBzmboRbaHbqFCzY3zOu/kpOK5Z4lhlQ2YDDR+lg0mhhnwuUWy02cQ9Abc8YU6ruHpsAhcK1V/lV9edBwtIcPCOZLVS3wmUtXtgiaeDR6WnmtjIg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=arm.com; dmarc=pass action=none header.from=arm.com; dkim=pass header.d=arm.com; arc=none Received: from PAXPR08MB6926.eurprd08.prod.outlook.com (2603:10a6:102:138::24) by DB9PR08MB7022.eurprd08.prod.outlook.com (2603:10a6:10:2c9::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5144.29; Thu, 14 Apr 2022 12:37:26 +0000 Received: from PAXPR08MB6926.eurprd08.prod.outlook.com ([fe80::2591:4a0f:a6ea:200]) by PAXPR08MB6926.eurprd08.prod.outlook.com ([fe80::2591:4a0f:a6ea:200%5]) with mapi id 15.20.5144.029; Thu, 14 Apr 2022 12:37:26 +0000 From: Kyrylo Tkachov To: Patrick Palka , "gcc-patches@gcc.gnu.org" CC: "libstdc++@gcc.gnu.org" Subject: RE: [PATCH] libstdc++: Optimize integer std::from_chars Thread-Topic: [PATCH] libstdc++: Optimize integer std::from_chars Thread-Index: AQHYT/u0XVd8K7l3CUqjmX7WqKENiqzvWKWg Date: Thu, 14 Apr 2022 12:37:25 +0000 Message-ID: References: <20220414123113.175965-1-ppalka@redhat.com> In-Reply-To: <20220414123113.175965-1-ppalka@redhat.com> Accept-Language: en-GB, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ts-tracking-id: 3A42B8CD08FB714D8A65DF65206FC76D.0 x-checkrecipientchecked: true Authentication-Results-Original: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com; X-MS-Office365-Filtering-Correlation-Id: 5329b107-e079-471f-e57c-08da1e138cd6 x-ms-traffictypediagnostic: DB9PR08MB7022:EE_|VE1EUR03FT023:EE_|AM8PR08MB5857:EE_ X-Microsoft-Antispam-PRVS: x-checkrecipientrouted: true nodisclaimer: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam-Untrusted: BCL:0; X-Microsoft-Antispam-Message-Info-Original: BRtZ5jCLbfVMeuh3eEgTFfS2AMKsDHHMPIP9S399GWoc4BTG7DFJv2VF2k48uKLefoquTomFuKQ4zqkE9Ib+5MYi4ufT1YRvsXLCmPdBF/btU/vY5E9YWLq0wr3oCECnjGK9cXWXDlI23xwV2L29UMFj2D9PBWBs0MfwUeZqrOIrjD/fLOsyfOsU61JHVXkCDSZnzNXX00tilZKpVnodY5FObSmSnPGEmVXTE+9J+OWa8c4Qkik3J/BjtQuFgczxgSpPys0fqbv5047jjUCw/9I7Eu9xWsXypOk6scMfCnzSiPtTCVxu8ldpWO16RJUDxsHPYdZp3M3OLSkp7pluLTIv58dJz87EJBx9uxYrErvtPD/7OQLwURj+2jJj1DIyT8LsvrzDXpyOAc5p+prGxYKLfIH4BkQTX+r0ZqdIc/kUZpgax1Hqxn15qZpf1nGMXL517zGlsIYK3RWEyo5fiv9YdhHE8e0EBcHwwkXy2JbdjfJBx8agXUWWyYKs+hmnLhumrsK816u33nNgjH9wKkmLzAceE66W0O8t1rKgT+559Rg6SWRlEQUw0kv/awCIFrfcjZG01cf6U4noT6ad5xbQyCtms49CCCXA1XlvZoxu7cGtFk/skUNoPwVLclAe3G1O4rcMiLqwMLXEqjqN64lFzlkTVomE6s8ok/ZbqsW45LSIgx3mx7ho1PcDcR5ko/CrLq1hXplOjU2AGD5QmA== X-Forefront-Antispam-Report-Untrusted: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:PAXPR08MB6926.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230001)(4636009)(366004)(8936002)(64756008)(66556008)(8676002)(66946007)(66476007)(186003)(53546011)(38100700002)(2906002)(122000001)(38070700005)(30864003)(5660300002)(83380400001)(7696005)(52536014)(6506007)(9686003)(26005)(86362001)(4326008)(76116006)(66446008)(110136005)(71200400001)(498600001)(33656002)(55016003); DIR:OUT; SFP:1101; Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR08MB7022 Original-Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com; X-EOPAttributedMessage: 0 X-MS-Exchange-Transport-CrossTenantHeadersStripped: VE1EUR03FT023.eop-EUR03.prod.protection.outlook.com X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id-Prvs: dcad8abe-31e8-46ef-97e8-08da1e138804 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: UEsvltcHtsYgE1vGdrkjALQcHcnnncs97M+T5ZvoCvaRsIT8A0HcpNi4KuvUStypUM/8wuQIutnp/kw0xhv34KCaaUuk96KGbXiMtsfs0W124P8dK1tZtUTpaghNDtD2dPftoEsQA7ttcJ+DhdSlfRShe+thuo2d9aUim8Ub36MgAkxVndGs5twPtzgcm0PuJYuUFn+o+rj4//AKNbw3t0PyDraoXQovegKOIlPkfbWnMxWi/6Gi0KUY7skxe7sXafFoZSDwUAwXZoYmVCNdwl0d/MmJgxOJnEhnsX3mRTpj5DQFc5LnIyPgK3ZNEMnAJ5KmvqgbT8hqqEUXHt/T+W8/j5Moj8xNrNK8PWeGgnUm/gXyC/bSZeTVM45MiGI5zZnTfG2ei1qPJU8B+pPzEHXGcr2xxAqvcIst2qDnx7oKf9Wm3+DD037paEltiEp/j+OlHYb1UZ1mJZYOtY4BabcEAEhWMz6g6ayERDw3cvHYBSgYEHy0E9G/4J4iug4mRuDIjWMfgTsf0JbOFjNB1XNCLU6yYz2C6Iyj7fOOYJz1FRLdQE+Te43z1OdmzloAk9qfPB5KCzgYeBwjunFQlloB6lqFY61dZEwwJpVAAunwTdC4VYfVgeTRSi67eOELPWXz3uUd4iSM2n7tuJnflglk4PPnIRfHYDgIMmMuakpUx/t6ltPOgwFA9xICJvAg X-Forefront-Antispam-Report: CIP:63.35.35.123; CTRY:IE; LANG:en; SCL:1; SRV:; IPV:CAL; SFV:NSPM; H:64aa7808-outbound-1.mta.getcheckrecipient.com; PTR:ec2-63-35-35-123.eu-west-1.compute.amazonaws.com; CAT:NONE; SFS:(13230001)(4636009)(36840700001)(46966006)(40470700004)(316002)(33656002)(356005)(9686003)(55016003)(2906002)(30864003)(508600001)(47076005)(336012)(186003)(26005)(110136005)(83380400001)(82310400005)(53546011)(7696005)(52536014)(8936002)(8676002)(4326008)(70586007)(5660300002)(36860700001)(70206006)(450100002)(86362001)(40460700003)(81166007)(6506007); DIR:OUT; SFP:1101; X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 14 Apr 2022 12:37:34.0264 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 5329b107-e079-471f-e57c-08da1e138cd6 X-MS-Exchange-CrossTenant-Id: f34e5979-57d9-4aaa-ad4d-b122a662184d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=f34e5979-57d9-4aaa-ad4d-b122a662184d; Ip=[63.35.35.123]; Helo=[64aa7808-outbound-1.mta.getcheckrecipient.com] X-MS-Exchange-CrossTenant-AuthSource: VE1EUR03FT023.eop-EUR03.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM8PR08MB5857 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_PASS, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE, UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 14 Apr 2022 12:37:40 -0000 > -----Original Message----- > From: Gcc-patches bounces+kyrylo.tkachov=3Darm.com@gcc.gnu.org> On Behalf Of Patrick Palka > via Gcc-patches > Sent: Thursday, April 14, 2022 1:31 PM > To: gcc-patches@gcc.gnu.org > Cc: libstdc++@gcc.gnu.org > Subject: [PATCH] libstdc++: Optimize integer std::from_chars >=20 > This applies the following optimizations to the integer std::from_chars > implementation: >=20 > 1. Use a lookup table for converting an alphanumeric digit to its > base-36 value instead of using a range test (for 0-9) and switch > (for a-z and A-Z). The table is constructed using a C++14 > constexpr function which doesn't assume a particular character > encoding or __CHAR_BIT__ value. The new conversion function > __from_chars_alnum_to_val is templated on whether we care > only about the decimal digits, in which case we can perform the > conversion with a single subtraction since the digit characters > are guaranteed to be contiguous (unlike the letters). > 2. Generalize __from_chars_binary to handle all power-of-two bases. > This function, now named __from_chars_pow2_base, is also templated > on whether we care only about the decimal digits in order to speed > up digit conversion for base 2, 4 and 8. > 3. In __from_chars_digit, use > static_cast(__c - '0') < __base > instead of > '0' <=3D __c && __c <=3D ('0' + (__base - 1)). > as the digit recognition test (exhaustively verified that the two > tests are equivalent). > 4. In __from_chars_alnum, use a nested loop to consume the rest of the > digits in the overflow case (mirroring __from_chars_digit) so that > the main loop doesn't have to maintain the __valid overflow flag. >=20 > At this point, __from_chars_digit is nearly identical to > __from_chars_alnum, so this patch combines the two functions, removing > the former and templatizing the latter according to whether we care only > about the decimal digits. Finally, >=20 > 5. In __from_chars_alnum, keep track of a lower bound on the number of > unused bits in the result and use that to omit the overflow check > when it's safe to do so. >=20 > In passing this replaces the non-portable function ascii_to_hexit > used by __floating_from_chars_hex with the new conversion function. >=20 > Here are some runtime measurements for a simple 15-line benchmark that > roundtrips printing/parsing 200 million integers via std::to/from_chars > (average of 5 runs): >=20 > Base Before After (seconds, lower is better) > 2 9.37 9.37 > 3 12.13 15.79 > 8 3.67 4.15 > 10 3.86 4.90 > 11 5.03 6.84 > 16 2.93 4.14 > 32 2.39 3.85 > 36 3.26 5.22 The after numbers look worse (higher)? Are the columns accidentally swapped= ? Thanks, Kyrill >=20 > Testedon x86_64-pc-linux-gnu, does this look OK for trunk? Also tested > against libc++'s from_chars tests for good measure. >=20 > libstdc++-v3/ChangeLog: >=20 > * include/std/charconv (__from_chars_alnum_to_val_table): Define. > (__from_chars_alnum_to_val): Define. > (__from_chars_binary): Rename to ... > (__from_chars_pow2_base): ... this. Generalize to handle any > power-of-two base using __from_chars_alnum_to_val. > (__from_chars_digit): Optimize digit recognition to a single > test instead of two tests. Use [[__unlikely___]] attribute. > (__from_chars_alpha_to_num): Remove. > (__from_chars_alnum): Use __from_chars_alnum_to_val. Use a > nested loop for the overflow case. > (from_chars): Adjust appropriately. > * src/c++17/floating_from_chars.cc (ascii_to_hexit): Remove. > (__floating_from_chars_hex): Use __from_chars_alnum_to_val > to recognize a hex digit instead. > --- > libstdc++-v3/include/std/charconv | 250 ++++++++---------- > libstdc++-v3/src/c++17/floating_from_chars.cc | 18 +- > 2 files changed, 105 insertions(+), 163 deletions(-) >=20 > diff --git a/libstdc++-v3/include/std/charconv b/libstdc++- > v3/include/std/charconv > index 2ce9c7d4cb9..5e44459749a 100644 > --- a/libstdc++-v3/include/std/charconv > +++ b/libstdc++-v3/include/std/charconv > @@ -407,176 +407,127 @@ namespace __detail > return true; > } >=20 > - /// std::from_chars implementation for integers in base 2. > - template > + // Construct and return a lookup table that maps 0-9, A-Z and a-z to t= he > + // corresponding corresponding base-36 value and maps all other > characters > + // to 127. > + constexpr auto > + __from_chars_alnum_to_val_table() > + { > + constexpr unsigned char __lower_letters[] > + =3D { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', > + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', > + 'u', 'v', 'w', 'x', 'y', 'z' }; > + constexpr unsigned char __upper_letters[] > + =3D { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', > + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', > + 'U', 'V', 'W', 'X', 'Y', 'Z' }; > + struct { unsigned char __data[1u << __CHAR_BIT__] =3D {}; } __table; > + for (auto& __entry : __table.__data) > + __entry =3D 127; > + for (int __i =3D 0; __i < 10; ++__i) > + __table.__data['0' + __i] =3D __i; > + for (int __i =3D 0; __i < 26; ++__i) > + { > + __table.__data[__lower_letters[__i]] =3D 10 + __i; > + __table.__data[__upper_letters[__i]] =3D 10 + __i; > + } > + return __table; > + } > + > + /// If _DecOnly is true: if the character is a decimal digit, then > + /// return its corresponding base-10 value, otherwise return a value >= =3D > 127. > + /// If _DecOnly is false: if the character is an alphanumeric digit, t= hen > + /// return its corresponding base-36 value, otherwise return a value >= =3D > 127. > + template > + unsigned char > + __from_chars_alnum_to_val(unsigned char __c) > + { > + if _GLIBCXX17_CONSTEXPR (_DecOnly) > + return __c - '0'; > + else > + { > + static constexpr auto __table =3D > __from_chars_alnum_to_val_table(); > + return __table.__data[__c]; > + } > + } > + > + /// std::from_chars implementation for integers in a power-of-two base= . > + /// If _DecOnly is true, then we may assume __base is at most 8. > + template > bool > - __from_chars_binary(const char*& __first, const char* __last, _Tp& > __val) > + __from_chars_pow2_base(const char*& __first, const char* __last, _Tp= & > __val, > + int __base) > { > static_assert(is_integral<_Tp>::value, "implementation bug"); > static_assert(is_unsigned<_Tp>::value, "implementation bug"); >=20 > + __glibcxx_assert((__base & (__base - 1)) =3D=3D 0); > + __glibcxx_assert(_DecOnly ? __base <=3D 8 : __base <=3D 32); > + const int __log2_base =3D __countr_zero(__base); > + > const ptrdiff_t __len =3D __last - __first; > ptrdiff_t __i =3D 0; > while (__i < __len && __first[__i] =3D=3D '0') > ++__i; > const ptrdiff_t __leading_zeroes =3D __i; >=20 > + unsigned char __leading_c =3D 0; > while (__i < __len) > { > - const unsigned char __c =3D (unsigned)__first[__i] - '0'; > - if (__c < 2) > - __val =3D (__val << 1) | __c; > - else > + const unsigned char __c =3D > __from_chars_alnum_to_val<_DecOnly>(__first[__i]); > + if (__c >=3D __base) > break; > + __val =3D (__val << __log2_base) | __c; > + > + if (__i =3D=3D __leading_zeroes) > + { > + // At the first iteration, remember the leading significant digit= . > + __glibcxx_assert(__leading_c =3D=3D 0 && __c !=3D 0); > + __leading_c =3D __c; > + } > __i++; > } > __first +=3D __i; > - return (__i - __leading_zeroes) <=3D __gnu_cxx::__int_traits<_Tp>:= :__digits; > + auto __significant_bits =3D (__i - __leading_zeroes) * __log2_base= ; > + if (__base !=3D 2 && __leading_c !=3D 0) > + // Compensate for a leading significant digit that didn't use all > + // of its available bits. > + __significant_bits -=3D __log2_base - __bit_width(__leading_c); > + __glibcxx_assert(__significant_bits >=3D 0); > + return __significant_bits <=3D __gnu_cxx::__int_traits<_Tp>::__dig= its; > } >=20 > - /// std::from_chars implementation for integers in bases 3 to 10. > - template > - bool > - __from_chars_digit(const char*& __first, const char* __last, _Tp& __= val, > - int __base) > - { > - static_assert(is_integral<_Tp>::value, "implementation bug"); > - static_assert(is_unsigned<_Tp>::value, "implementation bug"); > - > - auto __matches =3D [__base](char __c) { > - return '0' <=3D __c && __c <=3D ('0' + (__base - 1)); > - }; > - > - while (__first !=3D __last) > - { > - const char __c =3D *__first; > - if (__matches(__c)) > - { > - if (!__raise_and_add(__val, __base, __c - '0')) > - { > - while (++__first !=3D __last && __matches(*__first)) > - ; > - return false; > - } > - __first++; > - } > - else > - return true; > - } > - return true; > - } > - > - constexpr char > - __from_chars_alpha_to_num(char __c) > - { > - switch (__c) > - { > - case 'a': > - case 'A': > - return 10; > - case 'b': > - case 'B': > - return 11; > - case 'c': > - case 'C': > - return 12; > - case 'd': > - case 'D': > - return 13; > - case 'e': > - case 'E': > - return 14; > - case 'f': > - case 'F': > - return 15; > - case 'g': > - case 'G': > - return 16; > - case 'h': > - case 'H': > - return 17; > - case 'i': > - case 'I': > - return 18; > - case 'j': > - case 'J': > - return 19; > - case 'k': > - case 'K': > - return 20; > - case 'l': > - case 'L': > - return 21; > - case 'm': > - case 'M': > - return 22; > - case 'n': > - case 'N': > - return 23; > - case 'o': > - case 'O': > - return 24; > - case 'p': > - case 'P': > - return 25; > - case 'q': > - case 'Q': > - return 26; > - case 'r': > - case 'R': > - return 27; > - case 's': > - case 'S': > - return 28; > - case 't': > - case 'T': > - return 29; > - case 'u': > - case 'U': > - return 30; > - case 'v': > - case 'V': > - return 31; > - case 'w': > - case 'W': > - return 32; > - case 'x': > - case 'X': > - return 33; > - case 'y': > - case 'Y': > - return 34; > - case 'z': > - case 'Z': > - return 35; > - } > - return 127; > - } > - > - /// std::from_chars implementation for integers in bases 11 to 36. > - template > + /// std::from_chars implementation for integers in any base. > + /// If _DecOnly is true, then we may assume __base is at most 10. > + template > bool > __from_chars_alnum(const char*& __first, const char* __last, _Tp& __= val, > int __base) > { > - bool __valid =3D true; > - while (__first !=3D __last) > + if _GLIBCXX17_CONSTEXPR (_DecOnly) > + __glibcxx_assert(__base <=3D 10); > + > + const int __bits_per_digit =3D __bit_width(__base); > + int __unused_bits_lower_bound =3D > __gnu_cxx::__int_traits<_Tp>::__digits; > + for (; __first !=3D __last; ++__first) > { > - char __c =3D *__first; > - if ('0' <=3D __c && __c <=3D '9') // isdigit > - __c -=3D '0'; > - else > + const unsigned char __c =3D > __from_chars_alnum_to_val<_DecOnly>(*__first); > + if (__c >=3D __base) > + return true; > + > + __unused_bits_lower_bound -=3D __bits_per_digit; > + if (__unused_bits_lower_bound >=3D 0) [[__likely__]] > + /* We're definitely not going to overflow. */ > + __val =3D __val * __base + __c; > + else if (!__raise_and_add(__val, __base, __c)) [[__unlikely__]] > { > - __c =3D __from_chars_alpha_to_num(__c); > - if (__c >=3D __base) > - break; > + while (++__first !=3D __last > + && __from_chars_alnum_to_val<_DecOnly>(*__first) < > __base) > + ; > + return false; > } > - > - if (__builtin_expect(__valid, 1)) > - __valid =3D __raise_and_add(__val, __base, __c); > - __first++; > } > - return __valid; > + return true; > } >=20 > template > @@ -611,12 +562,17 @@ namespace __detail >=20 > const auto __start =3D __first; > bool __valid; > - if (__base =3D=3D 2) > - __valid =3D __detail::__from_chars_binary(__first, __last, __val); > + if ((__base & (__base - 1)) =3D=3D 0) > + { > + if (__base <=3D 8) > + __valid =3D __detail::__from_chars_pow2_base(__first, __last, > __val, __base); > + else > + __valid =3D __detail::__from_chars_pow2_base(__first, > __last, __val, __base); > + } > else if (__base <=3D 10) > - __valid =3D __detail::__from_chars_digit(__first, __last, __val, __base= ); > + __valid =3D __detail::__from_chars_alnum(__first, __last, __val, > __base); > else > - __valid =3D __detail::__from_chars_alnum(__first, __last, __val, > __base); > + __valid =3D __detail::__from_chars_alnum(__first, __last, __val, > __base); >=20 > if (__builtin_expect(__first =3D=3D __start, 0)) > __res.ec =3D errc::invalid_argument; > diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++- > v3/src/c++17/floating_from_chars.cc > index 4aa2483bc28..bbe03f7f068 100644 > --- a/libstdc++-v3/src/c++17/floating_from_chars.cc > +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc > @@ -451,20 +451,6 @@ namespace > #endif // USE_STRTOD_FOR_FROM_CHARS >=20 > #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && > _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 > - // If the given ASCII character represents a hexit, return that hexit. > - // Otherwise return -1. > - int > - ascii_to_hexit(char ch) > - { > - if (ch >=3D '0' && ch <=3D '9') > - return ch - '0'; > - if (ch >=3D 'a' && ch <=3D 'f') > - return ch - 'a' + 10; > - if (ch >=3D 'A' && ch <=3D 'F') > - return ch - 'A' + 10; > - return -1; > - } > - > // Return true iff [FIRST,LAST) begins with PREFIX, ignoring case. > bool > starts_with_ci(const char* first, const char* last, string_view prefix= ) > @@ -614,8 +600,8 @@ namespace > continue; > } >=20 > - int hexit =3D ascii_to_hexit(ch); > - if (hexit =3D=3D -1) > + int hexit =3D __detail::__from_chars_alnum_to_val(ch); > + if (hexit >=3D 16) > break; > seen_hexit =3D true; >=20 > -- > 2.36.0.rc2