Update googleurl to the latest version from Chromium The version used is 85adfe7b2524c588ad7f3804bd84065db8f492bf, from Tue Nov 3 18:24:30 2020 +0000
diff --git a/.bazelversion b/.bazelversion index 47b322c..40c341b 100644 --- a/.bazelversion +++ b/.bazelversion
@@ -1 +1 @@ -3.4.1 +3.6.0
diff --git a/AUTHORS b/AUTHORS index 48cfe49..160949c 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -43,6 +43,7 @@ Aleksandar Stojiljkovic <aleksandar.stojiljkovic@intel.com> Alex Gabriel <minilogo@gmail.com> Alex Gartrell <agartrell@cmu.edu> +Alex Gaynor <alex.gaynor@gmail.com> Alex Henrie <alexhenrie24@gmail.com> Alex Scheele <alexscheele@gmail.com> Alexander Douglas <agdoug@amazon.com> @@ -50,6 +51,7 @@ Alexander Rezepkin <etu@vivaldi.net> Alexander Shalamov <alexander.shalamov@intel.com> Alexander Sulfrian <alexander@sulfrian.net> +Alexander Zhirov <ciberst@gmail.com> Alexandre Abreu <wiss1976@gmail.com> Alexandru Chiculita <achicu@adobe.com> Alexey Korepanov <alexkorep@gmail.com> @@ -104,6 +106,7 @@ Arjun Karthik <arjunkar@amazon.com> Arman Ghotb <armanghotb@gmail.com> Armin Burgmeier <aburgmeier@bloomberg.net> +Arnaud Coomans <hello@acoomans.com> Arnaud Mandy <arnaud.mandy@intel.com> Arnaud Renevier <a.renevier@samsung.com> Arpita Bahuguna <a.bah@samsung.com> @@ -166,6 +169,7 @@ Caitlin Potter <caitpotter88@gmail.com> Calvin Mei <calvimei@amazon.com> Cameron Gutman <aicommander@gmail.com> +Carlos Santa <carlos.santa@intel.com> Catalin Badea <badea@adobe.com> Cathie Chen <cathiechen@tencent.com> Cem Kocagil <cem.kocagil@gmail.com> @@ -183,6 +187,8 @@ Changyeon Kim <cyzero.kim@samsung.com> Chanho Park <parkch98@gmail.com> Chansik Yun <chansik.yun@gmail.com> +Chany Arpin-Plante <chany.arpin@gmail.com> +Chanyong Moon <dev.chanyongmoon@gmail.com> Chaobin Zhang <zhchbin@gmail.com> Charles Vaughn <cvaughn@gmail.com> Cheng Zhao <zcbenz@gmail.com> @@ -225,8 +231,9 @@ Daniel Waxweiler <daniel.waxweiler@gmail.com> Dániel Bátyai <dbatyai@inf.u-szeged.hu> Dániel Vince <vinced@inf.u-szeged.hu> +Daoming Qiu <daoming.qiu@intel.com> Darshini KN <kn.darshini@samsung.com> -Dave Barker <kzar@kzar.co.uk> +Dave Vandyke <kzar@kzar.co.uk> David Benjamin <davidben@mit.edu> David Davidovic <david@davidovic.io> David Erceg <erceg.david@gmail.com> @@ -238,6 +245,7 @@ David Manouchehri <david@davidmanouchehri.com> David McAllister <mcdavid@amazon.com> David Michael Barr <david.barr@samsung.com> +David Sanders <dsanders11@ucsbalum.com> David Spellman <dspell@amazon.com> David Valachovic <adenflorian@gmail.com> Dax Kelson <dkelson@gurulabs.com> @@ -255,6 +263,8 @@ Diego Ferreiro Val <elfogris@gmail.com> Dillon Sellars <dill.sellars@gmail.com> Divya Bansal <divya.bansal@samsung.com> +Dmitry Shachnev <mitya57@gmail.com> +Dmitry Sokolov <dimanne@gmail.com> Dominic Farolino <domfarolino@gmail.com> Dominic Jodoin <dominic.jodoin@gmail.com> Dominik Röttsches <dominik.rottsches@intel.com> @@ -366,6 +376,7 @@ Heeyoun Lee <heeyoun.lee@samsung.com> Henrique Limas <henrique.ramos.limas@gmail.com> Himanshu Joshi <h.joshi@samsung.com> +Hodol Han <bab6ting@gmail.com> Holger Kraus <kraush@amazon.com> Hong Zheng <hong.zheng@intel.com> Hongbo Min <hongbo.min@intel.com> @@ -386,7 +397,9 @@ Hyungchan Kim <inlinechan@gmail.com> Hyungwook Lee <hyungwook.lee@navercorp.com> Hyungwook Lee <withlhw@gmail.com> +HyunJi Kim <hjkim3323@gmail.com> Hyunjun Shin <hyunjun.shin2@navercorp.com> +Hyunjun Shin <shjj1504@gmail.com> Hyunjune Kim <hyunjune.kim@samsung.com> Hyunki Baik <hyunki.baik@samsung.com> Ian Cullinan <cullinan@amazon.com> @@ -408,6 +421,7 @@ Jacob Clark <jacob.jh.clark@googlemail.com> Jacob Mandelson <jacob@mandelson.org> Jaehun Lim <ljaehun.lim@samsung.com> +Jaehyun Ko <jaehyun.dev@gmail.com> Jaehyun Lee <j-hyun.lee@samsung.com> Jaekyeom Kim <btapiz@gmail.com> Jaemin Seo <jaemin86.seo@samsung.com> @@ -447,6 +461,7 @@ Jesper Storm Bache <jsbache@gmail.com> Jesse Miller <jesse@jmiller.biz> Jesus Sanchez-Palencia <jesus.sanchez-palencia.fernandez.fil@intel.com> +Jiadong Chen <chenjiadong@huawei.com> Jiadong Zhu <jiadong.zhu@linaro.org> Jiahe Zhang <jiahe.zhang@intel.com> Jiajia Qin <jiajia.qin@intel.com> @@ -510,6 +525,7 @@ Joyer Huang <collger@gmail.com> Juan Jose Lopez Jaimez <jj.lopezjaimez@gmail.com> Juhui Lee <juhui24.lee@samsung.com> +Julian Geppert <spctstr@gmail.com> Julien Brianceau <jbriance@cisco.com> Julien Isorce <j.isorce@samsung.com> Julien Racle <jracle@logitech.com> @@ -579,6 +595,7 @@ Kyounga Ra <kyounga.ra@gmail.com> Kyoungdeok Kwon <kkd927@gmail.com> Kyung Yeol Kim <chitacan@gmail.com> +Kyungsun Lee <kyungsuny.lee@gmail.com> Kyungtae Kim <ktf.kim@samsung.com> Kyungyoung Heo <bbvch13531@gmail.com> Lalit Chandivade <lalit.chandivade@einfochips.com> @@ -588,6 +605,7 @@ Lauren Yeun Kim <lauren.yeun.kim@gmail.com> Lauri Oherd <lauri.oherd@gmail.com> Lavar Askew <open.hyperion@gmail.com> +Le Hoang Quyen <le.hoang.q@gmail.com> Legend Lee <guanxian.li@intel.com> Leith Bade <leith@leithalweapon.geek.nz> Lei Li <lli.kernel.kvm@gmail.com> @@ -605,12 +623,14 @@ Loo Rong Jie <loorongjie@gmail.com> Lorenzo Stoakes <lstoakes@gmail.com> Lu Guanqun <guanqun.lu@gmail.com> +Luc Shi <lei.a.shi@intel.com> Luca Di Domenico <luca94dd@gmail.com> Lucie Brozkova <lucinka.brozkova@gmail.com> Luiz Von Dentz <luiz.von.dentz@intel.com> Luka Dojcilovic <l.dojcilovic@gmail.com> Lukasz Krakowiak <lukasz.krakowiak@mobica.com> Luke Inman-Semerau <luke.semerau@gmail.com> +Luke Seunghoe Gu <gulukesh@gmail.com> Luke Zarko <lukezarko@gmail.com> Luoxi Pan <l.panpax@gmail.com> Maarten Lankhorst <m.b.lankhorst@gmail.com> @@ -630,6 +650,7 @@ Marc des Garets <marc.desgarets@googlemail.com> Marcin Wiacek <marcin@mwiacek.com> Marco Rodrigues <gothicx@gmail.com> +Mariam Ali <alimariam@noogler.google.com> Mario Pistrich <m.pistrich@gmail.com> Mario Sanchez Prada <mario.prada@samsung.com> Mariusz Mlynski <marius.mlynski@gmail.com> @@ -642,6 +663,7 @@ Martina Kollarova <martina.kollarova@intel.com> Masahiro Yado <yado.masa@gmail.com> Masaru Nishida <msr.i386@gmail.com> +Masayuki Wakizaka <mwakizaka0108@gmail.com> Matej Knopp <matej.knopp@gmail.com> Matheus Bratfisch <matheusbrat@gmail.com> Mathias Bynens <mathias@qiwi.be> @@ -686,8 +708,10 @@ Milko Leporis <milko.leporis@imgtec.com> Milton Chiang <milton.chiang@mediatek.com> Milutin Smiljanic <msmiljanic.gm@gmail.com> +Minchul Kang <tegongkang@gmail.com> Minggang Wang <minggang.wang@intel.com> Mingmin Xie <melvinxie@gmail.com> +Minjeong Kim <deoxyribonucleicacid150@gmail.com> Minjeong Lee <apenr1234@gmail.com> Minseok Koo <kei98301@gmail.com> Minsoo Max Koo <msu.koo@samsung.com> @@ -711,6 +735,7 @@ Nagarjuna Atluri <nagarjuna.a@samsung.com> Naiem Shaik <naiem.shaik@gmail.com> Naoki Takano <takano.naoki@gmail.com> +Nathan Mitchell <nathaniel.v.mitchell@gmail.com> Naveen Bobbili <naveenbobbili@motorola.com> Naveen Bobbili <qghc36@motorola.com> Naveen Kumar Devaraj <devarajn@amazon.com> @@ -719,6 +744,7 @@ Neal Gompa <ngompa13@gmail.com> Ned Williamson <nedwilliamson@gmail.com> Nedeljko Babic <nedeljko.babic@imgtec.com> +Nidhi Jaju <nidhijaju127@gmail.com> Nikhil Bansal <n.bansal@samsung.com> Nikhil Sahni <nikhil.sahni@samsung.com> Nikita Ofitserov <himikof@gmail.com> @@ -771,6 +797,7 @@ Peter Griffin <peter.griffin@linaro.org> Peter Molnar <pmolnar.u-szeged@partner.samsung.com> Peter Snyder <snyderp@gmail.com> +Peter Varga <pvarga@inf.u-szeged.hu> Peter Wong <peter.wm.wong@gmail.com> Philip Hanson <philip.hanson@intel.com> Philipp Hancke <fippo@andyet.net> @@ -792,6 +819,7 @@ Pritam Nikam <pritam.nikam@samsung.com> Puttaraju R <puttaraju.r@samsung.com> Qi Yang <qi1988.yang@samsung.com> +Qiang Zeng <zengqiang1@huawei.com> Qiankun Miao <qiankun.miao@intel.com> Qing Zhang <qing.zhang@intel.com> Qingmei Li <qingmei.li@vivo.com> @@ -815,6 +843,7 @@ Ravi Phaneendra Kasibhatla <r.kasibhatla@samsung.com> Ravi Phaneendra Kasibhatla <ravi.kasibhatla@motorola.com> Raviraj Sitaram <raviraj.p.sitaram@intel.com> +Reda Tawfik <redatawfik@noogler.google.com> Réda Housni Alaoui <alaoui.rda@gmail.com> Refael Ackermann <refack@gmail.com> Renata Hodovan <rhodovan.u-szeged@partner.samsung.com> @@ -833,6 +862,7 @@ Robert Hogan <robhogan@gmail.com> Robert Nagy <robert.nagy@gmail.com> Robert Sesek <rsesek@bluestatic.org> +Roger Zanoni <rogerzanoni@gmail.com> Roland Takacs <rtakacs.u-szeged@partner.samsung.com> Romain Pokrzywka <romain.pokrzywka@gmail.com> Rosen Dash <nqk836@motorola.com> @@ -844,6 +874,7 @@ Ruben Terrazas <rubentopo@gmail.com> Rufus Hamade <rufus.hamade@imgtec.com> Ruiyi Luo <luoruiyi2008@gmail.com> +Rulong Chen <rulong.crl@alibaba-inc.com> Russell Davis <russell.davis@gmail.com> Ryan Ackley <ryanackley@gmail.com> Ryan Norton <rnorton10@gmail.com> @@ -885,6 +916,7 @@ Sebastian Krzyszkowiak <dos@dosowisko.net> Seo Sanghyeon <sanxiyn@gmail.com> Seokju Kwon <seokju.kwon@gmail.com> +Seokho Song <0xdevssh@gmail.com> SeongTae Jeong <ferendevelop.gl@gmail.com> Sergey Kipet <sergey.kipet@gmail.com> Sergey Putilin <p.sergey@samsung.com> @@ -908,6 +940,7 @@ Sherry Mou <wenjinm@amazon.com> Shez Baig <sbaig1@bloomberg.net> Shigeki Ohtsu <shigeki.ohtsu@gmail.com> +Shicheng Wang <wangshicheng@xiaomi.com> Shiliu Wang <aofdwsl@gmail.com> Shiliu Wang <shiliu.wang@intel.com> Shilpa Shri <shilpa.shri@samsung.com> @@ -942,6 +975,7 @@ Stephen Searles <stephen.searles@gmail.com> Stephen Sigwart <ssigwart@gmail.com> Steve Sanders <steve@zanderz.com> +Steven Cohen <peragwin@gmail.com> Steven Pennington <spenn@engr.uvic.ca> Steven Roussey <sroussey@gmail.com> Subrahmanya Praveen Munukutla <sataya.m@samsung.com> @@ -951,6 +985,7 @@ Sujae Jo <sujae33.jo@gmail.com> Sujith S S <sujiths.s@samsung.com> Sunchang Li <johnstonli@tencent.com> +Sundoo Kim <nerdooit@gmail.com> Suneel Kota <suneel.kota@samsung.com> Sungguk Lim <limasdf@gmail.com> Sungmann Cho <sungmann.cho@gmail.com> @@ -971,6 +1006,7 @@ Taeheon Kim <skyrabbits1@gmail.com> Taeho Nam <thn7440@gmail.com> Taehoon Lee <taylor.hoon@gmail.com> +Tae Shin <taeshindev@gmail.com> Takashi Fujita <tgfjt.mail@gmail.com> Takeshi Kurosawa <taken.spc@gmail.com> Tanay Chowdhury <tanay.c@samsung.com> @@ -984,6 +1020,7 @@ Thiago Marcos P. Santos <thiago.santos@intel.com> Thomas Butter <tbutter@gmail.com> Thomas Conti <tomc@amazon.com> +Thomas Nguyen <haitung.nguyen@avast.com> Thomas White <im.toms.inbox@gmail.com> Tiago Vignatti <tiago.vignatti@intel.com> Tibor Dusnoki <tibor.dusnoki.91@gmail.com> @@ -1037,11 +1074,13 @@ Wesley Lancel <wesleylancel@gmail.com> Wei Wang <wei4.wang@intel.com> Wesley Wigham <wwigham@gmail.com> +Will Cohen <wwcohen@gmail.com> Will Hirsch <chromium@willhirsch.co.uk> Will Shackleton <w.shackleton@gmail.com> William Xie <william.xie@intel.com> Winston Chen <winston.c1@samsung.com> Xiang Long <xiang.long@intel.com> +XiangYang <yangxiang12@huawei.com> Xiangze Zhang <xiangze.zhang@intel.com> Xiaofeng Zhang <xiaofeng.zhang@intel.com> Xiaolei Yu <dreifachstein@gmail.com> @@ -1071,6 +1110,7 @@ Yi Sun <ratsunny@gmail.com> Yichen Jiang <jiangyichen123@gmail.com> Yifei Yu <yuyifei@xiaomi.com> +Yi Zhang <yi.y.zhang@intel.com> Yizhou Jiang <yizhou.jiang@intel.com> Yoav Weiss <yoav@yoav.ws> Yoav Zilberberg <yoav.zilberberg@gmail.com> @@ -1116,8 +1156,7 @@ Zoltan Kuscsik <zoltan.kuscsik@linaro.org> Zsolt Borbely <zsborbely.u-szeged@partner.samsung.com> 方觉 (Fang Jue) <fangjue23303@gmail.com> -Julian Geppert <spctstr@gmail.com> -Jiadong Chen <chenjiadong@huawei.com> +# Please DO NOT APPEND here. See comments at the top of the file. # END individuals section. # BEGIN organizations section. @@ -1130,8 +1169,10 @@ Cloudflare, Inc. <*@cloudflare.com> CloudMosa, Inc. <*@cloudmosa.com> Code Aurora Forum <*@codeaurora.org> +CodeWeavers, Inc. <*@codeweavers.com> Collabora Limited <*@collabora.com> Comodo CA Limited +CoSMo Software pvt ltd <*@cosmosoftware.io> Cosium <*@cosium.com> Dell Technologies Inc. <*@dell.corp-partner.google.com> Duck Duck Go, Inc. <*@duckduckgo.com> @@ -1140,7 +1181,9 @@ Facebook, Inc. <*@fb.com> Facebook, Inc. <*@oculus.com> Google Inc. <*@google.com> +Grammarly, Inc. <*@grammarly.com> Hewlett-Packard Development Company, L.P. <*@hp.com> +HyperConnect Inc. <*@hpcnt.com> IBM Inc. <*@*.ibm.com> IBM Inc. <*@ibm.com> Igalia S.L. <*@igalia.com> @@ -1160,6 +1203,7 @@ Neverware Inc. <*@neverware.com> NIKE, Inc. <*@nike.com> NVIDIA Corporation <*@nvidia.com> +OpenFin Inc. <*@openfin.co> Opera Software ASA <*@opera.com> Optical Tone Ltd <*@opticaltone.com> Pengutronix e.K. <*@pengutronix.de> @@ -1179,4 +1223,5 @@ Vewd Software AS <*@vewd.com> Vivaldi Technologies AS <*@vivaldi.com> Yandex LLC <*@yandex-team.ru> +# Please DO NOT APPEND here. See comments at the top of the file. # END organizations section.
diff --git a/base/BUILD b/base/BUILD index 00c6c91..2ab77e8 100644 --- a/base/BUILD +++ b/base/BUILD
@@ -9,12 +9,19 @@ hdrs = [ "compiler_specific.h", "containers/checked_iterators.h", + "containers/contiguous_iterator.h", "containers/span.h", "containers/util.h", "debug/leak_annotations.h", + "functional/identity.h", + "functional/invoke.h", + "functional/not_fn.h", "macros.h", "no_destructor.h", "optional.h", + "ranges/algorithm.h", + "ranges/functional.h", + "ranges/ranges.h", "stl_util.h", "template_util.h", ],
diff --git a/base/compiler_specific.h b/base/compiler_specific.h index 0cd36dc..0f960c1 100644 --- a/base/compiler_specific.h +++ b/base/compiler_specific.h
@@ -158,6 +158,19 @@ #endif #endif +// DISABLE_CFI_ICALL -- Disable Control Flow Integrity indirect call checks. +#if !defined(DISABLE_CFI_ICALL) +#if defined(OS_WIN) +// Windows also needs __declspec(guard(nocf)). +#define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") __declspec(guard(nocf)) +#else +#define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") +#endif +#endif +#if !defined(DISABLE_CFI_ICALL) +#define DISABLE_CFI_ICALL +#endif + // Macro useful for writing cross-platform function pointers. #if !defined(CDECL) #if defined(OS_WIN)
diff --git a/base/containers/contiguous_iterator.h b/base/containers/contiguous_iterator.h new file mode 100644 index 0000000..a1c1f9b --- /dev/null +++ b/base/containers/contiguous_iterator.h
@@ -0,0 +1,97 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_CONTIGUOUS_ITERATOR_H_ +#define BASE_CONTAINERS_CONTIGUOUS_ITERATOR_H_ + +#include <array> +#include <iterator> +#include <string> +#include <type_traits> +#include <vector> + +#include "base/containers/checked_iterators.h" +#include "base/template_util.h" + +namespace gurl_base { + +namespace internal { + +template <typename T> +struct PointsToObject : std::is_object<iter_value_t<T>> {}; + +// A pointer is a contiguous iterator. +// Reference: https://wg21.link/iterator.traits#5 +template <typename T> +struct IsPointer : std::is_pointer<T> {}; + +// An iterator to std::basic_string is contiguous. +// Reference: https://wg21.link/basic.string.general#2 +template <typename T, typename StringT = std::basic_string<iter_value_t<T>>> +struct IsStringIter + : conjunction<std::is_trivial<iter_value_t<T>>, + disjunction<std::is_same<T, typename StringT::const_iterator>, + std::is_same<T, typename StringT::iterator>>> {}; + +// An iterator to std::array is contiguous. +// Reference: https://wg21.link/array.overview#1 +template <typename T, typename ArrayT = std::array<iter_value_t<T>, 1>> +struct IsArrayIter + : disjunction<std::is_same<T, typename ArrayT::const_iterator>, + std::is_same<T, typename ArrayT::iterator>> {}; + +// An iterator to a non-bool std::vector is contiguous. +// Reference: https://wg21.link/vector.overview#2 +template <typename T, typename VectorT = std::vector<iter_value_t<T>>> +struct IsVectorIter + : conjunction<negation<std::is_same<iter_value_t<T>, bool>>, + disjunction<std::is_same<T, typename VectorT::const_iterator>, + std::is_same<T, typename VectorT::iterator>>> {}; + +// The result of passing a std::valarray to std::begin is a contiguous iterator. +// Note: Since all common standard library implementations (i.e. libc++, +// stdlibc++ and MSVC's STL) just use a pointer here, we perform a similar +// optimization. The corresponding unittest still ensures that this is working +// as intended. +// Reference: https://wg21.link/valarray.range#1 +template <typename T> +struct IsValueArrayIter : std::is_pointer<T> {}; + +// base's CheckedContiguousIterator is a contiguous iterator. +template <typename T, typename ValueT = iter_value_t<T>> +struct IsCheckedContiguousIter + : disjunction<std::is_same<T, gurl_base::CheckedContiguousConstIterator<ValueT>>, + std::is_same<T, gurl_base::CheckedContiguousIterator<ValueT>>> {}; + +// Check that the iterator points to an actual object, and is one of the +// iterator types mentioned above. +template <typename T> +struct IsContiguousIteratorImpl + : conjunction<PointsToObject<T>, + disjunction<IsPointer<T>, + IsStringIter<T>, + IsArrayIter<T>, + IsVectorIter<T>, + IsValueArrayIter<T>, + IsCheckedContiguousIter<T>>> {}; + +} // namespace internal + +// IsContiguousIterator is a type trait that determines whether a given type is +// a contiguous iterator. It is similar to C++20's contiguous_iterator concept, +// but due to a lack of the corresponding contiguous_iterator_tag relies on +// explicitly instantiating the type with iterators that are supposed to be +// contiguous iterators. +// References: +// - https://wg21.link/iterator.concept.contiguous +// - https://wg21.link/std.iterator.tags#lib:contiguous_iterator_tag +// - https://wg21.link/n4284 +template <typename T> +struct IsContiguousIterator + : internal::IsContiguousIteratorImpl< + std::remove_cv_t<std::remove_reference_t<T>>> {}; + +} // namespace base + +#endif // BASE_CONTAINERS_CONTIGUOUS_ITERATOR_H_
diff --git a/base/containers/span.h b/base/containers/span.h index 45a322d..53b6965 100644 --- a/base/containers/span.h +++ b/base/containers/span.h
@@ -16,6 +16,7 @@ #include "polyfills/base/check_op.h" #include "base/containers/checked_iterators.h" +#include "base/containers/contiguous_iterator.h" #include "base/macros.h" #include "base/stl_util.h" #include "base/template_util.h" @@ -72,6 +73,15 @@ template <typename From, typename To> using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>; +template <typename Iter, typename T> +using IteratorHasConvertibleReferenceType = + IsLegalDataConversion<std::remove_reference_t<iter_reference_t<Iter>>, T>; + +template <typename Iter, typename T> +using EnableIfCompatibleContiguousIterator = std::enable_if_t< + conjunction<IsContiguousIterator<Iter>, + IteratorHasConvertibleReferenceType<Iter, T>>::value>; + template <typename Container, typename T> using ContainerHasConvertibleData = IsLegalDataConversion< std::remove_pointer_t<decltype(gurl_base::data(std::declval<Container>()))>, @@ -132,6 +142,16 @@ size_t size_; }; +// must_not_be_dynamic_extent prevents |dynamic_extent| from being returned in a +// constexpr context. +template <size_t kExtent> +constexpr size_t must_not_be_dynamic_extent() { + static_assert( + kExtent != dynamic_extent, + "EXTENT should only be used for containers with a static extent."); + return kExtent; +} + } // namespace internal // A span is a value type that represents an array of elements of type T. Since @@ -240,14 +260,19 @@ static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent"); } - constexpr span(T* data, size_t size) noexcept - : ExtentStorage(size), data_(data) { - GURL_CHECK(Extent == dynamic_extent || Extent == size); + template <typename It, + typename = internal::EnableIfCompatibleContiguousIterator<It, T>> + constexpr span(It first, size_t count) noexcept + : ExtentStorage(count), data_(gurl_base::to_address(first)) { + GURL_CHECK(Extent == dynamic_extent || Extent == count); } - // Artificially templatized to break ambiguity for span(ptr, 0). - template <typename = void> - constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) { + template < + typename It, + typename End, + typename = internal::EnableIfCompatibleContiguousIterator<It, T>, + typename = std::enable_if_t<!std::is_convertible<End, size_t>::value>> + constexpr span(It begin, End end) noexcept : span(begin, end - begin) { // Note: GURL_CHECK_LE is not constexpr, hence regular GURL_CHECK must be used. GURL_CHECK(begin <= end); } @@ -432,14 +457,10 @@ } // Type-deducing helpers for constructing a span. -template <int&... ExplicitArgumentBarrier, typename T> -constexpr span<T> make_span(T* data, size_t size) noexcept { - return {data, size}; -} - -template <int&... ExplicitArgumentBarrier, typename T> -constexpr span<T> make_span(T* begin, T* end) noexcept { - return {begin, end}; +template <int&... ExplicitArgumentBarrier, typename It, typename EndOrSize> +constexpr auto make_span(It it, EndOrSize end_or_size) noexcept { + using T = std::remove_reference_t<iter_reference_t<It>>; + return span<T>(it, end_or_size); } // make_span utility function that deduces both the span's value_type and extent @@ -462,6 +483,15 @@ // Note: This will GURL_CHECK that N indeed matches size(container). // // Usage: auto static_span = gurl_base::make_span<N>(...); +template <size_t N, + int&... ExplicitArgumentBarrier, + typename It, + typename EndOrSize> +constexpr auto make_span(It it, EndOrSize end_or_size) noexcept { + using T = std::remove_reference_t<iter_reference_t<It>>; + return span<T, N>(it, end_or_size); +} + template <size_t N, int&... ExplicitArgumentBarrier, typename Container> constexpr auto make_span(Container&& container) noexcept { using T = @@ -471,4 +501,15 @@ } // namespace base +// EXTENT returns the size of any type that can be converted to a |gurl_base::span| +// with definite extent, i.e. everything that is a contiguous storage of some +// sort with static size. Specifically, this works for std::array in a constexpr +// context. Note: +// * |gurl_base::size| should be preferred for plain arrays. +// * In run-time contexts, functions such as |std::array::size| should be +// preferred. +#define EXTENT(x) \ + ::gurl_base::internal::must_not_be_dynamic_extent<decltype( \ + ::gurl_base::make_span(x))::extent>() + #endif // BASE_CONTAINERS_SPAN_H_
diff --git a/base/functional/identity.h b/base/functional/identity.h new file mode 100644 index 0000000..26af8e0 --- /dev/null +++ b/base/functional/identity.h
@@ -0,0 +1,28 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FUNCTIONAL_IDENTITY_H_ +#define BASE_FUNCTIONAL_IDENTITY_H_ + +#include <utility> + +namespace gurl_base { + +// Implementation of C++20's std::identity. +// +// Reference: +// - https://en.cppreference.com/w/cpp/utility/functional/identity +// - https://wg21.link/func.identity +struct identity { + template <typename T> + constexpr T&& operator()(T&& t) const noexcept { + return std::forward<T>(t); + } + + using is_transparent = void; +}; + +} // namespace base + +#endif // BASE_FUNCTIONAL_IDENTITY_H_
diff --git a/base/functional/invoke.h b/base/functional/invoke.h new file mode 100644 index 0000000..4399c6b --- /dev/null +++ b/base/functional/invoke.h
@@ -0,0 +1,152 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FUNCTIONAL_INVOKE_H_ +#define BASE_FUNCTIONAL_INVOKE_H_ + +#include <type_traits> +#include <utility> + +namespace gurl_base { + +namespace internal { + +// Helper struct and alias to deduce the class type from a member function +// pointer or member object pointer. +template <typename DecayedF> +struct member_pointer_class {}; + +template <typename ReturnT, typename ClassT> +struct member_pointer_class<ReturnT ClassT::*> { + using type = ClassT; +}; + +template <typename DecayedF> +using member_pointer_class_t = typename member_pointer_class<DecayedF>::type; + +// Utility struct to detect specializations of std::reference_wrapper. +template <typename T> +struct is_reference_wrapper : std::false_type {}; + +template <typename T> +struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {}; + +// Small helpers used below in internal::invoke to make the SFINAE more concise. +template <typename F> +const bool& IsMemFunPtr = + std::is_member_function_pointer<std::decay_t<F>>::value; + +template <typename F> +const bool& IsMemObjPtr = std::is_member_object_pointer<std::decay_t<F>>::value; + +template <typename F, + typename T, + typename MemPtrClass = member_pointer_class_t<std::decay_t<F>>> +const bool& IsMemPtrToBaseOf = + std::is_base_of<MemPtrClass, std::decay_t<T>>::value; + +template <typename T> +const bool& IsRefWrapper = is_reference_wrapper<std::decay_t<T>>::value; + +template <bool B> +using EnableIf = std::enable_if_t<B, bool>; + +// Invokes a member function pointer on a reference to an object of a suitable +// type. Covers bullet 1 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.1 +template <typename F, + typename T1, + typename... Args, + EnableIf<IsMemFunPtr<F> && IsMemPtrToBaseOf<F, T1>> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) { + return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...); +} + +// Invokes a member function pointer on a std::reference_wrapper to an object of +// a suitable type. Covers bullet 2 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.2 +template <typename F, + typename T1, + typename... Args, + EnableIf<IsMemFunPtr<F> && IsRefWrapper<T1>> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) { + return (t1.get().*f)(std::forward<Args>(args)...); +} + +// Invokes a member function pointer on a pointer-like type to an object of a +// suitable type. Covers bullet 3 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.3 +template <typename F, + typename T1, + typename... Args, + EnableIf<IsMemFunPtr<F> && !IsMemPtrToBaseOf<F, T1> && + !IsRefWrapper<T1>> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) { + return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...); +} + +// Invokes a member object pointer on a reference to an object of a suitable +// type. Covers bullet 4 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.4 +template <typename F, + typename T1, + EnableIf<IsMemObjPtr<F> && IsMemPtrToBaseOf<F, T1>> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) { + return std::forward<T1>(t1).*f; +} + +// Invokes a member object pointer on a std::reference_wrapper to an object of +// a suitable type. Covers bullet 5 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.5 +template <typename F, + typename T1, + EnableIf<IsMemObjPtr<F> && IsRefWrapper<T1>> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) { + return t1.get().*f; +} + +// Invokes a member object pointer on a pointer-like type to an object of a +// suitable type. Covers bullet 6 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.6 +template <typename F, + typename T1, + EnableIf<IsMemObjPtr<F> && !IsMemPtrToBaseOf<F, T1> && + !IsRefWrapper<T1>> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) { + return (*std::forward<T1>(t1)).*f; +} + +// Invokes a regular function or function object. Covers bullet 7 of the INVOKE +// definition. +// +// Reference: https://wg21.link/func.require#1.7 +template <typename F, typename... Args> +constexpr decltype(auto) InvokeImpl(F&& f, Args&&... args) { + return std::forward<F>(f)(std::forward<Args>(args)...); +} + +} // namespace internal + +// Implementation of C++17's std::invoke. This is not based on implementation +// referenced in original std::invoke proposal, but rather a manual +// implementation, so that it can be constexpr. +// +// References: +// - https://wg21.link/n4169#implementability +// - https://en.cppreference.com/w/cpp/utility/functional/invoke +// - https://wg21.link/func.invoke +template <typename F, typename... Args> +constexpr decltype(auto) invoke(F&& f, Args&&... args) { + return internal::InvokeImpl(std::forward<F>(f), std::forward<Args>(args)...); +} + +} // namespace base + +#endif // BASE_FUNCTIONAL_INVOKE_H_
diff --git a/base/functional/not_fn.h b/base/functional/not_fn.h new file mode 100644 index 0000000..7457d03 --- /dev/null +++ b/base/functional/not_fn.h
@@ -0,0 +1,56 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FUNCTIONAL_NOT_FN_H_ +#define BASE_FUNCTIONAL_NOT_FN_H_ + +#include <type_traits> +#include <utility> + +#include "base/functional/invoke.h" + +namespace gurl_base { + +namespace internal { + +template <typename F> +struct NotFnImpl { + F f; + + template <typename... Args> + constexpr decltype(auto) operator()(Args&&... args) & noexcept { + return !gurl_base::invoke(f, std::forward<Args>(args)...); + } + + template <typename... Args> + constexpr decltype(auto) operator()(Args&&... args) const& noexcept { + return !gurl_base::invoke(f, std::forward<Args>(args)...); + } + + template <typename... Args> + constexpr decltype(auto) operator()(Args&&... args) && noexcept { + return !gurl_base::invoke(std::move(f), std::forward<Args>(args)...); + } + + template <typename... Args> + constexpr decltype(auto) operator()(Args&&... args) const&& noexcept { + return !gurl_base::invoke(std::move(f), std::forward<Args>(args)...); + } +}; + +} // namespace internal + +// Implementation of C++17's std::not_fn. +// +// Reference: +// - https://en.cppreference.com/w/cpp/utility/functional/not_fn +// - https://wg21.link/func.not.fn +template <typename F> +constexpr internal::NotFnImpl<std::decay_t<F>> not_fn(F&& f) { + return {std::forward<F>(f)}; +} + +} // namespace base + +#endif // BASE_FUNCTIONAL_NOT_FN_H_
diff --git a/base/optional.h b/base/optional.h index f07cc66..c946364 100644 --- a/base/optional.h +++ b/base/optional.h
@@ -61,7 +61,7 @@ template <class... Args> void Init(Args&&... args) { GURL_DCHECK(!is_populated_); - ::new (&value_) T(std::forward<Args>(args)...); + ::new (std::addressof(value_)) T(std::forward<Args>(args)...); is_populated_ = true; } @@ -111,7 +111,7 @@ template <class... Args> void Init(Args&&... args) { GURL_DCHECK(!is_populated_); - ::new (&value_) T(std::forward<Args>(args)...); + ::new (std::addressof(value_)) T(std::forward<Args>(args)...); is_populated_ = true; } @@ -607,12 +607,12 @@ constexpr const T* operator->() const { GURL_CHECK(storage_.is_populated_); - return &storage_.value_; + return std::addressof(storage_.value_); } constexpr T* operator->() { GURL_CHECK(storage_.is_populated_); - return &storage_.value_; + return std::addressof(storage_.value_); } constexpr const T& operator*() const & {
diff --git a/base/ranges/algorithm.h b/base/ranges/algorithm.h new file mode 100644 index 0000000..649059f --- /dev/null +++ b/base/ranges/algorithm.h
@@ -0,0 +1,4996 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_RANGES_ALGORITHM_H_ +#define BASE_RANGES_ALGORITHM_H_ + +#include <algorithm> +#include <initializer_list> +#include <iterator> +#include <type_traits> +#include <utility> + +#include "base/functional/identity.h" +#include "base/functional/invoke.h" +#include "base/ranges/functional.h" +#include "base/ranges/ranges.h" +#include "base/template_util.h" + +namespace gurl_base { + +namespace internal { + +// Returns a transformed version of the unary predicate `pred` applying `proj` +// to its argument before invoking `pred` on it. +// Ensures that the return type of `invoke(pred, ...)` is convertible to bool. +template <typename Pred, typename Proj> +constexpr auto ProjectedUnaryPredicate(Pred& pred, Proj& proj) noexcept { + return [&pred, &proj](auto&& arg) -> bool { + return gurl_base::invoke(pred, + gurl_base::invoke(proj, std::forward<decltype(arg)>(arg))); + }; +} + +// Returns a transformed version of the binary predicate `pred` applying `proj1` +// and `proj2` to its arguments before invoking `pred` on them. +// +// Provides an opt-in to considers all four permutations of projections and +// argument types. This is sometimes necessary to allow usage with legacy +// non-ranges std:: algorithms that don't support projections. +// +// These permutations are assigned different priorities to break ambiguities in +// case several permutations are possible, e.g. when Proj1 and Proj2 are the +// same type. +// +// Note that even when opting in to using all permutations of projections, +// calling code should still ensure that the canonical mapping of {Proj1, Proj2} +// to {LHS, RHS} compiles for all members of the range. This can be done by +// adding the following constraint: +// +// typename = indirect_result_t<Pred&, +// projected<iterator_t<Range1>, Proj1>, +// projected<iterator_t<Range2>, Proj2>> +// +// Ensures that the return type of `invoke(pred, ...)` is convertible to bool. +template <typename Pred, typename Proj1, typename Proj2, bool kPermute = false> +class BinaryPredicateProjector { + public: + constexpr BinaryPredicateProjector(Pred& pred, Proj1& proj1, Proj2& proj2) + : pred_(pred), proj1_(proj1), proj2_(proj2) {} + + private: + template <typename ProjT, typename ProjU, typename T, typename U> + using InvokeResult = invoke_result_t<Pred&, + invoke_result_t<ProjT&, T&&>, + invoke_result_t<ProjU&, U&&>>; + + template <typename T, typename U, typename = InvokeResult<Proj1, Proj2, T, U>> + constexpr std::pair<Proj1&, Proj2&> GetProjs(priority_tag<3>) const { + return {proj1_, proj2_}; + } + + template <typename T, + typename U, + bool LazyPermute = kPermute, + typename = std::enable_if_t<LazyPermute>, + typename = InvokeResult<Proj2, Proj1, T, U>> + constexpr std::pair<Proj2&, Proj1&> GetProjs(priority_tag<2>) const { + return {proj2_, proj1_}; + } + + template <typename T, + typename U, + bool LazyPermute = kPermute, + typename = std::enable_if_t<LazyPermute>, + typename = InvokeResult<Proj1, Proj1, T, U>> + constexpr std::pair<Proj1&, Proj1&> GetProjs(priority_tag<1>) const { + return {proj1_, proj1_}; + } + + template <typename T, + typename U, + bool LazyPermute = kPermute, + typename = std::enable_if_t<LazyPermute>, + typename = InvokeResult<Proj2, Proj2, T, U>> + constexpr std::pair<Proj2&, Proj2&> GetProjs(priority_tag<0>) const { + return {proj2_, proj2_}; + } + + public: + template <typename T, typename U> + constexpr bool operator()(T&& lhs, U&& rhs) const { + auto projs = GetProjs<T, U>(priority_tag<3>()); + return gurl_base::invoke(pred_, gurl_base::invoke(projs.first, std::forward<T>(lhs)), + gurl_base::invoke(projs.second, std::forward<U>(rhs))); + } + + private: + Pred& pred_; + Proj1& proj1_; + Proj2& proj2_; +}; + +// Small wrappers around BinaryPredicateProjector to make the calling side more +// readable. +template <typename Pred, typename Proj1, typename Proj2> +constexpr auto ProjectedBinaryPredicate(Pred& pred, + Proj1& proj1, + Proj2& proj2) noexcept { + return BinaryPredicateProjector<Pred, Proj1, Proj2>(pred, proj1, proj2); +} + +template <typename Pred, typename Proj1, typename Proj2> +constexpr auto PermutedProjectedBinaryPredicate(Pred& pred, + Proj1& proj1, + Proj2& proj2) noexcept { + return BinaryPredicateProjector<Pred, Proj1, Proj2, true>(pred, proj1, proj2); +} + +// This alias is used below to restrict iterator based APIs to types for which +// `iterator_category` and the pre-increment and post-increment operators are +// defined. This is required in situations where otherwise an undesired overload +// would be chosen, e.g. copy_if. In spirit this is similar to C++20's +// std::input_or_output_iterator, a concept that each iterator should satisfy. +template <typename Iter, + typename = decltype(++std::declval<Iter&>()), + typename = decltype(std::declval<Iter&>()++)> +using iterator_category_t = + typename std::iterator_traits<Iter>::iterator_category; + +// This alias is used below to restrict range based APIs to types for which +// `iterator_category_t` is defined for the underlying iterator. This is +// required in situations where otherwise an undesired overload would be chosen, +// e.g. transform. In spirit this is similar to C++20's std::ranges::range, a +// concept that each range should satisfy. +template <typename Range> +using range_category_t = iterator_category_t<ranges::iterator_t<Range>>; + +} // namespace internal + +namespace ranges { + +// C++14 implementation of std::ranges::in_fun_result. Note the because C++14 +// lacks the `no_unique_address` attribute it is commented out. +// +// Reference: https://wg21.link/algorithms.results#:~:text=in_fun_result +template <typename I, typename F> +struct in_fun_result { + /* [[no_unique_address]] */ I in; + /* [[no_unique_address]] */ F fun; + + template <typename I2, + typename F2, + std::enable_if_t<std::is_convertible<const I&, I2>{} && + std::is_convertible<const F&, F2>{}>> + constexpr operator in_fun_result<I2, F2>() const& { + return {in, fun}; + } + + template <typename I2, + typename F2, + std::enable_if_t<std::is_convertible<I, I2>{} && + std::is_convertible<F, F2>{}>> + constexpr operator in_fun_result<I2, F2>() && { + return {std::move(in), std::move(fun)}; + } +}; + +// TODO(crbug.com/1071094): Implement the other result types. + +// [alg.nonmodifying] Non-modifying sequence operations +// Reference: https://wg21.link/alg.nonmodifying + +// [alg.all.of] All of +// Reference: https://wg21.link/alg.all.of + +// Let `E(i)` be `invoke(pred, invoke(proj, *i))`. +// +// Returns: `false` if `E(i)` is `false` for some iterator `i` in the range +// `[first, last)`, and `true` otherwise. +// +// Complexity: At most `last - first` applications of the predicate and any +// projection. +// +// Reference: https://wg21.link/alg.all.of#:~:text=ranges::all_of(I +template <typename InputIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr bool all_of(InputIterator first, + InputIterator last, + Pred pred, + Proj proj = {}) { + for (; first != last; ++first) { + if (!gurl_base::invoke(pred, gurl_base::invoke(proj, *first))) + return false; + } + + return true; +} + +// Let `E(i)` be `invoke(pred, invoke(proj, *i))`. +// +// Returns: `false` if `E(i)` is `false` for some iterator `i` in `range`, and +// `true` otherwise. +// +// Complexity: At most `size(range)` applications of the predicate and any +// projection. +// +// Reference: https://wg21.link/alg.all.of#:~:text=ranges::all_of(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr bool all_of(Range&& range, Pred pred, Proj proj = {}) { + return ranges::all_of(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// [alg.any.of] Any of +// Reference: https://wg21.link/alg.any.of + +// Let `E(i)` be `invoke(pred, invoke(proj, *i))`. +// +// Returns: `true` if `E(i)` is `true` for some iterator `i` in the range +// `[first, last)`, and `false` otherwise. +// +// Complexity: At most `last - first` applications of the predicate and any +// projection. +// +// Reference: https://wg21.link/alg.any.of#:~:text=ranges::any_of(I +template <typename InputIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr bool any_of(InputIterator first, + InputIterator last, + Pred pred, + Proj proj = {}) { + for (; first != last; ++first) { + if (gurl_base::invoke(pred, gurl_base::invoke(proj, *first))) + return true; + } + + return false; +} + +// Let `E(i)` be `invoke(pred, invoke(proj, *i))`. +// +// Returns: `true` if `E(i)` is `true` for some iterator `i` in `range`, and +// `false` otherwise. +// +// Complexity: At most `size(range)` applications of the predicate and any +// projection. +// +// Reference: https://wg21.link/alg.any.of#:~:text=ranges::any_of(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr bool any_of(Range&& range, Pred pred, Proj proj = {}) { + return ranges::any_of(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// [alg.none.of] None of +// Reference: https://wg21.link/alg.none.of + +// Let `E(i)` be `invoke(pred, invoke(proj, *i))`. +// +// Returns: `false` if `E(i)` is `true` for some iterator `i` in the range +// `[first, last)`, and `true` otherwise. +// +// Complexity: At most `last - first` applications of the predicate and any +// projection. +// +// Reference: https://wg21.link/alg.none.of#:~:text=ranges::none_of(I +template <typename InputIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr bool none_of(InputIterator first, + InputIterator last, + Pred pred, + Proj proj = {}) { + for (; first != last; ++first) { + if (gurl_base::invoke(pred, gurl_base::invoke(proj, *first))) + return false; + } + + return true; +} + +// Let `E(i)` be `invoke(pred, invoke(proj, *i))`. +// +// Returns: `false` if `E(i)` is `true` for some iterator `i` in `range`, and +// `true` otherwise. +// +// Complexity: At most `size(range)` applications of the predicate and any +// projection. +// +// Reference: https://wg21.link/alg.none.of#:~:text=ranges::none_of(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr bool none_of(Range&& range, Pred pred, Proj proj = {}) { + return ranges::none_of(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// [alg.foreach] For each +// Reference: https://wg21.link/alg.foreach + +// Reference: https://wg21.link/algorithm.syn#:~:text=for_each_result +template <typename I, typename F> +using for_each_result = in_fun_result<I, F>; + +// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the +// range `[first, last)`, starting from `first` and proceeding to `last - 1`. +// +// Returns: `{last, std::move(f)}`. +// +// Complexity: Applies `f` and `proj` exactly `last - first` times. +// +// Remarks: If `f` returns a result, the result is ignored. +// +// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each(I +template <typename InputIterator, + typename Fun, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr auto for_each(InputIterator first, + InputIterator last, + Fun f, + Proj proj = {}) { + for (; first != last; ++first) + gurl_base::invoke(f, gurl_base::invoke(proj, *first)); + return for_each_result<InputIterator, Fun>{first, std::move(f)}; +} + +// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the +// range `range`, starting from `begin(range)` and proceeding to `end(range) - +// 1`. +// +// Returns: `{last, std::move(f)}`. +// +// Complexity: Applies `f` and `proj` exactly `size(range)` times. +// +// Remarks: If `f` returns a result, the result is ignored. +// +// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each(R +template <typename Range, + typename Fun, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto for_each(Range&& range, Fun f, Proj proj = {}) { + return ranges::for_each(ranges::begin(range), ranges::end(range), + std::move(f), std::move(proj)); +} + +// Reference: https://wg21.link/algorithm.syn#:~:text=for_each_n_result +template <typename I, typename F> +using for_each_n_result = in_fun_result<I, F>; + +// Preconditions: `n >= 0` is `true`. +// +// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the +// range `[first, first + n)` in order. +// +// Returns: `{first + n, std::move(f)}`. +// +// Remarks: If `f` returns a result, the result is ignored. +// +// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each_n +template <typename InputIterator, + typename Size, + typename Fun, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr auto for_each_n(InputIterator first, Size n, Fun f, Proj proj = {}) { + while (n > 0) { + gurl_base::invoke(f, gurl_base::invoke(proj, *first)); + ++first; + --n; + } + + return for_each_n_result<InputIterator, Fun>{first, std::move(f)}; +} + +// [alg.find] Find +// Reference: https://wg21.link/alg.find + +// Let `E(i)` be `bool(invoke(proj, *i) == value)`. +// +// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)` +// is `true`. Returns `last` if no such iterator is found. +// +// Complexity: At most `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.find#:~:text=ranges::find(I +template <typename InputIterator, + typename T, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr auto find(InputIterator first, + InputIterator last, + const T& value, + Proj proj = {}) { + // Note: In order to be able to apply `proj` to each element in [first, last) + // we are dispatching to std::find_if instead of std::find. + return std::find_if(first, last, [&proj, &value](auto&& lhs) { + return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value; + }); +} + +// Let `E(i)` be `bool(invoke(proj, *i) == value)`. +// +// Returns: The first iterator `i` in `range` for which `E(i)` is `true`. +// Returns `end(range)` if no such iterator is found. +// +// Complexity: At most `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.find#:~:text=ranges::find(R +template <typename Range, + typename T, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto find(Range&& range, const T& value, Proj proj = {}) { + return ranges::find(ranges::begin(range), ranges::end(range), value, + std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)` +// is `true`. Returns `last` if no such iterator is found. +// +// Complexity: At most `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if(I +template <typename InputIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr auto find_if(InputIterator first, + InputIterator last, + Pred pred, + Proj proj = {}) { + return std::find_if(first, last, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Returns: The first iterator `i` in `range` for which `E(i)` is `true`. +// Returns `end(range)` if no such iterator is found. +// +// Complexity: At most `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto find_if(Range&& range, Pred pred, Proj proj = {}) { + return ranges::find_if(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// Let `E(i)` be `bool(!invoke(pred, invoke(proj, *i)))`. +// +// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)` +// is `true`. Returns `last` if no such iterator is found. +// +// Complexity: At most `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if_not(I +template <typename InputIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr auto find_if_not(InputIterator first, + InputIterator last, + Pred pred, + Proj proj = {}) { + return std::find_if_not(first, last, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(i)` be `bool(!invoke(pred, invoke(proj, *i)))`. +// +// Returns: The first iterator `i` in `range` for which `E(i)` is `true`. +// Returns `end(range)` if no such iterator is found. +// +// Complexity: At most `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if_not(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto find_if_not(Range&& range, Pred pred, Proj proj = {}) { + return ranges::find_if_not(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// [alg.find.end] Find end +// Reference: https://wg21.link/alg.find.end + +// Let: +// - `E(i,n)` be `invoke(pred, invoke(proj1, *(i + n)), +// invoke(proj2, *(first2 + n)))` +// +// - `i` be `last1` if `[first2, last2)` is empty, or if +// `(last2 - first2) > (last1 - first1)` is `true`, or if there is no iterator +// in the range `[first1, last1 - (last2 - first2))` such that for every +// non-negative integer `n < (last2 - first2)`, `E(i,n)` is `true`. Otherwise +// `i` is the last such iterator in `[first1, last1 - (last2 - first2))`. +// +// Returns: `i` +// Note: std::ranges::find_end(I1 first1,...) returns a range, rather than an +// iterator. For simplicitly we match std::find_end's return type instead. +// +// Complexity: +// At most `(last2 - first2) * (last1 - first1 - (last2 - first2) + 1)` +// applications of the corresponding predicate and any projections. +// +// Reference: https://wg21.link/alg.find.end#:~:text=ranges::find_end(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>, + typename = indirect_result_t<Pred&, + projected<ForwardIterator1, Proj1>, + projected<ForwardIterator2, Proj2>>> +constexpr auto find_end(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return std::find_end(first1, last1, first2, last2, + internal::ProjectedBinaryPredicate(pred, proj1, proj2)); +} + +// Let: +// - `E(i,n)` be `invoke(pred, invoke(proj1, *(i + n)), +// invoke(proj2, *(first2 + n)))` +// +// - `i` be `end(range1)` if `range2` is empty, or if +// `size(range2) > size(range1)` is `true`, or if there is no iterator in the +// range `[begin(range1), end(range1) - size(range2))` such that for every +// non-negative integer `n < size(range2)`, `E(i,n)` is `true`. Otherwise `i` +// is the last such iterator in `[begin(range1), end(range1) - size(range2))`. +// +// Returns: `i` +// Note: std::ranges::find_end(R1&& r1,...) returns a range, rather than an +// iterator. For simplicitly we match std::find_end's return type instead. +// +// Complexity: At most `size(range2) * (size(range1) - size(range2) + 1)` +// applications of the corresponding predicate and any projections. +// +// Reference: https://wg21.link/alg.find.end#:~:text=ranges::find_end(R1 +template <typename Range1, + typename Range2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Pred&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>> +constexpr auto find_end(Range1&& range1, + Range2&& range2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::find_end(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), + std::move(pred), std::move(proj1), std::move(proj2)); +} + +// [alg.find.first.of] Find first +// Reference: https://wg21.link/alg.find.first.of + +// Let `E(i,j)` be `bool(invoke(pred, invoke(proj1, *i), invoke(proj2, *j)))`. +// +// Effects: Finds an element that matches one of a set of values. +// +// Returns: The first iterator `i` in the range `[first1, last1)` such that for +// some iterator `j` in the range `[first2, last2)` `E(i,j)` holds. Returns +// `last1` if `[first2, last2)` is empty or if no such iterator is found. +// +// Complexity: At most `(last1 - first1) * (last2 - first2)` applications of the +// corresponding predicate and any projections. +// +// Reference: +// https://wg21.link/alg.find.first.of#:~:text=ranges::find_first_of(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>, + typename = indirect_result_t<Pred&, + projected<ForwardIterator1, Proj1>, + projected<ForwardIterator2, Proj2>>> +constexpr auto find_first_of(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return std::find_first_of( + first1, last1, first2, last2, + internal::ProjectedBinaryPredicate(pred, proj1, proj2)); +} + +// Let `E(i,j)` be `bool(invoke(pred, invoke(proj1, *i), invoke(proj2, *j)))`. +// +// Effects: Finds an element that matches one of a set of values. +// +// Returns: The first iterator `i` in `range1` such that for some iterator `j` +// in `range2` `E(i,j)` holds. Returns `end(range1)` if `range2` is empty or if +// no such iterator is found. +// +// Complexity: At most `size(range1) * size(range2)` applications of the +// corresponding predicate and any projections. +// +// Reference: +// https://wg21.link/alg.find.first.of#:~:text=ranges::find_first_of(R1 +template <typename Range1, + typename Range2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Pred&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>> +constexpr auto find_first_of(Range1&& range1, + Range2&& range2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::find_first_of( + ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), std::move(pred), std::move(proj1), std::move(proj2)); +} + +// [alg.adjacent.find] Adjacent find +// Reference: https://wg21.link/alg.adjacent.find + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))`. +// +// Returns: The first iterator `i` such that both `i` and `i + 1` are in the +// range `[first, last)` for which `E(i)` holds. Returns `last` if no such +// iterator is found. +// +// Complexity: Exactly `min((i - first) + 1, (last - first) - 1)` applications +// of the corresponding predicate, where `i` is `adjacent_find`'s return value. +// +// Reference: +// https://wg21.link/alg.adjacent.find#:~:text=ranges::adjacent_find(I +template <typename ForwardIterator, + typename Pred = ranges::equal_to, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto adjacent_find(ForwardIterator first, + ForwardIterator last, + Pred pred = {}, + Proj proj = {}) { + // Implementation inspired by cppreference.com: + // https://en.cppreference.com/w/cpp/algorithm/adjacent_find + // + // A reimplementation is required, because std::adjacent_find is not constexpr + // prior to C++20. Once we have C++20, we should switch to standard library + // implementation. + if (first == last) + return last; + + for (ForwardIterator next = first; ++next != last; ++first) { + if (gurl_base::invoke(pred, gurl_base::invoke(proj, *first), + gurl_base::invoke(proj, *next))) { + return first; + } + } + + return last; +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))`. +// +// Returns: The first iterator `i` such that both `i` and `i + 1` are in the +// range `range` for which `E(i)` holds. Returns `end(range)` if no such +// iterator is found. +// +// Complexity: Exactly `min((i - begin(range)) + 1, size(range) - 1)` +// applications of the corresponding predicate, where `i` is `adjacent_find`'s +// return value. +// +// Reference: +// https://wg21.link/alg.adjacent.find#:~:text=ranges::adjacent_find(R +template <typename Range, + typename Pred = ranges::equal_to, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto adjacent_find(Range&& range, Pred pred = {}, Proj proj = {}) { + return ranges::adjacent_find(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// [alg.count] Count +// Reference: https://wg21.link/alg.count + +// Let `E(i)` be `invoke(proj, *i) == value`. +// +// Effects: Returns the number of iterators `i` in the range `[first, last)` for +// which `E(i)` holds. +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.count#:~:text=ranges::count(I +template <typename InputIterator, + typename T, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr auto count(InputIterator first, + InputIterator last, + const T& value, + Proj proj = {}) { + // Note: In order to be able to apply `proj` to each element in [first, last) + // we are dispatching to std::count_if instead of std::count. + return std::count_if(first, last, [&proj, &value](auto&& lhs) { + return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value; + }); +} + +// Let `E(i)` be `invoke(proj, *i) == value`. +// +// Effects: Returns the number of iterators `i` in `range` for which `E(i)` +// holds. +// +// Complexity: Exactly `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.count#:~:text=ranges::count(R +template <typename Range, + typename T, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto count(Range&& range, const T& value, Proj proj = {}) { + return ranges::count(ranges::begin(range), ranges::end(range), value, + std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Effects: Returns the number of iterators `i` in the range `[first, last)` for +// which `E(i)` holds. +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.count#:~:text=ranges::count_if(I +template <typename InputIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>> +constexpr auto count_if(InputIterator first, + InputIterator last, + Pred pred, + Proj proj = {}) { + return std::count_if(first, last, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Effects: Returns the number of iterators `i` in `range` for which `E(i)` +// holds. +// +// Complexity: Exactly `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.count#:~:text=ranges::count_if(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto count_if(Range&& range, Pred pred, Proj proj = {}) { + return ranges::count_if(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// [mismatch] Mismatch +// Reference: https://wg21.link/mismatch + +// Let `E(n)` be `!invoke(pred, invoke(proj1, *(first1 + n)), +// invoke(proj2, *(first2 + n)))`. +// +// Let `N` be `min(last1 - first1, last2 - first2)`. +// +// Returns: `{ first1 + n, first2 + n }`, where `n` is the smallest integer in +// `[0, N)` such that `E(n)` holds, or `N` if no such integer exists. +// +// Complexity: At most `N` applications of the corresponding predicate and any +// projections. +// +// Reference: https://wg21.link/mismatch#:~:text=ranges::mismatch(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>, + typename = indirect_result_t<Pred&, + projected<ForwardIterator1, Proj1>, + projected<ForwardIterator2, Proj2>>> +constexpr auto mismatch(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return std::mismatch(first1, last1, first2, last2, + internal::ProjectedBinaryPredicate(pred, proj1, proj2)); +} + +// Let `E(n)` be `!invoke(pred, invoke(proj1, *(begin(range1) + n)), +// invoke(proj2, *(begin(range2) + n)))`. +// +// Let `N` be `min(size(range1), size(range2))`. +// +// Returns: `{ begin(range1) + n, begin(range2) + n }`, where `n` is the +// smallest integer in `[0, N)` such that `E(n)` holds, or `N` if no such +// integer exists. +// +// Complexity: At most `N` applications of the corresponding predicate and any +// projections. +// +// Reference: https://wg21.link/mismatch#:~:text=ranges::mismatch(R1 +template <typename Range1, + typename Range2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Pred&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>> +constexpr auto mismatch(Range1&& range1, + Range2&& range2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::mismatch(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), + std::move(pred), std::move(proj1), std::move(proj2)); +} + +// [alg.equal] Equal +// Reference: https://wg21.link/alg.equal + +// Let `E(i)` be +// `invoke(pred, invoke(proj1, *i), invoke(proj2, *(first2 + (i - first1))))`. +// +// Returns: If `last1 - first1 != last2 - first2`, return `false.` Otherwise +// return `true` if `E(i)` holds for every iterator `i` in the range `[first1, +// last1)`. Otherwise, returns `false`. +// +// Complexity: If the types of `first1`, `last1`, `first2`, and `last2` meet the +// `RandomAccessIterator` requirements and `last1 - first1 != last2 - first2`, +// then no applications of the corresponding predicate and each projection; +// otherwise, at most `min(last1 - first1, last2 - first2)` applications of the +// corresponding predicate and any projections. +// +// Reference: https://wg21.link/alg.equal#:~:text=ranges::equal(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>, + typename = indirect_result_t<Pred&, + projected<ForwardIterator1, Proj1>, + projected<ForwardIterator2, Proj2>>> +constexpr bool equal(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return std::equal(first1, last1, first2, last2, + internal::ProjectedBinaryPredicate(pred, proj1, proj2)); +} + +// Let `E(i)` be +// `invoke(pred, invoke(proj1, *i), +// invoke(proj2, *(begin(range2) + (i - begin(range1)))))`. +// +// Returns: If `size(range1) != size(range2)`, return `false.` Otherwise return +// `true` if `E(i)` holds for every iterator `i` in `range1`. Otherwise, returns +// `false`. +// +// Complexity: If the types of `begin(range1)`, `end(range1)`, `begin(range2)`, +// and `end(range2)` meet the `RandomAccessIterator` requirements and +// `size(range1) != size(range2)`, then no applications of the corresponding +// predicate and each projection; +// otherwise, at most `min(size(range1), size(range2))` applications of the +// corresponding predicate and any projections. +// +// Reference: https://wg21.link/alg.equal#:~:text=ranges::equal(R1 +template <typename Range1, + typename Range2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Pred&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>> +constexpr bool equal(Range1&& range1, + Range2&& range2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::equal(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), + std::move(pred), std::move(proj1), std::move(proj2)); +} + +// [alg.is.permutation] Is permutation +// Reference: https://wg21.link/alg.is.permutation + +// Returns: If `last1 - first1 != last2 - first2`, return `false`. Otherwise +// return `true` if there exists a permutation of the elements in the range +// `[first2, last2)`, bounded by `[pfirst, plast)`, such that +// `ranges::equal(first1, last1, pfirst, plast, pred, proj, proj)` returns +// `true`; otherwise, returns `false`. +// +// Complexity: No applications of the corresponding predicate if +// ForwardIterator1 and ForwardIterator2 meet the requirements of random access +// iterators and `last1 - first1 != last2 - first2`. Otherwise, exactly +// `last1 - first1` applications of the corresponding predicate and projections +// if `ranges::equal(first1, last1, first2, last2, pred, proj, proj)` would +// return true; +// otherwise, at worst `O(N^2)`, where `N` has the value `last1 - first1`. +// +// Reference: +// https://wg21.link/alg.is.permutation#:~:text=ranges::is_permutation(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>, + typename = indirect_result_t<Pred&, + projected<ForwardIterator1, Proj1>, + projected<ForwardIterator2, Proj2>>> +constexpr bool is_permutation(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // Needs to opt-in to all permutations, since std::is_permutation expects + // pred(proj1(lhs), proj1(rhs)) to compile. + return std::is_permutation( + first1, last1, first2, last2, + internal::PermutedProjectedBinaryPredicate(pred, proj1, proj2)); +} + +// Returns: If `size(range1) != size(range2)`, return `false`. Otherwise return +// `true` if there exists a permutation of the elements in `range2`, bounded by +// `[pbegin, pend)`, such that +// `ranges::equal(range1, [pbegin, pend), pred, proj, proj)` returns `true`; +// otherwise, returns `false`. +// +// Complexity: No applications of the corresponding predicate if Range1 and +// Range2 meet the requirements of random access ranges and +// `size(range1) != size(range2)`. Otherwise, exactly `size(range1)` +// applications of the corresponding predicate and projections if +// `ranges::equal(range1, range2, pred, proj, proj)` would return true; +// otherwise, at worst `O(N^2)`, where `N` has the value `size(range1)`. +// +// Reference: +// https://wg21.link/alg.is.permutation#:~:text=ranges::is_permutation(R1 +template <typename Range1, + typename Range2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Pred&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>> +constexpr bool is_permutation(Range1&& range1, + Range2&& range2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::is_permutation( + ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), std::move(pred), std::move(proj1), std::move(proj2)); +} + +// [alg.search] Search +// Reference: https://wg21.link/alg.search + +// Returns: `i`, where `i` is the first iterator in the range +// `[first1, last1 - (last2 - first2))` such that for every non-negative integer +// `n` less than `last2 - first2` the condition +// `bool(invoke(pred, invoke(proj1, *(i + n)), invoke(proj2, *(first2 + n))))` +// is `true`. +// Returns `last1` if no such iterator exists. +// Note: std::ranges::search(I1 first1,...) returns a range, rather than an +// iterator. For simplicitly we match std::search's return type instead. +// +// Complexity: At most `(last1 - first1) * (last2 - first2)` applications of the +// corresponding predicate and projections. +// +// Reference: https://wg21.link/alg.search#:~:text=ranges::search(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>, + typename = indirect_result_t<Pred&, + projected<ForwardIterator1, Proj1>, + projected<ForwardIterator2, Proj2>>> +constexpr auto search(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return std::search(first1, last1, first2, last2, + internal::ProjectedBinaryPredicate(pred, proj1, proj2)); +} + +// Returns: `i`, where `i` is the first iterator in the range +// `[begin(range1), end(range1) - size(range2))` such that for every +// non-negative integer `n` less than `size(range2)` the condition +// `bool(invoke(pred, invoke(proj1, *(i + n)), +// invoke(proj2, *(begin(range2) + n))))` is `true`. +// Returns `end(range1)` if no such iterator exists. +// Note: std::ranges::search(R1&& r1,...) returns a range, rather than an +// iterator. For simplicitly we match std::search's return type instead. +// +// Complexity: At most `size(range1) * size(range2)` applications of the +// corresponding predicate and projections. +// +// Reference: https://wg21.link/alg.search#:~:text=ranges::search(R1 +template <typename Range1, + typename Range2, + typename Pred = ranges::equal_to, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Pred&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>> +constexpr auto search(Range1&& range1, + Range2&& range2, + Pred pred = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::search(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), + std::move(pred), std::move(proj1), std::move(proj2)); +} + +// Mandates: The type `Size` is convertible to an integral type. +// +// Returns: `i` where `i` is the first iterator in the range +// `[first, last - count)` such that for every non-negative integer `n` less +// than `count`, the following condition holds: +// `invoke(pred, invoke(proj, *(i + n)), value)`. +// Returns `last` if no such iterator is found. +// Note: std::ranges::search_n(I1 first1,...) returns a range, rather than an +// iterator. For simplicitly we match std::search_n's return type instead. +// +// Complexity: At most `last - first` applications of the corresponding +// predicate and projection. +// +// Reference: https://wg21.link/alg.search#:~:text=ranges::search_n(I +template <typename ForwardIterator, + typename Size, + typename T, + typename Pred = ranges::equal_to, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto search_n(ForwardIterator first, + ForwardIterator last, + Size count, + const T& value, + Pred pred = {}, + Proj proj = {}) { + // The second arg is guaranteed to be `value`, so we'll simply apply the + // identity projection. + identity value_proj; + return std::search_n( + first, last, count, value, + internal::ProjectedBinaryPredicate(pred, proj, value_proj)); +} + +// Mandates: The type `Size` is convertible to an integral type. +// +// Returns: `i` where `i` is the first iterator in the range +// `[begin(range), end(range) - count)` such that for every non-negative integer +// `n` less than `count`, the following condition holds: +// `invoke(pred, invoke(proj, *(i + n)), value)`. +// Returns `end(arnge)` if no such iterator is found. +// Note: std::ranges::search_n(R1&& r1,...) returns a range, rather than an +// iterator. For simplicitly we match std::search_n's return type instead. +// +// Complexity: At most `size(range)` applications of the corresponding predicate +// and projection. +// +// Reference: https://wg21.link/alg.search#:~:text=ranges::search_n(R +template <typename Range, + typename Size, + typename T, + typename Pred = ranges::equal_to, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto search_n(Range&& range, + Size count, + const T& value, + Pred pred = {}, + Proj proj = {}) { + return ranges::search_n(ranges::begin(range), ranges::end(range), count, + value, std::move(pred), std::move(proj)); +} + +// [alg.modifying.operations] Mutating sequence operations +// Reference: https://wg21.link/alg.modifying.operations + +// [alg.copy] Copy +// Reference: https://wg21.link/alg.copy + +// Let N be `last - first`. +// +// Preconditions: `result` is not in the range `[first, last)`. +// +// Effects: Copies elements in the range `[first, last)` into the range +// `[result, result + N)` starting from `first` and proceeding to `last`. For +// each non-negative integer `n < N` , performs `*(result + n) = *(first + n)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy(I +template <typename InputIterator, + typename OutputIterator, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto copy(InputIterator first, + InputIterator last, + OutputIterator result) { + return std::copy(first, last, result); +} + +// Let N be `size(range)`. +// +// Preconditions: `result` is not in `range`. +// +// Effects: Copies elements in `range` into the range `[result, result + N)` +// starting from `begin(range)` and proceeding to `end(range)`. For each +// non-negative integer `n < N` , performs +// *(result + n) = *(begin(range) + n)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy(R +template <typename Range, + typename OutputIterator, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto copy(Range&& range, OutputIterator result) { + return ranges::copy(ranges::begin(range), ranges::end(range), result); +} + +// Let `N` be `max(0, n)`. +// +// Mandates: The type `Size` is convertible to an integral type. +// +// Effects: For each non-negative integer `i < N`, performs +// `*(result + i) = *(first + i)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_n +template <typename InputIterator, + typename Size, + typename OutputIterator, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto copy_n(InputIterator first, Size n, OutputIterator result) { + return std::copy_n(first, n, result); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`, and `N` be the number +// of iterators `i` in the range `[first, last)` for which the condition `E(i)` +// holds. +// +// Preconditions: The ranges `[first, last)` and +// `[result, result + (last - first))` do not overlap. +// +// Effects: Copies all of the elements referred to by the iterator `i` in the +// range `[first, last)` for which `E(i)` is true. +// +// Returns: `result + N` +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_if(I +template <typename InputIterator, + typename OutputIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto copy_if(InputIterator first, + InputIterator last, + OutputIterator result, + Pred pred, + Proj proj = {}) { + return std::copy_if(first, last, result, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`, and `N` be the number +// of iterators `i` in `range` for which the condition `E(i)` holds. +// +// Preconditions: `range` and `[result, result + size(range))` do not overlap. +// +// Effects: Copies all of the elements referred to by the iterator `i` in +// `range` for which `E(i)` is true. +// +// Returns: `result + N` +// +// Complexity: Exactly `size(range)` applications of the corresponding predicate +// and any projection. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_if(R +template <typename Range, + typename OutputIterator, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto copy_if(Range&& range, + OutputIterator result, + Pred pred, + Proj proj = {}) { + return ranges::copy_if(ranges::begin(range), ranges::end(range), result, + std::move(pred), std::move(proj)); +} + +// Let `N` be `last - first`. +// +// Preconditions: `result` is not in the range `(first, last]`. +// +// Effects: Copies elements in the range `[first, last)` into the range +// `[result - N, result)` starting from `last - 1` and proceeding to `first`. +// For each positive integer `n ≤ N`, performs `*(result - n) = *(last - n)`. +// +// Returns: `result - N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_backward(I1 +template <typename BidirectionalIterator1, + typename BidirectionalIterator2, + typename = internal::iterator_category_t<BidirectionalIterator1>, + typename = internal::iterator_category_t<BidirectionalIterator2>> +constexpr auto copy_backward(BidirectionalIterator1 first, + BidirectionalIterator1 last, + BidirectionalIterator2 result) { + return std::copy_backward(first, last, result); +} + +// Let `N` be `size(range)`. +// +// Preconditions: `result` is not in the range `(begin(range), end(range)]`. +// +// Effects: Copies elements in `range` into the range `[result - N, result)` +// starting from `end(range) - 1` and proceeding to `begin(range)`. For each +// positive integer `n ≤ N`, performs `*(result - n) = *(end(range) - n)`. +// +// Returns: `result - N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_backward(R +template <typename Range, + typename BidirectionalIterator, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<BidirectionalIterator>> +constexpr auto copy_backward(Range&& range, BidirectionalIterator result) { + return ranges::copy_backward(ranges::begin(range), ranges::end(range), + result); +} + +// [alg.move] Move +// Reference: https://wg21.link/alg.move + +// Let `E(n)` be `std::move(*(first + n))`. +// +// Let `N` be `last - first`. +// +// Preconditions: `result` is not in the range `[first, last)`. +// +// Effects: Moves elements in the range `[first, last)` into the range `[result, +// result + N)` starting from `first` and proceeding to `last`. For each +// non-negative integer `n < N`, performs `*(result + n) = E(n)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.move#:~:text=ranges::move(I +template <typename InputIterator, + typename OutputIterator, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto move(InputIterator first, + InputIterator last, + OutputIterator result) { + return std::move(first, last, result); +} + +// Let `E(n)` be `std::move(*(begin(range) + n))`. +// +// Let `N` be `size(range)`. +// +// Preconditions: `result` is not in `range`. +// +// Effects: Moves elements in `range` into the range `[result, result + N)` +// starting from `begin(range)` and proceeding to `end(range)`. For each +// non-negative integer `n < N`, performs `*(result + n) = E(n)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.move#:~:text=ranges::move(R +template <typename Range, + typename OutputIterator, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto move(Range&& range, OutputIterator result) { + return ranges::move(ranges::begin(range), ranges::end(range), result); +} + +// Let `E(n)` be `std::move(*(last - n))`. +// +// Let `N` be `last - first`. +// +// Preconditions: `result` is not in the range `(first, last]`. +// +// Effects: Moves elements in the range `[first, last)` into the range +// `[result - N, result)` starting from `last - 1` and proceeding to `first`. +// For each positive integer `n ≤ N`, performs `*(result - n) = E(n)`. +// +// Returns: `result - N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.move#:~:text=ranges::move_backward(I1 +template <typename BidirectionalIterator1, + typename BidirectionalIterator2, + typename = internal::iterator_category_t<BidirectionalIterator1>, + typename = internal::iterator_category_t<BidirectionalIterator2>> +constexpr auto move_backward(BidirectionalIterator1 first, + BidirectionalIterator1 last, + BidirectionalIterator2 result) { + return std::move_backward(first, last, result); +} + +// Let `E(n)` be `std::move(*(end(range) - n))`. +// +// Let `N` be `size(range)`. +// +// Preconditions: `result` is not in the range `(begin(range), end(range)]`. +// +// Effects: Moves elements in `range` into the range `[result - N, result)` +// starting from `end(range) - 1` and proceeding to `begin(range)`. For each +// positive integer `n ≤ N`, performs `*(result - n) = E(n)`. +// +// Returns: `result - N` +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.move#:~:text=ranges::move_backward(R +template <typename Range, + typename BidirectionalIterator, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<BidirectionalIterator>> +constexpr auto move_backward(Range&& range, BidirectionalIterator result) { + return ranges::move_backward(ranges::begin(range), ranges::end(range), + result); +} + +// [alg.swap] Swap +// Reference: https://wg21.link/alg.swap + +// Let `M` be `min(last1 - first1, last2 - first2)`. +// +// Preconditions: The two ranges `[first1, last1)` and `[first2, last2)` do not +// overlap. `*(first1 + n)` is swappable with `*(first2 + n)`. +// +// Effects: For each non-negative integer `n < M` performs +// `swap(*(first1 + n), *(first2 + n))` +// +// Returns: `first2 + M` +// +// Complexity: Exactly `M` swaps. +// +// Reference: https://wg21.link/alg.swap#:~:text=ranges::swap_ranges(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>> +constexpr auto swap_ranges(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2) { + // std::swap_ranges does not have a `last2` overload. Thus we need to + // adjust `last1` to ensure to not read past `last2`. + last1 = std::next(first1, std::min(std::distance(first1, last1), + std::distance(first2, last2))); + return std::swap_ranges(first1, last1, first2); +} + +// Let `M` be `min(size(range1), size(range2))`. +// +// Preconditions: The two ranges `range1` and `range2` do not overlap. +// `*(begin(range1) + n)` is swappable with `*(begin(range2) + n)`. +// +// Effects: For each non-negative integer `n < M` performs +// `swap(*(begin(range1) + n), *(begin(range2) + n))` +// +// Returns: `begin(range2) + M` +// +// Complexity: Exactly `M` swaps. +// +// Reference: https://wg21.link/alg.swap#:~:text=ranges::swap_ranges(R1 +template <typename Range1, + typename Range2, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>> +constexpr auto swap_ranges(Range1&& range1, Range2&& range2) { + return ranges::swap_ranges(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2)); +} + +// [alg.transform] Transform +// Reference: https://wg21.link/alg.transform + +// Let `N` be `last1 - first1`, +// `E(i)` be `invoke(op, invoke(proj, *(first1 + (i - result))))`. +// +// Preconditions: `op` does not invalidate iterators or subranges, nor modify +// elements in the ranges `[first1, first1 + N]`, and `[result, result + N]`. +// +// Effects: Assigns through every iterator `i` in the range +// `[result, result + N)` a new corresponding value equal to `E(i)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` applications of `op` and any projections. +// +// Remarks: result may be equal to `first1`. +// +// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(I +template <typename InputIterator, + typename OutputIterator, + typename UnaryOperation, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<UnaryOperation&, + projected<InputIterator, Proj>>> +constexpr auto transform(InputIterator first1, + InputIterator last1, + OutputIterator result, + UnaryOperation op, + Proj proj = {}) { + return std::transform(first1, last1, result, [&op, &proj](auto&& arg) { + return gurl_base::invoke(op, + gurl_base::invoke(proj, std::forward<decltype(arg)>(arg))); + }); +} + +// Let `N` be `size(range)`, +// `E(i)` be `invoke(op, invoke(proj, *(begin(range) + (i - result))))`. +// +// Preconditions: `op` does not invalidate iterators or subranges, nor modify +// elements in the ranges `[begin(range), end(range)]`, and +// `[result, result + N]`. +// +// Effects: Assigns through every iterator `i` in the range +// `[result, result + N)` a new corresponding value equal to `E(i)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` applications of `op` and any projections. +// +// Remarks: result may be equal to `begin(range)`. +// +// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(R +template <typename Range, + typename OutputIterator, + typename UnaryOperation, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<UnaryOperation&, + projected<iterator_t<Range>, Proj>>> +constexpr auto transform(Range&& range, + OutputIterator result, + UnaryOperation op, + Proj proj = {}) { + return ranges::transform(ranges::begin(range), ranges::end(range), result, + std::move(op), std::move(proj)); +} + +// Let: +// `N` be `min(last1 - first1, last2 - first2)`, +// `E(i)` be `invoke(binary_op, invoke(proj1, *(first1 + (i - result))), +// invoke(proj2, *(first2 + (i - result))))`. +// +// Preconditions: `binary_op` does not invalidate iterators or subranges, nor +// modify elements in the ranges `[first1, first1 + N]`, `[first2, first2 + N]`, +// and `[result, result + N]`. +// +// Effects: Assigns through every iterator `i` in the range +// `[result, result + N)` a new corresponding value equal to `E(i)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` applications of `binary_op`, and any projections. +// +// Remarks: `result` may be equal to `first1` or `first2`. +// +// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename OutputIterator, + typename BinaryOperation, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<BinaryOperation&, + projected<ForwardIterator1, Proj1>, + projected<ForwardIterator2, Proj2>>> +constexpr auto transform(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2, + OutputIterator result, + BinaryOperation binary_op, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // std::transform does not have a `last2` overload. Thus we need to adjust + // `last1` to ensure to not read past `last2`. + last1 = std::next(first1, std::min(std::distance(first1, last1), + std::distance(first2, last2))); + return std::transform( + first1, last1, first2, result, + [&binary_op, &proj1, &proj2](auto&& lhs, auto&& rhs) { + return gurl_base::invoke( + binary_op, gurl_base::invoke(proj1, std::forward<decltype(lhs)>(lhs)), + gurl_base::invoke(proj2, std::forward<decltype(rhs)>(rhs))); + }); +} + +// Let: +// `N` be `min(size(range1), size(range2)`, +// `E(i)` be `invoke(binary_op, invoke(proj1, *(begin(range1) + (i - result))), +// invoke(proj2, *(begin(range2) + (i - result))))` +// +// Preconditions: `binary_op` does not invalidate iterators or subranges, nor +// modify elements in the ranges `[begin(range1), end(range1)]`, +// `[begin(range2), end(range2)]`, and `[result, result + N]`. +// +// Effects: Assigns through every iterator `i` in the range +// `[result, result + N)` a new corresponding value equal to `E(i)`. +// +// Returns: `result + N` +// +// Complexity: Exactly `N` applications of `binary_op`, and any projections. +// +// Remarks: `result` may be equal to `begin(range1)` or `begin(range2)`. +// +// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(R1 +template <typename Range1, + typename Range2, + typename OutputIterator, + typename BinaryOperation, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<BinaryOperation&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>> +constexpr auto transform(Range1&& range1, + Range2&& range2, + OutputIterator result, + BinaryOperation binary_op, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::transform(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), result, + std::move(binary_op), std::move(proj1), + std::move(proj2)); +} + +// [alg.replace] Replace +// Reference: https://wg21.link/alg.replace + +// Let `E(i)` be `bool(invoke(proj, *i) == old_value)`. +// +// Mandates: `new_value` is writable to `first`. +// +// Effects: Substitutes elements referred by the iterator `i` in the range +// `[first, last)` with `new_value`, when `E(i)` is true. +// +// Returns: `last` +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace(I +template <typename ForwardIterator, + typename T, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto replace(ForwardIterator first, + ForwardIterator last, + const T& old_value, + const T& new_value, + Proj proj = {}) { + // Note: In order to be able to apply `proj` to each element in [first, last) + // we are dispatching to std::replace_if instead of std::replace. + std::replace_if( + first, last, + [&proj, &old_value](auto&& lhs) { + return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == + old_value; + }, + new_value); + return last; +} + +// Let `E(i)` be `bool(invoke(proj, *i) == old_value)`. +// +// Mandates: `new_value` is writable to `begin(range)`. +// +// Effects: Substitutes elements referred by the iterator `i` in `range` with +// `new_value`, when `E(i)` is true. +// +// Returns: `end(range)` +// +// Complexity: Exactly `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace(R +template <typename Range, + typename T, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto replace(Range&& range, + const T& old_value, + const T& new_value, + Proj proj = {}) { + return ranges::replace(ranges::begin(range), ranges::end(range), old_value, + new_value, std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Mandates: `new_value` is writable to `first`. +// +// Effects: Substitutes elements referred by the iterator `i` in the range +// `[first, last)` with `new_value`, when `E(i)` is true. +// +// Returns: `last` +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_if(I +template <typename ForwardIterator, + typename Predicate, + typename T, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto replace_if(ForwardIterator first, + ForwardIterator last, + Predicate pred, + const T& new_value, + Proj proj = {}) { + std::replace_if(first, last, internal::ProjectedUnaryPredicate(pred, proj), + new_value); + return last; +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Mandates: `new_value` is writable to `begin(range)`. +// +// Effects: Substitutes elements referred by the iterator `i` in `range` with +// `new_value`, when `E(i)` is true. +// +// Returns: `end(range)` +// +// Complexity: Exactly `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_if(R +template <typename Range, + typename Predicate, + typename T, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto replace_if(Range&& range, + Predicate pred, + const T& new_value, + Proj proj = {}) { + return ranges::replace_if(ranges::begin(range), ranges::end(range), + std::move(pred), new_value, std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(proj, *(first + (i - result))) == old_value)`. +// +// Mandates: The results of the expressions `*first` and `new_value` are +// writable to `result`. +// +// Preconditions: The ranges `[first, last)` and `[result, result + (last - +// first))` do not overlap. +// +// Effects: Assigns through every iterator `i` in the range `[result, result + +// (last - first))` a new corresponding value, `new_value` if `E(i)` is true, or +// `*(first + (i - result))` otherwise. +// +// Returns: `result + (last - first)`. +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy(I +template <typename InputIterator, + typename OutputIterator, + typename T, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto replace_copy(InputIterator first, + InputIterator last, + OutputIterator result, + const T& old_value, + const T& new_value, + Proj proj = {}) { + // Note: In order to be able to apply `proj` to each element in [first, last) + // we are dispatching to std::replace_copy_if instead of std::replace_copy. + std::replace_copy_if( + first, last, result, + [&proj, &old_value](auto&& lhs) { + return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == + old_value; + }, + new_value); + return last; +} + +// Let `E(i)` be +// `bool(invoke(proj, *(begin(range) + (i - result))) == old_value)`. +// +// Mandates: The results of the expressions `*begin(range)` and `new_value` are +// writable to `result`. +// +// Preconditions: The ranges `range` and `[result, result + size(range))` do not +// overlap. +// +// Effects: Assigns through every iterator `i` in the range `[result, result + +// size(range))` a new corresponding value, `new_value` if `E(i)` is true, or +// `*(begin(range) + (i - result))` otherwise. +// +// Returns: `result + size(range)`. +// +// Complexity: Exactly `size(range)` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy(R +template <typename Range, + typename OutputIterator, + typename T, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto replace_copy(Range&& range, + OutputIterator result, + const T& old_value, + const T& new_value, + Proj proj = {}) { + return ranges::replace_copy(ranges::begin(range), ranges::end(range), result, + old_value, new_value, std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *(first + (i - result)))))`. +// +// Mandates: The results of the expressions `*first` and `new_value` are +// writable to `result`. +// +// Preconditions: The ranges `[first, last)` and `[result, result + (last - +// first))` do not overlap. +// +// Effects: Assigns through every iterator `i` in the range `[result, result + +// (last - first))` a new corresponding value, `new_value` if `E(i)` is true, or +// `*(first + (i - result))` otherwise. +// +// Returns: `result + (last - first)`. +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy_if(I +template <typename InputIterator, + typename OutputIterator, + typename Predicate, + typename T, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto replace_copy_if(InputIterator first, + InputIterator last, + OutputIterator result, + Predicate pred, + const T& new_value, + Proj proj = {}) { + return std::replace_copy_if(first, last, result, + internal::ProjectedUnaryPredicate(pred, proj), + new_value); +} + +// Let `E(i)` be +// `bool(invoke(pred, invoke(proj, *(begin(range) + (i - result)))))`. +// +// Mandates: The results of the expressions `*begin(range)` and `new_value` are +// writable to `result`. +// +// Preconditions: The ranges `range` and `[result, result + size(range))` do not +// overlap. +// +// Effects: Assigns through every iterator `i` in the range `[result, result + +// size(range))` a new corresponding value, `new_value` if `E(i)` is true, or +// `*(begin(range) + (i - result))` otherwise. +// +// Returns: `result + size(range)`. +// +// Complexity: Exactly `size(range)` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy_if(R +template <typename Range, + typename OutputIterator, + typename Predicate, + typename T, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto replace_copy_if(Range&& range, + OutputIterator result, + Predicate pred, + const T& new_value, + Proj proj = {}) { + return ranges::replace_copy_if(ranges::begin(range), ranges::end(range), + result, pred, new_value, std::move(proj)); +} + +// [alg.fill] Fill +// Reference: https://wg21.link/alg.fill + +// Let `N` be `last - first`. +// +// Mandates: The expression `value` is writable to the output iterator. +// +// Effects: Assigns `value` through all the iterators in the range +// `[first, last)`. +// +// Returns: `last`. +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill(O +template <typename OutputIterator, + typename T, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto fill(OutputIterator first, OutputIterator last, const T& value) { + std::fill(first, last, value); + return last; +} + +// Let `N` be `size(range)`. +// +// Mandates: The expression `value` is writable to the output iterator. +// +// Effects: Assigns `value` through all the iterators in `range`. +// +// Returns: `end(range)`. +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill(R +template <typename Range, + typename T, + typename = internal::range_category_t<Range>> +constexpr auto fill(Range&& range, const T& value) { + return ranges::fill(ranges::begin(range), ranges::end(range), value); +} + +// Let `N` be `max(0, n)`. +// +// Mandates: The expression `value` is writable to the output iterator. +// The type `Size` is convertible to an integral type. +// +// Effects: Assigns `value` through all the iterators in `[first, first + N)`. +// +// Returns: `first + N`. +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill_n(O +template <typename OutputIterator, + typename Size, + typename T, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto fill_n(OutputIterator first, Size n, const T& value) { + return std::fill_n(first, n, value); +} + +// [alg.generate] Generate +// Reference: https://wg21.link/alg.generate + +// Let `N` be `last - first`. +// +// Effects: Assigns the result of successive evaluations of gen() through each +// iterator in the range `[first, last)`. +// +// Returns: `last`. +// +// Complexity: Exactly `N` evaluations of `gen()` and assignments. +// +// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate(O +template <typename OutputIterator, + typename Generator, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto generate(OutputIterator first, + OutputIterator last, + Generator gen) { + std::generate(first, last, std::move(gen)); + return last; +} + +// Let `N` be `size(range)`. +// +// Effects: Assigns the result of successive evaluations of gen() through each +// iterator in `range`. +// +// Returns: `end(range)`. +// +// Complexity: Exactly `N` evaluations of `gen()` and assignments. +// +// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate(R +template <typename Range, + typename Generator, + typename = internal::range_category_t<Range>> +constexpr auto generate(Range&& range, Generator gen) { + return ranges::generate(ranges::begin(range), ranges::end(range), + std::move(gen)); +} + +// Let `N` be `max(0, n)`. +// +// Mandates: `Size` is convertible to an integral type. +// +// Effects: Assigns the result of successive evaluations of gen() through each +// iterator in the range `[first, first + N)`. +// +// Returns: `first + N`. +// +// Complexity: Exactly `N` evaluations of `gen()` and assignments. +// +// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate_n(O +template <typename OutputIterator, + typename Size, + typename Generator, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto generate_n(OutputIterator first, Size n, Generator gen) { + return std::generate_n(first, n, std::move(gen)); +} + +// [alg.remove] Remove +// Reference: https://wg21.link/alg.remove + +// Let `E(i)` be `bool(invoke(proj, *i) == value)`. +// +// Effects: Eliminates all the elements referred to by iterator `i` in the range +// `[first, last)` for which `E(i)` holds. +// +// Returns: The end of the resulting range. +// +// Remarks: Stable. +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove(I +template <typename ForwardIterator, + typename T, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto remove(ForwardIterator first, + ForwardIterator last, + const T& value, + Proj proj = {}) { + // Note: In order to be able to apply `proj` to each element in [first, last) + // we are dispatching to std::remove_if instead of std::remove. + return std::remove_if(first, last, [&proj, &value](auto&& lhs) { + return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value; + }); +} + +// Let `E(i)` be `bool(invoke(proj, *i) == value)`. +// +// Effects: Eliminates all the elements referred to by iterator `i` in `range` +// for which `E(i)` holds. +// +// Returns: The end of the resulting range. +// +// Remarks: Stable. +// +// Complexity: Exactly `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove(R +template <typename Range, + typename T, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto remove(Range&& range, const T& value, Proj proj = {}) { + return ranges::remove(ranges::begin(range), ranges::end(range), value, + std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Effects: Eliminates all the elements referred to by iterator `i` in the range +// `[first, last)` for which `E(i)` holds. +// +// Returns: The end of the resulting range. +// +// Remarks: Stable. +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_if(I +template <typename ForwardIterator, + typename Predicate, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto remove_if(ForwardIterator first, + ForwardIterator last, + Predicate pred, + Proj proj = {}) { + return std::remove_if(first, last, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Effects: Eliminates all the elements referred to by iterator `i` in `range`. +// +// Returns: The end of the resulting range. +// +// Remarks: Stable. +// +// Complexity: Exactly `size(range)` applications of the corresponding predicate +// and any projection. +// +// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_if(R +template <typename Range, + typename Predicate, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto remove_if(Range&& range, Predicate pred, Proj proj = {}) { + return ranges::remove_if(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(proj, *i) == value)`. +// +// Let `N` be the number of elements in `[first, last)` for which `E(i)` is +// false. +// +// Mandates: `*first` is writable to `result`. +// +// Preconditions: The ranges `[first, last)` and `[result, result + (last - +// first))` do not overlap. +// +// Effects: Copies all the elements referred to by the iterator `i` in the range +// `[first, last)` for which `E(i)` is false. +// +// Returns: `result + N`. +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(I +template <typename InputIterator, + typename OutputIterator, + typename T, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto remove_copy(InputIterator first, + InputIterator last, + OutputIterator result, + const T& value, + Proj proj = {}) { + // Note: In order to be able to apply `proj` to each element in [first, last) + // we are dispatching to std::remove_copy_if instead of std::remove_copy. + return std::remove_copy_if(first, last, result, [&proj, &value](auto&& lhs) { + return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value; + }); +} + +// Let `E(i)` be `bool(invoke(proj, *i) == value)`. +// +// Let `N` be the number of elements in `range` for which `E(i)` is false. +// +// Mandates: `*begin(range)` is writable to `result`. +// +// Preconditions: The ranges `range` and `[result, result + size(range))` do not +// overlap. +// +// Effects: Copies all the elements referred to by the iterator `i` in `range` +// for which `E(i)` is false. +// +// Returns: `result + N`. +// +// Complexity: Exactly `size(range)` applications of the corresponding +// predicate and any projection. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(R +template <typename Range, + typename OutputIterator, + typename T, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto remove_copy(Range&& range, + OutputIterator result, + const T& value, + Proj proj = {}) { + return ranges::remove_copy(ranges::begin(range), ranges::end(range), result, + value, std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Let `N` be the number of elements in `[first, last)` for which `E(i)` is +// false. +// +// Mandates: `*first` is writable to `result`. +// +// Preconditions: The ranges `[first, last)` and `[result, result + (last - +// first))` do not overlap. +// +// Effects: Copies all the elements referred to by the iterator `i` in the range +// `[first, last)` for which `E(i)` is false. +// +// Returns: `result + N`. +// +// Complexity: Exactly `last - first` applications of the corresponding +// predicate and any projection. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy_if(I +template <typename InputIterator, + typename OutputIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto remove_copy_if(InputIterator first, + InputIterator last, + OutputIterator result, + Pred pred, + Proj proj = {}) { + return std::remove_copy_if(first, last, result, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`. +// +// Let `N` be the number of elements in `range` for which `E(i)` is false. +// +// Mandates: `*begin(range)` is writable to `result`. +// +// Preconditions: The ranges `range` and `[result, result + size(range))` do not +// overlap. +// +// Effects: Copies all the elements referred to by the iterator `i` in `range` +// for which `E(i)` is false. +// +// Returns: `result + N`. +// +// Complexity: Exactly `size(range)` applications of the corresponding +// predicate and any projection. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(R +template <typename Range, + typename OutputIterator, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto remove_copy_if(Range&& range, + OutputIterator result, + Pred pred, + Proj proj = {}) { + return ranges::remove_copy_if(ranges::begin(range), ranges::end(range), + result, std::move(pred), std::move(proj)); +} + +// [alg.unique] Unique +// Reference: https://wg21.link/alg.unique + +// Let `E(i)` be `bool(invoke(comp, invoke(proj, *(i - 1)), invoke(proj, *i)))`. +// +// Effects: For a nonempty range, eliminates all but the first element from +// every consecutive group of equivalent elements referred to by the iterator +// `i` in the range `[first + 1, last)` for which `E(i)` is true. +// +// Returns: The end of the resulting range. +// +// Complexity: For nonempty ranges, exactly `(last - first) - 1` applications of +// the corresponding predicate and no more than twice as many applications of +// any projection. +// +// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique(I +template <typename ForwardIterator, + typename Comp = ranges::equal_to, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>, + typename = indirect_result_t<Comp&, + projected<ForwardIterator, Proj>, + projected<ForwardIterator, Proj>>> +constexpr auto unique(ForwardIterator first, + ForwardIterator last, + Comp comp = {}, + Proj proj = {}) { + return std::unique(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Let `E(i)` be `bool(invoke(comp, invoke(proj, *(i - 1)), invoke(proj, *i)))`. +// +// Effects: For a nonempty range, eliminates all but the first element from +// every consecutive group of equivalent elements referred to by the iterator +// `i` in the range `[begin(range) + 1, end(range))` for which `E(i)` is true. +// +// Returns: The end of the resulting range. +// +// Complexity: For nonempty ranges, exactly `size(range) - 1` applications of +// the corresponding predicate and no more than twice as many applications of +// any projection. +// +// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique(R +template <typename Range, + typename Comp = ranges::equal_to, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto unique(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::unique(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// Let `E(i)` be `bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1))))`. +// +// Mandates: `*first` is writable to `result`. +// +// Preconditions: The ranges `[first, last)` and +// `[result, result + (last - first))` do not overlap. +// +// Effects: Copies only the first element from every consecutive group of equal +// elements referred to by the iterator `i` in the range `[first, last)` for +// which `E(i)` holds. +// +// Returns: `result + N`. +// +// Complexity: Exactly `last - first - 1` applications of the corresponding +// predicate and no more than twice as many applications of any projection. +// +// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique_copy(I +template <typename ForwardIterator, + typename OutputIterator, + typename Comp = ranges::equal_to, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto unique_copy(ForwardIterator first, + ForwardIterator last, + OutputIterator result, + Comp comp = {}, + Proj proj = {}) { + return std::unique_copy(first, last, result, + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Let `E(i)` be `bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1))))`. +// +// Mandates: `*begin(range)` is writable to `result`. +// +// Preconditions: The ranges `range` and `[result, result + size(range))` do not +// overlap. +// +// Effects: Copies only the first element from every consecutive group of equal +// elements referred to by the iterator `i` in `range` for which `E(i)` holds. +// +// Returns: `result + N`. +// +// Complexity: Exactly `size(range) - 1` applications of the corresponding +// predicate and no more than twice as many applications of any projection. +// +// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique_copy(R +template <typename Range, + typename OutputIterator, + typename Comp = ranges::equal_to, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto unique_copy(Range&& range, + OutputIterator result, + Comp comp = {}, + Proj proj = {}) { + return ranges::unique_copy(ranges::begin(range), ranges::end(range), result, + std::move(comp), std::move(proj)); +} + +// [alg.reverse] Reverse +// Reference: https://wg21.link/alg.reverse + +// Effects: For each non-negative integer `i < (last - first) / 2`, applies +// `std::iter_swap` to all pairs of iterators `first + i, (last - i) - 1`. +// +// Returns: `last`. +// +// Complexity: Exactly `(last - first)/2` swaps. +// +// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse(I +template <typename BidirectionalIterator, + typename = internal::iterator_category_t<BidirectionalIterator>> +constexpr auto reverse(BidirectionalIterator first, + BidirectionalIterator last) { + std::reverse(first, last); + return last; +} + +// Effects: For each non-negative integer `i < size(range) / 2`, applies +// `std::iter_swap` to all pairs of iterators +// `begin(range) + i, (end(range) - i) - 1`. +// +// Returns: `end(range)`. +// +// Complexity: Exactly `size(range)/2` swaps. +// +// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse(R +template <typename Range, typename = internal::range_category_t<Range>> +constexpr auto reverse(Range&& range) { + return ranges::reverse(ranges::begin(range), ranges::end(range)); +} + +// Let `N` be `last - first`. +// +// Preconditions: The ranges `[first, last)` and `[result, result + N)` do not +// overlap. +// +// Effects: Copies the range `[first, last)` to the range `[result, result + N)` +// such that for every non-negative integer `i < N` the following assignment +// takes place: `*(result + N - 1 - i) = *(first + i)`. +// +// Returns: `result + N`. +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse_copy(I +template <typename BidirectionalIterator, + typename OutputIterator, + typename = internal::iterator_category_t<BidirectionalIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto reverse_copy(BidirectionalIterator first, + BidirectionalIterator last, + OutputIterator result) { + return std::reverse_copy(first, last, result); +} + +// Let `N` be `size(range)`. +// +// Preconditions: The ranges `range` and `[result, result + N)` do not +// overlap. +// +// Effects: Copies `range` to the range `[result, result + N)` such that for +// every non-negative integer `i < N` the following assignment takes place: +// `*(result + N - 1 - i) = *(begin(range) + i)`. +// +// Returns: `result + N`. +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse_copy(R +template <typename Range, + typename OutputIterator, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto reverse_copy(Range&& range, OutputIterator result) { + return ranges::reverse_copy(ranges::begin(range), ranges::end(range), result); +} + +// [alg.rotate] Rotate +// Reference: https://wg21.link/alg.rotate + +// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges. +// +// Effects: For each non-negative integer `i < (last - first)`, places the +// element from the position `first + i` into position +// `first + (i + (last - middle)) % (last - first)`. +// +// Returns: `first + (last - middle)`. +// +// Complexity: At most `last - first` swaps. +// +// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate(I +template <typename ForwardIterator, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto rotate(ForwardIterator first, + ForwardIterator middle, + ForwardIterator last) { + return std::rotate(first, middle, last); +} + +// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid +// ranges. +// +// Effects: For each non-negative integer `i < size(range)`, places the element +// from the position `begin(range) + i` into position +// `begin(range) + (i + (end(range) - middle)) % size(range)`. +// +// Returns: `begin(range) + (end(range) - middle)`. +// +// Complexity: At most `size(range)` swaps. +// +// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate(R +template <typename Range, typename = internal::range_category_t<Range>> +constexpr auto rotate(Range&& range, iterator_t<Range> middle) { + return ranges::rotate(ranges::begin(range), middle, ranges::end(range)); +} + +// Let `N` be `last - first`. +// +// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges. The +// ranges `[first, last)` and `[result, result + N)` do not overlap. +// +// Effects: Copies the range `[first, last)` to the range `[result, result + N)` +// such that for each non-negative integer `i < N` the following assignment +// takes place: `*(result + i) = *(first + (i + (middle - first)) % N)`. +// +// Returns: `result + N`. +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate_copy(I +template <typename ForwardIterator, + typename OutputIterator, + typename = internal::iterator_category_t<ForwardIterator>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto rotate_copy(ForwardIterator first, + ForwardIterator middle, + ForwardIterator last, + OutputIterator result) { + return std::rotate_copy(first, middle, last, result); +} + +// Let `N` be `size(range)`. +// +// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid +// ranges. The ranges `range` and `[result, result + N)` do not overlap. +// +// Effects: Copies `range` to the range `[result, result + N)` such that for +// each non-negative integer `i < N` the following assignment takes place: +// `*(result + i) = *(begin(range) + (i + (middle - begin(range))) % N)`. +// +// Returns: `result + N`. +// +// Complexity: Exactly `N` assignments. +// +// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate_copy(R +template <typename Range, + typename OutputIterator, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator>> +constexpr auto rotate_copy(Range&& range, + iterator_t<Range> middle, + OutputIterator result) { + return ranges::rotate_copy(ranges::begin(range), middle, ranges::end(range), + result); +} + +// [alg.random.sample] Sample +// Reference: https://wg21.link/alg.random.sample + +// Currently not implemented due to lack of std::sample in C++14. +// TODO(crbug.com/1071094): Consider implementing a hand-rolled version. + +// [alg.random.shuffle] Shuffle +// Reference: https://wg21.link/alg.random.shuffle + +// Preconditions: The type `std::remove_reference_t<UniformRandomBitGenerator>` +// meets the uniform random bit generator requirements. +// +// Effects: Permutes the elements in the range `[first, last)` such that each +// possible permutation of those elements has equal probability of appearance. +// +// Returns: `last`. +// +// Complexity: Exactly `(last - first) - 1` swaps. +// +// Remarks: To the extent that the implementation of this function makes use of +// random numbers, the object referenced by g shall serve as the +// implementation's source of randomness. +// +// Reference: https://wg21.link/alg.random.shuffle#:~:text=ranges::shuffle(I +template <typename RandomAccessIterator, + typename UniformRandomBitGenerator, + typename = internal::iterator_category_t<RandomAccessIterator>> +constexpr auto shuffle(RandomAccessIterator first, + RandomAccessIterator last, + UniformRandomBitGenerator&& g) { + std::shuffle(first, last, std::forward<UniformRandomBitGenerator>(g)); + return last; +} + +// Preconditions: The type `std::remove_reference_t<UniformRandomBitGenerator>` +// meets the uniform random bit generator requirements. +// +// Effects: Permutes the elements in `range` such that each possible permutation +// of those elements has equal probability of appearance. +// +// Returns: `end(range)`. +// +// Complexity: Exactly `size(range) - 1` swaps. +// +// Remarks: To the extent that the implementation of this function makes use of +// random numbers, the object referenced by g shall serve as the +// implementation's source of randomness. +// +// Reference: https://wg21.link/alg.random.shuffle#:~:text=ranges::shuffle(R +template <typename Range, + typename UniformRandomBitGenerator, + typename = internal::range_category_t<Range>> +constexpr auto shuffle(Range&& range, UniformRandomBitGenerator&& g) { + return ranges::shuffle(ranges::begin(range), ranges::end(range), + std::forward<UniformRandomBitGenerator>(g)); +} + +// [alg.nonmodifying] Sorting and related operations +// Reference: https://wg21.link/alg.sorting + +// [alg.sort] Sorting +// Reference: https://wg21.link/alg.sort + +// [sort] sort +// Reference: https://wg21.link/sort + +// Effects: Sorts the elements in the range `[first, last)` with respect to +// `comp` and `proj`. +// +// Returns: `last`. +// +// Complexity: Let `N` be `last - first`. `O(N log N)` comparisons and +// projections. +// +// Reference: https://wg21.link/sort#:~:text=ranges::sort(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto sort(RandomAccessIterator first, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + std::sort(first, last, internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Effects: Sorts the elements in `range` with respect to `comp` and `proj`. +// +// Returns: `end(range)`. +// +// Complexity: Let `N` be `size(range)`. `O(N log N)` comparisons and +// projections. +// +// Reference: https://wg21.link/sort#:~:text=ranges::sort(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto sort(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::sort(ranges::begin(range), ranges::end(range), std::move(comp), + std::move(proj)); +} + +// [stable.sort] stable_sort +// Reference: https://wg21.link/stable.sort + +// Effects: Sorts the elements in the range `[first, last)` with respect to +// `comp` and `proj`. +// +// Returns: `last`. +// +// Complexity: Let `N` be `last - first`. If enough extra memory is available, +// `N log (N)` comparisons. Otherwise, at most `N log^2 (N)` comparisons. In +// either case, twice as many projections as the number of comparisons. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/stable.sort#:~:text=ranges::stable_sort(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto stable_sort(RandomAccessIterator first, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + std::stable_sort(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Effects: Sorts the elements in `range` with respect to `comp` and `proj`. +// +// Returns: `end(rang)`. +// +// Complexity: Let `N` be `size(range)`. If enough extra memory is available, +// `N log (N)` comparisons. Otherwise, at most `N log^2 (N)` comparisons. In +// either case, twice as many projections as the number of comparisons. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/stable.sort#:~:text=ranges::stable_sort(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto stable_sort(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::stable_sort(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [partial.sort] partial_sort +// Reference: https://wg21.link/partial.sort + +// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges. +// +// Effects: Places the first `middle - first` elements from the range +// `[first, last)` as sorted with respect to `comp` and `proj` into the range +// `[first, middle)`. The rest of the elements in the range `[middle, last)` are +// placed in an unspecified order. +// +// Returns: `last`. +// +// Complexity: Approximately `(last - first) * log(middle - first)` comparisons, +// and twice as many projections. +// +// Reference: https://wg21.link/partial.sort#:~:text=ranges::partial_sort(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto partial_sort(RandomAccessIterator first, + RandomAccessIterator middle, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + std::partial_sort(first, middle, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid +// ranges. +// +// Effects: Places the first `middle - begin(range)` elements from `range` as +// sorted with respect to `comp` and `proj` into the range +// `[begin(range), middle)`. The rest of the elements in the range +// `[middle, end(range))` are placed in an unspecified order. +// +// Returns: `end(range)`. +// +// Complexity: Approximately `size(range) * log(middle - begin(range))` +// comparisons, and twice as many projections. +// +// Reference: https://wg21.link/partial.sort#:~:text=ranges::partial_sort(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto partial_sort(Range&& range, + iterator_t<Range> middle, + Comp comp = {}, + Proj proj = {}) { + return ranges::partial_sort(ranges::begin(range), middle, ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [partial.sort.copy] partial_sort_copy +// Reference: https://wg21.link/partial.sort.copy + +// Let `N` be `min(last - first, result_last - result_first)`. +// +// Preconditions: For iterators `a1` and `b1` in `[first, last)`, and iterators +// `x2` and `y2` in `[result_first, result_last)`, after evaluating the +// assignment `*y2 = *b1`, let `E` be the value of `bool(invoke(comp, +// invoke(proj1, *a1), invoke(proj2, *y2)))`. Then, after evaluating the +// assignment `*x2 = *a1`, `E` is equal to `bool(invoke(comp, invoke(proj2, +// *x2), invoke(proj2, *y2)))`. +// +// Effects: Places the first `N` elements as sorted with respect to `comp` and +// `proj2` into the range `[result_first, result_first + N)`. +// +// Returns: `result_first + N`. +// +// Complexity: Approximately `(last - first) * log N` comparisons, and twice as +// many projections. +// +// Reference: +// https://wg21.link/partial.sort.copy#:~:text=ranges::partial_sort_copy(I1 +template <typename InputIterator, + typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<InputIterator, Proj1>, + projected<RandomAccessIterator, Proj2>>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj2>, + projected<InputIterator, Proj1>>> +constexpr auto partial_sort_copy(InputIterator first, + InputIterator last, + RandomAccessIterator result_first, + RandomAccessIterator result_last, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // Needs to opt-in to all permutations, since std::partial_sort_copy expects + // comp(proj2(lhs), proj1(rhs)) to compile. + return std::partial_sort_copy( + first, last, result_first, result_last, + internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2)); +} + +// Let `N` be `min(size(range), size(result_range))`. +// +// Preconditions: For iterators `a1` and `b1` in `range`, and iterators +// `x2` and `y2` in `result_range`, after evaluating the assignment +// `*y2 = *b1`, let `E` be the value of +// `bool(invoke(comp, invoke(proj1, *a1), invoke(proj2, *y2)))`. Then, after +// evaluating the assignment `*x2 = *a1`, `E` is equal to +// `bool(invoke(comp, invoke(proj2, *x2), invoke(proj2, *y2)))`. +// +// Effects: Places the first `N` elements as sorted with respect to `comp` and +// `proj2` into the range `[begin(result_range), begin(result_range) + N)`. +// +// Returns: `begin(result_range) + N`. +// +// Complexity: Approximately `size(range) * log N` comparisons, and twice as +// many projections. +// +// Reference: +// https://wg21.link/partial.sort.copy#:~:text=ranges::partial_sort_copy(R1 +template <typename Range1, + typename Range2, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range2>, Proj2>, + projected<iterator_t<Range1>, Proj1>>> +constexpr auto partial_sort_copy(Range1&& range, + Range2&& result_range, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::partial_sort_copy(ranges::begin(range), ranges::end(range), + ranges::begin(result_range), + ranges::end(result_range), std::move(comp), + std::move(proj1), std::move(proj2)); +} + +// [is.sorted] is_sorted +// Reference: https://wg21.link/is.sorted + +// Returns: The last iterator `i` in `[first, last]` for which the range +// `[first, i)` is sorted with respect to `comp` and `proj`. +// +// Complexity: Linear. +// +// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(I +template <typename ForwardIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>, + typename = indirect_result_t<Comp&, + projected<ForwardIterator, Proj>, + projected<ForwardIterator, Proj>>> +constexpr auto is_sorted_until(ForwardIterator first, + ForwardIterator last, + Comp comp = {}, + Proj proj = {}) { + // Implementation inspired by cppreference.com: + // https://en.cppreference.com/w/cpp/algorithm/is_sorted_until + // + // A reimplementation is required, because std::is_sorted_until is not + // constexpr prior to C++20. Once we have C++20, we should switch to standard + // library implementation. + if (first == last) + return last; + + for (ForwardIterator next = first; ++next != last; ++first) { + if (gurl_base::invoke(comp, gurl_base::invoke(proj, *next), + gurl_base::invoke(proj, *first))) { + return next; + } + } + + return last; +} + +// Returns: The last iterator `i` in `[begin(range), end(range)]` for which the +// range `[begin(range), i)` is sorted with respect to `comp` and `proj`. +// +// Complexity: Linear. +// +// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto is_sorted_until(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::is_sorted_until(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// Returns: Whether the range `[first, last)` is sorted with respect to `comp` +// and `proj`. +// +// Complexity: Linear. +// +// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(I +template <typename ForwardIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>, + typename = indirect_result_t<Comp&, + projected<ForwardIterator, Proj>, + projected<ForwardIterator, Proj>>> +constexpr auto is_sorted(ForwardIterator first, + ForwardIterator last, + Comp comp = {}, + Proj proj = {}) { + return ranges::is_sorted_until(first, last, std::move(comp), + std::move(proj)) == last; +} + +// Returns: Whether `range` is sorted with respect to `comp` and `proj`. +// +// Complexity: Linear. +// +// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto is_sorted(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::is_sorted(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [alg.nth.element] Nth element +// Reference: https://wg21.link/alg.nth.element + +// Preconditions: `[first, nth)` and `[nth, last)` are valid ranges. +// +// Effects: After `nth_element` the element in the position pointed to by `nth` +// is the element that would be in that position if the whole range were sorted +// with respect to `comp` and `proj`, unless `nth == last`. Also for every +// iterator `i` in the range `[first, nth)` and every iterator `j` in the range +// `[nth, last)` it holds that: +// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is false. +// +// Returns: `last`. +// +// Complexity: Linear on average. +// +// Reference: https://wg21.link/alg.nth.element#:~:text=ranges::nth_element(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto nth_element(RandomAccessIterator first, + RandomAccessIterator nth, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + std::nth_element(first, nth, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Preconditions: `[begin(range), nth)` and `[nth, end(range))` are valid +// ranges. +// +// Effects: After `nth_element` the element in the position pointed to by `nth` +// is the element that would be in that position if the whole range were sorted +// with respect to `comp` and `proj`, unless `nth == end(range)`. Also for every +// iterator `i` in the range `[begin(range), nth)` and every iterator `j` in the +// range `[nth, end(range))` it holds that: +// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is false. +// +// Returns: `end(range)`. +// +// Complexity: Linear on average. +// +// Reference: https://wg21.link/alg.nth.element#:~:text=ranges::nth_element(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto nth_element(Range&& range, + iterator_t<Range> nth, + Comp comp = {}, + Proj proj = {}) { + return ranges::nth_element(ranges::begin(range), nth, ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [alg.binary.search] Binary search +// Reference: https://wg21.link/alg.binary.search + +// [lower.bound] lower_bound +// Reference: https://wg21.link/lower.bound + +// Preconditions: The elements `e` of `[first, last)` are partitioned with +// respect to the expression `bool(invoke(comp, invoke(proj, e), value))`. +// +// Returns: The furthermost iterator `i` in the range `[first, last]` such that +// for every iterator `j` in the range `[first, i)`, +// `bool(invoke(comp, invoke(proj, *j), value))` is true. +// +// Complexity: At most `log_2(last - first) + O(1)` comparisons and projections. +// +// Reference: https://wg21.link/lower.bound#:~:text=ranges::lower_bound(I +template <typename ForwardIterator, + typename T, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto lower_bound(ForwardIterator first, + ForwardIterator last, + const T& value, + Comp comp = {}, + Proj proj = {}) { + // The second arg is guaranteed to be `value`, so we'll simply apply the + // identity projection. + identity value_proj; + return std::lower_bound( + first, last, value, + internal::ProjectedBinaryPredicate(comp, proj, value_proj)); +} + +// Preconditions: The elements `e` of `range` are partitioned with respect to +// the expression `bool(invoke(comp, invoke(proj, e), value))`. +// +// Returns: The furthermost iterator `i` in the range +// `[begin(range), end(range)]` such that for every iterator `j` in the range +// `[begin(range), i)`, `bool(invoke(comp, invoke(proj, *j), value))` is true. +// +// Complexity: At most `log_2(size(range)) + O(1)` comparisons and projections. +// +// Reference: https://wg21.link/lower.bound#:~:text=ranges::lower_bound(R +template <typename Range, + typename T, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto lower_bound(Range&& range, + const T& value, + Comp comp = {}, + Proj proj = {}) { + return ranges::lower_bound(ranges::begin(range), ranges::end(range), value, + std::move(comp), std::move(proj)); +} + +// [upper.bound] upper_bound +// Reference: https://wg21.link/upper.bound + +// Preconditions: The elements `e` of `[first, last)` are partitioned with +// respect to the expression `!bool(invoke(comp, value, invoke(proj, e)))`. +// +// Returns: The furthermost iterator `i` in the range `[first, last]` such that +// for every iterator `j` in the range `[first, i)`, +// `!bool(invoke(comp, value, invoke(proj, *j)))` is true. +// +// Complexity: At most `log_2(last - first) + O(1)` comparisons and projections. +// +// Reference: https://wg21.link/upper.bound#:~:text=ranges::upper_bound(I +template <typename ForwardIterator, + typename T, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto upper_bound(ForwardIterator first, + ForwardIterator last, + const T& value, + Comp comp = {}, + Proj proj = {}) { + // The first arg is guaranteed to be `value`, so we'll simply apply the + // identity projection. + identity value_proj; + return std::upper_bound( + first, last, value, + internal::ProjectedBinaryPredicate(comp, value_proj, proj)); +} + +// Preconditions: The elements `e` of `range` are partitioned with +// respect to the expression `!bool(invoke(comp, value, invoke(proj, e)))`. +// +// Returns: The furthermost iterator `i` in the range +// `[begin(range), end(range)]` such that for every iterator `j` in the range +// `[begin(range), i)`, `!bool(invoke(comp, value, invoke(proj, *j)))` is true. +// +// Complexity: At most `log_2(size(range)) + O(1)` comparisons and projections. +// +// Reference: https://wg21.link/upper.bound#:~:text=ranges::upper_bound(R +template <typename Range, + typename T, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto upper_bound(Range&& range, + const T& value, + Comp comp = {}, + Proj proj = {}) { + return ranges::upper_bound(ranges::begin(range), ranges::end(range), value, + std::move(comp), std::move(proj)); +} + +// [equal.range] equal_range +// Reference: https://wg21.link/equal.range + +// Preconditions: The elements `e` of `[first, last)` are partitioned with +// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and +// `!bool(invoke(comp, value, invoke(proj, e)))`. +// +// Returns: `{ranges::lower_bound(first, last, value, comp, proj), +// ranges::upper_bound(first, last, value, comp, proj)}`. +// +// Complexity: At most 2 ∗ log_2(last - first) + O(1) comparisons and +// projections. +// +// Reference: https://wg21.link/equal.range#:~:text=ranges::equal_range(I +template <typename ForwardIterator, + typename T, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto equal_range(ForwardIterator first, + ForwardIterator last, + const T& value, + Comp comp = {}, + Proj proj = {}) { + // Note: This does not dispatch to std::equal_range, as otherwise it would not + // be possible to prevent applying `proj` to `value`, which can result in + // unintended behavior. + return std::make_pair(ranges::lower_bound(first, last, value, comp, proj), + ranges::upper_bound(first, last, value, comp, proj)); +} + +// Preconditions: The elements `e` of `range` are partitioned with +// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and +// `!bool(invoke(comp, value, invoke(proj, e)))`. +// +// Returns: `{ranges::lower_bound(range, value, comp, proj), +// ranges::upper_bound(range, value, comp, proj)}`. +// +// Complexity: At most 2 ∗ log_2(size(range)) + O(1) comparisons and +// projections. +// +// Reference: https://wg21.link/equal.range#:~:text=ranges::equal_range(R +template <typename Range, + typename T, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto equal_range(Range&& range, + const T& value, + Comp comp = {}, + Proj proj = {}) { + return ranges::equal_range(ranges::begin(range), ranges::end(range), value, + std::move(comp), std::move(proj)); +} + +// [binary.search] binary_search +// Reference: https://wg21.link/binary.search + +// Preconditions: The elements `e` of `[first, last)` are partitioned with +// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and +// `!bool(invoke(comp, value, invoke(proj, e)))`. +// +// Returns: `true` if and only if for some iterator `i` in the range +// `[first, last)`, `!bool(invoke(comp, invoke(proj, *i), value)) && +// !bool(invoke(comp, value, invoke(proj, *i)))` is true. +// +// Complexity: At most `log_2(last - first) + O(1)` comparisons and projections. +// +// Reference: https://wg21.link/binary.search#:~:text=ranges::binary_search(I +template <typename ForwardIterator, + typename T, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto binary_search(ForwardIterator first, + ForwardIterator last, + const T& value, + Comp comp = {}, + Proj proj = {}) { + first = ranges::lower_bound(first, last, value, comp, proj); + return first != last && + !gurl_base::invoke(comp, value, gurl_base::invoke(proj, *first)); +} + +// Preconditions: The elements `e` of `range` are partitioned with +// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and +// `!bool(invoke(comp, value, invoke(proj, e)))`. +// +// Returns: `true` if and only if for some iterator `i` in `range` +// `!bool(invoke(comp, invoke(proj, *i), value)) && +// !bool(invoke(comp, value, invoke(proj, *i)))` is true. +// +// Complexity: At most `log_2(size(range)) + O(1)` comparisons and projections. +// +// Reference: https://wg21.link/binary.search#:~:text=ranges::binary_search(R +template <typename Range, + typename T, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto binary_search(Range&& range, + const T& value, + Comp comp = {}, + Proj proj = {}) { + return ranges::binary_search(ranges::begin(range), ranges::end(range), value, + std::move(comp), std::move(proj)); +} + +// [alg.partitions] Partitions +// Reference: https://wg21.link/alg.partitions + +// Returns: `true` if and only if the elements `e` of `[first, last)` are +// partitioned with respect to the expression +// `bool(invoke(pred, invoke(proj, e)))`. +// +// Complexity: Linear. At most `last - first` applications of `pred` and `proj`. +// +// Reference: https://wg21.link/alg.partitions#:~:text=ranges::is_partitioned(I +template <typename ForwardIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto is_partitioned(ForwardIterator first, + ForwardIterator last, + Pred pred, + Proj proj = {}) { + return std::is_partitioned(first, last, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Returns: `true` if and only if the elements `e` of `range` are partitioned +// with respect to the expression `bool(invoke(pred, invoke(proj, e)))`. +// +// Complexity: Linear. At most `size(range)` applications of `pred` and `proj`. +// +// Reference: https://wg21.link/alg.partitions#:~:text=ranges::is_partitioned(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto is_partitioned(Range&& range, Pred pred, Proj proj = {}) { + return ranges::is_partitioned(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`. +// +// Effects: Places all the elements `e` in `[first, last)` that satisfy `E(e)` +// before all the elements that do not. +// +// Returns: Let `i` be an iterator such that `E(*j)` is `true` for every +// iterator `j` in `[first, i)` and `false` for every iterator `j` in +// `[i, last)`. Returns: i. +// +// Complexity: Let `N = last - first`: +// Exactly `N` applications of the predicate and projection. At most `N / 2` +// swaps if the type of `first` models `bidirectional_iterator`, and at most `N` +// swaps otherwise. +// +// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition(I +template <typename ForwardIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto partition(ForwardIterator first, + ForwardIterator last, + Pred pred, + Proj proj = {}) { + return std::partition(first, last, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`. +// +// Effects: Places all the elements `e` in `range` that satisfy `E(e)` before +// all the elements that do not. +// +// Returns: Let `i` be an iterator such that `E(*j)` is `true` for every +// iterator `j` in `[begin(range), i)` and `false` for every iterator `j` in +// `[i, last)`. Returns: i. +// +// Complexity: Let `N = size(range)`: +// Exactly `N` applications of the predicate and projection. At most `N / 2` +// swaps if the type of `first` models `bidirectional_iterator`, and at most `N` +// swaps otherwise. +// +// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto partition(Range&& range, Pred pred, Proj proj = {}) { + return ranges::partition(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`. +// +// Effects: Places all the elements `e` in `[first, last)` that satisfy `E(e)` +// before all the elements that do not. The relative order of the elements in +// both groups is preserved. +// +// Returns: Let `i` be an iterator such that for every iterator `j` in +// `[first, i)`, `E(*j)` is `true`, and for every iterator `j` in the range +// `[i, last)`, `E(*j)` is `false`. Returns: `i`. +// +// Complexity: Let `N = last - first`: +// At most `N log N` swaps, but only `O(N)` swaps if there is enough extra +// memory. Exactly `N` applications of the predicate and projection. +// +// Reference: +// https://wg21.link/alg.partitions#:~:text=ranges::stable_partition(I +template <typename BidirectionalIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<BidirectionalIterator>> +constexpr auto stable_partition(BidirectionalIterator first, + BidirectionalIterator last, + Pred pred, + Proj proj = {}) { + return std::stable_partition(first, last, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`. +// +// Effects: Places all the elements `e` in `range` that satisfy `E(e)` before +// all the elements that do not. The relative order of the elements in both +// groups is preserved. +// +// Returns: Let `i` be an iterator such that for every iterator `j` in +// `[begin(range), i)`, `E(*j)` is `true`, and for every iterator `j` in the +// range `[i, end(range))`, `E(*j)` is `false`. Returns: `i`. +// +// Complexity: Let `N = size(range)`: +// At most `N log N` swaps, but only `O(N)` swaps if there is enough extra +// memory. Exactly `N` applications of the predicate and projection. +// +// Reference: +// https://wg21.link/alg.partitions#:~:text=ranges::stable_partition(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto stable_partition(Range&& range, Pred pred, Proj proj = {}) { + return ranges::stable_partition(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`. +// +// Mandates: The expression `*first` is writable to `out_true` and `out_false`. +// +// Preconditions: The input range and output ranges do not overlap. +// +// Effects: For each iterator `i` in `[first, last)`, copies `*i` to the output +// range beginning with `out_true` if `E(*i)` is `true`, or to the output range +// beginning with `out_false` otherwise. +// +// Returns: Let `o1` be the end of the output range beginning at `out_true`, and +// `o2` the end of the output range beginning at `out_false`. +// Returns `{o1, o2}`. +// +// Complexity: Exactly `last - first` applications of `pred` and `proj`. +// +// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_copy(I +template <typename InputIterator, + typename OutputIterator1, + typename OutputIterator2, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<InputIterator>, + typename = internal::iterator_category_t<OutputIterator1>, + typename = internal::iterator_category_t<OutputIterator2>> +constexpr auto partition_copy(InputIterator first, + InputIterator last, + OutputIterator1 out_true, + OutputIterator2 out_false, + Pred pred, + Proj proj = {}) { + return std::partition_copy(first, last, out_true, out_false, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`. +// +// Mandates: The expression `*begin(range)` is writable to `out_true` and +// `out_false`. +// +// Preconditions: The input range and output ranges do not overlap. +// +// Effects: For each iterator `i` in `range`, copies `*i` to the output range +// beginning with `out_true` if `E(*i)` is `true`, or to the output range +// beginning with `out_false` otherwise. +// +// Returns: Let `o1` be the end of the output range beginning at `out_true`, and +// `o2` the end of the output range beginning at `out_false`. +// Returns `{o1, o2}`. +// +// Complexity: Exactly `size(range)` applications of `pred` and `proj`. +// +// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_copy(R +template <typename Range, + typename OutputIterator1, + typename OutputIterator2, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = internal::iterator_category_t<OutputIterator1>, + typename = internal::iterator_category_t<OutputIterator2>> +constexpr auto partition_copy(Range&& range, + OutputIterator1 out_true, + OutputIterator2 out_false, + Pred pred, + Proj proj = {}) { + return ranges::partition_copy(ranges::begin(range), ranges::end(range), + out_true, out_false, std::move(pred), + std::move(proj)); +} + +// let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`. +// +// Preconditions: The elements `e` of `[first, last)` are partitioned with +// respect to `E(e)`. +// +// Returns: An iterator `mid` such that `E(*i)` is `true` for all iterators `i` +// in `[first, mid)`, and `false` for all iterators `i` in `[mid, last)`. +// +// Complexity: `O(log(last - first))` applications of `pred` and `proj`. +// +// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_point(I +template <typename ForwardIterator, + typename Pred, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>> +constexpr auto partition_point(ForwardIterator first, + ForwardIterator last, + Pred pred, + Proj proj = {}) { + return std::partition_point(first, last, + internal::ProjectedUnaryPredicate(pred, proj)); +} + +// let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`. +// +// Preconditions: The elements `e` of `range` are partitioned with respect to +// `E(e)`. +// +// Returns: An iterator `mid` such that `E(*i)` is `true` for all iterators `i` +// in `[begin(range), mid)`, and `false` for all iterators `i` in +// `[mid, end(range))`. +// +// Complexity: `O(log(size(range)))` applications of `pred` and `proj`. +// +// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_point(R +template <typename Range, + typename Pred, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto partition_point(Range&& range, Pred pred, Proj proj = {}) { + return ranges::partition_point(ranges::begin(range), ranges::end(range), + std::move(pred), std::move(proj)); +} + +// [alg.merge] Merge +// Reference: https://wg21.link/alg.merge + +// Let `N` be `(last1 - first1) + (last2 - first2)`. +// +// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted +// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting +// range does not overlap with either of the original ranges. +// +// Effects: Copies all the elements of the two ranges `[first1, last1)` and +// `[first2, last2)` into the range `[result, result_last)`, where `result_last` +// is `result + N`. If an element `a` precedes `b` in an input range, `a` is +// copied into the output range before `b`. If `e1` is an element of +// `[first1, last1)` and `e2` of `[first2, last2)`, `e2` is copied into the +// output range before `e1` if and only if +// `bool(invoke(comp, invoke(proj2, e2), invoke(proj1, e1)))` is `true`. +// +// Returns: `result_last`. +// +// Complexity: At most `N - 1` comparisons and applications of each projection. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.merge#:~:text=ranges::merge(I1 +template <typename InputIterator1, + typename InputIterator2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<InputIterator1>, + typename = internal::iterator_category_t<InputIterator2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<InputIterator1, Proj1>, + projected<InputIterator2, Proj2>>, + typename = indirect_result_t<Comp&, + projected<InputIterator2, Proj2>, + projected<InputIterator1, Proj1>>> +constexpr auto merge(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // Needs to opt-in to all permutations, since std::merge expects + // comp(proj2(lhs), proj1(rhs)) to compile. + return std::merge( + first1, last1, first2, last2, result, + internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2)); +} + +// Let `N` be `size(range1) + size(range2)`. +// +// Preconditions: The ranges `range1` and `range2` are sorted with respect to +// `comp` and `proj1` or `proj2`, respectively. The resulting range does not +// overlap with either of the original ranges. +// +// Effects: Copies all the elements of the two ranges `range1` and `range2` into +// the range `[result, result_last)`, where `result_last` is `result + N`. If an +// element `a` precedes `b` in an input range, `a` is copied into the output +// range before `b`. If `e1` is an element of `range1` and `e2` of `range2`, +// `e2` is copied into the output range before `e1` if and only if +// `bool(invoke(comp, invoke(proj2, e2), invoke(proj1, e1)))` is `true`. +// +// Returns: `result_last`. +// +// Complexity: At most `N - 1` comparisons and applications of each projection. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.merge#:~:text=ranges::merge(R1 +template <typename Range1, + typename Range2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range2>, Proj2>, + projected<iterator_t<Range1>, Proj1>>> +constexpr auto merge(Range1&& range1, + Range2&& range2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::merge(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), result, + std::move(comp), std::move(proj1), std::move(proj2)); +} + +// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges sorted +// with respect to `comp` and `proj`. +// +// Effects: Merges two sorted consecutive ranges `[first, middle)` and +// `[middle, last)`, putting the result of the merge into the range +// `[first, last)`. The resulting range is sorted with respect to `comp` and +// `proj`. +// +// Returns: `last`. +// +// Complexity: Let `N = last - first`: If enough additional memory is available, +// exactly `N - 1` comparisons. Otherwise, `O(N log N)` comparisons. In either +// case, twice as many projections as comparisons. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.merge#:~:text=ranges::inplace_merge(I +template <typename BidirectionalIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<BidirectionalIterator>> +constexpr auto inplace_merge(BidirectionalIterator first, + BidirectionalIterator middle, + BidirectionalIterator last, + Comp comp = {}, + Proj proj = {}) { + std::inplace_merge(first, middle, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid +// ranges sorted with respect to `comp` and `proj`. +// +// Effects: Merges two sorted consecutive ranges `[begin(range), middle)` and +// `[middle, end(range))`, putting the result of the merge into `range`. The +// resulting range is sorted with respect to `comp` and `proj`. +// +// Returns: `end(range)`. +// +// Complexity: Let `N = size(range)`: If enough additional memory is available, +// exactly `N - 1` comparisons. Otherwise, `O(N log N)` comparisons. In either +// case, twice as many projections as comparisons. +// +// Remarks: Stable. +// +// Reference: https://wg21.link/alg.merge#:~:text=ranges::inplace_merge(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto inplace_merge(Range&& range, + iterator_t<Range> middle, + Comp comp = {}, + Proj proj = {}) { + return ranges::inplace_merge(ranges::begin(range), middle, ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [alg.set.operations] Set operations on sorted structures +// Reference: https://wg21.link/alg.set.operations + +// [includes] includes +// Reference: https://wg21.link/includes + +// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted +// with respect to `comp` and `proj1` or `proj2`, respectively. +// +// Returns: `true` if and only if `[first2, last2)` is a subsequence of +// `[first1, last1)`. +// +// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1` +// comparisons and applications of each projection. +// +// Reference: https://wg21.link/includes#:~:text=ranges::includes(I1 +template <typename InputIterator1, + typename InputIterator2, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<InputIterator1>, + typename = internal::iterator_category_t<InputIterator2>, + typename = indirect_result_t<Comp&, + projected<InputIterator1, Proj1>, + projected<InputIterator2, Proj2>>, + typename = indirect_result_t<Comp&, + projected<InputIterator2, Proj2>, + projected<InputIterator1, Proj1>>> +constexpr auto includes(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // Needs to opt-in to all permutations, since std::includes expects + // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile. + return std::includes( + first1, last1, first2, last2, + internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2)); +} + +// Preconditions: The ranges `range1` and `range2` are sorted with respect to +// `comp` and `proj1` or `proj2`, respectively. +// +// Returns: `true` if and only if `range2` is a subsequence of `range1`. +// +// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and +// applications of each projection. +// +// Reference: https://wg21.link/includes#:~:text=ranges::includes(R1 +template <typename Range1, + typename Range2, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range2>, Proj2>, + projected<iterator_t<Range1>, Proj1>>> +constexpr auto includes(Range1&& range1, + Range2&& range2, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::includes(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), + std::move(comp), std::move(proj1), std::move(proj2)); +} + +// [set.union] set_union +// Reference: https://wg21.link/set.union + +// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted +// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting +// range does not overlap with either of the original ranges. +// +// Effects: Constructs a sorted union of the elements from the two ranges; that +// is, the set of elements that are present in one or both of the ranges. +// +// Returns: The end of the constructed range. +// +// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1` +// comparisons and applications of each projection. +// +// Remarks: Stable. If `[first1, last1)` contains `m` elements that are +// equivalent to each other and `[first2, last2)` contains `n` elements that are +// equivalent to them, then all `m` elements from the first range are copied to +// the output range, in order, and then the final `max(n - m , 0)` elements from +// the second range are copied to the output range, in order. +// +// Reference: https://wg21.link/set.union#:~:text=ranges::set_union(I1 +template <typename InputIterator1, + typename InputIterator2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<InputIterator1>, + typename = internal::iterator_category_t<InputIterator2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<InputIterator1, Proj1>, + projected<InputIterator2, Proj2>>, + typename = indirect_result_t<Comp&, + projected<InputIterator2, Proj2>, + projected<InputIterator1, Proj1>>> +constexpr auto set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // Needs to opt-in to all permutations, since std::set_union expects + // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile. + return std::set_union( + first1, last1, first2, last2, result, + internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2)); +} + +// Preconditions: The ranges `range1` and `range2` are sorted with respect to +// `comp` and `proj1` or `proj2`, respectively. The resulting range does not +// overlap with either of the original ranges. +// +// Effects: Constructs a sorted union of the elements from the two ranges; that +// is, the set of elements that are present in one or both of the ranges. +// +// Returns: The end of the constructed range. +// +// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and +// applications of each projection. +// +// Remarks: Stable. If `range1` contains `m` elements that are equivalent to +// each other and `range2` contains `n` elements that are equivalent to them, +// then all `m` elements from the first range are copied to the output range, in +// order, and then the final `max(n - m , 0)` elements from the second range are +// copied to the output range, in order. +// +// Reference: https://wg21.link/set.union#:~:text=ranges::set_union(R1 +template <typename Range1, + typename Range2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range2>, Proj2>, + projected<iterator_t<Range1>, Proj1>>> +constexpr auto set_union(Range1&& range1, + Range2&& range2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::set_union(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), result, + std::move(comp), std::move(proj1), std::move(proj2)); +} + +// [set.intersection] set_intersection +// Reference: https://wg21.link/set.intersection + +// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted +// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting +// range does not overlap with either of the original ranges. +// +// Effects: Constructs a sorted intersection of the elements from the two +// ranges; that is, the set of elements that are present in both of the ranges. +// +// Returns: The end of the constructed range. +// +// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1` +// comparisons and applications of each projection. +// +// Remarks: Stable. If `[first1, last1)` contains `m` elements that are +// equivalent to each other and `[first2, last2)` contains `n` elements that are +// equivalent to them, the first `min(m, n)` elements are copied from the first +// range to the output range, in order. +// +// Reference: +// https://wg21.link/set.intersection#:~:text=ranges::set_intersection(I1 +template <typename InputIterator1, + typename InputIterator2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<InputIterator1>, + typename = internal::iterator_category_t<InputIterator2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<InputIterator1, Proj1>, + projected<InputIterator2, Proj2>>, + typename = indirect_result_t<Comp&, + projected<InputIterator2, Proj2>, + projected<InputIterator1, Proj1>>> +constexpr auto set_intersection(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // Needs to opt-in to all permutations, since std::set_intersection expects + // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile. + return std::set_intersection( + first1, last1, first2, last2, result, + internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2)); +} + +// Preconditions: The ranges `range1` and `range2` are sorted with respect to +// `comp` and `proj1` or `proj2`, respectively. The resulting range does not +// overlap with either of the original ranges. +// +// Effects: Constructs a sorted intersection of the elements from the two +// ranges; that is, the set of elements that are present in both of the ranges. +// +// Returns: The end of the constructed range. +// +// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and +// applications of each projection. +// +// Remarks: Stable. If `range1` contains `m` elements that are equivalent to +// each other and `range2` contains `n` elements that are equivalent to them, +// the first `min(m, n)` elements are copied from the first range to the output +// range, in order. +// +// Reference: +// https://wg21.link/set.intersection#:~:text=ranges::set_intersection(R1 +template <typename Range1, + typename Range2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range2>, Proj2>, + projected<iterator_t<Range1>, Proj1>>> +constexpr auto set_intersection(Range1&& range1, + Range2&& range2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::set_intersection(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), + result, std::move(comp), std::move(proj1), + std::move(proj2)); +} + +// [set.difference] set_difference +// Reference: https://wg21.link/set.difference + +// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted +// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting +// range does not overlap with either of the original ranges. +// +// Effects: Copies the elements of the range `[first1, last1)` which are not +// present in the range `[first2, last2)` to the range beginning at `result`. +// The elements in the constructed range are sorted. +// +// Returns: The end of the constructed range. +// +// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1` +// comparisons and applications of each projection. +// +// Remarks: If `[first1, last1)` contains `m` elements that are equivalent to +// each other and `[first2, last2)` contains `n` elements that are equivalent to +// them, the last `max(m - n, 0)` elements from `[first1, last1)` are copied to +// the output range, in order. +// +// Reference: +// https://wg21.link/set.difference#:~:text=ranges::set_difference(I1 +template <typename InputIterator1, + typename InputIterator2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<InputIterator1>, + typename = internal::iterator_category_t<InputIterator2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<InputIterator1, Proj1>, + projected<InputIterator2, Proj2>>, + typename = indirect_result_t<Comp&, + projected<InputIterator2, Proj2>, + projected<InputIterator1, Proj1>>> +constexpr auto set_difference(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // Needs to opt-in to all permutations, since std::set_difference expects + // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile. + return std::set_difference( + first1, last1, first2, last2, result, + internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2)); +} + +// Preconditions: The ranges `range1` and `range2` are sorted with respect to +// `comp` and `proj1` or `proj2`, respectively. The resulting range does not +// overlap with either of the original ranges. +// +// Effects: Copies the elements of `range1` which are not present in `range2` +// to the range beginning at `result`. The elements in the constructed range are +// sorted. +// +// Returns: The end of the constructed range. +// +// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and +// applications of each projection. +// +// Remarks: Stable. If `range1` contains `m` elements that are equivalent to +// each other and `range2` contains `n` elements that are equivalent to them, +// the last `max(m - n, 0)` elements from `range1` are copied to the output +// range, in order. +// +// Reference: +// https://wg21.link/set.difference#:~:text=ranges::set_difference(R1 +template <typename Range1, + typename Range2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range2>, Proj2>, + projected<iterator_t<Range1>, Proj1>>> +constexpr auto set_difference(Range1&& range1, + Range2&& range2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::set_difference(ranges::begin(range1), ranges::end(range1), + ranges::begin(range2), ranges::end(range2), + result, std::move(comp), std::move(proj1), + std::move(proj2)); +} + +// [set.symmetric.difference] set_symmetric_difference +// Reference: https://wg21.link/set.symmetric.difference + +// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted +// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting +// range does not overlap with either of the original ranges. +// +// Effects: Copies the elements of the range `[first1, last1)` that are not +// present in the range `[first2, last2)`, and the elements of the range +// `[first2, last2)` that are not present in the range `[first1, last1)` to the +// range beginning at `result`. The elements in the constructed range are +// sorted. +// +// Returns: The end of the constructed range. +// +// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1` +// comparisons and applications of each projection. +// +// Remarks: Stable. If `[first1, last1)` contains `m` elements that are +// equivalent to each other and `[first2, last2)` contains `n` elements that are +// equivalent to them, then `|m - n|` of those elements shall be copied to the +// output range: the last `m - n` of these elements from `[first1, last1)` if +// `m > n`, and the last `n - m` of these elements from `[first2, last2)` if +// `m < n`. In either case, the elements are copied in order. +// +// Reference: +// https://wg21.link/set.symmetric.difference#:~:text=set_symmetric_difference(I1 +template <typename InputIterator1, + typename InputIterator2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<InputIterator1>, + typename = internal::iterator_category_t<InputIterator2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<InputIterator1, Proj1>, + projected<InputIterator2, Proj2>>, + typename = indirect_result_t<Comp&, + projected<InputIterator2, Proj2>, + projected<InputIterator1, Proj1>>> +constexpr auto set_symmetric_difference(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + // Needs to opt-in to all permutations, since std::set_symmetric_difference + // expects comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to + // compile. + return std::set_symmetric_difference( + first1, last1, first2, last2, result, + internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2)); +} + +// Preconditions: The ranges `range1` and `range2` are sorted with respect to +// `comp` and `proj1` or `proj2`, respectively. The resulting range does not +// overlap with either of the original ranges. +// +// Effects: Copies the elements of `range1` that are not present in `range2`, +// and the elements of `range2` that are not present in `range1` to the range +// beginning at `result`. The elements in the constructed range are sorted. +// +// Returns: The end of the constructed range. +// +// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and +// applications of each projection. +// +// Remarks: Stable. If `range1` contains `m` elements that are equivalent to +// each other and `range2` contains `n` elements that are equivalent to them, +// then `|m - n|` of those elements shall be copied to the output range: the +// last `m - n` of these elements from `range1` if `m > n`, and the last `n - m` +// of these elements from `range2` if `m < n`. In either case, the elements are +// copied in order. +// +// Reference: +// https://wg21.link/set.symmetric.difference#:~:text=set_symmetric_difference(R1 +template <typename Range1, + typename Range2, + typename OutputIterator, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = internal::iterator_category_t<OutputIterator>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range2>, Proj2>, + projected<iterator_t<Range1>, Proj1>>> +constexpr auto set_symmetric_difference(Range1&& range1, + Range2&& range2, + OutputIterator result, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::set_symmetric_difference( + ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), result, std::move(comp), std::move(proj1), + std::move(proj2)); +} + +// [alg.heap.operations] Heap operations +// Reference: https://wg21.link/alg.heap.operations + +// [push.heap] push_heap +// Reference: https://wg21.link/push.heap + +// Preconditions: The range `[first, last - 1)` is a valid heap with respect to +// `comp` and `proj`. +// +// Effects: Places the value in the location `last - 1` into the resulting heap +// `[first, last)`. +// +// Returns: `last`. +// +// Complexity: At most `log(last - first)` comparisons and twice as many +// projections. +// +// Reference: https://wg21.link/push.heap#:~:text=ranges::push_heap(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto push_heap(RandomAccessIterator first, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + std::push_heap(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Preconditions: The range `[begin(range), end(range) - 1)` is a valid heap +// with respect to `comp` and `proj`. +// +// Effects: Places the value in the location `end(range) - 1` into the resulting +// heap `range`. +// +// Returns: `end(range)`. +// +// Complexity: At most `log(size(range))` comparisons and twice as many +// projections. +// +// Reference: https://wg21.link/push.heap#:~:text=ranges::push_heap(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto push_heap(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::push_heap(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [pop.heap] pop_heap +// Reference: https://wg21.link/pop.heap + +// Preconditions: The range `[first, last)` is a valid non-empty heap with +// respect to `comp` and `proj`. +// +// Effects: Swaps the value in the location `first` with the value in the +// location `last - 1` and makes `[first, last - 1)` into a heap with respect to +// `comp` and `proj`. +// +// Returns: `last`. +// +// Complexity: At most `2 log(last - first)` comparisons and twice as many +// projections. +// +// Reference: https://wg21.link/pop.heap#:~:text=ranges::pop_heap(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto pop_heap(RandomAccessIterator first, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + std::pop_heap(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Preconditions: `range` is a valid non-empty heap with respect to `comp` and +// `proj`. +// +// Effects: Swaps the value in the location `begin(range)` with the value in the +// location `end(range) - 1` and makes `[begin(range), end(range) - 1)` into a +// heap with respect to `comp` and `proj`. +// +// Returns: `end(range)`. +// +// Complexity: At most `2 log(size(range))` comparisons and twice as many +// projections. +// +// Reference: https://wg21.link/pop.heap#:~:text=ranges::pop_heap(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto pop_heap(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::pop_heap(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [make.heap] make_heap +// Reference: https://wg21.link/make.heap + +// Effects: Constructs a heap with respect to `comp` and `proj` out of the range +// `[first, last)`. +// +// Returns: `last`. +// +// Complexity: At most `3 log(last - first)` comparisons and twice as many +// projections. +// +// Reference: https://wg21.link/make.heap#:~:text=ranges::make_heap(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto make_heap(RandomAccessIterator first, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + std::make_heap(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Effects: Constructs a heap with respect to `comp` and `proj` out of `range`. +// +// Returns: `end(range)`. +// +// Complexity: At most `3 log(size(range))` comparisons and twice as many +// projections. +// +// Reference: https://wg21.link/make.heap#:~:text=ranges::make_heap(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto make_heap(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::make_heap(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [sort.heap] sort_heap +// Reference: https://wg21.link/sort.heap + +// Preconditions: The range `[first, last)` is a valid heap with respect to +// `comp` and `proj`. +// +// Effects: Sorts elements in the heap `[first, last)` with respect to `comp` +// and `proj`. +// +// Returns: `last`. +// +// Complexity: At most `2 N log N` comparisons, where `N = last - first`, and +// twice as many projections. +// +// Reference: https://wg21.link/sort.heap#:~:text=ranges::sort_heap(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto sort_heap(RandomAccessIterator first, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + std::sort_heap(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return last; +} + +// Preconditions: `range` is a valid heap with respect to `comp` and `proj`. +// +// Effects: Sorts elements in the heap `range` with respect to `comp` and +// `proj`. +// +// Returns: `end(range)`. +// +// Complexity: At most `2 N log N` comparisons, where `N = size(range)`, and +// twice as many projections. +// +// Reference: https://wg21.link/sort.heap#:~:text=ranges::sort_heap(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto sort_heap(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::sort_heap(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [is.heap] is_heap +// Reference: https://wg21.link/is.heap + +// Returns: Whether the range `[first, last)` is a heap with respect to `comp` +// and `proj`. +// +// Complexity: Linear. +// +// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto is_heap(RandomAccessIterator first, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + return std::is_heap(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Returns: Whether `range` is a heap with respect to `comp` and `proj`. +// +// Complexity: Linear. +// +// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto is_heap(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::is_heap(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// Returns: The last iterator `i` in `[first, last]` for which the range +// `[first, i)` is a heap with respect to `comp` and `proj`. +// +// Complexity: Linear. +// +// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap_until(I +template <typename RandomAccessIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<RandomAccessIterator>, + typename = indirect_result_t<Comp&, + projected<RandomAccessIterator, Proj>, + projected<RandomAccessIterator, Proj>>> +constexpr auto is_heap_until(RandomAccessIterator first, + RandomAccessIterator last, + Comp comp = {}, + Proj proj = {}) { + return std::is_heap_until( + first, last, internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Returns: The last iterator `i` in `[begin(range), end(range)]` for which the +// range `[begin(range), i)` is a heap with respect to `comp` and `proj`. +// +// Complexity: Linear. +// +// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap_until(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto is_heap_until(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::is_heap_until(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [alg.min.max] Minimum and maximum +// Reference: https://wg21.link/alg.min.max + +// Returns: The smaller value. Returns the first argument when the arguments are +// equivalent. +// +// Complexity: Exactly one comparison and two applications of the projection, if +// any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min +template <typename T, typename Comp = ranges::less, typename Proj = identity> +constexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}) { + return gurl_base::invoke(comp, gurl_base::invoke(proj, b), gurl_base::invoke(proj, a)) ? b + : a; +} + +// Preconditions: `!empty(ilist)`. +// +// Returns: The smallest value in the input range. Returns a copy of the +// leftmost element when several elements are equivalent to the smallest. +// +// Complexity: Exactly `size(ilist) - 1` comparisons and twice as many +// applications of the projection, if any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min(initializer_list +template <typename T, typename Comp = ranges::less, typename Proj = identity> +constexpr T min(std::initializer_list<T> ilist, + Comp comp = {}, + Proj proj = {}) { + return *std::min_element( + ilist.begin(), ilist.end(), + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Preconditions: `!empty(range)`. +// +// Returns: The smallest value in the input range. Returns a copy of the +// leftmost element when several elements are equivalent to the smallest. +// +// Complexity: Exactly `size(range) - 1` comparisons and twice as many +// applications of the projection, if any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto min(Range&& range, Comp comp = {}, Proj proj = {}) { + return *std::min_element( + ranges::begin(range), ranges::end(range), + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Returns: The larger value. Returns the first argument when the arguments are +// equivalent. +// +// Complexity: Exactly one comparison and two applications of the projection, if +// any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max +template <typename T, typename Comp = ranges::less, typename Proj = identity> +constexpr const T& max(const T& a, const T& b, Comp comp = {}, Proj proj = {}) { + return gurl_base::invoke(comp, gurl_base::invoke(proj, a), gurl_base::invoke(proj, b)) ? b + : a; +} + +// Preconditions: `!empty(ilist)`. +// +// Returns: The largest value in the input range. Returns a copy of the leftmost +// element when several elements are equivalent to the largest. +// +// Complexity: Exactly `size(ilist) - 1` comparisons and twice as many +// applications of the projection, if any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max(initializer_list +template <typename T, typename Comp = ranges::less, typename Proj = identity> +constexpr T max(std::initializer_list<T> ilist, + Comp comp = {}, + Proj proj = {}) { + return *std::max_element( + ilist.begin(), ilist.end(), + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Preconditions: `!empty(range)`. +// +// Returns: The largest value in the input range. Returns a copy of the leftmost +// element when several elements are equivalent to the smallest. +// +// Complexity: Exactly `size(range) - 1` comparisons and twice as many +// applications of the projection, if any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto max(Range&& range, Comp comp = {}, Proj proj = {}) { + return *std::max_element( + ranges::begin(range), ranges::end(range), + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Returns: `{b, a}` if `b` is smaller than `a`, and `{a, b}` otherwise. +// +// Complexity: Exactly one comparison and two applications of the projection, if +// any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax +template <typename T, typename Comp = ranges::less, typename Proj = identity> +constexpr auto minmax(const T& a, const T& b, Comp comp = {}, Proj proj = {}) { + return std::minmax(a, b, + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Preconditions: `!empty(ilist)`. +// +// Returns: Let `X` be the return type. Returns `X{x, y}`, where `x` is a copy +// of the leftmost element with the smallest value and `y` a copy of the +// rightmost element with the largest value in the input range. +// +// Complexity: At most `(3/2) size(ilist)` applications of the corresponding +// predicate and twice as many applications of the projection, if any. +// +// Reference: +// https://wg21.link/alg.min.max#:~:text=ranges::minmax(initializer_list +template <typename T, typename Comp = ranges::less, typename Proj = identity> +constexpr auto minmax(std::initializer_list<T> ilist, + Comp comp = {}, + Proj proj = {}) { + auto it = + std::minmax_element(ranges::begin(ilist), ranges::end(ilist), + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return std::pair<T, T>{*it.first, *it.second}; +} + +// Preconditions: `!empty(range)`. +// +// Returns: Let `X` be the return type. Returns `X{x, y}`, where `x` is a copy +// of the leftmost element with the smallest value and `y` a copy of the +// rightmost element with the largest value in the input range. +// +// Complexity: At most `(3/2) size(range)` applications of the corresponding +// predicate and twice as many applications of the projection, if any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>> +constexpr auto minmax(Range&& range, Comp comp = {}, Proj proj = {}) { + using T = range_value_t<Range>; + auto it = + std::minmax_element(ranges::begin(range), ranges::end(range), + internal::ProjectedBinaryPredicate(comp, proj, proj)); + return std::pair<T, T>{*it.first, *it.second}; +} + +// Returns: The first iterator i in the range `[first, last)` such that for +// every iterator `j` in the range `[first, last)`, +// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is `false`. Returns +// `last` if `first == last`. +// +// Complexity: Exactly `max(last - first - 1, 0)` comparisons and twice as +// many projections. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min_element(I +template <typename ForwardIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>, + typename = indirect_result_t<Comp&, + projected<ForwardIterator, Proj>, + projected<ForwardIterator, Proj>>> +constexpr auto min_element(ForwardIterator first, + ForwardIterator last, + Comp comp = {}, + Proj proj = {}) { + return std::min_element(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Returns: The first iterator i in `range` such that for every iterator `j` in +// `range`, `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is `false`. +// Returns `end(range)` if `empty(range)`. +// +// Complexity: Exactly `max(size(range) - 1, 0)` comparisons and twice as many +// projections. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min_element(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto min_element(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::min_element(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// Returns: The first iterator i in the range `[first, last)` such that for +// every iterator `j` in the range `[first, last)`, +// `bool(invoke(comp, invoke(proj, *i), invoke(proj, *j)))` is `false`. +// Returns `last` if `first == last`. +// +// Complexity: Exactly `max(last - first - 1, 0)` comparisons and twice as +// many projections. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max_element(I +template <typename ForwardIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>, + typename = indirect_result_t<Comp&, + projected<ForwardIterator, Proj>, + projected<ForwardIterator, Proj>>> +constexpr auto max_element(ForwardIterator first, + ForwardIterator last, + Comp comp = {}, + Proj proj = {}) { + return std::max_element(first, last, + internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Returns: The first iterator i in `range` such that for every iterator `j` +// in `range`, `bool(invoke(comp, invoke(proj, *j), invoke(proj, *j)))` is +// `false`. Returns `end(range)` if `empty(range)`. +// +// Complexity: Exactly `max(size(range) - 1, 0)` comparisons and twice as many +// projections. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max_element(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto max_element(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::max_element(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// Returns: `{first, first}` if `[first, last)` is empty, otherwise `{m, M}`, +// where `m` is the first iterator in `[first, last)` such that no iterator in +// the range refers to a smaller element, and where `M` is the last iterator +// in +// `[first, last)` such that no iterator in the range refers to a larger +// element. +// +// Complexity: Let `N` be `last - first`. At most `max(3/2 (N − 1), 0)` +// comparisons and twice as many applications of the projection, if any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax_element(I +template <typename ForwardIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<ForwardIterator>, + typename = indirect_result_t<Comp&, + projected<ForwardIterator, Proj>, + projected<ForwardIterator, Proj>>> +constexpr auto minmax_element(ForwardIterator first, + ForwardIterator last, + Comp comp = {}, + Proj proj = {}) { + return std::minmax_element( + first, last, internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Returns: `{begin(range), begin(range)}` if `range` is empty, otherwise +// `{m, M}`, where `m` is the first iterator in `range` such that no iterator +// in the range refers to a smaller element, and where `M` is the last +// iterator in `range` such that no iterator in the range refers to a larger +// element. +// +// Complexity: Let `N` be `size(range)`. At most `max(3/2 (N − 1), 0)` +// comparisons and twice as many applications of the projection, if any. +// +// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax_element(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto minmax_element(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::minmax_element(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// [alg.clamp] Bounded value +// Reference: https://wg21.link/alg.clamp + +// Preconditions: `bool(invoke(comp, invoke(proj, hi), invoke(proj, lo)))` is +// `false`. +// +// Returns: `lo` if `bool(invoke(comp, invoke(proj, v), invoke(proj, lo)))` is +// `true`, `hi` if `bool(invoke(comp, invoke(proj, hi), invoke(proj, v)))` is +// `true`, otherwise `v`. +// +// Complexity: At most two comparisons and three applications of the +// projection. +// +// Reference: https://wg21.link/alg.clamp#:~:text=ranges::clamp +template <typename T, typename Comp = ranges::less, typename Proj = identity> +constexpr const T& clamp(const T& v, + const T& lo, + const T& hi, + Comp comp = {}, + Proj proj = {}) { + auto&& projected_v = gurl_base::invoke(proj, v); + if (gurl_base::invoke(comp, projected_v, gurl_base::invoke(proj, lo))) + return lo; + + return gurl_base::invoke(comp, gurl_base::invoke(proj, hi), projected_v) ? hi : v; +} + +// [alg.lex.comparison] Lexicographical comparison +// Reference: https://wg21.link/alg.lex.comparison + +// Returns: `true` if and only if the sequence of elements defined by the range +// `[first1, last1)` is lexicographically less than the sequence of elements +// defined by the range `[first2, last2)`. +// +// Complexity: At most `2 min(last1 - first1, last2 - first2)` applications of +// the corresponding comparison and each projection, if any. +// +// Remarks: If two sequences have the same number of elements and their +// corresponding elements (if any) are equivalent, then neither sequence is +// lexicographically less than the other. If one sequence is a proper prefix of +// the other, then the shorter sequence is lexicographically less than the +// longer sequence. Otherwise, the lexicographical comparison of the sequences +// yields the same result as the comparison of the first corresponding pair of +// elements that are not equivalent. +// +// Reference: +// https://wg21.link/alg.lex.comparison#:~:text=lexicographical_compare(I1 +template <typename ForwardIterator1, + typename ForwardIterator2, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::iterator_category_t<ForwardIterator1>, + typename = internal::iterator_category_t<ForwardIterator2>, + typename = indirect_result_t<Comp&, + projected<ForwardIterator1, Proj1>, + projected<ForwardIterator2, Proj2>>, + typename = indirect_result_t<Comp&, + projected<ForwardIterator2, Proj2>, + projected<ForwardIterator1, Proj1>>> +constexpr bool lexicographical_compare(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) { + auto&& projected_first1 = gurl_base::invoke(proj1, *first1); + auto&& projected_first2 = gurl_base::invoke(proj2, *first2); + if (gurl_base::invoke(comp, projected_first1, projected_first2)) + return true; + if (gurl_base::invoke(comp, projected_first2, projected_first1)) + return false; + } + + // `first2 != last2` is equivalent to `first1 == last1 && first2 != last2` + // here, since we broke out of the loop above. + return first2 != last2; +} + +// Returns: `true` if and only if the sequence of elements defined by `range1` +// is lexicographically less than the sequence of elements defined by `range2`. +// +// Complexity: At most `2 min(size(range1), size(range2))` applications of the +// corresponding comparison and each projection, if any. +// +// Remarks: If two sequences have the same number of elements and their +// corresponding elements (if any) are equivalent, then neither sequence is +// lexicographically less than the other. If one sequence is a proper prefix of +// the other, then the shorter sequence is lexicographically less than the +// longer sequence. Otherwise, the lexicographical comparison of the sequences +// yields the same result as the comparison of the first corresponding pair of +// elements that are not equivalent. +// +// Reference: +// https://wg21.link/alg.lex.comparison#:~:text=lexicographical_compare(R1 +template <typename Range1, + typename Range2, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + typename = internal::range_category_t<Range1>, + typename = internal::range_category_t<Range2>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range1>, Proj1>, + projected<iterator_t<Range2>, Proj2>>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range2>, Proj2>, + projected<iterator_t<Range1>, Proj1>>> +constexpr bool lexicographical_compare(Range1&& range1, + Range2&& range2, + Comp comp = {}, + Proj1 proj1 = {}, + Proj2 proj2 = {}) { + return ranges::lexicographical_compare( + ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), std::move(comp), std::move(proj1), std::move(proj2)); +} + +// [alg.permutation.generators] Permutation generators +// Reference: https://wg21.link/alg.permutation.generators + +// Effects: Takes a sequence defined by the range `[first, last)` and transforms +// it into the next permutation. The next permutation is found by assuming that +// the set of all permutations is lexicographically sorted with respect to +// `comp` and `proj`. If no such permutation exists, transforms the sequence +// into the first permutation; that is, the ascendingly-sorted one. +// +// Returns: `true` if a next permutation was found and otherwise `false`. +// +// Complexity: At most `(last - first) / 2` swaps. +// +// Reference: +// https://wg21.link/alg.permutation.generators#:~:text=next_permutation(I +template <typename BidirectionalIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<BidirectionalIterator>, + typename = indirect_result_t<Comp&, + projected<BidirectionalIterator, Proj>, + projected<BidirectionalIterator, Proj>>> +constexpr auto next_permutation(BidirectionalIterator first, + BidirectionalIterator last, + Comp comp = {}, + Proj proj = {}) { + return std::next_permutation( + first, last, internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Effects: Takes a sequence defined by `range` and transforms it into the next +// permutation. The next permutation is found by assuming that the set of all +// permutations is lexicographically sorted with respect to `comp` and `proj`. +// If no such permutation exists, transforms the sequence into the first +// permutation; that is, the ascendingly-sorted one. +// +// Returns: `true` if a next permutation was found and otherwise `false`. +// +// Complexity: At most `size(range) / 2` swaps. +// +// Reference: +// https://wg21.link/alg.permutation.generators#:~:text=next_permutation(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto next_permutation(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::next_permutation(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +// Effects: Takes a sequence defined by the range `[first, last)` and transforms +// it into the previous permutation. The previous permutation is found by +// assuming that the set of all permutations is lexicographically sorted with +// respect to `comp` and `proj`. If no such permutation exists, transforms the +// sequence into the last permutation; that is, the decreasingly-sorted one. +// +// Returns: `true` if a next permutation was found and otherwise `false`. +// +// Complexity: At most `(last - first) / 2` swaps. +// +// Reference: +// https://wg21.link/alg.permutation.generators#:~:text=prev_permutation(I +template <typename BidirectionalIterator, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::iterator_category_t<BidirectionalIterator>, + typename = indirect_result_t<Comp&, + projected<BidirectionalIterator, Proj>, + projected<BidirectionalIterator, Proj>>> +constexpr auto prev_permutation(BidirectionalIterator first, + BidirectionalIterator last, + Comp comp = {}, + Proj proj = {}) { + return std::prev_permutation( + first, last, internal::ProjectedBinaryPredicate(comp, proj, proj)); +} + +// Effects: Takes a sequence defined by `range` and transforms it into the +// previous permutation. The previous permutation is found by assuming that the +// set of all permutations is lexicographically sorted with respect to `comp` +// and `proj`. If no such permutation exists, transforms the sequence into the +// last permutation; that is, the decreasingly-sorted one. +// +// Returns: `true` if a previous permutation was found and otherwise `false`. +// +// Complexity: At most `size(range) / 2` swaps. +// +// Reference: +// https://wg21.link/alg.permutation.generators#:~:text=prev_permutation(R +template <typename Range, + typename Comp = ranges::less, + typename Proj = identity, + typename = internal::range_category_t<Range>, + typename = indirect_result_t<Comp&, + projected<iterator_t<Range>, Proj>, + projected<iterator_t<Range>, Proj>>> +constexpr auto prev_permutation(Range&& range, Comp comp = {}, Proj proj = {}) { + return ranges::prev_permutation(ranges::begin(range), ranges::end(range), + std::move(comp), std::move(proj)); +} + +} // namespace ranges + +} // namespace base + +#endif // BASE_RANGES_ALGORITHM_H_
diff --git a/base/ranges/functional.h b/base/ranges/functional.h new file mode 100644 index 0000000..e71abab --- /dev/null +++ b/base/ranges/functional.h
@@ -0,0 +1,32 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_RANGES_FUNCTIONAL_H_ +#define BASE_RANGES_FUNCTIONAL_H_ + +#include <functional> +#include <type_traits> +#include <utility> + +namespace gurl_base { + +namespace ranges { + +// Simplified implementations of C++20's std::ranges comparison function +// objects. As opposed to the std::ranges implementation, these versions do not +// constrain the passed-in types. +// +// Reference: https://wg21.link/range.cmp +using equal_to = std::equal_to<>; +using not_equal_to = std::not_equal_to<>; +using greater = std::greater<>; +using less = std::less<>; +using greater_equal = std::greater_equal<>; +using less_equal = std::less_equal<>; + +} // namespace ranges + +} // namespace base + +#endif // BASE_RANGES_FUNCTIONAL_H_
diff --git a/base/ranges/ranges.h b/base/ranges/ranges.h new file mode 100644 index 0000000..7eef2d6 --- /dev/null +++ b/base/ranges/ranges.h
@@ -0,0 +1,140 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_RANGES_RANGES_H_ +#define BASE_RANGES_RANGES_H_ + +#include <array> +#include <iterator> +#include <type_traits> +#include <utility> + +#include "base/template_util.h" + +namespace gurl_base { + +namespace internal { + +// Overload for C array. +template <typename T, size_t N> +constexpr T* begin(T (&array)[N], priority_tag<2>) { + return array; +} + +// Overload for mutable std::array. Required since std::array::begin is not +// constexpr prior to C++17. Needs to dispatch to the const overload since only +// const operator[] is constexpr in C++14. +template <typename T, size_t N> +constexpr T* begin(std::array<T, N>& array, priority_tag<2> tag) { + return const_cast<T*>(begin(const_cast<const std::array<T, N>&>(array), tag)); +} + +// Overload for const std::array. Required since std::array::begin is not +// constexpr prior to C++17. +template <typename T, size_t N> +constexpr const T* begin(const std::array<T, N>& array, priority_tag<2>) { + return N != 0 ? &array[0] : nullptr; +} + +// Generic container overload. +template <typename Range> +constexpr auto begin(Range&& range, priority_tag<1>) + -> decltype(std::forward<Range>(range).begin()) { + return std::forward<Range>(range).begin(); +} + +// Overload for free begin() function. +template <typename Range> +constexpr auto begin(Range&& range, priority_tag<0>) + -> decltype(begin(std::forward<Range>(range))) { + return begin(std::forward<Range>(range)); +} + +// Overload for C array. +template <typename T, size_t N> +constexpr T* end(T (&array)[N], priority_tag<2>) { + return array + N; +} + +// Overload for mutable std::array. Required since std::array::end is not +// constexpr prior to C++17. Needs to dispatch to the const overload since only +// const operator[] is constexpr in C++14. +template <typename T, size_t N> +constexpr T* end(std::array<T, N>& array, priority_tag<2> tag) { + return const_cast<T*>(end(const_cast<const std::array<T, N>&>(array), tag)); +} + +// Overload for const std::array. Required since std::array::end is not +// constexpr prior to C++17. +template <typename T, size_t N> +constexpr const T* end(const std::array<T, N>& array, priority_tag<2>) { + return N != 0 ? (&array[0]) + N : nullptr; +} + +// Generic container overload. +template <typename Range> +constexpr auto end(Range&& range, priority_tag<1>) + -> decltype(std::forward<Range>(range).end()) { + return std::forward<Range>(range).end(); +} + +// Overload for free end() function. +template <typename Range> +constexpr auto end(Range&& range, priority_tag<0>) + -> decltype(end(std::forward<Range>(range))) { + return end(std::forward<Range>(range)); +} + +} // namespace internal + +namespace ranges { + +// Simplified implementation of C++20's std::ranges::begin. +// As opposed to std::ranges::begin, this implementation does does not check +// whether begin() returns an iterator and does not inhibit ADL. +// +// The trailing return type and dispatch to the internal implementation is +// necessary to be SFINAE friendly. +// +// Reference: https://wg21.link/range.access.begin +template <typename Range> +constexpr auto begin(Range&& range) noexcept + -> decltype(internal::begin(std::forward<Range>(range), + internal::priority_tag<2>())) { + return internal::begin(std::forward<Range>(range), + internal::priority_tag<2>()); +} + +// Simplified implementation of C++20's std::ranges::end. +// As opposed to std::ranges::end, this implementation does does not check +// whether end() returns an iterator and does not inhibit ADL. +// +// The trailing return type and dispatch to the internal implementation is +// necessary to be SFINAE friendly. +// +// Reference: - https://wg21.link/range.access.end +template <typename Range> +constexpr auto end(Range&& range) noexcept + -> decltype(internal::end(std::forward<Range>(range), + internal::priority_tag<2>())) { + return internal::end(std::forward<Range>(range), internal::priority_tag<2>()); +} + +// Implementation of C++20's std::ranges::iterator_t. +// +// Reference: https://wg21.link/ranges.syn#:~:text=iterator_t +template <typename Range> +using iterator_t = decltype(ranges::begin(std::declval<Range&>())); + +// Implementation of C++20's std::ranges::range_value_t. +// +// Reference: https://wg21.link/ranges.syn#:~:text=range_value_t +template <typename Range> +using range_value_t = iter_value_t<iterator_t<Range>>; + +} // namespace ranges + +} // namespace base + +#endif // BASE_RANGES_RANGES_H_
diff --git a/base/stl_util.h b/base/stl_util.h index 14d2b6f..49fd272 100644 --- a/base/stl_util.h +++ b/base/stl_util.h
@@ -171,6 +171,23 @@ template <typename T> void as_const(const T&& t) = delete; +// Simplified C++14 implementation of C++20's std::to_address. +// Note: This does not consider specializations of pointer_traits<>::to_address, +// since that member function may only be present in C++20 and later. +// +// Reference: https://wg21.link/pointer.conversion#lib:to_address +template <typename T> +constexpr T* to_address(T* p) noexcept { + static_assert(!std::is_function<T>::value, + "Error: T must not be a function type."); + return p; +} + +template <typename Ptr> +constexpr auto to_address(const Ptr& p) noexcept { + return to_address(p.operator->()); +} + // Returns a const reference to the underlying container of a container adapter. // Works for std::priority_queue, std::queue, and std::stack. template <class A> @@ -447,10 +464,22 @@ std::forward<Args>(args)...); } -// Returns true if the container is sorted. +// Returns true if the container is sorted. Requires constexpr std::begin/end, +// which exists for arrays in C++14. +// Note that std::is_sorted is constexpr beginning C++20 and this should be +// switched to use it when C++20 is supported. template <typename Container> -bool STLIsSorted(const Container& cont) { - return std::is_sorted(std::begin(cont), std::end(cont)); +constexpr bool STLIsSorted(const Container& cont) { + auto it = std::begin(cont); + const auto end = std::end(cont); + if (it == end) + return true; + + for (auto prev = it++; it != end; prev = it++) { + if (*it < *prev) + return false; + } + return true; } // Returns a new ResultType containing the difference of two sorted containers. @@ -701,6 +730,14 @@ return optional.has_value() ? &optional.value() : nullptr; } +// Helper for creating an Optional<T> from a potentially nullptr T*. +template <class T> +gurl_base::Optional<T> OptionalFromPtr(const T* value) { + if (value) + return gurl_base::Optional<T>(*value); + return gurl_base::nullopt; +} + } // namespace base #endif // BASE_STL_UTIL_H_
diff --git a/base/strings/abseil_string_conversions.cc b/base/strings/abseil_string_conversions.cc new file mode 100644 index 0000000..e7c746e --- /dev/null +++ b/base/strings/abseil_string_conversions.cc
@@ -0,0 +1,30 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/abseil_string_conversions.h" + +#include <vector> + +#include "base/containers/span.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_piece.h" +#include "third_party/abseil-cpp/absl/strings/string_view.h" + +namespace gurl_base { + +std::vector<absl::string_view> StringPiecesToStringViews( + span<const StringPiece> pieces) { + std::vector<absl::string_view> views(pieces.size()); + ranges::transform(pieces, views.begin(), &StringPieceToStringView); + return views; +} + +std::vector<StringPiece> StringViewsToStringPieces( + span<const absl::string_view> views) { + std::vector<StringPiece> pieces(views.size()); + ranges::transform(views, pieces.begin(), &StringViewToStringPiece); + return pieces; +} + +} // namespace base
diff --git a/base/strings/abseil_string_conversions.h b/base/strings/abseil_string_conversions.h new file mode 100644 index 0000000..c821d93 --- /dev/null +++ b/base/strings/abseil_string_conversions.h
@@ -0,0 +1,37 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRINGS_ABSEIL_STRING_CONVERSIONS_H_ +#define BASE_STRINGS_ABSEIL_STRING_CONVERSIONS_H_ + +#include <vector> + +#include "polyfills/base/base_export.h" +#include "base/containers/span.h" +#include "base/strings/string_piece.h" +#include "third_party/abseil-cpp/absl/strings/string_view.h" + +namespace gurl_base { + +// Converts `piece` to a string view, pointing to the same piece of memory. +constexpr absl::string_view StringPieceToStringView(StringPiece piece) { + return {piece.data(), piece.size()}; +} + +// Converts `view` to a string piece, pointing to the same piece of memory. +constexpr StringPiece StringViewToStringPiece(absl::string_view view) { + return {view.data(), view.size()}; +} + +// Converts `pieces` to string views, pointing to the same piece of memory. +BASE_EXPORT std::vector<absl::string_view> StringPiecesToStringViews( + span<const StringPiece> pieces); + +// Converts `views` to string pieces, pointing to the same piece of memory. +BASE_EXPORT std::vector<StringPiece> StringViewsToStringPieces( + span<const absl::string_view> views); + +} // namespace base + +#endif // BASE_STRINGS_ABSEIL_STRING_CONVERSIONS_H_
diff --git a/base/strings/abseil_string_conversions_unittest.cc b/base/strings/abseil_string_conversions_unittest.cc new file mode 100644 index 0000000..e5f70f0 --- /dev/null +++ b/base/strings/abseil_string_conversions_unittest.cc
@@ -0,0 +1,69 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/abseil_string_conversions.h" + +#include <vector> + +#include "base/containers/span.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_piece_forward.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/strings/string_view.h" + +namespace gurl_base { + +namespace { +using string_view = absl::string_view; +} // namespace + +TEST(AbseilStringConversionsTest, StringPieceToStringView) { + static constexpr StringPiece kPiece = "foo"; + static constexpr string_view kView = StringPieceToStringView(kPiece); + static_assert(kPiece.data() == kView.data(), ""); + static_assert(kPiece.size() == kView.size(), ""); +} + +TEST(AbseilStringConversionsTest, StringViewToStringPiece) { + static constexpr string_view kView = "bar"; + static constexpr StringPiece kPiece = StringViewToStringPiece(kView); + static_assert(kView.data() == kPiece.data(), ""); + static_assert(kView.size() == kPiece.size(), ""); +} + +TEST(AbseilStringConversionsTest, StringPiecesToStringViews) { + static constexpr StringPiece kFoo = "foo"; + static constexpr StringPiece kBar = "bar"; + static constexpr StringPiece kBaz = "baz"; + + const std::vector<StringPiece> kPieces = {kFoo, kBar, kBaz}; + const std::vector<string_view> kViews = StringPiecesToStringViews(kPieces); + + ASSERT_EQ(kViews.size(), 3u); + EXPECT_EQ(kViews[0].data(), kFoo); + EXPECT_EQ(kViews[0].size(), 3u); + EXPECT_EQ(kViews[1].data(), kBar); + EXPECT_EQ(kViews[1].size(), 3u); + EXPECT_EQ(kViews[2].data(), kBaz); + EXPECT_EQ(kViews[2].size(), 3u); +} + +TEST(AbseilStringConversionsTest, StringViewsToStringPieces) { + static constexpr string_view kFoo = "foo"; + static constexpr string_view kBar = "bar"; + static constexpr string_view kBaz = "baz"; + + const std::vector<string_view> kViews = {kFoo, kBar, kBaz}; + const std::vector<StringPiece> kPieces = StringViewsToStringPieces(kViews); + + ASSERT_EQ(kPieces.size(), 3u); + EXPECT_EQ(kPieces[0].data(), kFoo); + EXPECT_EQ(kPieces[0].size(), 3u); + EXPECT_EQ(kPieces[1].data(), kBar); + EXPECT_EQ(kPieces[1].size(), 3u); + EXPECT_EQ(kPieces[2].data(), kBaz); + EXPECT_EQ(kPieces[2].size(), 3u); +} + +} // namespace base
diff --git a/base/strings/escape.cc b/base/strings/escape.cc new file mode 100644 index 0000000..cf46fca --- /dev/null +++ b/base/strings/escape.cc
@@ -0,0 +1,443 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/escape.h" + +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversion_utils.h" +#include "base/third_party/icu/icu_utf.h" + +namespace gurl_base { + +namespace { + +// Contains nonzero when the corresponding character is unescapable for normal +// URLs. These characters are the ones that may change the parsing of a URL, so +// we don't want to unescape them sometimes. In many case we won't want to +// unescape spaces, but that is controlled by parameters to Unescape*. +// +// The basic rule is that we can't unescape anything that would changing parsing +// like # or ?. We also can't unescape &, =, or + since that could be part of a +// query and that could change the server's parsing of the query. Nor can we +// unescape \ since src/url/ will convert it to a /. +// +// Lastly, we can't unescape anything that doesn't have a canonical +// representation in a URL. This means that unescaping will change the URL, and +// you could get different behavior if you copy and paste the URL, or press +// enter in the URL bar. The list of characters that fall into this category +// are the ones labeled PASS (allow either escaped or unescaped) in the big +// lookup table at the top of url/url_canon_path.cc. Also, characters +// that have CHAR_QUERY set in url/url_canon_internal.cc but are not +// allowed in query strings according to http://www.ietf.org/rfc/rfc3261.txt are +// not unescaped, to avoid turning a valid url according to spec into an +// invalid one. +// clang-format off +const char kUrlUnescape[128] = { +// Null, control chars... + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// ' ' ! " # $ % & ' ( ) * + , - . / + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, +// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, +// @ A B C D E F G H I J K L M N O + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// P Q R S T U V W X Y Z [ \ ] ^ _ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, +// ` a b c d e f g h i j k l m n o + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// p q r s t u v w x y z { | } ~ <NBSP> + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, +}; +// clang-format on + +// Attempts to unescape the sequence at |index| within |escaped_text|. If +// successful, sets |value| to the unescaped value. Returns whether +// unescaping succeeded. +bool UnescapeUnsignedByteAtIndex(StringPiece escaped_text, + size_t index, + unsigned char* value) { + if ((index + 2) >= escaped_text.size()) + return false; + if (escaped_text[index] != '%') + return false; + char most_sig_digit(escaped_text[index + 1]); + char least_sig_digit(escaped_text[index + 2]); + if (IsHexDigit(most_sig_digit) && IsHexDigit(least_sig_digit)) { + *value = + HexDigitToInt(most_sig_digit) * 16 + HexDigitToInt(least_sig_digit); + return true; + } + return false; +} + +// Attempts to unescape and decode a UTF-8-encoded percent-escaped character at +// the specified index. On success, returns true, sets |code_point_out| to be +// the character's code point and |unescaped_out| to be the unescaped UTF-8 +// string. |unescaped_out| will always be 1/3rd the length of the substring of +// |escaped_text| that corresponds to the unescaped character. +bool UnescapeUTF8CharacterAtIndex(StringPiece escaped_text, + size_t index, + uint32_t* code_point_out, + std::string* unescaped_out) { + GURL_DCHECK(unescaped_out->empty()); + + unsigned char bytes[CBU8_MAX_LENGTH]; + if (!UnescapeUnsignedByteAtIndex(escaped_text, index, &bytes[0])) + return false; + + size_t num_bytes = 1; + + // If this is a lead byte, need to collect trail bytes as well. + if (CBU8_IS_LEAD(bytes[0])) { + // Look for the last trail byte of the UTF-8 character. Give up once + // reach max character length number of bytes, or hit an unescaped + // character. No need to check length of escaped_text, as + // UnescapeUnsignedByteAtIndex checks lengths. + while (num_bytes < size(bytes) && + UnescapeUnsignedByteAtIndex(escaped_text, index + num_bytes * 3, + &bytes[num_bytes]) && + CBU8_IS_TRAIL(bytes[num_bytes])) { + ++num_bytes; + } + } + + int32_t char_index = 0; + // Check if the unicode "character" that was just unescaped is valid. + if (!ReadUnicodeCharacter(reinterpret_cast<char*>(bytes), num_bytes, + &char_index, code_point_out)) { + return false; + } + + // It's possible that a prefix of |bytes| forms a valid UTF-8 character, + // and the rest are not valid UTF-8, so need to update |num_bytes| based + // on the result of ReadUnicodeCharacter(). + num_bytes = char_index + 1; + *unescaped_out = std::string(reinterpret_cast<char*>(bytes), num_bytes); + return true; +} + +// This method takes a Unicode code point and returns true if it should be +// unescaped, based on |rules|. +bool ShouldUnescapeCodePoint(UnescapeRule::Type rules, uint32_t code_point) { + // If this is an ASCII character, use the lookup table. + if (code_point < 0x80) { + return kUrlUnescape[code_point] || + // Allow some additional unescaping when flags are set. + (code_point == ' ' && (rules & UnescapeRule::SPACES)) || + // Allow any of the prohibited but non-control characters when doing + // "special" chars. + ((code_point == '/' || code_point == '\\') && + (rules & UnescapeRule::PATH_SEPARATORS)) || + (code_point > ' ' && code_point != '/' && code_point != '\\' && + (rules & UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)); + } + + // Compare the code point against a list of characters that can be used + // to spoof other URLs. + // + // Can't use icu to make this cleaner, because Cronet cannot depend on + // icu, and currently uses this file. + // TODO(https://crbug.com/829873): Try to make this use icu, both to + // protect against regressions as the Unicode standard is updated and to + // reduce the number of long lists of characters. + return !( + // Per http://tools.ietf.org/html/rfc3987#section-4.1, certain BiDi + // control characters are not allowed to appear unescaped in URLs. + code_point == 0x200E || // LEFT-TO-RIGHT MARK (%E2%80%8E) + code_point == 0x200F || // RIGHT-TO-LEFT MARK (%E2%80%8F) + code_point == 0x202A || // LEFT-TO-RIGHT EMBEDDING (%E2%80%AA) + code_point == 0x202B || // RIGHT-TO-LEFT EMBEDDING (%E2%80%AB) + code_point == 0x202C || // POP DIRECTIONAL FORMATTING (%E2%80%AC) + code_point == 0x202D || // LEFT-TO-RIGHT OVERRIDE (%E2%80%AD) + code_point == 0x202E || // RIGHT-TO-LEFT OVERRIDE (%E2%80%AE) + + // The Unicode Technical Report (TR9) as referenced by RFC 3987 above has + // since added some new BiDi control characters that are not safe to + // unescape. http://www.unicode.org/reports/tr9 + code_point == 0x061C || // ARABIC LETTER MARK (%D8%9C) + code_point == 0x2066 || // LEFT-TO-RIGHT ISOLATE (%E2%81%A6) + code_point == 0x2067 || // RIGHT-TO-LEFT ISOLATE (%E2%81%A7) + code_point == 0x2068 || // FIRST STRONG ISOLATE (%E2%81%A8) + code_point == 0x2069 || // POP DIRECTIONAL ISOLATE (%E2%81%A9) + + // The following spoofable characters are also banned in unescaped URLs, + // because they could be used to imitate parts of a web browser's UI. + code_point == 0x1F50F || // LOCK WITH INK PEN (%F0%9F%94%8F) + code_point == 0x1F510 || // CLOSED LOCK WITH KEY (%F0%9F%94%90) + code_point == 0x1F512 || // LOCK (%F0%9F%94%92) + code_point == 0x1F513 || // OPEN LOCK (%F0%9F%94%93) + + // Spaces are also banned, as they can be used to scroll text out of view. + code_point == 0x0085 || // NEXT LINE (%C2%85) + code_point == 0x00A0 || // NO-BREAK SPACE (%C2%A0) + code_point == 0x1680 || // OGHAM SPACE MARK (%E1%9A%80) + code_point == 0x2000 || // EN QUAD (%E2%80%80) + code_point == 0x2001 || // EM QUAD (%E2%80%81) + code_point == 0x2002 || // EN SPACE (%E2%80%82) + code_point == 0x2003 || // EM SPACE (%E2%80%83) + code_point == 0x2004 || // THREE-PER-EM SPACE (%E2%80%84) + code_point == 0x2005 || // FOUR-PER-EM SPACE (%E2%80%85) + code_point == 0x2006 || // SIX-PER-EM SPACE (%E2%80%86) + code_point == 0x2007 || // FIGURE SPACE (%E2%80%87) + code_point == 0x2008 || // PUNCTUATION SPACE (%E2%80%88) + code_point == 0x2009 || // THIN SPACE (%E2%80%89) + code_point == 0x200A || // HAIR SPACE (%E2%80%8A) + code_point == 0x2028 || // LINE SEPARATOR (%E2%80%A8) + code_point == 0x2029 || // PARAGRAPH SEPARATOR (%E2%80%A9) + code_point == 0x202F || // NARROW NO-BREAK SPACE (%E2%80%AF) + code_point == 0x205F || // MEDIUM MATHEMATICAL SPACE (%E2%81%9F) + code_point == 0x3000 || // IDEOGRAPHIC SPACE (%E3%80%80) + // U+2800 is rendered as a space, but is not considered whitespace (see + // crbug.com/1068531). + code_point == 0x2800 || // BRAILLE PATTERN BLANK (%E2%A0%80) + + // Default Ignorable ([:Default_Ignorable_Code_Point=Yes:]) and Format + // characters ([:Cf:]) are also banned (see crbug.com/824715). + code_point == 0x00AD || // SOFT HYPHEN (%C2%AD) + code_point == 0x034F || // COMBINING GRAPHEME JOINER (%CD%8F) + // Arabic number formatting + (code_point >= 0x0600 && code_point <= 0x0605) || + // U+061C is already banned as a BiDi control character. + code_point == 0x06DD || // ARABIC END OF AYAH (%DB%9D) + code_point == 0x070F || // SYRIAC ABBREVIATION MARK (%DC%8F) + code_point == 0x08E2 || // ARABIC DISPUTED END OF AYAH (%E0%A3%A2) + code_point == 0x115F || // HANGUL CHOSEONG FILLER (%E1%85%9F) + code_point == 0x1160 || // HANGUL JUNGSEONG FILLER (%E1%85%A0) + code_point == 0x17B4 || // KHMER VOWEL INHERENT AQ (%E1%9E%B4) + code_point == 0x17B5 || // KHMER VOWEL INHERENT AA (%E1%9E%B5) + code_point == 0x180B || // MONGOLIAN FREE VARIATION SELECTOR ONE + // (%E1%A0%8B) + code_point == 0x180C || // MONGOLIAN FREE VARIATION SELECTOR TWO + // (%E1%A0%8C) + code_point == 0x180D || // MONGOLIAN FREE VARIATION SELECTOR THREE + // (%E1%A0%8D) + code_point == 0x180E || // MONGOLIAN VOWEL SEPARATOR (%E1%A0%8E) + code_point == 0x200B || // ZERO WIDTH SPACE (%E2%80%8B) + code_point == 0x200C || // ZERO WIDTH SPACE NON-JOINER (%E2%80%8C) + code_point == 0x200D || // ZERO WIDTH JOINER (%E2%80%8D) + // U+200E, U+200F, U+202A--202E, and U+2066--2069 are already banned as + // BiDi control characters. + code_point == 0x2060 || // WORD JOINER (%E2%81%A0) + code_point == 0x2061 || // FUNCTION APPLICATION (%E2%81%A1) + code_point == 0x2062 || // INVISIBLE TIMES (%E2%81%A2) + code_point == 0x2063 || // INVISIBLE SEPARATOR (%E2%81%A3) + code_point == 0x2064 || // INVISIBLE PLUS (%E2%81%A4) + code_point == 0x2065 || // null (%E2%81%A5) + // 0x2066--0x2069 are already banned as a BiDi control characters. + // General Punctuation - Deprecated (U+206A--206F) + (code_point >= 0x206A && code_point <= 0x206F) || + code_point == 0x3164 || // HANGUL FILLER (%E3%85%A4) + (code_point >= 0xFFF0 && code_point <= 0xFFF8) || // null + // Variation selectors (%EF%B8%80 -- %EF%B8%8F) + (code_point >= 0xFE00 && code_point <= 0xFE0F) || + code_point == 0xFEFF || // ZERO WIDTH NO-BREAK SPACE (%EF%BB%BF) + code_point == 0xFFA0 || // HALFWIDTH HANGUL FILLER (%EF%BE%A0) + code_point == 0xFFF9 || // INTERLINEAR ANNOTATION ANCHOR (%EF%BF%B9) + code_point == 0xFFFA || // INTERLINEAR ANNOTATION SEPARATOR (%EF%BF%BA) + code_point == 0xFFFB || // INTERLINEAR ANNOTATION TERMINATOR (%EF%BF%BB) + code_point == 0x110BD || // KAITHI NUMBER SIGN (%F0%91%82%BD) + code_point == 0x110CD || // KAITHI NUMBER SIGN ABOVE (%F0%91%83%8D) + // Egyptian hieroglyph formatting (%F0%93%90%B0 -- %F0%93%90%B8) + (code_point >= 0x13430 && code_point <= 0x13438) || + // Shorthand format controls (%F0%9B%B2%A0 -- %F0%9B%B2%A3) + (code_point >= 0x1BCA0 && code_point <= 0x1BCA3) || + // Beams and slurs (%F0%9D%85%B3 -- %F0%9D%85%BA) + (code_point >= 0x1D173 && code_point <= 0x1D17A) || + // Tags, Variation Selectors, nulls + (code_point >= 0xE0000 && code_point <= 0xE0FFF)); +} + +// Unescapes |escaped_text| according to |rules|, returning the resulting +// string. Fills in an |adjustments| parameter, if non-nullptr, so it reflects +// the alterations done to the string that are not one-character-to-one- +// character. The resulting |adjustments| will always be sorted by increasing +// offset. +std::string UnescapeURLWithAdjustmentsImpl( + StringPiece escaped_text, + UnescapeRule::Type rules, + OffsetAdjuster::Adjustments* adjustments) { + if (adjustments) + adjustments->clear(); + // Do not unescape anything, return the |escaped_text| text. + if (rules == UnescapeRule::NONE) + return escaped_text.as_string(); + + // The output of the unescaping is always smaller than the input, so we can + // reserve the input size to make sure we have enough buffer and don't have + // to allocate in the loop below. + std::string result; + result.reserve(escaped_text.length()); + + // Locations of adjusted text. + for (size_t i = 0, max = escaped_text.size(); i < max;) { + // Try to unescape the character. + uint32_t code_point; + std::string unescaped; + if (!UnescapeUTF8CharacterAtIndex(escaped_text, i, &code_point, + &unescaped)) { + // Check if the next character can be unescaped, but not as a valid UTF-8 + // character. In that case, just unescaped and write the non-sense + // character. + // + // TODO(https://crbug.com/829868): Do not unescape illegal UTF-8 + // sequences. + unsigned char non_utf8_byte; + if (UnescapeUnsignedByteAtIndex(escaped_text, i, &non_utf8_byte)) { + result.push_back(non_utf8_byte); + if (adjustments) + adjustments->push_back(OffsetAdjuster::Adjustment(i, 3, 1)); + i += 3; + continue; + } + + // Character is not escaped, so append as is, unless it's a '+' and + // REPLACE_PLUS_WITH_SPACE is being applied. + if (escaped_text[i] == '+' && + (rules & UnescapeRule::REPLACE_PLUS_WITH_SPACE)) { + result.push_back(' '); + } else { + result.push_back(escaped_text[i]); + } + ++i; + continue; + } + + GURL_DCHECK(!unescaped.empty()); + + if (!ShouldUnescapeCodePoint(rules, code_point)) { + // If it's a valid UTF-8 character, but not safe to unescape, copy all + // bytes directly. + result.append(escaped_text.begin() + i, + escaped_text.begin() + i + 3 * unescaped.length()); + i += unescaped.length() * 3; + continue; + } + + // If the code point is allowed, and append the entire unescaped character. + result.append(unescaped); + if (adjustments) { + for (size_t j = 0; j < unescaped.length(); ++j) { + adjustments->push_back(OffsetAdjuster::Adjustment(i + j * 3, 3, 1)); + } + } + i += 3 * unescaped.length(); + } + + return result; +} + +} // namespace + +std::string UnescapeURLComponent(StringPiece escaped_text, + UnescapeRule::Type rules) { + return UnescapeURLWithAdjustmentsImpl(escaped_text, rules, nullptr); +} + +string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments( + StringPiece text, + UnescapeRule::Type rules, + OffsetAdjuster::Adjustments* adjustments) { + string16 result; + OffsetAdjuster::Adjustments unescape_adjustments; + std::string unescaped_url( + UnescapeURLWithAdjustmentsImpl(text, rules, &unescape_adjustments)); + if (UTF8ToUTF16WithAdjustments(unescaped_url.data(), unescaped_url.length(), + &result, adjustments)) { + // Character set looks like it's valid. + if (adjustments) { + OffsetAdjuster::MergeSequentialAdjustments(unescape_adjustments, + adjustments); + } + return result; + } + // Character set is not valid. Return the escaped version. + return UTF8ToUTF16WithAdjustments(text, adjustments); +} + +std::string UnescapeBinaryURLComponent(StringPiece escaped_text, + UnescapeRule::Type rules) { + // Only NORMAL and REPLACE_PLUS_WITH_SPACE are supported. + GURL_DCHECK(rules != UnescapeRule::NONE); + GURL_DCHECK(!(rules & + ~(UnescapeRule::NORMAL | UnescapeRule::REPLACE_PLUS_WITH_SPACE))); + + std::string unescaped_text; + + // The output of the unescaping is always smaller than the input, so we can + // reserve the input size to make sure we have enough buffer and don't have + // to allocate in the loop below. + // Increase capacity before size, as just resizing can grow capacity + // needlessly beyond our requested size. + unescaped_text.reserve(escaped_text.size()); + unescaped_text.resize(escaped_text.size()); + + size_t output_index = 0; + + for (size_t i = 0, max = escaped_text.size(); i < max;) { + unsigned char byte; + // UnescapeUnsignedByteAtIndex does bounds checking, so this is always safe + // to call. + if (UnescapeUnsignedByteAtIndex(escaped_text, i, &byte)) { + unescaped_text[output_index++] = byte; + i += 3; + continue; + } + + if ((rules & UnescapeRule::REPLACE_PLUS_WITH_SPACE) && + escaped_text[i] == '+') { + unescaped_text[output_index++] = ' '; + ++i; + continue; + } + + unescaped_text[output_index++] = escaped_text[i++]; + } + + GURL_DCHECK_LE(output_index, unescaped_text.size()); + unescaped_text.resize(output_index); + return unescaped_text; +} + +bool UnescapeBinaryURLComponentSafe(StringPiece escaped_text, + bool fail_on_path_separators, + std::string* unescaped_text) { + unescaped_text->clear(); + + std::set<unsigned char> illegal_encoded_bytes; + for (char c = '\x00'; c < '\x20'; ++c) { + illegal_encoded_bytes.insert(c); + } + if (fail_on_path_separators) { + illegal_encoded_bytes.insert('/'); + illegal_encoded_bytes.insert('\\'); + } + if (ContainsEncodedBytes(escaped_text, illegal_encoded_bytes)) + return false; + + *unescaped_text = UnescapeBinaryURLComponent(escaped_text); + return true; +} + +bool ContainsEncodedBytes(StringPiece escaped_text, + const std::set<unsigned char>& bytes) { + for (size_t i = 0, max = escaped_text.size(); i < max;) { + unsigned char byte; + // UnescapeUnsignedByteAtIndex does bounds checking, so this is always safe + // to call. + if (UnescapeUnsignedByteAtIndex(escaped_text, i, &byte)) { + if (bytes.find(byte) != bytes.end()) + return true; + + i += 3; + continue; + } + + ++i; + } + + return false; +} + +} // namespace base \ No newline at end of file
diff --git a/base/strings/escape.h b/base/strings/escape.h new file mode 100644 index 0000000..0bb6aea --- /dev/null +++ b/base/strings/escape.h
@@ -0,0 +1,121 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRINGS_ESCAPE_H_ +#define BASE_STRINGS_ESCAPE_H_ + +#include <stdint.h> + +#include <set> +#include <string> + +#include "polyfills/base/base_export.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "base/strings/utf_offset_string_conversions.h" + +namespace gurl_base { + +class UnescapeRule { + public: + // A combination of the following flags that is passed to the unescaping + // functions. + typedef uint32_t Type; + + enum { + // Don't unescape anything at all. + NONE = 0, + + // Don't unescape anything special, but all normal unescaping will happen. + // This is a placeholder and can't be combined with other flags (since it's + // just the absence of them). All other unescape rules imply "normal" in + // addition to their special meaning. Things like escaped letters, digits, + // and most symbols will get unescaped with this mode. + NORMAL = 1 << 0, + + // Convert %20 to spaces. In some places where we're showing URLs, we may + // want this. In places where the URL may be copied and pasted out, then + // you wouldn't want this since it might not be interpreted in one piece + // by other applications. Other UTF-8 spaces will not be unescaped. + SPACES = 1 << 1, + + // Unescapes '/' and '\\'. If these characters were unescaped, the resulting + // URL won't be the same as the source one. Moreover, they are dangerous to + // unescape in strings that will be used as file paths or names. This value + // should only be used when slashes don't have special meaning, like data + // URLs. + PATH_SEPARATORS = 1 << 2, + + // Unescapes various characters that will change the meaning of URLs, + // including '%', '+', '&', '#'. Does not unescape path separators. + // If these characters were unescaped, the resulting URL won't be the same + // as the source one. This flag is used when generating final output like + // filenames for URLs where we won't be interpreting as a URL and want to do + // as much unescaping as possible. + URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS = 1 << 3, + + // URL queries use "+" for space. This flag controls that replacement. + REPLACE_PLUS_WITH_SPACE = 1 << 4, + }; +}; + +// Unescapes |escaped_text| and returns the result. +// Unescaping consists of looking for the exact pattern "%XX", where each X is +// a hex digit, and converting to the character with the numerical value of +// those digits. Thus "i%20=%203%3b" unescapes to "i = 3;", if the +// "UnescapeRule::SPACES" used. +// +// This method does not ensure that the output is a valid string using any +// character encoding. However, it does leave escaped certain byte sequences +// that would be dangerous to display to the user, because if interpreted as +// UTF-8, they could be used to mislead the user. Callers that want to +// unconditionally unescape everything for uses other than displaying data to +// the user should use UnescapeBinaryURLComponent(). +BASE_EXPORT std::string UnescapeURLComponent(StringPiece escaped_text, + UnescapeRule::Type rules); + +// Unescapes the given substring as a URL, and then tries to interpret the +// result as being encoded as UTF-8. If the result is convertible into UTF-8, it +// will be returned as converted. If it is not, the original escaped string will +// be converted into a string16 and returned. |adjustments| provides +// information on how the original string was adjusted to get the string +// returned. +BASE_EXPORT string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments( + StringPiece text, + UnescapeRule::Type rules, + OffsetAdjuster::Adjustments* adjustments); + +// Unescapes a component of a URL for use as binary data. Unlike +// UnescapeURLComponent, leaves nothing unescaped, including nulls, invalid +// characters, characters that are unsafe to display, etc. This should *not* +// be used when displaying the decoded data to the user. +// +// Only the NORMAL and REPLACE_PLUS_WITH_SPACE rules are allowed. +BASE_EXPORT std::string UnescapeBinaryURLComponent( + StringPiece escaped_text, + UnescapeRule::Type rules = UnescapeRule::NORMAL); + +// Variant of UnescapeBinaryURLComponent(). Writes output to |unescaped_text|. +// Returns true on success, returns false and clears |unescaped_text| on +// failure. Fails on characters escaped that are unsafe to unescape in some +// contexts, which are defined as characters "\0" through "\x1F" (Which includes +// CRLF but not space), and optionally path separators. Path separators include +// both forward and backward slashes on all platforms. Does not fail if any of +// those characters appear unescaped in the input string. +BASE_EXPORT bool UnescapeBinaryURLComponentSafe(StringPiece escaped_text, + bool fail_on_path_separators, + std::string* unescaped_text); + +// Returns true if |escaped_text| contains any element of |bytes| in +// percent-encoded form. +// +// For example, if |bytes| is {'%', '/'}, returns true if |escaped_text| +// contains "%25" or "%2F", but not if it just contains bare '%' or '/' +// characters. +BASE_EXPORT bool ContainsEncodedBytes(StringPiece escaped_text, + const std::set<unsigned char>& bytes); + +} // namespace base + +#endif // BASE_STRINGS_ESCAPE_H_
diff --git a/base/strings/escape_unittest.cc b/base/strings/escape_unittest.cc new file mode 100644 index 0000000..e6c0b1a --- /dev/null +++ b/base/strings/escape_unittest.cc
@@ -0,0 +1,429 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <algorithm> +#include <string> + +#include "base/strings/escape.h" + +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gurl_base { + +struct UnescapeURLCase { + const char* input; + UnescapeRule::Type rules; + const char* output; +}; + +struct UnescapeAndDecodeCase { + const char* input; + + // The expected output when run through UnescapeURL. + const char* url_unescaped; + + // The expected output when run through UnescapeQuery. + const char* query_unescaped; + + // The expected output when run through UnescapeAndDecodeURLComponent. + const wchar_t* decoded; +}; + +struct AdjustOffsetCase { + const char* input; + size_t input_offset; + size_t output_offset; +}; + +TEST(EscapeTest, DataURLWithAccentedCharacters) { + const std::string url = + "text/html;charset=utf-8,%3Chtml%3E%3Cbody%3ETonton,%20ton%20th%C3" + "%A9%20t'a-t-il%20%C3%B4t%C3%A9%20ta%20toux%20"; + + OffsetAdjuster::Adjustments adjustments; + UnescapeAndDecodeUTF8URLComponentWithAdjustments(url, UnescapeRule::SPACES, + &adjustments); +} + +TEST(EscapeTest, UnescapeURLComponent) { + const UnescapeURLCase kUnescapeCases[] = { + {"", UnescapeRule::NORMAL, ""}, + {"%2", UnescapeRule::NORMAL, "%2"}, + {"%%%%%%", UnescapeRule::NORMAL, "%%%%%%"}, + {"Don't escape anything", UnescapeRule::NORMAL, "Don't escape anything"}, + {"Invalid %escape %2", UnescapeRule::NORMAL, "Invalid %escape %2"}, + {"Some%20random text %25%2dOK", UnescapeRule::NONE, + "Some%20random text %25%2dOK"}, + {"Some%20random text %25%2dOK", UnescapeRule::NORMAL, + "Some%20random text %25-OK"}, + {"Some%20random text %25%E1%A6", UnescapeRule::NORMAL, + "Some%20random text %25\xE1\xA6"}, + {"Some%20random text %25%E1%A6OK", UnescapeRule::NORMAL, + "Some%20random text %25\xE1\xA6OK"}, + {"Some%20random text %25%E1%A6%99OK", UnescapeRule::NORMAL, + "Some%20random text %25\xE1\xA6\x99OK"}, + + // BiDi Control characters should not be unescaped. + {"Some%20random text %25%D8%9COK", UnescapeRule::NORMAL, + "Some%20random text %25%D8%9COK"}, + {"Some%20random text %25%E2%80%8EOK", UnescapeRule::NORMAL, + "Some%20random text %25%E2%80%8EOK"}, + {"Some%20random text %25%E2%80%8FOK", UnescapeRule::NORMAL, + "Some%20random text %25%E2%80%8FOK"}, + {"Some%20random text %25%E2%80%AAOK", UnescapeRule::NORMAL, + "Some%20random text %25%E2%80%AAOK"}, + {"Some%20random text %25%E2%80%ABOK", UnescapeRule::NORMAL, + "Some%20random text %25%E2%80%ABOK"}, + {"Some%20random text %25%E2%80%AEOK", UnescapeRule::NORMAL, + "Some%20random text %25%E2%80%AEOK"}, + {"Some%20random text %25%E2%81%A6OK", UnescapeRule::NORMAL, + "Some%20random text %25%E2%81%A6OK"}, + {"Some%20random text %25%E2%81%A9OK", UnescapeRule::NORMAL, + "Some%20random text %25%E2%81%A9OK"}, + + // Certain banned characters should not be unescaped. + // U+1F50F LOCK WITH INK PEN + {"Some%20random text %25%F0%9F%94%8FOK", UnescapeRule::NORMAL, + "Some%20random text %25%F0%9F%94%8FOK"}, + // U+1F510 CLOSED LOCK WITH KEY + {"Some%20random text %25%F0%9F%94%90OK", UnescapeRule::NORMAL, + "Some%20random text %25%F0%9F%94%90OK"}, + // U+1F512 LOCK + {"Some%20random text %25%F0%9F%94%92OK", UnescapeRule::NORMAL, + "Some%20random text %25%F0%9F%94%92OK"}, + // U+1F513 OPEN LOCK + {"Some%20random text %25%F0%9F%94%93OK", UnescapeRule::NORMAL, + "Some%20random text %25%F0%9F%94%93OK"}, + + // Spaces + {"(%C2%85)(%C2%A0)(%E1%9A%80)(%E2%80%80)", UnescapeRule::NORMAL, + "(%C2%85)(%C2%A0)(%E1%9A%80)(%E2%80%80)"}, + {"(%E2%80%81)(%E2%80%82)(%E2%80%83)(%E2%80%84)", UnescapeRule::NORMAL, + "(%E2%80%81)(%E2%80%82)(%E2%80%83)(%E2%80%84)"}, + {"(%E2%80%85)(%E2%80%86)(%E2%80%87)(%E2%80%88)", UnescapeRule::NORMAL, + "(%E2%80%85)(%E2%80%86)(%E2%80%87)(%E2%80%88)"}, + {"(%E2%80%89)(%E2%80%8A)(%E2%80%A8)(%E2%80%A9)", UnescapeRule::NORMAL, + "(%E2%80%89)(%E2%80%8A)(%E2%80%A8)(%E2%80%A9)"}, + {"(%E2%80%AF)(%E2%81%9F)(%E3%80%80)", UnescapeRule::NORMAL, + "(%E2%80%AF)(%E2%81%9F)(%E3%80%80)"}, + {"(%E2%A0%80)", UnescapeRule::NORMAL, "(%E2%A0%80)"}, + + // Default Ignorable and Formatting characters should not be unescaped. + {"(%E2%81%A5)(%EF%BF%B0)(%EF%BF%B8)", UnescapeRule::NORMAL, + "(%E2%81%A5)(%EF%BF%B0)(%EF%BF%B8)"}, + {"(%F3%A0%82%80)(%F3%A0%83%BF)(%F3%A0%87%B0)", UnescapeRule::NORMAL, + "(%F3%A0%82%80)(%F3%A0%83%BF)(%F3%A0%87%B0)"}, + {"(%F3%A0%BF%BF)(%C2%AD)(%CD%8F)", UnescapeRule::NORMAL, + "(%F3%A0%BF%BF)(%C2%AD)(%CD%8F)"}, + {"(%D8%80%20)(%D8%85)(%DB%9D)(%DC%8F)(%E0%A3%A2)", UnescapeRule::NORMAL, + "(%D8%80%20)(%D8%85)(%DB%9D)(%DC%8F)(%E0%A3%A2)"}, + {"(%E1%85%9F)(%E1%85%A0)(%E1%9E%B4)(%E1%9E%B5)", UnescapeRule::NORMAL, + "(%E1%85%9F)(%E1%85%A0)(%E1%9E%B4)(%E1%9E%B5)"}, + {"(%E1%A0%8B)(%E1%A0%8C)(%E1%A0%8D)(%E1%A0%8E)", UnescapeRule::NORMAL, + "(%E1%A0%8B)(%E1%A0%8C)(%E1%A0%8D)(%E1%A0%8E)"}, + {"(%E2%80%8B)(%E2%80%8C)(%E2%80%8D)(%E2%81%A0)", UnescapeRule::NORMAL, + "(%E2%80%8B)(%E2%80%8C)(%E2%80%8D)(%E2%81%A0)"}, + {"(%E2%81%A1)(%E2%81%A2)(%E2%81%A3)(%E2%81%A4)", UnescapeRule::NORMAL, + "(%E2%81%A1)(%E2%81%A2)(%E2%81%A3)(%E2%81%A4)"}, + {"(%E3%85%A4)(%EF%BB%BF)(%EF%BE%A0)(%EF%BF%B9)", UnescapeRule::NORMAL, + "(%E3%85%A4)(%EF%BB%BF)(%EF%BE%A0)(%EF%BF%B9)"}, + {"(%EF%BF%BB)(%F0%91%82%BD)(%F0%91%83%8D)", UnescapeRule::NORMAL, + "(%EF%BF%BB)(%F0%91%82%BD)(%F0%91%83%8D)"}, + {"(%F0%93%90%B0)(%F0%93%90%B8)", UnescapeRule::NORMAL, + "(%F0%93%90%B0)(%F0%93%90%B8)"}, + // General Punctuation - Deprecated (U+206A--206F) + {"(%E2%81%AA)(%E2%81%AD)(%E2%81%AF)", UnescapeRule::NORMAL, + "(%E2%81%AA)(%E2%81%AD)(%E2%81%AF)"}, + // Variation selectors (U+FE00--FE0F) + {"(%EF%B8%80)(%EF%B8%8C)(%EF%B8%8D)", UnescapeRule::NORMAL, + "(%EF%B8%80)(%EF%B8%8C)(%EF%B8%8D)"}, + // Shorthand format controls (U+1BCA0--1BCA3) + {"(%F0%9B%B2%A0)(%F0%9B%B2%A1)(%F0%9B%B2%A3)", UnescapeRule::NORMAL, + "(%F0%9B%B2%A0)(%F0%9B%B2%A1)(%F0%9B%B2%A3)"}, + // Musical symbols beams and slurs (U+1D173--1D17A) + {"(%F0%9D%85%B3)(%F0%9D%85%B9)(%F0%9D%85%BA)", UnescapeRule::NORMAL, + "(%F0%9D%85%B3)(%F0%9D%85%B9)(%F0%9D%85%BA)"}, + // Tags block (U+E0000--E007F), includes unassigned points + {"(%F3%A0%80%80)(%F3%A0%80%81)(%F3%A0%81%8F)", UnescapeRule::NORMAL, + "(%F3%A0%80%80)(%F3%A0%80%81)(%F3%A0%81%8F)"}, + // Ideographic-specific variation selectors (U+E0100--E01EF) + {"(%F3%A0%84%80)(%F3%A0%84%90)(%F3%A0%87%AF)", UnescapeRule::NORMAL, + "(%F3%A0%84%80)(%F3%A0%84%90)(%F3%A0%87%AF)"}, + + // Two spoofing characters in a row should not be unescaped. + {"%D8%9C%D8%9C", UnescapeRule::NORMAL, "%D8%9C%D8%9C"}, + // Non-spoofing characters surrounded by spoofing characters should be + // unescaped. + {"%D8%9C%C2%A1%D8%9C%C2%A1", UnescapeRule::NORMAL, + "%D8%9C\xC2\xA1%D8%9C\xC2\xA1"}, + // Invalid UTF-8 characters surrounded by spoofing characters should be + // unescaped. + {"%D8%9C%85%D8%9C%85", UnescapeRule::NORMAL, "%D8%9C\x85%D8%9C\x85"}, + // Test with enough trail bytes to overflow the CBU8_MAX_LENGTH-byte + // buffer. The first two bytes are a spoofing character as well. + {"%D8%9C%9C%9C%9C%9C%9C%9C%9C%9C%9C", UnescapeRule::NORMAL, + "%D8%9C\x9C\x9C\x9C\x9C\x9C\x9C\x9C\x9C\x9C"}, + + {"Some%20random text %25%2dOK", UnescapeRule::SPACES, + "Some random text %25-OK"}, + {"Some%20random text %25%2dOK", UnescapeRule::PATH_SEPARATORS, + "Some%20random text %25-OK"}, + {"Some%20random text %25%2dOK", + UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS, + "Some%20random text %-OK"}, + {"Some%20random text %25%2dOK", + UnescapeRule::SPACES | + UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS, + "Some random text %-OK"}, + {"%A0%B1%C2%D3%E4%F5", UnescapeRule::NORMAL, "\xA0\xB1\xC2\xD3\xE4\xF5"}, + {"%Aa%Bb%Cc%Dd%Ee%Ff", UnescapeRule::NORMAL, "\xAa\xBb\xCc\xDd\xEe\xFf"}, + // Certain URL-sensitive characters should not be unescaped unless asked. + {"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+", + UnescapeRule::SPACES, "Hello %13%10world %23# %3F? %3D= %26& %25% %2B+"}, + {"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+", + UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS, + "Hello%20%13%10world ## ?? == && %% ++"}, + // We can neither escape nor unescape '@' since some websites expect it to + // be preserved as either '@' or "%40". + // See http://b/996720 and http://crbug.com/23933 . + {"me@my%40example", UnescapeRule::NORMAL, "me@my%40example"}, + // Control characters. + {"%01%02%03%04%05%06%07%08%09 %25", + UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS, + "%01%02%03%04%05%06%07%08%09 %"}, + {"Hello%20%13%10%02", UnescapeRule::SPACES, "Hello %13%10%02"}, + + // '/' and '\\' should only be unescaped by PATH_SEPARATORS. + {"%2F%5C", UnescapeRule::PATH_SEPARATORS, "/\\"}, + }; + + for (const auto unescape_case : kUnescapeCases) { + EXPECT_EQ(unescape_case.output, + UnescapeURLComponent(unescape_case.input, unescape_case.rules)); + } + + // Test NULL character unescaping, which can't be tested above since those are + // just char pointers. + std::string input("Null"); + input.push_back(0); // Also have a NULL in the input. + input.append("%00%39Test"); + + std::string expected = "Null"; + expected.push_back(0); + expected.append("%009Test"); + EXPECT_EQ(expected, UnescapeURLComponent(input, UnescapeRule::NORMAL)); +} + +TEST(EscapeTest, UnescapeAndDecodeUTF8URLComponentWithAdjustments) { + const UnescapeAndDecodeCase unescape_cases[] = { + {"%", "%", "%", L"%"}, + {"+", "+", " ", L"+"}, + {"%2+", "%2+", "%2 ", L"%2+"}, + {"+%%%+%%%", "+%%%+%%%", " %%% %%%", L"+%%%+%%%"}, + {"Don't escape anything", "Don't escape anything", + "Don't escape anything", L"Don't escape anything"}, + {"+Invalid %escape %2+", "+Invalid %escape %2+", " Invalid %escape %2 ", + L"+Invalid %escape %2+"}, + {"Some random text %25%2dOK", "Some random text %25-OK", + "Some random text %25-OK", L"Some random text %25-OK"}, + {"%01%02%03%04%05%06%07%08%09", "%01%02%03%04%05%06%07%08%09", + "%01%02%03%04%05%06%07%08%09", L"%01%02%03%04%05%06%07%08%09"}, + {"%E4%BD%A0+%E5%A5%BD", "\xE4\xBD\xA0+\xE5\xA5\xBD", + "\xE4\xBD\xA0 \xE5\xA5\xBD", L"\x4f60+\x597d"}, + {"%ED%ED", // Invalid UTF-8. + "\xED\xED", "\xED\xED", L"%ED%ED"}, // Invalid UTF-8 -> kept unescaped. + }; + + for (const auto& unescape_case : unescape_cases) { + std::string unescaped = + UnescapeURLComponent(unescape_case.input, UnescapeRule::NORMAL); + EXPECT_EQ(std::string(unescape_case.url_unescaped), unescaped); + + unescaped = UnescapeURLComponent(unescape_case.input, + UnescapeRule::REPLACE_PLUS_WITH_SPACE); + EXPECT_EQ(std::string(unescape_case.query_unescaped), unescaped); + + // The adjustments argument is covered by the next test. + // + // TODO: Need to test unescape_spaces and unescape_percent. + string16 decoded = UnescapeAndDecodeUTF8URLComponentWithAdjustments( + unescape_case.input, UnescapeRule::NORMAL, nullptr); + EXPECT_EQ(WideToUTF16(unescape_case.decoded), decoded); + } +} + +TEST(EscapeTest, AdjustOffset) { + const AdjustOffsetCase adjust_cases[] = { + {"", 0, 0}, + {"test", 0, 0}, + {"test", 2, 2}, + {"test", 4, 4}, + {"test", std::string::npos, std::string::npos}, + {"%2dtest", 6, 4}, + {"%2dtest", 3, 1}, + {"%2dtest", 2, std::string::npos}, + {"%2dtest", 1, std::string::npos}, + {"%2dtest", 0, 0}, + {"test%2d", 2, 2}, + {"test%2e", 2, 2}, + {"%E4%BD%A0+%E5%A5%BD", 9, 1}, + {"%E4%BD%A0+%E5%A5%BD", 6, std::string::npos}, + {"%E4%BD%A0+%E5%A5%BD", 0, 0}, + {"%E4%BD%A0+%E5%A5%BD", 10, 2}, + {"%E4%BD%A0+%E5%A5%BD", 19, 3}, + + {"hi%41test%E4%BD%A0+%E5%A5%BD", 18, 8}, + {"hi%41test%E4%BD%A0+%E5%A5%BD", 15, std::string::npos}, + {"hi%41test%E4%BD%A0+%E5%A5%BD", 9, 7}, + {"hi%41test%E4%BD%A0+%E5%A5%BD", 19, 9}, + {"hi%41test%E4%BD%A0+%E5%A5%BD", 28, 10}, + {"hi%41test%E4%BD%A0+%E5%A5%BD", 0, 0}, + {"hi%41test%E4%BD%A0+%E5%A5%BD", 2, 2}, + {"hi%41test%E4%BD%A0+%E5%A5%BD", 3, std::string::npos}, + {"hi%41test%E4%BD%A0+%E5%A5%BD", 5, 3}, + + {"%E4%BD%A0+%E5%A5%BDhi%41test", 9, 1}, + {"%E4%BD%A0+%E5%A5%BDhi%41test", 6, std::string::npos}, + {"%E4%BD%A0+%E5%A5%BDhi%41test", 0, 0}, + {"%E4%BD%A0+%E5%A5%BDhi%41test", 10, 2}, + {"%E4%BD%A0+%E5%A5%BDhi%41test", 19, 3}, + {"%E4%BD%A0+%E5%A5%BDhi%41test", 21, 5}, + {"%E4%BD%A0+%E5%A5%BDhi%41test", 22, std::string::npos}, + {"%E4%BD%A0+%E5%A5%BDhi%41test", 24, 6}, + {"%E4%BD%A0+%E5%A5%BDhi%41test", 28, 10}, + + {"%ED%B0%80+%E5%A5%BD", 6, 6}, // not convertible to UTF-8 + }; + + for (const auto& adjust_case : adjust_cases) { + size_t offset = adjust_case.input_offset; + OffsetAdjuster::Adjustments adjustments; + UnescapeAndDecodeUTF8URLComponentWithAdjustments( + adjust_case.input, UnescapeRule::NORMAL, &adjustments); + OffsetAdjuster::AdjustOffset(adjustments, &offset); + EXPECT_EQ(adjust_case.output_offset, offset) + << "input=" << adjust_case.input + << " offset=" << adjust_case.input_offset; + } +} + +TEST(EscapeTest, UnescapeBinaryURLComponent) { + const UnescapeURLCase kTestCases[] = { + // Check that ASCII characters with special handling in + // UnescapeURLComponent() are still unescaped. + {"%09%20%25foo%2F", UnescapeRule::NORMAL, "\x09 %foo/"}, + + // UTF-8 Characters banned by UnescapeURLComponent() should also be + // unescaped. + {"Some random text %D8%9COK", UnescapeRule::NORMAL, + "Some random text \xD8\x9COK"}, + {"Some random text %F0%9F%94%8FOK", UnescapeRule::NORMAL, + "Some random text \xF0\x9F\x94\x8FOK"}, + + // As should invalid UTF-8 characters. + {"%A0%A0%E9%E9%A0%A0%A0%A0", UnescapeRule::NORMAL, + "\xA0\xA0\xE9\xE9\xA0\xA0\xA0\xA0"}, + + // And valid UTF-8 characters that are not banned by + // UnescapeURLComponent() should be unescaped, too! + {"%C2%A1%C2%A1", UnescapeRule::NORMAL, "\xC2\xA1\xC2\xA1"}, + + // '+' should be left alone by default + {"++%2B++", UnescapeRule::NORMAL, "+++++"}, + // But should magically be turned into a space if requested. + {"++%2B++", UnescapeRule::REPLACE_PLUS_WITH_SPACE, " + "}, + }; + + for (const auto& test_case : kTestCases) { + EXPECT_EQ(test_case.output, + UnescapeBinaryURLComponent(test_case.input, test_case.rules)); + } + + // Test NULL character unescaping, which can't be tested above since those are + // just char pointers. + std::string input("Null"); + input.push_back(0); // Also have a NULL in the input. + input.append("%00%39Test"); + + std::string expected("Null"); + expected.push_back(0); + expected.push_back(0); + expected.append("9Test"); + EXPECT_EQ(expected, UnescapeBinaryURLComponent(input)); +} + +TEST(EscapeTest, UnescapeBinaryURLComponentSafe) { + const struct TestCase { + const char* input; + // Expected output. Null if call is expected to fail when + // |fail_on_path_separators| is false. + const char* expected_output; + // Whether |input| has any escaped path separators. + bool has_path_separators; + } kTestCases[] = { + // Spaces, percents, and invalid UTF-8 characters are all successfully + // unescaped. + {"%20%25foo%81", " %foo\x81", false}, + + // Characters disallowed unconditionally. + {"foo%00", nullptr, false}, + {"foo%01", nullptr, false}, + {"foo%0A", nullptr, false}, + {"foo%0D", nullptr, false}, + + // Path separators. + {"foo%2F", "foo/", true}, + {"foo%5C", "foo\\", true}, + + // Characters that are considered invalid to escape are ignored if passed + // in unescaped. + {"foo\x01\r/\\", "foo\x01\r/\\", false}, + }; + + for (const auto& test_case : kTestCases) { + SCOPED_TRACE(test_case.input); + + std::string output = "foo"; + if (!test_case.expected_output) { + EXPECT_FALSE(UnescapeBinaryURLComponentSafe( + test_case.input, false /* fail_on_path_separators */, &output)); + EXPECT_TRUE(output.empty()); + EXPECT_FALSE(UnescapeBinaryURLComponentSafe( + test_case.input, true /* fail_on_path_separators */, &output)); + EXPECT_TRUE(output.empty()); + continue; + } + EXPECT_TRUE(UnescapeBinaryURLComponentSafe( + test_case.input, false /* fail_on_path_separators */, &output)); + EXPECT_EQ(test_case.expected_output, output); + if (test_case.has_path_separators) { + EXPECT_FALSE(UnescapeBinaryURLComponentSafe( + test_case.input, true /* fail_on_path_separators */, &output)); + EXPECT_TRUE(output.empty()); + } else { + output = "foo"; + EXPECT_TRUE(UnescapeBinaryURLComponentSafe( + test_case.input, true /* fail_on_path_separators */, &output)); + EXPECT_EQ(test_case.expected_output, output); + } + } +} + +TEST(EscapeTest, ContainsEncodedBytes) { + EXPECT_FALSE(ContainsEncodedBytes("abc/def", {'/', '\\'})); + EXPECT_FALSE(ContainsEncodedBytes("abc%2Fdef", {'%'})); + EXPECT_TRUE(ContainsEncodedBytes("abc%252Fdef", {'%'})); + EXPECT_TRUE(ContainsEncodedBytes("abc%2Fdef", {'/', '\\'})); + EXPECT_TRUE(ContainsEncodedBytes("abc%5Cdef", {'/', '\\'})); + EXPECT_TRUE(ContainsEncodedBytes("abc%2fdef", {'/', '\\'})); + + // Should be looking for byte values, not UTF-8 character values. + EXPECT_TRUE(ContainsEncodedBytes("caf%C3%A9", {'\xc3'})); + EXPECT_FALSE(ContainsEncodedBytes("caf%C3%A9", {'\xe9'})); +} + +} // namespace base
diff --git a/base/strings/safe_sprintf.cc b/base/strings/safe_sprintf.cc index 726ccf9..e8bb070 100644 --- a/base/strings/safe_sprintf.cc +++ b/base/strings/safe_sprintf.cc
@@ -174,7 +174,7 @@ // |count_| will also be incremented by the number of bytes that were meant // to be emitted. The |pad| character is typically either a ' ' space // or a '0' zero, but other non-NUL values are legal. - // Returns "false", iff the the |buffer_| filled up (i.e. |count_| + // Returns "false", iff the |buffer_| filled up (i.e. |count_| // overflowed |size_|) at any time during padding. inline bool Pad(char pad, size_t padding, size_t len) { DEBUG_CHECK(pad);
diff --git a/base/strings/string_number_conversions_unittest.cc b/base/strings/string_number_conversions_unittest.cc index 6f8d171..62a31f6 100644 --- a/base/strings/string_number_conversions_unittest.cc +++ b/base/strings/string_number_conversions_unittest.cc
@@ -44,7 +44,8 @@ {0, "0", "0"}, {-1, "-1", "18446744073709551615"}, { - std::numeric_limits<int64_t>::max(), "9223372036854775807", + std::numeric_limits<int64_t>::max(), + "9223372036854775807", "9223372036854775807", }, {std::numeric_limits<int64_t>::min(), "-9223372036854775808", @@ -111,30 +112,32 @@ int output; bool success; } cases[] = { - {"0", 0, true}, - {"42", 42, true}, - {"42\x99", 42, false}, - {"\x99" "42\x99", 0, false}, - {"-2147483648", INT_MIN, true}, - {"2147483647", INT_MAX, true}, - {"", 0, false}, - {" 42", 42, false}, - {"42 ", 42, false}, - {"\t\n\v\f\r 42", 42, false}, - {"blah42", 0, false}, - {"42blah", 42, false}, - {"blah42blah", 0, false}, - {"-273.15", -273, false}, - {"+98.6", 98, false}, - {"--123", 0, false}, - {"++123", 0, false}, - {"-+123", 0, false}, - {"+-123", 0, false}, - {"-", 0, false}, - {"-2147483649", INT_MIN, false}, - {"-99999999999", INT_MIN, false}, - {"2147483648", INT_MAX, false}, - {"99999999999", INT_MAX, false}, + {"0", 0, true}, + {"42", 42, true}, + {"42\x99", 42, false}, + {"\x99" + "42\x99", + 0, false}, + {"-2147483648", INT_MIN, true}, + {"2147483647", INT_MAX, true}, + {"", 0, false}, + {" 42", 42, false}, + {"42 ", 42, false}, + {"\t\n\v\f\r 42", 42, false}, + {"blah42", 0, false}, + {"42blah", 42, false}, + {"blah42blah", 0, false}, + {"-273.15", -273, false}, + {"+98.6", 98, false}, + {"--123", 0, false}, + {"++123", 0, false}, + {"-+123", 0, false}, + {"+-123", 0, false}, + {"-", 0, false}, + {"-2147483649", INT_MIN, false}, + {"-99999999999", INT_MIN, false}, + {"2147483648", INT_MAX, false}, + {"99999999999", INT_MAX, false}, }; for (const auto& i : cases) { @@ -163,7 +166,7 @@ EXPECT_EQ(6, output); output = 0; - const char16 negative_wide_input[] = { 0xFF4D, '4', '2', 0}; + const char16 negative_wide_input[] = {0xFF4D, '4', '2', 0}; EXPECT_FALSE(StringToInt(string16(negative_wide_input), &output)); EXPECT_EQ(0, output); } @@ -174,31 +177,33 @@ unsigned output; bool success; } cases[] = { - {"0", 0, true}, - {"42", 42, true}, - {"42\x99", 42, false}, - {"\x99" "42\x99", 0, false}, - {"-2147483648", 0, false}, - {"2147483647", INT_MAX, true}, - {"", 0, false}, - {" 42", 42, false}, - {"42 ", 42, false}, - {"\t\n\v\f\r 42", 42, false}, - {"blah42", 0, false}, - {"42blah", 42, false}, - {"blah42blah", 0, false}, - {"-273.15", 0, false}, - {"+98.6", 98, false}, - {"--123", 0, false}, - {"++123", 0, false}, - {"-+123", 0, false}, - {"+-123", 0, false}, - {"-", 0, false}, - {"-2147483649", 0, false}, - {"-99999999999", 0, false}, - {"4294967295", UINT_MAX, true}, - {"4294967296", UINT_MAX, false}, - {"99999999999", UINT_MAX, false}, + {"0", 0, true}, + {"42", 42, true}, + {"42\x99", 42, false}, + {"\x99" + "42\x99", + 0, false}, + {"-2147483648", 0, false}, + {"2147483647", INT_MAX, true}, + {"", 0, false}, + {" 42", 42, false}, + {"42 ", 42, false}, + {"\t\n\v\f\r 42", 42, false}, + {"blah42", 0, false}, + {"42blah", 42, false}, + {"blah42blah", 0, false}, + {"-273.15", 0, false}, + {"+98.6", 98, false}, + {"--123", 0, false}, + {"++123", 0, false}, + {"-+123", 0, false}, + {"+-123", 0, false}, + {"-", 0, false}, + {"-2147483649", 0, false}, + {"-99999999999", 0, false}, + {"4294967295", UINT_MAX, true}, + {"4294967296", UINT_MAX, false}, + {"99999999999", UINT_MAX, false}, }; for (const auto& i : cases) { @@ -227,7 +232,7 @@ EXPECT_EQ(6U, output); output = 0; - const char16 negative_wide_input[] = { 0xFF4D, '4', '2', 0}; + const char16 negative_wide_input[] = {0xFF4D, '4', '2', 0}; EXPECT_FALSE(StringToUint(string16(negative_wide_input), &output)); EXPECT_EQ(0U, output); } @@ -439,35 +444,35 @@ int64_t output; bool success; } cases[] = { - {"0", 0, true}, - {"42", 66, true}, - {"-42", -66, true}, - {"+42", 66, true}, - {"7fffffff", INT_MAX, true}, - {"-80000000", INT_MIN, true}, - {"80000000", INT_MAX, false}, // Overflow test. - {"-80000001", INT_MIN, false}, // Underflow test. - {"0x42", 66, true}, - {"-0x42", -66, true}, - {"+0x42", 66, true}, - {"0x7fffffff", INT_MAX, true}, - {"-0x80000000", INT_MIN, true}, - {"-80000000", INT_MIN, true}, - {"80000000", INT_MAX, false}, // Overflow test. - {"-80000001", INT_MIN, false}, // Underflow test. - {"0x0f", 15, true}, - {"0f", 15, true}, - {" 45", 0x45, false}, - {"\t\n\v\f\r 0x45", 0x45, false}, - {" 45", 0x45, false}, - {"45 ", 0x45, false}, - {"45:", 0x45, false}, - {"efgh", 0xef, false}, - {"0xefgh", 0xef, false}, - {"hgfe", 0, false}, - {"-", 0, false}, - {"", 0, false}, - {"0x", 0, false}, + {"0", 0, true}, + {"42", 66, true}, + {"-42", -66, true}, + {"+42", 66, true}, + {"7fffffff", INT_MAX, true}, + {"-80000000", INT_MIN, true}, + {"80000000", INT_MAX, false}, // Overflow test. + {"-80000001", INT_MIN, false}, // Underflow test. + {"0x42", 66, true}, + {"-0x42", -66, true}, + {"+0x42", 66, true}, + {"0x7fffffff", INT_MAX, true}, + {"-0x80000000", INT_MIN, true}, + {"-80000000", INT_MIN, true}, + {"80000000", INT_MAX, false}, // Overflow test. + {"-80000001", INT_MIN, false}, // Underflow test. + {"0x0f", 15, true}, + {"0f", 15, true}, + {" 45", 0x45, false}, + {"\t\n\v\f\r 0x45", 0x45, false}, + {" 45", 0x45, false}, + {"45 ", 0x45, false}, + {"45:", 0x45, false}, + {"efgh", 0xef, false}, + {"0xefgh", 0xef, false}, + {"hgfe", 0, false}, + {"-", 0, false}, + {"", 0, false}, + {"0x", 0, false}, }; for (const auto& i : cases) { @@ -478,7 +483,9 @@ // One additional test to verify that conversion of numbers in strings with // embedded NUL characters. The NUL and extra data after it should be // interpreted as junk after the number. - const char input[] = "0xc0ffee\0" "9"; + const char input[] = + "0xc0ffee\0" + "9"; std::string input_string(input, gurl_base::size(input) - 1); int output; EXPECT_FALSE(HexStringToInt(input_string, &output)); @@ -543,7 +550,9 @@ // One additional test to verify that conversion of numbers in strings with // embedded NUL characters. The NUL and extra data after it should be // interpreted as junk after the number. - const char input[] = "0xc0ffee\0" "9"; + const char input[] = + "0xc0ffee\0" + "9"; std::string input_string(input, gurl_base::size(input) - 1); uint32_t output; EXPECT_FALSE(HexStringToUInt(input_string, &output)); @@ -602,7 +611,9 @@ // One additional test to verify that conversion of numbers in strings with // embedded NUL characters. The NUL and extra data after it should be // interpreted as junk after the number. - const char input[] = "0xc0ffee\0" "9"; + const char input[] = + "0xc0ffee\0" + "9"; std::string input_string(input, gurl_base::size(input) - 1); int64_t output; EXPECT_FALSE(HexStringToInt64(input_string, &output)); @@ -665,7 +676,9 @@ // One additional test to verify that conversion of numbers in strings with // embedded NUL characters. The NUL and extra data after it should be // interpreted as junk after the number. - const char input[] = "0xc0ffee\0" "9"; + const char input[] = + "0xc0ffee\0" + "9"; std::string input_string(input, gurl_base::size(input) - 1); uint64_t output; EXPECT_FALSE(HexStringToUInt64(input_string, &output)); @@ -680,23 +693,23 @@ size_t output_len; bool success; } cases[] = { - {"0", "", 0, false}, // odd number of characters fails - {"00", "\0", 1, true}, - {"42", "\x42", 1, true}, - {"-42", "", 0, false}, // any non-hex value fails - {"+42", "", 0, false}, - {"7fffffff", "\x7f\xff\xff\xff", 4, true}, - {"80000000", "\x80\0\0\0", 4, true}, - {"deadbeef", "\xde\xad\xbe\xef", 4, true}, - {"DeadBeef", "\xde\xad\xbe\xef", 4, true}, - {"0x42", "", 0, false}, // leading 0x fails (x is not hex) - {"0f", "\xf", 1, true}, - {"45 ", "\x45", 1, false}, - {"efgh", "\xef", 1, false}, - {"", "", 0, false}, - {"0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8, true}, - {"0123456789ABCDEF012345", - "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45", 11, true}, + {"0", "", 0, false}, // odd number of characters fails + {"00", "\0", 1, true}, + {"42", "\x42", 1, true}, + {"-42", "", 0, false}, // any non-hex value fails + {"+42", "", 0, false}, + {"7fffffff", "\x7f\xff\xff\xff", 4, true}, + {"80000000", "\x80\0\0\0", 4, true}, + {"deadbeef", "\xde\xad\xbe\xef", 4, true}, + {"DeadBeef", "\xde\xad\xbe\xef", 4, true}, + {"0x42", "", 0, false}, // leading 0x fails (x is not hex) + {"0f", "\xf", 1, true}, + {"45 ", "\x45", 1, false}, + {"efgh", "\xef", 1, false}, + {"", "", 0, false}, + {"0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8, true}, + {"0123456789ABCDEF012345", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45", + 11, true}, }; for (size_t test_i = 0; test_i < gurl_base::size(cases); ++test_i) { @@ -767,77 +780,79 @@ double output; bool success; } cases[] = { - // Test different forms of zero. - {"0", 0.0, true}, - {"+0", 0.0, true}, - {"-0", 0.0, true}, - {"0.0", 0.0, true}, - {"000000000000000000000000000000.0", 0.0, true}, - {"0.000000000000000000000000000", 0.0, true}, + // Test different forms of zero. + {"0", 0.0, true}, + {"+0", 0.0, true}, + {"-0", 0.0, true}, + {"0.0", 0.0, true}, + {"000000000000000000000000000000.0", 0.0, true}, + {"0.000000000000000000000000000", 0.0, true}, - // Test the answer. - {"42", 42.0, true}, - {"-42", -42.0, true}, + // Test the answer. + {"42", 42.0, true}, + {"-42", -42.0, true}, - // Test variances of an ordinary number. - {"123.45", 123.45, true}, - {"-123.45", -123.45, true}, - {"+123.45", 123.45, true}, + // Test variances of an ordinary number. + {"123.45", 123.45, true}, + {"-123.45", -123.45, true}, + {"+123.45", 123.45, true}, - // Test different forms of representation. - {"2.99792458e8", 299792458.0, true}, - {"149597870.691E+3", 149597870691.0, true}, - {"6.", 6.0, true}, + // Test different forms of representation. + {"2.99792458e8", 299792458.0, true}, + {"149597870.691E+3", 149597870691.0, true}, + {"6.", 6.0, true}, - // Test around the largest/smallest value that a double can represent. - {"9e307", 9e307, true}, - {"1.7976e308", 1.7976e308, true}, - {"1.7977e308", HUGE_VAL, false}, - {"1.797693134862315807e+308", HUGE_VAL, true}, - {"1.797693134862315808e+308", HUGE_VAL, false}, - {"9e308", HUGE_VAL, false}, - {"9e309", HUGE_VAL, false}, - {"9e999", HUGE_VAL, false}, - {"9e1999", HUGE_VAL, false}, - {"9e19999", HUGE_VAL, false}, - {"9e99999999999999999999", HUGE_VAL, false}, - {"-9e307", -9e307, true}, - {"-1.7976e308", -1.7976e308, true}, - {"-1.7977e308", -HUGE_VAL, false}, - {"-1.797693134862315807e+308", -HUGE_VAL, true}, - {"-1.797693134862315808e+308", -HUGE_VAL, false}, - {"-9e308", -HUGE_VAL, false}, - {"-9e309", -HUGE_VAL, false}, - {"-9e999", -HUGE_VAL, false}, - {"-9e1999", -HUGE_VAL, false}, - {"-9e19999", -HUGE_VAL, false}, - {"-9e99999999999999999999", -HUGE_VAL, false}, + // Test around the largest/smallest value that a double can represent. + {"9e307", 9e307, true}, + {"1.7976e308", 1.7976e308, true}, + {"1.7977e308", HUGE_VAL, false}, + {"1.797693134862315807e+308", HUGE_VAL, true}, + {"1.797693134862315808e+308", HUGE_VAL, false}, + {"9e308", HUGE_VAL, false}, + {"9e309", HUGE_VAL, false}, + {"9e999", HUGE_VAL, false}, + {"9e1999", HUGE_VAL, false}, + {"9e19999", HUGE_VAL, false}, + {"9e99999999999999999999", HUGE_VAL, false}, + {"-9e307", -9e307, true}, + {"-1.7976e308", -1.7976e308, true}, + {"-1.7977e308", -HUGE_VAL, false}, + {"-1.797693134862315807e+308", -HUGE_VAL, true}, + {"-1.797693134862315808e+308", -HUGE_VAL, false}, + {"-9e308", -HUGE_VAL, false}, + {"-9e309", -HUGE_VAL, false}, + {"-9e999", -HUGE_VAL, false}, + {"-9e1999", -HUGE_VAL, false}, + {"-9e19999", -HUGE_VAL, false}, + {"-9e99999999999999999999", -HUGE_VAL, false}, - // Test more exponents. - {"1e-2", 0.01, true}, - {"42 ", 42.0, false}, - {" 1e-2", 0.01, false}, - {"1e-2 ", 0.01, false}, - {"-1E-7", -0.0000001, true}, - {"01e02", 100, true}, - {"2.3e15", 2.3e15, true}, - {"100e-309", 100e-309, true}, + // Test more exponents. + {"1e-2", 0.01, true}, + {"42 ", 42.0, false}, + {" 1e-2", 0.01, false}, + {"1e-2 ", 0.01, false}, + {"-1E-7", -0.0000001, true}, + {"01e02", 100, true}, + {"2.3e15", 2.3e15, true}, + {"100e-309", 100e-309, true}, - // Test some invalid cases. - {"\t\n\v\f\r -123.45e2", -12345.0, false}, - {"+123 e4", 123.0, false}, - {"123e ", 123.0, false}, - {"123e", 123.0, false}, - {" 2.99", 2.99, false}, - {"1e3.4", 1000.0, false}, - {"nothing", 0.0, false}, - {"-", 0.0, false}, - {"+", 0.0, false}, - {"", 0.0, false}, + // Test some invalid cases. + {"\t\n\v\f\r -123.45e2", -12345.0, false}, + {"+123 e4", 123.0, false}, + {"123e ", 123.0, false}, + {"123e", 123.0, false}, + {"10.5px", 10.5, false}, + {"11.5e2em", 1150, false}, + {" 2.99", 2.99, false}, + {"1e3.4", 1000.0, false}, + {"nothing", 0.0, false}, + {"-", 0.0, false}, + {"+", 0.0, false}, + {"", 0.0, false}, - // crbug.org/588726 - {"-0.0010000000000000000000000000000000000000001e-256", - -1.0000000000000001e-259, true}, + // crbug.org/588726 + {"-0.0010000000000000000000000000000000000000001e-256", + -1.0000000000000001e-259, true}, }; for (size_t i = 0; i < gurl_base::size(cases); ++i) { @@ -854,7 +869,9 @@ // One additional test to verify that conversion of numbers in strings with // embedded NUL characters. The NUL and extra data after it should be // interpreted as junk after the number. - const char input[] = "3.14\0" "159"; + const char input[] = + "3.14\0" + "159"; std::string input_string(input, gurl_base::size(input) - 1); double output; EXPECT_FALSE(StringToDouble(input_string, &output)); @@ -886,8 +903,8 @@ double input = 0; memcpy(&input, input_bytes, gurl_base::size(input_bytes)); EXPECT_EQ("1.335179083776e+12", NumberToString(input)); - const char input_bytes2[8] = - {0, 0, 0, '\xa0', '\xda', '\x6c', '\x73', '\x42'}; + const char input_bytes2[8] = {0, 0, 0, '\xa0', + '\xda', '\x6c', '\x73', '\x42'}; input = 0; memcpy(&input, input_bytes2, gurl_base::size(input_bytes2)); EXPECT_EQ("1.33489033216e+12", NumberToString(input));
diff --git a/base/strings/string_piece.cc b/base/strings/string_piece.cc index ee043de..62ba11f 100644 --- a/base/strings/string_piece.cc +++ b/base/strings/string_piece.cc
@@ -387,26 +387,5 @@ return find_last_not_ofT(self, c, pos); } -template<typename STR> -BasicStringPiece<STR> substrT(const BasicStringPiece<STR>& self, - size_t pos, - size_t n) { - if (pos > self.size()) pos = self.size(); - if (n > self.size() - pos) n = self.size() - pos; - return BasicStringPiece<STR>(self.data() + pos, n); -} - -StringPiece substr(const StringPiece& self, - size_t pos, - size_t n) { - return substrT(self, pos, n); -} - -StringPiece16 substr(const StringPiece16& self, - size_t pos, - size_t n) { - return substrT(self, pos, n); -} - } // namespace internal } // namespace base
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h index d155496..de54e94 100644 --- a/base/strings/string_piece.h +++ b/base/strings/string_piece.h
@@ -129,13 +129,6 @@ char c, size_t pos); -BASE_EXPORT StringPiece substr(const StringPiece& self, - size_t pos, - size_t n); -BASE_EXPORT StringPiece16 substr(const StringPiece16& self, - size_t pos, - size_t n); - } // namespace internal // BasicStringPiece ------------------------------------------------------------ @@ -205,19 +198,19 @@ constexpr const value_type* data() const { return ptr_; } constexpr size_type size() const noexcept { return length_; } constexpr size_type length() const noexcept { return length_; } - bool empty() const { return length_ == 0; } + constexpr bool empty() const noexcept { return length_ == 0; } constexpr value_type operator[](size_type i) const { GURL_CHECK(i < length_); return ptr_[i]; } - value_type front() const { + constexpr value_type front() const { GURL_CHECK_NE(0UL, length_); return ptr_[0]; } - value_type back() const { + constexpr value_type back() const { GURL_CHECK_NE(0UL, length_); return ptr_[length_ - 1]; } @@ -244,19 +237,24 @@ } // This is the style of conversion preferred by std::string_view in C++17. - explicit operator STRING_TYPE() const { return as_string(); } - - STRING_TYPE as_string() const { - // std::string doesn't like to take a NULL pointer even with a 0 size. + explicit operator STRING_TYPE() const { return empty() ? STRING_TYPE() : STRING_TYPE(data(), size()); } - const_iterator begin() const { return ptr_; } - const_iterator end() const { return ptr_ + length_; } - const_reverse_iterator rbegin() const { + // Deprecated, use operator STRING_TYPE() instead. + // TODO(crbug.com/1049498): Remove for all STRING_TYPEs. + template <typename StrT = STRING_TYPE, + typename = std::enable_if_t<std::is_same<StrT, std::string>::value>> + STRING_TYPE as_string() const { + return STRING_TYPE(*this); + } + + constexpr const_iterator begin() const noexcept { return ptr_; } + constexpr const_iterator end() const noexcept { return ptr_ + length_; } + constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(ptr_ + length_); } - const_reverse_iterator rend() const { + constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(ptr_); } @@ -267,21 +265,6 @@ return internal::copy(*this, buf, n, pos); } - // Does "this" start with "x" - constexpr bool starts_with(BasicStringPiece x) const noexcept { - return ( - (this->length_ >= x.length_) && - (CharTraits<value_type>::compare(this->ptr_, x.ptr_, x.length_) == 0)); - } - - // Does "this" end with "x" - constexpr bool ends_with(BasicStringPiece x) const noexcept { - return ((this->length_ >= x.length_) && - (CharTraits<value_type>::compare( - this->ptr_ + (this->length_ - x.length_), x.ptr_, x.length_) == - 0)); - } - // find: Search for a character or substring at a given offset. size_type find(const BasicStringPiece<STRING_TYPE>& s, size_type pos = 0) const { @@ -339,9 +322,11 @@ } // substr. - BasicStringPiece substr(size_type pos, - size_type n = BasicStringPiece::npos) const { - return internal::substr(*this, pos, n); + constexpr BasicStringPiece substr( + size_type pos, + size_type n = BasicStringPiece::npos) const { + GURL_CHECK_LE(pos, size()); + return {data() + pos, std::min(n, size() - pos)}; } protected:
diff --git a/base/strings/string_piece_unittest.cc b/base/strings/string_piece_unittest.cc index 0777549..e0d812b 100644 --- a/base/strings/string_piece_unittest.cc +++ b/base/strings/string_piece_unittest.cc
@@ -299,8 +299,8 @@ ASSERT_EQ(a.rfind(c, 0U), Piece::npos); ASSERT_EQ(b.rfind(c), Piece::npos); ASSERT_EQ(b.rfind(c, 0U), Piece::npos); - ASSERT_EQ(a.rfind(d), static_cast<size_t>(a.as_string().rfind(TypeParam()))); - ASSERT_EQ(a.rfind(e), a.as_string().rfind(TypeParam())); + ASSERT_EQ(a.rfind(d), static_cast<size_t>(a.rfind(TypeParam()))); + ASSERT_EQ(a.rfind(e), a.rfind(TypeParam())); ASSERT_EQ(a.rfind(d), static_cast<size_t>(TypeParam(a).rfind(TypeParam()))); ASSERT_EQ(a.rfind(e), TypeParam(a).rfind(TypeParam())); ASSERT_EQ(a.rfind(d, 12), 12U); @@ -474,11 +474,7 @@ ASSERT_EQ(a.substr(23, 99), c); ASSERT_EQ(a.substr(0), a); ASSERT_EQ(a.substr(3, 2), TestFixture::as_string("de")); - // empty string nonsense - ASSERT_EQ(a.substr(99, 2), e); - ASSERT_EQ(d.substr(99), e); ASSERT_EQ(d.substr(0, 99), e); - ASSERT_EQ(d.substr(99, 99), e); } TYPED_TEST(CommonStringPieceTest, CheckCustom) { @@ -521,12 +517,6 @@ c = {foobar.c_str(), 7}; // Note, has an embedded NULL ASSERT_NE(c, a); - // as_string - TypeParam s3(a.as_string().c_str(), 7); // Note, has an embedded NULL - ASSERT_EQ(c, s3); - TypeParam s4(e.as_string()); - ASSERT_TRUE(s4.empty()); - // operator STRING_TYPE() TypeParam s5(TypeParam(a).c_str(), 7); // Note, has an embedded NULL ASSERT_EQ(c, s5); @@ -543,30 +533,6 @@ StringPiece e; std::string s2; - // starts_with - ASSERT_TRUE(a.starts_with(a)); - ASSERT_TRUE(a.starts_with("foo")); - ASSERT_TRUE(a.starts_with(e)); - ASSERT_TRUE(b.starts_with(s1)); - ASSERT_TRUE(b.starts_with(b)); - ASSERT_TRUE(b.starts_with(e)); - ASSERT_TRUE(e.starts_with("")); - ASSERT_TRUE(!a.starts_with(b)); - ASSERT_TRUE(!b.starts_with(a)); - ASSERT_TRUE(!e.starts_with(a)); - - // ends with - ASSERT_TRUE(a.ends_with(a)); - ASSERT_TRUE(a.ends_with("bar")); - ASSERT_TRUE(a.ends_with(e)); - ASSERT_TRUE(b.ends_with(s1)); - ASSERT_TRUE(b.ends_with(b)); - ASSERT_TRUE(b.ends_with(e)); - ASSERT_TRUE(e.ends_with("")); - ASSERT_TRUE(!a.ends_with(b)); - ASSERT_TRUE(!b.ends_with(a)); - ASSERT_TRUE(!e.ends_with(a)); - StringPiece c; c = {"foobar", 6}; ASSERT_EQ(c, a); @@ -584,10 +550,6 @@ TypeParam str(s); ASSERT_EQ(str.length(), 0U); ASSERT_EQ(str, TypeParam()); - - str = s.as_string(); - ASSERT_EQ(str.length(), 0U); - ASSERT_EQ(str, TypeParam()); } TYPED_TEST(CommonStringPieceTest, CheckComparisons2) { @@ -607,21 +569,6 @@ ASSERT_GT(abc.compare(BasicStringPiece<TypeParam>(alphabet_y)), 0); } -// Test operations only supported by std::string version. -TEST(StringPieceTest, CheckComparisons2) { - StringPiece abc("abcdefghijklmnopqrstuvwxyz"); - - // starts_with - ASSERT_TRUE(abc.starts_with(abc)); - ASSERT_TRUE(abc.starts_with("abcdefghijklm")); - ASSERT_TRUE(!abc.starts_with("abcdefguvwxyz")); - - // ends_with - ASSERT_TRUE(abc.ends_with(abc)); - ASSERT_TRUE(!abc.ends_with("abcdefguvwxyz")); - ASSERT_TRUE(abc.ends_with("nopqrstuvwxyz")); -} - TYPED_TEST(CommonStringPieceTest, StringCompareNotAmbiguous) { ASSERT_TRUE(TestFixture::as_string("hello").c_str() == TestFixture::as_string("hello")); @@ -656,10 +603,7 @@ TEST(StringPiece16Test, CheckConversion) { // Make sure that we can convert from UTF8 to UTF16 and back. We use a two // byte character (G clef) to test this. - ASSERT_EQ( - UTF16ToUTF8( - StringPiece16(UTF8ToUTF16("\xf0\x9d\x84\x9e")).as_string()), - "\xf0\x9d\x84\x9e"); + ASSERT_EQ(UTF16ToUTF8(UTF8ToUTF16("\xf0\x9d\x84\x9e")), "\xf0\x9d\x84\x9e"); } TYPED_TEST(CommonStringPieceTest, CheckConstructors) { @@ -730,6 +674,11 @@ StringPiece piece; ASSERT_DEATH_IF_SUPPORTED(piece.remove_prefix(1), ""); } + + { + StringPiece piece; + ASSERT_DEATH_IF_SUPPORTED(piece.substr(1), ""); + } } TEST(StringPieceTest, ConstexprData) { @@ -769,6 +718,14 @@ } } +TEST(StringPieceTest, ConstexprFront) { + static_assert(StringPiece("abc").front() == 'a', ""); +} + +TEST(StringPieceTest, ConstexprBack) { + static_assert(StringPiece("abc").back() == 'c', ""); +} + TEST(StringPieceTest, Compare) { constexpr StringPiece piece = "def"; @@ -783,32 +740,19 @@ static_assert(piece.compare("ghij") == -1, ""); } -TEST(StringPieceTest, StartsWith) { - constexpr StringPiece piece("abc"); +TEST(StringPieceTest, Substr) { + constexpr StringPiece piece = "abcdefghijklmnopqrstuvwxyz"; - static_assert(piece.starts_with(""), ""); - static_assert(piece.starts_with("a"), ""); - static_assert(piece.starts_with("ab"), ""); - static_assert(piece.starts_with("abc"), ""); - - static_assert(!piece.starts_with("b"), ""); - static_assert(!piece.starts_with("bc"), ""); - - static_assert(!piece.starts_with("abcd"), ""); -} - -TEST(StringPieceTest, EndsWith) { - constexpr StringPiece piece("abc"); - - static_assert(piece.ends_with(""), ""); - static_assert(piece.ends_with("c"), ""); - static_assert(piece.ends_with("bc"), ""); - static_assert(piece.ends_with("abc"), ""); - - static_assert(!piece.ends_with("a"), ""); - static_assert(!piece.ends_with("ab"), ""); - - static_assert(!piece.ends_with("abcd"), ""); + static_assert(piece.substr(0, 2) == "ab", ""); + static_assert(piece.substr(0, 3) == "abc", ""); + static_assert(piece.substr(0, 4) == "abcd", ""); + static_assert(piece.substr(3, 2) == "de", ""); + static_assert(piece.substr(3, 3) == "def", ""); + static_assert(piece.substr(23) == "xyz", ""); + static_assert(piece.substr(23, 3) == "xyz", ""); + static_assert(piece.substr(23, 99) == "xyz", ""); + static_assert(piece.substr(0) == piece, ""); + static_assert(piece.substr(0, 99) == piece, ""); } } // namespace base
diff --git a/base/strings/string_split.h b/base/strings/string_split.h index d9676c7..039a049 100644 --- a/base/strings/string_split.h +++ b/base/strings/string_split.h
@@ -45,8 +45,7 @@ // To split on either commas or semicolons, keeping all whitespace: // // std::vector<std::string> tokens = gurl_base::SplitString( -// input, ", WARN_UNUSED_RESULT;", gurl_base::KEEP_WHITESPACE, -// gurl_base::SPLIT_WANT_ALL) WARN_UNUSED_RESULT; +// input, ",;", gurl_base::KEEP_WHITESPACE, gurl_base::SPLIT_WANT_ALL); BASE_EXPORT std::vector<std::string> SplitString(StringPiece input, StringPiece separators, WhitespaceHandling whitespace,
diff --git a/base/strings/string_util.h b/base/strings/string_util.h index 1445283..f43a8ac 100644 --- a/base/strings/string_util.h +++ b/base/strings/string_util.h
@@ -313,18 +313,22 @@ INSENSITIVE_ASCII, }; -BASE_EXPORT bool StartsWith(StringPiece str, - StringPiece search_for, - CompareCase case_sensitivity); -BASE_EXPORT bool StartsWith(StringPiece16 str, - StringPiece16 search_for, - CompareCase case_sensitivity); -BASE_EXPORT bool EndsWith(StringPiece str, - StringPiece search_for, - CompareCase case_sensitivity); -BASE_EXPORT bool EndsWith(StringPiece16 str, - StringPiece16 search_for, - CompareCase case_sensitivity); +BASE_EXPORT bool StartsWith( + StringPiece str, + StringPiece search_for, + CompareCase case_sensitivity = CompareCase::SENSITIVE); +BASE_EXPORT bool StartsWith( + StringPiece16 str, + StringPiece16 search_for, + CompareCase case_sensitivity = CompareCase::SENSITIVE); +BASE_EXPORT bool EndsWith( + StringPiece str, + StringPiece search_for, + CompareCase case_sensitivity = CompareCase::SENSITIVE); +BASE_EXPORT bool EndsWith( + StringPiece16 str, + StringPiece16 search_for, + CompareCase case_sensitivity = CompareCase::SENSITIVE); // Determines the type of ASCII character, independent of locale (the C // library versions will change based on locale).
diff --git a/base/strings/string_util_internal.h b/base/strings/string_util_internal.h index ace0665..006aeb0 100644 --- a/base/strings/string_util_internal.h +++ b/base/strings/string_util_internal.h
@@ -5,8 +5,11 @@ #ifndef BASE_STRINGS_STRING_UTIL_INTERNAL_H_ #define BASE_STRINGS_STRING_UTIL_INTERNAL_H_ +#include <algorithm> + #include "polyfills/base/logging.h" #include "polyfills/base/notreached.h" +#include "base/ranges/algorithm.h" #include "base/strings/string_piece.h" #include "base/third_party/icu/icu_utf.h" @@ -130,7 +133,7 @@ size_t end = (positions & TRIM_TRAILING) ? input.find_last_not_of(trim_chars) + 1 : input.size(); - return input.substr(begin, end - begin); + return input.substr(std::min(begin, input.size()), end - begin); } template <typename STR> @@ -578,8 +581,7 @@ ReplacementOffset r_offset(index, static_cast<int>(formatted.size())); r_offsets.insert( - std::upper_bound(r_offsets.begin(), r_offsets.end(), r_offset, - &CompareParameter), + ranges::upper_bound(r_offsets, r_offset, &CompareParameter), r_offset); } if (index < substitutions)
diff --git a/base/strings/string_util_posix.h b/base/strings/string_util_posix.h index 457e258..7d5a67b 100644 --- a/base/strings/string_util_posix.h +++ b/base/strings/string_util_posix.h
@@ -12,6 +12,7 @@ #include <wchar.h> #include "polyfills/base/check.h" +#include "base/strings/string_piece.h" namespace gurl_base { @@ -32,6 +33,17 @@ return ::vswprintf(buffer, size, format, arguments); } +// These mirror the APIs in string_util_win.h. Since gurl_base::StringPiece is +// already the native string type on POSIX platforms these APIs are simple +// no-ops. +inline StringPiece AsCrossPlatformPiece(StringPiece str) { + return str; +} + +inline StringPiece AsNativeStringPiece(StringPiece str) { + return str; +} + } // namespace base #endif // BASE_STRINGS_STRING_UTIL_POSIX_H_
diff --git a/base/strings/string_util_win.h b/base/strings/string_util_win.h index 2765748..51a6a2b 100644 --- a/base/strings/string_util_win.h +++ b/base/strings/string_util_win.h
@@ -107,6 +107,18 @@ return string16(as_u16cstr(str.data()), str.size()); } +// Compatibility shim for cross-platform code that passes a StringPieceType to a +// cross platform string utility function. Most of these functions are only +// implemented for gurl_base::StringPiece and gurl_base::StringPiece16, which is why +// gurl_base::WStringPieces need to be converted on API boundaries. +inline StringPiece16 AsCrossPlatformPiece(WStringPiece str) { + return AsStringPiece16(str); +} + +inline WStringPiece AsNativeStringPiece(StringPiece16 str) { + return AsWStringPiece(str); +} + // The following section contains overloads of the cross-platform APIs for // std::wstring and gurl_base::WStringPiece. These are only enabled if std::wstring // and gurl_base::string16 are distinct types, as otherwise this would result in an @@ -135,7 +147,7 @@ BASE_EXPORT bool TrimString(WStringPiece input, WStringPiece trim_chars, - std::string* output); + std::wstring* output); BASE_EXPORT WStringPiece TrimString(WStringPiece input, WStringPiece trim_chars, @@ -159,13 +171,15 @@ BASE_EXPORT bool EqualsASCII(StringPiece16 str, StringPiece ascii); -BASE_EXPORT bool StartsWith(WStringPiece str, - WStringPiece search_for, - CompareCase case_sensitivity); +BASE_EXPORT bool StartsWith( + WStringPiece str, + WStringPiece search_for, + CompareCase case_sensitivity = CompareCase::SENSITIVE); -BASE_EXPORT bool EndsWith(WStringPiece str, - WStringPiece search_for, - CompareCase case_sensitivity); +BASE_EXPORT bool EndsWith( + WStringPiece str, + WStringPiece search_for, + CompareCase case_sensitivity = CompareCase::SENSITIVE); BASE_EXPORT void ReplaceFirstSubstringAfterOffset(std::wstring* str, size_t start_offset,
diff --git a/base/strings/sys_string_conversions.h b/base/strings/sys_string_conversions.h index a2b4cce..4183d26 100644 --- a/base/strings/sys_string_conversions.h +++ b/base/strings/sys_string_conversions.h
@@ -18,7 +18,7 @@ #include "base/strings/string_piece.h" #include "build/build_config.h" -#if defined(OS_MACOSX) +#if defined(OS_APPLE) #include <CoreFoundation/CoreFoundation.h> #include "base/mac/scoped_cftyperef.h" @@ -28,7 +28,7 @@ #else class NSString; #endif -#endif // OS_MACOSX +#endif // OS_APPLE namespace gurl_base { @@ -63,7 +63,7 @@ // Mac-specific ---------------------------------------------------------------- -#if defined(OS_MACOSX) +#if defined(OS_APPLE) // Converts between STL strings and CFStringRefs/NSStrings. @@ -89,7 +89,7 @@ BASE_EXPORT std::string SysNSStringToUTF8(NSString* ref) WARN_UNUSED_RESULT; BASE_EXPORT string16 SysNSStringToUTF16(NSString* ref) WARN_UNUSED_RESULT; -#endif // defined(OS_MACOSX) +#endif // defined(OS_APPLE) } // namespace base
diff --git a/base/strings/sys_string_conversions_unittest.cc b/base/strings/sys_string_conversions_unittest.cc index 0e78d43..2f31dcc 100644 --- a/base/strings/sys_string_conversions_unittest.cc +++ b/base/strings/sys_string_conversions_unittest.cc
@@ -75,8 +75,8 @@ EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null)); } -#if defined(OS_LINUX) // Tests depend on setting a specific Linux locale. - +// Tests depend on setting a specific Linux locale. +#if defined(OS_LINUX) || defined(OS_CHROMEOS) TEST(SysStrings, SysWideToNativeMB) { #if !defined(SYSTEM_NATIVE_UTF8) ScopedLocale locale("en_US.UTF-8"); @@ -191,6 +191,6 @@ EXPECT_EQ(wide, trip); } } -#endif // OS_LINUX +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) } // namespace base
diff --git a/base/template_util.h b/base/template_util.h index 8c2b185..4b69c7a 100644 --- a/base/template_util.h +++ b/base/template_util.h
@@ -89,6 +89,15 @@ void_t<typename std::iterator_traits<T>::iterator_category>> : std::true_type {}; +// Helper to express preferences in an overload set. If more than one overload +// are available for a given set of parameters the overload with the higher +// priority will be chosen. +template <size_t I> +struct priority_tag : priority_tag<I - 1> {}; + +template <> +struct priority_tag<0> {}; + } // namespace internal // is_trivially_copyable is especially hard to get right. @@ -224,6 +233,59 @@ template <typename B> struct negation : bool_constant<!static_cast<bool>(B::value)> {}; +// Implementation of C++17's std::invoke_result_t. +// +// This implementation adds references to `Functor` and `Args` to work around +// some quirks of std::result_of_t. See the #Notes section of [1] for details. +// +// References: +// [1] https://en.cppreference.com/w/cpp/types/result_of +// [2] https://wg21.link/meta.type.synop#lib:invoke_result_t +template <typename Functor, typename... Args> +using invoke_result_t = std::result_of_t<Functor && (Args && ...)>; + +// Simplified implementation of C++20's std::iter_value_t. +// As opposed to std::iter_value_t, this implementation does not restrict +// the type of `Iter` and does not consider specializations of +// `indirectly_readable_traits`. +// +// Reference: https://wg21.link/readable.traits#2 +template <typename Iter> +using iter_value_t = typename std::iterator_traits< + std::remove_cv_t<std::remove_reference_t<Iter>>>::value_type; + +// Simplified implementation of C++20's std::iter_reference_t. +// As opposed to std::iter_reference_t, this implementation does not restrict +// the type of `Iter`. +// +// Reference: https://wg21.link/iterator.synopsis#:~:text=iter_reference_t +template <typename Iter> +using iter_reference_t = decltype(*std::declval<Iter&>()); + +// Simplified implementation of C++20's std::indirect_result_t. As opposed to +// std::indirect_result_t, this implementation does not restrict the type of +// `Func` and `Iters`. +// +// Reference: https://wg21.link/iterator.synopsis#:~:text=indirect_result_t +template <typename Func, typename... Iters> +using indirect_result_t = invoke_result_t<Func, iter_reference_t<Iters>...>; + +// Simplified implementation of C++20's std::projected. As opposed to +// std::projected, this implementation does not explicitly restrict the type of +// `Iter` and `Proj`, but rather does so implicitly by requiring +// `indirect_result_t<Proj, Iter>` is a valid type. This is required for SFINAE +// friendliness. +// +// Reference: https://wg21.link/projected +template <typename Iter, + typename Proj, + typename IndirectResultT = indirect_result_t<Proj, Iter>> +struct projected { + using value_type = std::remove_cv_t<std::remove_reference_t<IndirectResultT>>; + + IndirectResultT operator*() const; // not defined +}; + } // namespace base #undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
diff --git a/base/third_party/icu/BUILD b/base/third_party/icu/BUILD index ca5c9af..f35bdd4 100644 --- a/base/third_party/icu/BUILD +++ b/base/third_party/icu/BUILD
@@ -5,7 +5,6 @@ cc_library( name = "icu", - srcs = ["icu_utf.cc"], hdrs = ["icu_utf.h"], copts = build_config.default_copts, visibility = ["//visibility:public"],
diff --git a/base/third_party/icu/README.chromium b/base/third_party/icu/README.chromium index 297e89a..4f398d9 100644 --- a/base/third_party/icu/README.chromium +++ b/base/third_party/icu/README.chromium
@@ -6,12 +6,12 @@ This file has the relevant components from ICU copied to handle basic UTF8/16/32 conversions. Components are copied from umachine.h, utf.h, utf8.h, and utf16.h -into icu_utf.h, and from utf_impl.cpp into icu_utf.cc. +into icu_utf.h. -The main change is that U_/U8_/U16_ prefixes have been replaced with -CBU_/CBU8_/CBU16_ (for "Chrome Base") to avoid confusion with the "real" ICU -macros should ICU be in use on the system. For the same reason, the functions -and types have been put in the "base_icu" namespace. +The main change is that U_/U8_/U16_/UPRV_ prefixes have been replaced with +CBU_/CBU8_/CBU16_/CBUPRV_ (for "Chrome Base") to avoid confusion with the "real" +ICU macros should ICU be in use on the system. For the same reason, the +functions and types have been put in the "base_icu" namespace. Note that this license file is marked as NOT_SHIPPED, since a more complete ICU license is included from //third_party/icu/README.chromium
diff --git a/base/third_party/icu/icu_utf.cc b/base/third_party/icu/icu_utf.cc deleted file mode 100644 index a3262b0..0000000 --- a/base/third_party/icu/icu_utf.cc +++ /dev/null
@@ -1,131 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: utf_impl.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999sep13 -* created by: Markus W. Scherer -* -* This file provides implementation functions for macros in the utfXX.h -* that would otherwise be too long as macros. -*/ - -#include "base/third_party/icu/icu_utf.h" - -namespace base_icu { - -// source/common/utf_impl.cpp - -static const UChar32 -utf8_errorValue[6]={ - // Same values as UTF8_ERROR_VALUE_1, UTF8_ERROR_VALUE_2, UTF_ERROR_VALUE, - // but without relying on the obsolete unicode/utf_old.h. - 0x15, 0x9f, 0xffff, - 0x10ffff -}; - -static UChar32 -errorValue(int32_t count, int8_t strict) { - if(strict>=0) { - return utf8_errorValue[count]; - } else if(strict==-3) { - return 0xfffd; - } else { - return CBU_SENTINEL; - } -} - -/* - * Handle the non-inline part of the U8_NEXT() and U8_NEXT_FFFD() macros - * and their obsolete sibling UTF8_NEXT_CHAR_SAFE(). - * - * U8_NEXT() supports NUL-terminated strings indicated via length<0. - * - * The "strict" parameter controls the error behavior: - * <0 "Safe" behavior of U8_NEXT(): - * -1: All illegal byte sequences yield U_SENTINEL=-1. - * -2: Same as -1, except for lenient treatment of surrogate code points as legal. - * Some implementations use this for roundtripping of - * Unicode 16-bit strings that are not well-formed UTF-16, that is, they - * contain unpaired surrogates. - * -3: All illegal byte sequences yield U+FFFD. - * 0 Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., FALSE): - * All illegal byte sequences yield a positive code point such that this - * result code point would be encoded with the same number of bytes as - * the illegal sequence. - * >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(..., TRUE): - * Same as the obsolete "safe" behavior, but non-characters are also treated - * like illegal sequences. - * - * Note that a UBool is the same as an int8_t. - */ -UChar32 -utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict) { - // *pi is one after byte c. - int32_t i=*pi; - // length can be negative for NUL-terminated strings: Read and validate one byte at a time. - if(i==length || c>0xf4) { - // end of string, or not a lead byte - } else if(c>=0xf0) { - // Test for 4-byte sequences first because - // U8_NEXT() handles shorter valid sequences inline. - uint8_t t1=s[i], t2, t3; - c&=7; - if(CBU8_IS_VALID_LEAD4_AND_T1(c, t1) && - ++i!=length && (t2=s[i]-0x80)<=0x3f && - ++i!=length && (t3=s[i]-0x80)<=0x3f) { - ++i; - c=(c<<18)|((t1&0x3f)<<12)|(t2<<6)|t3; - // strict: forbid non-characters like U+fffe - if(strict<=0 || !CBU_IS_UNICODE_NONCHAR(c)) { - *pi=i; - return c; - } - } - } else if(c>=0xe0) { - c&=0xf; - if(strict!=-2) { - uint8_t t1=s[i], t2; - if(CBU8_IS_VALID_LEAD3_AND_T1(c, t1) && - ++i!=length && (t2=s[i]-0x80)<=0x3f) { - ++i; - c=(c<<12)|((t1&0x3f)<<6)|t2; - // strict: forbid non-characters like U+fffe - if(strict<=0 || !CBU_IS_UNICODE_NONCHAR(c)) { - *pi=i; - return c; - } - } - } else { - // strict=-2 -> lenient: allow surrogates - uint8_t t1=s[i]-0x80, t2; - if(t1<=0x3f && (c>0 || t1>=0x20) && - ++i!=length && (t2=s[i]-0x80)<=0x3f) { - *pi=i+1; - return (c<<12)|(t1<<6)|t2; - } - } - } else if(c>=0xc2) { - uint8_t t1=s[i]-0x80; - if(t1<=0x3f) { - *pi=i+1; - return ((c-0xc0)<<6)|t1; - } - } // else 0x80<=c<0xc2 is not a lead byte - - /* error handling */ - c=errorValue(i-*pi, strict); - *pi=i; - return c; -} - -} // namespace base_icu
diff --git a/base/third_party/icu/icu_utf.h b/base/third_party/icu/icu_utf.h index 2ba8231..16792c4 100644 --- a/base/third_party/icu/icu_utf.h +++ b/base/third_party/icu/icu_utf.h
@@ -60,6 +60,25 @@ */ #define CBU_SENTINEL (-1) +/** + * \def UPRV_BLOCK_MACRO_BEGIN + * Defined as the "do" keyword by default. + * @internal + */ +#ifndef CBUPRV_BLOCK_MACRO_BEGIN +#define CBUPRV_BLOCK_MACRO_BEGIN do +#endif + +/** + * \def UPRV_BLOCK_MACRO_END + * Defined as "while (FALSE)" by default. + * @internal + */ +#ifndef CBUPRV_BLOCK_MACRO_END +#define CBUPRV_BLOCK_MACRO_END while (0) +#endif + + // source/common/unicode/utf.h /** @@ -147,21 +166,6 @@ #define CBU8_IS_VALID_LEAD4_AND_T1(lead, t1) (CBU8_LEAD4_T1_BITS[(uint8_t)(t1)>>4]&(1<<((lead)&7))) /** - * Function for handling "next code point" with error-checking. - * - * This is internal since it is not meant to be called directly by external clie -nts; - * however it is U_STABLE (not U_INTERNAL) since it is called by public macros i -n this - * file and thus must remain stable, and should not be hidden when other interna -l - * functions are hidden (otherwise public macros would fail to compile). - * @internal - */ -UChar32 -utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, ::base_icu::UChar32 c, ::base_icu::UBool strict); - -/** * Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)? * @param c 8-bit code unit (byte) * @return TRUE or FALSE @@ -230,29 +234,36 @@ * @see U8_NEXT_UNSAFE * @stable ICU 2.4 */ -#define CBU8_NEXT(s, i, length, c) { \ +#define CBU8_NEXT(s, i, length, c) CBU8_INTERNAL_NEXT_OR_SUB(s, i, length, c, CBU_SENTINEL) + +/** @internal */ +#define CBU8_INTERNAL_NEXT_OR_SUB(s, i, length, c, sub) CBUPRV_BLOCK_MACRO_BEGIN { \ (c)=(uint8_t)(s)[(i)++]; \ if(!CBU8_IS_SINGLE(c)) { \ - uint8_t __t1, __t2; \ - if( /* handle U+0800..U+FFFF inline */ \ - (0xe0<=(c) && (c)<0xf0) && \ - (((i)+1)<(length) || (length)<0) && \ - CBU8_IS_VALID_LEAD3_AND_T1((c), __t1=(s)[i]) && \ - (__t2=(s)[(i)+1]-0x80)<=0x3f) { \ - (c)=(((c)&0xf)<<12)|((__t1&0x3f)<<6)|__t2; \ - (i)+=2; \ - } else if( /* handle U+0080..U+07FF inline */ \ - ((c)<0xe0 && (c)>=0xc2) && \ - ((i)!=(length)) && \ - (__t1=(s)[i]-0x80)<=0x3f) { \ - (c)=(((c)&0x1f)<<6)|__t1; \ - ++(i); \ + uint8_t __t = 0; \ + if((i)!=(length) && \ + /* fetch/validate/assemble all but last trail byte */ \ + ((c)>=0xe0 ? \ + ((c)<0xf0 ? /* U+0800..U+FFFF except surrogates */ \ + CBU8_LEAD3_T1_BITS[(c)&=0xf]&(1<<((__t=(s)[i])>>5)) && \ + (__t&=0x3f, 1) \ + : /* U+10000..U+10FFFF */ \ + ((c)-=0xf0)<=4 && \ + CBU8_LEAD4_T1_BITS[(__t=(s)[i])>>4]&(1<<(c)) && \ + ((c)=((c)<<6)|(__t&0x3f), ++(i)!=(length)) && \ + (__t=(s)[i]-0x80)<=0x3f) && \ + /* valid second-to-last trail byte */ \ + ((c)=((c)<<6)|__t, ++(i)!=(length)) \ + : /* U+0080..U+07FF */ \ + (c)>=0xc2 && ((c)&=0x1f, 1)) && \ + /* last trail byte */ \ + (__t=(s)[i]-0x80)<=0x3f && \ + ((c)=((c)<<6)|__t, ++(i), 1)) { \ } else { \ - /* function call for "complicated" and error cases */ \ - (c)=::base_icu::utf8_nextCharSafeBody((const uint8_t *)s, &(i), (length), c, -1); \ + (c)=(sub); /* ill-formed*/ \ } \ } \ -} +} CBUPRV_BLOCK_MACRO_END /** * Append a code point to a string, overwriting 1 to 4 bytes. @@ -267,24 +278,25 @@ * @see U8_APPEND * @stable ICU 2.4 */ -#define CBU8_APPEND_UNSAFE(s, i, c) { \ - if((uint32_t)(c)<=0x7f) { \ - (s)[(i)++]=(uint8_t)(c); \ +#define CBU8_APPEND_UNSAFE(s, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \ + uint32_t __uc=(c); \ + if(__uc<=0x7f) { \ + (s)[(i)++]=(uint8_t)__uc; \ } else { \ - if((uint32_t)(c)<=0x7ff) { \ - (s)[(i)++]=(uint8_t)(((c)>>6)|0xc0); \ + if(__uc<=0x7ff) { \ + (s)[(i)++]=(uint8_t)((__uc>>6)|0xc0); \ } else { \ - if((uint32_t)(c)<=0xffff) { \ - (s)[(i)++]=(uint8_t)(((c)>>12)|0xe0); \ + if(__uc<=0xffff) { \ + (s)[(i)++]=(uint8_t)((__uc>>12)|0xe0); \ } else { \ - (s)[(i)++]=(uint8_t)(((c)>>18)|0xf0); \ - (s)[(i)++]=(uint8_t)((((c)>>12)&0x3f)|0x80); \ + (s)[(i)++]=(uint8_t)((__uc>>18)|0xf0); \ + (s)[(i)++]=(uint8_t)(((__uc>>12)&0x3f)|0x80); \ } \ - (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); \ + (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \ } \ - (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); \ + (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \ } \ -} +} CBUPRV_BLOCK_MACRO_END // source/common/unicode/utf16.h @@ -384,6 +396,45 @@ #define CBU16_MAX_LENGTH 2 /** + * Get a code point from a string at a random-access offset, + * without changing the offset. + * "Safe" macro, handles unpaired surrogates and checks for string boundaries. + * + * The offset may point to either the lead or trail surrogate unit + * for a supplementary code point, in which case the macro will read + * the adjacent matching surrogate as well. + * + * The length can be negative for a NUL-terminated string. + * + * If the offset points to a single, unpaired surrogate, then + * c is set to that unpaired surrogate. + * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT. + * + * @param s const UChar * string + * @param start starting string offset (usually 0) + * @param i string offset, must be start<=i<length + * @param length string length + * @param c output UChar32 variable + * @see U16_GET_UNSAFE + * @stable ICU 2.4 + */ +#define CBU16_GET(s, start, i, length, c) CBUPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[i]; \ + if(CBU16_IS_SURROGATE(c)) { \ + uint16_t __c2; \ + if(CBU16_IS_SURROGATE_LEAD(c)) { \ + if((i)+1!=(length) && CBU16_IS_TRAIL(__c2=(s)[(i)+1])) { \ + (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \ + } \ + } else { \ + if((i)>(start) && CBU16_IS_LEAD(__c2=(s)[(i)-1])) { \ + (c)=CBU16_GET_SUPPLEMENTARY(__c2, (c)); \ + } \ + } \ + } \ +} CBUPRV_BLOCK_MACRO_END + +/** * Get a code point from a string at a code point boundary offset, * and advance the offset to the next code point boundary. * (Post-incrementing forward iteration.) @@ -404,7 +455,7 @@ * @see U16_NEXT_UNSAFE * @stable ICU 2.4 */ -#define CBU16_NEXT(s, i, length, c) { \ +#define CBU16_NEXT(s, i, length, c) CBUPRV_BLOCK_MACRO_BEGIN { \ (c)=(s)[(i)++]; \ if(CBU16_IS_LEAD(c)) { \ uint16_t __c2; \ @@ -413,7 +464,7 @@ (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \ } \ } \ -} +} CBUPRV_BLOCK_MACRO_END /** * Append a code point to a string, overwriting 1 or 2 code units. @@ -428,14 +479,88 @@ * @see U16_APPEND * @stable ICU 2.4 */ -#define CBU16_APPEND_UNSAFE(s, i, c) { \ +#define CBU16_APPEND_UNSAFE(s, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \ if((uint32_t)(c)<=0xffff) { \ (s)[(i)++]=(uint16_t)(c); \ } else { \ (s)[(i)++]=(uint16_t)(((c)>>10)+0xd7c0); \ (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \ } \ -} +} CBUPRV_BLOCK_MACRO_END + +/** + * Adjust a random-access offset to a code point boundary + * at the start of a code point. + * If the offset points to the trail surrogate of a surrogate pair, + * then the offset is decremented. + * Otherwise, it is not modified. + * "Safe" macro, handles unpaired surrogates and checks for string boundaries. + * + * @param s const UChar * string + * @param start starting string offset (usually 0) + * @param i string offset, must be start<=i + * @see U16_SET_CP_START_UNSAFE + * @stable ICU 2.4 + */ +#define CBU16_SET_CP_START(s, start, i) CBUPRV_BLOCK_MACRO_BEGIN { \ + if(CBU16_IS_TRAIL((s)[i]) && (i)>(start) && CBU16_IS_LEAD((s)[(i)-1])) { \ + --(i); \ + } \ +} CBUPRV_BLOCK_MACRO_END + +/** + * Move the string offset from one code point boundary to the previous one + * and get the code point between them. + * (Pre-decrementing backward iteration.) + * "Safe" macro, handles unpaired surrogates and checks for string boundaries. + * + * The input offset may be the same as the string length. + * If the offset is behind a trail surrogate unit + * for a supplementary code point, then the macro will read + * the preceding lead surrogate as well. + * If the offset is behind a lead surrogate or behind a single, unpaired + * trail surrogate, then c is set to that unpaired surrogate. + * + * @param s const UChar * string + * @param start starting string offset (usually 0) + * @param i string offset, must be start<i + * @param c output UChar32 variable + * @see U16_PREV_UNSAFE + * @stable ICU 2.4 + */ +#define CBU16_PREV(s, start, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[--(i)]; \ + if(CBU16_IS_TRAIL(c)) { \ + uint16_t __c2; \ + if((i)>(start) && CBU16_IS_LEAD(__c2=(s)[(i)-1])) { \ + --(i); \ + (c)=CBU16_GET_SUPPLEMENTARY(__c2, (c)); \ + } \ + } \ +} CBUPRV_BLOCK_MACRO_END + +/** + * Adjust a random-access offset to a code point boundary after a code point. + * If the offset is behind the lead surrogate of a surrogate pair, + * then the offset is incremented. + * Otherwise, it is not modified. + * The input offset may be the same as the string length. + * "Safe" macro, handles unpaired surrogates and checks for string boundaries. + * + * The length can be negative for a NUL-terminated string. + * + * @param s const UChar * string + * @param start int32_t starting string offset (usually 0) + * @param i int32_t string offset, start<=i<=length + * @param length int32_t string length + * @see U16_SET_CP_LIMIT_UNSAFE + * @stable ICU 2.4 + */ +#define CBU16_SET_CP_LIMIT(s, start, i, length) CBUPRV_BLOCK_MACRO_BEGIN { \ + if((start)<(i) && ((i)<(length) || (length)<0) && CBU16_IS_LEAD((s)[(i)-1]) && CBU16_IS_TRAIL((s)[i])) { \ + ++(i); \ + } \ +} CBUPRV_BLOCK_MACRO_END } // namesapce base_icu
diff --git a/build/build_config.h b/build/build_config.h index 6a6028d..c69df41 100644 --- a/build/build_config.h +++ b/build/build_config.h
@@ -3,15 +3,35 @@ // found in the LICENSE file. // This file adds defines about the platform we're currently building on. +// // Operating System: -// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) / -// OS_NACL (NACL_SFI or NACL_NONSFI) / OS_NACL_SFI / OS_NACL_NONSFI -// OS_CHROMEOS is set by the build system +// OS_AIX / OS_ANDROID / OS_ASMJS / OS_FREEBSD / OS_FUCHSIA / OS_IOS / +// OS_LINUX / OS_MAC / OS_NACL (SFI or NONSFI) / OS_NETBSD / OS_OPENBSD / +// OS_QNX / OS_SOLARIS / OS_WIN +// Operating System family: +// OS_APPLE: IOS or MAC +// OS_BSD: FREEBSD or NETBSD or OPENBSD +// OS_POSIX: AIX or ANDROID or ASMJS or CHROMEOS or FREEBSD or IOS or LINUX +// or MAC or NACL or NETBSD or OPENBSD or QNX or SOLARIS +// +// /!\ Note: OS_CHROMEOS is set by the build system, not this file +// // Compiler: // COMPILER_MSVC / COMPILER_GCC +// // Processor: -// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64) -// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS +// ARCH_CPU_ARM64 / ARCH_CPU_ARMEL / ARCH_CPU_MIPS / ARCH_CPU_MIPS64 / +// ARCH_CPU_MIPS64EL / ARCH_CPU_MIPSEL / ARCH_CPU_PPC64 / ARCH_CPU_S390 / +// ARCH_CPU_S390X / ARCH_CPU_X86 / ARCH_CPU_X86_64 +// Processor family: +// ARCH_CPU_ARM_FAMILY: ARMEL or ARM64 +// ARCH_CPU_MIPS_FAMILY: MIPS64EL or MIPSEL or MIPS64 or MIPS +// ARCH_CPU_PPC64_FAMILY: PPC64 +// ARCH_CPU_S390_FAMILY: S390 or S390X +// ARCH_CPU_X86_FAMILY: X86 or X86_64 +// Processor features: +// ARCH_CPU_31_BITS / ARCH_CPU_32_BITS / ARCH_CPU_64_BITS +// ARCH_CPU_BIG_ENDIAN / ARCH_CPU_LITTLE_ENDIAN #ifndef BUILD_BUILD_CONFIG_H_ #define BUILD_BUILD_CONFIG_H_ @@ -31,20 +51,25 @@ #elif defined(ANDROID) #define OS_ANDROID 1 #elif defined(__APPLE__) -// only include TargetConditions after testing ANDROID as some android builds -// on mac don't have this header available and it's not needed unless the target -// is really mac/ios. +// Only include TargetConditionals after testing ANDROID as some Android builds +// on the Mac have this header available and it's not needed unless the target +// is really an Apple platform. #include <TargetConditionals.h> -#define OS_MACOSX 1 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #define OS_IOS 1 +#else +#define OS_MAC 1 #endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #elif defined(__linux__) +#if !defined(OS_CHROMEOS) +// Do not define OS_LINUX on Chrome OS build. +// The OS_CHROMEOS macro is defined in GN. #define OS_LINUX 1 -// include a system header to pull in features.h for glibc/uclibc macros. +#endif // !defined(OS_CHROMEOS) +// Include a system header to pull in features.h for glibc/uclibc macros. #include <unistd.h> #if defined(__GLIBC__) && !defined(__UCLIBC__) -// we really are using glibc, not uClibc pretending to be glibc +// We really are using glibc, not uClibc pretending to be glibc. #define LIBC_GLIBC 1 #endif #elif defined(_WIN32) @@ -71,6 +96,10 @@ // NOTE: Adding a new port? Please follow // https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md +#if defined(OS_MAC) || defined(OS_IOS) +#define OS_APPLE 1 +#endif + // For access to standard BSD features, use OS_BSD instead of a // more specific macro. #if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD) @@ -79,10 +108,11 @@ // For access to standard POSIXish features, use OS_POSIX instead of a // more specific macro. -#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \ - defined(OS_FREEBSD) || defined(OS_LINUX) || defined(OS_MACOSX) || \ - defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) || \ - defined(OS_QNX) || defined(OS_SOLARIS) +#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \ + defined(OS_FREEBSD) || defined(OS_IOS) || defined(OS_LINUX) || \ + defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_NACL) || \ + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \ + defined(OS_SOLARIS) #define OS_POSIX 1 #endif
diff --git a/copy.bara.sky b/copy.bara.sky index 328fbab..fefc63d 100644 --- a/copy.bara.sky +++ b/copy.bara.sky
@@ -14,8 +14,11 @@ "LICENSE", "base/compiler_specific.h", "base/containers/checked_iterators.h", + "base/containers/contiguous_iterator.h", "base/containers/span.h", "base/containers/util.h", + "base/functional/*.h", + "base/ranges/*.h", "base/macros.h", "base/debug/leak_annotations.h", "base/no_destructor.h", @@ -88,7 +91,7 @@ core.replace( '"third_party/icu/source/common/unicode/${file}.h"', "<unicode/${file}.h>", - regex_groups = {"file": "\w+"}, + regex_groups = {"file": "\\w+"}, ), ]
diff --git a/url/gurl.cc b/url/gurl.cc index 5ac3ea1..68f3f8c 100644 --- a/url/gurl.cc +++ b/url/gurl.cc
@@ -485,7 +485,7 @@ if (has_host() || has_username() || has_password() || has_port()) return false; - if (!path_piece().starts_with(allowed_path)) + if (!gurl_base::StartsWith(path_piece(), allowed_path)) return false; if (path_piece().size() == allowed_path.size()) {
diff --git a/url/gurl_fuzzer.cc b/url/gurl_fuzzer.cc index a07c195..3b28aea 100644 --- a/url/gurl_fuzzer.cc +++ b/url/gurl_fuzzer.cc
@@ -3,7 +3,9 @@ // found in the LICENSE file. #include "base/at_exit.h" +#include "polyfills/base/check_op.h" #include "base/i18n/icu_util.h" +#include "base/no_destructor.h" #include "url/gurl.h" struct TestCase { @@ -15,53 +17,73 @@ TestCase* test_case = new TestCase(); -// Empty replacements cause no change when applied. -GURL::Replacements* no_op = new GURL::Replacements(); +// Checks that GURL's canonicalization is idempotent. This can help discover +// issues like https://crbug.com/1128999. +void CheckIdempotency(const GURL& url) { + if (!url.is_valid()) + return; + const std::string& spec = url.spec(); + GURL recanonicalized(spec); + GURL_CHECK(recanonicalized.is_valid()); + GURL_CHECK_EQ(spec, recanonicalized.spec()); +} + +// Checks that |url.spec()| is preserved across a call to ReplaceComponents with +// zero replacements, which is effectively a copy. This can help discover issues +// like https://crbug.com/1075515. +void CheckReplaceComponentsPreservesSpec(const GURL& url) { + static const gurl_base::NoDestructor<GURL::Replacements> no_op; + GURL copy = url.ReplaceComponents(*no_op); + GURL_CHECK_EQ(url.is_valid(), copy.is_valid()); + if (url.is_valid()) { + GURL_CHECK_EQ(url.spec(), copy.spec()); + } +} // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < 1) return 0; - - gurl_base::StringPiece string_piece_input(reinterpret_cast<const char*>(data), - size); - GURL url_from_string_piece(string_piece_input); - // Copying by applying empty replacements exercises interesting code paths. - // This can help discover issues like https://crbug.com/1075515. - GURL copy = url_from_string_piece.ReplaceComponents(*no_op); - GURL_CHECK_EQ(url_from_string_piece.is_valid(), copy.is_valid()); - if (url_from_string_piece.is_valid()) { - GURL_CHECK_EQ(url_from_string_piece.spec(), copy.spec()); + { + gurl_base::StringPiece string_piece_input(reinterpret_cast<const char*>(data), + size); + const GURL url_from_string_piece(string_piece_input); + CheckIdempotency(url_from_string_piece); + CheckReplaceComponentsPreservesSpec(url_from_string_piece); } - // Test for StringPiece16 if size is even. if (size % 2 == 0) { gurl_base::StringPiece16 string_piece_input16( reinterpret_cast<const gurl_base::char16*>(data), size / 2); - - GURL url_from_string_piece16(string_piece_input16); + const GURL url_from_string_piece16(string_piece_input16); + CheckIdempotency(url_from_string_piece16); + CheckReplaceComponentsPreservesSpec(url_from_string_piece16); } - // Resolve relative url tests. - size_t size_t_bytes = sizeof(size_t); - if (size < size_t_bytes + 1) { - return 0; - } - size_t relative_size = - *reinterpret_cast<const size_t*>(data) % (size - size_t_bytes); - std::string relative_string( - reinterpret_cast<const char*>(data + size_t_bytes), relative_size); - gurl_base::StringPiece string_piece_part_input( - reinterpret_cast<const char*>(data + size_t_bytes + relative_size), - size - relative_size - size_t_bytes); - GURL url_from_string_piece_part(string_piece_part_input); - url_from_string_piece_part.Resolve(relative_string); + { + size_t size_t_bytes = sizeof(size_t); + if (size < size_t_bytes + 1) { + return 0; + } + size_t relative_size = + *reinterpret_cast<const size_t*>(data) % (size - size_t_bytes); + std::string relative_string( + reinterpret_cast<const char*>(data + size_t_bytes), relative_size); + gurl_base::StringPiece string_piece_part_input( + reinterpret_cast<const char*>(data + size_t_bytes + relative_size), + size - relative_size - size_t_bytes); + const GURL url_from_string_piece_part(string_piece_part_input); + CheckIdempotency(url_from_string_piece_part); + CheckReplaceComponentsPreservesSpec(url_from_string_piece_part); - if (relative_size % 2 == 0) { - gurl_base::string16 relative_string16( - reinterpret_cast<const gurl_base::char16*>(data + size_t_bytes), - relative_size / 2); - url_from_string_piece_part.Resolve(relative_string16); + url_from_string_piece_part.Resolve(relative_string); + + if (relative_size % 2 == 0) { + gurl_base::string16 relative_string16( + reinterpret_cast<const gurl_base::char16*>(data + size_t_bytes), + relative_size / 2); + url_from_string_piece_part.Resolve(relative_string16); + } } return 0; }
diff --git a/url/origin.cc b/url/origin.cc index 574c512..d04e557 100644 --- a/url/origin.cc +++ b/url/origin.cc
@@ -244,7 +244,7 @@ return Origin(Nonce(), tuple_); } -std::string Origin::GetDebugString() const { +std::string Origin::GetDebugString(bool include_nonce) const { // Handle non-opaque origins first, as they are simpler. if (!opaque()) { std::string out = Serialize(); @@ -256,11 +256,15 @@ // For opaque origins, log the nonce and precursor as well. Without this, // EXPECT_EQ failures between opaque origins are nearly impossible to // understand. - std::string nonce = nonce_->raw_token().is_empty() - ? std::string("nonce TBD") - : nonce_->raw_token().ToString(); - - std::string out = gurl_base::StrCat({Serialize(), " [internally: (", nonce, ")"}); + std::string out = gurl_base::StrCat({Serialize(), " [internally:"}); + if (include_nonce) { + out += " ("; + if (nonce_->raw_token().is_empty()) + out += "nonce TBD"; + else + out += nonce_->raw_token().ToString(); + out += ")"; + } if (!tuple_.IsValid()) gurl_base::StrAppend(&out, {" anonymous]"}); else @@ -283,11 +287,20 @@ GURL_DCHECK_EQ(0U, port()); } +gurl_base::Optional<std::string> Origin::SerializeWithNonce() const { + return SerializeWithNonceImpl(); +} + +gurl_base::Optional<std::string> Origin::SerializeWithNonceAndInitIfNeeded() { + GetNonceForSerialization(); + return SerializeWithNonceImpl(); +} + // The pickle is saved in the following format, in order: // string - tuple_.GetURL().spec(). // uint64_t (if opaque) - high bits of nonce if opaque. 0 if not initialized. // uint64_t (if opaque) - low bits of nonce if opaque. 0 if not initialized. -gurl_base::Optional<std::string> Origin::SerializeWithNonce() const { +gurl_base::Optional<std::string> Origin::SerializeWithNonceImpl() const { if (!opaque() && !tuple_.IsValid()) return gurl_base::nullopt; @@ -438,7 +451,8 @@ const url::Origin* value) : gurl_base::debug::ScopedCrashKeyString( crash_key, - value ? value->GetDebugString() : "nullptr") {} + value ? value->GetDebugString(false /* include_nonce */) + : "nullptr") {} ScopedOriginCrashKey::~ScopedOriginCrashKey() = default;
diff --git a/url/origin.h b/url/origin.h index 2aef330..f4c1bf8 100644 --- a/url/origin.h +++ b/url/origin.h
@@ -58,6 +58,7 @@ namespace net { class NetworkIsolationKey; class OpaqueNonTransientNetworkIsolationKeyTest; +class SchemefulSite; } // namespace net namespace url { @@ -288,7 +289,7 @@ // Creates a string representation of the object that can be used for logging // and debugging. It serializes the internal state, such as the nonce value // and precursor information. - std::string GetDebugString() const; + std::string GetDebugString(bool include_nonce = true) const; #if defined(OS_ANDROID) gurl_base::android::ScopedJavaLocalRef<jobject> CreateJavaObject() const; @@ -299,6 +300,9 @@ private: friend class blink::SecurityOrigin; friend class net::NetworkIsolationKey; + // SchemefulSite needs access to the serialization/deserialization logic which + // includes the nonce. + friend class net::SchemefulSite; friend class net::OpaqueNonTransientNetworkIsolationKeyTest; friend class OriginTest; friend struct mojo::UrlOriginAdapter; @@ -395,11 +399,17 @@ gurl_base::Optional<gurl_base::UnguessableToken> GetNonceForSerialization() const; // Serializes this Origin, including its nonce if it is opaque. If an opaque - // origin's |tuple_| is invalid or the nonce isn't initialized, nullopt is - // returned. Use of this method should be limited as an opaque origin will - // never be matchable in future browser sessions. + // origin's |tuple_| is invalid nullopt is returned. If the nonce is not + // initialized, a nonce of 0 is used. Use of this method should be limited as + // an opaque origin will never be matchable in future browser sessions. gurl_base::Optional<std::string> SerializeWithNonce() const; + // Like SerializeWithNonce(), but forces |nonce_| to be initialized prior to + // serializing. + gurl_base::Optional<std::string> SerializeWithNonceAndInitIfNeeded(); + + gurl_base::Optional<std::string> SerializeWithNonceImpl() const; + // Deserializes an origin from |ToValueWithNonce|. Returns nullopt if the // value was invalid in any way. static gurl_base::Optional<Origin> Deserialize(const std::string& value);
diff --git a/url/origin_unittest.cc b/url/origin_unittest.cc index 8fe1cef..7793b9a 100644 --- a/url/origin_unittest.cc +++ b/url/origin_unittest.cc
@@ -109,6 +109,11 @@ return origin.SerializeWithNonce(); } + gurl_base::Optional<std::string> SerializeWithNonceAndInitIfNeeded( + Origin& origin) { + return origin.SerializeWithNonceAndInitIfNeeded(); + } + gurl_base::Optional<Origin> Deserialize(const std::string& value) { return Origin::Deserialize(value); } @@ -846,6 +851,10 @@ http_opaque_origin.GetDebugString().c_str(), ::testing::MatchesRegex( "null \\[internally: \\(\\w*\\) derived from http://192.168.9.1\\]")); + EXPECT_THAT( + http_opaque_origin.GetDebugString(false /* include_nonce */).c_str(), + ::testing::MatchesRegex( + "null \\[internally: derived from http://192.168.9.1\\]")); Origin data_origin = Origin::Create(GURL("data:")); EXPECT_STREQ(data_origin.GetDebugString().c_str(), @@ -857,6 +866,9 @@ EXPECT_THAT( data_derived_origin.GetDebugString().c_str(), ::testing::MatchesRegex("null \\[internally: \\(\\w*\\) anonymous\\]")); + EXPECT_THAT( + data_derived_origin.GetDebugString(false /* include_nonce */).c_str(), + ::testing::MatchesRegex("null \\[internally: anonymous\\]")); Origin file_origin = Origin::Create(GURL("file:///etc/passwd")); EXPECT_STREQ(file_origin.GetDebugString().c_str(), @@ -926,6 +938,19 @@ // Can't use DoEqualityComparisons here since empty nonces are never == unless // they are the same object. EXPECT_EQ(opaque.GetDebugString(), deserialized.value().GetDebugString()); + + // Now force initialization of the nonce prior to serialization. + for (const GURL& url : invalid_urls) { + SCOPED_TRACE(url.spec()); + Origin origin = Origin::Create(url); + gurl_base::Optional<std::string> serialized = + SerializeWithNonceAndInitIfNeeded(origin); + gurl_base::Optional<Origin> deserialized = Deserialize(std::move(*serialized)); + ASSERT_TRUE(deserialized.has_value()); + + // The nonce should have been initialized prior to Serialization(). + EXPECT_EQ(origin, deserialized.value()); + } } TEST_F(OriginTest, DeserializeValidNonce) {
diff --git a/url/third_party/mozilla/DIR_METADATA b/url/third_party/mozilla/DIR_METADATA new file mode 100644 index 0000000..fb07a25 --- /dev/null +++ b/url/third_party/mozilla/DIR_METADATA
@@ -0,0 +1,11 @@ +# Metadata information for this directory. +# +# For more information on DIR_METADATA files, see: +# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# +# For the schema of this file, see Metadata message: +# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto + +monorail { + component: "Internals>Core" +} \ No newline at end of file
diff --git a/url/third_party/mozilla/OWNERS b/url/third_party/mozilla/OWNERS deleted file mode 100644 index 3605f48..0000000 --- a/url/third_party/mozilla/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -# COMPONENT: Internals>Core
diff --git a/url/url_canon_etc.cc b/url/url_canon_etc.cc index 23d1235..8482c35 100644 --- a/url/url_canon_etc.cc +++ b/url/url_canon_etc.cc
@@ -245,10 +245,9 @@ } // clang-format off -// Percent-escape all "C0 controls" (0x00-0x1F) -// https://infra.spec.whatwg.org/#c0-control along with the characters ' ' -// (0x20), '"' (0x22), '<' (0x3C), '>' (0x3E), and '`' (0x60): -const bool kShouldEscapeCharInRef[0x80] = { +// Percent-escape all characters from the fragment percent-encode set +// https://url.spec.whatwg.org/#fragment-percent-encode-set +const bool kShouldEscapeCharInFragment[0x80] = { // Control characters (0x00-0x1F) true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, @@ -276,8 +275,8 @@ false, false, false, false, false, false, false, false, // p q r s t u v w false, false, false, false, false, false, false, false, -// x y z { | } ~ - false, false, false, false, false, false, false +// x y z { | } ~ DELETE + false, false, false, false, false, false, false, true }; // clang-format on @@ -307,7 +306,7 @@ UCHAR current_char = static_cast<UCHAR>(spec[i]); if (current_char < 0x80) { - if (kShouldEscapeCharInRef[current_char]) + if (kShouldEscapeCharInFragment[current_char]) AppendEscapedChar(static_cast<unsigned char>(spec[i]), output); else output->push_back(static_cast<char>(spec[i]));
diff --git a/url/url_canon_pathurl.cc b/url/url_canon_pathurl.cc index 62fe22f..0330b06 100644 --- a/url/url_canon_pathurl.cc +++ b/url/url_canon_pathurl.cc
@@ -26,13 +26,16 @@ if (separator) output->push_back(separator); // Copy the path using path URL's more lax escaping rules (think for - // javascript:). We convert to UTF-8 and escape non-ASCII, but leave all - // ASCII characters alone. This helps readability of JavaStript. + // javascript:). We convert to UTF-8 and escape characters from the + // C0 control percent-encode set, but leave all other characters alone. + // This helps readability of JavaScript. + // https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state + // https://url.spec.whatwg.org/#c0-control-percent-encode-set new_component->begin = output->length(); int end = component.end(); for (int i = component.begin; i < end; i++) { UCHAR uch = static_cast<UCHAR>(source[i]); - if (uch < 0x20 || uch >= 0x80) + if (uch < 0x20 || uch > 0x7E) AppendUTF8EscapedChar(source, &i, end, output); else output->push_back(static_cast<char>(uch));
diff --git a/url/url_canon_stdstring.h b/url/url_canon_stdstring.h index 82ee9db..9b7943a 100644 --- a/url/url_canon_stdstring.h +++ b/url/url_canon_stdstring.h
@@ -54,33 +54,55 @@ // // The contents of the StringPieces are not copied and must remain valid until // the StringPieceReplacements object goes out of scope. -template<typename STR> +// +// In order to make it harder to misuse the API the setters do not accept rvalue +// references to std::strings. +// Note: Extra const char* overloads are necessary to break ambiguities that +// would otherwise exist for char literals. +template <typename STR> class StringPieceReplacements : public Replacements<typename STR::value_type> { + private: + using CharT = typename STR::value_type; + using StringPieceT = gurl_base::BasicStringPiece<STR>; + using ParentT = Replacements<CharT>; + using SetterFun = void (ParentT::*)(const CharT*, const Component&); + + void SetImpl(SetterFun fun, StringPieceT str) { + (this->*fun)(str.data(), Component(0, static_cast<int>(str.size()))); + } + public: - void SetSchemeStr(const gurl_base::BasicStringPiece<STR>& s) { - this->SetScheme(s.data(), Component(0, static_cast<int>(s.length()))); - } - void SetUsernameStr(const gurl_base::BasicStringPiece<STR>& s) { - this->SetUsername(s.data(), Component(0, static_cast<int>(s.length()))); - } - void SetPasswordStr(const gurl_base::BasicStringPiece<STR>& s) { - this->SetPassword(s.data(), Component(0, static_cast<int>(s.length()))); - } - void SetHostStr(const gurl_base::BasicStringPiece<STR>& s) { - this->SetHost(s.data(), Component(0, static_cast<int>(s.length()))); - } - void SetPortStr(const gurl_base::BasicStringPiece<STR>& s) { - this->SetPort(s.data(), Component(0, static_cast<int>(s.length()))); - } - void SetPathStr(const gurl_base::BasicStringPiece<STR>& s) { - this->SetPath(s.data(), Component(0, static_cast<int>(s.length()))); - } - void SetQueryStr(const gurl_base::BasicStringPiece<STR>& s) { - this->SetQuery(s.data(), Component(0, static_cast<int>(s.length()))); - } - void SetRefStr(const gurl_base::BasicStringPiece<STR>& s) { - this->SetRef(s.data(), Component(0, static_cast<int>(s.length()))); - } + void SetSchemeStr(const CharT* str) { SetImpl(&ParentT::SetScheme, str); } + void SetSchemeStr(StringPieceT str) { SetImpl(&ParentT::SetScheme, str); } + void SetSchemeStr(const STR&&) = delete; + + void SetUsernameStr(const CharT* str) { SetImpl(&ParentT::SetUsername, str); } + void SetUsernameStr(StringPieceT str) { SetImpl(&ParentT::SetUsername, str); } + void SetUsernameStr(const STR&&) = delete; + + void SetPasswordStr(const CharT* str) { SetImpl(&ParentT::SetPassword, str); } + void SetPasswordStr(StringPieceT str) { SetImpl(&ParentT::SetPassword, str); } + void SetPasswordStr(const STR&&) = delete; + + void SetHostStr(const CharT* str) { SetImpl(&ParentT::SetHost, str); } + void SetHostStr(StringPieceT str) { SetImpl(&ParentT::SetHost, str); } + void SetHostStr(const STR&&) = delete; + + void SetPortStr(const CharT* str) { SetImpl(&ParentT::SetPort, str); } + void SetPortStr(StringPieceT str) { SetImpl(&ParentT::SetPort, str); } + void SetPortStr(const STR&&) = delete; + + void SetPathStr(const CharT* str) { SetImpl(&ParentT::SetPath, str); } + void SetPathStr(StringPieceT str) { SetImpl(&ParentT::SetPath, str); } + void SetPathStr(const STR&&) = delete; + + void SetQueryStr(const CharT* str) { SetImpl(&ParentT::SetQuery, str); } + void SetQueryStr(StringPieceT str) { SetImpl(&ParentT::SetQuery, str); } + void SetQueryStr(const STR&&) = delete; + + void SetRefStr(const CharT* str) { SetImpl(&ParentT::SetRef, str); } + void SetRefStr(StringPieceT str) { SetImpl(&ParentT::SetRef, str); } + void SetRefStr(const STR&&) = delete; }; } // namespace url