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