From: Andreas Tille Date: Sat, 16 Jan 2021 16:30:37 +0000 (+0000) Subject: Import r-cran-s2_1.0.4.orig.tar.gz X-Git-Tag: archive/raspbian/1.1.0-1+rpi1~1^2^2~1 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=73572f7ba6049b4730f68ace2faad50025e458ef;p=r-cran-s2.git Import r-cran-s2_1.0.4.orig.tar.gz [dgit import orig r-cran-s2_1.0.4.orig.tar.gz] --- 73572f7ba6049b4730f68ace2faad50025e458ef diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 0000000..5aec611 --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,44 @@ +Package: s2 +Title: Spherical Geometry Operators Using the S2 Geometry Library +Version: 1.0.4 +Authors@R: c( + person(given = "Dewey", + family = "Dunnington", + role = c("aut"), + email = "dewey@fishandwhistle.net", + comment = c(ORCID = "0000-0002-9415-4582")), + person(given = "Edzer", + family = "Pebesma", + role = c("aut", "cre"), + email = "edzer.pebesma@uni-muenster.de", + comment = c(ORCID = "0000-0001-8049-7069")), + person("Ege", "Rubak", email="rubak@math.aau.dk", role = c("aut")), + person("Jeroen", "Ooms", , "jeroen.ooms@stat.ucla.edu", role = "ctb", comment = "configure script"), + person(family = "Google, Inc.", role = "cph", comment = "Original s2geometry.io source code") + ) +Description: Provides R bindings for Google's s2 library for geometric calculations on + the sphere. High-performance constructors and exporters provide high compatibility + with existing spatial packages, transformers construct new geometries from existing + geometries, predicates provide a means to select geometries based on spatial + relationships, and accessors extract information about geometries. +License: Apache License (== 2.0) +Encoding: UTF-8 +LazyData: true +RoxygenNote: 7.1.1 +LinkingTo: Rcpp, wk +Imports: Rcpp, wk +Suggests: testthat, vctrs +URL: https://r-spatial.github.io/s2/, https://github.com/r-spatial/s2, + https://s2geometry.io/ +BugReports: https://github.com/r-spatial/s2/issues +Depends: R (>= 3.0.0) +NeedsCompilation: yes +Packaged: 2021-01-04 15:35:03 UTC; edzer +Author: Dewey Dunnington [aut] (), + Edzer Pebesma [aut, cre] (), + Ege Rubak [aut], + Jeroen Ooms [ctb] (configure script), + Google, Inc. [cph] (Original s2geometry.io source code) +Maintainer: Edzer Pebesma +Repository: CRAN +Date/Publication: 2021-01-05 05:10:02 UTC diff --git a/MD5 b/MD5 new file mode 100644 index 0000000..d3156d0 --- /dev/null +++ b/MD5 @@ -0,0 +1,395 @@ +b0d140c3a6a007b1192cae3e0569df2f *DESCRIPTION +5129788c48d634be1089286a041d824a *NAMESPACE +458d1fd3761859921eb8b30d0a27fd3b *NEWS.md +49c283041cce525dd99897186dcc8584 *R/RcppExports.R +8567a6b7a3c75cad01a581303546fced *R/data.R +8aa358cca3ffcc922b9bde02ef1accd4 *R/s2-accessors.R +4ba8ec7d16efeadbc21c3ab5ac3dc6a6 *R/s2-bounds.R +a9b85fd1af5d8ffba7f46f9d16b333ee *R/s2-constructors-formatters.R +0968515e318a2c7a7372bddad1be6c23 *R/s2-earth.R +f29b8dcf891e34b8feae519619914151 *R/s2-geography.R +f7370a030a2faf0fb9a79166fc012ea2 *R/s2-lnglat.R +11b87d96773046950186957f2af9478b *R/s2-matrix.R +e10121cec72be69c2500e9121fcc128e *R/s2-options.R +1bab90d590a0e66114c86739ba0d6ce4 *R/s2-package.R +e78c2f5f3bc790e3fa91d3f5e037754e *R/s2-point.R +447d319c0cbec576de0695a518292089 *R/s2-predicates.R +382b5047b50a837dee2795239ec216ad *R/s2-transformers.R +28d0bdc778944f01d0203a6083305b1a *R/s2-xptr.R +4e8bc5586b795db8a2f8e65074ad05c1 *R/utils.R +514ae694f7596f697979c5cbce1fbc37 *R/vctrs.R +474285eb7c0d146036962c8f7248d1bb *R/zzz.R +27f780fb97d6a58b2b6453c28ab740df *README.md +a1c350c18af8b26ad476e0bee45ce1d3 *build/partial.rdb +cc6eebf14a3f516ec74168f3ea416fb2 *cleanup +9401515addc6f00f403443fd0715fcf9 *configure +d41d8cd98f00b204e9800998ecf8427e *configure.win +6d0a8453b0e12b4654d468d1e7248961 *data/s2_data_tbl_cities.rda +b0b89f3499ca649d9a78f77096e4f5da *data/s2_data_tbl_countries.rda +5bbeed937d4c3e766c02ce396f77cefc *data/s2_data_tbl_timezones.rda +afabc65ce7a01abdb14efb1abccd7247 *inst/include/cpp-compat.h +8428235fc1165d0c32375e2e21a686fe *inst/include/s2/_fp_contract_off.h +782cb0deddf3125910ebf4b02a8efc46 *inst/include/s2/base/casts.h +4c10c37f35c3ea32da84e18e427e0a8f *inst/include/s2/base/commandlineflags.h +95d68a4bda8ac1ed1238c06999c7fe8f *inst/include/s2/base/integral_types.h +ab21c7db7743b7fbef680e8edd02c198 *inst/include/s2/base/log_severity.h +23e37fa14dfbbf7e1e232816f351d7cd *inst/include/s2/base/logging.h +84003e3ea7ed2316b94891afd7d425f0 *inst/include/s2/base/mutex.h +2dca5b2433d232fc5fd572920892de77 *inst/include/s2/base/port.h +7217ae2988c45fe3c657a6fbb5a01334 *inst/include/s2/base/spinlock.h +c71fa2b58596fe3acc23b3191fb30c01 *inst/include/s2/base/stringprintf.h +a5325b3b1a8569a91a072858d9961a76 *inst/include/s2/base/strtoint.h +8a23b88128feaefecac82381870b7e1b *inst/include/s2/base/timer.h +5c886186eca3428ebe67594a29a43066 *inst/include/s2/encoded_s2cell_id_vector.h +f20c096d5c4d4272d6160b94321986e8 *inst/include/s2/encoded_s2point_vector.h +8c71134bbac741ab14ad58645c587efd *inst/include/s2/encoded_s2shape_index.h +90dca9b7842ad1910d1de858afad423c *inst/include/s2/encoded_string_vector.h +40708275f82de6dd1d212c57c36ecee8 *inst/include/s2/encoded_uint_vector.h +e7cbe5a6c88b9a286b17adb5e690286f *inst/include/s2/id_set_lexicon.h +d1d7c273710a5a6dccb132f810f2ed53 *inst/include/s2/mutable_s2shape_index.h +a5fd0e84e01c56c79c50f208ae1b08b9 *inst/include/s2/r1interval.h +e363bde392175774fc26dcde1c3b5076 *inst/include/s2/r2.h +4b12844b6a0327fbc6f0488b4fbea0b3 *inst/include/s2/r2rect.h +279bd4aa8ae3c5f5a5b71cd8e4b66c2c *inst/include/s2/s1angle.h +c3c25817e0a471da972a15c3a4ce8da0 *inst/include/s2/s1chord_angle.h +2a46bfae65f4a24dbbc0c8a7df478c4c *inst/include/s2/s1interval.h +99423d6009cb68d7879793f9311e1bb7 *inst/include/s2/s2boolean_operation.h +f0e436fe4992a1c5f451c08e7be373e8 *inst/include/s2/s2builder.h +cc61ae8fff8370c728bf6de7513e06d9 *inst/include/s2/s2builder_graph.h +0558b2bb40025b583974ac1a5d2b5857 *inst/include/s2/s2builder_layer.h +4b17df092dd21d0d5b22499a7a0d8d63 *inst/include/s2/s2builderutil_closed_set_normalizer.h +dc965c407adeb3070db3510de3b1f0bd *inst/include/s2/s2builderutil_find_polygon_degeneracies.h +59390c82ae51ffd8dc8a2154c7f5cc6b *inst/include/s2/s2builderutil_graph_shape.h +66c64baff11d7ed56ac3e7f93fdd5ac0 *inst/include/s2/s2builderutil_lax_polygon_layer.h +40506cccfe04e37e54a5bc8f1bde6023 *inst/include/s2/s2builderutil_s2point_vector_layer.h +cbbba4e593a1ec4c5554b1021939548a *inst/include/s2/s2builderutil_s2polygon_layer.h +9a36a2942ed405fe0b4b9b2e81bf0a65 *inst/include/s2/s2builderutil_s2polyline_layer.h +929a066a7fd622cbc8f1a41b2d579962 *inst/include/s2/s2builderutil_s2polyline_vector_layer.h +14fae38b064b536a50fd99090bf2bb80 *inst/include/s2/s2builderutil_snap_functions.h +464db150b43db1a4a05c4aef25f9fdb1 *inst/include/s2/s2builderutil_testing.h +1aaf20b1794973131d85bbd6d16fc296 *inst/include/s2/s2cap.h +cc050d60fc6b31479ad2cfc444aebc2b *inst/include/s2/s2cell.h +2ee5d8157ce731621c92127411af51dc *inst/include/s2/s2cell_id.h +c967db283c31cf3b04720da4aaf951be *inst/include/s2/s2cell_index.h +ca583789e8fe43d4acea37b55e0f5930 *inst/include/s2/s2cell_union.h +b0e46b1f9b0302bf28993c83b1c29952 *inst/include/s2/s2centroids.h +1022d523b50d2186b166ca68dc0deb11 *inst/include/s2/s2closest_cell_query.h +15e3edaba2c2363568b38435c1ccb49e *inst/include/s2/s2closest_cell_query_base.h +dca82b1156a1a4c8f587ded132c2f253 *inst/include/s2/s2closest_edge_query.h +283c65cbbae272d89e5c311ea02297cd *inst/include/s2/s2closest_edge_query_base.h +0974a6dabc5a7541eebf0e81656e522c *inst/include/s2/s2closest_edge_query_testing.h +e982b798e56156c65fb5ddf919a0520c *inst/include/s2/s2closest_point_query.h +838bd8a8fe4cbc3f4828e024d8dc6bae *inst/include/s2/s2closest_point_query_base.h +c5ac9b7327a1f2eb8c9b8683973bc48c *inst/include/s2/s2contains_point_query.h +9d28d2f3379f7dea949e76b5e82a3ac9 *inst/include/s2/s2contains_vertex_query.h +7c2da76e9c9917194d48939ee919f088 *inst/include/s2/s2convex_hull_query.h +e2a5abdcf611242bdf5cf0edf0eea5ad *inst/include/s2/s2coords.h +28c4af3631469e232aab685be3e33122 *inst/include/s2/s2coords_internal.h +3e4f0adaabe0eca3bc103088214106ad *inst/include/s2/s2crossing_edge_query.h +12e9446c9a552088c70b7b3062c1ba5b *inst/include/s2/s2debug.h +7298228fc7e23751181ad62e211a81b8 *inst/include/s2/s2distance_target.h +b90e02812e7d1afa32d15de3a0245dd8 *inst/include/s2/s2earth.h +5f01c8ad544b54056e6291838925512a *inst/include/s2/s2edge_clipping.h +91b536b7351c85048ac5f4fc8f9c36f3 *inst/include/s2/s2edge_crosser.h +a9ac7f4dd56aff0550689af376c3438f *inst/include/s2/s2edge_crossings.h +482bff39a41ea67b1073b9cfdc11400b *inst/include/s2/s2edge_crossings_internal.h +525ad6e536828fd0f783a532962bde59 *inst/include/s2/s2edge_distances.h +0f223cfa5b4b1153617bd64d47f68212 *inst/include/s2/s2edge_tessellator.h +99195e00ed62fc8127a6de6305730930 *inst/include/s2/s2edge_vector_shape.h +131a6fb00a28c854c2baadce7716e20e *inst/include/s2/s2error.h +89b12540c5e64ede6c1101f9d62da052 *inst/include/s2/s2furthest_edge_query.h +2a746c7606baf6562e51ed195e282afd *inst/include/s2/s2latlng.h +bd642eda609967162d94ac500632e454 *inst/include/s2/s2latlng_rect.h +486a11f20722a978644e99ec990516ad *inst/include/s2/s2latlng_rect_bounder.h +6aaf1e13f42ac06794d6179a45992a12 *inst/include/s2/s2lax_loop_shape.h +fdd99751ecd044e5de5cbebfb4eca06c *inst/include/s2/s2lax_polygon_shape.h +4d4f60c49166c8657258de45884c4087 *inst/include/s2/s2lax_polyline_shape.h +14cced4195983265280bfdbca39b9047 *inst/include/s2/s2loop.h +eef153a4e7bb190f620c250f78ac7c1c *inst/include/s2/s2loop_measures.h +ad2641cab77de9fe057639d5afdf4edd *inst/include/s2/s2max_distance_targets.h +b230afb1b144cfe2dfa5241d0d935cc1 *inst/include/s2/s2measures.h +c974a90f9d2ed8199af139ad33147aa4 *inst/include/s2/s2metrics.h +49d8a47ed9a0f58dd97b2258b1764e77 *inst/include/s2/s2min_distance_targets.h +b2841070cce458abff94905776f74eae *inst/include/s2/s2padded_cell.h +af4bfede404056bb6ecc81a6ec4ed141 *inst/include/s2/s2point.h +07518908a403a0b2a797c207fbf41e86 *inst/include/s2/s2point_compression.h +5fdf216ef50d1d05028f2506491342a5 *inst/include/s2/s2point_index.h +762457ccfc48a099fb96c63eb2db4829 *inst/include/s2/s2point_region.h +660d207e2a9aa481fecafd94e234ea6b *inst/include/s2/s2point_span.h +7d75a6945e15b9c78a713a59bd85f199 *inst/include/s2/s2point_vector_shape.h +6f748c0d5c03c3eaa0fecab91579975a *inst/include/s2/s2pointutil.h +cfec075841d3e7114220ef9e0d69e0c3 *inst/include/s2/s2polygon.h +0a0f0cfcdac7b7dea18ccf795c3c8e11 *inst/include/s2/s2polyline.h +c0f9b77a9439bc0c84d9dfe2dcbcc25b *inst/include/s2/s2polyline_alignment.h +eff6795f7a30bf765249022e2fdbab30 *inst/include/s2/s2polyline_alignment_internal.h +3b78b40b711bc148a7cc8f01f044d9a7 *inst/include/s2/s2polyline_measures.h +a12b87a494ef6e98dd8b19091ebf8cd8 *inst/include/s2/s2polyline_simplifier.h +3e580e8e5db3954a6918420df5d50630 *inst/include/s2/s2predicates.h +e819c0019ab31b8a7772353a762f6439 *inst/include/s2/s2predicates_internal.h +fa7c158fb73fced84c86e73bc40a4215 *inst/include/s2/s2projections.h +eef1eb7446a79b5b6430af8eede7a698 *inst/include/s2/s2r2rect.h +2234c6d0d865cc61ca6959c01e0015e4 *inst/include/s2/s2region.h +64f8f1de545faf4dcafdb206cfca2bf6 *inst/include/s2/s2region_coverer.h +bc10b64ffa44cbe8566d7daf5fb025b8 *inst/include/s2/s2region_intersection.h +24e9c6bee5ce52ebbf1307333d70301a *inst/include/s2/s2region_term_indexer.h +05b971668fa80d8d89579ad964aa7f27 *inst/include/s2/s2region_union.h +9e0666e7c03402dbf71fec6da5aa9244 *inst/include/s2/s2shape.h +5a143a135855c6c2a43d687cedbbc837 *inst/include/s2/s2shape_index.h +2f6f557e792611c362fee453ca16eb88 *inst/include/s2/s2shape_index_buffered_region.h +1caf918a11c8bafc0835861870fbd0ea *inst/include/s2/s2shape_index_measures.h +b095a57fa9ad2adfa3cc31f6b8d6fffd *inst/include/s2/s2shape_index_region.h +ce9d81e0539f9c3a136e07d12c46cb05 *inst/include/s2/s2shape_measures.h +36f49316d02ac4f5c1124d7a3365419f *inst/include/s2/s2shapeutil_build_polygon_boundaries.h +3c7dde9ddf64c467729e08fb30741bbe *inst/include/s2/s2shapeutil_coding.h +f6d89268f04abb12ccd63236d4362060 *inst/include/s2/s2shapeutil_contains_brute_force.h +1344b756712c76ac8c390abdb5a5886e *inst/include/s2/s2shapeutil_count_edges.h +e0937ffaeaa73535cb291409f285f2e3 *inst/include/s2/s2shapeutil_edge_iterator.h +53160e1333206256c06775aceb56ff6e *inst/include/s2/s2shapeutil_get_reference_point.h +d12c9628c73beb0f42bb462060facbe7 *inst/include/s2/s2shapeutil_range_iterator.h +a249fc71604963665d21c311c0e1c391 *inst/include/s2/s2shapeutil_shape_edge.h +bdf516e73992d5a15fdaa074596f8d6f *inst/include/s2/s2shapeutil_shape_edge_id.h +007fda069d8144645da7f2c7462a8359 *inst/include/s2/s2shapeutil_testing.h +f21bfbc04ce2d1d819512176b45954bb *inst/include/s2/s2shapeutil_visit_crossing_edge_pairs.h +d2af245ac0b0cff65e44c8b28a0c3451 *inst/include/s2/s2testing.h +388d47b1e65b685972ca35be834ce2c4 *inst/include/s2/s2text_format.h +3c1f74157144820e8e5008fdc433e018 *inst/include/s2/s2wedge_relations.h +d3ffa54eb4cab5a70d23bee5cbc17b3e *inst/include/s2/sequence_lexicon.h +5660849c05e4728e74114429b9b28339 *inst/include/s2/strings/ostringstream.h +5e58c81561186cabf746091253ece578 *inst/include/s2/strings/serialize.h +4774a2d59a247bb335bacbe5eb6eb265 *inst/include/s2/third_party/absl/algorithm/algorithm.h +c5c22df9e5ec5e04e0085325f30268b7 *inst/include/s2/third_party/absl/base/attributes.h +9055c69eff1250d7b55406f466283b45 *inst/include/s2/third_party/absl/base/casts.h +e153758883f2d0704a42a3791a7092bd *inst/include/s2/third_party/absl/base/config.h +5fced460e821fb0e729790dedee70978 *inst/include/s2/third_party/absl/base/dynamic_annotations.h +b942c8aeb47262065298f7c84599f8ae *inst/include/s2/third_party/absl/base/internal/atomic_hook.h +0132a8836d59bbc8ebbfd499bced5e57 *inst/include/s2/third_party/absl/base/internal/identity.h +cacec291159c34faf2124f02ae587149 *inst/include/s2/third_party/absl/base/internal/inline_variable.h +215c83c3b57dc7299452971b5751111b *inst/include/s2/third_party/absl/base/internal/invoke.h +a1544a26ecf4f13213df38bfa8ca2ec1 *inst/include/s2/third_party/absl/base/internal/raw_logging.h +95a923cf95eb8115790ea3aa39ba4544 *inst/include/s2/third_party/absl/base/internal/throw_delegate.h +2eb0626e04425567d155077fd975f454 *inst/include/s2/third_party/absl/base/internal/unaligned_access.h +c3dcb836940be43411fd4c4890bc649e *inst/include/s2/third_party/absl/base/log_severity.h +ae09d12a126842ad529872c06130bd23 *inst/include/s2/third_party/absl/base/macros.h +2de40013561322141fd61eb7f8e7939f *inst/include/s2/third_party/absl/base/optimization.h +a62aa9b3fa1d55d8e959929984065260 *inst/include/s2/third_party/absl/base/policy_checks.h +4ec15da9fbdf62615835e19927c81c9d *inst/include/s2/third_party/absl/base/port.h +c1eef28bafed8aea62144ee837a81c7d *inst/include/s2/third_party/absl/base/thread_annotations.h +ba3327fbea8fed70386f15a0e898a3fa *inst/include/s2/third_party/absl/container/fixed_array.h +e12a61a76d8ed6380d17d3c6293dfb5d *inst/include/s2/third_party/absl/container/inlined_vector.h +4fa2041669639f60524e2191a528b058 *inst/include/s2/third_party/absl/container/internal/compressed_tuple.h +720e6131f877ba385b4174e4e3ca5a95 *inst/include/s2/third_party/absl/container/internal/container_memory.h +c8d7b706e67864565bc035dfe6735d2c *inst/include/s2/third_party/absl/container/internal/layout.h +c3e6cf276acd1ac325e8d5f5134511c5 *inst/include/s2/third_party/absl/memory/memory.h +cf8a2d3e5aea9659c5cda43962a5bda9 *inst/include/s2/third_party/absl/meta/type_traits.h +a429f3aaa1ed1c847ea9c9ae7530ec0e *inst/include/s2/third_party/absl/numeric/int128.h +95dc4b1604f6514051107ef12c4084cd *inst/include/s2/third_party/absl/numeric/int128_have_intrinsic.inc +0af74de0605ecb52abf0a99f6c4f375c *inst/include/s2/third_party/absl/numeric/int128_no_intrinsic.inc +90d00026063ac43ffbc93066ff6df857 *inst/include/s2/third_party/absl/strings/ascii.h +5261e3adf5c680466c5c8099fb515cec *inst/include/s2/third_party/absl/strings/ascii_ctype.h +a3c35c225cf76eaabcee8b7f24f72a7c *inst/include/s2/third_party/absl/strings/internal/bits.h +e8699c817825346c1d849332fd3ccdb2 *inst/include/s2/third_party/absl/strings/internal/memutil.h +c2f0428da28989afc38bc590bbacb2ef *inst/include/s2/third_party/absl/strings/internal/resize_uninitialized.h +14f2714ff679c7ef2777b7021290c7af *inst/include/s2/third_party/absl/strings/match.h +4f3281e25b414a4b244835e228173c36 *inst/include/s2/third_party/absl/strings/numbers.h +408b2c9f4079600fd721ceb4ad1a4403 *inst/include/s2/third_party/absl/strings/str_cat.h +20d77d9f1d45146178390b640f963653 *inst/include/s2/third_party/absl/strings/str_join.h +af303dc872a7afff5c4b7d9213609ecc *inst/include/s2/third_party/absl/strings/str_split.h +58505d1fe7175e0ec054c0bf8c9e7417 *inst/include/s2/third_party/absl/strings/string_view.h +27833d1d0b3809492956eaab1f767c08 *inst/include/s2/third_party/absl/strings/strip.h +ff057e84f9c7d95441204c08a99b6624 *inst/include/s2/third_party/absl/types/span.h +4b112f693a7fe580bfdcc0294bdaa16d *inst/include/s2/third_party/absl/utility/utility.h +3bfcb45aa9bfa89827d30afba984f79c *inst/include/s2/util/bits/bit-interleave.h +01651f0b0520524dbb4584decd493d3b *inst/include/s2/util/bits/bits.h +5f6a88e3427ce3578a61423ab34608d5 *inst/include/s2/util/coding/coder.h +c4fe67fd8a294e03bfc46ce9fb1134d1 *inst/include/s2/util/coding/nth-derivative.h +2e711fab16d3ec015ecc12d522b8b0ae *inst/include/s2/util/coding/transforms.h +c5e3e09198b1b08e701f3776454f01dd *inst/include/s2/util/coding/varint.h +d02fbefd15c14ec0bd673853a526bfc8 *inst/include/s2/util/endian/endian.h +0d177612b828c957b49d097e94a28b32 *inst/include/s2/util/gtl/btree.h +fb878c4cc124f8cd73ff043b9fe9e278 *inst/include/s2/util/gtl/btree_container.h +52e6fdc3fa81363e043d7e5a35a697ef *inst/include/s2/util/gtl/btree_map.h +81e2de007193adce24cd9d5f5739bf5e *inst/include/s2/util/gtl/btree_set.h +29d3a30325dc78051b3aa1b812fb71c7 *inst/include/s2/util/gtl/compact_array.h +a05aa6118ec536e2715ce0089096d564 *inst/include/s2/util/gtl/container_logging.h +524152483057734236c014aaf39cabe3 *inst/include/s2/util/gtl/dense_hash_set.h +e6d5083ac705a0300e77fe096a7a0589 *inst/include/s2/util/gtl/densehashtable.h +fa56660fc40be26b3dbfc7115c56674e *inst/include/s2/util/gtl/hashtable_common.h +a899a9f61f6eff376a230da0cf6ed16a *inst/include/s2/util/gtl/layout.h +f1dc5e28aa905fbf4af9530f7cf14111 *inst/include/s2/util/gtl/legacy_random_shuffle.h +a36fbccb7e3c064e5a16b4ac1e7842f3 *inst/include/s2/util/hash/mix.h +b0a3631c4d2beb9b19d9e3a0639c0cf0 *inst/include/s2/util/math/exactfloat/exactfloat.h +0c3be461d027e0cccb9c0facb12cd046 *inst/include/s2/util/math/mathutil.h +160830f8d1d1983f1b773f953abff72d *inst/include/s2/util/math/matrix3x3.h +54ea617707ee52e966ce4e475067ec28 *inst/include/s2/util/math/vector.h +5b091051cad1329e1e2b535e40e9c142 *inst/include/s2/util/math/vector3_hash.h +1bdf37d9bef7eba2b40d5baad2403f8d *inst/include/s2/util/units/length-units.h +a5add3e948f775d2f77331e97b5398ea *inst/include/s2/util/units/physical-units.h +400af05643ac08d897c2100a0a68f1aa *inst/include/s2/value_lexicon.h +3c3a7db94dc9033c9c0c6ef7d6d0c8a4 *man/as_s2_geography.Rd +ed01c59b91c1a9c3382d1f2ab67e60cc *man/figures/rc300.png +0d32af4cce70305a4ad68adb26e7c89f *man/s2-package.Rd +0814caa3efd1d23d1a02cd3da15d3424 *man/s2_boundary.Rd +a906d7f9956940ceebe0bd879c14e5f2 *man/s2_bounds_cap.Rd +74e5a897578b3e75ccafbd46bcd3e2fa *man/s2_closest_feature.Rd +d96650f409fa8eb02fe4d5f54d00eb7d *man/s2_contains.Rd +addd614791ada510339317f68202d348 *man/s2_data_tbl_countries.Rd +d7a1ee72a69458a577921e02c2f4142f *man/s2_earth_radius_meters.Rd +b1c82724ddfd755007942e7763d7b333 *man/s2_geog_point.Rd +022dbf15f8934cc4accf0c41a55a4a44 *man/s2_is_collection.Rd +e1a47506a896f0144af3c34265fef2b1 *man/s2_lnglat.Rd +22b3d3bc9001655c786bd6515fe874d2 *man/s2_options.Rd +a86f2581e1af6031167a29956341993c *man/s2_point.Rd +7928b4cf96caa1ee6349d575e7d5899f *src/Makevars.in +c7a379bba69903eac2145a7b5e0b7be1 *src/Makevars.win +294dd54a661879e3713ca07fff5dc459 *src/RcppExports.cpp +5f46d29bc94bcdfc28e2a4df442310de *src/cpp-compat.cpp +fdce8bafee6053b81f1b8c8039b41493 *src/geography-collection.h +8f89404ccd6210e21b6b5b29867597ab *src/geography-operator.h +a7705e137e495ff2a4e380556763c555 *src/geography.h +0365ec85c22784d83cce5b84d71d794a *src/init.cpp +e2fbee45aca698d82ddac432ada8b39a *src/point-geography.h +82b010ba5876d13494164c57f7125a03 *src/polygon-geography.h +025a71734811c66df0767952306ff45b *src/polyline-geography.h +5ad00dddb331ad9ab3528b330c3e9a05 *src/s2-accessors.cpp +40989d3a6148de2a42117013f098d21c *src/s2-bounds.cpp +fb1fff2046ade79c266428478bceb49b *src/s2-constructors-formatters.cpp +bc596d9aaa251dd5884d1537b0630fc6 *src/s2-geography.cpp +2a18a33f6a16832c371549cee3ef1829 *src/s2-lnglat.cpp +27938e376941d899bf9052fcfe51e366 *src/s2-matrix.cpp +3faf84f0e15b9e9807e2c13d7a81a031 *src/s2-options.h +26da724d2fb9ccb3b7579e9a7f49001e *src/s2-point.cpp +980e286c3b8ad05def8cb5729e83a98f *src/s2-predicates.cpp +e66dde63f2c8c100f82291f25712bc9e *src/s2-transformers.cpp +2a3ac9aacf9ff1513a19f70b91902c83 *src/s2-xptr.cpp +5870223619be64e10cd385cd100ba1e0 *src/s2/base/stringprintf.cc +c10c3c7ca58239c0915bacffc70e37e1 *src/s2/base/strtoint.cc +a85315a801220cea6aedb7545bed0675 *src/s2/encoded_s2cell_id_vector.cc +46fc24762b4540e4fca69d7ae755de0c *src/s2/encoded_s2point_vector.cc +ef7ca1699c3646abc615e0fdf400f10d *src/s2/encoded_s2shape_index.cc +2a2a38594d08c617c2b2a277e278e320 *src/s2/encoded_string_vector.cc +788f48e5bbfca22ed1e396d4bd675ad3 *src/s2/id_set_lexicon.cc +e77194370c1950bc155ee61708af57b0 *src/s2/mutable_s2shape_index.cc +da84b9bfc2aeabd4f8ae0f84343623e7 *src/s2/r2rect.cc +e7ca7f23a7e8fe6f707e4169f1aadb77 *src/s2/s1angle.cc +d9387a9af7477f6b82ec73d206cfd8d9 *src/s2/s1chord_angle.cc +34874873bee5edbf9af38cd7692ab3ae *src/s2/s1interval.cc +511d35103ca70d436f998e8a02aac9c5 *src/s2/s2boolean_operation.cc +451752fc8769396106cc1a751942673a *src/s2/s2builder.cc +13695f3a4b75e18becb5d3828e81da65 *src/s2/s2builder_graph.cc +840316e7a7b185fc36824c2fefdce8d3 *src/s2/s2builderutil_closed_set_normalizer.cc +af468682dc5753112e46f2ace3393cb7 *src/s2/s2builderutil_find_polygon_degeneracies.cc +93c788e6753481cf0ac93247a1349b62 *src/s2/s2builderutil_lax_polygon_layer.cc +6c0c3e9f73a2d693d25392a6f053cca9 *src/s2/s2builderutil_s2point_vector_layer.cc +d38e7886a72b3ddceec6fe97cbe020b0 *src/s2/s2builderutil_s2polygon_layer.cc +bc24648fc1ba139430b08df68cc45004 *src/s2/s2builderutil_s2polyline_layer.cc +9ed299b94f9fd05d3b228cbbb77a29e9 *src/s2/s2builderutil_s2polyline_vector_layer.cc +0db1a581191eee0bdbbb3f889994453d *src/s2/s2builderutil_snap_functions.cc +0e7eb31b407e02950751a990c528833d *src/s2/s2builderutil_testing.cc +15661d854ecdbd9b5adb37f4fa38e7fd *src/s2/s2cap.cc +d89508276ed169153d1e80d85989bfd5 *src/s2/s2cell.cc +593d094df40de93980e92961647b8660 *src/s2/s2cell_id.cc +b77c66885dd9ce9decb058448a53bf2b *src/s2/s2cell_index.cc +2df8fa84fabd7e103ecbcf3dddec4ab7 *src/s2/s2cell_union.cc +cf1a07e72197832f2b1302f7b3630f98 *src/s2/s2centroids.cc +247c9902f0b40b738f8636d6436fbdb6 *src/s2/s2closest_cell_query.cc +bed4f6b983774f4c6b4a6596ad46ee08 *src/s2/s2closest_edge_query.cc +09e2f693ad6e491a4d50eb8b5d966840 *src/s2/s2closest_point_query.cc +8cea937d5d1881cdb71d1287f5474cff *src/s2/s2contains_vertex_query.cc +6122ec2910ab45960634d81f2058a9e0 *src/s2/s2convex_hull_query.cc +effdbbf64bb7f235381201657e021ea6 *src/s2/s2coords.cc +48655b45fa96f0da0b234ca68fe4b54d *src/s2/s2crossing_edge_query.cc +cbeeb65a7261dc8430dcf6edc24deb94 *src/s2/s2debug.cc +a863fc668a334819762480cc0646c898 *src/s2/s2earth.cc +d58b020152efbe99f672b8952460d2ce *src/s2/s2edge_clipping.cc +c5fd68f5fab9c30285a9d1995be3ddde *src/s2/s2edge_crosser.cc +69bb6ff8072fe88be08b2e693071ea65 *src/s2/s2edge_crossings.cc +2afc2b3b5d5c5eee69d662c490aeac3a *src/s2/s2edge_distances.cc +75acbcdde0e718a3a990fa63c78c3423 *src/s2/s2edge_tessellator.cc +483c797be9c9eb4b9a9059577e548e7b *src/s2/s2error.cc +fe0c39101c8c58f45b75fc4c36261a82 *src/s2/s2furthest_edge_query.cc +8f0404619c8c69989e81a9eafb8abe68 *src/s2/s2latlng.cc +e42ec23a4fdf8b6aea983db2ca3cc4d9 *src/s2/s2latlng_rect.cc +f8689b6a3eef9053601d310ca6146f5c *src/s2/s2latlng_rect_bounder.cc +6ea59dae602c6e87765068c7c4357baf *src/s2/s2lax_loop_shape.cc +fc760f9c0ff8545929b4a1e0da1e6ff5 *src/s2/s2lax_polygon_shape.cc +56c7b3c6c942d0cffe353b03ee605a8f *src/s2/s2lax_polyline_shape.cc +e9089ad010d3996decf00ad060e2c9b1 *src/s2/s2loop.cc +436911cd7d5a6dcc5fd8b5415c6e0603 *src/s2/s2loop_measures.cc +ca46f698320fb52b1a5a963c3dda9584 *src/s2/s2max_distance_targets.cc +66d7e4a5a3700d4b5a8c7e013fcfc370 *src/s2/s2measures.cc +b70ef8989674552ca3d0e0c9fcb754a8 *src/s2/s2metrics.cc +03adc6de0eef0e8578423e8fadd5542f *src/s2/s2min_distance_targets.cc +94bd23bc709062647f0bdc993dfc7933 *src/s2/s2padded_cell.cc +ebcad838fe84cee7d007143e3cbac687 *src/s2/s2point_compression.cc +7b88d7113b91cd8d5f0c1ad5bbacc7c4 *src/s2/s2point_region.cc +0475a05e50a35ca03b06e084178cd31a *src/s2/s2pointutil.cc +eacacbc76f611ff5f249666746bb414a *src/s2/s2polygon.cc +4a91c95c87427a46f759ca2f469f38b5 *src/s2/s2polyline.cc +52d66d8e55dab56d35a33029e25182ce *src/s2/s2polyline_alignment.cc +c206c2fa52e4a5588b1024f45c63595b *src/s2/s2polyline_measures.cc +eab00b14b7643e320f8044521eaa16e4 *src/s2/s2polyline_simplifier.cc +ff7549a0d6195b34b24867fff4cc6d06 *src/s2/s2predicates.cc +7478e1714d9dcaf520159458d10e383b *src/s2/s2projections.cc +8baa87c45aa11148a5207f367237e753 *src/s2/s2r2rect.cc +57778c7c84ae75e67ed68e76f9026ec0 *src/s2/s2region.cc +cfc92b8d78c68ae15377d9e1c5a8df13 *src/s2/s2region_coverer.cc +f622c31665d97c607e1cb5106c295959 *src/s2/s2region_intersection.cc +b41c960fdbefe867c01ee524e3929d3f *src/s2/s2region_term_indexer.cc +f8232c70a590ed11666c93181c9d7343 *src/s2/s2region_union.cc +25ac091ec0963d3216b86e86a050eae9 *src/s2/s2shape_index.cc +906e1e16bc8aa9f5d4e99651a6cdf0fe *src/s2/s2shape_index_buffered_region.cc +c0543285953743a9605aa3ef63ad6127 *src/s2/s2shape_index_measures.cc +23c94c388142e68594cbf29b677d2bbc *src/s2/s2shape_measures.cc +a71d6a318728f4f4d425a283fe298eb2 *src/s2/s2shapeutil_build_polygon_boundaries.cc +dbe0790cef156dde776484cacc699485 *src/s2/s2shapeutil_coding.cc +afd2720bf07ba0aa5f94dfe9ba0c4d3a *src/s2/s2shapeutil_contains_brute_force.cc +5c5fc095963d0f42ddbb64977d5d267f *src/s2/s2shapeutil_edge_iterator.cc +ae2b2addb5cb94cd24c57c195e1b0b49 *src/s2/s2shapeutil_get_reference_point.cc +6d1353bb32206db05048ee50a073c0e7 *src/s2/s2shapeutil_range_iterator.cc +2d43581ce7e9638ee9d4390016d422df *src/s2/s2shapeutil_visit_crossing_edge_pairs.cc +969daf687171d174fbe727b6727f65bb *src/s2/s2testing.cc +40f4712317f6b98e06a78be740f60409 *src/s2/s2text_format.cc +518f25738bd53f6439d3708f56a85435 *src/s2/s2wedge_relations.cc +9b6952846f960ce7c8f86d425a6cfc53 *src/s2/strings/ostringstream.cc +eca9409a0404788acbef02f2fd4f51df *src/s2/strings/serialize.cc +ed362e76c37bc3b132df67914ff1ae63 *src/s2/third_party/absl/base/dynamic_annotations.cc +1bf91ab480aa123b927f4b3873f3cb82 *src/s2/third_party/absl/base/internal/raw_logging.cc +c894340e8006bfa71425539409d16115 *src/s2/third_party/absl/base/internal/throw_delegate.cc +238a3a6f859b344e23ebf1e8215d2824 *src/s2/third_party/absl/numeric/int128.cc +d87d99e4a3010c3ada6f90dcd0f9610f *src/s2/third_party/absl/strings/ascii.cc +77bb965ed3aca0c14e26b379046729ac *src/s2/third_party/absl/strings/internal/memutil.cc +918e0ac8d6b33177ac95bb9cbddb8ffe *src/s2/third_party/absl/strings/match.cc +07e47a54105f2a19e1955ecbc10f8ba3 *src/s2/third_party/absl/strings/numbers.cc +b2ded99aff2adda60c7936c80e01580e *src/s2/third_party/absl/strings/str_cat.cc +b022ff16e439872f0bb3a608194481c4 *src/s2/third_party/absl/strings/str_split.cc +4877473e8e04e7f4c25a0c7dda833641 *src/s2/third_party/absl/strings/string_view.cc +04a1c7524bf937976acbdd7a63a298b9 *src/s2/third_party/absl/strings/strip.cc +c90e431dac905cc9e596e1b1071b7ea1 *src/s2/util/bits/bit-interleave.cc +7fce87b2c7658b46bea4fd2096c902fb *src/s2/util/bits/bits.cc +c0d70edbf2ac3f698c17d89959e326f9 *src/s2/util/coding/coder.cc +b1348c0fb971b3302725b248a6299581 *src/s2/util/coding/varint.cc +2fc5ca8d2efaf82b72d5120a102e1aa0 *src/s2/util/math/exactfloat/exactfloat.cc +2037118f434c6f41bc8014de2eb18c92 *src/s2/util/math/mathutil.cc +2aba55dd12417d944cd91a2f02b1d946 *src/s2/util/units/length-units.cc +4495750656420f7cd041bf0bf27ec020 *src/tests/soname.h +d105b4c3460bf21c7c7ef886eaaa29d2 *src/wk-geography.h +a9ffa55303a6dcba9c5c9c9d322e2009 *tests/area.R +960ec65d25d1018d2084d81ed3324fdd *tests/area.Rout +6a0d9b8264490c375aab2554285f41ec *tests/testthat.R +2852576d235eb67783d7b1118d8d9800 *tests/testthat/test-data.R +5515aee1bf7a1360c0f1de84a6b58eeb *tests/testthat/test-s2-accessors.R +1047ccf796c487587c03286eb8ad3eb2 *tests/testthat/test-s2-bounds.R +cfb96b2f1b55571dd58adcbeb254950f *tests/testthat/test-s2-constructors-formatters.R +75ad7009cf6227ca8239c31198787956 *tests/testthat/test-s2-earth.R +7a6af524d3f7aadc667753dd5eea7548 *tests/testthat/test-s2-geography.R +adfd5e8008ceeea58ba3ee5e71ca040b *tests/testthat/test-s2-lnglat.R +7b587e0c348fd70e7f1308e33f48aac9 *tests/testthat/test-s2-matrix.R +589616aabb1c291a3984f521274815bf *tests/testthat/test-s2-options.R +9be8d6955daa3c211af562f4f41c639a *tests/testthat/test-s2-point.R +0688d88be5a5f999507d227ee30c99dc *tests/testthat/test-s2-predicates.R +b7505b66cb1b1b254137ebd00c86c225 *tests/testthat/test-s2-transformers.R +e0de43269c8f8eb622ef3aa6c6d6f01a *tests/testthat/test-s2-xptr.R +28f67bf7ece751b0003f0cb923fefa24 *tests/testthat/test-utils.R +960c0d7e6a792c605d49ccfe2ac6a968 *tests/testthat/test-vctrs.R +0ec27c181a66ce5b72bc8c3940e9e00e *tools/version.c +c649ea1343c76b81870be0fa55f6feb3 *tools/winlibs.R diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 0000000..19a5329 --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,123 @@ +# Generated by roxygen2: do not edit by hand + +S3method("[",s2_xptr) +S3method("[<-",s2_geography) +S3method("[<-",s2_lnglat) +S3method("[<-",s2_point) +S3method("[[",s2_xptr) +S3method("[[<-",s2_geography) +S3method("[[<-",s2_lnglat) +S3method("[[<-",s2_point) +S3method(as.character,s2_geography) +S3method(as.data.frame,s2_lnglat) +S3method(as.data.frame,s2_point) +S3method(as.matrix,s2_lnglat) +S3method(as.matrix,s2_point) +S3method(as_s2_geography,WKB) +S3method(as_s2_geography,blob) +S3method(as_s2_geography,character) +S3method(as_s2_geography,default) +S3method(as_s2_geography,logical) +S3method(as_s2_geography,s2_geography) +S3method(as_s2_geography,s2_lnglat) +S3method(as_s2_geography,s2_point) +S3method(as_s2_geography,wk_wkb) +S3method(as_s2_geography,wk_wkt) +S3method(as_s2_lnglat,character) +S3method(as_s2_lnglat,matrix) +S3method(as_s2_lnglat,s2_geography) +S3method(as_s2_lnglat,s2_lnglat) +S3method(as_s2_lnglat,s2_point) +S3method(as_s2_lnglat,wk_wkb) +S3method(as_s2_lnglat,wk_wkt) +S3method(as_s2_point,matrix) +S3method(as_s2_point,s2_geography) +S3method(as_s2_point,s2_lnglat) +S3method(as_s2_point,s2_point) +S3method(as_wkb,s2_geography) +S3method(as_wkb,s2_lnglat) +S3method(as_wkt,s2_geography) +S3method(as_wkt,s2_lnglat) +S3method(c,s2_xptr) +S3method(format,s2_geography) +S3method(format,s2_lnglat) +S3method(format,s2_point) +S3method(print,s2_xptr) +S3method(rep,s2_xptr) +S3method(rep_len,s2_xptr) +export(as_s2_geography) +export(as_s2_lnglat) +export(as_s2_point) +export(s2_area) +export(s2_as_binary) +export(s2_as_text) +export(s2_boundary) +export(s2_bounds_cap) +export(s2_bounds_rect) +export(s2_buffer_cells) +export(s2_centroid) +export(s2_centroid_agg) +export(s2_closest_feature) +export(s2_closest_point) +export(s2_contains) +export(s2_contains_matrix) +export(s2_covered_by) +export(s2_covered_by_matrix) +export(s2_covers) +export(s2_covers_matrix) +export(s2_data_cities) +export(s2_data_countries) +export(s2_data_timezones) +export(s2_difference) +export(s2_dimension) +export(s2_disjoint) +export(s2_disjoint_matrix) +export(s2_distance) +export(s2_distance_matrix) +export(s2_dwithin) +export(s2_dwithin_matrix) +export(s2_earth_radius_meters) +export(s2_equals) +export(s2_equals_matrix) +export(s2_farthest_feature) +export(s2_geog_from_text) +export(s2_geog_from_wkb) +export(s2_geog_point) +export(s2_intersection) +export(s2_intersects) +export(s2_intersects_box) +export(s2_intersects_matrix) +export(s2_is_collection) +export(s2_is_empty) +export(s2_length) +export(s2_lnglat) +export(s2_make_line) +export(s2_make_polygon) +export(s2_max_distance) +export(s2_max_distance_matrix) +export(s2_may_intersect_matrix) +export(s2_minimum_clearance_line_between) +export(s2_num_points) +export(s2_options) +export(s2_perimeter) +export(s2_point) +export(s2_rebuild) +export(s2_simplify) +export(s2_snap_distance) +export(s2_snap_identity) +export(s2_snap_level) +export(s2_snap_precision) +export(s2_snap_to_grid) +export(s2_sym_difference) +export(s2_touches) +export(s2_touches_matrix) +export(s2_union) +export(s2_union_agg) +export(s2_within) +export(s2_within_matrix) +export(s2_x) +export(s2_y) +importFrom(Rcpp,sourceCpp) +importFrom(wk,as_wkb) +importFrom(wk,as_wkt) +useDynLib(s2, .registration = TRUE) diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..8f3f650 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,31 @@ +# s2 1.0.4 + +* Fixed errors that resulted from compilation on clang 12.2 (#88, #89). + +# s2 1.0.3 + +* Fixed CRAN check errors (#80). + +# s2 1.0.2 + +* Fixed CRAN check errors (#71, #75, #72). + +# s2 1.0.1 + +* Added layer creation options to `s2_options()`, which now uses strings + rather than numeric codes to specify boolean operation options, geography + construction options, and builder options (#70). +* Added `s2_rebuild()` and `s2_simplify()`, which wrap the S2 C++ `S2Builder` + class to provide simplification and fixing of invalid geographies (#70). +* The s2 package now builds and passes the CMD check on Solaris (#66, #67). +* Renamed `s2_latlng()` to `s2_lnglat()` to keep axis order consistent + throughout the package (#69). +* Added `s2_bounds_cap()` and `s2_bounds_rect()` to compute bounding areas + using geographic coordinates (@edzer, #63). +* `s2_*_matrix()` predicates now efficiently use indexing to compute the + results of many predicate comparisons (#61). + +# s2 1.0.0 + +This version is a complete rewrite of the former s2 CRAN package, entirely +backwards incompatible with previous versions. diff --git a/R/RcppExports.R b/R/RcppExports.R new file mode 100644 index 0000000..88a62b8 --- /dev/null +++ b/R/RcppExports.R @@ -0,0 +1,263 @@ +# Generated by using Rcpp::compileAttributes() -> do not edit by hand +# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +cpp_s2_init <- function() { + invisible(.Call(`_s2_cpp_s2_init`)) +} + +cpp_s2_is_collection <- function(geog) { + .Call(`_s2_cpp_s2_is_collection`, geog) +} + +cpp_s2_dimension <- function(geog) { + .Call(`_s2_cpp_s2_dimension`, geog) +} + +cpp_s2_num_points <- function(geog) { + .Call(`_s2_cpp_s2_num_points`, geog) +} + +cpp_s2_is_empty <- function(geog) { + .Call(`_s2_cpp_s2_is_empty`, geog) +} + +cpp_s2_area <- function(geog) { + .Call(`_s2_cpp_s2_area`, geog) +} + +cpp_s2_length <- function(geog) { + .Call(`_s2_cpp_s2_length`, geog) +} + +cpp_s2_perimeter <- function(geog) { + .Call(`_s2_cpp_s2_perimeter`, geog) +} + +cpp_s2_x <- function(geog) { + .Call(`_s2_cpp_s2_x`, geog) +} + +cpp_s2_y <- function(geog) { + .Call(`_s2_cpp_s2_y`, geog) +} + +cpp_s2_distance <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_distance`, geog1, geog2) +} + +cpp_s2_max_distance <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_max_distance`, geog1, geog2) +} + +cpp_s2_bounds_cap <- function(geog) { + .Call(`_s2_cpp_s2_bounds_cap`, geog) +} + +cpp_s2_bounds_rect <- function(geog) { + .Call(`_s2_cpp_s2_bounds_rect`, geog) +} + +cpp_s2_geog_point <- function(x, y) { + .Call(`_s2_cpp_s2_geog_point`, x, y) +} + +cpp_s2_make_line <- function(x, y, featureId) { + .Call(`_s2_cpp_s2_make_line`, x, y, featureId) +} + +cpp_s2_make_polygon <- function(x, y, featureId, ringId, oriented, check) { + .Call(`_s2_cpp_s2_make_polygon`, x, y, featureId, ringId, oriented, check) +} + +s2_geography_from_wkb <- function(wkb, oriented, check) { + .Call(`_s2_s2_geography_from_wkb`, wkb, oriented, check) +} + +s2_geography_from_wkt <- function(wkt, oriented, check) { + .Call(`_s2_s2_geography_from_wkt`, wkt, oriented, check) +} + +s2_geography_full <- function(x) { + .Call(`_s2_s2_geography_full`, x) +} + +s2_geography_to_wkt <- function(s2_geography, precision, trim) { + .Call(`_s2_s2_geography_to_wkt`, s2_geography, precision, trim) +} + +s2_geography_to_wkb <- function(s2_geography, endian) { + .Call(`_s2_s2_geography_to_wkb`, s2_geography, endian) +} + +s2_geography_format <- function(s2_geography, maxCoords, precision, trim) { + .Call(`_s2_s2_geography_format`, s2_geography, maxCoords, precision, trim) +} + +s2_lnglat_from_numeric <- function(lng, lat) { + .Call(`_s2_s2_lnglat_from_numeric`, lng, lat) +} + +s2_lnglat_from_s2_point <- function(s2_point) { + .Call(`_s2_s2_lnglat_from_s2_point`, s2_point) +} + +data_frame_from_s2_lnglat <- function(xptr) { + .Call(`_s2_data_frame_from_s2_lnglat`, xptr) +} + +cpp_s2_closest_feature <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_closest_feature`, geog1, geog2) +} + +cpp_s2_farthest_feature <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_farthest_feature`, geog1, geog2) +} + +cpp_s2_may_intersect_matrix <- function(geog1, geog2, maxEdgesPerCell, maxFeatureCells, s2options) { + .Call(`_s2_cpp_s2_may_intersect_matrix`, geog1, geog2, maxEdgesPerCell, maxFeatureCells, s2options) +} + +cpp_s2_contains_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_contains_matrix`, geog1, geog2, s2options) +} + +cpp_s2_within_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_within_matrix`, geog1, geog2, s2options) +} + +cpp_s2_intersects_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_intersects_matrix`, geog1, geog2, s2options) +} + +cpp_s2_equals_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_equals_matrix`, geog1, geog2, s2options) +} + +cpp_s2_touches_matrix <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_touches_matrix`, geog1, geog2, s2options) +} + +cpp_s2_dwithin_matrix <- function(geog1, geog2, distance) { + .Call(`_s2_cpp_s2_dwithin_matrix`, geog1, geog2, distance) +} + +cpp_s2_distance_matrix <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_distance_matrix`, geog1, geog2) +} + +cpp_s2_max_distance_matrix <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_max_distance_matrix`, geog1, geog2) +} + +cpp_s2_contains_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_contains_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_within_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_within_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_intersects_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_intersects_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_disjoint_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_disjoint_matrix_brute_force`, geog1, geog2, s2options) +} + +cpp_s2_equals_matrix_brute_force <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_equals_matrix_brute_force`, geog1, geog2, s2options) +} + +s2_point_from_numeric <- function(x, y, z) { + .Call(`_s2_s2_point_from_numeric`, x, y, z) +} + +s2_point_from_s2_lnglat <- function(s2_lnglat) { + .Call(`_s2_s2_point_from_s2_lnglat`, s2_lnglat) +} + +data_frame_from_s2_point <- function(s2_point) { + .Call(`_s2_data_frame_from_s2_point`, s2_point) +} + +cpp_s2_intersects <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_intersects`, geog1, geog2, s2options) +} + +cpp_s2_equals <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_equals`, geog1, geog2, s2options) +} + +cpp_s2_contains <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_contains`, geog1, geog2, s2options) +} + +cpp_s2_touches <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_touches`, geog1, geog2, s2options) +} + +cpp_s2_dwithin <- function(geog1, geog2, distance) { + .Call(`_s2_cpp_s2_dwithin`, geog1, geog2, distance) +} + +cpp_s2_intersects_box <- function(geog, lng1, lat1, lng2, lat2, detail, s2options) { + .Call(`_s2_cpp_s2_intersects_box`, geog, lng1, lat1, lng2, lat2, detail, s2options) +} + +cpp_s2_intersection <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_intersection`, geog1, geog2, s2options) +} + +cpp_s2_union <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_union`, geog1, geog2, s2options) +} + +cpp_s2_difference <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_difference`, geog1, geog2, s2options) +} + +cpp_s2_sym_difference <- function(geog1, geog2, s2options) { + .Call(`_s2_cpp_s2_sym_difference`, geog1, geog2, s2options) +} + +cpp_s2_union_agg <- function(geog, s2options, naRm) { + .Call(`_s2_cpp_s2_union_agg`, geog, s2options, naRm) +} + +cpp_s2_centroid_agg <- function(geog, naRm) { + .Call(`_s2_cpp_s2_centroid_agg`, geog, naRm) +} + +cpp_s2_closest_point <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_closest_point`, geog1, geog2) +} + +cpp_s2_minimum_clearance_line_between <- function(geog1, geog2) { + .Call(`_s2_cpp_s2_minimum_clearance_line_between`, geog1, geog2) +} + +cpp_s2_centroid <- function(geog) { + .Call(`_s2_cpp_s2_centroid`, geog) +} + +cpp_s2_boundary <- function(geog) { + .Call(`_s2_cpp_s2_boundary`, geog) +} + +cpp_s2_rebuild <- function(geog, s2options) { + .Call(`_s2_cpp_s2_rebuild`, geog, s2options) +} + +cpp_s2_buffer_cells <- function(geog, distance, maxCells, minLevel) { + .Call(`_s2_cpp_s2_buffer_cells`, geog, distance, maxCells, minLevel) +} + +s2_xptr_test <- function(size) { + .Call(`_s2_s2_xptr_test`, size) +} + +s2_xptr_test_op <- function(s2_xptr_test) { + invisible(.Call(`_s2_s2_xptr_test_op`, s2_xptr_test)) +} + diff --git a/R/data.R b/R/data.R new file mode 100644 index 0000000..80afa35 --- /dev/null +++ b/R/data.R @@ -0,0 +1,72 @@ + +#' Low-resolution world boundaries, timezones, and cities +#' +#' Well-known binary versions of the [Natural Earth](https://www.naturalearthdata.com/) +#' low-resolution world boundaries and timezone boundaries. +#' +#' @param name The name of a country, continent, city, or `NULL` +#' for all features. +#' @param utc_offset_min,utc_offset_max Minimum and/or maximum timezone +#' offsets. +#' +#' @format A data.frame with columns `name` (character), and +#' `geometry` (wk_wkb) +#' @source [Natural Earth Data](https://www.naturalearthdata.com/) +#' @examples +#' head(s2_data_countries()) +#' s2_data_countries("Germany") +#' s2_data_countries("Europe") +#' +#' head(s2_data_timezones()) +#' s2_data_timezones(-4) +#' +#' head(s2_data_cities()) +#' s2_data_cities("Cairo") +#' +"s2_data_tbl_countries" + +#' @rdname s2_data_tbl_countries +"s2_data_tbl_timezones" + +#' @rdname s2_data_tbl_countries +"s2_data_tbl_cities" + +#' @rdname s2_data_tbl_countries +#' @export +s2_data_countries <- function(name = NULL) { + df <- s2::s2_data_tbl_countries + if (is.null(name)) { + wkb <- df$geometry + } else { + wkb <- structure(df$geometry[(df$name %in% name) | (df$continent %in% name)], class = "wk_wkb") + } + + as_s2_geography(wkb) +} + +#' @rdname s2_data_tbl_countries +#' @export +s2_data_timezones <- function(utc_offset_min = NULL, utc_offset_max = utc_offset_min) { + df <- s2::s2_data_tbl_timezones + if (is.null(utc_offset_min)) { + wkb <- df$geometry + } else { + matches <- (df$utc_offset >= utc_offset_min) & (df$utc_offset <= utc_offset_max) + wkb <- structure(df$geometry[matches], class = "wk_wkb") + } + + as_s2_geography(wkb) +} + +#' @rdname s2_data_tbl_countries +#' @export +s2_data_cities <- function(name = NULL) { + df <- s2::s2_data_tbl_cities + if (is.null(name)) { + wkb <- df$geometry + } else { + wkb <- structure(df$geometry[(df$name %in% name)], class = "wk_wkb") + } + + as_s2_geography(wkb) +} diff --git a/R/s2-accessors.R b/R/s2-accessors.R new file mode 100644 index 0000000..c987f7a --- /dev/null +++ b/R/s2-accessors.R @@ -0,0 +1,136 @@ + +#' S2 Geography Accessors +#' +#' Accessors extract information about [geography vectors][as_s2_geography]. +#' +#' @param x,y [geography vectors][as_s2_geography]. These inputs +#' are passed to [as_s2_geography()], so you can pass other objects +#' (e.g., character vectors of well-known text) directly. +#' @param radius Radius of the earth. Defaults to the average radius of +#' the earth in meters as defined by [s2_earth_radius_meters()]. +#' +#' @export +#' +#' @seealso +#' BigQuery's geography function reference: +#' +#' - [ST_ISCOLLECTION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_iscollection) +#' - [ST_DIMENSION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dimension) +#' - [ST_NUMPOINTS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_numpoints) +#' - [ST_ISEMPTY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_isempty) +#' - [ST_AREA](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_area) +#' - [ST_LENGTH](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_length) +#' - [ST_PERIMETER](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_perimeter) +#' - [ST_X](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_x) +#' - [ST_Y](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_y) +#' - [ST_DISTANCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_distance) +#' - [ST_MAXDISTANCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_maxdistance) +#' +#' @examples +#' # s2_is_collection() tests for multiple geometries in one feature +#' s2_is_collection(c("POINT (-64 45)", "MULTIPOINT ((-64 45), (8 72))")) +#' +#' # s2_dimension() returns 0 for point, 1 for line, 2 for polygon +#' s2_dimension( +#' c( +#' "GEOMETRYCOLLECTION EMPTY", +#' "POINT (-64 45)", +#' "LINESTRING (-64 45, 8 72)", +#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", +#' "GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 8 72))" +#' ) +#' ) +#' +#' # s2_num_points() counts points +#' s2_num_points(c("POINT (-64 45)", "LINESTRING (-64 45, 8 72)")) +#' +#' # s2_is_empty tests for emptiness +#' s2_is_empty(c("POINT (-64 45)", "POINT EMPTY")) +#' +#' # calculate area, length, and perimeter +#' s2_area("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") +#' s2_perimeter("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") +#' s2_length(s2_boundary("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")) +#' +#' # extract x and y coordinates from points +#' s2_x(c("POINT (-64 45)", "POINT EMPTY")) +#' s2_y(c("POINT (-64 45)", "POINT EMPTY")) +#' +#' # calculate minimum and maximum distance between two geometries +#' s2_distance( +#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", +#' "POINT (-64 45)" +#' ) +#' s2_max_distance( +#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", +#' "POINT (-64 45)" +#' ) +#' +s2_is_collection <- function(x) { + cpp_s2_is_collection(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_dimension <- function(x) { + cpp_s2_dimension(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_num_points <- function(x) { + cpp_s2_num_points(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_is_empty <- function(x) { + cpp_s2_is_empty(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_area <- function(x, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), radius) + cpp_s2_area(recycled[[1]]) * radius ^ 2 +} + +#' @rdname s2_is_collection +#' @export +s2_length <- function(x, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), radius) + cpp_s2_length(recycled[[1]]) * radius +} + +#' @rdname s2_is_collection +#' @export +s2_perimeter <- function(x, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), radius) + cpp_s2_perimeter(recycled[[1]]) * radius +} + +#' @rdname s2_is_collection +#' @export +s2_x <- function(x) { + cpp_s2_x(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_y <- function(x) { + cpp_s2_y(as_s2_geography(x)) +} + +#' @rdname s2_is_collection +#' @export +s2_distance <- function(x, y, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), radius) + cpp_s2_distance(recycled[[1]], recycled[[2]]) * radius +} + +#' @rdname s2_is_collection +#' @export +s2_max_distance <- function(x, y, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), radius) + cpp_s2_max_distance(recycled[[1]], recycled[[2]]) * radius +} diff --git a/R/s2-bounds.R b/R/s2-bounds.R new file mode 100644 index 0000000..6c4f792 --- /dev/null +++ b/R/s2-bounds.R @@ -0,0 +1,35 @@ + +#' Compute feature-wise and aggregate bounds +#' +#' [s2_bounds_rect()] returns a bounding latitude-longitude +#' rectangle that contains the region; [s2_bounds_cap()] returns a bounding circle +#' represented by a centre point (lat, lng) and an angle. The bound may not be tight +#' for points, polylines and geometry collections. The rectangle returned may depend on +#' the order of points or polylines. `lng_lo` values larger than `lng_hi` indicate +#' regions that span the antimeridian, see the Fiji example. +#' +#' @inheritParams s2_is_collection +#' @export +#' @return Both functions return a `data.frame`: +#' +#' - [s2_bounds_rect()]: Columns `minlng`, `minlat`, `maxlng`, `maxlat` (degrees) +#' - [s2_bounds_cap()]: Columns `lng`, `lat`, `angle` (degrees) +#' +#' @examples +#' s2_bounds_cap(s2_data_countries("Antarctica")) +#' s2_bounds_cap(s2_data_countries("Netherlands")) +#' s2_bounds_cap(s2_data_countries("Fiji")) +#' +#' s2_bounds_rect(s2_data_countries("Antarctica")) +#' s2_bounds_rect(s2_data_countries("Netherlands")) +#' s2_bounds_rect(s2_data_countries("Fiji")) +#' +s2_bounds_cap <- function(x) { + cpp_s2_bounds_cap(as_s2_geography(x)) +} + +#' @rdname s2_bounds_cap +#' @export +s2_bounds_rect <- function(x) { + cpp_s2_bounds_rect(as_s2_geography(x)) +} diff --git a/R/s2-constructors-formatters.R b/R/s2-constructors-formatters.R new file mode 100644 index 0000000..46c1496 --- /dev/null +++ b/R/s2-constructors-formatters.R @@ -0,0 +1,139 @@ + +#' Create and Format Geography Vectors +#' +#' These functions create and export [geography vectors][as_s2_geography]. +#' Unlike the BigQuery geography constructors, these functions do not sanitize +#' invalid or redundant input using [s2_union()]. Note that when creating polygons +#' using [s2_make_polygon()], rings can be open or closed. +#' +#' @inheritParams s2_is_collection +#' @inheritParams as_s2_geography +#' @param precision The number of significant digits to export when +#' writing well-known text. If `trim = FALSE`, the number of +#' digits after the decimal place. +#' @param trim Should trailing zeroes be included after the decimal place? +#' @param endian The endian-ness of the well-known binary. See [wk::wkb_translate_wkb()]. +#' @param longitude,latitude Vectors of latitude and longitude +#' @param wkt_string Well-known text +#' @param wkb_bytes A `list()` of `raw()` +#' @param feature_id,ring_id Vectors for which a change in +#' sequential values indicates a new feature or ring. Use [factor()] +#' to convert from a character vector. +#' +#' @export +#' +#' @seealso +#' See [as_s2_geography()] for other ways to construct geography vectors. +#' +#' BigQuery's geography function reference: +#' +#' - [ST_GEOGPOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogpoint) +#' - [ST_MAKELINE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makeline) +#' - [ST_MAKEPOLYGON](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makepolygon) +#' - [ST_GEOGFROMTEXT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromtext) +#' - [ST_GEOGFROMWKB](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromwkb) +#' - [ST_ASTEXT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_astext) +#' - [ST_ASBINARY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_asbinary) +#' +#' @examples +#' # create point geographies using coordinate values: +#' s2_geog_point(-64, 45) +#' +#' # create line geographies using coordinate values: +#' s2_make_line(c(-64, 8), c(45, 71)) +#' +#' # optionally, separate features using feature_id: +#' s2_make_line( +#' c(-64, 8, -27, -27), c(45, 71, 0, 45), +#' feature_id = c(1, 1, 2, 2) +#' ) +#' +#' # create polygon geographies using coordinate values: +#' # (rings can be open or closed) +#' s2_make_polygon(c(-45, 8, 0), c(64, 71, 90)) +#' +#' # optionally, separate rings and/or features using +#' # ring_id and/or feature_id +#' s2_make_polygon( +#' c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45), +#' c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30), +#' feature_id = c(rep(1, 8), rep(2, 3)), +#' ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1) +#' ) +#' +#' # import and export well-known text +#' (geog <- s2_geog_from_text("POINT (-64 45)")) +#' s2_as_text(geog) +#' +#' # import and export well-known binary +#' (geog <- s2_geog_from_wkb(wk::as_wkb("POINT (-64 45)"))) +#' s2_as_binary(geog) +#' +s2_geog_point <- function(longitude, latitude) { + recycled <- recycle_common(longitude, latitude) + new_s2_xptr(cpp_s2_geog_point(recycled[[1]], recycled[[2]]), "s2_geography") +} + +#' @rdname s2_geog_point +#' @export +s2_make_line <- function(longitude, latitude, feature_id = 1L) { + recycled <- recycle_common(longitude, latitude, feature_id) + new_s2_xptr(cpp_s2_make_line(recycled[[1]], recycled[[2]], featureId = recycled[[3]]), "s2_geography") +} + +#' @rdname s2_geog_point +#' @export +s2_make_polygon <- function(longitude, latitude, feature_id = 1L, ring_id = 1L, + oriented = FALSE, check = TRUE) { + recycled <- recycle_common(longitude, latitude, feature_id, ring_id) + new_s2_xptr( + cpp_s2_make_polygon( + recycled[[1]], recycled[[2]], + featureId = recycled[[3]], + ringId = recycled[[4]], + oriented = oriented, + check = check + ), + "s2_geography" + ) +} + +#' @rdname s2_geog_point +#' @export +s2_geog_from_text <- function(wkt_string, oriented = FALSE, check = TRUE) { + wk::validate_wk_wkt(wkt_string) + new_s2_xptr( + s2_geography_from_wkt( + wkt_string, + oriented = oriented, + check = check + ), + "s2_geography" + ) +} + +#' @rdname s2_geog_point +#' @export +s2_geog_from_wkb <- function(wkb_bytes, oriented = FALSE, check = TRUE) { + wk::validate_wk_wkb(wkb_bytes) + new_s2_xptr( + s2_geography_from_wkb( + wkb_bytes, + oriented = oriented, + check = check + ), + "s2_geography" + ) +} + +#' @rdname s2_geog_point +#' @export +s2_as_text <- function(x, precision = 16, trim = TRUE) { + s2_geography_to_wkt(as_s2_geography(x), precision = precision, trim = trim) +} + +#' @rdname s2_geog_point +#' @export +s2_as_binary <- function(x, endian = wk::wk_platform_endian()) { + structure(s2_geography_to_wkb(as_s2_geography(x), endian = endian), class = "blob") +} diff --git a/R/s2-earth.R b/R/s2-earth.R new file mode 100644 index 0000000..4814130 --- /dev/null +++ b/R/s2-earth.R @@ -0,0 +1,23 @@ + +#' Earth Constants +#' +#' According to Yoder (1995), the radius of the earth is +#' 6371.01 km. These functions are used to set the +#' default radis for functions that return a distance +#' or accept a distance as input +#' (e.g., [s2_distance()] and [s2_dwithin()]). +#' +#' @export +#' +#' @references +#' Yoder, C.F. 1995. "Astrometric and Geodetic Properties of Earth and the +#' Solar System" in Global Earth Physics, A Handbook of Physical Constants, +#' AGU Reference Shelf 1, American Geophysical Union, Table 2. +#' \doi{10.1029/RF001p0001} +#' +#' @examples +#' s2_earth_radius_meters() +#' +s2_earth_radius_meters <- function() { + 6371.01 * 1000 +} diff --git a/R/s2-geography.R b/R/s2-geography.R new file mode 100644 index 0000000..6f39d44 --- /dev/null +++ b/R/s2-geography.R @@ -0,0 +1,151 @@ + +#' Create an S2 Geography Vector +#' +#' Geography vectors are arrays of points, lines, polygons, and/or collections +#' of these. Geography vectors assume coordinates are longitude and latitude +#' on a perfect sphere. +#' +#' The coercion function [as_s2_geography()] is used to wrap the input +#' of most functions in the s2 package so that you can use other objects with +#' an unambiguious interpretation as a geography vector. Geography vectors +#' have a minimal [vctrs][vctrs::vctrs-package] implementation, so you can +#' use these objects in tibble, dplyr, and other packages that use the vctrs +#' framework. +#' +#' @param x An object that can be converted to an s2_geography vector +#' @param oriented TRUE if polygon ring directions are known to be correct +#' (i.e., exterior rings are defined counter clockwise and interior +#' rings are defined clockwise). +#' @param check Use `check = FALSE` to skip error on invalid geometries +#' @param ... Unused +#' +#' @return An object with class s2_geography +#' @export +#' +#' @seealso +#' [s2_geog_from_wkb()], [s2_geog_from_text()], [s2_geog_point()], +#' [s2_make_line()], [s2_make_polygon()] for other ways to +#' create geography vectors, and [s2_as_binary()] and [s2_as_text()] +#' for other ways to export them. +#' +as_s2_geography <- function(x, ...) { + UseMethod("as_s2_geography") +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.default <- function(x, ...) { + as_s2_geography(wk::as_wkb(x)) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.s2_geography <- function(x, ...) { + x +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.s2_lnglat <- function(x, ...) { + df <- data_frame_from_s2_lnglat(x) + new_s2_xptr(cpp_s2_geog_point(df[[1]], df[[2]]), "s2_geography") +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.s2_point <- function(x, ...) { + as_s2_geography(as_s2_lnglat(x)) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.wk_wkb <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkb(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.WKB <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkb(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.blob <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkb(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.wk_wkt <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkt(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.character <- function(x, ..., oriented = FALSE, check = TRUE) { + new_s2_xptr( + s2_geography_from_wkt(x, oriented = oriented, check = check), + "s2_geography" + ) +} + +#' @rdname as_s2_geography +#' @export +as_s2_geography.logical <- function(x, ...) { + stopifnot(isTRUE(x)) + new_s2_xptr(s2_geography_full(TRUE), "s2_geography") +} + +#' @importFrom wk as_wkb +#' @rdname as_s2_geography +#' @export +as_wkb.s2_geography <- function(x, ...) { + wk::new_wk_wkb(s2_geography_to_wkb(x, wk::wk_platform_endian())) +} + +#' @importFrom wk as_wkt +#' @rdname as_s2_geography +#' @export +as_wkt.s2_geography <- function(x, ...) { + wk::new_wk_wkt(s2_geography_to_wkt(x, precision = 16, trim = TRUE)) +} + + +#' @export +`[<-.s2_geography` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_geography(value) + new_s2_xptr(x, "s2_geography") +} + +#' @export +`[[<-.s2_geography` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_geography(value) + new_s2_xptr(x, "s2_geography") +} + +#' @export +format.s2_geography <- function(x, ..., max_coords = 5, precision = 9, trim = TRUE) { + paste0("<", s2_geography_format(x, max_coords, precision, trim), ">") +} + +# this is what gets called by the RStudio viewer, for which +# format() is best suited (s2_as_text() is more explicit for WKT output) +#' @export +as.character.s2_geography <- function(x, ..., max_coords = 5, precision = 9, trim = TRUE) { + format(x, ..., max_coords = max_coords, precision = precision, trim = trim) +} diff --git a/R/s2-lnglat.R b/R/s2-lnglat.R new file mode 100644 index 0000000..59ceaad --- /dev/null +++ b/R/s2-lnglat.R @@ -0,0 +1,113 @@ + +#' Create an S2 LngLat Vector +#' +#' This class represents a latitude and longitude on the Earth's surface. +#' Most calculations in S2 convert this to a [as_s2_point()], which is a +#' unit vector representation of this value. +#' +#' @param lat,lng Vectors of latitude and longitude values in degrees. +#' @param x A [s2_lnglat()] vector or an object that can be coerced to one. +#' @param ... Unused +#' +#' @return An object with class s2_lnglat +#' @export +#' +#' @examples +#' s2_lnglat(45, -64) # Halifax, Nova Scotia! +#' as.data.frame(s2_lnglat(45, -64)) +#' +s2_lnglat <- function(lng, lat) { + recycled <- recycle_common(as.double(lng), as.double(lat)) + new_s2_xptr(s2_lnglat_from_numeric(recycled[[1]], recycled[[2]]), "s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat <- function(x, ...) { + UseMethod("as_s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.s2_lnglat <- function(x, ...) { + x +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.s2_point <- function(x, ...) { + new_s2_xptr(s2_lnglat_from_s2_point(x), "s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.s2_geography <- function(x, ...) { + new_s2_xptr(s2_lnglat_from_numeric(cpp_s2_x(x), cpp_s2_y(x)), "s2_lnglat") +} + +#' @rdname s2_lnglat +#' @export +as_s2_lnglat.matrix <- function(x, ...) { + s2_lnglat(x[, 1, drop = TRUE], x[, 2, drop = TRUE]) +} + +#' @export +as_s2_lnglat.character <- function(x, ...) { + as_s2_lnglat.wk_wkt(x) +} + +#' @export +as_s2_lnglat.wk_wkt <- function(x, ...) { + as_s2_lnglat(as_s2_geography(x), ...) +} + +#' @export +as_s2_lnglat.wk_wkb <- function(x, ...) { + as_s2_lnglat(as_s2_geography(x), ...) +} + +#' @rdname s2_lnglat +#' @export +as.data.frame.s2_lnglat <- function(x, ...) { + as.data.frame(data_frame_from_s2_lnglat(x)) +} + +#' @rdname s2_lnglat +#' @export +as.matrix.s2_lnglat <- function(x, ...) { + as.matrix(as.data.frame(data_frame_from_s2_lnglat(x))) +} + +#' @rdname s2_lnglat +#' @importFrom wk as_wkb +#' @export +as_wkb.s2_lnglat <- function(x, ...) { + as_wkb(as_s2_geography(x), ...) +} + +#' @rdname s2_lnglat +#' @importFrom wk as_wkt +#' @export +as_wkt.s2_lnglat <- function(x, ...) { + as_wkt(as_s2_geography(x), ...) +} + +#' @export +`[<-.s2_lnglat` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_lnglat(value) + new_s2_xptr(x, "s2_lnglat") +} + +#' @export +`[[<-.s2_lnglat` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_lnglat(value) + new_s2_xptr(x, "s2_lnglat") +} + +#' @export +format.s2_lnglat <- function(x, ...) { + df <- as.data.frame(x) + sprintf("(%s, %s)", format(df$lng, trim = TRUE), format(df$lat, trim = TRUE)) +} diff --git a/R/s2-matrix.R b/R/s2-matrix.R new file mode 100644 index 0000000..83388fe --- /dev/null +++ b/R/s2-matrix.R @@ -0,0 +1,170 @@ + +#' Matrix Functions +#' +#' These functions are similar to accessors and predicates, but instead of +#' recycling `x` and `y` to a common length and returning a vector of that +#' length, these functions return a vector of length `x` with each element +#' `i` containing information about how the entire vector `y` relates to +#' the feature at `x[i]`. +#' +#' @inheritParams s2_is_collection +#' @inheritParams s2_contains +#' @param x,y Geography vectors, coerced using [as_s2_geography()]. +#' `x` is considered the source, where as `y` is considered the target. +#' @param max_edges_per_cell For [s2_may_intersect_matrix()], +#' this values controls the nature of the index on `y`, with higher values +#' leading to coarser index. Values should be between 10 and 50; the default +#' of 50 is adequate for most use cases, but for specialized operations users +#' may wish to use a lower value to increase performance. +#' @param max_feature_cells For [s2_may_intersect_matrix()], this value +#' controls the approximation of `x` used to identify potential intersections +#' on `y`. The default value of 4 gives the best performance for most operations, +#' but for specialized operations users may wish to use a higher value to increase +#' performance. +#' +#' @return A vector of length `x`. +#' @export +#' +#' @seealso +#' See pairwise predicate functions (e.g., [s2_intersects()]). +#' +#' @examples +#' city_names <- c("Vatican City", "San Marino", "Luxembourg") +#' cities <- s2_data_cities(city_names) +#' country_names <- s2_data_tbl_countries$name +#' countries <- s2_data_countries() +#' +#' # closest feature returns y indices of the closest feature +#' # for each feature in x +#' country_names[s2_closest_feature(cities, countries)] +#' +#' # farthest feature returns y indices of the farthest feature +#' # for each feature in x +#' country_names[s2_farthest_feature(cities, countries)] +#' +#' # predicate matrices +#' country_names[s2_intersects_matrix(cities, countries)[[1]]] +#' +#' # distance matrices +#' s2_distance_matrix(cities, cities) +#' s2_max_distance_matrix(cities, countries[1:4]) +#' +s2_closest_feature <- function(x, y) { + cpp_s2_closest_feature(as_s2_geography(x), as_s2_geography(y)) +} + +#' @rdname s2_closest_feature +#' @export +s2_farthest_feature <- function(x, y) { + cpp_s2_farthest_feature(as_s2_geography(x), as_s2_geography(y)) +} + +#' @rdname s2_closest_feature +#' @export +s2_distance_matrix <- function(x, y, radius = s2_earth_radius_meters()) { + cpp_s2_distance_matrix(as_s2_geography(x), as_s2_geography(y)) * radius +} + +#' @rdname s2_closest_feature +#' @export +s2_max_distance_matrix <- function(x, y, radius = s2_earth_radius_meters()) { + cpp_s2_max_distance_matrix(as_s2_geography(x), as_s2_geography(y)) * radius +} + +#' @rdname s2_closest_feature +#' @export +s2_contains_matrix <- function(x, y, options = s2_options(model = "open")) { + cpp_s2_contains_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_within_matrix <- function(x, y, options = s2_options(model = "open")) { + cpp_s2_within_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_covers_matrix <- function(x, y, options = s2_options(model = "closed")) { + cpp_s2_contains_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_covered_by_matrix <- function(x, y, options = s2_options(model = "closed")) { + cpp_s2_within_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_intersects_matrix <- function(x, y, options = s2_options()) { + cpp_s2_intersects_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_disjoint_matrix <- function(x, y, options = s2_options()) { + # disjoint is the odd one out, in that it requires a negation of intersects + # this is inconvenient to do on the C++ level, and is easier to maintain + # with setdiff() here (unless somebody complains that this is slow) + intersection <- cpp_s2_intersects_matrix(as_s2_geography(x), as_s2_geography(y), options) + Map(setdiff, list(seq_along(y)), intersection) +} + +#' @rdname s2_closest_feature +#' @export +s2_equals_matrix <- function(x, y, options = s2_options()) { + cpp_s2_equals_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_touches_matrix <- function(x, y, options = s2_options()) { + cpp_s2_touches_matrix(as_s2_geography(x), as_s2_geography(y), options) +} + +#' @rdname s2_closest_feature +#' @export +s2_dwithin_matrix <- function(x, y, distance, radius = s2_earth_radius_meters()) { + cpp_s2_dwithin_matrix(as_s2_geography(x), as_s2_geography(y), distance / radius) +} + +#' @rdname s2_closest_feature +#' @export +s2_may_intersect_matrix <- function(x, y, max_edges_per_cell = 50, max_feature_cells = 4) { + cpp_s2_may_intersect_matrix( + as_s2_geography(x), as_s2_geography(y), + max_edges_per_cell, max_feature_cells, + s2_options() + ) +} + +# ------- for testing, non-indexed versions of matrix operators ------- + +s2_contains_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_contains_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_within_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_within_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_covers_matrix_brute_force <- function(x, y, options = s2_options(model = "closed")) { + cpp_s2_contains_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_covered_by_matrix_brute_force <- function(x, y, options = s2_options(model = "closed")) { + cpp_s2_within_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_intersects_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_intersects_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_disjoint_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_disjoint_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} + +s2_equals_matrix_brute_force <- function(x, y, options = s2_options()) { + cpp_s2_equals_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options) +} diff --git a/R/s2-options.R b/R/s2-options.R new file mode 100644 index 0000000..6cfc266 --- /dev/null +++ b/R/s2-options.R @@ -0,0 +1,142 @@ + +#' Geography Operation Options +#' +#' These functions specify defaults for options used to perform operations +#' and construct geometries. These are used in predicates (e.g., [s2_intersects()]), +#' and boolean operations (e.g., [s2_intersection()]) to specify the model for +#' containment and how new geometries should be constructed. +#' +#' @param model One of 'open', 'semi-open' (default for polygons), +#' or 'closed' (default for polylines). See section 'Model' +#' @param snap Use `s2_snap_identity()`, `s2_snap_distance()`, `s2_snap_level()`, +#' or `s2_snap_precision()` to specify how or if coordinate rounding should +#' occur. +#' @param snap_radius As opposed to the snap function, which specifies +#' the maximum distance a vertex should move, the snap radius (in radians) sets +#' the minimum distance between vertices of the output that don't cause vertices +#' to move more than the distance specified by the snap function. This can be used +#' to simplify the result of a boolean operation. Use -1 to specify that any +#' minimum distance is acceptable. +#' @param duplicate_edges Use `TRUE` to keep duplicate edges (e.g., duplicate +#' points). +#' @param edge_type One of 'directed' (default) or 'undirected'. +#' @param polyline_type One of 'path' (default) or 'walk'. If 'walk', +#' polylines that backtrack are preserved. +#' @param polyline_sibling_pairs One of 'discard' (default) or 'keep'. +#' @param simplify_edge_chains Use `TRUE` to remove vertices that are within +#' `snap_radius` of the original vertex. +#' @param split_crossing_edges Use `TRUE` to split crossing polyline edges +#' when creating geometries. +#' @param idempotent Use `FALSE` to apply snap even if snapping is not necessary +#' to satisfy vertex constraints. +#' @param validate Use `TRUE` to validate the result from the builder. +#' @param level A value from 0 to 30 corresponding to the cell level +#' at which snapping should occur. +#' @param distance A distance (in radians) denoting the maximum +#' distance a vertex should move in the snapping process. +#' @param precision A number by which coordinates should be multiplied +#' before being rounded. Rounded to the nearest exponent of 10. +#' +#' @section Model: +#' The geometry model indicates whether or not a geometry includes its boundaries. +#' Boundaries of line geometries are its end points. +#' OPEN geometries do not contain their boundary (`model = "open"`); CLOSED +#' geometries (`model = "closed"`) contain their boundary; SEMI-OPEN geometries +#' (`model = "semi-open"`) contain half of their boundaries, such that when two polygons +#' do not overlap or two lines do not cross, no point exist that belong to +#' more than one of the geometries. (This latter form, half-closed, is +#' not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on +#' which that is based). The default values for [s2_contains()] (open) +#' and covers/covered_by (closed) correspond to the SFA standard specification +#' of these operators. +#' +#' @export +#' +#' @examples +#' # use s2_options() to specify containment models, snap level +#' # layer creation options, and builder options +#' s2_options(model = "closed", snap = s2_snap_level(30)) +#' +s2_options <- function(model = NULL, + snap = s2_snap_identity(), + snap_radius = -1, + duplicate_edges = FALSE, + edge_type = "directed", + validate = FALSE, + polyline_type = "path", + polyline_sibling_pairs = "keep", + simplify_edge_chains = FALSE, + split_crossing_edges = FALSE, + idempotent = FALSE) { + # check snap radius (passing in a huge snap radius can cause problems) + if (snap_radius > 3) { + stop( + "Snap radius is too large. Did you pass in a snap radius in meters instead of radians?", + call. = FALSE + ) + } + + structure( + list( + # model needs to be "unset" by default because there are differences in polygon + # and polyline handling by default that are good defaults to preserve + model = if (is.null(model)) -1 else match_option(model, c("open", "semi-open", "closed"), "model"), + snap = snap, + snap_radius = snap_radius, + duplicate_edges = duplicate_edges, + edge_type = match_option(edge_type, c("directed", "undirected"), "edge_type"), + validate = validate, + polyline_type = match_option(polyline_type, c("path", "walk"), "polyline_type"), + polyline_sibling_pairs = match_option( + polyline_sibling_pairs, + c("discard", "keep"), + "polyline_sibling_pairs" + ), + simplify_edge_chains = simplify_edge_chains, + split_crossing_edges = split_crossing_edges, + idempotent = idempotent + ), + class = "s2_options" + ) +} + +#' @rdname s2_options +#' @export +s2_snap_identity <- function() { + structure(list(), class = "snap_identity") +} + +#' @rdname s2_options +#' @export +s2_snap_level <- function(level) { + if (level > 30) { + stop("`level` must be an intger between 1 and 30", call. = FALSE) + } + + structure(list(level = level), class = "snap_level") +} + +#' @rdname s2_options +#' @export +s2_snap_precision <- function(precision) { + structure(list(exponent = round(log10(precision))), class = "snap_precision") +} + +#' @rdname s2_options +#' @export +s2_snap_distance <- function(distance) { + structure(list(distance = distance), class = "snap_distance") +} + + +match_option <- function(x, options, arg) { + result <- match(x, options) + if (identical(result, NA_integer_)) { + stop( + sprintf("`%s` must be one of %s", arg, paste0('"', options, '"', collapse = ", ")), + call. = FALSE + ) + } + + result +} diff --git a/R/s2-package.R b/R/s2-package.R new file mode 100644 index 0000000..6c757a5 --- /dev/null +++ b/R/s2-package.R @@ -0,0 +1,10 @@ +#' @keywords internal +"_PACKAGE" + +# The following block is used by usethis to automatically manage +# roxygen namespace tags. Modify with care! +## usethis namespace: start +#' @useDynLib s2, .registration = TRUE +#' @importFrom Rcpp sourceCpp +## usethis namespace: end +NULL diff --git a/R/s2-point.R b/R/s2-point.R new file mode 100644 index 0000000..681f3ba --- /dev/null +++ b/R/s2-point.R @@ -0,0 +1,89 @@ + +#' Create an S2 Point Vector +#' +#' In S2 terminology, a "point" is a 3-dimensional unit vector representation +#' of an [s2_lnglat()]. Internally, all s2 objects are stored as +#' 3-dimensional unit vectors. +#' +#' @param x,y,z Vectors of latitude and longitude values in degrees. +#' @param ... Unused +#' +#' @return An object with class s2_point +#' @export +#' +#' @examples +#' lnglat <- s2_lnglat(-64, 45) # Halifax, Nova Scotia! +#' as_s2_point(lnglat) +#' as.data.frame(as_s2_point(lnglat)) +#' +s2_point <- function(x, y, z) { + recycled <- recycle_common(as.double(x), as.double(y), as.double(z)) + new_s2_xptr(s2_point_from_numeric(recycled[[1]], recycled[[2]], recycled[[3]]), "s2_point") +} + +#' @rdname s2_point +#' @export +as_s2_point <- function(x, ...) { + UseMethod("as_s2_point") +} + +#' @rdname s2_point +#' @export +as_s2_point.s2_point <- function(x, ...) { + x +} + +#' @rdname s2_point +#' @export +as_s2_point.s2_lnglat <- function(x, ...) { + new_s2_xptr(s2_point_from_s2_lnglat(x), "s2_point") +} + +#' @rdname s2_point +#' @export +as_s2_point.s2_geography <- function(x, ...) { + as_s2_point(as_s2_lnglat(x)) +} + +#' @rdname s2_point +#' @export +as_s2_point.matrix <- function(x, ...) { + s2_point(x[, 1, drop = TRUE], x[, 2, drop = TRUE], x[, 3, drop = TRUE]) +} + +#' @rdname s2_point +#' @export +as.data.frame.s2_point <- function(x, ...) { + as.data.frame(data_frame_from_s2_point(x)) +} + +#' @rdname s2_point +#' @export +as.matrix.s2_point <- function(x, ...) { + as.matrix(as.data.frame(data_frame_from_s2_point(x))) +} + +#' @export +`[<-.s2_point` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_point(value) + new_s2_xptr(x, "s2_point") +} + +#' @export +`[[<-.s2_point` <- function(x, i, value) { + x <- unclass(x) + x[i] <- as_s2_point(value) + new_s2_xptr(x, "s2_point") +} + +#' @export +format.s2_point <- function(x, ...) { + df <- as.data.frame(x) + sprintf( + "[%s %s %s]", + format(df$x, trim = TRUE), + format(df$y, trim = TRUE), + format(df$z, trim = TRUE) + ) +} diff --git a/R/s2-predicates.R b/R/s2-predicates.R new file mode 100644 index 0000000..45701f0 --- /dev/null +++ b/R/s2-predicates.R @@ -0,0 +1,171 @@ + +#' S2 Geography Predicates +#' +#' These functions operate two geography vectors (pairwise), and return +#' a logical vector. +#' +#' @inheritParams s2_is_collection +#' @inheritParams s2_boundary +#' @param distance A distance on the surface of the earth in the same units +#' as `radius`. +#' @param lng1,lat1,lng2,lat2 A latitude/longitude range +#' @param detail The number of points with which to approximate +#' non-geodesic edges. +#' +#' @inheritSection s2_options Model +#' +#' @export +#' +#' @seealso +#' Matrix versions of these predicates (e.g., [s2_intersects_matrix()]). +#' +#' BigQuery's geography function reference: +#' +#' - [ST_CONTAINS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_contains) +#' - [ST_COVEREDBY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_coveredby) +#' - [ST_COVERS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_covers) +#' - [ST_DISJOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_disjoint) +#' - [ST_EQUALS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_equals) +#' - [ST_INTERSECTS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersects) +#' - [ST_INTERSECTSBOX](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersectsbox) +#' - [ST_TOUCHES](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_touches) +#' - [ST_WITHIN](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_within) +#' - [ST_DWITHIN](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dwithin) +#' +#' @examples +#' s2_contains( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_within( +#' c("POINT (5 5)", "POINT (-1 1)"), +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))" +#' ) +#' +#' s2_covered_by( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_covers( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_disjoint( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_intersects( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_equals( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0))", +#' "POLYGON ((-1 -1, 10 0, 10 10, 0 10, -1 -1))" +#' ) +#' ) +#' +#' s2_intersects( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)") +#' ) +#' +#' s2_intersects_box( +#' c("POINT (5 5)", "POINT (-1 1)"), +#' 0, 0, 10, 10 +#' ) +#' +#' s2_touches( +#' "POLYGON ((0 0, 0 1, 1 1, 0 0))", +#' c("POINT (0 0)", "POINT (0.5 0.75)", "POINT (0 0.5)") +#' ) +#' +#' s2_dwithin( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)"), +#' 0 # distance in meters +#' ) +#' +#' s2_dwithin( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' c("POINT (5 5)", "POINT (-1 1)"), +#' 1e6 # distance in meters +#' ) +#' +s2_contains <- function(x, y, options = s2_options(model = "open")) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_contains(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_within <- function(x, y, options = s2_options(model = "open")) { + s2_contains(y, x, options) +} + +#' @rdname s2_contains +#' @export +s2_covered_by <- function(x, y, options = s2_options(model = "closed")) { + s2_covers(y, x, options) +} + +#' @rdname s2_contains +#' @export +s2_covers <- function(x, y, options = s2_options(model = "closed")) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_contains(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_disjoint <- function(x, y, options = s2_options()) { + !s2_intersects(x, y, options) +} + +#' @rdname s2_contains +#' @export +s2_intersects <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_intersects(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_equals <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_equals(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_intersects_box <- function(x, lng1, lat1, lng2, lat2, detail = 1000, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), lng1, lat1, lng2, lat2, detail) + cpp_s2_intersects_box( + recycled[[1]], + recycled[[2]], recycled[[3]], + recycled[[4]], recycled[[5]], + detail = recycled[[6]], + s2options = options + ) +} + +#' @rdname s2_contains +#' @export +s2_touches <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + cpp_s2_touches(recycled[[1]], recycled[[2]], options) +} + +#' @rdname s2_contains +#' @export +s2_dwithin <- function(x, y, distance, radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), distance / radius) + cpp_s2_dwithin(recycled[[1]], recycled[[2]], recycled[[3]]) +} diff --git a/R/s2-transformers.R b/R/s2-transformers.R new file mode 100644 index 0000000..2ee380f --- /dev/null +++ b/R/s2-transformers.R @@ -0,0 +1,208 @@ + +#' S2 Geography Transformations +#' +#' These functions operate on one or more geography vectors and +#' return a geography vector. +#' +#' @inheritParams s2_is_collection +#' @param na.rm For aggregate calculations use `na.rm = TRUE` +#' to drop missing values. +#' @param grid_size The grid size to which coordinates should be snapped; +#' will be rounded to the nearest power of 10. +#' @param options An [s2_options()] object describing the polygon/polyline +#' model to use and the snap level. +#' @param distance The distance to buffer, in units of `radius`. +#' @param max_cells The maximum number of cells to approximate a buffer. +#' @param min_level The minimum cell level used to approximate a buffer +#' (1 - 30). Setting this value too high will result in unnecessarily +#' large geographies, but may help improve buffers along long, narrow +#' regions. +#' @param tolerance The minimum distance between vertexes to use when +#' simplifying a geography. +#' +#' @inheritSection s2_options Model +#' +#' @export +#' +#' @seealso +#' BigQuery's geography function reference: +#' +#' - [ST_BOUNDARY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_boundary) +#' - [ST_CENTROID](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_centroid) +#' - [ST_CLOSESTPOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_closestpoint) +#' - [ST_DIFFERENCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_difference) +#' - [ST_INTERSECTION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersection) +#' - [ST_UNION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union) +#' - [ST_SNAPTOGRID](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_snaptogrid) +#' - [ST_SIMPLIFY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_simplify) +#' - [ST_UNION_AGG](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union_agg) +#' - [ST_CENTROID_AGG](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#s2_centroid_agg) +#' +#' @examples +#' # returns the boundary: +#' # empty for point, endpoints of a linestring, +#' # perimeter of a polygon +#' s2_boundary("POINT (-64 45)") +#' s2_boundary("LINESTRING (0 0, 10 0)") +#' s2_boundary("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") +#' +#' # returns the area-weighted centroid, element-wise +#' s2_centroid("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") +#' s2_centroid("LINESTRING (0 0, 10 0)") +#' +#' # returns the unweighted centroid of the entire input +#' s2_centroid_agg(c("POINT (0 0)", "POINT (10 0)")) +#' +#' # returns the closest point on x to y +#' s2_closest_point( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POINT (0 90)" # north pole! +#' ) +#' +#' # returns the shortest possible line between x and y +#' s2_minimum_clearance_line_between( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POINT (0 90)" # north pole! +#' ) +#' +#' # binary operations: difference, symmetric difference, intersection and union +#' s2_difference( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' s2_sym_difference( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' s2_intersection( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' s2_union( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' # use s2_union_agg() to aggregate geographies in a vector +#' s2_union_agg( +#' c( +#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", +#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" +#' ), +#' # 32 bit platforms may need to set snap rounding +#' s2_options(snap = s2_snap_level(30)) +#' ) +#' +#' # snap to grid rounds coordinates to a specified grid size +#' s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2) +#' +s2_boundary <- function(x) { + new_s2_xptr(cpp_s2_boundary(as_s2_geography(x)), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_centroid <- function(x) { + new_s2_xptr(cpp_s2_centroid(as_s2_geography(x)), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_closest_point <- function(x, y) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_xptr(cpp_s2_closest_point(recycled[[1]], recycled[[2]]), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_minimum_clearance_line_between <- function(x, y) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_xptr(cpp_s2_minimum_clearance_line_between(recycled[[1]], recycled[[2]]), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_difference <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_xptr(cpp_s2_difference(recycled[[1]], recycled[[2]], options), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_sym_difference <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_xptr(cpp_s2_sym_difference(recycled[[1]], recycled[[2]], options), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_intersection <- function(x, y, options = s2_options()) { + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_xptr(cpp_s2_intersection(recycled[[1]], recycled[[2]], options), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_union <- function(x, y = NULL, options = s2_options()) { + if (is.null(y)) { + y <- as_s2_geography("POINT EMPTY") + } + + recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y)) + new_s2_xptr(cpp_s2_union(recycled[[1]], recycled[[2]], options), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_snap_to_grid <- function(x, grid_size) { + s2_rebuild( + x, + options = s2_options( + snap = s2_snap_precision(10^(-log10(grid_size))), + duplicate_edges = TRUE + ) + ) +} + +#' @rdname s2_boundary +#' @export +s2_simplify <- function(x, tolerance, radius = s2_earth_radius_meters()) { + s2_rebuild(x, options = s2_options(snap_radius = tolerance / radius, simplify_edge_chains = TRUE)) +} + +#' @rdname s2_boundary +#' @export +s2_rebuild <- function(x, options = s2_options()) { + new_s2_xptr(cpp_s2_rebuild(as_s2_geography(x), options), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_buffer_cells <- function(x, distance, max_cells = 1000, min_level = -1, + radius = s2_earth_radius_meters()) { + recycled <- recycle_common(as_s2_geography(x), distance / radius) + new_s2_xptr(cpp_s2_buffer_cells(recycled[[1]], recycled[[2]], max_cells, min_level), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_centroid_agg <- function(x, na.rm = FALSE) { + new_s2_xptr(cpp_s2_centroid_agg(as_s2_geography(x), naRm = na.rm), "s2_geography") +} + +#' @rdname s2_boundary +#' @export +s2_union_agg <- function(x, options = s2_options(), na.rm = FALSE) { + new_s2_xptr(cpp_s2_union_agg(as_s2_geography(x), options, na.rm), "s2_geography") +} diff --git a/R/s2-xptr.R b/R/s2-xptr.R new file mode 100644 index 0000000..1d1d8f3 --- /dev/null +++ b/R/s2-xptr.R @@ -0,0 +1,80 @@ + +#' Create vectors of XPtr objects +#' +#' @param x A bare `list()` of external pointers +#' @param class A character vector subclass +#' @param ... Unused +#' +#' @return An object of class s2_xptr +#' @noRd +#' +new_s2_xptr <- function(x = list(), class = character()) { + if (!is.list(x) || is.object(x)) { + stop("x must be a bare list of 'externalptr' objects") + } + + class(x) <- union(class, "s2_xptr") + x +} + +validate_s2_xptr <- function(x) { + type <- vapply(unclass(x), typeof, character(1)) + valid_items <- type %in% c("externalptr", "NULL") + if (any(!valid_items)) { + stop("Items must be externalptr objects or NULL") + } + + invisible(x) +} + +#' @export +`[.s2_xptr` <- function(x, i) { + new_s2_xptr(NextMethod(), class(x)) +} + +# makes lapply() along these vectors possible +#' @export +`[[.s2_xptr` <- function(x, i) { + x[i] +} + +#' @export +`c.s2_xptr` <- function(...) { + # make sure all items inherit the same top-level class + dots <- list(...) + inherits_first <- vapply(dots, inherits, class(dots[[1]])[1], FUN.VALUE = logical(1)) + if (!all(inherits_first)) { + stop(sprintf("All items must inherit from '%s'", class(dots[[1]])[1])) + } + + xptr <- new_s2_xptr(NextMethod(), class(dots[[1]])) + validate_s2_xptr(xptr) + xptr +} + +#' @export +rep.s2_xptr <- function(x, ...) { + if (length(x) == 0) { + new_s2_xptr(list(), class(x)) + } else { + new_s2_xptr(NextMethod(), class(x)) + } +} + +#' @method rep_len s2_xptr +#' @export +rep_len.s2_xptr <- function(x, length.out) { + rep(x, length.out = length.out) +} + +#' @export +print.s2_xptr <- function(x, ...) { + cat(sprintf("<%s[%s]>\n", class(x)[1], length(x))) + if (length(x) == 0) { + return(invisible(x)) + } + + out <- stats::setNames(format(x, ...), names(x)) + print(out, quote = FALSE) + invisible(x) +} diff --git a/R/utils.R b/R/utils.R new file mode 100644 index 0000000..ffb3f1a --- /dev/null +++ b/R/utils.R @@ -0,0 +1,27 @@ + +recycle_common <- function(...) { + dots <- list(...) + lengths <- vapply(dots, length, integer(1)) + non_constant_lengths <- unique(lengths[lengths != 1]) + if (length(non_constant_lengths) == 0) { + final_length <- 1 + } else if(length(non_constant_lengths) == 1) { + final_length <- non_constant_lengths + } else { + lengths_label <- paste0(non_constant_lengths, collapse = ", ") + stop(sprintf("Incompatible lengths: %s", lengths_label)) + } + + lapply(dots, rep_len, final_length) +} + +expect_wkt_equal <- function(x, y, precision = 16) { + testthat::expect_equal( + s2_geography_to_wkt(as_s2_geography(x), precision = precision, trim = TRUE), + s2_geography_to_wkt(as_s2_geography(y), precision = precision, trim = TRUE) + ) +} + +expect_near <- function(x, y, epsilon) { + testthat::expect_true(abs(y - x) < epsilon) +} diff --git a/R/vctrs.R b/R/vctrs.R new file mode 100644 index 0000000..0097ed9 --- /dev/null +++ b/R/vctrs.R @@ -0,0 +1,36 @@ + +vec_proxy.s2_geography <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_geography <- function(x, ...) { + new_s2_xptr(x, "s2_geography") +} + +vec_ptype_abbr.s2_geography <- function(x, ...) { + "s2_geography" +} + +vec_proxy.s2_point <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_point <- function(x, ...) { + new_s2_xptr(x, "s2_point") +} + +vec_ptype_abbr.s2_point <- function(x, ...) { + "s2_point" +} + +vec_proxy.s2_lnglat <- function(x, ...) { + unclass(x) +} + +vec_restore.s2_lnglat <- function(x, ...) { + new_s2_xptr(x, "s2_lnglat") +} + +vec_ptype_abbr.s2_lnglat <- function(x, ...) { + "s2_lnglat" +} diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..2c83c3d --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,72 @@ + +# nocov start +.onLoad <- function(...) { + # call c++ init + cpp_s2_init() + + # dynamically register vctrs dependencies + for (cls in c("s2_geography", "s2_point", "s2_lnglat")) { + s3_register("vctrs::vec_proxy", cls) + s3_register("vctrs::vec_restore", cls) + s3_register("vctrs::vec_ptype_abbr", cls) + } +} + +s3_register <- function(generic, class, method = NULL) { + stopifnot(is.character(generic), length(generic) == 1) + stopifnot(is.character(class), length(class) == 1) + + pieces <- strsplit(generic, "::")[[1]] + stopifnot(length(pieces) == 2) + package <- pieces[[1]] + generic <- pieces[[2]] + + caller <- parent.frame() + + get_method_env <- function() { + top <- topenv(caller) + if (isNamespace(top)) { + asNamespace(environmentName(top)) + } else { + caller + } + } + get_method <- function(method, env) { + if (is.null(method)) { + get(paste0(generic, ".", class), envir = get_method_env()) + } else { + method + } + } + + method_fn <- get_method(method) + stopifnot(is.function(method_fn)) + + # Always register hook in case package is later unloaded & reloaded + setHook( + packageEvent(package, "onLoad"), + function(...) { + ns <- asNamespace(package) + + # Refresh the method, it might have been updated by `devtools::load_all()` + method_fn <- get_method(method) + + registerS3method(generic, class, method_fn, envir = ns) + } + ) + + # Avoid registration failures during loading (pkgload or regular) + if (!isNamespaceLoaded(package)) { + return(invisible()) + } + + envir <- asNamespace(package) + + # Only register if generic can be accessed + if (exists(generic, envir)) { + registerS3method(generic, class, method_fn, envir = envir) + } + + invisible() +} +# nocov end diff --git a/README.md b/README.md new file mode 100644 index 0000000..0a9bf7b --- /dev/null +++ b/README.md @@ -0,0 +1,180 @@ + + + +# s2 + + + +[![Lifecycle: +experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) +![R-CMD-check](https://github.com/r-spatial/s2/workflows/R-CMD-check/badge.svg) +[![codecov](https://codecov.io/gh/r-spatial/s2/branch/master/graph/badge.svg)](https://codecov.io/gh/r-spatial/s2) +[![CRAN](http://www.r-pkg.org/badges/version/s2)](https://cran.r-project.org/package=s2) + + +The goal of s2 is to provide R bindings to Google’s +[S2Geometry](https://s2geometry.io) library. The package exposes an API +similar to Google’s [BigQuery Geography +API](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions), +whose functions also operate on spherical geometries. This package is a +complete rewrite of an earlier CRAN package s2 with versions up to +0.4-2, for which the sources are found +[here](https://github.com/spatstat/s2/). + +## Installation + +You can install the released version of s2 from +[CRAN](https://CRAN.R-project.org) with: + +``` r +install.packages("s2") +``` + +And the development version from [GitHub](https://github.com/) with: + +``` r +# install.packages("remotes") +remotes::install_github("r-spatial/s2") +``` + +## Example + +The s2 package provides geometry transformers and predicates similar to +those found in [GEOS](https://trac.osgeo.org/geos/), except instead of +assuming a planar geometry, s2’s functions work in latitude and +longitude and assume a spherical geometry: + +``` r +library(s2) + +s2_contains( + # polygon containing much of the northern hemisphere + "POLYGON ((-63.5 44.6, -149.75 61.20, 116.4 40.2, 13.5 52.51, -63.5 44.6))", + # ...should contain the north pole + "POINT (0 90)" +) +#> [1] TRUE +``` + +The [sf package](https://r-spatial.github.io/sf/) uses s2 for geographic +coordinates with `sf::sf_use_s2(TRUE)`, and will become the default +after sf version 1.0.0. The sf package also supports creating s2 vectors +using `as_s2_geography()`: + +``` r +library(dplyr) +library(sf) + +nc_s2 <- read_sf(system.file("shape/nc.shp", package = "sf")) %>% + mutate(geometry = as_s2_geography(geometry)) %>% + as_tibble() %>% + select(NAME, geometry) + +nc_s2 +#> # A tibble: 100 x 2 +#> NAME geometry +#> +#> 1 Ashe 2 Alleghany 3 Surry 4 Currituck 5 Northampton 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … with 90 more rows +``` + +Use accessors to extract information about geometries: + +``` r +nc_s2 %>% + mutate( + area = s2_area(geometry), + perimeter = s2_perimeter(geometry) + ) +#> # A tibble: 100 x 4 +#> NAME geometry area perimeter +#> +#> 1 Ashe 2 Alleghany 3 Surry 4 Currituck 5 Northamp… 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … with 90 more rows +``` + +Use predicates to subset vectors: + +``` r +nc_s2 %>% + filter(s2_contains(geometry, "POINT (-80.9313 35.6196)")) +#> # A tibble: 1 x 2 +#> NAME geometry +#> +#> 1 Catawba % + mutate(geometry = s2_boundary(geometry)) +#> # A tibble: 100 x 2 +#> NAME geometry +#> +#> 1 Ashe 2 Alleghany 3 Surry 4 Currituck 5 Northampton 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … with 90 more rows +``` + +Finally, use the WKB or WKT exporters to export to sf or some other +package: + +``` r +nc_s2 %>% + mutate(geometry = st_as_sfc(s2_as_binary(geometry))) %>% + st_as_sf() +#> Simple feature collection with 100 features and 1 field +#> geometry type: GEOMETRY +#> dimension: XY +#> bbox: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965 +#> CRS: NA +#> # A tibble: 100 x 2 +#> NAME geometry +#> +#> 1 Ashe POLYGON ((-81.45289 36.23959, -81.43104 36.26072, -81.41233 36.26… +#> 2 Alleghany POLYGON ((-81.17667 36.41544, -81.15337 36.42474, -81.1384 36.417… +#> 3 Surry POLYGON ((-80.45301 36.25709, -80.43531 36.55104, -80.61105 36.55… +#> 4 Currituck MULTIPOLYGON (((-75.94193 36.29434, -75.95751 36.25945, -75.91376… +#> 5 Northampt… POLYGON ((-77.14196 36.41706, -77.13932 36.45648, -77.12733 36.47… +#> 6 Hertford POLYGON ((-76.7075 36.26613, -76.74135 36.31517, -76.92408 36.392… +#> 7 Camden POLYGON ((-76.01735 36.33773, -76.03288 36.33598, -76.04395 36.35… +#> 8 Gates POLYGON ((-76.46035 36.3739, -76.50246 36.45229, -76.49834 36.503… +#> 9 Warren POLYGON ((-78.13472 36.23658, -78.10963 36.21351, -78.05835 36.21… +#> 10 Stokes POLYGON ((-80.02406 36.54502, -80.0481 36.54713, -80.43531 36.551… +#> # … with 90 more rows +``` + +## Acknowledgment + +This project gratefully acknowledges financial +[support](https://www.r-consortium.org/projects) from the + + + diff --git a/build/partial.rdb b/build/partial.rdb new file mode 100644 index 0000000..c16aa9b Binary files /dev/null and b/build/partial.rdb differ diff --git a/cleanup b/cleanup new file mode 100755 index 0000000..3fd9cd2 --- /dev/null +++ b/cleanup @@ -0,0 +1,3 @@ +#!/bin/sh +rm -f src/Makevars configure.log autobrew +rm `find src -name *.o` diff --git a/configure b/configure new file mode 100755 index 0000000..bf20a71 --- /dev/null +++ b/configure @@ -0,0 +1,117 @@ +# Anticonf (tm) script by Jeroen Ooms (2020) +# This script will query 'pkg-config' for the required cflags and ldflags. +# If pkg-config is unavailable or does not find the library, try setting +# INCLUDE_DIR and LIB_DIR manually via e.g: +# R CMD INSTALL --configure-vars='INCLUDE_DIR=/.../include LIB_DIR=/.../lib' + +# Library settings +PKG_CONFIG_NAME="openssl" +PKG_DEB_NAME="libssl-dev" +PKG_RPM_NAME="openssl-devel" +PKG_CSW_NAME="libssl_dev" +PKG_BREW_NAME="openssl@1.1" +PKG_TEST_FILE="tools/version.c" +PKG_LIBS="-lssl -lcrypto" +PKG_CFLAGS="" + +# Use pkg-config if available +pkg-config ${PKG_CONFIG_NAME} --atleast-version=1.0 2>/dev/null +if [ $? -eq 0 ]; then + PKGCONFIG_CFLAGS=`pkg-config --cflags ${PKG_CONFIG_NAME}` + PKGCONFIG_LIBS=`pkg-config --libs ${PKG_CONFIG_NAME}` +fi + +# Note that cflags may be empty in case of success +if [ "$INCLUDE_DIR" ] || [ "$LIB_DIR" ]; then + echo "Found INCLUDE_DIR and/or LIB_DIR!" + PKG_CFLAGS="-I$INCLUDE_DIR $PKG_CFLAGS" + PKG_LIBS="-L$LIB_DIR $PKG_LIBS" +elif [ "$PKGCONFIG_CFLAGS" ] || [ "$PKGCONFIG_LIBS" ]; then + echo "Found pkg-config cflags and libs!" + PKG_CFLAGS=${PKGCONFIG_CFLAGS} + PKG_LIBS=${PKGCONFIG_LIBS} +elif [ `uname` = "Darwin" ]; then + brew --version 2>/dev/null + if [ $? -eq 0 ]; then + BREWDIR=`brew --prefix` + PKG_CFLAGS="-I$BREWDIR/opt/openssl/include" + PKG_LIBS="-L$BREWDIR/opt/openssl/lib $PKG_LIBS" + else + curl -sfL "https://autobrew.github.io/scripts/$PKG_BREW_NAME" > autobrew + . autobrew + fi +fi + +# Find compiler +CC=`${R_HOME}/bin/R CMD config CC` +CFLAGS=`${R_HOME}/bin/R CMD config CFLAGS` +CPPFLAGS=`${R_HOME}/bin/R CMD config CPPFLAGS` + +# For debugging +echo "Testing compiler using PKG_CFLAGS=$PKG_CFLAGS" + +# Test configuration +${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E ${PKG_TEST_FILE} >/dev/null 2>configure.log + +# Customize the error +if [ $? -ne 0 ]; then + echo "--------------------------- [ANTICONF] --------------------------------" + echo "Configuration failed because $PKG_CONFIG_NAME was not found. Try installing:" + echo " * deb: $PKG_DEB_NAME (Debian, Ubuntu, etc)" + echo " * rpm: $PKG_RPM_NAME (Fedora, CentOS, RHEL)" + echo " * csw: $PKG_CSW_NAME (Solaris)" + echo " * brew: $PKG_BREW_NAME (Mac OSX)" + echo "If $PKG_CONFIG_NAME is already installed, check that 'pkg-config' is in your" + echo "PATH and PKG_CONFIG_PATH contains a $PKG_CONFIG_NAME.pc file. If pkg-config" + echo "is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:" + echo "R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'" + echo "-------------------------- [ERROR MESSAGE] ---------------------------" + cat configure.log + echo "--------------------------------------------------------------------" + exit 1 +fi + +# Try to link against the correct OpenSSL version +if [ -z "$AUTOBREW" ]; then +SONAME=`${CC} -E ${PKG_CFLAGS} src/tests/soname.h | sh | xargs` +if [ "$SONAME" ]; then +if [ `uname` = "Darwin" ]; then + PKG_LIBS_VERSIONED=`echo "${PKG_LIBS}" | sed "s/-lssl/-lssl.${SONAME}/" | sed "s/-lcrypto/-lcrypto.${SONAME}/"` +else + PKG_LIBS_VERSIONED=`echo "${PKG_LIBS}" | sed "s/-lssl/-l:libssl.so.${SONAME}/" | sed "s/-lcrypto/-l:libcrypto.so.${SONAME}/"` +fi + +# Test if versioned linking works +${CC} ${PKG_CFLAGS} src/tests/main.c ${PKG_LIBS_VERSIONED} -o src/main.exe 2>/dev/null +if [ $? -eq 0 ]; then PKG_LIBS="${PKG_LIBS_VERSIONED}"; fi + +# Suppress opensslv3 warnings for now +if [ "$SONAME" = "3" ]; then +PKG_CFLAGS="$PKG_CFLAGS -DOPENSSL_SUPPRESS_DEPRECATED" +fi + +fi #SONAME +fi #AUTOBREW + +# Define system endianness (compile-time endianness using system/compiler +# defines isn't detected on Solaris) +# based on endian detection from the feather package by @hadley +R_ENDIAN=`${R_HOME}/bin/Rscript -e 'cat(.Platform$endian)'` +# Trim off any warning messages that Rscript appends in front of the platform endianness +R_ENDIAN=`expr "$R_ENDIAN" : '.*\(little\)$'` +SYS_ENDIAN="" +if [ "$R_ENDIAN" = "little" ]; then + PKG_CFLAGS="$PKG_CFLAGS -DIS_LITTLE_ENDIAN" +else + PKG_CFLAGS="$PKG_CFLAGS -DIS_BIG_ENDIAN" +fi + +echo "Using PKG_LIBS=$PKG_LIBS" +echo "Using PKG_CFLAGS=$PKG_CFLAGS" + + +# Write to Makevars +sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" src/Makevars.in > src/Makevars + +# Success +exit 0 diff --git a/configure.win b/configure.win new file mode 100755 index 0000000..e69de29 diff --git a/data/s2_data_tbl_cities.rda b/data/s2_data_tbl_cities.rda new file mode 100644 index 0000000..317ea50 Binary files /dev/null and b/data/s2_data_tbl_cities.rda differ diff --git a/data/s2_data_tbl_countries.rda b/data/s2_data_tbl_countries.rda new file mode 100644 index 0000000..a94b448 Binary files /dev/null and b/data/s2_data_tbl_countries.rda differ diff --git a/data/s2_data_tbl_timezones.rda b/data/s2_data_tbl_timezones.rda new file mode 100644 index 0000000..1d96a0b Binary files /dev/null and b/data/s2_data_tbl_timezones.rda differ diff --git a/inst/include/cpp-compat.h b/inst/include/cpp-compat.h new file mode 100644 index 0000000..7e18796 --- /dev/null +++ b/inst/include/cpp-compat.h @@ -0,0 +1,16 @@ + +#ifndef CPP_COMPAT_H +#define CPP_COMPAT_H + +#include + +void cpp_compat_printf(const char* fmt, ...); +[[ noreturn ]] void cpp_compat_abort(); +[[ noreturn ]] void cpp_compat_exit(int code); +int cpp_compat_random(); +void cpp_compat_srandom(int seed); + +extern std::ostream& cpp_compat_cerr; +extern std::ostream& cpp_compat_cout; + +#endif diff --git a/inst/include/s2/_fp_contract_off.h b/inst/include/s2/_fp_contract_off.h new file mode 100644 index 0000000..a053c7a --- /dev/null +++ b/inst/include/s2/_fp_contract_off.h @@ -0,0 +1,60 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2__FP_CONTRACT_OFF_H_ +#define S2__FP_CONTRACT_OFF_H_ + +// Turn off the fused multiply-add optimization ("fp-contract"). With +// fp-contract on, any expression of the form "a * b + c" has two possible +// results, and the compiler is free to choose either of them. Effectively +// this makes it impossible to write deterministic functions that involve +// floating-point math. +// +// S2 requires deterministic arithmetic for correctness. We need to turn off +// fp-contract for the entire compilation unit, because S2 has public inline +// functions, and the optimization is controlled by the setting in effect when +// inline functions are instantiated (not when they are defined). +// +// Note that there is a standard C pragma to turn off FP contraction: +// #pragma STDC FP_CONTRACT OFF +// but it is not implemented in GCC because the standard pragma allows control +// at the level of compound statements rather than entire functions. +// +// This file may be included with other files in any order, as long as it +// appears before the first non-inline function definition. It is +// named with an underscore so that it is included first among the S2 headers. + +// TODO(compiler-team): Figure out how to do this in a portable way. +#if defined(HAVE_ARMEABI_V7A) +// Some android builds use a buggy compiler that runs out of memory while +// parsing the pragma (--cpu=armeabi-v7a). + +#elif defined(__ANDROID__) +// Other android builds use a buggy compiler that crashes with an internal +// error (Android NDK R9). + +#elif defined(__clang__) +// Clang supports the standard C++ pragma for turning off this optimization. +#pragma STDC FP_CONTRACT OFF + +#elif defined(__GNUC__) +// GCC defines its own pragma that operates at the function level rather than +// the statement level. +#pragma GCC optimize("fp-contract=off") +#endif + +#endif // S2__FP_CONTRACT_OFF_H_ diff --git a/inst/include/s2/base/casts.h b/inst/include/s2/base/casts.h new file mode 100644 index 0000000..dae1516 --- /dev/null +++ b/inst/include/s2/base/casts.h @@ -0,0 +1,318 @@ +// Copyright 2009 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + + +// +// Various Google-specific casting templates. +// +// This code is compiled directly on many platforms, including client +// platforms like Windows, Mac, and embedded systems. Before making +// any changes here, make sure that you're not breaking any platforms. +// + +#ifndef S2_BASE_CASTS_H_ +#define S2_BASE_CASTS_H_ + +#include // for use with down_cast<> +#include // for enumeration casts and tests +#include + +#include "s2/third_party/absl/base/casts.h" +#include "s2/third_party/absl/base/macros.h" + +// An "upcast", i.e. a conversion from a pointer to an object to a pointer to a +// base subobject, always succeeds if the base is unambiguous and accessible, +// and so it's fine to use implicit_cast. +// +// A "downcast", i.e. a conversion from a pointer to an object to a pointer +// to a more-derived object that may contain the original object as a base +// subobject, cannot safely be done using static_cast, because you do not +// generally know whether the source object is really the base subobject of +// a containing, more-derived object of the target type. Thus, when you +// downcast in a polymorphic type hierarchy, you should use the following +// function template. +// +// In debug mode, we use dynamic_cast to double-check whether the downcast is +// legal (we die if it's not). In normal mode, we do the efficient static_cast +// instead. Thus, it's important to test in debug mode to make sure the cast is +// legal! +// +// This is the only place in the codebase we should use dynamic_cast. +// In particular, you should NOT use dynamic_cast for RTTI, e.g. for +// code like this: +// if (auto* p = dynamic_cast(foo)) HandleASubclass1Object(p); +// if (auto* p = dynamic_cast(foo)) HandleASubclass2Object(p); +// You should design the code some other way not to need this. + +template // use like this: down_cast(foo); +inline To down_cast(From* f) { // so we only accept pointers + static_assert( + (std::is_base_of::type>::value), + "target type not derived from source type"); + + // We skip the assert and hence the dynamic_cast if RTTI is disabled. +#if !defined(__GNUC__) || defined(__GXX_RTTI) + // Uses RTTI in dbg and fastbuild. asserts are disabled in opt builds. + assert(f == nullptr || dynamic_cast(f) != nullptr); +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + + return static_cast(f); +} + +// Overload of down_cast for references. Use like this: down_cast(foo). +// The code is slightly convoluted because we're still using the pointer +// form of dynamic cast. (The reference form throws an exception if it +// fails.) +// +// There's no need for a special const overload either for the pointer +// or the reference form. If you call down_cast with a const T&, the +// compiler will just bind From to const T. +template +inline To down_cast(From& f) { + static_assert( + std::is_lvalue_reference::value, "target type not a reference"); + static_assert( + (std::is_base_of::type>::value), + "target type not derived from source type"); + + // We skip the assert and hence the dynamic_cast if RTTI is disabled. +#if !defined(__GNUC__) || defined(__GXX_RTTI) + // RTTI: debug mode only + assert(dynamic_cast::type*>(&f) != + nullptr); +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + + return static_cast(f); +} + +// **** Enumeration Casts and Tests +// +// C++ requires that the value of an integer that is converted to an +// enumeration be within the value bounds of the enumeration. Modern +// compilers can and do take advantage of this requirement to optimize +// programs. So, using a raw static_cast with enums can be bad. See +// +// The following templates and macros enable casting from an int to an enum +// with checking against the appropriate bounds. First, when defining an +// enumeration, identify the limits of the values of its enumerators. +// +// enum A { A_min = -18, A_max = 33 }; +// MAKE_ENUM_LIMITS(A, A_min, A_max) +// +// Convert an int to an enum in one of two ways. The prefered way is a +// tight conversion, which ensures that A_min <= value <= A_max. +// +// A var = tight_enum_cast(3); +// +// However, the C++ language defines the set of possible values for an +// enumeration to be essentially the range of a bitfield that can represent +// all the enumerators, i.e. those within the nearest containing power +// of two. In the example above, the nearest positive power of two is 64, +// and so the upper bound is 63. The nearest negative power of two is +// -32 and so the lower bound is -32 (two's complement), which is upgraded +// to match the upper bound, becoming -64. The values within this range +// of -64 to 63 are valid, according to the C++ standard. You can cast +// values within this range as follows. +// +// A var = loose_enum_cast(45); +// +// These casts will log a message if the value does not reside within the +// specified range, and will be fatal when in debug mode. +// +// For those times when an assert too strong, there are test functions. +// +// bool var = tight_enum_test(3); +// bool var = loose_enum_test(45); +// +// For code that needs to use the enumeration value if and only if +// it is good, there is a function that both tests and casts. +// +// int i = ....; +// A var; +// if (tight_enum_test_cast(i, &var)) +// .... // use valid var with value as indicated by i +// else +// .... // handle invalid enum cast +// +// The enum test/cast facility is currently limited to enumerations that +// fit within an int. It is also limited to two's complement ints. + +// ** Implementation Description +// +// The enum_limits template class captures the minimum and maximum +// enumerator. All uses of this template are intended to be of +// specializations, so the generic has a field to identify itself as +// not specialized. The test/cast templates assert specialization. + +template +class enum_limits { + public: + static const Enum min_enumerator = 0; + static const Enum max_enumerator = 0; + static const bool is_specialized = false; +}; + +// Now we define the macro to define the specialization for enum_limits. +// The specialization checks that the enumerators fit within an int. +// This checking relies on integral promotion. + +#define MAKE_ENUM_LIMITS(ENUM_TYPE, ENUM_MIN, ENUM_MAX) \ +template <> \ +class enum_limits { \ +public: \ + static const ENUM_TYPE min_enumerator = ENUM_MIN; \ + static const ENUM_TYPE max_enumerator = ENUM_MAX; \ + static const bool is_specialized = true; \ + static_assert(ENUM_MIN >= INT_MIN, "enumerator too negative for int"); \ + static_assert(ENUM_MAX <= INT_MAX, "enumerator too positive for int"); \ +}; + +// The loose enum test/cast is actually the more complicated one, +// because of the problem of finding the bounds. +// +// The unary upper bound, ub, on a positive number is its positive +// saturation, i.e. for a value v within pow(2,k-1) <= v < pow(2,k), +// the upper bound is pow(2,k)-1. +// +// The unary lower bound, lb, on a negative number is its negative +// saturation, i.e. for a value v within -pow(2,k) <= v < -pow(2,k-1), +// the lower bound is -pow(2,k). +// +// The actual bounds are (1) the binary upper bound over the maximum +// enumerator and the one's complement of a negative minimum enumerator +// and (2) the binary lower bound over the minimum enumerator and the +// one's complement of the positive maximum enumerator, except that if no +// enumerators are negative, the lower bound is zero. +// +// The algorithm relies heavily on the observation that +// +// a,b>0 then ub(a,b) == ub(a) | ub(b) == ub(a|b) +// a,b<0 then lb(a,b) == lb(a) & lb(b) == lb(a&b) +// +// Note that the compiler will boil most of this code away +// because of value propagation on the constant enumerator bounds. + +template +inline bool loose_enum_test(int e_val) { + static_assert(enum_limits::is_specialized, "missing MAKE_ENUM_LIMITS"); + const Enum e_min = enum_limits::min_enumerator; + const Enum e_max = enum_limits::max_enumerator; + static_assert(sizeof(e_val) == 4 || sizeof(e_val) == 8, + "unexpected int size"); + + // Find the unary bounding negative number of e_min and e_max. + + // Find the unary bounding negative number of e_max. + // This would be b_min = e_max < 0 ? e_max : ~e_max, + // but we want to avoid branches to help the compiler. + int e_max_sign = e_max >> (sizeof(e_val)*8 - 1); + int b_min = ~e_max_sign ^ e_max; + + // Find the binary bounding negative of both e_min and e_max. + b_min &= e_min; + + // However, if e_min is positive, the result will be positive. + // Now clear all bits right of the most significant clear bit, + // which is a negative saturation for negative numbers. + // In the case of positive numbers, this is flush to zero. + b_min &= b_min >> 1; + b_min &= b_min >> 2; + b_min &= b_min >> 4; + b_min &= b_min >> 8; + b_min &= b_min >> 16; +#if INT_MAX > 2147483647 + b_min &= b_min >> 32; +#endif + + // Find the unary bounding positive number of e_max. + int b_max = e_max_sign ^ e_max; + + // Find the binary bounding positive number of that + // and the unary bounding positive number of e_min. + int e_min_sign = e_min >> (sizeof(e_val)*8 - 1); + b_max |= e_min_sign ^ e_min; + + // Now set all bits right of the most significant set bit, + // which is a positive saturation for positive numbers. + b_max |= b_max >> 1; + b_max |= b_max >> 2; + b_max |= b_max >> 4; + b_max |= b_max >> 8; + b_max |= b_max >> 16; +#if INT_MAX > 2147483647 + b_max |= b_max >> 32; +#endif + + // Finally test the bounds. + return b_min <= e_val && e_val <= b_max; +} + +template +inline bool tight_enum_test(int e_val) { + static_assert(enum_limits::is_specialized, "missing MAKE_ENUM_LIMITS"); + const Enum e_min = enum_limits::min_enumerator; + const Enum e_max = enum_limits::max_enumerator; + return e_min <= e_val && e_val <= e_max; +} + +template +inline bool loose_enum_test_cast(int e_val, Enum* e_var) { + if (loose_enum_test(e_val)) { + *e_var = static_cast(e_val); + return true; + } else { + return false; + } +} + +template +inline bool tight_enum_test_cast(int e_val, Enum* e_var) { + if (tight_enum_test(e_val)) { + *e_var = static_cast(e_val); + return true; + } else { + return false; + } +} + +// The plain casts require logging, and we get header recursion if +// it is done directly. So, we do it indirectly. +// The following function is defined in logging.cc. + +namespace base { +namespace internal { + +void WarnEnumCastError(int value_of_int); + +} // namespace internal +} // namespace base + +template +inline Enum loose_enum_cast(int e_val) { + if (!loose_enum_test(e_val)) { + base::internal::WarnEnumCastError(e_val); + } + return static_cast(e_val); +} + +template +inline Enum tight_enum_cast(int e_val) { + if (!tight_enum_test(e_val)) { + base::internal::WarnEnumCastError(e_val); + } + return static_cast(e_val); +} + +#endif // S2_BASE_CASTS_H_ diff --git a/inst/include/s2/base/commandlineflags.h b/inst/include/s2/base/commandlineflags.h new file mode 100644 index 0000000..1763be0 --- /dev/null +++ b/inst/include/s2/base/commandlineflags.h @@ -0,0 +1,51 @@ +// Copyright Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef S2_BASE_COMMANDLINEFLAGS_H_ +#define S2_BASE_COMMANDLINEFLAGS_H_ + +#ifdef S2_USE_GFLAGS + +#include + +#else // !defined(S2_USE_GFLAGS) + +#include + +#include "s2/base/integral_types.h" + +#define DEFINE_bool(name, default_value, description) \ + bool FLAGS_##name = default_value +#define DECLARE_bool(name) \ + extern bool FLAGS_##name + +#define DEFINE_double(name, default_value, description) \ + double FLAGS_##name = default_value +#define DECLARE_double(name) \ + extern double FLAGS_##name + +#define DEFINE_int32(name, default_value, description) \ + int32 FLAGS_##name = default_value +#define DECLARE_int32(name) \ + extern int32 FLAGS_##name + +#define DEFINE_string(name, default_value, description) \ + std::string FLAGS_##name = default_value +#define DECLARE_string(name) \ + extern std::string FLAGS_##name + +#endif // !defined(S2_USE_GFLAGS) + +#endif // S2_BASE_COMMANDLINEFLAGS_H_ diff --git a/inst/include/s2/base/integral_types.h b/inst/include/s2/base/integral_types.h new file mode 100644 index 0000000..d20f35f --- /dev/null +++ b/inst/include/s2/base/integral_types.h @@ -0,0 +1,31 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef S2_BASE_INTEGRAL_TYPES_H_ +#define S2_BASE_INTEGRAL_TYPES_H_ + +using int8 = signed char; +using int16 = short; +using int32 = int; +using int64 = long long; + +using uint8 = unsigned char; +using uint16 = unsigned short; +using uint32 = unsigned int; +using uint64 = unsigned long long; + +using uword_t = unsigned long; + +#endif // S2_BASE_INTEGRAL_TYPES_H_ diff --git a/inst/include/s2/base/log_severity.h b/inst/include/s2/base/log_severity.h new file mode 100644 index 0000000..57095ff --- /dev/null +++ b/inst/include/s2/base/log_severity.h @@ -0,0 +1,40 @@ +// Copyright Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef S2_BASE_LOG_SEVERITY_H_ +#define S2_BASE_LOG_SEVERITY_H_ + +#ifdef S2_USE_GLOG + +#include + +#else // !defined(S2_USE_GLOG) + +#include "s2/third_party/absl/base/log_severity.h" + +// Stay compatible with glog. +namespace google { + +#ifdef NDEBUG +constexpr bool DEBUG_MODE = false; +#else +constexpr bool DEBUG_MODE = true; +#endif + +} // namespace google + +#endif // !defined(S2_USE_GLOG) + +#endif // S2_BASE_LOG_SEVERITY_H_ diff --git a/inst/include/s2/base/logging.h b/inst/include/s2/base/logging.h new file mode 100644 index 0000000..07feb5e --- /dev/null +++ b/inst/include/s2/base/logging.h @@ -0,0 +1,177 @@ +// Copyright Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef S2_BASE_LOGGING_H_ +#define S2_BASE_LOGGING_H_ +#include "cpp-compat.h" + +#ifdef S2_USE_GLOG + +#include + +// The names CHECK, etc. are too common and may conflict with other +// packages. We use S2_CHECK to make it easier to switch to +// something other than GLOG for logging. + +#define S2_LOG LOG +#define S2_LOG_IF LOG_IF +#define S2_DLOG_IF DLOG_IF + +#define S2_CHECK CHECK +#define S2_CHECK_EQ CHECK_EQ +#define S2_CHECK_NE CHECK_NE +#define S2_CHECK_LT CHECK_LT +#define S2_CHECK_LE CHECK_LE +#define S2_CHECK_GT CHECK_GT +#define S2_CHECK_GE CHECK_GE + +#define S2_DCHECK DCHECK +#define S2_DCHECK_EQ DCHECK_EQ +#define S2_DCHECK_NE DCHECK_NE +#define S2_DCHECK_LT DCHECK_LT +#define S2_DCHECK_LE DCHECK_LE +#define S2_DCHECK_GT DCHECK_GT +#define S2_DCHECK_GE DCHECK_GE + +#define S2_VLOG VLOG +#define S2_VLOG_IS_ON VLOG_IS_ON + +#else // !defined(S2_USE_GLOG) + +#include + +#include "s2/base/log_severity.h" +#include "s2/third_party/absl/base/attributes.h" +#include "s2/third_party/absl/base/log_severity.h" + +class S2LogMessage { + public: + S2LogMessage(const char* file, int line, + absl::LogSeverity severity, std::ostream& stream) + : severity_(severity), stream_(stream) { + if (enabled()) { + stream_ << file << ":" << line << " " + << absl::LogSeverityName(severity) << " "; + } + } + ~S2LogMessage() { if (enabled()) stream_ << std::endl; } + + std::ostream& stream() { return stream_; } + + // silences an 'unused member' compiler warning + absl::LogSeverity severity() { return severity_; } + + private: + bool enabled() const { +#ifdef ABSL_MIN_LOG_LEVEL + return (static_cast(severity_) >= ABSL_MIN_LOG_LEVEL || + severity_ >= absl::LogSeverity::kFatal); +#else + return true; +#endif + } + + absl::LogSeverity severity_; + std::ostream& stream_; +}; + +// Same as S2LogMessage, but destructor is marked no-return to avoid +// "no return value warnings" in functions that return non-void. +class S2FatalLogMessage : public S2LogMessage { + public: + S2FatalLogMessage(const char* file, int line, + absl::LogSeverity severity, std::ostream& stream) + ABSL_ATTRIBUTE_COLD + : S2LogMessage(file, line, severity, stream) {} + ABSL_ATTRIBUTE_NORETURN ~S2FatalLogMessage() { cpp_compat_abort(); } +}; + +// Logging stream that does nothing. +struct S2NullStream { + template + S2NullStream& operator<<(const T& v) { return *this; } +}; + +// Used to suppress "unused value" warnings. +struct S2LogMessageVoidify { + // Must have precedence lower than << but higher than ?:. + void operator&(std::ostream&) {} +}; + +#define S2_LOG_MESSAGE_(LogMessageClass, log_severity) \ + LogMessageClass(__FILE__, __LINE__, log_severity, cpp_compat_cerr) +#define S2_LOG_INFO \ + S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kInfo) +#define S2_LOG_WARNING \ + S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kWarning) +#define S2_LOG_ERROR \ + S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kError) +#define S2_LOG_FATAL \ + S2_LOG_MESSAGE_(S2FatalLogMessage, absl::LogSeverity::kFatal) +#ifndef NDEBUG +#define S2_LOG_DFATAL S2_LOG_FATAL +#else +#define S2_LOG_DFATAL S2_LOG_ERROR +#endif + +#define S2_LOG(severity) S2_LOG_##severity.stream() + +// Implementing this as if (...) {} else S2_LOG(...) will cause dangling else +// warnings when someone does if (...) S2_LOG_IF(...), so do this tricky +// thing instead. +#define S2_LOG_IF(severity, condition) \ + !(condition) ? (void)0 : S2LogMessageVoidify() & S2_LOG(severity) + +#define S2_CHECK(condition) \ + S2_LOG_IF(FATAL, ABSL_PREDICT_FALSE(!(condition))) \ + << ("Check failed: " #condition " ") + +#ifndef NDEBUG + +#define S2_DLOG_IF S2_LOG_IF +#define S2_DCHECK S2_CHECK + +#else // defined(NDEBUG) + +#define S2_DLOG_IF(severity, condition) \ + while (false && (condition)) S2NullStream() +#define S2_DCHECK(condition) \ + while (false && (condition)) S2NullStream() + +#endif // defined(NDEBUG) + +#define S2_CHECK_OP(op, val1, val2) S2_CHECK((val1) op (val2)) +#define S2_CHECK_EQ(val1, val2) S2_CHECK_OP(==, val1, val2) +#define S2_CHECK_NE(val1, val2) S2_CHECK_OP(!=, val1, val2) +#define S2_CHECK_LT(val1, val2) S2_CHECK_OP(<, val1, val2) +#define S2_CHECK_LE(val1, val2) S2_CHECK_OP(<=, val1, val2) +#define S2_CHECK_GT(val1, val2) S2_CHECK_OP(>, val1, val2) +#define S2_CHECK_GE(val1, val2) S2_CHECK_OP(>=, val1, val2) + +#define S2_DCHECK_OP(op, val1, val2) S2_DCHECK((val1) op (val2)) +#define S2_DCHECK_EQ(val1, val2) S2_DCHECK_OP(==, val1, val2) +#define S2_DCHECK_NE(val1, val2) S2_DCHECK_OP(!=, val1, val2) +#define S2_DCHECK_LT(val1, val2) S2_DCHECK_OP(<, val1, val2) +#define S2_DCHECK_LE(val1, val2) S2_DCHECK_OP(<=, val1, val2) +#define S2_DCHECK_GT(val1, val2) S2_DCHECK_OP(>, val1, val2) +#define S2_DCHECK_GE(val1, val2) S2_DCHECK_OP(>=, val1, val2) + +// We don't support VLOG. +#define S2_VLOG(verbose_level) S2NullStream() +#define S2_VLOG_IS_ON(verbose_level) (false) + +#endif // !defined(S2_USE_GLOG) + +#endif // S2_BASE_LOGGING_H_ diff --git a/inst/include/s2/base/mutex.h b/inst/include/s2/base/mutex.h new file mode 100644 index 0000000..031d21e --- /dev/null +++ b/inst/include/s2/base/mutex.h @@ -0,0 +1,61 @@ +// Copyright Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef S2_BASE_MUTEX_H_ +#define S2_BASE_MUTEX_H_ + +#include +#include + +namespace absl { + +class Mutex { + public: + Mutex() = default; + ~Mutex() = default; + Mutex(Mutex const&) = delete; + Mutex& operator=(Mutex const&) = delete; + + inline void Lock() { mutex_.lock(); } + inline void Unlock() { mutex_.unlock(); } + + private: + std::mutex mutex_; + + friend class CondVar; +}; + +class CondVar { + public: + CondVar() = default; + ~CondVar() = default; + CondVar(CondVar const&) = delete; + CondVar& operator=(CondVar const&) = delete; + + inline void Wait(Mutex* mu) { + std::unique_lock lock(mu->mutex_, std::adopt_lock); + cond_var_.wait(lock); + lock.release(); + } + inline void Signal() { cond_var_.notify_one(); } + inline void SignalAll() { cond_var_.notify_all(); } + + private: + std::condition_variable cond_var_; +}; + +} // namespace absl + +#endif // S2_BASE_MUTEX_H_ diff --git a/inst/include/s2/base/port.h b/inst/include/s2/base/port.h new file mode 100644 index 0000000..70d6675 --- /dev/null +++ b/inst/include/s2/base/port.h @@ -0,0 +1,1013 @@ +// Copyright Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef S2_BASE_PORT_H_ +#define S2_BASE_PORT_H_ + +// This file contains things that are not used in third_party/absl but needed by +// - Platform specific requirement +// - MSVC +// - Utility macros +// - Endianness +// - Hash +// - Global variables +// - Type alias +// - Predefined system/language macros +// - Predefined system/language functions +// - Performance optimization (alignment) +// - Obsolete + +#include +#include +#include +#include + +#include "s2/base/integral_types.h" +#include "s2/third_party/absl/base/config.h" +#include "s2/third_party/absl/base/port.h" + +#ifdef SWIG +%include "third_party/absl/base/port.h" +#endif + +// ----------------------------------------------------------------------------- +// MSVC Specific Requirements +// ----------------------------------------------------------------------------- + +#ifdef _MSC_VER /* if Visual C++ */ + +#include // Must come before +#include +#include // _getpid() +#include +#undef ERROR +#undef DELETE +#undef DIFFERENCE +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define S_IRUSR 00400 +#define S_IWUSR 00200 +#define S_IXUSR 00100 +#define S_IRGRP 00040 +#define S_IWGRP 00020 +#define S_IXGRP 00010 +#define S_IROTH 00004 +#define S_IWOTH 00002 +#define S_IXOTH 00001 + +// This compiler flag can be easily overlooked on MSVC. +// _CHAR_UNSIGNED gets set with the /J flag. +#ifndef _CHAR_UNSIGNED +#error chars must be unsigned! Use the /J flag on the compiler command line. // NOLINT +#endif + +// Allow comparisons between signed and unsigned values. +// +// Lots of Google code uses this pattern: +// for (int i = 0; i < container.size(); ++i) +// Since size() returns an unsigned value, this warning would trigger +// frequently. Very few of these instances are actually bugs since containers +// rarely exceed MAX_INT items. Unfortunately, there are bugs related to +// signed-unsigned comparisons that have been missed because we disable this +// warning. For example: +// const long stop_time = os::GetMilliseconds() + kWaitTimeoutMillis; +// while (os::GetMilliseconds() <= stop_time) { ... } +#pragma warning(disable : 4018) // level 3 +#pragma warning(disable : 4267) // level 3 + +// Don't warn about unused local variables. +// +// extension to silence particular instances of this warning. There's no way +// to define ABSL_ATTRIBUTE_UNUSED to quiet particular instances of this warning +// in VC++, so we disable it globally. Currently, there aren't many false +// positives, so perhaps we can address those in the future and re-enable these +// warnings, which sometimes catch real bugs. +#pragma warning(disable : 4101) // level 3 + +// Allow initialization and assignment to a smaller type without warnings about +// possible loss of data. +// +// There is a distinct warning, 4267, that warns about size_t conversions to +// smaller types, but we don't currently disable that warning. +// +// Correct code can be written in such a way as to avoid false positives +// by making the conversion explicit, but Google code isn't usually that +// verbose. There are too many false positives to address at this time. Note +// that this warning triggers at levels 2, 3, and 4 depending on the specific +// type of conversion. By disabling it, we not only silence minor narrowing +// conversions but also serious ones. +#pragma warning(disable : 4244) // level 2, 3, and 4 + +// Allow silent truncation of double to float. +// +// Silencing this warning has caused us to miss some subtle bugs. +#pragma warning(disable : 4305) // level 1 + +// Allow a constant to be assigned to a type that is too small. +// +// I don't know why we allow this at all. I can't think of a case where this +// wouldn't be a bug, but enabling the warning breaks many builds today. +#pragma warning(disable : 4307) // level 2 + +// Allow passing the this pointer to an initializer even though it refers +// to an uninitialized object. +// +// Some observer implementations rely on saving the this pointer. Those are +// safe because the pointer is not dereferenced until after the object is fully +// constructed. This could however, obscure other instances. In the future, we +// should look into disabling this warning locally rather globally. +#pragma warning(disable : 4355) // level 1 and 4 + +// Allow implicit coercion from an integral type to a bool. +// +// These could be avoided by making the code more explicit, but that's never +// been the style here, so there would be many false positives. It's not +// obvious if a true positive would ever help to find an actual bug. +#pragma warning(disable : 4800) // level 3 + +#endif // _MSC_VER + +// ----------------------------------------------------------------------------- +// Utility Macros +// ----------------------------------------------------------------------------- + +// OS_IOS +#if defined(__APPLE__) +// Currently, blaze supports iOS yet doesn't define a flag. Mac users have +// traditionally defined OS_IOS themselves via other build systems, since mac +// hasn't been supported by blaze. +// TODO(user): Remove this when all toolchains make the proper defines. +#include +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#ifndef OS_IOS +#define OS_IOS 1 +#endif +#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#endif // defined(__APPLE__) + +// __GLIBC_PREREQ +#if defined __linux__ +// GLIBC-related macros. +#include + +#ifndef __GLIBC_PREREQ +#define __GLIBC_PREREQ(a, b) 0 // not a GLIBC system +#endif +#endif // __linux__ + +// STATIC_ANALYSIS +// Klocwork static analysis tool's C/C++ complier kwcc +#if defined(__KLOCWORK__) +#define STATIC_ANALYSIS +#endif // __KLOCWORK__ + +// SIZEOF_MEMBER, OFFSETOF_MEMBER +#define SIZEOF_MEMBER(t, f) sizeof(reinterpret_cast(4096)->f) + +#define OFFSETOF_MEMBER(t, f) \ + (reinterpret_cast(&(reinterpret_cast(16)->f)) - \ + reinterpret_cast(16)) + +// LANG_CXX11 +// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least +// gcc-4.7 and clang-3.1 (2011-12-13). __cplusplus was defined to 1 +// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is +// defined according to the language version in effect thereafter. +// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite +// reasonably good C++11 support, so we set LANG_CXX for it and +// newer versions (_MSC_VER >= 1900). +#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ + (defined(_MSC_VER) && _MSC_VER >= 1900)) +// DEPRECATED: Do not key off LANG_CXX11. Instead, write more accurate condition +// that checks whether the C++ feature you need is available or missing, and +// define a more specific feature macro (GOOGLE_HAVE_FEATURE_FOO). You can check +// http://en.cppreference.com/w/cpp/compiler_support for compiler support on C++ +// features. +// Define this to 1 if the code is compiled in C++11 mode; leave it +// undefined otherwise. Do NOT define it to 0 -- that causes +// '#ifdef LANG_CXX11' to behave differently from '#if LANG_CXX11'. +#define LANG_CXX11 1 +#endif + +// This sanity check can be removed when all references to +// LANG_CXX11 is removed from the code base. +#if defined(__cplusplus) && !defined(LANG_CXX11) && !defined(SWIG) +#error "LANG_CXX11 is required." +#endif + +// GOOGLE_OBSCURE_SIGNAL +#if defined(__APPLE__) +// No SIGPWR on MacOSX. SIGINFO seems suitably obscure. +#define GOOGLE_OBSCURE_SIGNAL SIGINFO +#else +/* We use SIGPWR since that seems unlikely to be used for other reasons. */ +#define GOOGLE_OBSCURE_SIGNAL SIGPWR +#endif + +// ABSL_FUNC_PTR_TO_CHAR_PTR +// On some platforms, a "function pointer" points to a function descriptor +// rather than directly to the function itself. +// Use ABSL_FUNC_PTR_TO_CHAR_PTR(func) to get a char-pointer to the first +// instruction of the function func. +// TODO(b/30407660): Move this macro into Abseil when symbolizer is released in +// Abseil. +#if defined(__cplusplus) +#if (defined(__powerpc__) && !(_CALL_ELF > 1)) || defined(__ia64) +// use opd section for function descriptors on these platforms, the function +// address is the first word of the descriptor +namespace absl { +enum { kPlatformUsesOPDSections = 1 }; +} // namespace absl +#define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast(func)[0]) +#else // not PPC or IA64 +namespace absl { +enum { kPlatformUsesOPDSections = 0 }; +} // namespace absl +#define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast(func)) +#endif // PPC or IA64 +#endif // __cplusplus + +// ----------------------------------------------------------------------------- +// Utility Functions +// ----------------------------------------------------------------------------- + +// sized_delete +#ifdef __cplusplus +namespace base { +// We support C++14's sized deallocation for all C++ builds, +// though for other toolchains, we fall back to using delete. +inline void sized_delete(void *ptr, size_t size) { +#ifdef GOOGLE_HAVE_SIZED_DELETE + ::operator delete(ptr, size); +#else + (void)size; + ::operator delete(ptr); +#endif // GOOGLE_HAVE_SIZED_DELETE +} + +inline void sized_delete_array(void *ptr, size_t size) { +#ifdef GOOGLE_HAVE_SIZED_DELETEARRAY + ::operator delete[](ptr, size); +#else + (void) size; + ::operator delete[](ptr); +#endif +} +} // namespace base +#endif // __cplusplus + +// ----------------------------------------------------------------------------- +// Endianness +// ----------------------------------------------------------------------------- + +// IS_LITTLE_ENDIAN, IS_BIG_ENDIAN + +// Allow compiler -D defines to override detection here +// which occasionally fails (e.g., on CRAN Solaris) +#if defined(IS_LITTLE_ENDIAN) +#undef IS_BIG_ENDIAN +#elif defined(IS_BIG_ENDIAN) +#undef IS_LITTLE_ENDIAN +#else + +#if defined __linux__ || defined OS_ANDROID || defined(__ANDROID__) +// TODO(user): http://b/21460321; use one of OS_ANDROID or __ANDROID__. +// _BIG_ENDIAN +#include + +#elif defined(__APPLE__) + +// BIG_ENDIAN +#include // NOLINT(build/include) +/* Let's try and follow the Linux convention */ +#define __BYTE_ORDER BYTE_ORDER +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN + +#endif + +// defines __BYTE_ORDER for MSVC +#ifdef _MSC_VER +#define __BYTE_ORDER __LITTLE_ENDIAN +#define IS_LITTLE_ENDIAN +#else + +// define the macros IS_LITTLE_ENDIAN or IS_BIG_ENDIAN +// using the above endian definitions from endian.h if +// endian.h was included +#ifdef __BYTE_ORDER +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define IS_LITTLE_ENDIAN +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define IS_BIG_ENDIAN +#endif + +#else // __BYTE_ORDER + +#if defined(__LITTLE_ENDIAN__) +#define IS_LITTLE_ENDIAN +#elif defined(__BIG_ENDIAN__) +#define IS_BIG_ENDIAN +#endif + +#endif // __BYTE_ORDER +#endif // _MSC_VER +#endif // #if defined(IS_LITTLE_ENDIAN) ... #else + +// byte swap functions (bswap_16, bswap_32, bswap_64). + +// The following guarantees declaration of the byte swap functions +#ifdef _MSC_VER +#include // NOLINT(build/include) +#define bswap_16(x) _byteswap_ushort(x) +#define bswap_32(x) _byteswap_ulong(x) +#define bswap_64(x) _byteswap_uint64(x) + +#elif defined(__APPLE__) +// Mac OS X / Darwin features +#include +#define bswap_16(x) OSSwapInt16(x) +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) + +#elif defined(__GLIBC__) || defined(__BIONIC__) || defined(__ASYLO__) +#include // IWYU pragma: export + +#else + +static inline uint16 bswap_16(uint16 x) { +#ifdef __cplusplus + return static_cast(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); +#else + return (uint16)(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); // NOLINT +#endif // __cplusplus +} +#define bswap_16(x) bswap_16(x) +static inline uint32 bswap_32(uint32 x) { + return (((x & 0xFF) << 24) | + ((x & 0xFF00) << 8) | + ((x & 0xFF0000) >> 8) | + ((x & 0xFF000000) >> 24)); +} +#define bswap_32(x) bswap_32(x) +static inline uint64 bswap_64(uint64 x) { + return (((x & 0xFFULL) << 56) | + ((x & 0xFF00ULL) << 40) | + ((x & 0xFF0000ULL) << 24) | + ((x & 0xFF000000ULL) << 8) | + ((x & 0xFF00000000ULL) >> 8) | + ((x & 0xFF0000000000ULL) >> 24) | + ((x & 0xFF000000000000ULL) >> 40) | + ((x & 0xFF00000000000000ULL) >> 56)); +} +#define bswap_64(x) bswap_64(x) + +#endif + +// ----------------------------------------------------------------------------- +// Hash +// ----------------------------------------------------------------------------- + +#ifdef __cplusplus +#ifdef STL_MSVC // not always the same as _MSC_VER +#include "s2/third_party/absl/base/internal/port_hash.inc" +#else +struct PortableHashBase {}; +#endif // STL_MSVC +#endif // __cplusplus + +// ----------------------------------------------------------------------------- +// Global Variables +// ----------------------------------------------------------------------------- + +// PATH_SEPARATOR +// Define the OS's path separator +// +// NOTE: Assuming the path separator at compile time is discouraged. +// Prefer instead to be tolerant of both possible separators whenever possible. +#ifdef __cplusplus // C won't merge duplicate const variables at link time +// Some headers provide a macro for this (GCC's system.h), remove it so that we +// can use our own. +#undef PATH_SEPARATOR +#if defined(_WIN32) +const char PATH_SEPARATOR = '\\'; +#else +const char PATH_SEPARATOR = '/'; +#endif // _WIN32 +#endif // __cplusplus + +// ----------------------------------------------------------------------------- +// Type Alias +// ----------------------------------------------------------------------------- + +// uint, ushort, ulong +#if defined __linux__ +// The uint mess: +// mysql.h sets _GNU_SOURCE which sets __USE_MISC in +// sys/types.h typedefs uint if __USE_MISC +// mysql typedefs uint if HAVE_UINT not set +// The following typedef is carefully considered, and should not cause +// any clashes +#if !defined(__USE_MISC) +#if !defined(HAVE_UINT) +#define HAVE_UINT 1 +typedef unsigned int uint; +#endif // !HAVE_UINT +#if !defined(HAVE_USHORT) +#define HAVE_USHORT 1 +typedef unsigned short ushort; // NOLINT +#endif // !HAVE_USHORT +#if !defined(HAVE_ULONG) +#define HAVE_ULONG 1 +typedef unsigned long ulong; // NOLINT +#endif // !HAVE_ULONG +#endif // !__USE_MISC + +#endif // __linux__ + +#ifdef _MSC_VER /* if Visual C++ */ +// VC++ doesn't understand "uint" +#ifndef HAVE_UINT +#define HAVE_UINT 1 +typedef unsigned int uint; +#endif // !HAVE_UINT +#endif // _MSC_VER + +#ifdef _MSC_VER +// uid_t +// MSVC doesn't have uid_t +typedef int uid_t; + +// pid_t +// Defined all over the place. +typedef int pid_t; +#endif // _MSC_VER + +// mode_t +#ifdef _MSC_VER +// From stat.h +typedef unsigned int mode_t; +#endif // _MSC_VER + +// sig_t +#ifdef _MSC_VER +typedef void (*sig_t)(int); +#endif // _MSC_VER + +// u_int16_t, int16_t +#ifdef _MSC_VER +// u_int16_t, int16_t don't exist in MSVC +typedef unsigned short u_int16_t; // NOLINT +typedef short int16_t; // NOLINT +#endif // _MSC_VER + +// using std::hash +#ifdef _MSC_VER +#ifdef __cplusplus +// Define a minimal set of things typically available in the global +// namespace in Google code. ::string is handled elsewhere, and uniformly +// for all targets. +#include +using std::hash; +#endif // __cplusplus +#endif // _MSC_VER + +// printf macros +// __STDC_FORMAT_MACROS must be defined before inttypes.h inclusion */ +#if defined(__APPLE__) +/* From MacOSX's inttypes.h: + * "C++ implementations should define these macros only when + * __STDC_FORMAT_MACROS is defined before is included." */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif /* __STDC_FORMAT_MACROS */ +#endif /* __APPLE__ */ + +// printf macros for size_t, in the style of inttypes.h +#if defined(_LP64) || defined(__APPLE__) +#define __PRIS_PREFIX "z" +#else +#define __PRIS_PREFIX +#endif + +// Use these macros after a % in a printf format string +// to get correct 32/64 bit behavior, like this: +// size_t size = records.size(); +// printf("%" PRIuS "\n", size); +#define PRIdS __PRIS_PREFIX "d" +#define PRIxS __PRIS_PREFIX "x" +#define PRIuS __PRIS_PREFIX "u" +#define PRIXS __PRIS_PREFIX "X" +#define PRIoS __PRIS_PREFIX "o" + +#define GPRIuPTHREAD "lu" +#define GPRIxPTHREAD "lx" +#if defined(__APPLE__) +#define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast(pthreadt) +#else +#define PRINTABLE_PTHREAD(pthreadt) pthreadt +#endif + +#ifdef PTHREADS_REDHAT_WIN32 +#include // NOLINT(build/include) +#include // NOLINT(build/include) +// pthread_t is not a simple integer or pointer on Win32 +std::ostream &operator<<(std::ostream &out, const pthread_t &thread_id); +#endif + +// ----------------------------------------------------------------------------- +// Predefined System/Language Macros +// ----------------------------------------------------------------------------- + +// EXFULL +#if defined(__APPLE__) +// Linux has this in +#define EXFULL ENOMEM // not really that great a translation... +#endif // __APPLE__ +#ifdef _MSC_VER +// This actually belongs in errno.h but there's a name conflict in errno +// on WinNT. They (and a ton more) are also found in Winsock2.h, but +// if'd out under NT. We need this subset at minimum. +#define EXFULL ENOMEM // not really that great a translation... +#endif // _MSC_VER + +// MSG_NOSIGNAL +#if defined(__APPLE__) +// Doesn't exist on OSX. +#define MSG_NOSIGNAL 0 +#endif // __APPLE__ + +// __ptr_t +#if defined(__APPLE__) +// Linux has this in +#define __ptr_t void * +#endif // __APPLE__ +#ifdef _MSC_VER +// From glob.h +#define __ptr_t void * +#endif + +// HUGE_VALF +#ifdef _MSC_VER +#include // for HUGE_VAL + +#ifndef HUGE_VALF +#define HUGE_VALF (static_cast(HUGE_VAL)) +#endif +#endif // _MSC_VER + +// MAP_ANONYMOUS +#if defined(__APPLE__) +// For mmap, Linux defines both MAP_ANONYMOUS and MAP_ANON and says MAP_ANON is +// deprecated. In Darwin, MAP_ANON is all there is. +#if !defined MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif // !MAP_ANONYMOUS +#endif // __APPLE__ + +// PATH_MAX +// You say tomato, I say atotom +#ifdef _MSC_VER +#define PATH_MAX MAX_PATH +#endif + +// ----------------------------------------------------------------------------- +// Predefined System/Language Functions +// ----------------------------------------------------------------------------- + +// strtoq, strtouq, atoll +#ifdef _MSC_VER +#define strtoq _strtoi64 +#define strtouq _strtoui64 +#define atoll _atoi64 +#endif // _MSC_VER + +#ifdef _MSC_VER +// You say tomato, I say _tomato +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define strdup _strdup +#define tempnam _tempnam +#define chdir _chdir +#define getpid _getpid +#define getcwd _getcwd +#define putenv _putenv +#define timezone _timezone +#define tzname _tzname +#endif // _MSC_VER + +// random, srandom +#ifdef _MSC_VER +// You say tomato, I say toma +inline int random() { return rand(); } +inline void srandom(unsigned int seed) { srand(seed); } +#endif // _MSC_VER + +// bcopy, bzero +#ifdef _MSC_VER +// You say juxtapose, I say transpose +#define bcopy(s, d, n) memcpy(d, s, n) +// Really from +inline void bzero(void *s, int n) { memset(s, 0, n); } +#endif // _MSC_VER + +// gethostbyname +#if defined(_WIN32) || defined(__APPLE__) +// gethostbyname() *is* thread-safe for Windows native threads. It is also +// safe on Mac OS X and iOS, where it uses thread-local storage, even though the +// manpages claim otherwise. For details, see +// http://lists.apple.com/archives/Darwin-dev/2006/May/msg00008.html +#else +// gethostbyname() is not thread-safe. So disallow its use. People +// should either use the HostLookup::Lookup*() methods, or gethostbyname_r() +#define gethostbyname gethostbyname_is_not_thread_safe_DO_NOT_USE +#endif + +// ----------------------------------------------------------------------------- +// Performance Optimization +// ----------------------------------------------------------------------------- + +// Alignment + +// Unaligned APIs + +// Portable handling of unaligned loads, stores, and copies. +// On some platforms, like ARM, the copy functions can be more efficient +// then a load and a store. +// +// It is possible to implement all of these these using constant-length memcpy +// calls, which is portable and will usually be inlined into simple loads and +// stores if the architecture supports it. However, such inlining usually +// happens in a pass that's quite late in compilation, which means the resulting +// loads and stores cannot participate in many other optimizations, leading to +// overall worse code. +// TODO(user): These APIs are forked in Abseil, see +// LLVM, we should reimplement these APIs with functions calling memcpy(), and +// maybe publish them in Abseil. + +// The unaligned API is C++ only. The declarations use C++ features +// (namespaces, inline) which are absent or incompatible in C. +#if defined(__cplusplus) + +#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \ + defined(MEMORY_SANITIZER) +// Consider we have an unaligned load/store of 4 bytes from address 0x...05. +// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and +// will miss a bug if 08 is the first unaddressable byte. +// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will +// miss a race between this access and some other accesses to 08. +// MemorySanitizer will correctly propagate the shadow on unaligned stores +// and correctly report bugs on unaligned loads, but it may not properly +// update and report the origin of the uninitialized memory. +// For all three tools, replacing an unaligned access with a tool-specific +// callback solves the problem. + +// Make sure uint16_t/uint32_t/uint64_t are defined. +#include + +extern "C" { +uint16_t __sanitizer_unaligned_load16(const void *p); +uint32_t __sanitizer_unaligned_load32(const void *p); +uint64_t __sanitizer_unaligned_load64(const void *p); +void __sanitizer_unaligned_store16(void *p, uint16_t v); +void __sanitizer_unaligned_store32(void *p, uint32_t v); +void __sanitizer_unaligned_store64(void *p, uint64_t v); +} // extern "C" + +inline uint16 UNALIGNED_LOAD16(const void *p) { + return __sanitizer_unaligned_load16(p); +} + +inline uint32 UNALIGNED_LOAD32(const void *p) { + return __sanitizer_unaligned_load32(p); +} + +inline uint64 UNALIGNED_LOAD64(const void *p) { + return __sanitizer_unaligned_load64(p); +} + +inline void UNALIGNED_STORE16(void *p, uint16 v) { + __sanitizer_unaligned_store16(p, v); +} + +inline void UNALIGNED_STORE32(void *p, uint32 v) { + __sanitizer_unaligned_store32(p, v); +} + +inline void UNALIGNED_STORE64(void *p, uint64 v) { + __sanitizer_unaligned_store64(p, v); +} + +#elif defined(UNDEFINED_BEHAVIOR_SANITIZER) + +inline uint16 UNALIGNED_LOAD16(const void *p) { + uint16 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint32 UNALIGNED_LOAD32(const void *p) { + uint32 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); } + +inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); } + +inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } + +#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \ + defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \ + defined(__ppc64__) || defined(__PPC64__) + +// x86 and x86-64 can perform unaligned loads/stores directly; +// modern PowerPC hardware can also do unaligned integer loads and stores; +// but note: the FPU still sends unaligned loads and stores to a trap handler! + +#define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD64(_p) (*reinterpret_cast(_p)) + +#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast(_p) = (_val)) + +#elif defined(__arm__) && !defined(__ARM_ARCH_5__) && \ + !defined(__ARM_ARCH_5T__) && !defined(__ARM_ARCH_5TE__) && \ + !defined(__ARM_ARCH_5TEJ__) && !defined(__ARM_ARCH_6__) && \ + !defined(__ARM_ARCH_6J__) && !defined(__ARM_ARCH_6K__) && \ + !defined(__ARM_ARCH_6Z__) && !defined(__ARM_ARCH_6ZK__) && \ + !defined(__ARM_ARCH_6T2__) + +// ARMv7 and newer support native unaligned accesses, but only of 16-bit +// and 32-bit values (not 64-bit); older versions either raise a fatal signal, +// do an unaligned read and rotate the words around a bit, or do the reads very +// slowly (trip through kernel mode). There's no simple #define that says just +// “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 +// sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define, +// so in time, maybe we can move on to that. +// +// This is a mess, but there's not much we can do about it. +// +// To further complicate matters, only LDR instructions (single reads) are +// allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we +// explicitly tell the compiler that these accesses can be unaligned, it can and +// will combine accesses. On armcc, the way to signal this is done by accessing +// through the type (uint32 __packed *), but GCC has no such attribute +// (it ignores __attribute__((packed)) on individual variables). However, +// we can tell it that a _struct_ is unaligned, which has the same effect, +// so we do that. + +namespace base { +namespace internal { + +struct Unaligned16Struct { + uint16 value; + uint8 dummy; // To make the size non-power-of-two. +} ABSL_ATTRIBUTE_PACKED; + +struct Unaligned32Struct { + uint32 value; + uint8 dummy; // To make the size non-power-of-two. +} ABSL_ATTRIBUTE_PACKED; + +} // namespace internal +} // namespace base + +#define UNALIGNED_LOAD16(_p) \ + ((reinterpret_cast(_p))->value) +#define UNALIGNED_LOAD32(_p) \ + ((reinterpret_cast(_p))->value) + +#define UNALIGNED_STORE16(_p, _val) \ + ((reinterpret_cast< ::base::internal::Unaligned16Struct *>(_p))->value = \ + (_val)) +#define UNALIGNED_STORE32(_p, _val) \ + ((reinterpret_cast< ::base::internal::Unaligned32Struct *>(_p))->value = \ + (_val)) + +// TODO(user): NEON supports unaligned 64-bit loads and stores. +// See if that would be more efficient on platforms supporting it, +// at least for copies. + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } + +#else + +#define NEED_ALIGNED_LOADS + +// These functions are provided for architectures that don't support +// unaligned loads and stores. + +inline uint16 UNALIGNED_LOAD16(const void *p) { + uint16 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint32 UNALIGNED_LOAD32(const void *p) { + uint32 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); } + +inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); } + +inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } + +#endif + +// The UNALIGNED_LOADW and UNALIGNED_STOREW macros load and store values +// of type uword_t. +#ifdef _LP64 +#define UNALIGNED_LOADW(_p) UNALIGNED_LOAD64(_p) +#define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE64(_p, _val) +#else +#define UNALIGNED_LOADW(_p) UNALIGNED_LOAD32(_p) +#define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE32(_p, _val) +#endif + +inline void UnalignedCopy16(const void *src, void *dst) { + UNALIGNED_STORE16(dst, UNALIGNED_LOAD16(src)); +} + +inline void UnalignedCopy32(const void *src, void *dst) { + UNALIGNED_STORE32(dst, UNALIGNED_LOAD32(src)); +} + +inline void UnalignedCopy64(const void *src, void *dst) { + if (sizeof(void *) == 8) { + UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src)); + } else { + const char *src_char = reinterpret_cast(src); + char *dst_char = reinterpret_cast(dst); + + UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char)); + UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4)); + } +} + +#endif // defined(__cplusplus), end of unaligned API + +// aligned_malloc, aligned_free +#if defined(__ANDROID__) || defined(__ASYLO__) || defined(_WIN32) +#include // for memalign() +#endif + +// __ASYLO__ platform uses newlib without an underlying OS, which provides +// memalign, but not posix_memalign. +#if defined(__cplusplus) && \ + (((defined(__GNUC__) || defined(__APPLE__) || \ + defined(__NVCC__)) && \ + !defined(SWIG)) || \ + ((__GNUC__ >= 3 || defined(__clang__)) && defined(__ANDROID__)) || \ + defined(__ASYLO__)) +inline void *aligned_malloc(size_t size, size_t minimum_alignment) { +#if defined(__ANDROID__) || defined(OS_ANDROID) || defined(__ASYLO__) || defined(_WIN32) || defined(__sun) || defined(sun) +# if defined(_WIN32) + return _aligned_malloc(size, minimum_alignment); +# else + return memalign(minimum_alignment, size); +# endif +#else // !__ANDROID__ && !OS_ANDROID && !__ASYLO__ + // posix_memalign requires that the requested alignment be at least + // sizeof(void*). In this case, fall back on malloc which should return memory + // aligned to at least the size of a pointer. + const size_t required_alignment = sizeof(void*); + if (minimum_alignment < required_alignment) + return malloc(size); + void *ptr = nullptr; + if (posix_memalign(&ptr, minimum_alignment, size) == 0) + return ptr; + return nullptr; +#endif +} + +inline void aligned_free(void *aligned_memory) { + free(aligned_memory); +} + +#elif defined(_MSC_VER) // MSVC + +inline void *aligned_malloc(size_t size, size_t minimum_alignment) { + return _aligned_malloc(size, minimum_alignment); +} + +inline void aligned_free(void *aligned_memory) { + _aligned_free(aligned_memory); +} + +#endif // aligned_malloc, aligned_free + +// ALIGNED_CHAR_ARRAY +// +// Provides a char array with the exact same alignment as another type. The +// first parameter must be a complete type, the second parameter is how many +// of that type to provide space for. +// +// ALIGNED_CHAR_ARRAY(struct stat, 16) storage_; +// +#if defined(__cplusplus) +#undef ALIGNED_CHAR_ARRAY +// Because MSVC and older GCCs require that the argument to their alignment +// construct to be a literal constant integer, we use a template instantiated +// at all the possible powers of two. +#ifndef SWIG +template struct AlignType { }; +template struct AlignType<0, size> { typedef char result[size]; }; +#if defined(_MSC_VER) +#define BASE_PORT_H_ALIGN_ATTRIBUTE(X) __declspec(align(X)) +#define BASE_PORT_H_ALIGN_OF(T) __alignof(T) +#elif defined(__GNUC__) || defined(__INTEL_COMPILER) +#define BASE_PORT_H_ALIGN_ATTRIBUTE(X) __attribute__((aligned(X))) +#define BASE_PORT_H_ALIGN_OF(T) __alignof__(T) +#endif + +#if defined(BASE_PORT_H_ALIGN_ATTRIBUTE) + +#define BASE_PORT_H_ALIGNTYPE_TEMPLATE(X) \ + template struct AlignType { \ + typedef BASE_PORT_H_ALIGN_ATTRIBUTE(X) char result[size]; \ + } + +BASE_PORT_H_ALIGNTYPE_TEMPLATE(1); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(2); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(4); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(8); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(16); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(32); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(64); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(128); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(256); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(512); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(1024); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(2048); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(4096); +BASE_PORT_H_ALIGNTYPE_TEMPLATE(8192); +// Any larger and MSVC++ will complain. + +#define ALIGNED_CHAR_ARRAY(T, Size) \ + typename AlignType::result + +#undef BASE_PORT_H_ALIGNTYPE_TEMPLATE +#undef BASE_PORT_H_ALIGN_ATTRIBUTE + +#else // defined(BASE_PORT_H_ALIGN_ATTRIBUTE) +#define ALIGNED_CHAR_ARRAY \ + you_must_define_ALIGNED_CHAR_ARRAY_for_your_compiler_in_base_port_h +#endif // defined(BASE_PORT_H_ALIGN_ATTRIBUTE) + +#else // !SWIG + +// SWIG can't represent alignment and doesn't care about alignment on data +// members (it works fine without it). +template +struct AlignType { typedef char result[Size]; }; +#define ALIGNED_CHAR_ARRAY(T, Size) AlignType::result + +// Enough to parse with SWIG, will never be used by running code. +#define BASE_PORT_H_ALIGN_OF(Type) 16 + +#endif // !SWIG +#else // __cplusplus +#define ALIGNED_CHAR_ARRAY ALIGNED_CHAR_ARRAY_is_not_available_without_Cplusplus +#endif // __cplusplus + +#endif // S2_BASE_PORT_H_ diff --git a/inst/include/s2/base/spinlock.h b/inst/include/s2/base/spinlock.h new file mode 100644 index 0000000..5a5a36f --- /dev/null +++ b/inst/include/s2/base/spinlock.h @@ -0,0 +1,60 @@ +// Copyright Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef S2_BASE_SPINLOCK_H_ +#define S2_BASE_SPINLOCK_H_ + +#include + + +class SpinLock { + public: + SpinLock() = default; + ~SpinLock() = default; + SpinLock(SpinLock const&) = delete; + SpinLock& operator=(SpinLock const&) = delete; + + inline void Lock() { + while (locked_.exchange(true, std::memory_order_acquire)) { + // Spin. + continue; + } + } + + inline void Unlock() { + locked_.store(false, std::memory_order_release); + } + + inline bool IsHeld() const { + return locked_.load(std::memory_order_relaxed); + } + + private: + std::atomic_bool locked_{false}; +}; + +class SpinLockHolder { + public: + inline explicit SpinLockHolder(SpinLock* l) : lock_(l) { lock_->Lock(); } + inline ~SpinLockHolder() { lock_->Unlock(); } + + SpinLockHolder(const SpinLockHolder&) = delete; + SpinLockHolder& operator=(const SpinLockHolder&) = delete; + + private: + SpinLock* lock_; +}; + +#endif // S2_BASE_SPINLOCK_H_ diff --git a/inst/include/s2/base/stringprintf.h b/inst/include/s2/base/stringprintf.h new file mode 100644 index 0000000..cb8e775 --- /dev/null +++ b/inst/include/s2/base/stringprintf.h @@ -0,0 +1,53 @@ +// Copyright Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// NOTE: See third_party/absl/strings for more options. +// +// As of 2017q4, most use of these routines is considered legacy: use +// of absl::StrCat, absl::Substitute, or absl::StrFormat is preferred for +// performance and safety reasons. + +#ifndef S2_BASE_STRINGPRINTF_H_ +#define S2_BASE_STRINGPRINTF_H_ + +#include +#include +#include + +#include "s2/base/port.h" + +// Return a C++ string +extern string StringPrintf(const char* format, ...) + // Tell the compiler to do printf format string checking. + ABSL_PRINTF_ATTRIBUTE(1, 2); + +// Store result into a supplied string and return it +extern const string& SStringPrintf(string* dst, const char* format, ...) + // Tell the compiler to do printf format string checking. + ABSL_PRINTF_ATTRIBUTE(2, 3); + +// Append result to a supplied string +extern void StringAppendF(string* dst, const char* format, ...) + // Tell the compiler to do printf format string checking. + ABSL_PRINTF_ATTRIBUTE(2, 3); + +// Lower-level routine that takes a va_list and appends to a specified +// string. All other routines are just convenience wrappers around it. +// +// Implementation note: the va_list is never modified, this implementation +// always operates on copies. +extern void StringAppendV(string* dst, const char* format, va_list ap); + +#endif // S2_BASE_STRINGPRINTF_H_ diff --git a/inst/include/s2/base/strtoint.h b/inst/include/s2/base/strtoint.h new file mode 100644 index 0000000..20aeda7 --- /dev/null +++ b/inst/include/s2/base/strtoint.h @@ -0,0 +1,106 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// +// Architecture-neutral plug compatible replacements for strtol() friends. +// +// Long's have different lengths on ILP-32 and LP-64 platforms, and so overflow +// behavior across the two varies when strtol() and similar are used to parse +// 32-bit integers. Similar problems exist with atoi(), because although it +// has an all-integer interface, it uses strtol() internally, and so suffers +// from the same narrowing problems on assignments to int. +// +// Examples: +// errno = 0; +// i = strtol("3147483647", nullptr, 10); +// printf("%d, errno %d\n", i, errno); +// // 32-bit platform: 2147483647, errno 34 +// // 64-bit platform: -1147483649, errno 0 +// +// printf("%d\n", atoi("3147483647")); +// // 32-bit platform: 2147483647 +// // 64-bit platform: -1147483649 +// +// A way round this is to define local replacements for these, and use them +// instead of the standard libc functions. +// +// In most 32-bit cases the replacements can be inlined away to a call to the +// libc function. In a couple of 64-bit cases, however, adapters are required, +// to provide the right overflow and errno behavior. +// + +#ifndef S2_BASE_STRTOINT_H_ +#define S2_BASE_STRTOINT_H_ + +#include // For strtol* functions. +#include +#include "s2/base/integral_types.h" +#include "s2/base/port.h" +#include "s2/third_party/absl/base/macros.h" + +// Adapter functions for handling overflow and errno. +int32 strto32_adapter(const char *nptr, char **endptr, int base); +uint32 strtou32_adapter(const char *nptr, char **endptr, int base); + +// Conversions to a 32-bit integer can pass the call to strto[u]l on 32-bit +// platforms, but need a little extra work on 64-bit platforms. +inline int32 strto32(const char *nptr, char **endptr, int base) { + if (sizeof(int32) == sizeof(long)) + return static_cast(strtol(nptr, endptr, base)); + else + return strto32_adapter(nptr, endptr, base); +} + +inline uint32 strtou32(const char *nptr, char **endptr, int base) { + if (sizeof(uint32) == sizeof(unsigned long)) + return static_cast(strtoul(nptr, endptr, base)); + else + return strtou32_adapter(nptr, endptr, base); +} + +// For now, long long is 64-bit on all the platforms we care about, so these +// functions can simply pass the call to strto[u]ll. +inline int64 strto64(const char *nptr, char **endptr, int base) { + static_assert(sizeof(int64) == sizeof(long long), + "sizeof int64 is not sizeof long long"); + return strtoll(nptr, endptr, base); +} + +inline uint64 strtou64(const char *nptr, char **endptr, int base) { + static_assert(sizeof(uint64) == sizeof(unsigned long long), + "sizeof uint64 is not sizeof long long"); + return strtoull(nptr, endptr, base); +} + +// Although it returns an int, atoi() is implemented in terms of strtol, and +// so has differing overflow and underflow behavior. atol is the same. +inline int32 atoi32(const char *nptr) { + return strto32(nptr, nullptr, 10); +} + +inline int64 atoi64(const char *nptr) { + return strto64(nptr, nullptr, 10); +} + +// Convenience versions of the above that take a string argument. +inline int32 atoi32(const string &s) { + return atoi32(s.c_str()); +} + +inline int64 atoi64(const string &s) { + return atoi64(s.c_str()); +} + +#endif // S2_BASE_STRTOINT_H_ diff --git a/inst/include/s2/base/timer.h b/inst/include/s2/base/timer.h new file mode 100644 index 0000000..90419c5 --- /dev/null +++ b/inst/include/s2/base/timer.h @@ -0,0 +1,50 @@ +// Copyright Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef S2_BASE_TIMER_H_ +#define S2_BASE_TIMER_H_ + +#include + +#include "s2/base/integral_types.h" + +class CycleTimer { + public: + CycleTimer() = default; + + void Start() { + start_ = Now(); + } + + int64 GetInMs() const { + using msec = std::chrono::milliseconds; + return std::chrono::duration_cast(GetDuration()).count(); + } + + private: + using Clock = std::chrono::high_resolution_clock; + + static Clock::time_point Now() { + return Clock::now(); + } + + Clock::duration GetDuration() const { + return Now() - start_; + } + + Clock::time_point start_; +}; + +#endif // S2_BASE_TIMER_H_ diff --git a/inst/include/s2/encoded_s2cell_id_vector.h b/inst/include/s2/encoded_s2cell_id_vector.h new file mode 100644 index 0000000..afc738c --- /dev/null +++ b/inst/include/s2/encoded_s2cell_id_vector.h @@ -0,0 +1,110 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_ENCODED_S2CELL_ID_VECTOR_H_ +#define S2_ENCODED_S2CELL_ID_VECTOR_H_ + +#include "s2/third_party/absl/types/span.h" +#include "s2/encoded_uint_vector.h" +#include "s2/s2cell_id.h" + +namespace s2coding { + +// Encodes a vector of S2CellIds in a format that can later be decoded as an +// EncodedS2CellIdVector. The S2CellIds do not need to be valid. +// +// REQUIRES: "encoder" uses the default constructor, so that its buffer +// can be enlarged as necessary by calling Ensure(int). +void EncodeS2CellIdVector(absl::Span v, Encoder* encoder); + +// This class represents an encoded vector of S2CellIds. Values are decoded +// only when they are accessed. This allows for very fast initialization and +// no additional memory use beyond the encoded data. The encoded data is not +// owned by this class; typically it points into a large contiguous buffer +// that contains other encoded data as well. +// +// This is one of several helper classes that allow complex data structures to +// be initialized from an encoded format in constant time and then decoded on +// demand. This can be a big performance advantage when only a small part of +// the data structure is actually used. +// +// The S2CellIds do not need to be sorted or at the same level. The +// implementation is biased towards minimizing decoding times rather than +// space usage. +// +// NOTE: If your S2CellIds represent S2Points that have been snapped to +// S2CellId centers, then EncodedS2PointVector is both faster and smaller. +class EncodedS2CellIdVector { + public: + // Constructs an uninitialized object; requires Init() to be called. + EncodedS2CellIdVector() {} + + // Initializes the EncodedS2CellIdVector. + // + // REQUIRES: The Decoder data buffer must outlive this object. + bool Init(Decoder* decoder); + + // Returns the size of the original vector. + size_t size() const; + + // Returns the element at the given index. + S2CellId operator[](int i) const; + + // Returns the index of the first element x such that (x >= target), or + // size() if no such element exists. + // + // REQUIRES: The vector elements are sorted in non-decreasing order. + size_t lower_bound(S2CellId target) const; + + // Decodes and returns the entire original vector. + std::vector Decode() const; + + private: + // Values are decoded as (base_ + (deltas_[i] << shift_)). + EncodedUintVector deltas_; + uint64 base_; + uint8 shift_; +}; + + +////////////////// Implementation details follow //////////////////// + + +inline size_t EncodedS2CellIdVector::size() const { + return deltas_.size(); +} + +inline S2CellId EncodedS2CellIdVector::operator[](int i) const { + return S2CellId((deltas_[i] << shift_) + base_); +} + +inline size_t EncodedS2CellIdVector::lower_bound(S2CellId target) const { + // We optimize the search by converting "target" to the corresponding delta + // value and then searching directly in the deltas_ vector. + // + // To invert operator[], we essentially compute ((target - base_) >> shift_) + // except that we also need to round up when shifting. The first two cases + // ensure that "target" doesn't wrap around past zero when we do this. + if (target.id() <= base_) return 0; + if (target >= S2CellId::End(S2CellId::kMaxLevel)) return size(); + return deltas_.lower_bound( + (target.id() - base_ + (1ULL << shift_) - 1) >> shift_); +} + +} // namespace s2coding + +#endif // S2_ENCODED_S2CELL_ID_VECTOR_H_ diff --git a/inst/include/s2/encoded_s2point_vector.h b/inst/include/s2/encoded_s2point_vector.h new file mode 100644 index 0000000..c692cd7 --- /dev/null +++ b/inst/include/s2/encoded_s2point_vector.h @@ -0,0 +1,149 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_ENCODED_S2POINT_VECTOR_H_ +#define S2_ENCODED_S2POINT_VECTOR_H_ + +#include +#include "s2/third_party/absl/types/span.h" +#include "s2/encoded_string_vector.h" +#include "s2/encoded_uint_vector.h" +#include "s2/s2point.h" + +namespace s2coding { + +// Controls whether to optimize for speed or size when encoding points. (Note +// that encoding is always lossless, and that currently compact encodings are +// only possible when points have been snapped to S2CellId centers.) +enum class CodingHint : uint8 { FAST, COMPACT }; + +// Encodes a vector of S2Points in a format that can later be decoded as an +// EncodedS2PointVector. +// +// REQUIRES: "encoder" uses the default constructor, so that its buffer +// can be enlarged as necessary by calling Ensure(int). +void EncodeS2PointVector(absl::Span points, CodingHint hint, + Encoder* encoder); + +// This class represents an encoded vector of S2Points. Values are decoded +// only when they are accessed. This allows for very fast initialization and +// no additional memory use beyond the encoded data. The encoded data is not +// owned by this class; typically it points into a large contiguous buffer +// that contains other encoded data as well. +// +// This is one of several helper classes that allow complex data structures to +// be initialized from an encoded format in constant time and then decoded on +// demand. This can be a big performance advantage when only a small part of +// the data structure is actually used. +class EncodedS2PointVector { + public: + // Constructs an uninitialized object; requires Init() to be called. + EncodedS2PointVector() {} + + // Initializes the EncodedS2PointVector. + // + // REQUIRES: The Decoder data buffer must outlive this object. + bool Init(Decoder* decoder); + + // Returns the size of the original vector. + size_t size() const; + + // Returns the element at the given index. + S2Point operator[](int i) const; + + // Decodes and returns the entire original vector. + std::vector Decode() const; + + // TODO(ericv): Consider adding a method that returns an adjacent pair of + // points. This would save some decoding overhead. + + private: + friend void EncodeS2PointVector(absl::Span, CodingHint, + Encoder*); + friend void EncodeS2PointVectorFast(absl::Span, Encoder*); + friend void EncodeS2PointVectorCompact(absl::Span, Encoder*); + + bool InitUncompressedFormat(Decoder* decoder); + bool InitCellIdsFormat(Decoder* decoder); + S2Point DecodeCellIdsFormat(int i) const; + + // We use a tagged union to represent multiple formats, as opposed to an + // abstract base class or templating. This represents the best compromise + // between performance, space, and convenience. Note that the overhead of + // checking the tag is trivial and will typically be branch-predicted + // perfectly. + // + // TODO(ericv): Once additional formats have been implemented, consider + // using std::variant<> instead. It's unclear whether this would have + // better or worse performance than the current approach. + + // dd: These structs are anonymous in the upstream S2 code; however, + // this generates CMD-check failure due to the [-Wnested-anon-types] + // (anonymous types declared in an anonymous union are an extension) + // The approach here just names the types. + struct CellIDStruct { + EncodedStringVector blocks; + uint64 base; + uint8 level; + bool have_exceptions; + + // TODO(ericv): Use std::atomic_flag to cache the last point decoded in + // a thread-safe way. This reduces benchmark times for actual polygon + // operations (e.g. S2ClosestEdgeQuery) by about 15%. + }; + + struct UncompressedStruct { + const S2Point* points; + }; + + enum Format : uint8 { + UNCOMPRESSED = 0, + CELL_IDS = 1, + }; + Format format_; + uint32 size_; + union { + struct UncompressedStruct uncompressed_; + struct CellIDStruct cell_ids_; + }; +}; + + +////////////////// Implementation details follow //////////////////// + + +inline size_t EncodedS2PointVector::size() const { + return size_; +} + +inline S2Point EncodedS2PointVector::operator[](int i) const { + switch (format_) { + case Format::UNCOMPRESSED: + return uncompressed_.points[i]; + + case Format::CELL_IDS: + return DecodeCellIdsFormat(i); + + default: + S2_LOG(DFATAL) << "Unrecognized format"; + return S2Point(); + } +} + +} // namespace s2coding + +#endif // S2_ENCODED_S2POINT_VECTOR_H_ diff --git a/inst/include/s2/encoded_s2shape_index.h b/inst/include/s2/encoded_s2shape_index.h new file mode 100644 index 0000000..3232474 --- /dev/null +++ b/inst/include/s2/encoded_s2shape_index.h @@ -0,0 +1,276 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_ENCODED_S2SHAPE_INDEX_H_ +#define S2_ENCODED_S2SHAPE_INDEX_H_ + +#include "s2/encoded_s2cell_id_vector.h" +#include "s2/encoded_string_vector.h" +#include "s2/mutable_s2shape_index.h" + +class EncodedS2ShapeIndex final : public S2ShapeIndex { + public: + using Options = MutableS2ShapeIndex::Options; + using ShapeFactory = S2ShapeIndex::ShapeFactory; + + // Creates an index that must be initialized by calling Init(). + EncodedS2ShapeIndex(); + + ~EncodedS2ShapeIndex() override; + + // Initializes the EncodedS2ShapeIndex, returning true on success. + // + // This method does not decode the S2Shape objects in the index; this is + // the responsibility of the client-provided function "shape_factory" + // (see s2shapeutil_coding.h). Example usage: + // + // index.Init(decoder, s2shapeutil::LazyDecodeShapeFactory(decoder)); + // + // Note that the encoded shape vector must *precede* the encoded S2ShapeIndex + // in the Decoder's data buffer in this example. + bool Init(Decoder* decoder, const ShapeFactory& shape_factory); + + const Options& options() const { return options_; } + + // The number of distinct shape ids in the index. This equals the number of + // shapes in the index provided that no shapes have ever been removed. + // (Shape ids are not reused.) + int num_shape_ids() const override { return shapes_.size(); } + + // Return a pointer to the shape with the given id, or nullptr if the shape + // has been removed from the index. + S2Shape* shape(int id) const override; + + // Minimizes memory usage by requesting that any data structures that can be + // rebuilt should be discarded. This method invalidates all iterators. + // + // Like all non-const methods, this method is not thread-safe. + void Minimize() override; + + class Iterator final : public IteratorBase { + public: + // Default constructor; must be followed by a call to Init(). + Iterator(); + + // Constructs an iterator positioned as specified. By default iterators + // are unpositioned, since this avoids an extra seek in this situation + // where one of the seek methods (such as Locate) is immediately called. + // + // If you want to position the iterator at the beginning, e.g. in order to + // loop through the entire index, do this instead: + // + // for (EncodedS2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN); + // !it.done(); it.Next()) { ... } + explicit Iterator(const EncodedS2ShapeIndex* index, + InitialPosition pos = UNPOSITIONED); + + // Initializes an iterator for the given EncodedS2ShapeIndex. + void Init(const EncodedS2ShapeIndex* index, + InitialPosition pos = UNPOSITIONED); + + // Inherited non-virtual methods: + // S2CellId id() const; + // const S2ShapeIndexCell& cell() const; + // bool done() const; + // S2Point center() const; + + // IteratorBase API: + void Begin() override; + void Finish() override; + void Next() override; + bool Prev() override; + void Seek(S2CellId target) override; + bool Locate(const S2Point& target) override; + CellRelation Locate(S2CellId target) override; + + protected: + const S2ShapeIndexCell* GetCell() const override; + std::unique_ptr Clone() const override; + void Copy(const IteratorBase& other) override; + + private: + void Refresh(); // Updates the IteratorBase fields. + const EncodedS2ShapeIndex* index_; + int32 cell_pos_; // Current position in the vector of index cells. + int32 num_cells_; + }; + + // Returns the number of bytes currently occupied by the index (including any + // unused space at the end of vectors, etc). It has the same thread safety + // as the other "const" methods (see introduction). + size_t SpaceUsed() const override; + + protected: + std::unique_ptr NewIterator(InitialPosition pos) const override; + + private: + friend class Iterator; + + // Returns a value indicating that a shape has not been decoded yet. + inline static S2Shape* kUndecodedShape() { + return reinterpret_cast(1); + } + + // Like std::atomic, but defaults to kUndecodedShape(). + class AtomicShape : public std::atomic { + public: + AtomicShape() : std::atomic(kUndecodedShape()) {} + }; + + S2Shape* GetShape(int id) const; + const S2ShapeIndexCell* GetCell(int i) const; + bool cell_decoded(int i) const; + bool test_and_set_cell_decoded(int i) const; + int max_cell_cache_size() const; + + std::unique_ptr shape_factory_; + + // The options specified for this index. + Options options_; + + // A vector containing all shapes in the index. Initially all shapes are + // set to kUndecodedShape(); as shapes are decoded, they are added to the + // vector using std::atomic::compare_exchange_strong. + mutable std::vector shapes_; + + // A vector containing the S2CellIds of each cell in the index. + s2coding::EncodedS2CellIdVector cell_ids_; + + // A vector containing the encoded contents of each cell in the index. + s2coding::EncodedStringVector encoded_cells_; + + // A raw array containing the decoded contents of each cell in the index. + // Initially all values are *uninitialized memory*. The cells_decoded_ + // field below keeps track of which elements are present. + mutable std::unique_ptr[]> cells_; + + // A bit vector indicating which elements of cells_ have been decoded. + // All other elements of cells_ contain uninitialized (random) memory. + mutable std::vector> cells_decoded_; + + // In order to minimize destructor time when very few cells of a large + // S2ShapeIndex are needed, we keep track of the indices of the first few + // cells to be decoded. This lets us avoid scanning the cells_decoded_ + // vector when the number of cells decoded is very small. + mutable std::vector cell_cache_; + + // Protects all updates to cells_ and cells_decoded_. + mutable SpinLock cells_lock_; + + EncodedS2ShapeIndex(const EncodedS2ShapeIndex&) = delete; + void operator=(const EncodedS2ShapeIndex&) = delete; +}; + + +////////////////// Implementation details follow //////////////////// + + +inline EncodedS2ShapeIndex::Iterator::Iterator() : index_(nullptr) { +} + +inline EncodedS2ShapeIndex::Iterator::Iterator( + const EncodedS2ShapeIndex* index, InitialPosition pos) { + Init(index, pos); +} + +inline void EncodedS2ShapeIndex::Iterator::Init( + const EncodedS2ShapeIndex* index, InitialPosition pos) { + index_ = index; + num_cells_ = index->cell_ids_.size(); + cell_pos_ = (pos == BEGIN) ? 0 : num_cells_; + Refresh(); +} + +inline void EncodedS2ShapeIndex::Iterator::Refresh() { + if (cell_pos_ == num_cells_) { + set_finished(); + } else { + // It's faster to initialize the cell to nullptr even if it has already + // been decoded, since algorithms frequently don't need it (i.e., based on + // the S2CellId they might not need to look at the cell contents). + set_state(index_->cell_ids_[cell_pos_], nullptr); + } +} + +inline void EncodedS2ShapeIndex::Iterator::Begin() { + cell_pos_ = 0; + Refresh(); +} + +inline void EncodedS2ShapeIndex::Iterator::Finish() { + cell_pos_ = num_cells_; + Refresh(); +} + +inline void EncodedS2ShapeIndex::Iterator::Next() { + S2_DCHECK(!done()); + ++cell_pos_; + Refresh(); +} + +inline bool EncodedS2ShapeIndex::Iterator::Prev() { + if (cell_pos_ == 0) return false; + --cell_pos_; + Refresh(); + return true; +} + +inline void EncodedS2ShapeIndex::Iterator::Seek(S2CellId target) { + cell_pos_ = index_->cell_ids_.lower_bound(target); + Refresh(); +} + +inline std::unique_ptr +EncodedS2ShapeIndex::NewIterator(InitialPosition pos) const { + return absl::make_unique(this, pos); +} + +inline S2Shape* EncodedS2ShapeIndex::shape(int id) const { + S2Shape* shape = shapes_[id].load(std::memory_order_relaxed); + if (shape != kUndecodedShape()) return shape; + return GetShape(id); +} + +// Returns true if the given cell has been decoded yet. +inline bool EncodedS2ShapeIndex::cell_decoded(int i) const { + uint64 group_bits = cells_decoded_[i >> 6].load(std::memory_order_relaxed); + return (group_bits & (1ULL << (i & 63))) != 0; +} + +// Marks the given cell as decoded and returns true if it was already marked. +inline bool EncodedS2ShapeIndex::test_and_set_cell_decoded(int i) const { + std::atomic* group = &cells_decoded_[i >> 6]; + uint64 group_bits = group->load(std::memory_order_relaxed); + uint64 test_bit = 1ULL << (i & 63); + group->store(group_bits | test_bit, std::memory_order_relaxed); + return (group_bits & test_bit) != 0; +} + +inline int EncodedS2ShapeIndex::max_cell_cache_size() const { + // The cell cache is sized so that scanning decoded_cells_ in the destructor + // costs about 30 cycles per decoded cell in the worst case. (This overhead + // is acceptable relative to the other costs of decoding each cell.) + // + // For example, if there are 65,536 cells then we won't need to scan + // encoded_cells_ unless we decode at least (65536/2048) == 32 cells. It + // takes about 1 cycle per 64 cells to scan encoded_cells_, so that works + // out to (65536/64) == 1024 cycles. However this cost is amortized over + // the 32 cells decoded, which works out to 32 cycles per cell. + return cell_ids_.size() >> 11; +} + +#endif // S2_ENCODED_S2SHAPE_INDEX_H_ diff --git a/inst/include/s2/encoded_string_vector.h b/inst/include/s2/encoded_string_vector.h new file mode 100644 index 0000000..bd6984c --- /dev/null +++ b/inst/include/s2/encoded_string_vector.h @@ -0,0 +1,164 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_ENCODED_STRING_VECTOR_H_ +#define S2_ENCODED_STRING_VECTOR_H_ + +#include +#include +#include "s2/third_party/absl/strings/string_view.h" +#include "s2/third_party/absl/types/span.h" +#include "s2/encoded_uint_vector.h" + +namespace s2coding { + +// This class allows an EncodedStringVector to be created by adding strings +// incrementally. It also supports adding strings that are the output of +// another Encoder. For example, to create a vector of encoded S2Polygons, +// you can do this: +// +// void EncodePolygons(const vector& polygons, Encoder* encoder) { +// StringVectorEncoder encoded_polygons; +// for (auto polygon : polygons) { +// polygon->Encode(encoded_polygons.AddViaEncoder()); +// } +// encoded_polygons.Encode(encoder); +// } +class StringVectorEncoder { + public: + StringVectorEncoder(); + + // Adds a string to the encoded vector. + void Add(const string& str); + + // Adds a string to the encoded vector by means of the given Encoder. The + // string consists of all output added to the encoder before the next call + // to any method of this class (after which the encoder is no longer valid). + Encoder* AddViaEncoder(); + + // Appends the EncodedStringVector representation to the given Encoder. + // + // REQUIRES: "encoder" uses the default constructor, so that its buffer + // can be enlarged as necessary by calling Ensure(int). + void Encode(Encoder* encoder); + + // Encodes a vector of strings in a format that can later be decoded as an + // EncodedStringVector. + // + // REQUIRES: "encoder" uses the default constructor, so that its buffer + // can be enlarged as necessary by calling Ensure(int). + static void Encode(absl::Span v, Encoder* encoder); + + private: + // A vector consisting of the starting offset of each string in the + // encoder's data buffer, plus a final entry pointing just past the end of + // the last string. + std::vector offsets_; + Encoder data_; +}; + +// This class represents an encoded vector of strings. Values are decoded +// only when they are accessed. This allows for very fast initialization and +// no additional memory use beyond the encoded data. The encoded data is not +// owned by this class; typically it points into a large contiguous buffer +// that contains other encoded data as well. +// +// This is one of several helper classes that allow complex data structures to +// be initialized from an encoded format in constant time and then decoded on +// demand. This can be a big performance advantage when only a small part of +// the data structure is actually used. +class EncodedStringVector { + public: + // Constructs an uninitialized object; requires Init() to be called. + EncodedStringVector() {} + + // Initializes the EncodedStringVector. Returns false on errors, leaving + // the vector in an unspecified state. + // + // REQUIRES: The Decoder data buffer must outlive this object. + bool Init(Decoder* decoder); + + // Resets the vector to be empty. + void Clear(); + + // Returns the size of the original vector. + size_t size() const; + + // Returns the string at the given index. + absl::string_view operator[](int i) const; + + // Returns a Decoder initialized with the string at the given index. + Decoder GetDecoder(int i) const; + + // Returns a pointer to the start of the string at the given index. This is + // faster than operator[] but returns an unbounded string. + const char* GetStart(int i) const; + + // Returns the entire vector of original strings. Requires that the + // data buffer passed to the constructor persists until the result vector is + // no longer needed. + std::vector Decode() const; + + private: + EncodedUintVector offsets_; + const char* data_; +}; + + +////////////////// Implementation details follow //////////////////// + + +inline void StringVectorEncoder::Add(const string& str) { + offsets_.push_back(data_.length()); + data_.Ensure(str.size()); + data_.putn(str.data(), str.size()); +} + +inline Encoder* StringVectorEncoder::AddViaEncoder() { + offsets_.push_back(data_.length()); + return &data_; +} + +inline void EncodedStringVector::Clear() { + offsets_.Clear(); + data_ = nullptr; +} + +inline size_t EncodedStringVector::size() const { + return offsets_.size(); +} + +inline absl::string_view EncodedStringVector::operator[](int i) const { + uint64 start = (i == 0) ? 0 : offsets_[i - 1]; + uint64 limit = offsets_[i]; + return absl::string_view(data_ + start, limit - start); +} + +inline Decoder EncodedStringVector::GetDecoder(int i) const { + uint64 start = (i == 0) ? 0 : offsets_[i - 1]; + uint64 limit = offsets_[i]; + return Decoder(data_ + start, limit - start); +} + +inline const char* EncodedStringVector::GetStart(int i) const { + uint64 start = (i == 0) ? 0 : offsets_[i - 1]; + return data_ + start; +} + +} // namespace s2coding + +#endif // S2_ENCODED_STRING_VECTOR_H_ diff --git a/inst/include/s2/encoded_uint_vector.h b/inst/include/s2/encoded_uint_vector.h new file mode 100644 index 0000000..e0daf7e --- /dev/null +++ b/inst/include/s2/encoded_uint_vector.h @@ -0,0 +1,299 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_ENCODED_UINT_VECTOR_H_ +#define S2_ENCODED_UINT_VECTOR_H_ + +#include +#include +#include "s2/third_party/absl/base/internal/unaligned_access.h" +#include "s2/third_party/absl/types/span.h" +#include "s2/util/coding/coder.h" + +namespace s2coding { + +// Encodes a vector of unsigned integers in a format that can later be +// decoded as an EncodedUintVector. +// +// REQUIRES: T is an unsigned integer type. +// REQUIRES: 2 <= sizeof(T) <= 8 +// REQUIRES: "encoder" uses the default constructor, so that its buffer +// can be enlarged as necessary by calling Ensure(int). +template +void EncodeUintVector(absl::Span v, Encoder* encoder); + +// This class represents an encoded vector of unsigned integers of type T. +// Values are decoded only when they are accessed. This allows for very fast +// initialization and no additional memory use beyond the encoded data. +// The encoded data is not owned by this class; typically it points into a +// large contiguous buffer that contains other encoded data as well. +// +// This is one of several helper classes that allow complex data structures to +// be initialized from an encoded format in constant time and then decoded on +// demand. This can be a big performance advantage when only a small part of +// the data structure is actually used. +// +// Values are encoded using a fixed number of bytes per value, where the +// number of bytes depends on the largest value present. +// +// REQUIRES: T is an unsigned integer type. +// REQUIRES: 2 <= sizeof(T) <= 8 +template +class EncodedUintVector { + public: + static_assert(std::is_unsigned::value, "Unsupported signed integer"); + static_assert(sizeof(T) & 0xe, "Unsupported integer length"); + + // Constructs an uninitialized object; requires Init() to be called. + EncodedUintVector() {} + + // Initializes the EncodedUintVector. Returns false on errors, leaving the + // vector in an unspecified state. + // + // REQUIRES: The Decoder data buffer must outlive this object. + bool Init(Decoder* decoder); + + // Resets the vector to be empty. + void Clear(); + + // Returns the size of the original vector. + size_t size() const; + + // Returns the element at the given index. + T operator[](int i) const; + + // Returns the index of the first element x such that (x >= target), or + // size() if no such element exists. + // + // REQUIRES: The vector elements are sorted in non-decreasing order. + size_t lower_bound(T target) const; + + // Decodes and returns the entire original vector. + std::vector Decode() const; + + private: + template size_t lower_bound(T target) const; + + const char* data_; + uint32 size_; + uint8 len_; +}; + +// Encodes an unsigned integer in little-endian format using "length" bytes. +// (The client must ensure that the encoder's buffer is large enough.) +// +// REQUIRES: T is an unsigned integer type. +// REQUIRES: 2 <= sizeof(T) <= 8 +// REQUIRES: 0 <= length <= sizeof(T) +// REQUIRES: value < 256 ** length +// REQUIRES: encoder->avail() >= length +template +void EncodeUintWithLength(T value, int length, Encoder* encoder); + +// Decodes a variable-length integer consisting of "length" bytes starting at +// "ptr" in little-endian format. +// +// REQUIRES: T is an unsigned integer type. +// REQUIRES: 2 <= sizeof(T) <= 8 +// REQUIRES: 0 <= length <= sizeof(T) +template +T GetUintWithLength(const void* ptr, int length); + +// Decodes and consumes a variable-length integer consisting of "length" bytes +// in little-endian format. Returns false if not enough bytes are available. +// +// REQUIRES: T is an unsigned integer type. +// REQUIRES: 2 <= sizeof(T) <= 8 +// REQUIRES: 0 <= length <= sizeof(T) +template +bool DecodeUintWithLength(int length, Decoder* decoder, T* result); + + +////////////////// Implementation details follow //////////////////// + + +template +inline void EncodeUintWithLength(T value, int length, Encoder* encoder) { + static_assert(std::is_unsigned::value, "Unsupported signed integer"); + static_assert(sizeof(T) & 0xe, "Unsupported integer length"); + S2_DCHECK(length >= 0 && length <= sizeof(T)); + S2_DCHECK_GE(encoder->avail(), length); + + while (--length >= 0) { + encoder->put8(value); + value >>= 8; + } + S2_DCHECK_EQ(value, 0); +} + +template +inline T GetUintWithLength(const char* ptr, int length) { + static_assert(std::is_unsigned::value, "Unsupported signed integer"); + static_assert(sizeof(T) & 0xe, "Unsupported integer length"); + S2_DCHECK(length >= 0 && length <= sizeof(T)); + + // Note that the following code is faster than any of the following: + // + // - A loop that repeatedly loads and shifts one byte. + // - memcpying "length" bytes to a local variable of type T. + // - A switch statement that handles each length optimally. + // + // The following code is slightly faster: + // + // T mask = (length == 0) ? 0 : ~T{0} >> 8 * (sizeof(T) - length); + // return *reinterpret_cast(ptr) & mask; + // + // However this technique is unsafe because in extremely rare cases it might + // access out-of-bounds heap memory. (This can only happen if "ptr" is + // within (sizeof(T) - length) bytes of the end of a memory page and the + // following page in the address space is unmapped.) + + if (length & sizeof(T)) { + if (sizeof(T) == 8) return ABSL_INTERNAL_UNALIGNED_LOAD64(ptr); + if (sizeof(T) == 4) return ABSL_INTERNAL_UNALIGNED_LOAD32(ptr); + if (sizeof(T) == 2) return ABSL_INTERNAL_UNALIGNED_LOAD16(ptr); + S2_DCHECK_EQ(sizeof(T), 1); + return *ptr; + } + T x = 0; + ptr += length; + if (sizeof(T) > 4 && (length & 4)) { + x = ABSL_INTERNAL_UNALIGNED_LOAD32(ptr -= sizeof(uint32)); + } + if (sizeof(T) > 2 && (length & 2)) { + x = (x << 16) + ABSL_INTERNAL_UNALIGNED_LOAD16(ptr -= sizeof(uint16)); + } + if (sizeof(T) > 1 && (length & 1)) { + x = (x << 8) + static_cast(*--ptr); + } + return x; +} + +template +bool DecodeUintWithLength(int length, Decoder* decoder, T* result) { + if (decoder->avail() < length) return false; + const char* ptr = reinterpret_cast(decoder->ptr()); + *result = GetUintWithLength(ptr, length); + decoder->skip(length); + return true; +} + +template +void EncodeUintVector(absl::Span v, Encoder* encoder) { + // The encoding is as follows: + // + // varint64: (v.size() * sizeof(T)) | (len - 1) + // array of v.size() elements ["len" bytes each] + // + // Note that we don't allow (len == 0) since this would require an extra bit + // to encode the length. + + T one_bits = 1; // Ensures len >= 1. + for (auto x : v) one_bits |= x; + int len = (Bits::FindMSBSetNonZero64(one_bits) >> 3) + 1; + S2_DCHECK(len >= 1 && len <= 8); + + // Note that the multiplication is optimized into a bit shift. + encoder->Ensure(Varint::kMax64 + v.size() * len); + uint64 size_len = (uint64{v.size()} * sizeof(T)) | (len - 1); + encoder->put_varint64(size_len); + for (auto x : v) { + EncodeUintWithLength(x, len, encoder); + } +} + +template +bool EncodedUintVector::Init(Decoder* decoder) { + uint64 size_len; + if (!decoder->get_varint64(&size_len)) return false; + size_ = size_len / sizeof(T); // Optimized into bit shift. + len_ = (size_len & (sizeof(T) - 1)) + 1; + if (size_ > std::numeric_limits::max() / sizeof(T)) return false; + size_t bytes = size_ * len_; + if (decoder->avail() < bytes) return false; + data_ = reinterpret_cast(decoder->ptr()); + decoder->skip(bytes); + return true; +} + +template +void EncodedUintVector::Clear() { + size_ = 0; + data_ = nullptr; +} + +template +inline size_t EncodedUintVector::size() const { + return size_; +} + +template +inline T EncodedUintVector::operator[](int i) const { + S2_DCHECK(i >= 0 && i < size_); + return GetUintWithLength(data_ + i * len_, len_); +} + +template +size_t EncodedUintVector::lower_bound(T target) const { + static_assert(sizeof(T) & 0xe, "Unsupported integer length"); + S2_DCHECK(len_ >= 1 && len_ <= sizeof(T)); + + // TODO(ericv): Consider using the unused 28 bits of "len_" to store the + // last result of lower_bound() to be used as a hint. This should help in + // common situation where the same element is looked up repeatedly. This + // would require declaring the new field (length_lower_bound_hint_) as + // mutable std::atomic (accessed using std::memory_order_relaxed) + // with a custom copy constructor that resets the hint component to zero. + switch (len_) { + case 1: return lower_bound<1>(target); + case 2: return lower_bound<2>(target); + case 3: return lower_bound<3>(target); + case 4: return lower_bound<4>(target); + case 5: return lower_bound<5>(target); + case 6: return lower_bound<6>(target); + case 7: return lower_bound<7>(target); + default: return lower_bound<8>(target); + } +} + +template template +inline size_t EncodedUintVector::lower_bound(T target) const { + size_t lo = 0, hi = size_; + while (lo < hi) { + size_t mid = (lo + hi) >> 1; + T value = GetUintWithLength(data_ + mid * length, length); + if (value < target) { + lo = mid + 1; + } else { + hi = mid; + } + } + return lo; +} + +template +std::vector EncodedUintVector::Decode() const { + std::vector result(size_); + for (int i = 0; i < size_; ++i) { + result[i] = (*this)[i]; + } + return result; +} + +} // namespace s2coding + +#endif // S2_ENCODED_UINT_VECTOR_H_ diff --git a/inst/include/s2/id_set_lexicon.h b/inst/include/s2/id_set_lexicon.h new file mode 100644 index 0000000..25ace84 --- /dev/null +++ b/inst/include/s2/id_set_lexicon.h @@ -0,0 +1,199 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_ID_SET_LEXICON_H_ +#define S2_ID_SET_LEXICON_H_ + +#include +#include + +#include "s2/base/integral_types.h" +#include "s2/base/logging.h" +#include "s2/sequence_lexicon.h" + +// IdSetLexicon is a class for compactly representing sets of non-negative +// integers such as array indices ("id sets"). It is especially suitable when +// either (1) there are many duplicate sets, or (2) there are many singleton +// or empty sets. See also ValueLexicon and SequenceLexicon. +// +// Each distinct id set is mapped to a 32-bit integer. Empty and singleton +// sets take up no additional space whatsoever; the set itself is represented +// by the unique id assigned to the set. Sets of size 2 or more occupy about +// 11 bytes per set plus 4 bytes per element (as compared to 24 bytes per set +// plus 4 bytes per element for std::vector). Duplicate sets are +// automatically eliminated. Note also that id sets are referred to using +// 32-bit integers rather than 64-bit pointers. +// +// This class is especially useful in conjunction with ValueLexicon. For +// example, suppose that you want to label objects with a set of strings. You +// could use a ValueLexicon to map the strings to "label ids" (32-bit +// integers), and then use IdSetLexicon to map each set of labels to a "label +// set id". Each reference to that label set then takes up only 4 bytes. +// +// Example usage: +// +// ValueLexicon labels_; +// IdSetLexicon label_sets_; +// +// int32 GetLabelSet(const vector& label_strings) { +// vector label_ids; +// for (const auto& str : label_strings) { +// label_ids.push_back(labels_.Add(str)); +// } +// return label_sets_.Add(label_ids); +// } +// +// int label_set_id = GetLabelSet(...); +// for (auto id : label_sets_.id_set(label_set_id)) { +// S2_LOG(INFO) << id; +// } +// +// This class is similar to SequenceLexicon, except: +// +// 1. Empty and singleton sets are represented implicitly; they use no space. +// 2. Sets are represented rather than sequences; the ordering of values is +// not important and duplicates are removed. +// 3. The values must be 32-bit non-negative integers (only). +class IdSetLexicon { + public: + IdSetLexicon(); + ~IdSetLexicon(); + + // IdSetLexicon is movable and copyable. + IdSetLexicon(const IdSetLexicon&); + IdSetLexicon& operator=(const IdSetLexicon&); + IdSetLexicon(IdSetLexicon&&); + IdSetLexicon& operator=(IdSetLexicon&&); + + // Clears all data from the lexicon. + void Clear(); + + // Add the given set of integers to the lexicon if it is not already + // present, and return the unique id for this set. "begin" and "end" are + // forward iterators over a sequence of values that can be converted to + // non-negative 32-bit integers. The values are automatically sorted and + // duplicates are removed. Returns a signed integer representing this set. + // + // REQUIRES: All values in [begin, end) are non-negative 32-bit integers. + template + int32 Add(FwdIterator begin, FwdIterator end); + + // Add the given set of integers to the lexicon if it is not already + // present, and return the unique id for this set. This is a convenience + // method equivalent to Add(std::begin(container), std::end(container)). + template + int32 Add(const Container& container); + + // Convenience method that returns the unique id for a singleton set. + // Note that because singleton sets take up no space, this method is + // const. Equivalent to calling Add(&id, &id + 1). + int32 AddSingleton(int32 id) const; + + // Convenience method that returns the unique id for the empty set. Note + // that because the empty set takes up no space and has a fixed id, this + // method is static. Equivalent to calling Add() with an empty container. + static int32 EmptySetId(); + + // Iterator type; please treat this as an opaque forward iterator. + using Iterator = const int32*; + + // This class represents a set of integers stored in the IdSetLexicon. + class IdSet { + public: + Iterator begin() const; + Iterator end() const; + size_t size() const; + + private: + friend class IdSetLexicon; + IdSet(); + IdSet(Iterator begin, Iterator end); + explicit IdSet(int32 singleton_id); + Iterator begin_, end_; + int32 singleton_id_; + }; + // Return the set of integers corresponding to an id returned by Add(). + IdSet id_set(int32 set_id) const; + + private: + // Choose kEmptySetId to be the last id that will ever be generated. + // (Non-negative ids are reserved for singleton sets.) + static const int32 kEmptySetId = std::numeric_limits::min(); + int32 AddInternal(std::vector* ids); + + SequenceLexicon id_sets_; + + std::vector tmp_; // temporary storage used during Add() +}; + + +////////////////// Implementation details follow //////////////////// + + +inline IdSetLexicon::Iterator IdSetLexicon::IdSet::begin() const { + return begin_; +} + +inline IdSetLexicon::Iterator IdSetLexicon::IdSet::end() const { + return end_; +} + +inline size_t IdSetLexicon::IdSet::size() const { + return end_ - begin_; +} + +inline IdSetLexicon::IdSet::IdSet() + : begin_(&singleton_id_), end_(begin_) { +} + +inline IdSetLexicon::IdSet::IdSet(Iterator begin, Iterator end) + : begin_(begin), end_(end) { +} + +inline IdSetLexicon::IdSet::IdSet(int32 singleton_id) + : begin_(&singleton_id_), end_(&singleton_id_ + 1), + singleton_id_(singleton_id) { +} + +inline int32 IdSetLexicon::AddSingleton(int32 id) const { + S2_DCHECK_GE(id, 0); + S2_DCHECK_LE(id, std::numeric_limits::max()); + // Singleton sets are represented by their element. + return id; +} + +/*static*/ inline int32 IdSetLexicon::EmptySetId() { + return kEmptySetId; +} + +template +int32 IdSetLexicon::Add(FwdIterator begin, FwdIterator end) { + tmp_.clear(); + for (; begin != end; ++begin) { + S2_DCHECK_GE(*begin, 0); + S2_DCHECK_LE(*begin, std::numeric_limits::max()); + tmp_.push_back(*begin); + } + return AddInternal(&tmp_); +} + +template +int32 IdSetLexicon::Add(const Container& container) { + return Add(std::begin(container), std::end(container)); +} + +#endif // S2_ID_SET_LEXICON_H_ diff --git a/inst/include/s2/mutable_s2shape_index.h b/inst/include/s2/mutable_s2shape_index.h new file mode 100644 index 0000000..e65363b --- /dev/null +++ b/inst/include/s2/mutable_s2shape_index.h @@ -0,0 +1,600 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_MUTABLE_S2SHAPE_INDEX_H_ +#define S2_MUTABLE_S2SHAPE_INDEX_H_ + +#include +#include +#include +#include +#include +#include + + +#include "s2/base/integral_types.h" +#include "s2/base/logging.h" +#include "s2/base/mutex.h" +#include "s2/base/spinlock.h" +#include "s2/_fp_contract_off.h" +#include "s2/s2cell_id.h" +#include "s2/s2pointutil.h" +#include "s2/s2shape.h" +#include "s2/s2shape_index.h" +#include "s2/third_party/absl/base/macros.h" +#include "s2/third_party/absl/base/thread_annotations.h" +#include "s2/third_party/absl/memory/memory.h" +#include "s2/util/gtl/btree_map.h" + +// MutableS2ShapeIndex is a class for in-memory indexing of polygonal geometry. +// The objects in the index are known as "shapes", and may consist of points, +// polylines, and/or polygons, possibly overlapping. The index makes it very +// fast to answer queries such as finding nearby shapes, measuring distances, +// testing for intersection and containment, etc. +// +// MutableS2ShapeIndex allows not only building an index, but also updating it +// incrementally by adding or removing shapes (hence its name). It is one of +// several implementations of the S2ShapeIndex interface. MutableS2ShapeIndex +// is designed to be compact; usually it is smaller than the underlying +// geometry being indexed. It is capable of indexing up to hundreds of +// millions of edges. The index is also fast to construct. +// +// There are a number of built-in classes that work with S2ShapeIndex objects. +// Generally these classes accept any collection of geometry that can be +// represented by an S2ShapeIndex, i.e. any combination of points, polylines, +// and polygons. Such classes include: +// +// - S2ContainsPointQuery: returns the shape(s) that contain a given point. +// +// - S2ClosestEdgeQuery: returns the closest edge(s) to a given point, edge, +// S2CellId, or S2ShapeIndex. +// +// - S2CrossingEdgeQuery: returns the edge(s) that cross a given edge. +// +// - S2BooleanOperation: computes boolean operations such as union, +// and boolean predicates such as containment. +// +// - S2ShapeIndexRegion: computes approximations for a collection of geometry. +// +// - S2ShapeIndexBufferedRegion: computes approximations that have been +// expanded by a given radius. +// +// Here is an example showing how to build an index for a set of polygons, and +// then then determine which polygon(s) contain each of a set of query points: +// +// void TestContainment(const vector& points, +// const vector& polygons) { +// MutableS2ShapeIndex index; +// for (auto polygon : polygons) { +// index.Add(absl::make_unique(polygon)); +// } +// auto query = MakeS2ContainsPointQuery(&index); +// for (const auto& point : points) { +// for (S2Shape* shape : query.GetContainingShapes(point)) { +// S2Polygon* polygon = polygons[shape->id()]; +// ... do something with (point, polygon) ... +// } +// } +// } +// +// This example uses S2Polygon::Shape, which is one example of an S2Shape +// object. S2Polyline and S2Loop also have nested Shape classes, and there are +// additional S2Shape types defined in *_shape.h. +// +// Internally, MutableS2ShapeIndex is essentially a map from S2CellIds to the +// set of shapes that intersect each S2CellId. It is adaptively refined to +// ensure that no cell contains more than a small number of edges. +// +// For efficiency, updates are batched together and applied lazily on the +// first subsequent query. Locking is used to ensure that MutableS2ShapeIndex +// has the same thread-safety properties as "vector": const methods are +// thread-safe, while non-const methods are not thread-safe. This means that +// if one thread updates the index, you must ensure that no other thread is +// reading or updating the index at the same time. +// +// TODO(ericv): MutableS2ShapeIndex has an Encode() method that allows the +// index to be serialized. An encoded S2ShapeIndex can be decoded either into +// its original form (MutableS2ShapeIndex) or into an EncodedS2ShapeIndex. +// The key property of EncodedS2ShapeIndex is that it can be constructed +// instantaneously, since the index is kept in its original encoded form. +// Data is decoded only when an operation needs it. For example, to determine +// which shapes(s) contain a given query point only requires decoding the data +// in the S2ShapeIndexCell that contains that point. +class MutableS2ShapeIndex final : public S2ShapeIndex { + private: + using CellMap = gtl::btree_map; + + public: + // Options that affect construction of the MutableS2ShapeIndex. + class Options { + public: + Options(); + + // The maximum number of edges per cell. If a cell has more than this + // many edges that are not considered "long" relative to the cell size, + // then it is subdivided. (Whether an edge is considered "long" is + // controlled by --s2shape_index_cell_size_to_long_edge_ratio flag.) + // + // Values between 10 and 50 represent a reasonable balance between memory + // usage, construction time, and query time. Small values make queries + // faster, while large values make construction faster and use less memory. + // Values higher than 50 do not save significant additional memory, and + // query times can increase substantially, especially for algorithms that + // visit all pairs of potentially intersecting edges (such as polygon + // validation), since this is quadratic in the number of edges per cell. + // + // Note that the *average* number of edges per cell is generally slightly + // less than half of the maximum value defined here. + // + // Defaults to value given by --s2shape_index_default_max_edges_per_cell. + int max_edges_per_cell() const { return max_edges_per_cell_; } + void set_max_edges_per_cell(int max_edges_per_cell); + + private: + int max_edges_per_cell_; + }; + + // Creates a MutableS2ShapeIndex that uses the default option settings. + // Option values may be changed by calling Init(). + MutableS2ShapeIndex(); + + // Create a MutableS2ShapeIndex with the given options. + explicit MutableS2ShapeIndex(const Options& options); + + ~MutableS2ShapeIndex() override; + + // Initialize a MutableS2ShapeIndex with the given options. This method may + // only be called when the index is empty (i.e. newly created or Reset() has + // just been called). + void Init(const Options& options); + + const Options& options() const { return options_; } + + // The number of distinct shape ids that have been assigned. This equals + // the number of shapes in the index provided that no shapes have ever been + // removed. (Shape ids are not reused.) + int num_shape_ids() const override { + return static_cast(shapes_.size()); + } + + // Returns a pointer to the shape with the given id, or nullptr if the shape + // has been removed from the index. + S2Shape* shape(int id) const override { return shapes_[id].get(); } + + // Minimizes memory usage by requesting that any data structures that can be + // rebuilt should be discarded. This method invalidates all iterators. + // + // Like all non-const methods, this method is not thread-safe. + void Minimize() override; + + // Appends an encoded representation of the S2ShapeIndex to "encoder". + // + // This method does not encode the S2Shapes in the index; it is the client's + // responsibility to encode them separately. For example: + // + // s2shapeutil::CompactEncodeTaggedShapes(index, encoder); + // index.Encode(encoder); + // + // REQUIRES: "encoder" uses the default constructor, so that its buffer + // can be enlarged as necessary by calling Ensure(int). + void Encode(Encoder* encoder) const; + + // Decodes an S2ShapeIndex, returning true on success. + // + // This method does not decode the S2Shape objects in the index; this is + // the responsibility of the client-provided function "shape_factory" + // (see s2shapeutil_coding.h). Example usage: + // + // index.Init(decoder, s2shapeutil::LazyDecodeShapeFactory(decoder)); + // + // Note that the S2Shape vector must be encoded *before* the S2ShapeIndex in + // this example. + bool Init(Decoder* decoder, const ShapeFactory& shape_factory); + + class Iterator final : public IteratorBase { + public: + // Default constructor; must be followed by a call to Init(). + Iterator(); + + // Constructs an iterator positioned as specified. By default iterators + // are unpositioned, since this avoids an extra seek in this situation + // where one of the seek methods (such as Locate) is immediately called. + // + // If you want to position the iterator at the beginning, e.g. in order to + // loop through the entire index, do this instead: + // + // for (MutableS2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN); + // !it.done(); it.Next()) { ... } + explicit Iterator(const MutableS2ShapeIndex* index, + InitialPosition pos = UNPOSITIONED); + + // Initializes an iterator for the given MutableS2ShapeIndex. This method + // may also be called in order to restore an iterator to a valid state + // after the underlying index has been updated (although it is usually + // easier just to declare a new iterator whenever required, since iterator + // construction is cheap). + void Init(const MutableS2ShapeIndex* index, + InitialPosition pos = UNPOSITIONED); + + // Initialize an iterator for the given MutableS2ShapeIndex without + // applying any pending updates. This can be used to observe the actual + // current state of the index without modifying it in any way. + void InitStale(const MutableS2ShapeIndex* index, + InitialPosition pos = UNPOSITIONED); + + // Inherited non-virtual methods: + // S2CellId id() const; + // bool done() const; + // S2Point center() const; + const S2ShapeIndexCell& cell() const; + + // IteratorBase API: + void Begin() override; + void Finish() override; + void Next() override; + bool Prev() override; + void Seek(S2CellId target) override; + bool Locate(const S2Point& target) override; + CellRelation Locate(S2CellId target) override; + + protected: + const S2ShapeIndexCell* GetCell() const override; + std::unique_ptr Clone() const override; + void Copy(const IteratorBase& other) override; + + private: + void Refresh(); // Updates the IteratorBase fields. + const MutableS2ShapeIndex* index_; + CellMap::const_iterator iter_, end_; + }; + + // Takes ownership of the given shape and adds it to the index. Also + // assigns a unique id to the shape (shape->id()) and returns that id. + // Shape ids are assigned sequentially starting from 0 in the order shapes + // are added. Invalidates all iterators and their associated data. + int Add(std::unique_ptr shape); + + // Removes the given shape from the index and return ownership to the caller. + // Invalidates all iterators and their associated data. + std::unique_ptr Release(int shape_id); + + // Resets the index to its original state and returns ownership of all + // shapes to the caller. This method is much more efficient than removing + // all shapes one at a time. + std::vector> ReleaseAll(); + + // Resets the index to its original state and deletes all shapes. Any + // options specified via Init() are preserved. + void Clear(); + + // Returns the number of bytes currently occupied by the index (including any + // unused space at the end of vectors, etc). It has the same thread safety + // as the other "const" methods (see introduction). + size_t SpaceUsed() const override; + + // Calls to Add() and Release() are normally queued and processed on the + // first subsequent query (in a thread-safe way). This has many advantages, + // the most important of which is that sometimes there *is* no subsequent + // query, which lets us avoid building the index completely. + // + // This method forces any pending updates to be applied immediately. + // Calling this method is rarely a good idea. (One valid reason is to + // exclude the cost of building the index from benchmark results.) + void ForceBuild(); + + // Returns true if there are no pending updates that need to be applied. + // This can be useful to avoid building the index unnecessarily, or for + // choosing between two different algorithms depending on whether the index + // is available. + // + // The returned index status may be slightly out of date if the index was + // built in a different thread. This is fine for the intended use (as an + // efficiency hint), but it should not be used by internal methods (see + // MaybeApplyUpdates). + bool is_fresh() const; + + protected: + std::unique_ptr NewIterator(InitialPosition pos) const override; + + private: + friend class EncodedS2ShapeIndex; + friend class Iterator; + friend class MutableS2ShapeIndexTest; + friend class S2Stats; + + struct BatchDescriptor; + struct ClippedEdge; + class EdgeAllocator; + struct FaceEdge; + class InteriorTracker; + struct RemovedShape; + + using ShapeIdSet = std::vector; + + // When adding a new encoding, be aware that old binaries will not be able + // to decode it. + static const unsigned char kCurrentEncodingVersionNumber = 0; + + // Internal methods are documented with their definitions. + bool is_first_update() const; + bool is_shape_being_removed(int shape_id) const; + void MaybeApplyUpdates() const; + void ApplyUpdatesThreadSafe(); + void ApplyUpdatesInternal(); + void GetUpdateBatches(std::vector* batches) const; + static void GetBatchSizes(int num_items, int max_batches, + double final_bytes_per_item, + double high_water_bytes_per_item, + double preferred_max_bytes_per_batch, + std::vector* batch_sizes); + void ReserveSpace(const BatchDescriptor& batch, + std::vector all_edges[6]) const; + void AddShape(int id, std::vector all_edges[6], + InteriorTracker* tracker) const; + void RemoveShape(const RemovedShape& removed, + std::vector all_edges[6], + InteriorTracker* tracker) const; + void AddFaceEdge(FaceEdge* edge, std::vector all_edges[6]) const; + void UpdateFaceEdges(int face, const std::vector& face_edges, + InteriorTracker* tracker); + S2CellId ShrinkToFit(const S2PaddedCell& pcell, const R2Rect& bound) const; + void SkipCellRange(S2CellId begin, S2CellId end, InteriorTracker* tracker, + EdgeAllocator* alloc, bool disjoint_from_index); + void UpdateEdges(const S2PaddedCell& pcell, + std::vector* edges, + InteriorTracker* tracker, EdgeAllocator* alloc, + bool disjoint_from_index); + void AbsorbIndexCell(const S2PaddedCell& pcell, + const Iterator& iter, + std::vector* edges, + InteriorTracker* tracker, + EdgeAllocator* alloc); + int GetEdgeMaxLevel(const S2Shape::Edge& edge) const; + static int CountShapes(const std::vector& edges, + const ShapeIdSet& cshape_ids); + bool MakeIndexCell(const S2PaddedCell& pcell, + const std::vector& edges, + InteriorTracker* tracker); + static void TestAllEdges(const std::vector& edges, + InteriorTracker* tracker); + inline static const ClippedEdge* UpdateBound(const ClippedEdge* edge, + int u_end, double u, + int v_end, double v, + EdgeAllocator* alloc); + static const ClippedEdge* ClipUBound(const ClippedEdge* edge, + int u_end, double u, + EdgeAllocator* alloc); + static const ClippedEdge* ClipVBound(const ClippedEdge* edge, + int v_end, double v, + EdgeAllocator* alloc); + static void ClipVAxis(const ClippedEdge* edge, const R1Interval& middle, + std::vector child_edges[2], + EdgeAllocator* alloc); + + // The amount by which cells are "padded" to compensate for numerical errors + // when clipping line segments to cell boundaries. + static const double kCellPadding; + + // The shapes in the index, accessed by their shape id. Removed shapes are + // replaced by nullptr pointers. + std::vector> shapes_; + + // A map from S2CellId to the set of clipped shapes that intersect that + // cell. The cell ids cover a set of non-overlapping regions on the + // sphere. Note that this field is updated lazily (see below). Const + // methods *must* call MaybeApplyUpdates() before accessing this field. + // (The easiest way to achieve this is simply to use an Iterator.) + CellMap cell_map_; + + // The options supplied for this index. + Options options_; + + // The id of the first shape that has been queued for addition but not + // processed yet. + int pending_additions_begin_ = 0; + + // The representation of an edge that has been queued for removal. + struct RemovedShape { + int32 shape_id; + bool has_interior; // Belongs to a shape of dimension 2. + bool contains_tracker_origin; + std::vector edges; + }; + + // The set of shapes that have been queued for removal but not processed + // yet. Note that we need to copy the edge data since the caller is free to + // destroy the shape once Release() has been called. This field is present + // only when there are removed shapes to process (to save memory). + std::unique_ptr> pending_removals_; + + // Additions and removals are queued and processed on the first subsequent + // query. There are several reasons to do this: + // + // - It is significantly more efficient to process updates in batches. + // - Often the index will never be queried, in which case we can save both + // the time and memory required to build it. Examples: + // + S2Loops that are created simply to pass to an S2Polygon. (We don't + // need the S2Loop index, because S2Polygon builds its own index.) + // + Applications that load a database of geometry and then query only + // a small fraction of it. + // + Applications that only read and write geometry (Decode/Encode). + // + // The main drawback is that we need to go to some extra work to ensure that + // "const" methods are still thread-safe. Note that the goal is *not* to + // make this class thread-safe in general, but simply to hide the fact that + // we defer some of the indexing work until query time. + // + // The textbook approach to this problem would be to use a mutex and a + // condition variable. Unfortunately pthread mutexes are huge (40 bytes). + // Instead we use spinlock (which is only 4 bytes) to guard a few small + // fields representing the current update status, and only create additional + // state while the update is actually occurring. + mutable SpinLock lock_; + + enum IndexStatus { + STALE, // There are pending updates. + UPDATING, // Updates are currently being applied. + FRESH, // There are no pending updates. + }; + // Reads and writes to this field are guarded by "lock_". + std::atomic index_status_; + + // UpdateState holds temporary data related to thread synchronization. It + // is only allocated while updates are being applied. + struct UpdateState { + // This mutex is used as a condition variable. It is locked by the + // updating thread for the entire duration of the update; other threads + // lock it in order to wait until the update is finished. + absl::Mutex wait_mutex; + + // The number of threads currently waiting on "wait_mutex_". The + // UpdateState can only be freed when this number reaches zero. + // + // Reads and writes to this field are guarded by "lock_". + int num_waiting; + + UpdateState() : num_waiting(0) { + } + + ~UpdateState() { + S2_DCHECK_EQ(0, num_waiting); + } + }; + std::unique_ptr update_state_; + + // Documented in the .cc file. + void UnlockAndSignal() + UNLOCK_FUNCTION(lock_) + UNLOCK_FUNCTION(update_state_->wait_mutex); + + MutableS2ShapeIndex(const MutableS2ShapeIndex&) = delete; + void operator=(const MutableS2ShapeIndex&) = delete; +}; + + +////////////////// Implementation details follow //////////////////// + + +inline MutableS2ShapeIndex::Iterator::Iterator() : index_(nullptr) { +} + +inline MutableS2ShapeIndex::Iterator::Iterator( + const MutableS2ShapeIndex* index, InitialPosition pos) { + Init(index, pos); +} + +inline void MutableS2ShapeIndex::Iterator::Init( + const MutableS2ShapeIndex* index, InitialPosition pos) { + index->MaybeApplyUpdates(); + InitStale(index, pos); +} + +inline void MutableS2ShapeIndex::Iterator::InitStale( + const MutableS2ShapeIndex* index, InitialPosition pos) { + index_ = index; + end_ = index_->cell_map_.end(); + if (pos == BEGIN) { + iter_ = index_->cell_map_.begin(); + } else { + iter_ = end_; + } + Refresh(); +} + +inline const S2ShapeIndexCell& MutableS2ShapeIndex::Iterator::cell() const { + // Since MutableS2ShapeIndex always sets the "cell_" field, we can skip the + // logic in the base class that conditionally calls GetCell(). + return *raw_cell(); +} + +inline void MutableS2ShapeIndex::Iterator::Refresh() { + if (iter_ == end_) { + set_finished(); + } else { + set_state(iter_->first, iter_->second); + } +} + +inline void MutableS2ShapeIndex::Iterator::Begin() { + // Make sure that the index has not been modified since Init() was called. + S2_DCHECK(index_->is_fresh()); + iter_ = index_->cell_map_.begin(); + Refresh(); +} + +inline void MutableS2ShapeIndex::Iterator::Finish() { + iter_ = end_; + Refresh(); +} + +inline void MutableS2ShapeIndex::Iterator::Next() { + S2_DCHECK(!done()); + ++iter_; + Refresh(); +} + +inline bool MutableS2ShapeIndex::Iterator::Prev() { + if (iter_ == index_->cell_map_.begin()) return false; + --iter_; + Refresh(); + return true; +} + +inline void MutableS2ShapeIndex::Iterator::Seek(S2CellId target) { + iter_ = index_->cell_map_.lower_bound(target); + Refresh(); +} + +inline std::unique_ptr +MutableS2ShapeIndex::NewIterator(InitialPosition pos) const { + return absl::make_unique(this, pos); +} + +inline bool MutableS2ShapeIndex::is_fresh() const { + return index_status_.load(std::memory_order_relaxed) == FRESH; +} + +// Return true if this is the first update to the index. +inline bool MutableS2ShapeIndex::is_first_update() const { + // Note that it is not sufficient to check whether cell_map_ is empty, since + // entries are added during the update process. + return pending_additions_begin_ == 0; +} + +// Given that the given shape is being updated, return true if it is being +// removed (as opposed to being added). +inline bool MutableS2ShapeIndex::is_shape_being_removed(int shape_id) const { + // All shape ids being removed are less than all shape ids being added. + return shape_id < pending_additions_begin_; +} + +// Ensure that any pending updates have been applied. This method must be +// called before accessing the cell_map_ field, even if the index_status_ +// appears to be FRESH, because a memory barrier is required in order to +// ensure that all the index updates are visible if the updates were done in +// another thread. +inline void MutableS2ShapeIndex::MaybeApplyUpdates() const { + // To avoid acquiring and releasing the spinlock on every query, we use + // atomic operations when testing whether the status is FRESH and when + // updating the status to be FRESH. This guarantees that any thread that + // sees a status of FRESH will also see the corresponding index updates. + if (index_status_.load(std::memory_order_acquire) != FRESH) { + const_cast(this)->ApplyUpdatesThreadSafe(); + } +} + +#endif // S2_MUTABLE_S2SHAPE_INDEX_H_ diff --git a/inst/include/s2/r1interval.h b/inst/include/s2/r1interval.h new file mode 100644 index 0000000..f120803 --- /dev/null +++ b/inst/include/s2/r1interval.h @@ -0,0 +1,220 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_R1INTERVAL_H_ +#define S2_R1INTERVAL_H_ + +#include +#include +#include +#include + +#include "s2/base/logging.h" +#include "s2/_fp_contract_off.h" +#include "s2/util/math/vector.h" // IWYU pragma: export + +// An R1Interval represents a closed, bounded interval on the real line. +// It is capable of representing the empty interval (containing no points) +// and zero-length intervals (containing a single point). +// +// This class is intended to be copied by value as desired. It uses +// the default copy constructor and assignment operator. +class R1Interval { + public: + // Constructor. If lo > hi, the interval is empty. + R1Interval(double lo, double hi) : bounds_(lo, hi) {} + + // The default constructor creates an empty interval. (Any interval where + // lo > hi is considered to be empty.) + // + // Note: Don't construct an interval using the default constructor and + // set_lo()/set_hi(), since this technique doesn't work with S1Interval and + // is bad programming style anyways. If you need to set both endpoints, use + // the constructor above: + // + // lat_bounds_ = R1Interval(lat_lo, lat_hi); + R1Interval() : bounds_(1, 0) {} + + // Returns an empty interval. + static R1Interval Empty() { return R1Interval(); } + + // Convenience method to construct an interval containing a single point. + static R1Interval FromPoint(double p) { return R1Interval(p, p); } + + // Convenience method to construct the minimal interval containing + // the two given points. This is equivalent to starting with an empty + // interval and calling AddPoint() twice, but it is more efficient. + static R1Interval FromPointPair(double p1, double p2) { + if (p1 <= p2) { + return R1Interval(p1, p2); + } else { + return R1Interval(p2, p1); + } + } + + // Accessors methods. + double lo() const { return bounds_[0]; } + double hi() const { return bounds_[1]; } + + // Methods to modify one endpoint of an existing R1Interval. Do not use + // these methods if you want to replace both endpoints of the interval; use + // a constructor instead. For example: + // + // *lat_bounds = R1Interval(lat_lo, lat_hi); + void set_lo(double p) { bounds_[0] = p; } + void set_hi(double p) { bounds_[1] = p; } + + // Methods that allow the R1Interval to be accessed as a vector. (The + // recommended style is to use lo() and hi() whenever possible, but these + // methods are useful when the endpoint to be selected is not constant.) + double operator[](int i) const { return bounds_[i]; } + double& operator[](int i) { return bounds_[i]; } + const Vector2_d& bounds() const { return bounds_; } + Vector2_d* mutable_bounds() { return &bounds_; } + + // Return true if the interval is empty, i.e. it contains no points. + bool is_empty() const { return lo() > hi(); } + + // Return the center of the interval. For empty intervals, + // the result is arbitrary. + double GetCenter() const { return 0.5 * (lo() + hi()); } + + // Return the length of the interval. The length of an empty interval + // is negative. + double GetLength() const { return hi() - lo(); } + + bool Contains(double p) const { + return p >= lo() && p <= hi(); + } + + bool InteriorContains(double p) const { + return p > lo() && p < hi(); + } + + // Return true if this interval contains the interval 'y'. + bool Contains(const R1Interval& y) const { + if (y.is_empty()) return true; + return y.lo() >= lo() && y.hi() <= hi(); + } + + // Return true if the interior of this interval contains the entire + // interval 'y' (including its boundary). + bool InteriorContains(const R1Interval& y) const { + if (y.is_empty()) return true; + return y.lo() > lo() && y.hi() < hi(); + } + + // Return true if this interval intersects the given interval, + // i.e. if they have any points in common. + bool Intersects(const R1Interval& y) const { + if (lo() <= y.lo()) { + return y.lo() <= hi() && y.lo() <= y.hi(); + } else { + return lo() <= y.hi() && lo() <= hi(); + } + } + + // Return true if the interior of this interval intersects + // any point of the given interval (including its boundary). + bool InteriorIntersects(const R1Interval& y) const { + return y.lo() < hi() && lo() < y.hi() && lo() < hi() && y.lo() <= y.hi(); + } + + // Return the Hausdorff distance to the given interval 'y'. For two + // R1Intervals x and y, this distance is defined as + // h(x, y) = max_{p in x} min_{q in y} d(p, q). + double GetDirectedHausdorffDistance(const R1Interval& y) const { + if (is_empty()) return 0.0; + if (y.is_empty()) return HUGE_VAL; + return std::max(0.0, std::max(hi() - y.hi(), y.lo() - lo())); + } + + // Expand the interval so that it contains the given point "p". + void AddPoint(double p) { + if (is_empty()) { set_lo(p); set_hi(p); } + else if (p < lo()) { set_lo(p); } // NOLINT + else if (p > hi()) { set_hi(p); } // NOLINT + } + + // Expand the interval so that it contains the given interval "y". + void AddInterval(const R1Interval& y) { + if (y.is_empty()) return; + if (is_empty()) { *this = y; return; } + if (y.lo() < lo()) set_lo(y.lo()); + if (y.hi() > hi()) set_hi(y.hi()); + } + + // Return the closest point in the interval to the given point "p". + // The interval must be non-empty. + double Project(double p) const { + S2_DCHECK(!is_empty()); + return std::max(lo(), std::min(hi(), p)); + } + + // Return an interval that has been expanded on each side by the given + // distance "margin". If "margin" is negative, then shrink the interval on + // each side by "margin" instead. The resulting interval may be empty. Any + // expansion of an empty interval remains empty. + R1Interval Expanded(double margin) const { + if (is_empty()) return *this; + return R1Interval(lo() - margin, hi() + margin); + } + + // Return the smallest interval that contains this interval and the + // given interval "y". + R1Interval Union(const R1Interval& y) const { + if (is_empty()) return y; + if (y.is_empty()) return *this; + return R1Interval(std::min(lo(), y.lo()), std::max(hi(), y.hi())); + } + + // Return the intersection of this interval with the given interval. + // Empty intervals do not need to be special-cased. + R1Interval Intersection(const R1Interval& y) const { + return R1Interval(std::max(lo(), y.lo()), std::min(hi(), y.hi())); + } + + // Return true if two intervals contain the same set of points. + bool operator==(const R1Interval& y) const { + return (lo() == y.lo() && hi() == y.hi()) || (is_empty() && y.is_empty()); + } + + // Return true if two intervals do not contain the same set of points. + bool operator!=(const R1Interval& y) const { + return !operator==(y); + } + + // Return true if this interval can be transformed into the given interval + // by moving each endpoint by at most "max_error". The empty interval is + // considered to be positioned arbitrarily on the real line, thus any + // interval with (length <= 2*max_error) matches the empty interval. + bool ApproxEquals(const R1Interval& y, double max_error = 1e-15) const { + if (is_empty()) return y.GetLength() <= 2 * max_error; + if (y.is_empty()) return GetLength() <= 2 * max_error; + return (std::fabs(y.lo() - lo()) <= max_error && + std::fabs(y.hi() - hi()) <= max_error); + } + + private: + Vector2_d bounds_; +}; + +inline std::ostream& operator<<(std::ostream& os, const R1Interval& x) { + return os << "[" << x.lo() << ", " << x.hi() << "]"; +} + +#endif // S2_R1INTERVAL_H_ diff --git a/inst/include/s2/r2.h b/inst/include/s2/r2.h new file mode 100644 index 0000000..9336e14 --- /dev/null +++ b/inst/include/s2/r2.h @@ -0,0 +1,26 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_R2_H_ +#define S2_R2_H_ + +#include "s2/_fp_contract_off.h" +#include "s2/util/math/vector.h" // IWYU pragma: export + +using R2Point = Vector2_d; + +#endif // S2_R2_H_ diff --git a/inst/include/s2/r2rect.h b/inst/include/s2/r2rect.h new file mode 100644 index 0000000..7700fae --- /dev/null +++ b/inst/include/s2/r2rect.h @@ -0,0 +1,234 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_R2RECT_H_ +#define S2_R2RECT_H_ + +#include + +#include "s2/base/logging.h" +#include "s2/_fp_contract_off.h" +#include "s2/r1interval.h" +#include "s2/r2.h" + +// An R2Rect represents a closed axis-aligned rectangle in the (x,y) plane. +// +// This class is intended to be copied by value as desired. It uses +// the default copy constructor and assignment operator, however it is +// not a "plain old datatype" (POD) because it has virtual functions. +class R2Rect { + public: + // Construct a rectangle from the given lower-left and upper-right points. + R2Rect(const R2Point& lo, const R2Point& hi); + + // Construct a rectangle from the given intervals in x and y. The two + // intervals must either be both empty or both non-empty. + R2Rect(const R1Interval& x, const R1Interval& y); + + // The default constructor creates an empty R2Rect. + R2Rect(); + + // The canonical empty rectangle. Use is_empty() to test for empty + // rectangles, since they have more than one representation. + static R2Rect Empty(); + + // Construct a rectangle from a center point and size in each dimension. + // Both components of size should be non-negative, i.e. this method cannot + // be used to create an empty rectangle. + static R2Rect FromCenterSize(const R2Point& center, const R2Point& size); + + // Convenience method to construct a rectangle containing a single point. + static R2Rect FromPoint(const R2Point& p); + + // Convenience method to construct the minimal bounding rectangle containing + // the two given points. This is equivalent to starting with an empty + // rectangle and calling AddPoint() twice. Note that it is different than + // the R2Rect(lo, hi) constructor, where the first point is always + // used as the lower-left corner of the resulting rectangle. + static R2Rect FromPointPair(const R2Point& p1, const R2Point& p2); + + // Accessor methods. + const R1Interval& x() const { return bounds_[0]; } + const R1Interval& y() const { return bounds_[1]; } + R2Point lo() const { return R2Point(x().lo(), y().lo()); } + R2Point hi() const { return R2Point(x().hi(), y().hi()); } + + // Methods that allow the R2Rect to be accessed as a vector. + const R1Interval& operator[](int i) const { return bounds_[i]; } + R1Interval& operator[](int i) { return bounds_[i]; } + + // Return true if the rectangle is valid, which essentially just means + // that if the bound for either axis is empty then both must be. + bool is_valid() const; + + // Return true if the rectangle is empty, i.e. it contains no points at all. + bool is_empty() const; + + // Return the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order. + // Vertex 0 is in the lower-left corner. For convenience, the argument is + // reduced modulo 4 to the range [0..3]. + R2Point GetVertex(int k) const; + + // Return the vertex in direction "i" along the x-axis (0=left, 1=right) and + // direction "j" along the y-axis (0=down, 1=up). Equivalently, return the + // vertex constructed by selecting endpoint "i" of the x-interval (0=lo, + // 1=hi) and vertex "j" of the y-interval. + R2Point GetVertex(int i, int j) const; + + // Return the center of the rectangle in (x,y)-space. + R2Point GetCenter() const; + + // Return the width and height of this rectangle in (x,y)-space. Empty + // rectangles have a negative width and height. + R2Point GetSize() const; + + // Return true if the rectangle contains the given point. Note that + // rectangles are closed regions, i.e. they contain their boundary. + bool Contains(const R2Point& p) const; + + // Return true if and only if the given point is contained in the interior + // of the region (i.e. the region excluding its boundary). + bool InteriorContains(const R2Point& p) const; + + // Return true if and only if the rectangle contains the given other + // rectangle. + bool Contains(const R2Rect& other) const; + + // Return true if and only if the interior of this rectangle contains all + // points of the given other rectangle (including its boundary). + bool InteriorContains(const R2Rect& other) const; + + // Return true if this rectangle and the given other rectangle have any + // points in common. + bool Intersects(const R2Rect& other) const; + + // Return true if and only if the interior of this rectangle intersects + // any point (including the boundary) of the given other rectangle. + bool InteriorIntersects(const R2Rect& other) const; + + // Expand the rectangle to include the given point. The rectangle is + // expanded by the minimum amount possible. + void AddPoint(const R2Point& p); + + // Expand the rectangle to include the given other rectangle. This is the + // same as replacing the rectangle by the union of the two rectangles, but + // is somewhat more efficient. + void AddRect(const R2Rect& other); + + // Return the closest point in the rectangle to the given point "p". + // The rectangle must be non-empty. + R2Point Project(const R2Point& p) const; + + // Return a rectangle that has been expanded on each side in the x-direction + // by margin.x(), and on each side in the y-direction by margin.y(). If + // either margin is empty, then shrink the interval on the corresponding + // sides instead. The resulting rectangle may be empty. Any expansion of + // an empty rectangle remains empty. + R2Rect Expanded(const R2Point& margin) const; + R2Rect Expanded(double margin) const; + + // Return the smallest rectangle containing the union of this rectangle and + // the given rectangle. + R2Rect Union(const R2Rect& other) const; + + // Return the smallest rectangle containing the intersection of this + // rectangle and the given rectangle. + R2Rect Intersection(const R2Rect& other) const; + + // Return true if two rectangles contains the same set of points. + bool operator==(const R2Rect& other) const; + + // Return true if the x- and y-intervals of the two rectangles are the same + // up to the given tolerance (see r1interval.h for details). + bool ApproxEquals(const R2Rect& other, double max_error = 1e-15) const; + + private: + R1Interval bounds_[2]; +}; + +inline R2Rect::R2Rect(const R2Point& lo, const R2Point& hi) { + bounds_[0] = R1Interval(lo.x(), hi.x()); + bounds_[1] = R1Interval(lo.y(), hi.y()); + S2_DCHECK(is_valid()); +} + +inline R2Rect::R2Rect(const R1Interval& x, const R1Interval& y) { + bounds_[0] = x; + bounds_[1] = y; + S2_DCHECK(is_valid()); +} + +inline R2Rect::R2Rect() { + // The default R1Interval constructor creates an empty interval. + S2_DCHECK(is_valid()); +} + +inline R2Rect R2Rect::Empty() { + return R2Rect(R1Interval::Empty(), R1Interval::Empty()); +} + +inline bool R2Rect::is_valid() const { + // The x/y ranges must either be both empty or both non-empty. + return x().is_empty() == y().is_empty(); +} + +inline bool R2Rect::is_empty() const { + return x().is_empty(); +} + +inline R2Rect R2Rect::FromPoint(const R2Point& p) { + return R2Rect(p, p); +} + +inline R2Point R2Rect::GetVertex(int k) const { + // Twiddle bits to return the points in CCW order (lower left, lower right, + // upper right, upper left). + int j = (k >> 1) & 1; + return GetVertex(j ^ (k & 1), j); +} + +inline R2Point R2Rect::GetVertex(int i, int j) const { + return R2Point(bounds_[0][i], bounds_[1][j]); +} + +inline R2Point R2Rect::GetCenter() const { + return R2Point(x().GetCenter(), y().GetCenter()); +} + +inline R2Point R2Rect::GetSize() const { + return R2Point(x().GetLength(), y().GetLength()); +} + +inline bool R2Rect::Contains(const R2Point& p) const { + return x().Contains(p.x()) && y().Contains(p.y()); +} + +inline bool R2Rect::InteriorContains(const R2Point& p) const { + return x().InteriorContains(p.x()) && y().InteriorContains(p.y()); +} + +inline R2Rect R2Rect::Expanded(double margin) const { + return Expanded(R2Point(margin, margin)); +} + +inline bool R2Rect::operator==(const R2Rect& other) const { + return x() == other.x() && y() == other.y(); +} + +std::ostream& operator<<(std::ostream& os, const R2Rect& r); + +#endif // S2_R2RECT_H_ diff --git a/inst/include/s2/s1angle.h b/inst/include/s2/s1angle.h new file mode 100644 index 0000000..32f36a4 --- /dev/null +++ b/inst/include/s2/s1angle.h @@ -0,0 +1,336 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_S1ANGLE_H_ +#define S2_S1ANGLE_H_ + +#include +#include +#include +#include + +#include "s2/base/integral_types.h" +#include "s2/_fp_contract_off.h" +#include "s2/s2point.h" +#include "s2/util/math/mathutil.h" +#include "s2/util/math/vector.h" + +class S2LatLng; + +#ifndef SWIG +#define IFNDEF_SWIG(x) x +#else +#define IFNDEF_SWIG(x) +#endif + +// This class represents a one-dimensional angle (as opposed to a +// two-dimensional solid angle). It has methods for converting angles to +// or from radians, degrees, and the E5/E6/E7 representations (i.e. degrees +// multiplied by 1e5/1e6/1e7 and rounded to the nearest integer). +// +// The internal representation is a double-precision value in radians, so +// conversion to and from radians is exact. Conversions between E5, E6, E7, +// and Degrees are not always exact; for example, Degrees(3.1) is different +// from E6(3100000) or E7(310000000). However, the following properties are +// guaranteed for any integer "n", provided that "n" is in the input range of +// both functions: +// +// Degrees(n) == E6(1000000 * n) +// Degrees(n) == E7(10000000 * n) +// E6(n) == E7(10 * n) +// +// The corresponding properties are *not* true for E5, so if you use E5 then +// don't test for exact equality when comparing to other formats such as +// Degrees or E7. +// +// The following conversions between degrees and radians are exact: +// +// Degrees(180) == Radians(M_PI) +// Degrees(45 * k) == Radians(k * M_PI / 4) for k == 0..8 +// +// These identities also hold when the arguments are scaled up or down by any +// power of 2. Some similar identities are also true, for example, +// Degrees(60) == Radians(M_PI / 3), but be aware that this type of identity +// does not hold in general. For example, Degrees(3) != Radians(M_PI / 60). +// +// Similarly, the conversion to radians means that Angle::Degrees(x).degrees() +// does not always equal "x". For example, +// +// S1Angle::Degrees(45 * k).degrees() == 45 * k for k == 0..8 +// but S1Angle::Degrees(60).degrees() != 60. +// +// This means that when testing for equality, you should allow for numerical +// errors (EXPECT_DOUBLE_EQ) or convert to discrete E5/E6/E7 values first. +// +// CAVEAT: All of the above properties depend on "double" being the usual +// 64-bit IEEE 754 type (which is true on almost all modern platforms). +// +// This class is intended to be copied by value as desired. It uses +// the default copy constructor and assignment operator. +class S1Angle { + public: + // These methods construct S1Angle objects from their measure in radians + // or degrees. + static constexpr S1Angle Radians(double radians); + static constexpr S1Angle Degrees(double degrees); + static constexpr S1Angle E5(int32 e5); + static constexpr S1Angle E6(int32 e6); + static constexpr S1Angle E7(int32 e7); + + // Convenience functions -- to use when args have been fixed32s in protos. + // + // The arguments are static_cast into int32, so very large unsigned values + // are treated as negative numbers. + static constexpr S1Angle UnsignedE6(uint32 e6); + static constexpr S1Angle UnsignedE7(uint32 e7); + + // The default constructor yields a zero angle. This is useful for STL + // containers and class methods with output arguments. + IFNDEF_SWIG(constexpr) S1Angle() : radians_(0) {} + + // Return an angle larger than any finite angle. + static constexpr S1Angle Infinity(); + + // A explicit shorthand for the default constructor. + static constexpr S1Angle Zero(); + + // Return the angle between two points, which is also equal to the distance + // between these points on the unit sphere. The points do not need to be + // normalized. This function has a maximum error of 3.25 * DBL_EPSILON (or + // 2.5 * DBL_EPSILON for angles up to 1 radian). If either point is + // zero-length (e.g. an uninitialized S2Point), or almost zero-length, the + // resulting angle will be zero. + S1Angle(const S2Point& x, const S2Point& y); + + // Like the constructor above, but return the angle (i.e., distance) between + // two S2LatLng points. This function has about 15 digits of accuracy for + // small distances but only about 8 digits of accuracy as the distance + // approaches 180 degrees (i.e., nearly-antipodal points). + S1Angle(const S2LatLng& x, const S2LatLng& y); + + constexpr double radians() const; + constexpr double degrees() const; + + int32 e5() const; + int32 e6() const; + int32 e7() const; + + // Return the absolute value of an angle. + S1Angle abs() const; + + // Comparison operators. + friend bool operator==(S1Angle x, S1Angle y); + friend bool operator!=(S1Angle x, S1Angle y); + friend bool operator<(S1Angle x, S1Angle y); + friend bool operator>(S1Angle x, S1Angle y); + friend bool operator<=(S1Angle x, S1Angle y); + friend bool operator>=(S1Angle x, S1Angle y); + + // Simple arithmetic operators for manipulating S1Angles. + friend S1Angle operator-(S1Angle a); + friend S1Angle operator+(S1Angle a, S1Angle b); + friend S1Angle operator-(S1Angle a, S1Angle b); + friend S1Angle operator*(double m, S1Angle a); + friend S1Angle operator*(S1Angle a, double m); + friend S1Angle operator/(S1Angle a, double m); + friend double operator/(S1Angle a, S1Angle b); + S1Angle& operator+=(S1Angle a); + S1Angle& operator-=(S1Angle a); + S1Angle& operator*=(double m); + S1Angle& operator/=(double m); + + // Trigonmetric functions (not necessary but slightly more convenient). + friend double sin(S1Angle a); + friend double cos(S1Angle a); + friend double tan(S1Angle a); + + // Return the angle normalized to the range (-180, 180] degrees. + S1Angle Normalized() const; + + // Normalize this angle to the range (-180, 180] degrees. + void Normalize(); + + // When S1Angle is used as a key in one of the btree container types + // (util/btree), indicate that linear rather than binary search should be + // used. This is much faster when the comparison function is cheap. + typedef std::true_type goog_btree_prefer_linear_node_search; + + private: + explicit IFNDEF_SWIG(constexpr) S1Angle(double radians) : radians_(radians) {} + double radians_; +}; + + +////////////////// Implementation details follow //////////////////// + + +inline constexpr S1Angle S1Angle::Infinity() { + return S1Angle(std::numeric_limits::infinity()); +} + +inline constexpr S1Angle S1Angle::Zero() { + return S1Angle(0); +} + +inline constexpr double S1Angle::radians() const { + return radians_; +} + +inline constexpr double S1Angle::degrees() const { + return (180 / M_PI) * radians_; +} + +// Note that the E5, E6, and E7 conversion involve two multiplications rather +// than one. This is mainly for backwards compatibility (changing this would +// break many tests), but it does have the nice side effect that conversions +// between Degrees, E6, and E7 are exact when the arguments are integers. + +inline int32 S1Angle::e5() const { + return MathUtil::FastIntRound(1e5 * degrees()); +} + +inline int32 S1Angle::e6() const { + return MathUtil::FastIntRound(1e6 * degrees()); +} + +inline int32 S1Angle::e7() const { + return MathUtil::FastIntRound(1e7 * degrees()); +} + +inline S1Angle S1Angle::abs() const { + return S1Angle(std::fabs(radians_)); +} + +inline bool operator==(S1Angle x, S1Angle y) { + return x.radians() == y.radians(); +} + +inline bool operator!=(S1Angle x, S1Angle y) { + return x.radians() != y.radians(); +} + +inline bool operator<(S1Angle x, S1Angle y) { + return x.radians() < y.radians(); +} + +inline bool operator>(S1Angle x, S1Angle y) { + return x.radians() > y.radians(); +} + +inline bool operator<=(S1Angle x, S1Angle y) { + return x.radians() <= y.radians(); +} + +inline bool operator>=(S1Angle x, S1Angle y) { + return x.radians() >= y.radians(); +} + +inline S1Angle operator-(S1Angle a) { + return S1Angle::Radians(-a.radians()); +} + +inline S1Angle operator+(S1Angle a, S1Angle b) { + return S1Angle::Radians(a.radians() + b.radians()); +} + +inline S1Angle operator-(S1Angle a, S1Angle b) { + return S1Angle::Radians(a.radians() - b.radians()); +} + +inline S1Angle operator*(double m, S1Angle a) { + return S1Angle::Radians(m * a.radians()); +} + +inline S1Angle operator*(S1Angle a, double m) { + return S1Angle::Radians(m * a.radians()); +} + +inline S1Angle operator/(S1Angle a, double m) { + return S1Angle::Radians(a.radians() / m); +} + +inline double operator/(S1Angle a, S1Angle b) { + return a.radians() / b.radians(); +} + +inline S1Angle& S1Angle::operator+=(S1Angle a) { + radians_ += a.radians(); + return *this; +} + +inline S1Angle& S1Angle::operator-=(S1Angle a) { + radians_ -= a.radians(); + return *this; +} + +inline S1Angle& S1Angle::operator*=(double m) { + radians_ *= m; + return *this; +} + +inline S1Angle& S1Angle::operator/=(double m) { + radians_ /= m; + return *this; +} + +inline double sin(S1Angle a) { + return sin(a.radians()); +} + +inline double cos(S1Angle a) { + return cos(a.radians()); +} + +inline double tan(S1Angle a) { + return tan(a.radians()); +} + +inline constexpr S1Angle S1Angle::Radians(double radians) { + return S1Angle(radians); +} + +inline constexpr S1Angle S1Angle::Degrees(double degrees) { + return S1Angle((M_PI / 180) * degrees); +} + +inline constexpr S1Angle S1Angle::E5(int32 e5) { + return Degrees(1e-5 * e5); +} + +inline constexpr S1Angle S1Angle::E6(int32 e6) { + return Degrees(1e-6 * e6); +} + +inline constexpr S1Angle S1Angle::E7(int32 e7) { + return Degrees(1e-7 * e7); +} + +inline constexpr S1Angle S1Angle::UnsignedE6(uint32 e6) { + return E6(static_cast(e6)); +} + +inline constexpr S1Angle S1Angle::UnsignedE7(uint32 e7) { + return E7(static_cast(e7)); +} + +// Writes the angle in degrees with 7 digits of precision after the +// decimal point, e.g. "17.3745904". +std::ostream& operator<<(std::ostream& os, S1Angle a); + +#undef IFNDEF_SWIG + +#endif // S2_S1ANGLE_H_ diff --git a/inst/include/s2/s1chord_angle.h b/inst/include/s2/s1chord_angle.h new file mode 100644 index 0000000..84abffd --- /dev/null +++ b/inst/include/s2/s1chord_angle.h @@ -0,0 +1,369 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_S1CHORD_ANGLE_H_ +#define S2_S1CHORD_ANGLE_H_ + +#include +#include +#include +#include + +#include "s2/_fp_contract_off.h" +#include "s2/s1angle.h" +#include "s2/s2pointutil.h" + +// S1ChordAngle represents the angle subtended by a chord (i.e., the straight +// line segment connecting two points on the sphere). Its representation +// makes it very efficient for computing and comparing distances, but unlike +// S1Angle it is only capable of representing angles between 0 and Pi radians. +// Generally, S1ChordAngle should only be used in loops where many angles need +// to be calculated and compared. Otherwise it is simpler to use S1Angle. +// +// S1ChordAngle also loses some accuracy as the angle approaches Pi radians. +// Specifically, the representation of (Pi - x) radians has an error of about +// (1e-15 / x), with a maximum error of about 2e-8 radians (about 13cm on the +// Earth's surface). For comparison, for angles up to 90 degrees (10000km) +// the worst-case representation error is about 2e-16 radians (1 nanometer), +// which is about the same as S1Angle. +// +// This class is intended to be copied by value as desired. It uses +// the default copy constructor and assignment operator. +class S1ChordAngle { + public: + // The default constructor yields a zero angle. This is useful for STL + // containers and class methods with output arguments. + S1ChordAngle() : length2_(0) {} + + // Construct the S1ChordAngle corresponding to the distance between the two + // given points. The points must be unit length. + S1ChordAngle(const S2Point& x, const S2Point& y); + + // Return the zero chord angle. + static S1ChordAngle Zero(); + + // Return a chord angle of 90 degrees (a "right angle"). + static S1ChordAngle Right(); + + // Return a chord angle of 180 degrees (a "straight angle"). This is the + // maximum finite chord angle. + static S1ChordAngle Straight(); + + // Return a chord angle larger than any finite chord angle. The only valid + // operations on Infinity() are comparisons, S1Angle conversions, and + // Successor() / Predecessor(). + static S1ChordAngle Infinity(); + + // Return a chord angle smaller than Zero(). The only valid operations on + // Negative() are comparisons, S1Angle conversions, and Successor() / + // Predecessor(). + static S1ChordAngle Negative(); + + // Conversion from an S1Angle. Angles outside the range [0, Pi] are handled + // as follows: Infinity() is mapped to Infinity(), negative angles are + // mapped to Negative(), and finite angles larger than Pi are mapped to + // Straight(). + // + // Note that this operation is relatively expensive and should be avoided. + // To use S1ChordAngle effectively, you should structure your code so that + // input arguments are converted to S1ChordAngles at the beginning of your + // algorithm, and results are converted back to S1Angles only at the end. + explicit S1ChordAngle(S1Angle angle); + + // Convenience methods implemented by converting from an S1Angle. + static S1ChordAngle Radians(double radians); + static S1ChordAngle Degrees(double degrees); + static S1ChordAngle E5(int32 e5); + static S1ChordAngle E6(int32 e6); + static S1ChordAngle E7(int32 e7); + + // Construct an S1ChordAngle that is an upper bound on the given S1Angle, + // i.e. such that FastUpperBoundFrom(x).ToAngle() >= x. Unlike the S1Angle + // constructor above, this method is very fast, and the bound is accurate to + // within 1% for distances up to about 3100km on the Earth's surface. + static S1ChordAngle FastUpperBoundFrom(S1Angle angle); + + // Construct an S1ChordAngle from the squared chord length. Note that the + // argument is automatically clamped to a maximum of 4.0 to handle possible + // roundoff errors. The argument must be non-negative. + static S1ChordAngle FromLength2(double length2); + + // Converts to an S1Angle. Can be used just like an S1Angle constructor: + // + // S1ChordAngle x = ...; + // return S1Angle(x); + // + // Infinity() is converted to S1Angle::Infinity(), and Negative() is + // converted to an unspecified negative S1Angle. + // + // Note that the conversion uses trigonometric functions and therefore + // should be avoided in inner loops. + explicit operator S1Angle() const; + + // Converts to an S1Angle (equivalent to the operator above). + S1Angle ToAngle() const; + + // Convenience methods implemented by calling ToAngle() first. Note that + // because of the S1Angle conversion these methods are relatively expensive + // (despite their lowercase names), so the results should be cached if they + // are needed inside loops. + double radians() const; + double degrees() const; + int32 e5() const; + int32 e6() const; + int32 e7() const; + + // All operators and functions are declared here so that we can put them all + // in one place. (The compound assignment operators must be put here.) + + // Comparison operators. + friend bool operator==(S1ChordAngle x, S1ChordAngle y); + friend bool operator!=(S1ChordAngle x, S1ChordAngle y); + friend bool operator<(S1ChordAngle x, S1ChordAngle y); + friend bool operator>(S1ChordAngle x, S1ChordAngle y); + friend bool operator<=(S1ChordAngle x, S1ChordAngle y); + friend bool operator>=(S1ChordAngle x, S1ChordAngle y); + + // Comparison predicates. + bool is_zero() const; + bool is_negative() const; + bool is_infinity() const; + bool is_special() const; // Negative or infinity. + + // Only addition and subtraction of S1ChordAngles is supported. These + // methods add or subtract the corresponding S1Angles, and clamp the result + // to the range [0, Pi]. Both arguments must be non-negative and + // non-infinite. + // + // REQUIRES: !a.is_special() && !b.is_special() + friend S1ChordAngle operator+(S1ChordAngle a, S1ChordAngle b); + friend S1ChordAngle operator-(S1ChordAngle a, S1ChordAngle b); + S1ChordAngle& operator+=(S1ChordAngle a); + S1ChordAngle& operator-=(S1ChordAngle a); + + // Trigonmetric functions. It is more accurate and efficient to call these + // rather than first converting to an S1Angle. + friend double sin(S1ChordAngle a); + friend double cos(S1ChordAngle a); + friend double tan(S1ChordAngle a); + + // Returns sin(a)**2, but computed more efficiently. + friend double sin2(S1ChordAngle a); + + // The squared length of the chord. (Most clients will not need this.) + double length2() const { return length2_; } + + // Returns the smallest representable S1ChordAngle larger than this object. + // This can be used to convert a "<" comparison to a "<=" comparison. For + // example: + // + // S2ClosestEdgeQuery query(...); + // S1ChordAngle limit = ...; + // if (query.IsDistanceLess(target, limit.Successor())) { + // // Distance to "target" is less than or equal to "limit". + // } + // + // Note the following special cases: + // Negative().Successor() == Zero() + // Straight().Successor() == Infinity() + // Infinity().Successor() == Infinity() + S1ChordAngle Successor() const; + + // Like Successor(), but returns the largest representable S1ChordAngle less + // than this object. + // + // Note the following special cases: + // Infinity().Predecessor() == Straight() + // Zero().Predecessor() == Negative() + // Negative().Predecessor() == Negative() + S1ChordAngle Predecessor() const; + + // Returns a new S1ChordAngle that has been adjusted by the given error + // bound (which can be positive or negative). "error" should be the value + // returned by one of the error bound methods below. For example: + // S1ChordAngle a(x, y); + // S1ChordAngle a1 = a.PlusError(a.GetS2PointConstructorMaxError()); + S1ChordAngle PlusError(double error) const; + + // Return the maximum error in length2() for the S1ChordAngle(x, y) + // constructor, assuming that "x" and "y" are normalized to within the + // bounds guaranteed by S2Point::Normalize(). (The error is defined with + // respect to the true distance after the points are projected to lie + // exactly on the sphere.) + double GetS2PointConstructorMaxError() const; + + // Return the maximum error in length2() for the S1Angle constructor. + double GetS1AngleConstructorMaxError() const; + + // Return true if the internal representation is valid. Negative() and + // Infinity() are both considered valid. + bool is_valid() const; + + // When S1ChordAngle is used as a key in one of the btree container types + // (util/btree), indicate that linear rather than binary search should be + // used. This is much faster when the comparison function is cheap. + typedef std::true_type goog_btree_prefer_linear_node_search; + + private: + // S1ChordAngles are represented by the squared chord length, which can + // range from 0 to 4. Infinity() uses an infinite squared length. + explicit S1ChordAngle(double length2) : length2_(length2) { + S2_DCHECK(is_valid()); + } + double length2_; +}; + + +////////////////// Implementation details follow //////////////////// + + +inline S1ChordAngle::S1ChordAngle(const S2Point& x, const S2Point& y) { + S2_DCHECK(S2::IsUnitLength(x)); + S2_DCHECK(S2::IsUnitLength(y)); + // The squared distance may slightly exceed 4.0 due to roundoff errors. + // The maximum error in the result is 2 * DBL_EPSILON * length2_. + length2_ = std::min(4.0, (x - y).Norm2()); + S2_DCHECK(is_valid()); +} + +inline S1ChordAngle S1ChordAngle::FromLength2(double length2) { + return S1ChordAngle(std::min(4.0, length2)); +} + +inline S1ChordAngle S1ChordAngle::Zero() { + return S1ChordAngle(0); +} + +inline S1ChordAngle S1ChordAngle::Right() { + return S1ChordAngle(2); +} + +inline S1ChordAngle S1ChordAngle::Straight() { + return S1ChordAngle(4); +} + +inline S1ChordAngle S1ChordAngle::Infinity() { + return S1ChordAngle(std::numeric_limits::infinity()); +} + +inline S1ChordAngle S1ChordAngle::Negative() { + return S1ChordAngle(-1); +} + +inline S1ChordAngle S1ChordAngle::Radians(double radians) { + return S1ChordAngle(S1Angle::Radians(radians)); +} + +inline S1ChordAngle S1ChordAngle::Degrees(double degrees) { + return S1ChordAngle(S1Angle::Degrees(degrees)); +} + +inline S1ChordAngle S1ChordAngle::E5(int32 e5) { + return S1ChordAngle(S1Angle::E5(e5)); +} + +inline S1ChordAngle S1ChordAngle::E6(int32 e6) { + return S1ChordAngle(S1Angle::E6(e6)); +} + +inline S1ChordAngle S1ChordAngle::E7(int32 e7) { + return S1ChordAngle(S1Angle::E7(e7)); +} + +inline S1ChordAngle S1ChordAngle::FastUpperBoundFrom(S1Angle angle) { + // This method uses the distance along the surface of the sphere as an upper + // bound on the distance through the sphere's interior. + return S1ChordAngle::FromLength2(angle.radians() * angle.radians()); +} + +inline S1ChordAngle::operator S1Angle() const { + return ToAngle(); +} + +inline double S1ChordAngle::radians() const { + return ToAngle().radians(); +} + +inline double S1ChordAngle::degrees() const { + return ToAngle().degrees(); +} + +inline int32 S1ChordAngle::e5() const { + return ToAngle().e5(); +} + +inline int32 S1ChordAngle::e6() const { + return ToAngle().e6(); +} + +inline int32 S1ChordAngle::e7() const { + return ToAngle().e7(); +} + +inline bool S1ChordAngle::is_zero() const { + return length2_ == 0; +} + +inline bool S1ChordAngle::is_negative() const { + // TODO(ericv): Consider stricter check here -- only allow Negative(). + return length2_ < 0; +} + +inline bool S1ChordAngle::is_infinity() const { + return length2_ == std::numeric_limits::infinity(); +} + +inline bool S1ChordAngle::is_special() const { + return is_negative() || is_infinity(); +} + +inline bool operator==(S1ChordAngle x, S1ChordAngle y) { + return x.length2() == y.length2(); +} + +inline bool operator!=(S1ChordAngle x, S1ChordAngle y) { + return x.length2() != y.length2(); +} + +inline bool operator<(S1ChordAngle x, S1ChordAngle y) { + return x.length2() < y.length2(); +} + +inline bool operator>(S1ChordAngle x, S1ChordAngle y) { + return x.length2() > y.length2(); +} + +inline bool operator<=(S1ChordAngle x, S1ChordAngle y) { + return x.length2() <= y.length2(); +} + +inline bool operator>=(S1ChordAngle x, S1ChordAngle y) { + return x.length2() >= y.length2(); +} + +inline S1ChordAngle& S1ChordAngle::operator+=(S1ChordAngle a) { + return (*this = *this + a); +} + +inline S1ChordAngle& S1ChordAngle::operator-=(S1ChordAngle a) { + return (*this = *this - a); +} + +// Outputs the chord angle as the equivalent S1Angle. +std::ostream& operator<<(std::ostream& os, S1ChordAngle a); + +#endif // S2_S1CHORD_ANGLE_H_ diff --git a/inst/include/s2/s1interval.h b/inst/include/s2/s1interval.h new file mode 100644 index 0000000..b37471a --- /dev/null +++ b/inst/include/s2/s1interval.h @@ -0,0 +1,266 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_S1INTERVAL_H_ +#define S2_S1INTERVAL_H_ + +#include +#include +#include + +#include "s2/base/logging.h" +#include "s2/_fp_contract_off.h" +#include "s2/util/math/vector.h" // IWYU pragma: export + +// An S1Interval represents a closed interval on a unit circle (also known +// as a 1-dimensional sphere). It is capable of representing the empty +// interval (containing no points), the full interval (containing all +// points), and zero-length intervals (containing a single point). +// +// Points are represented by the angle they make with the positive x-axis in +// the range [-Pi, Pi]. An interval is represented by its lower and upper +// bounds (both inclusive, since the interval is closed). The lower bound may +// be greater than the upper bound, in which case the interval is "inverted" +// (i.e. it passes through the point (-1, 0)). +// +// Note that the point (-1, 0) has two valid representations, Pi and -Pi. +// The normalized representation of this point internally is Pi, so that +// endpoints of normal intervals are in the range (-Pi, Pi]. However, we +// take advantage of the point -Pi to construct two special intervals: +// the Full() interval is [-Pi, Pi], and the Empty() interval is [Pi, -Pi]. +// +// This class is intended to be copied by value as desired. It uses +// the default copy constructor and assignment operator. +class S1Interval { + public: + // Constructor. Both endpoints must be in the range -Pi to Pi inclusive. + // The value -Pi is converted internally to Pi except for the Full() + // and Empty() intervals. + S1Interval(double lo, double hi); + + // The default constructor creates an empty interval. + // + // Note: Don't construct an interval using the default constructor and + // set_lo()/set_hi(). If you need to set both endpoints, use the + // constructor above: + // + // lng_bounds_ = S1Interval(lng_lo, lng_hi); + S1Interval(); + + // Returns the empty interval. + static S1Interval Empty(); + + // Returns the full interval. + static S1Interval Full(); + + // Convenience method to construct an interval containing a single point. + static S1Interval FromPoint(double p); + + // Convenience method to construct the minimal interval containing + // the two given points. This is equivalent to starting with an empty + // interval and calling AddPoint() twice, but it is more efficient. + static S1Interval FromPointPair(double p1, double p2); + + // Accessors methods. + double lo() const { return bounds_[0]; } + double hi() const { return bounds_[1]; } + + // Methods that allow the S1Interval to be accessed as a vector. (The + // recommended style is to use lo() and hi() whenever possible, but these + // methods are useful when the endpoint to be selected is not constant.) + // + // Only const versions of these methods are provided, since S1Interval + // has invariants that must be maintained after each update. + double operator[](int i) const { return bounds_[i]; } + const Vector2_d& bounds() const { return bounds_; } + + // An interval is valid if neither bound exceeds Pi in absolute value, + // and the value -Pi appears only in the Empty() and Full() intervals. + bool is_valid() const; + + // Return true if the interval contains all points on the unit circle. + bool is_full() const { return lo() == -M_PI && hi() == M_PI; } + + // Return true if the interval is empty, i.e. it contains no points. + bool is_empty() const { return lo() == M_PI && hi() == -M_PI; } + + // Return true if lo() > hi(). (This is true for empty intervals.) + bool is_inverted() const { return lo() > hi(); } + + // Return the midpoint of the interval. For full and empty intervals, + // the result is arbitrary. + double GetCenter() const; + + // Return the length of the interval. The length of an empty interval + // is negative. + double GetLength() const; + + // Return the complement of the interior of the interval. An interval and + // its complement have the same boundary but do not share any interior + // values. The complement operator is not a bijection, since the complement + // of a singleton interval (containing a single value) is the same as the + // complement of an empty interval. + S1Interval Complement() const; + + // Return the midpoint of the complement of the interval. For full and empty + // intervals, the result is arbitrary. For a singleton interval (containing a + // single point), the result is its antipodal point on S1. + double GetComplementCenter() const; + + // Return true if the interval (which is closed) contains the point 'p'. + bool Contains(double p) const; + + // Return true if the interior of the interval contains the point 'p'. + bool InteriorContains(double p) const; + + // Return true if the interval contains the given interval 'y'. + // Works for empty, full, and singleton intervals. + bool Contains(const S1Interval& y) const; + + // Returns true if the interior of this interval contains the entire + // interval 'y'. Note that x.InteriorContains(x) is true only when + // x is the empty or full interval, and x.InteriorContains(S1Interval(p,p)) + // is equivalent to x.InteriorContains(p). + bool InteriorContains(const S1Interval& y) const; + + // Return true if the two intervals contain any points in common. + // Note that the point +/-Pi has two representations, so the intervals + // [-Pi,-3] and [2,Pi] intersect, for example. + bool Intersects(const S1Interval& y) const; + + // Return true if the interior of this interval contains any point of the + // interval 'y' (including its boundary). Works for empty, full, and + // singleton intervals. + bool InteriorIntersects(const S1Interval& y) const; + + // Return the Hausdorff distance to the given interval 'y'. For two + // S1Intervals x and y, this distance is defined by + // h(x, y) = max_{p in x} min_{q in y} d(p, q), + // where d(.,.) is measured along S1. + double GetDirectedHausdorffDistance(const S1Interval& y) const; + + // Expand the interval by the minimum amount necessary so that it + // contains the given point "p" (an angle in the range [-Pi, Pi]). + void AddPoint(double p); + + // Return the closest point in the interval to the given point "p". + // The interval must be non-empty. + double Project(double p) const; + + // Return an interval that has been expanded on each side by the given + // distance "margin". If "margin" is negative, then shrink the interval on + // each side by "margin" instead. The resulting interval may be empty or + // full. Any expansion (positive or negative) of a full interval remains + // full, and any expansion of an empty interval remains empty. + S1Interval Expanded(double margin) const; + + // Return the smallest interval that contains this interval and the + // given interval "y". + S1Interval Union(const S1Interval& y) const; + + // Return the smallest interval that contains the intersection of this + // interval with "y". Note that the region of intersection may + // consist of two disjoint intervals. + S1Interval Intersection(const S1Interval& y) const; + + // Return true if two intervals contains the same set of points. + bool operator==(const S1Interval& y) const; + + // Return true if this interval can be transformed into the given interval by + // moving each endpoint by at most "max_error" (and without the endpoints + // crossing, which would invert the interval). Empty and full intervals are + // considered to start at an arbitrary point on the unit circle, thus any + // interval with (length <= 2*max_error) matches the empty interval, and any + // interval with (length >= 2*Pi - 2*max_error) matches the full interval. + bool ApproxEquals(const S1Interval& y, double max_error = 1e-15) const; + + // Low-level methods to modify one endpoint of an existing S1Interval. + // These methods should really be private because setting just one endpoint + // can violate the invariants maintained by S1Interval. In particular: + // + // - It is not valid to call these methods on an Empty() or Full() + // interval, since these intervals do not have any endpoints. + // + // - It is not allowed to set an endpoint to -Pi. (When these methods are + // used internally, values of -Pi have already been normalized to Pi.) + // + // The preferred way to modify both endpoints of an interval is to use a + // constructor, e.g. lng = S1Interval(lng_lo, lng_hi). + void set_lo(double p); + void set_hi(double p); + + private: + enum ArgsChecked { ARGS_CHECKED }; + + // Internal constructor that assumes that both arguments are in the + // correct range, i.e. normalization from -Pi to Pi is already done. + S1Interval(double lo, double hi, ArgsChecked dummy); + + // Return true if the interval (which is closed) contains the point 'p'. + // Skips the normalization of 'p' from -Pi to Pi. + bool FastContains(double p) const; + + Vector2_d bounds_; +}; + +inline S1Interval::S1Interval(double lo, double hi) : bounds_(lo, hi) { + if (lo == -M_PI && hi != M_PI) set_lo(M_PI); + if (hi == -M_PI && lo != M_PI) set_hi(M_PI); + S2_DCHECK(is_valid()); +} + +inline S1Interval::S1Interval(double lo, double hi, ArgsChecked dummy) + : bounds_(lo, hi) { + S2_DCHECK(is_valid()); +} + +inline S1Interval::S1Interval() : bounds_(M_PI, -M_PI) { +} + +inline S1Interval S1Interval::Empty() { + return S1Interval(); +} + +inline S1Interval S1Interval::Full() { + return S1Interval(-M_PI, M_PI, ARGS_CHECKED); +} + +inline bool S1Interval::is_valid() const { + return (std::fabs(lo()) <= M_PI && std::fabs(hi()) <= M_PI && + !(lo() == -M_PI && hi() != M_PI) && + !(hi() == -M_PI && lo() != M_PI)); +} + +inline bool S1Interval::operator==(const S1Interval& y) const { + return lo() == y.lo() && hi() == y.hi(); +} + +inline void S1Interval::set_lo(double p) { + bounds_[0] = p; + S2_DCHECK(is_valid()); +} + +inline void S1Interval::set_hi(double p) { + bounds_[1] = p; + S2_DCHECK(is_valid()); +} + +inline std::ostream& operator<<(std::ostream& os, const S1Interval& x) { + return os << "[" << x.lo() << ", " << x.hi() << "]"; +} + +#endif // S2_S1INTERVAL_H_ diff --git a/inst/include/s2/s2boolean_operation.h b/inst/include/s2/s2boolean_operation.h new file mode 100644 index 0000000..acceb06 --- /dev/null +++ b/inst/include/s2/s2boolean_operation.h @@ -0,0 +1,501 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) + +#ifndef S2_S2BOOLEAN_OPERATION_H_ +#define S2_S2BOOLEAN_OPERATION_H_ + +#include +#include +#include +#include "s2/s2builder.h" +#include "s2/s2builder_graph.h" +#include "s2/s2builder_layer.h" +#include "s2/value_lexicon.h" + +// This class implements boolean operations (intersection, union, difference, +// and symmetric difference) for regions whose boundaries are defined by +// geodesic edges. +// +// S2BooleanOperation operates on exactly two input regions at a time. Each +// region is represented as an S2ShapeIndex and may contain any number of +// points, polylines, and polygons. The region is essentially the union of +// these objects, except that polygon interiors must be disjoint from all +// other geometry (including other polygon interiors). If the input geometry +// for a region does not meet this condition, it can be normalized by +// computing its union first. Note that points or polylines are allowed to +// coincide with the boundaries of polygons. +// +// Degeneracies are supported. A polygon loop or polyline may consist of a +// single edge from a vertex to itself, and polygons may contain "sibling +// pairs" consisting of an edge and its corresponding reverse edge. Polygons +// must not have any duplicate edges (due to the requirement that polygon +// interiors are disjoint), but polylines may have duplicate edges or can even +// be self-intersecting. +// +// Points and polyline edges are treated as multisets: if the same point or +// polyline edge appears multiple times in the input, it will appear multiple +// times in the output. For example, the union of a point with an identical +// point consists of two points. This feature is useful for modeling large +// sets of points or polylines as a single region while maintaining their +// distinct identities, even when the points or polylines intersect each +// other. It is also useful for reconstructing polylines that loop back on +// themselves. If duplicate geometry is not desired, it can be merged by +// GraphOptions::DuplicateEdges::MERGE in the S2Builder output layer. +// +// Polylines are always considered to be directed. Polyline edges between the +// same pair of vertices are defined to intersect even if the two edges are in +// opposite directions. (Undirected polylines can be modeled by specifying +// GraphOptions::EdgeType::UNDIRECTED in the S2Builder output layer.) +// +// The output of each operation is sent to an S2Builder::Layer provided by the +// client. This allows clients to build any representation of the geometry +// they choose. It also allows the client to do additional postprocessing of +// the output before building data structures; for example, the client can +// easily discard degeneracies or convert them to another data type. +// +// The boundaries of polygons and polylines can be modeled as open, semi-open, +// or closed. Polyline boundaries are controlled by the PolylineModel class, +// whose options are as follows: +// +// - In the OPEN model, polylines do not contain their first or last vertex +// except for one special case: namely, if the polyline forms a loop and +// the polyline_loops_have_boundaries() option is set to false, then the +// first/last vertex is contained. +// +// - In the SEMI_OPEN model, polylines contain all vertices except the last. +// Therefore if one polyline starts where another polyline stops, the two +// polylines do not intersect. +// +// - In the CLOSED model, polylines contain all of their vertices. +// +// When multiple polylines are present, they are processed independently and +// have no effect on each other. For example, in the OPEN boundary model the +// polyline ABC contains the vertex B, while set of polylines {AB, BC} does +// not. (If you want to treat the polylines as a union instead, with +// boundaries merged according to the "mod 2" rule, this can be achieved by +// reassembling the edges into maximal polylines using S2PolylineVectorLayer +// with EdgeType::UNDIRECTED, DuplicateEdges::MERGE, and PolylineType::WALK.) +// +// Polygon boundaries are controlled by the PolygonModel class, which has the +// following options: +// +// - In the OPEN model, polygons do not contain their vertices or edges. +// This implies that a polyline that follows the boundary of a polygon will +// not intersect it. +// +// - In the SEMI_OPEN model, polygon point containment is defined such that +// if several polygons tile the region around a vertex, then exactly one of +// those polygons contains that vertex. Similarly polygons contain all of +// their edges, but none of their reversed edges. This implies that a +// polyline and polygon edge with the same endpoints intersect if and only +// if they are in the same direction. (This rule ensures that if a +// polyline is intersected with a polygon and its complement, the two +// resulting polylines do not have any edges in common.) +// +// - In the CLOSED model, polygons contain all their vertices, edges, and +// reversed edges. This implies that a polyline that shares an edge (in +// either direction) with a polygon is defined to intersect it. Similarly, +// this is the only model where polygons that touch at a vertex or along an +// edge intersect. +// +// Note that PolylineModel and PolygonModel are defined as separate classes in +// order to allow for possible future extensions. +// +// Operations between geometry of different dimensions are defined as follows: +// +// - For UNION, the higher-dimensional shape always wins. For example the +// union of a closed polygon A with a polyline B that coincides with the +// boundary of A consists only of the polygon A. +// +// - For INTERSECTION, the lower-dimensional shape always wins. For example, +// the intersection of a closed polygon A with a point B that coincides +// with a vertex of A consists only of the point B. +// +// - For DIFFERENCE, higher-dimensional shapes are not affected by +// subtracting lower-dimensional shapes. For example, subtracting a point +// or polyline from a polygon A yields the original polygon A. This rule +// exists because in general, it is impossible to represent the output +// using the specified boundary model(s). (Consider subtracting one vertex +// from a PolylineModel::CLOSED polyline, or subtracting one edge from a +// PolygonModel::CLOSED polygon.) If you want to perform operations like +// this, consider representing all boundaries explicitly (topological +// boundaries) using OPEN boundary models. Another option for polygons is +// to subtract a degenerate loop, which yields a polygon with a degenerate +// hole (see S2LaxPolygonShape). +// +// Note that in the case of Precision::EXACT operations, the above remarks +// only apply to the output before snapping. Snapping may cause nearby +// distinct edges to become coincident, e.g. a polyline may become coincident +// with a polygon boundary. However also note that S2BooleanOperation is +// perfectly happy to accept such geometry as input. +// +// Note the following differences between S2BooleanOperation and the similar +// S2MultiBooleanOperation class: +// +// - S2BooleanOperation operates on exactly two regions at a time, whereas +// S2MultiBooleanOperation operates on any number of regions. +// +// - S2BooleanOperation is potentially much faster when the input is already +// represented as S2ShapeIndexes. The algorithm is output sensitive and is +// often sublinear in the input size. This can be a big advantage if, say, +// +// - S2BooleanOperation supports exact predicates and the corresponding +// exact operations (i.e., operations that are equivalent to computing the +// exact result and then snap rounding it). +// +// - S2MultiBooleanOperation has better error guarantees when there are many +// regions, since it requires only one snapping operation for any number of +// input regions. +// +// Example usage: +// S2ShapeIndex a, b; // Input geometry, e.g. containing polygons. +// S2Polygon polygon; // Output geometry. +// S2BooleanOperation::Options options; +// options.set_snap_function(snap_function); +// S2BooleanOperation op(S2BooleanOperation::OpType::INTERSECTION, +// absl::make_unique(&polygon), +// options); +// S2Error error; +// if (!op.Build(a, b, &error)) { +// S2_LOG(ERROR) << error; +// ... +// } +// +// If the output includes objects of different dimensions, they can be +// assembled into different layers with code like this: +// +// vector points; +// vector> polylines; +// S2Polygon polygon; +// S2BooleanOperation op( +// S2BooleanOperation::OpType::UNION, +// absl::make_unique(&points), +// absl::make_unique(&polylines), +// absl::make_unique(&polygon)); + +class S2BooleanOperation { + public: + // The supported operation types. + enum class OpType { + UNION, // Contained by either region. + INTERSECTION, // Contained by both regions. + DIFFERENCE, // Contained by the first region but not the second. + SYMMETRIC_DIFFERENCE // Contained by one region but not the other. + }; + // Translates OpType to one of the strings above. + static const char* OpTypeToString(OpType op_type); + + // Defines whether polygons are considered to contain their vertices and/or + // edges (see definitions above). + enum class PolygonModel { OPEN, SEMI_OPEN, CLOSED }; + + // Defines whether polylines are considered to contain their endpoints + // (see definitions above). + enum class PolylineModel { OPEN, SEMI_OPEN, CLOSED }; + + // With Precision::EXACT, the operation is evaluated using the exact input + // geometry. Predicates that use this option will produce exact results; + // for example, they can distinguish between a polyline that barely + // intersects a polygon from one that barely misses it. Constructive + // operations (ones that yield new geometry, as opposed to predicates) are + // implemented by computing the exact result and then snap rounding it + // according to the given snap_function() (see below). This is as close as + // it is possible to get to the exact result while requiring that vertex + // coordinates have type "double". + // + // With Precision::SNAPPED, the input regions are snapped together *before* + // the operation is evaluated. So for example, two polygons that overlap + // slightly will be treated as though they share a common boundary, and + // similarly two polygons that are slightly separated from each other will + // be treated as though they share a common boundary. Snapped results are + // useful for dealing with points, since in S2 the only points that lie + // exactly on a polyline or polygon edge are the endpoints of that edge. + // + // Conceptually, the difference between these two options is that with + // Precision::SNAPPED, the inputs are snap rounded (together), whereas with + // Precision::EXACT only the result is snap rounded. + enum class Precision { EXACT, SNAPPED }; + + // SourceId identifies an edge from one of the two input S2ShapeIndexes. + // It consists of a region id (0 or 1), a shape id within that region's + // S2ShapeIndex, and an edge id within that shape. + class SourceId { + public: + SourceId(); + SourceId(int region_id, int32 shape_id, int32 edge_id); + explicit SourceId(int32 special_edge_id); + int region_id() const { return region_id_; } + int32 shape_id() const { return shape_id_; } + int32 edge_id() const { return edge_id_; } + // TODO(ericv): Convert to functions, define all 6 comparisons. + bool operator==(SourceId other) const; + bool operator<(SourceId other) const; + + private: + uint32 region_id_ : 1; + uint32 shape_id_ : 31; + int32 edge_id_; + }; + + class Options { + public: + Options(); + + // Convenience constructor that calls set_snap_function(). + explicit Options(const S2Builder::SnapFunction& snap_function); + + // Specifies the function to be used for snap rounding. + // + // DEFAULT: s2builderutil::IdentitySnapFunction(S1Angle::Zero()) + // - This does no snapping and preserves all input vertices exactly unless + // there are crossing edges, in which case the snap radius is increased + // to the maximum intersection point error (S2::kIntersectionError). + const S2Builder::SnapFunction& snap_function() const; + void set_snap_function(const S2Builder::SnapFunction& snap_function); + + // Defines whether polygons are considered to contain their vertices + // and/or edges (see comments above). + // + // DEFAULT: PolygonModel::SEMI_OPEN + PolygonModel polygon_model() const; + void set_polygon_model(PolygonModel model); + + // Defines whether polylines are considered to contain their vertices (see + // comments above). + // + // DEFAULT: PolylineModel::CLOSED + PolylineModel polyline_model() const; + void set_polyline_model(PolylineModel model); + + // Specifies whether a polyline loop is considered to have a non-empty + // boundary. By default this option is true, meaning that even if the + // first and last vertices of a polyline are the same, the polyline is + // considered to have a well-defined "start" and "end". For example, if + // the polyline boundary model is OPEN then the polyline loop would not + // include the start/end vertices. These are the best semantics for most + // applications, such as GPS tracks or road network segments. + // + // If the polyline forms a loop and this option is set to false, then + // instead the first and last vertices are considered to represent a + // single vertex in the interior of the polyline. In this case the + // boundary of the polyline is empty, meaning that the first/last vertex + // will be contained by the polyline even if the boundary model is OPEN. + // (Note that this option also has a small effect on the CLOSED boundary + // model, because the first/last vertices of a polyline loop are + // considered to represent one vertex rather than two.) + // + // The main reason for this option is to implement the "mod 2 union" + // boundary semantics of the OpenGIS Simple Features spec. This can be + // achieved by making sure that all polylines are constructed using + // S2Builder::Graph::PolylineType::WALK (which ensures that all polylines + // are as long as possible), and then setting this option to false. + // + // DEFAULT: true + bool polyline_loops_have_boundaries() const; + void set_polyline_loops_have_boundaries(bool value); + + // Specifies whether the operation should use the exact input geometry + // (Precision::EXACT), or whether the two input regions should be snapped + // together first (Precision::SNAPPED). + // + // DEFAULT: Precision::EXACT + Precision precision() const; + // void set_precision(Precision precision); + + // If true, the input geometry is interpreted as representing nearby + // geometry that has been snapped or simplified. It then outputs a + // conservative result based on the value of polygon_model() and + // polyline_model(). For the most part, this only affects the handling of + // degeneracies. + // + // - If the model is OPEN, the result is as open as possible. For + // example, the intersection of two identical degenerate shells is empty + // under PolygonModel::OPEN because they could have been disjoint before + // snapping. Similarly, two identical degenerate polylines have an + // empty intersection under PolylineModel::OPEN. + // + // - If the model is CLOSED, the result is as closed as possible. In the + // case of the DIFFERENCE operation, this is equivalent to evaluating + // A - B as Closure(A) - Interior(B). For other operations, it affects + // only the handling of degeneracies. For example, the union of two + // identical degenerate holes is empty under PolygonModel::CLOSED + // (i.e., the hole disappears) because the holes could have been + // disjoint before snapping. + // + // - If the model is SEMI_OPEN, the result is as degenerate as possible. + // New degeneracies will not be created, but all degeneracies that + // coincide with the opposite region's boundary are retained unless this + // would cause a duplicate polygon edge to be created. This model is + // is very useful for working with input data that has both positive and + // negative degeneracies (i.e., degenerate shells and holes). + // + // DEFAULT: false + bool conservative_output() const; + // void set_conservative_output(bool conservative); + + // If specified, then each output edge will be labelled with one or more + // SourceIds indicating which input edge(s) it corresponds to. This + // can be useful if your input geometry has additional data that needs to + // be propagated from the input to the output (e.g., elevations). + // + // You can access the labels by using an S2Builder::Layer type that + // supports labels, such as S2PolygonLayer. The layer outputs a + // "label_set_lexicon" and an "label_set_id" for each edge. You can then + // look up the source information for each edge like this: + // + // for (int32 label : label_set_lexicon.id_set(label_set_id)) { + // const SourceId& src = source_id_lexicon.value(label); + // // region_id() specifies which S2ShapeIndex the edge is from (0 or 1). + // DoSomething(src.region_id(), src.shape_id(), src.edge_id()); + // } + // + // DEFAULT: nullptr + ValueLexicon* source_id_lexicon() const; + // void set_source_id_lexicon(ValueLexicon* source_id_lexicon); + + // Options may be assigned and copied. + Options(const Options& options); + Options& operator=(const Options& options); + + private: + std::unique_ptr snap_function_; + PolygonModel polygon_model_ = PolygonModel::SEMI_OPEN; + PolylineModel polyline_model_ = PolylineModel::CLOSED; + bool polyline_loops_have_boundaries_ = true; + Precision precision_ = Precision::EXACT; + bool conservative_output_ = false; + ValueLexicon* source_id_lexicon_ = nullptr; + }; + + S2BooleanOperation(OpType op_type, + std::unique_ptr layer, + const Options& options = Options()); + + // Specifies that the output boundary edges should be sent to three + // different layers according to their dimension. Points (represented by + // degenerate edges) are sent to layer 0, polyline edges are sent to + // layer 1, and polygon edges are sent to layer 2. + // + // The dimension of an edge is defined as the minimum dimension of the two + // input edges that produced it. For example, the intersection of two + // crossing polyline edges is a considered to be a degenerate polyline + // rather than a point, so it is sent to layer 1. Clients can easily + // reclassify such polylines as points if desired, but this rule makes it + // easier for clients that want to process point, polyline, and polygon + // inputs differently. + // + // The layers are always built in the order 0, 1, 2, and all arguments to + // the Build() calls are guaranteed to be valid until the last call returns. + // All Graph objects have the same set of vertices and the same lexicon + // objects, in order to make it easier to write classes that process all the + // edges in parallel. + S2BooleanOperation(OpType op_type, + std::vector> layers, + const Options& options = Options()); + + OpType op_type() const { return op_type_; } + + // Executes the given operation. Returns true on success, and otherwise + // sets "error" appropriately. (This class does not generate any errors + // itself, but the S2Builder::Layer might.) + bool Build(const S2ShapeIndex& a, const S2ShapeIndex& b, + S2Error* error); + + // Convenience method that returns true if the result of the given operation + // is empty. + static bool IsEmpty(OpType op_type, + const S2ShapeIndex& a, const S2ShapeIndex& b, + const Options& options = Options()); + + // Convenience method that returns true if A intersects B. + static bool Intersects(const S2ShapeIndex& a, const S2ShapeIndex& b, + const Options& options = Options()) { + return !IsEmpty(OpType::INTERSECTION, b, a, options); + } + + // Convenience method that returns true if A contains B, i.e., if the + // difference (B - A) is empty. + static bool Contains(const S2ShapeIndex& a, const S2ShapeIndex& b, + const Options& options = Options()) { + return IsEmpty(OpType::DIFFERENCE, b, a, options); + } + + // Convenience method that returns true if the symmetric difference of A and + // B is empty. (Note that A and B may still not be identical, e.g. A may + // contain two copies of a polyline while B contains one.) + static bool Equals(const S2ShapeIndex& a, const S2ShapeIndex& b, + const Options& options = Options()) { + return IsEmpty(OpType::SYMMETRIC_DIFFERENCE, b, a, options); + } + + private: + class Impl; // The actual implementation. + + // Internal constructor to reduce code duplication. + S2BooleanOperation(OpType op_type, const Options& options); + + // Specifies that "result_empty" should be set to indicate whether the exact + // result of the operation is empty. This constructor is used to efficiently + // test boolean relationships (see IsEmpty above). + S2BooleanOperation(OpType op_type, bool* result_empty, + const Options& options = Options()); + + OpType op_type_; + Options options_; + + // The input regions. + const S2ShapeIndex* regions_[2]; + + // The output consists either of zero layers, one layer, or three layers. + std::vector> layers_; + + // The following field is set if and only if there are no output layers. + bool* result_empty_; +}; + + +////////////////// Implementation details follow //////////////////// + + +inline S2BooleanOperation::SourceId::SourceId() + : region_id_(0), shape_id_(0), edge_id_(-1) { +} + +inline S2BooleanOperation::SourceId::SourceId( + int region_id, int32 shape_id, int32 edge_id) + : region_id_(region_id), shape_id_(shape_id), edge_id_(edge_id) { +} + +inline S2BooleanOperation::SourceId::SourceId(int special_edge_id) + : region_id_(0), shape_id_(0), edge_id_(special_edge_id) { +} + +inline bool S2BooleanOperation::SourceId::operator==(SourceId other) const { + return (region_id_ == other.region_id_ && + shape_id_ == other.shape_id_ && + edge_id_ == other.edge_id_); +} + +inline bool S2BooleanOperation::SourceId::operator<(SourceId other) const { + if (region_id_ < other.region_id_) return true; + if (region_id_ > other.region_id_) return false; + if (shape_id_ < other.shape_id_) return true; + if (shape_id_ > other.shape_id_) return false; + return edge_id_ < other.edge_id_; +} + +#endif // S2_S2BOOLEAN_OPERATION_H_ diff --git a/inst/include/s2/s2builder.h b/inst/include/s2/s2builder.h new file mode 100644 index 0000000..308583e --- /dev/null +++ b/inst/include/s2/s2builder.h @@ -0,0 +1,1057 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Author: ericv@google.com (Eric Veach) +// +// This class is a replacement for S2PolygonBuilder. Once all clients have +// been updated to use this class, S2PolygonBuilder will be removed. + +#ifndef S2_S2BUILDER_H_ +#define S2_S2BUILDER_H_ + +#include +#include +#include +#include "s2/base/integral_types.h" +#include "s2/third_party/absl/base/macros.h" +#include "s2/_fp_contract_off.h" +#include "s2/id_set_lexicon.h" +#include "s2/mutable_s2shape_index.h" +#include "s2/s1angle.h" +#include "s2/s1chord_angle.h" +#include "s2/s2cell_id.h" +#include "s2/s2error.h" +#include "s2/s2point_index.h" +#include "s2/s2shape_index.h" +#include "s2/util/gtl/compact_array.h" + +class S2Loop; +class S2Polygon; +class S2Polyline; + +// S2Builder is a tool for assembling polygonal geometry from edges. Here are +// some of the things it is designed for: +// +// 1. Building polygons, polylines, and polygon meshes from unsorted +// collections of edges. +// +// 2. Snapping geometry to discrete representations (such as S2CellId centers +// or E7 lat/lng coordinates) while preserving the input topology and with +// guaranteed error bounds. +// +// 3. Simplifying geometry (e.g. for indexing, display, or storage). +// +// 4. Importing geometry from other formats, including repairing geometry +// that has errors. +// +// 5. As a tool for implementing more complex operations such as polygon +// intersections and unions. +// +// The implementation is based on the framework of "snap rounding". Unlike +// most snap rounding implementations, S2Builder defines edges as geodesics on +// the sphere (straight lines) and uses the topology of the sphere (i.e., +// there are no "seams" at the poles or 180th meridian). The algorithm is +// designed to be 100% robust for arbitrary input geometry. It offers the +// following properties: +// +// - Guaranteed bounds on how far input vertices and edges can move during +// the snapping process (i.e., at most the given "snap_radius"). +// +// - Guaranteed minimum separation between edges and vertices other than +// their endpoints (similar to the goals of Iterated Snap Rounding). In +// other words, edges that do not intersect in the output are guaranteed +// to have a minimum separation between them. +// +// - Idempotency (similar to the goals of Stable Snap Rounding), i.e. if the +// input already meets the output criteria then it will not be modified. +// +// - Preservation of the input topology (up to the creation of +// degeneracies). This means that there exists a continuous deformation +// from the input to the output such that no vertex crosses an edge. In +// other words, self-intersections won't be created, loops won't change +// orientation, etc. +// +// - The ability to snap to arbitrary discrete point sets (such as S2CellId +// centers, E7 lat/lng points on the sphere, or simply a subset of the +// input vertices), rather than being limited to an integer grid. +// +// Here are some of its other features: +// +// - It can handle both directed and undirected edges. Undirected edges can +// be useful for importing data from other formats, e.g. where loops have +// unspecified orientations. +// +// - It can eliminate self-intersections by finding all edge pairs that cross +// and adding a new vertex at each intersection point. +// +// - It can simplify polygons to within a specified tolerance. For example, +// if two vertices are close enough they will be merged, and if an edge +// passes nearby a vertex then it will be rerouted through that vertex. +// Optionally, it can also detect nearly straight chains of short edges and +// replace them with a single long edge, while maintaining the same +// accuracy, separation, and topology guarantees ("simplify_edge_chains"). +// +// - It supports many different output types through the concept of "layers" +// (polylines, polygons, polygon meshes, etc). You can build multiple +// layers at once in order to ensure that snapping does not create +// intersections between different objects (for example, you can simplify a +// set of contour lines without the risk of having them cross each other). +// +// - It supports edge labels, which allow you to attach arbitrary information +// to edges and have it preserved during the snapping process. (This can +// also be achieved using layers, at a coarser level of granularity.) +// +// Caveats: +// +// - Because S2Builder only works with edges, it cannot distinguish between +// the empty and full polygons. If your application can generate both the +// empty and full polygons, you must implement logic outside of this class. +// +// Example showing how to snap a polygon to E7 coordinates: +// +// using s2builderutil::IntLatLngSnapFunction; +// S2Builder builder(S2Builder::Options(IntLatLngSnapFunction(7))); +// S2Polygon output; +// builder.StartLayer(absl::make_unique(&output)); +// builder.AddPolygon(input); +// S2Error error; +// if (!builder.Build(&error)) { +// S2_LOG(ERROR) << error; +// ... +// } +class S2Builder { + public: + // Indicates whether the input edges are undirected. Typically this is + // specified for each output layer (e.g., s2builderutil::S2PolygonLayer). + // + // Directed edges are preferred, since otherwise the output is ambiguous. + // For example, output polygons may be the *inverse* of the intended result + // (e.g., a polygon intended to represent the world's oceans may instead + // represent the world's land masses). Directed edges are also somewhat + // more efficient. + // + // However even with undirected edges, most S2Builder layer types try to + // preserve the input edge direction whenever possible. Generally, edges + // are reversed only when it would yield a simpler output. For example, + // S2PolygonLayer assumes that polygons created from undirected edges should + // cover at most half of the sphere. Similarly, S2PolylineVectorLayer + // assembles edges into as few polylines as possible, even if this means + // reversing some of the "undirected" input edges. + // + // For shapes with interiors, directed edges should be oriented so that the + // interior is to the left of all edges. This means that for a polygon with + // holes, the outer loops ("shells") should be directed counter-clockwise + // while the inner loops ("holes") should be directed clockwise. Note that + // S2Builder::AddPolygon() follows this convention automatically. + enum class EdgeType { DIRECTED, UNDIRECTED }; + + // A SnapFunction restricts the locations of the output vertices. For + // example, there are predefined snap functions that require vertices to be + // located at S2CellId centers or at E5/E6/E7 coordinates. The SnapFunction + // can also specify a minimum spacing between vertices (the "snap radius"). + // + // A SnapFunction defines the following methods: + // + // 1. The SnapPoint() method, which snaps a point P to a nearby point (the + // "candidate snap site"). Any point may be returned, including P + // itself (this is the "identity snap function"). + // + // 2. "snap_radius", the maximum distance that vertices can move when + // snapped. The snap_radius must be at least as large as the maximum + // distance between P and SnapPoint(P) for any point P. + // + // 3. "max_edge_deviation", the maximum distance that edges can move when + // snapped. It is slightly larger than "snap_radius" because when a + // geodesic edge is snapped, the center of the edge moves further than + // its endpoints. This value is computed automatically by S2Builder. + // + // 4. "min_vertex_separation", the guaranteed minimum distance between + // vertices in the output. This is generally a fraction of + // "snap_radius" where the fraction depends on the snap function. + // + // 5. A "min_edge_vertex_separation", the guaranteed minimum distance + // between edges and non-incident vertices in the output. This is + // generally a fraction of "snap_radius" where the fraction depends on + // the snap function. + // + // It is important to note that SnapPoint() does not define the actual + // mapping from input vertices to output vertices, since the points it + // returns (the candidate snap sites) are further filtered to ensure that + // they are separated by at least the snap radius. For example, if you + // specify E7 coordinates (2cm resolution) and a snap radius of 10m, then a + // subset of points returned by SnapPoint will be chosen (the "snap sites"), + // and each input vertex will be mapped to the closest site. Therefore you + // cannot assume that P is necessarily snapped to SnapPoint(P). + // + // S2Builder makes the following guarantees: + // + // 1. Every vertex is at a location returned by SnapPoint(). + // + // 2. Vertices are within "snap_radius" of the corresponding input vertex. + // + // 3. Edges are within "max_edge_deviation" of the corresponding input edge + // (a distance slightly larger than "snap_radius"). + // + // 4. Vertices are separated by at least "min_vertex_separation" + // (a fraction of "snap_radius" that depends on the snap function). + // + // 5. Edges and non-incident vertices are separated by at least + // "min_edge_vertex_separation" (a fraction of "snap_radius"). + // + // 6. Vertex and edge locations do not change unless one of the conditions + // above is not already met (idempotency / stability). + // + // 7. The topology of the input geometry is preserved (up to the creation + // of degeneracies). This means that there exists a continuous + // deformation from the input to the output such that no vertex + // crosses an edge. + class SnapFunction { + public: + virtual ~SnapFunction() {} + + // The maximum distance that vertices can move when snapped. + // + // If the snap radius is zero, then vertices are snapped together only if + // they are identical. Edges will not be snapped to any vertices other + // than their endpoints, even if there are vertices whose distance to the + // edge is zero, unless split_crossing_edges() is true. + // + // REQUIRES: snap_radius() <= kMaxSnapRadius + virtual S1Angle snap_radius() const = 0; + + // The maximum supported snap radius (equivalent to about 7800km). + static S1Angle kMaxSnapRadius(); + + // The maximum distance that the center of an edge can move when snapped. + // This is slightly larger than "snap_radius" because when a geodesic edge + // is snapped, the center of the edge moves further than its endpoints. + S1Angle max_edge_deviation() const; + + // The guaranteed minimum distance between vertices in the output. + // This is generally some fraction of "snap_radius". + virtual S1Angle min_vertex_separation() const = 0; + + // The guaranteed minimum spacing between edges and non-incident vertices + // in the output. This is generally some fraction of "snap_radius". + virtual S1Angle min_edge_vertex_separation() const = 0; + + // Returns a candidate snap site for the given point. The final vertex + // locations are a subset of the snap sites returned by this function + // (spaced at least "min_vertex_separation" apart). + // + // The only requirement is that SnapPoint(x) must return a point whose + // distance from "x" is no greater than "snap_radius". + virtual S2Point SnapPoint(const S2Point& point) const = 0; + + // Returns a deep copy of this SnapFunction. + virtual std::unique_ptr Clone() const = 0; + }; + + class Options { + public: + Options(); + + // Convenience constructor that calls set_snap_function(). + explicit Options(const SnapFunction& snap_function); + + // Sets the desired snap function. The snap function is copied + // internally, so you can safely pass a temporary object. + // + // Note that if your input data includes vertices that were created using + // S2::GetIntersection(), then you should use a "snap_radius" of + // at least S2::kIntersectionSnapRadius, e.g. by calling + // + // options.set_snap_function(s2builderutil::IdentitySnapFunction( + // S2::kIntersectionSnapRadius)); + // + // DEFAULT: s2builderutil::IdentitySnapFunction(S1Angle::Zero()) + // [This does no snapping and preserves all input vertices exactly.] + const SnapFunction& snap_function() const; + void set_snap_function(const SnapFunction& snap_function); + + // If true, then detect all pairs of crossing edges and eliminate them by + // adding a new vertex at their intersection point. + // + // When this option is true, the effective snap_radius() for edges is + // increased by S2::kIntersectionError to take into account the + // additional error when computing intersection points. In other words, + // edges may move by up to snap_radius() + S2::kIntersectionError. + // + // Undirected edges should always be used when the output is a polygon, + // since splitting a directed loop at a self-intersection converts it into + // two loops that don't define a consistent interior according to the + // "interior is on the left" rule. (On the other hand, it is fine to use + // directed edges when defining a polygon *mesh* because in that case the + // input consists of sibling edge pairs.) + // + // Self-intersections can also arise when importing data from a 2D + // projection. You can minimize this problem by subdividing the input + // edges so that the S2 edges (which are geodesics) stay close to the + // original projected edges (which are curves on the sphere). This can + // be done using s2builderutil::EdgeSplitter(), for example. + // + // DEFAULT: false + bool split_crossing_edges() const; + void set_split_crossing_edges(bool split_crossing_edges); + + // If true, then simplify the output geometry by replacing nearly straight + // chains of short edges with a single long edge. + // + // The combined effect of snapping and simplifying will not change the + // input by more than the guaranteed tolerances (see the list documented + // with the SnapFunction class). For example, simplified edges are + // guaranteed to pass within snap_radius() of the *original* positions of + // all vertices that were removed from that edge. This is a much tighter + // guarantee than can be achieved by snapping and simplifying separately. + // + // However, note that this option does not guarantee idempotency. In + // other words, simplifying geometry that has already been simplified once + // may simplify it further. (This is unavoidable, since tolerances are + // measured with respect to the original geometry, which is no longer + // available when the geometry is simplified a second time.) + // + // When the output consists of multiple layers, simplification is + // guaranteed to be consistent: for example, edge chains are simplified in + // the same way across layers, and simplification preserves topological + // relationships between layers (e.g., no crossing edges will be created). + // Note that edge chains in different layers do not need to be identical + // (or even have the same number of vertices, etc) in order to be + // simplified together. All that is required is that they are close + // enough together so that the same simplified edge can meet all of their + // individual snapping guarantees. + // + // Note that edge chains are approximated as parametric curves rather than + // point sets. This means that if an edge chain backtracks on itself (for + // example, ABCDEFEDCDEFGH) then such backtracking will be preserved to + // within snap_radius() (for example, if the preceding point were all in a + // straight line then the edge chain would be simplified to ACFCFH, noting + // that C and F have degree > 2 and therefore can't be simplified away). + // + // Simplified edges are assigned all labels associated with the edges of + // the simplified chain. + // + // For this option to have any effect, a SnapFunction with a non-zero + // snap_radius() must be specified. Also note that vertices specified + // using ForceVertex are never simplified away. + // + // DEFAULT: false + bool simplify_edge_chains() const; + void set_simplify_edge_chains(bool simplify_edge_chains); + + // If true, then snapping occurs only when the input geometry does not + // already meet the S2Builder output guarantees (see the SnapFunction + // class description for details). This means that if all input vertices + // are at snapped locations, all vertex pairs are separated by at least + // min_vertex_separation(), and all edge-vertex pairs are separated by at + // least min_edge_vertex_separation(), then no snapping is done. + // + // If false, then all vertex pairs and edge-vertex pairs closer than + // "snap_radius" will be considered for snapping. This can be useful, for + // example, if you know that your geometry contains errors and you want to + // make sure that features closer together than "snap_radius" are merged. + // + // This option is automatically turned off by simplify_edge_chains(), + // since simplifying edge chains is never guaranteed to be idempotent. + // + // DEFAULT: true + bool idempotent() const; + void set_idempotent(bool idempotent); + + // Options may be assigned and copied. + Options(const Options& options); + Options& operator=(const Options& options); + + private: + std::unique_ptr snap_function_; + bool split_crossing_edges_ = false; + bool simplify_edge_chains_ = false; + bool idempotent_ = true; + }; + + // The following classes are only needed by Layer implementations. + class GraphOptions; + class Graph; + + // For output layers that represent polygons, there is an ambiguity inherent + // in spherical geometry that does not exist in planar geometry. Namely, if + // a polygon has no edges, does it represent the empty polygon (containing + // no points) or the full polygon (containing all points)? This ambiguity + // also occurs for polygons that consist only of degeneracies, e.g. a + // degenerate loop with only two edges could be either a degenerate shell in + // the empty polygon or a degenerate hole in the full polygon. + // + // To resolve this ambiguity, an IsFullPolygonPredicate may be specified for + // each output layer (see AddIsFullPolygonPredicate below). If the output + // after snapping consists only of degenerate edges and/or sibling pairs + // (including the case where there are no edges at all), then the layer + // implementation calls the given predicate to determine whether the polygon + // is empty or full except for those degeneracies. The predicate is given + // an S2Builder::Graph containing the output edges, but note that in general + // the predicate must also have knowledge of the input geometry in order to + // determine the correct result. + // + // This predicate is only needed by layers that are assembled into polygons. + // It is not used by other layer types. + using IsFullPolygonPredicate = + std::function; + + // Default constructor; requires Init() to be called. + S2Builder(); + + // Convenience constructor that calls Init(). Note that to use the default + // options, C++ syntax requires an extra layer of parentheses: + // + // S2Builder builder{S2Builder::Options()}; + explicit S2Builder(const Options& options); + + // Initializes an S2Builder with the given options. + void Init(const Options& options); + const Options& options() const { return options_; } + + // Starts a new output layer. This method must be called before adding any + // edges to the S2Builder. You may call this method multiple times to build + // multiple geometric objects that are snapped to the same set of sites. + // + // For example, if you have a set of contour lines, then you could put each + // contour line in a separate layer. This keeps the contour lines separate + // from each other, while also ensuring that no crossing edges are created + // when they are snapped and/or simplified. (This is not true if the + // contour lines are snapped or simplified independently.) + // + // Similarly, if you have a set of polygons that share common boundaries + // (e.g., countries), you can snap and/or simplify them at the same time by + // putting them in different layers, while ensuring that their boundaries + // remain consistent (i.e., no crossing edges or T-vertices are introduced). + // + // Ownership of the layer is transferred to the S2Builder. Example usage: + // + // S2Polyline line1, line2; + // builder.StartLayer(make_unique(&line1))); + // ... Add edges using builder.AddEdge(), etc ... + // builder.StartLayer(make_unique(&line2))); + // ... Add edges using builder.AddEdge(), etc ... + // S2Error error; + // S2_CHECK(builder.Build(&error)) << error; // Builds "line1" & "line2" + class Layer; + void StartLayer(std::unique_ptr layer); + + // Adds a degenerate edge (representing a point) to the current layer. + void AddPoint(const S2Point& v); + + // Adds the given edge to the current layer. + void AddEdge(const S2Point& v0, const S2Point& v1); + + // Adds the edges in the given polyline. (Note that if the polyline + // consists of 0 or 1 vertices, this method does nothing.) + void AddPolyline(const S2Polyline& polyline); + + // Adds the edges in the given loop. If the sign() of the loop is negative + // (i.e. this loop represents a hole within a polygon), the edge directions + // are automatically reversed to ensure that the polygon interior is always + // to the left of every edge. + void AddLoop(const S2Loop& loop); + + // Adds the loops in the given polygon. Loops representing holes have their + // edge directions automatically reversed as described for AddLoop(). Note + // that this method does not distinguish between the empty and full polygons, + // i.e. adding a full polygon has the same effect as adding an empty one. + void AddPolygon(const S2Polygon& polygon); + + // Adds the edges of the given shape to the current layer. + void AddShape(const S2Shape& shape); + + // For layers that are assembled into polygons, this method specifies a + // predicate that is called when the output consists entirely of degenerate + // edges and/or sibling pairs. The predicate is given an S2Builder::Graph + // containing the output edges (if any) and is responsible for deciding + // whether this graph represents the empty polygon (possibly with degenerate + // shells) or the full polygon (possibly with degenerate holes). Note that + // this cannot be determined from the output edges alone; it also requires + // knowledge of the input geometry. (Also see IsFullPolygonPredicate above.) + // + // This method should be called at most once per layer; additional calls + // simply overwrite the previous value for the current layer. + // + // The default predicate simply returns false (i.e., degenerate polygons are + // assumed to be empty). Arguably it would better to return an error in + // this case, but the fact is that relatively few clients need to be able to + // construct full polygons, and it is unreasonable to expect all such + // clients to supply an appropriate predicate. + // + // The reason for having a predicate rather than a boolean value is that the + // predicate is responsible for determining whether the output polygon is + // empty or full. In general the input geometry is not degenerate, but + // rather collapses into a degenerate configuration due to snapping and/or + // simplification. + // + // TODO(ericv): Provide standard predicates to handle common cases, + // e.g. valid input geometry that becomes degenerate due to snapping. + void AddIsFullPolygonPredicate(IsFullPolygonPredicate predicate); + + // A predicate that returns an error indicating that no polygon predicate + // has been specified. + static bool IsFullPolygonUnspecified(const S2Builder::Graph& g, + S2Error* error); + + // Returns a predicate that returns a constant value (true or false); + static IsFullPolygonPredicate IsFullPolygon(bool is_full); + + // Forces a vertex to be located at the given position. This can be used to + // prevent certain input vertices from moving. However if you are trying to + // preserve part of the input boundary, be aware that this option does not + // prevent edges from being split by new vertices. + // + // Forced vertices are never snapped; if this is desired then you need to + // call options().snap_function().SnapPoint() explicitly. Forced vertices + // are also never simplified away (if simplify_edge_chains() is used). + // + // Caveat: Since this method can place vertices arbitrarily close together, + // S2Builder makes no minimum separation guaranteees with forced vertices. + void ForceVertex(const S2Point& vertex); + + // Every edge can have a set of non-negative integer labels attached to it. + // When used with an appropriate layer type, you can then retrieve the + // labels associated with each output edge. This can be useful when merging + // or combining data from several sources. (Note that in many cases it is + // easier to use separate output layers rather than labels.) + // + // Labels are 32-bit non-negative integers. To support other label types, + // you can use ValueLexicon to store the set of unique labels seen so far: + // + // ValueLexicon my_label_lexicon; + // builder.set_label(my_label_lexicon.Add(label)); + // + // The current set of labels is represented as a stack. This makes it easy + // to add and remove labels hierarchically (e.g., polygon 5, loop 2). Use + // set_label() and clear_labels() if you need at most one label per edge. + // + using Label = int32; + + // Clear the stack of labels. + void clear_labels(); + + // Add a label to the stack. + // REQUIRES: label >= 0. + void push_label(Label label); + + // Remove a label from the stack. + void pop_label(); + + // Convenience function that clears the stack and adds a single label. + // REQUIRES: label >= 0. + void set_label(Label label); + + // Performs the requested edge splitting, snapping, simplification, etc, and + // then assembles the resulting edges into the requested output layers. + // + // Returns true if all edges were assembled; otherwise sets "error" + // appropriately. Depending on the error, some or all output layers may + // have been created. Automatically resets the S2Builder state so that it + // can be reused. + // + // REQUIRES: error != nullptr. + bool Build(S2Error* error); + + // Clears all input data and resets the builder state. Any options + // specified are preserved. + void Reset(); + + private: + ////////////////////// Input Types ///////////////////////// + // All types associated with the S2Builder inputs are prefixed with "Input". + + // Identifies an input vertex. + using InputVertexId = int32; + + // Defines an input edge. + using InputEdge = std::pair; + + // Identifies an input edge. + using InputEdgeId = int32; + + // Identifies the set of input edge ids that were snapped to a given edge. + using InputEdgeIdSetId = int32; + + // Sort key for prioritizing input vertices. (Note that keys are *not* + // compared using std::less; see SortInputVertices for details.) + using InputVertexKey = std::pair; + + ////////////////////// Output Types ///////////////////////// + // These types define the output vertices and edges. + + // Identifies a snapped vertex ("snap site"). If there is only one layer, + // than SiteId is the same as Graph::VertexId, but if there are many layers + // then each Graph may contain only a subset of the sites. Also see + // GraphOptions::allow_vertex_filtering(). + using SiteId = int32; + + // Defines an output edge. + using Edge = std::pair; + + // Identifies an output edge. + using EdgeId = int32; + + // Identifies an output edge in a particular layer. + using LayerEdgeId = std::pair; + + class EdgeChainSimplifier; + + InputVertexId AddVertex(const S2Point& v); + void ChooseSites(); + void CopyInputEdges(); + std::vector SortInputVertices(); + void AddEdgeCrossings(const MutableS2ShapeIndex& input_edge_index); + void AddForcedSites(S2PointIndex* site_index); + bool is_forced(SiteId v) const; + void ChooseInitialSites(S2PointIndex* site_index); + S2Point SnapSite(const S2Point& point) const; + void CollectSiteEdges(const S2PointIndex& site_index); + void SortSitesByDistance(const S2Point& x, + gtl::compact_array* sites) const; + void AddExtraSites(const MutableS2ShapeIndex& input_edge_index); + void MaybeAddExtraSites(InputEdgeId edge_id, + InputEdgeId max_edge_id, + const std::vector& chain, + const MutableS2ShapeIndex& input_edge_index, + std::vector* snap_queue); + void AddExtraSite(const S2Point& new_site, + InputEdgeId max_edge_id, + const MutableS2ShapeIndex& input_edge_index, + std::vector* snap_queue); + S2Point GetSeparationSite(const S2Point& site_to_avoid, + const S2Point& v0, const S2Point& v1, + InputEdgeId input_edge_id) const; + S2Point GetCoverageEndpoint(const S2Point& p, const S2Point& x, + const S2Point& y, const S2Point& n) const; + void SnapEdge(InputEdgeId e, std::vector* chain) const; + + void BuildLayers(); + void BuildLayerEdges( + std::vector>* layer_edges, + std::vector>* layer_input_edge_ids, + IdSetLexicon* input_edge_id_set_lexicon); + void AddSnappedEdges( + InputEdgeId begin, InputEdgeId end, const GraphOptions& options, + std::vector* edges, std::vector* input_edge_ids, + IdSetLexicon* input_edge_id_set_lexicon, + std::vector>* site_vertices) const; + void MaybeAddInputVertex( + InputVertexId v, SiteId id, + std::vector>* site_vertices) const; + void AddSnappedEdge(SiteId src, SiteId dst, InputEdgeIdSetId id, + EdgeType edge_type, std::vector* edges, + std::vector* input_edge_ids) const; + void SimplifyEdgeChains( + const std::vector>& site_vertices, + std::vector>* layer_edges, + std::vector>* layer_input_edge_ids, + IdSetLexicon* input_edge_id_set_lexicon) const; + void MergeLayerEdges( + const std::vector>& layer_edges, + const std::vector>& layer_input_edge_ids, + std::vector* edges, + std::vector* input_edge_ids, + std::vector* edge_layers) const; + static bool StableLessThan(const Edge& a, const Edge& b, + const LayerEdgeId& ai, const LayerEdgeId& bi); + + //////////// Parameters ///////////// + + // S2Builder options. + Options options_; + + // The maximum distance (inclusive) that a vertex can move when snapped, + // equal to S1ChordAngle(options_.snap_function().snap_radius()). + S1ChordAngle site_snap_radius_ca_; + + // The maximum distance (inclusive) that an edge can move when snapping to a + // snap site. It can be slightly larger than the site snap radius when + // edges are being split at crossings. + S1ChordAngle edge_snap_radius_ca_; + + S1Angle max_edge_deviation_; + S1ChordAngle edge_site_query_radius_ca_; + S1ChordAngle min_edge_length_to_split_ca_; + + S1Angle min_site_separation_; + S1ChordAngle min_site_separation_ca_; + S1ChordAngle min_edge_site_separation_ca_; + S1ChordAngle min_edge_site_separation_ca_limit_; + + S1ChordAngle max_adjacent_site_separation_ca_; + + // The squared sine of the edge snap radius. This is equivalent to the snap + // radius (squared) for distances measured through the interior of the + // sphere to the plane containing an edge. This value is used only when + // interpolating new points along edges (see GetSeparationSite). + double edge_snap_radius_sin2_; + + // A copy of the argument to Build(). + S2Error* error_; + + // True if snapping was requested. This is true if either snap_radius() is + // positive, or split_crossing_edges() is true (which implicitly requests + // snapping to ensure that both crossing edges are snapped to the + // intersection point). + bool snapping_requested_; + + // Initially false, and set to true when it is discovered that at least one + // input vertex or edge does not meet the output guarantees (e.g., that + // vertices are separated by at least snap_function.min_vertex_separation). + bool snapping_needed_; + + //////////// Input Data ///////////// + + // A flag indicating whether label_set_ has been modified since the last + // time label_set_id_ was computed. + bool label_set_modified_; + + std::vector input_vertices_; + std::vector input_edges_; + + std::vector> layers_; + std::vector layer_options_; + std::vector layer_begins_; + std::vector layer_is_full_polygon_predicates_; + + // Each input edge has "label set id" (an int32) representing the set of + // labels attached to that edge. This vector is populated only if at least + // one label is used. + using LabelSetId = int32; + std::vector label_set_ids_; + IdSetLexicon label_set_lexicon_; + + // The current set of labels (represented as a stack). + std::vector; + // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, + // but this seems to be mostly pedantic. + template + using EnableIfForwardIterator = absl::enable_if_t::iterator_category, + std::forward_iterator_tag>::value>; + static constexpr bool NoexceptCopyable() { + return std::is_nothrow_copy_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool NoexceptMovable() { + return std::is_nothrow_move_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool DefaultConstructorIsNonTrivial() { + return !absl::is_trivially_default_constructible::value; + } + + public: + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename allocator_type::value_type; + using pointer = typename allocator_type::pointer; + using const_pointer = typename allocator_type::const_pointer; + using reference = typename allocator_type::reference; + using const_reference = typename allocator_type::const_reference; + using size_type = typename allocator_type::size_type; + using difference_type = typename allocator_type::difference_type; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static constexpr size_type inline_elements = + (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) + : static_cast(N)); + + FixedArray( + const FixedArray& other, + const allocator_type& a = allocator_type()) noexcept(NoexceptCopyable()) + : FixedArray(other.begin(), other.end(), a) {} + + FixedArray( + FixedArray&& other, + const allocator_type& a = allocator_type()) noexcept(NoexceptMovable()) + : FixedArray(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end()), a) {} + + // Creates an array object that can store `n` elements. + // Note that trivially constructible elements will be uninitialized. + explicit FixedArray(size_type n, const allocator_type& a = allocator_type()) + : storage_(n, a) { + if (DefaultConstructorIsNonTrivial()) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end()); + } + } + + // Creates an array initialized with `n` copies of `val`. + FixedArray(size_type n, const value_type& val, + const allocator_type& a = allocator_type()) + : storage_(n, a) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end(), val); + } + + // Creates an array initialized with the size and contents of `init_list`. + FixedArray(std::initializer_list init_list, + const allocator_type& a = allocator_type()) + : FixedArray(init_list.begin(), init_list.end(), a) {} + + // Creates an array initialized with the elements from the input + // range. The array's size will always be `std::distance(first, last)`. + // REQUIRES: Iterator must be a forward_iterator or better. + // TODO(user) The same disambiguation in std::vector requires only + // InputIterator, not ForwardIterator. Investigate this restriction. + template * = nullptr> + FixedArray(Iterator first, Iterator last, + const allocator_type& a = allocator_type()) + : storage_(std::distance(first, last), a) { + memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last); + } + + ~FixedArray() noexcept { + for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) { + AllocatorTraits::destroy(storage_.alloc(), cur); + } + } + + // Assignments are deleted because they break the invariant that the size of a + // `FixedArray` never changes. + void operator=(FixedArray&&) = delete; + void operator=(const FixedArray&) = delete; + + // FixedArray::size() + // + // Returns the length of the fixed array. + size_type size() const { return storage_.size(); } + + // FixedArray::max_size() + // + // Returns the largest possible value of `std::distance(begin(), end())` for a + // `FixedArray`. This is equivalent to the most possible addressable bytes + // over the number of bytes taken by T. + constexpr size_type max_size() const { + return (std::numeric_limits::max)() / sizeof(value_type); + } + + // FixedArray::empty() + // + // Returns whether or not the fixed array is empty. + bool empty() const { return size() == 0; } + + // FixedArray::memsize() + // + // Returns the memory size of the fixed array in bytes. + size_t memsize() const { return size() * sizeof(value_type); } + + // FixedArray::data() + // + // Returns a const T* pointer to elements of the `FixedArray`. This pointer + // can be used to access (but not modify) the contained elements. + const_pointer data() const { return AsValueType(storage_.begin()); } + + // Overload of FixedArray::data() to return a T* pointer to elements of the + // fixed array. This pointer can be used to access and modify the contained + // elements. + pointer data() { return AsValueType(storage_.begin()); } + + // FixedArray::operator[] + // + // Returns a reference the ith element of the fixed array. + // REQUIRES: 0 <= i < size() + reference operator[](size_type i) { + assert(i < size()); + return data()[i]; + } + + // Overload of FixedArray::operator()[] to return a const reference to the + // ith element of the fixed array. + // REQUIRES: 0 <= i < size() + const_reference operator[](size_type i) const { + assert(i < size()); + return data()[i]; + } + + // FixedArray::at + // + // Bounds-checked access. Returns a reference to the ith element of the + // fiexed array, or throws std::out_of_range + reference at(size_type i) { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + } + return data()[i]; + } + + // Overload of FixedArray::at() to return a const reference to the ith element + // of the fixed array. + const_reference at(size_type i) const { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + } + return data()[i]; + } + + // FixedArray::front() + // + // Returns a reference to the first element of the fixed array. + reference front() { return *begin(); } + + // Overload of FixedArray::front() to return a reference to the first element + // of a fixed array of const values. + const_reference front() const { return *begin(); } + + // FixedArray::back() + // + // Returns a reference to the last element of the fixed array. + reference back() { return *(end() - 1); } + + // Overload of FixedArray::back() to return a reference to the last element + // of a fixed array of const values. + const_reference back() const { return *(end() - 1); } + + // FixedArray::begin() + // + // Returns an iterator to the beginning of the fixed array. + iterator begin() { return data(); } + + // Overload of FixedArray::begin() to return a const iterator to the + // beginning of the fixed array. + const_iterator begin() const { return data(); } + + // FixedArray::cbegin() + // + // Returns a const iterator to the beginning of the fixed array. + const_iterator cbegin() const { return begin(); } + + // FixedArray::end() + // + // Returns an iterator to the end of the fixed array. + iterator end() { return data() + size(); } + + // Overload of FixedArray::end() to return a const iterator to the end of the + // fixed array. + const_iterator end() const { return data() + size(); } + + // FixedArray::cend() + // + // Returns a const iterator to the end of the fixed array. + const_iterator cend() const { return end(); } + + // FixedArray::rbegin() + // + // Returns a reverse iterator from the end of the fixed array. + reverse_iterator rbegin() { return reverse_iterator(end()); } + + // Overload of FixedArray::rbegin() to return a const reverse iterator from + // the end of the fixed array. + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + // FixedArray::crbegin() + // + // Returns a const reverse iterator from the end of the fixed array. + const_reverse_iterator crbegin() const { return rbegin(); } + + // FixedArray::rend() + // + // Returns a reverse iterator from the beginning of the fixed array. + reverse_iterator rend() { return reverse_iterator(begin()); } + + // Overload of FixedArray::rend() for returning a const reverse iterator + // from the beginning of the fixed array. + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // FixedArray::crend() + // + // Returns a reverse iterator from the beginning of the fixed array. + const_reverse_iterator crend() const { return rend(); } + + // FixedArray::fill() + // + // Assigns the given `value` to all elements in the fixed array. + void fill(const value_type& val) { std::fill(begin(), end(), val); } + + // Relational operators. Equality operators are elementwise using + // `operator==`, while order operators order FixedArrays lexicographically. + friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) { + return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), + rhs.end()); + } + + friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) { + return rhs < lhs; + } + + friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) { + return !(rhs < lhs); + } + + friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) { + return !(lhs < rhs); + } + + template + friend H AbslHashValue(H h, const FixedArray& v) { + return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()), + v.size()); + } + + private: + // StorageElement + // + // For FixedArrays with a C-style-array value_type, StorageElement is a POD + // wrapper struct called StorageElementWrapper that holds the value_type + // instance inside. This is needed for construction and destruction of the + // entire array regardless of how many dimensions it has. For all other cases, + // StorageElement is just an alias of value_type. + // + // Maintainer's Note: The simpler solution would be to simply wrap value_type + // in a struct whether it's an array or not. That causes some paranoid + // diagnostics to misfire, believing that 'data()' returns a pointer to a + // single element, rather than the packed array that it really is. + // e.g.: + // + // FixedArray buf(1); + // sprintf(buf.data(), "foo"); + // + // error: call to int __builtin___sprintf_chk(etc...) + // will always overflow destination buffer [-Werror] + // + template , + size_t InnerN = std::extent::value> + struct StorageElementWrapper { + InnerT array[InnerN]; + }; + + using StorageElement = + absl::conditional_t::value, + StorageElementWrapper, value_type>; + using StorageElementBuffer = + absl::aligned_storage_t; + + static pointer AsValueType(pointer ptr) { return ptr; } + static pointer AsValueType(StorageElementWrapper* ptr) { + return std::addressof(ptr->array); + } + + static_assert(sizeof(StorageElement) == sizeof(value_type), ""); + static_assert(alignof(StorageElement) == alignof(value_type), ""); + + struct NonEmptyInlinedStorage { + StorageElement* data() { + return reinterpret_cast(inlined_storage_.data()); + } + +#ifdef ADDRESS_SANITIZER + void* RedzoneBegin() { return &redzone_begin_; } + void* RedzoneEnd() { return &redzone_end_ + 1; } +#endif // ADDRESS_SANITIZER + + void AnnotateConstruct(size_type); + void AnnotateDestruct(size_type); + + ADDRESS_SANITIZER_REDZONE(redzone_begin_); + std::array inlined_storage_; + ADDRESS_SANITIZER_REDZONE(redzone_end_); + }; + + struct EmptyInlinedStorage { + StorageElement* data() { return nullptr; } + void AnnotateConstruct(size_type) {} + void AnnotateDestruct(size_type) {} + }; + + using InlinedStorage = + absl::conditional_t; + + // Storage + // + // An instance of Storage manages the inline and out-of-line memory for + // instances of FixedArray. This guarantees that even when construction of + // individual elements fails in the FixedArray constructor body, the + // destructor for Storage will still be called and out-of-line memory will be + // properly deallocated. + // + class Storage : public InlinedStorage { + public: + Storage(size_type n, const allocator_type& a) + : size_alloc_(n, a), data_(InitializeData()) {} + + ~Storage() noexcept { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateDestruct(size()); + } else { + AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size()); + } + } + + size_type size() const { return size_alloc_.template get<0>(); } + StorageElement* begin() const { return data_; } + StorageElement* end() const { return begin() + size(); } + allocator_type& alloc() { + return size_alloc_.template get<1>(); + } + + private: + static bool UsingInlinedStorage(size_type n) { + return n <= inline_elements; + } + + StorageElement* InitializeData() { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateConstruct(size()); + return InlinedStorage::data(); + } else { + return reinterpret_cast( + AllocatorTraits::allocate(alloc(), size())); + } + } + + // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s + container_internal::CompressedTuple size_alloc_; + StorageElement* data_; + }; + + Storage storage_; +}; + +template +constexpr size_t FixedArray::kInlineBytesDefault; + +template +constexpr typename FixedArray::size_type + FixedArray::inline_elements; + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateConstruct( + typename FixedArray::size_type n) { +#ifdef ADDRESS_SANITIZER + if (!n) return; + ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n); + ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin()); +#endif // ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateDestruct( + typename FixedArray::size_type n) { +#ifdef ADDRESS_SANITIZER + if (!n) return; + ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd()); + ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data()); +#endif // ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} +} // namespace absl + +using absl::FixedArray; + +#endif // S2_THIRD_PARTY_ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/inst/include/s2/third_party/absl/container/inlined_vector.h b/inst/include/s2/third_party/absl/container/inlined_vector.h new file mode 100644 index 0000000..2da7934 --- /dev/null +++ b/inst/include/s2/third_party/absl/container/inlined_vector.h @@ -0,0 +1,1453 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: inlined_vector.h +// ----------------------------------------------------------------------------- +// +// This header file contains the declaration and definition of an "inlined +// vector" which behaves in an equivalent fashion to a `std::vector`, except +// that storage for small sequences of the vector are provided inline without +// requiring any heap allocation. +// +// An `absl::InlinedVector` specifies the default capacity `N` as one of +// its template parameters. Instances where `size() <= N` hold contained +// elements in inline space. Typically `N` is very small so that sequences that +// are expected to be short do not require allocations. +// +// An `absl::InlinedVector` does not usually require a specific allocator. If +// the inlined vector grows beyond its initial constraints, it will need to +// allocate (as any normal `std::vector` would). This is usually performed with +// the default allocator (defined as `std::allocator`). Optionally, a custom +// allocator type may be specified as `A` in `absl::InlinedVector`. + +#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_INLINED_VECTOR_H_ +#define S2_THIRD_PARTY_ABSL_CONTAINER_INLINED_VECTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "s2/third_party/absl/algorithm/algorithm.h" +#include "s2/third_party/absl/base/internal/throw_delegate.h" +#include "s2/third_party/absl/base/optimization.h" +#include "s2/third_party/absl/base/port.h" +#include "s2/third_party/absl/memory/memory.h" + + +namespace absl { + +// ----------------------------------------------------------------------------- +// InlinedVector +// ----------------------------------------------------------------------------- +// +// An `absl::InlinedVector` is designed to be a drop-in replacement for +// `std::vector` for use cases where the vector's size is sufficiently small +// that it can be inlined. If the inlined vector does grow beyond its estimated +// capacity, it will trigger an initial allocation on the heap, and will behave +// as a `std:vector`. The API of the `absl::InlinedVector` within this file is +// designed to cover the same API footprint as covered by `std::vector`. +template > +class InlinedVector { + static_assert(N > 0, "InlinedVector requires inline capacity greater than 0"); + constexpr static typename A::size_type inlined_capacity() { + return static_cast(N); + } + + template + using DisableIfIntegral = + absl::enable_if_t::value>; + + template + using EnableIfInputIterator = absl::enable_if_t::iterator_category, + std::input_iterator_tag>::value>; + + template + using IteratorCategory = + typename std::iterator_traits::iterator_category; + + using rvalue_reference = typename A::value_type&&; + + public: + using allocator_type = A; + using value_type = typename allocator_type::value_type; + using pointer = typename allocator_type::pointer; + using const_pointer = typename allocator_type::const_pointer; + using reference = typename allocator_type::reference; + using const_reference = typename allocator_type::const_reference; + using size_type = typename allocator_type::size_type; + using difference_type = typename allocator_type::difference_type; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + // --------------------------------------------------------------------------- + // InlinedVector Constructors and Destructor + // --------------------------------------------------------------------------- + + // Creates an empty inlined vector with a default initialized allocator. + InlinedVector() noexcept(noexcept(allocator_type())) + : allocator_and_tag_(allocator_type()) {} + + // Creates an empty inlined vector with a specified allocator. + explicit InlinedVector(const allocator_type& alloc) noexcept + : allocator_and_tag_(alloc) {} + + // Creates an inlined vector with `n` copies of `value_type()`. + explicit InlinedVector(size_type n, + const allocator_type& alloc = allocator_type()) + : allocator_and_tag_(alloc) { + InitAssign(n); + } + + // Creates an inlined vector with `n` copies of `v`. + InlinedVector(size_type n, const_reference v, + const allocator_type& alloc = allocator_type()) + : allocator_and_tag_(alloc) { + InitAssign(n, v); + } + + // Creates an inlined vector of copies of the values in `init_list`. + InlinedVector(std::initializer_list init_list, + const allocator_type& alloc = allocator_type()) + : allocator_and_tag_(alloc) { + AppendRange(init_list.begin(), init_list.end()); + } + + // Creates an inlined vector with elements constructed from the provided + // Iterator range [`first`, `last`). + // + // NOTE: The `enable_if` prevents ambiguous interpretation between a call to + // this constructor with two integral arguments and a call to the above + // `InlinedVector(size_type, const_reference)` constructor. + template * = nullptr> + InlinedVector(InputIterator first, InputIterator last, + const allocator_type& alloc = allocator_type()) + : allocator_and_tag_(alloc) { + AppendRange(first, last); + } + + // Creates a copy of `other` using `other`'s allocator. + InlinedVector(const InlinedVector& other); + + // Creates a copy of `other` but with a specified allocator. + InlinedVector(const InlinedVector& other, const allocator_type& alloc); + + // Creates an inlined vector by moving in the contents of `other`. + // + // NOTE: This move constructor does not allocate and only moves the underlying + // objects, so its `noexcept` specification depends on whether moving the + // underlying objects can throw or not. We assume: + // a) move constructors should only throw due to allocation failure and + // b) if `value_type`'s move constructor allocates, it uses the same + // allocation function as the `InlinedVector`'s allocator, so the move + // constructor is non-throwing if the allocator is non-throwing or + // `value_type`'s move constructor is specified as `noexcept`. + InlinedVector(InlinedVector&& v) noexcept( + absl::allocator_is_nothrow::value || + std::is_nothrow_move_constructible::value); + + // Creates an inlined vector by moving in the contents of `other`. + // + // NOTE: This move constructor allocates and subsequently moves the underlying + // objects, so its `noexcept` specification depends on whether the allocation + // can throw and whether moving the underlying objects can throw. Based on the + // same assumptions as above, the `noexcept` specification is dominated by + // whether the allocation can throw regardless of whether `value_type`'s move + // constructor is specified as `noexcept`. + InlinedVector(InlinedVector&& v, const allocator_type& alloc) noexcept( + absl::allocator_is_nothrow::value); + + ~InlinedVector() { clear(); } + + // --------------------------------------------------------------------------- + // InlinedVector Member Accessors + // --------------------------------------------------------------------------- + + // `InlinedVector::empty()` + // + // Checks if the inlined vector has no elements. + bool empty() const noexcept { return !size(); } + + // `InlinedVector::size()` + // + // Returns the number of elements in the inlined vector. + size_type size() const noexcept { return tag().size(); } + + // `InlinedVector::max_size()` + // + // Returns the maximum number of elements the vector can hold. + size_type max_size() const noexcept { + // One bit of the size storage is used to indicate whether the inlined + // vector is allocated. As a result, the maximum size of the container that + // we can express is half of the max for `size_type`. + return (std::numeric_limits::max)() / 2; + } + + // `InlinedVector::capacity()` + // + // Returns the number of elements that can be stored in the inlined vector + // without requiring a reallocation of underlying memory. + // + // NOTE: For most inlined vectors, `capacity()` should equal + // `inlined_capacity()`. For inlined vectors which exceed this capacity, they + // will no longer be inlined and `capacity()` will equal its capacity on the + // allocated heap. + size_type capacity() const noexcept { + return allocated() ? allocation().capacity() : inlined_capacity(); + } + + // `InlinedVector::data()` + // + // Returns a `pointer` to elements of the inlined vector. This pointer can be + // used to access and modify the contained elements. + // Only results within the range [`0`, `size()`) are defined. + pointer data() noexcept { + return allocated() ? allocated_space() : inlined_space(); + } + + // Overload of `InlinedVector::data()` to return a `const_pointer` to elements + // of the inlined vector. This pointer can be used to access (but not modify) + // the contained elements. + const_pointer data() const noexcept { + return allocated() ? allocated_space() : inlined_space(); + } + + // `InlinedVector::operator[]()` + // + // Returns a `reference` to the `i`th element of the inlined vector using the + // array operator. + reference operator[](size_type i) { + assert(i < size()); + return data()[i]; + } + + // Overload of `InlinedVector::operator[]()` to return a `const_reference` to + // the `i`th element of the inlined vector. + const_reference operator[](size_type i) const { + assert(i < size()); + return data()[i]; + } + + // `InlinedVector::at()` + // + // Returns a `reference` to the `i`th element of the inlined vector. + reference at(size_type i) { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "InlinedVector::at() failed bounds check"); + } + return data()[i]; + } + + // Overload of `InlinedVector::at()` to return a `const_reference` to the + // `i`th element of the inlined vector. + const_reference at(size_type i) const { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "InlinedVector::at() failed bounds check"); + } + return data()[i]; + } + + // `InlinedVector::front()` + // + // Returns a `reference` to the first element of the inlined vector. + reference front() { + assert(!empty()); + return at(0); + } + + // Overload of `InlinedVector::front()` returns a `const_reference` to the + // first element of the inlined vector. + const_reference front() const { + assert(!empty()); + return at(0); + } + + // `InlinedVector::back()` + // + // Returns a `reference` to the last element of the inlined vector. + reference back() { + assert(!empty()); + return at(size() - 1); + } + + // Overload of `InlinedVector::back()` to return a `const_reference` to the + // last element of the inlined vector. + const_reference back() const { + assert(!empty()); + return at(size() - 1); + } + + // `InlinedVector::begin()` + // + // Returns an `iterator` to the beginning of the inlined vector. + iterator begin() noexcept { return data(); } + + // Overload of `InlinedVector::begin()` to return a `const_iterator` to + // the beginning of the inlined vector. + const_iterator begin() const noexcept { return data(); } + + // `InlinedVector::end()` + // + // Returns an `iterator` to the end of the inlined vector. + iterator end() noexcept { return data() + size(); } + + // Overload of `InlinedVector::end()` to return a `const_iterator` to the + // end of the inlined vector. + const_iterator end() const noexcept { return data() + size(); } + + // `InlinedVector::cbegin()` + // + // Returns a `const_iterator` to the beginning of the inlined vector. + const_iterator cbegin() const noexcept { return begin(); } + + // `InlinedVector::cend()` + // + // Returns a `const_iterator` to the end of the inlined vector. + const_iterator cend() const noexcept { return end(); } + + // `InlinedVector::rbegin()` + // + // Returns a `reverse_iterator` from the end of the inlined vector. + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + + // Overload of `InlinedVector::rbegin()` to return a + // `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + // `InlinedVector::rend()` + // + // Returns a `reverse_iterator` from the beginning of the inlined vector. + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + + // Overload of `InlinedVector::rend()` to return a `const_reverse_iterator` + // from the beginning of the inlined vector. + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + // `InlinedVector::crbegin()` + // + // Returns a `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + + // `InlinedVector::crend()` + // + // Returns a `const_reverse_iterator` from the beginning of the inlined + // vector. + const_reverse_iterator crend() const noexcept { return rend(); } + + // `InlinedVector::get_allocator()` + // + // Returns a copy of the allocator of the inlined vector. + allocator_type get_allocator() const { return allocator(); } + + + // --------------------------------------------------------------------------- + // InlinedVector Member Mutators + // --------------------------------------------------------------------------- + + // `InlinedVector::operator=()` + // + // Replaces the contents of the inlined vector with copies of the elements in + // the provided `std::initializer_list`. + InlinedVector& operator=(std::initializer_list init_list) { + AssignRange(init_list.begin(), init_list.end()); + return *this; + } + + // Overload of `InlinedVector::operator=()` to replace the contents of the + // inlined vector with the contents of `other`. + InlinedVector& operator=(const InlinedVector& other) { + if (ABSL_PREDICT_FALSE(this == &other)) return *this; + + // Optimized to avoid reallocation. + // Prefer reassignment to copy construction for elements. + if (size() < other.size()) { // grow + reserve(other.size()); + std::copy(other.begin(), other.begin() + size(), begin()); + std::copy(other.begin() + size(), other.end(), std::back_inserter(*this)); + } else { // maybe shrink + erase(begin() + other.size(), end()); + std::copy(other.begin(), other.end(), begin()); + } + return *this; + } + + // Overload of `InlinedVector::operator=()` to replace the contents of the + // inlined vector with the contents of `other`. + // + // NOTE: As a result of calling this overload, `other` may be empty or it's + // contents may be left in a moved-from state. + InlinedVector& operator=(InlinedVector&& other) { + if (ABSL_PREDICT_FALSE(this == &other)) return *this; + + if (other.allocated()) { + clear(); + tag().set_allocated_size(other.size()); + init_allocation(other.allocation()); + other.tag() = Tag(); + } else { + if (allocated()) clear(); + // Both are inlined now. + if (size() < other.size()) { + auto mid = std::make_move_iterator(other.begin() + size()); + std::copy(std::make_move_iterator(other.begin()), mid, begin()); + UninitializedCopy(mid, std::make_move_iterator(other.end()), end()); + } else { + auto new_end = std::copy(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end()), begin()); + Destroy(new_end, end()); + } + tag().set_inline_size(other.size()); + } + return *this; + } + + // `InlinedVector::assign()` + // + // Replaces the contents of the inlined vector with `n` copies of `v`. + void assign(size_type n, const_reference v) { + if (n <= size()) { // Possibly shrink + std::fill_n(begin(), n, v); + erase(begin() + n, end()); + return; + } + // Grow + reserve(n); + std::fill_n(begin(), size(), v); + if (allocated()) { + UninitializedFill(allocated_space() + size(), allocated_space() + n, v); + tag().set_allocated_size(n); + } else { + UninitializedFill(inlined_space() + size(), inlined_space() + n, v); + tag().set_inline_size(n); + } + } + + // Overload of `InlinedVector::assign()` to replace the contents of the + // inlined vector with copies of the values in the provided + // `std::initializer_list`. + void assign(std::initializer_list init_list) { + AssignRange(init_list.begin(), init_list.end()); + } + + // Overload of `InlinedVector::assign()` to replace the contents of the + // inlined vector with values constructed from the range [`first`, `last`). + template * = nullptr> + void assign(InputIterator first, InputIterator last) { + AssignRange(first, last); + } + + // `InlinedVector::resize()` + // + // Resizes the inlined vector to contain `n` elements. If `n` is smaller than + // the inlined vector's current size, extra elements are destroyed. If `n` is + // larger than the initial size, new elements are value-initialized. + void resize(size_type n); + + // Overload of `InlinedVector::resize()` to resize the inlined vector to + // contain `n` elements where, if `n` is larger than `size()`, the new values + // will be copy-constructed from `v`. + void resize(size_type n, const_reference v); + + // `InlinedVector::insert()` + // + // Copies `v` into `position`, returning an `iterator` pointing to the newly + // inserted element. + iterator insert(const_iterator position, const_reference v) { + return emplace(position, v); + } + + // Overload of `InlinedVector::insert()` for moving `v` into `position`, + // returning an iterator pointing to the newly inserted element. + iterator insert(const_iterator position, rvalue_reference v) { + return emplace(position, std::move(v)); + } + + // Overload of `InlinedVector::insert()` for inserting `n` contiguous copies + // of `v` starting at `position`. Returns an `iterator` pointing to the first + // of the newly inserted elements. + iterator insert(const_iterator position, size_type n, const_reference v) { + return InsertWithCount(position, n, v); + } + + // Overload of `InlinedVector::insert()` for copying the contents of the + // `std::initializer_list` into the vector starting at `position`. Returns an + // `iterator` pointing to the first of the newly inserted elements. + iterator insert(const_iterator position, + std::initializer_list init_list) { + return insert(position, init_list.begin(), init_list.end()); + } + + // Overload of `InlinedVector::insert()` for inserting elements constructed + // from the range [`first`, `last`). Returns an `iterator` pointing to the + // first of the newly inserted elements. + // + // NOTE: The `enable_if` is intended to disambiguate the two three-argument + // overloads of `insert()`. + template > + iterator insert(const_iterator position, InputIterator first, + InputIterator last) { + return InsertWithRange(position, first, last, + IteratorCategory()); + } + + // `InlinedVector::emplace()` + // + // Constructs and inserts an object in the inlined vector at the given + // `position`, returning an `iterator` pointing to the newly emplaced element. + template + iterator emplace(const_iterator position, Args&&... args); + + // `InlinedVector::emplace_back()` + // + // Constructs and appends a new element to the end of the inlined vector, + // returning a `reference` to the emplaced element. + template + reference emplace_back(Args&&... args) { + size_type s = size(); + assert(s <= capacity()); + if (ABSL_PREDICT_FALSE(s == capacity())) { + return GrowAndEmplaceBack(std::forward(args)...); + } + assert(s < capacity()); + + pointer space; + if (allocated()) { + tag().set_allocated_size(s + 1); + space = allocated_space(); + } else { + tag().set_inline_size(s + 1); + space = inlined_space(); + } + return Construct(space + s, std::forward(args)...); + } + + // `InlinedVector::push_back()` + // + // Appends a copy of `v` to the end of the inlined vector. + void push_back(const_reference v) { static_cast(emplace_back(v)); } + + // Overload of `InlinedVector::push_back()` for moving `v` into a newly + // appended element. + void push_back(rvalue_reference v) { + static_cast(emplace_back(std::move(v))); + } + + // `InlinedVector::pop_back()` + // + // Destroys the element at the end of the inlined vector and shrinks the size + // by `1` (unless the inlined vector is empty, in which case this is a no-op). + void pop_back() noexcept { + assert(!empty()); + size_type s = size(); + if (allocated()) { + Destroy(allocated_space() + s - 1, allocated_space() + s); + tag().set_allocated_size(s - 1); + } else { + Destroy(inlined_space() + s - 1, inlined_space() + s); + tag().set_inline_size(s - 1); + } + } + + // `InlinedVector::erase()` + // + // Erases the element at `position` of the inlined vector, returning an + // `iterator` pointing to the first element following the erased element. + // + // NOTE: May return the end iterator, which is not dereferencable. + iterator erase(const_iterator position) { + assert(position >= begin()); + assert(position < end()); + + iterator pos = const_cast(position); + std::move(pos + 1, end(), pos); + pop_back(); + return pos; + } + + // Overload of `InlinedVector::erase()` for erasing all elements in the + // range [`from`, `to`) in the inlined vector. Returns an `iterator` pointing + // to the first element following the range erased or the end iterator if `to` + // was the end iterator. + iterator erase(const_iterator from, const_iterator to); + + // `InlinedVector::clear()` + // + // Destroys all elements in the inlined vector, sets the size of `0` and + // deallocates the heap allocation if the inlined vector was allocated. + void clear() noexcept { + size_type s = size(); + if (allocated()) { + Destroy(allocated_space(), allocated_space() + s); + allocation().Dealloc(allocator()); + } else if (s != 0) { // do nothing for empty vectors + Destroy(inlined_space(), inlined_space() + s); + } + tag() = Tag(); + } + + // `InlinedVector::reserve()` + // + // Enlarges the underlying representation of the inlined vector so it can hold + // at least `n` elements. This method does not change `size()` or the actual + // contents of the vector. + // + // NOTE: If `n` does not exceed `capacity()`, `reserve()` will have no + // effects. Otherwise, `reserve()` will reallocate, performing an n-time + // element-wise move of everything contained. + void reserve(size_type n) { + if (n > capacity()) { + // Make room for new elements + EnlargeBy(n - size()); + } + } + + // `InlinedVector::shrink_to_fit()` + // + // Reduces memory usage by freeing unused memory. After this call, calls to + // `capacity()` will be equal to `(std::max)(inlined_capacity(), size())`. + // + // If `size() <= inlined_capacity()` and the elements are currently stored on + // the heap, they will be moved to the inlined storage and the heap memory + // will be deallocated. + // + // If `size() > inlined_capacity()` and `size() < capacity()` the elements + // will be moved to a smaller heap allocation. + void shrink_to_fit() { + const auto s = size(); + if (ABSL_PREDICT_FALSE(!allocated() || s == capacity())) return; + + if (s <= inlined_capacity()) { + // Move the elements to the inlined storage. + // We have to do this using a temporary, because `inlined_storage` and + // `allocation_storage` are in a union field. + auto temp = std::move(*this); + assign(std::make_move_iterator(temp.begin()), + std::make_move_iterator(temp.end())); + return; + } + + // Reallocate storage and move elements. + // We can't simply use the same approach as above, because `assign()` would + // call into `reserve()` internally and reserve larger capacity than we need + Allocation new_allocation(allocator(), s); + UninitializedCopy(std::make_move_iterator(allocated_space()), + std::make_move_iterator(allocated_space() + s), + new_allocation.buffer()); + ResetAllocation(new_allocation, s); + } + + // `InlinedVector::swap()` + // + // Swaps the contents of this inlined vector with the contents of `other`. + void swap(InlinedVector& other); + + template + friend Hash AbslHashValue(Hash hash, const InlinedVector& inlined_vector) { + const_pointer p = inlined_vector.data(); + size_type n = inlined_vector.size(); + return Hash::combine(Hash::combine_contiguous(std::move(hash), p, n), n); + } + + private: + // Holds whether the vector is allocated or not in the lowest bit and the size + // in the high bits: + // `size_ = (size << 1) | is_allocated;` + class Tag { + public: + Tag() : size_(0) {} + size_type size() const { return size_ / 2; } + void add_size(size_type n) { size_ += n * 2; } + void set_inline_size(size_type n) { size_ = n * 2; } + void set_allocated_size(size_type n) { size_ = (n * 2) + 1; } + bool allocated() const { return size_ % 2; } + + private: + size_type size_; + }; + + // Derives from `allocator_type` to use the empty base class optimization. + // If the `allocator_type` is stateless, we can store our instance for free. + class AllocatorAndTag : private allocator_type { + public: + explicit AllocatorAndTag(const allocator_type& a) : allocator_type(a) {} + + Tag& tag() { return tag_; } + const Tag& tag() const { return tag_; } + + allocator_type& allocator() { return *this; } + const allocator_type& allocator() const { return *this; } + + private: + Tag tag_; + }; + + class Allocation { + public: + Allocation(allocator_type& a, size_type capacity) + : capacity_(capacity), buffer_(Create(a, capacity)) {} + + void Dealloc(allocator_type& a) { + std::allocator_traits::deallocate(a, buffer_, capacity_); + } + + size_type capacity() const { return capacity_; } + + const_pointer buffer() const { return buffer_; } + + pointer buffer() { return buffer_; } + + private: + static pointer Create(allocator_type& a, size_type n) { + return std::allocator_traits::allocate(a, n); + } + + size_type capacity_; + pointer buffer_; + }; + + const Tag& tag() const { return allocator_and_tag_.tag(); } + + Tag& tag() { return allocator_and_tag_.tag(); } + + Allocation& allocation() { + return reinterpret_cast(rep_.allocation_storage.allocation); + } + + const Allocation& allocation() const { + return reinterpret_cast( + rep_.allocation_storage.allocation); + } + + void init_allocation(const Allocation& allocation) { + new (&rep_.allocation_storage.allocation) Allocation(allocation); + } + + // TODO(absl-team): investigate whether the reinterpret_cast is appropriate. + pointer inlined_space() { + return reinterpret_cast( + std::addressof(rep_.inlined_storage.inlined[0])); + } + + const_pointer inlined_space() const { + return reinterpret_cast( + std::addressof(rep_.inlined_storage.inlined[0])); + } + + pointer allocated_space() { return allocation().buffer(); } + + const_pointer allocated_space() const { return allocation().buffer(); } + + const allocator_type& allocator() const { + return allocator_and_tag_.allocator(); + } + + allocator_type& allocator() { return allocator_and_tag_.allocator(); } + + bool allocated() const { return tag().allocated(); } + + // Enlarge the underlying representation so we can store `size_ + delta` elems + // in allocated space. The size is not changed, and any newly added memory is + // not initialized. + void EnlargeBy(size_type delta); + + // Shift all elements from `position` to `end()` by `n` places to the right. + // If the vector needs to be enlarged, memory will be allocated. + // Returns `iterator`s pointing to the start of the previously-initialized + // portion and the start of the uninitialized portion of the created gap. + // The number of initialized spots is `pair.second - pair.first`. The number + // of raw spots is `n - (pair.second - pair.first)`. + // + // Updates the size of the InlinedVector internally. + std::pair ShiftRight(const_iterator position, + size_type n); + + void ResetAllocation(Allocation new_allocation, size_type new_size) { + if (allocated()) { + Destroy(allocated_space(), allocated_space() + size()); + assert(begin() == allocated_space()); + allocation().Dealloc(allocator()); + allocation() = new_allocation; + } else { + Destroy(inlined_space(), inlined_space() + size()); + init_allocation(new_allocation); // bug: only init once + } + tag().set_allocated_size(new_size); + } + + template + reference GrowAndEmplaceBack(Args&&... args) { + assert(size() == capacity()); + const size_type s = size(); + + Allocation new_allocation(allocator(), 2 * capacity()); + + reference new_element = + Construct(new_allocation.buffer() + s, std::forward(args)...); + UninitializedCopy(std::make_move_iterator(data()), + std::make_move_iterator(data() + s), + new_allocation.buffer()); + + ResetAllocation(new_allocation, s + 1); + + return new_element; + } + + void InitAssign(size_type n); + + void InitAssign(size_type n, const_reference v); + + template + reference Construct(pointer p, Args&&... args) { + std::allocator_traits::construct( + allocator(), p, std::forward(args)...); + return *p; + } + + template + void UninitializedCopy(Iterator src, Iterator src_last, pointer dst) { + for (; src != src_last; ++dst, ++src) Construct(dst, *src); + } + + template + void UninitializedFill(pointer dst, pointer dst_last, const Args&... args) { + for (; dst != dst_last; ++dst) Construct(dst, args...); + } + + // Destroy [`from`, `to`) in place. + void Destroy(pointer from, pointer to); + + template + void AppendRange(Iterator first, Iterator last, std::input_iterator_tag) { + std::copy(first, last, std::back_inserter(*this)); + } + + template + void AppendRange(Iterator first, Iterator last, std::forward_iterator_tag); + + template + void AppendRange(Iterator first, Iterator last) { + AppendRange(first, last, IteratorCategory()); + } + + template + void AssignRange(Iterator first, Iterator last, std::input_iterator_tag); + + template + void AssignRange(Iterator first, Iterator last, std::forward_iterator_tag); + + template + void AssignRange(Iterator first, Iterator last) { + AssignRange(first, last, IteratorCategory()); + } + + iterator InsertWithCount(const_iterator position, size_type n, + const_reference v); + + template + iterator InsertWithRange(const_iterator position, InputIterator first, + InputIterator last, std::input_iterator_tag); + + template + iterator InsertWithRange(const_iterator position, ForwardIterator first, + ForwardIterator last, std::forward_iterator_tag); + + // Stores either the inlined or allocated representation + union Rep { + using ValueTypeBuffer = + absl::aligned_storage_t; + using AllocationBuffer = + absl::aligned_storage_t; + + // Structs wrap the buffers to perform indirection that solves a bizarre + // compilation error on Visual Studio (all known versions). + struct InlinedRep { + // `N` used in place of `inlined_capacity()` due to b/119696660 + ValueTypeBuffer inlined[N]; + }; + struct AllocatedRep { + AllocationBuffer allocation; + }; + + InlinedRep inlined_storage; + AllocatedRep allocation_storage; + }; + + AllocatorAndTag allocator_and_tag_; + Rep rep_; +}; + +// ----------------------------------------------------------------------------- +// InlinedVector Non-Member Functions +// ----------------------------------------------------------------------------- + +// `swap()` +// +// Swaps the contents of two inlined vectors. This convenience function +// simply calls `InlinedVector::swap()`. +template +void swap(InlinedVector& a, + InlinedVector& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +// `operator==()` +// +// Tests the equivalency of the contents of two inlined vectors. +template +bool operator==(const InlinedVector& a, + const InlinedVector& b) { + return absl::equal(a.begin(), a.end(), b.begin(), b.end()); +} + +// `operator!=()` +// +// Tests the inequality of the contents of two inlined vectors. +template +bool operator!=(const InlinedVector& a, + const InlinedVector& b) { + return !(a == b); +} + +// `operator<()` +// +// Tests whether the contents of one inlined vector are less than the contents +// of another through a lexicographical comparison operation. +template +bool operator<(const InlinedVector& a, + const InlinedVector& b) { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +} + +// `operator>()` +// +// Tests whether the contents of one inlined vector are greater than the +// contents of another through a lexicographical comparison operation. +template +bool operator>(const InlinedVector& a, + const InlinedVector& b) { + return b < a; +} + +// `operator<=()` +// +// Tests whether the contents of one inlined vector are less than or equal to +// the contents of another through a lexicographical comparison operation. +template +bool operator<=(const InlinedVector& a, + const InlinedVector& b) { + return !(b < a); +} + +// `operator>=()` +// +// Tests whether the contents of one inlined vector are greater than or equal to +// the contents of another through a lexicographical comparison operation. +template +bool operator>=(const InlinedVector& a, + const InlinedVector& b) { + return !(a < b); +} + +// ----------------------------------------------------------------------------- +// Implementation of InlinedVector +// +// Do not depend on any below implementation details! +// ----------------------------------------------------------------------------- + +template +InlinedVector::InlinedVector(const InlinedVector& other) + : allocator_and_tag_(other.allocator()) { + reserve(other.size()); + if (allocated()) { + UninitializedCopy(other.begin(), other.end(), allocated_space()); + tag().set_allocated_size(other.size()); + } else { + UninitializedCopy(other.begin(), other.end(), inlined_space()); + tag().set_inline_size(other.size()); + } +} + +template +InlinedVector::InlinedVector(const InlinedVector& other, + const allocator_type& alloc) + : allocator_and_tag_(alloc) { + reserve(other.size()); + if (allocated()) { + UninitializedCopy(other.begin(), other.end(), allocated_space()); + tag().set_allocated_size(other.size()); + } else { + UninitializedCopy(other.begin(), other.end(), inlined_space()); + tag().set_inline_size(other.size()); + } +} + +template +InlinedVector::InlinedVector(InlinedVector&& other) noexcept( + absl::allocator_is_nothrow::value || + std::is_nothrow_move_constructible::value) + : allocator_and_tag_(other.allocator_and_tag_) { + if (other.allocated()) { + // We can just steal the underlying buffer from the source. + // That leaves the source empty, so we clear its size. + init_allocation(other.allocation()); + other.tag() = Tag(); + } else { + UninitializedCopy( + std::make_move_iterator(other.inlined_space()), + std::make_move_iterator(other.inlined_space() + other.size()), + inlined_space()); + } +} + +template +InlinedVector::InlinedVector(InlinedVector&& other, + const allocator_type& alloc) noexcept( // + absl::allocator_is_nothrow::value) + : allocator_and_tag_(alloc) { + if (other.allocated()) { + if (alloc == other.allocator()) { + // We can just steal the allocation from the source. + tag() = other.tag(); + init_allocation(other.allocation()); + other.tag() = Tag(); + } else { + // We need to use our own allocator + reserve(other.size()); + UninitializedCopy(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end()), + allocated_space()); + tag().set_allocated_size(other.size()); + } + } else { + UninitializedCopy( + std::make_move_iterator(other.inlined_space()), + std::make_move_iterator(other.inlined_space() + other.size()), + inlined_space()); + tag().set_inline_size(other.size()); + } +} + +template +void InlinedVector::InitAssign(size_type n, const_reference v) { + if (n > inlined_capacity()) { + Allocation new_allocation(allocator(), n); + init_allocation(new_allocation); + UninitializedFill(allocated_space(), allocated_space() + n, v); + tag().set_allocated_size(n); + } else { + UninitializedFill(inlined_space(), inlined_space() + n, v); + tag().set_inline_size(n); + } +} + +template +void InlinedVector::InitAssign(size_type n) { + if (n > inlined_capacity()) { + Allocation new_allocation(allocator(), n); + init_allocation(new_allocation); + UninitializedFill(allocated_space(), allocated_space() + n); + tag().set_allocated_size(n); + } else { + UninitializedFill(inlined_space(), inlined_space() + n); + tag().set_inline_size(n); + } +} + +template +void InlinedVector::resize(size_type n) { + size_type s = size(); + if (n < s) { + erase(begin() + n, end()); + return; + } + reserve(n); + assert(capacity() >= n); + + // Fill new space with elements constructed in-place. + if (allocated()) { + UninitializedFill(allocated_space() + s, allocated_space() + n); + tag().set_allocated_size(n); + } else { + UninitializedFill(inlined_space() + s, inlined_space() + n); + tag().set_inline_size(n); + } +} + +template +void InlinedVector::resize(size_type n, const_reference v) { + size_type s = size(); + if (n < s) { + erase(begin() + n, end()); + return; + } + reserve(n); + assert(capacity() >= n); + + // Fill new space with copies of 'v'. + if (allocated()) { + UninitializedFill(allocated_space() + s, allocated_space() + n, v); + tag().set_allocated_size(n); + } else { + UninitializedFill(inlined_space() + s, inlined_space() + n, v); + tag().set_inline_size(n); + } +} + +template +template +auto InlinedVector::emplace(const_iterator position, Args&&... args) + -> iterator { + assert(position >= begin()); + assert(position <= end()); + if (ABSL_PREDICT_FALSE(position == end())) { + emplace_back(std::forward(args)...); + return end() - 1; + } + + T new_t = T(std::forward(args)...); + + auto range = ShiftRight(position, 1); + if (range.first == range.second) { + // constructing into uninitialized memory + Construct(range.first, std::move(new_t)); + } else { + // assigning into moved-from object + *range.first = T(std::move(new_t)); + } + + return range.first; +} + +template +auto InlinedVector::erase(const_iterator from, const_iterator to) + -> iterator { + assert(begin() <= from); + assert(from <= to); + assert(to <= end()); + + iterator range_start = const_cast(from); + iterator range_end = const_cast(to); + + size_type s = size(); + ptrdiff_t erase_gap = std::distance(range_start, range_end); + if (erase_gap > 0) { + pointer space; + if (allocated()) { + space = allocated_space(); + tag().set_allocated_size(s - erase_gap); + } else { + space = inlined_space(); + tag().set_inline_size(s - erase_gap); + } + std::move(range_end, space + s, range_start); + Destroy(space + s - erase_gap, space + s); + } + return range_start; +} + +template +void InlinedVector::swap(InlinedVector& other) { + using std::swap; // Augment ADL with `std::swap`. + if (ABSL_PREDICT_FALSE(this == &other)) return; + + if (allocated() && other.allocated()) { + // Both out of line, so just swap the tag, allocation, and allocator. + swap(tag(), other.tag()); + swap(allocation(), other.allocation()); + swap(allocator(), other.allocator()); + return; + } + if (!allocated() && !other.allocated()) { + // Both inlined: swap up to smaller size, then move remaining elements. + InlinedVector* a = this; + InlinedVector* b = &other; + if (size() < other.size()) { + swap(a, b); + } + + const size_type a_size = a->size(); + const size_type b_size = b->size(); + assert(a_size >= b_size); + // `a` is larger. Swap the elements up to the smaller array size. + std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size, + b->inlined_space()); + + // Move the remaining elements: + // [`b_size`, `a_size`) from `a` -> [`b_size`, `a_size`) from `b` + b->UninitializedCopy(a->inlined_space() + b_size, + a->inlined_space() + a_size, + b->inlined_space() + b_size); + a->Destroy(a->inlined_space() + b_size, a->inlined_space() + a_size); + + swap(a->tag(), b->tag()); + swap(a->allocator(), b->allocator()); + assert(b->size() == a_size); + assert(a->size() == b_size); + return; + } + + // One is out of line, one is inline. + // We first move the elements from the inlined vector into the + // inlined space in the other vector. We then put the other vector's + // pointer/capacity into the originally inlined vector and swap + // the tags. + InlinedVector* a = this; + InlinedVector* b = &other; + if (a->allocated()) { + swap(a, b); + } + assert(!a->allocated()); + assert(b->allocated()); + const size_type a_size = a->size(); + const size_type b_size = b->size(); + // In an optimized build, `b_size` would be unused. + static_cast(b_size); + + // Made Local copies of `size()`, don't need `tag()` accurate anymore + swap(a->tag(), b->tag()); + + // Copy `b_allocation` out before `b`'s union gets clobbered by `inline_space` + Allocation b_allocation = b->allocation(); + + b->UninitializedCopy(a->inlined_space(), a->inlined_space() + a_size, + b->inlined_space()); + a->Destroy(a->inlined_space(), a->inlined_space() + a_size); + + a->allocation() = b_allocation; + + if (a->allocator() != b->allocator()) { + swap(a->allocator(), b->allocator()); + } + + assert(b->size() == a_size); + assert(a->size() == b_size); +} + +template +void InlinedVector::EnlargeBy(size_type delta) { + const size_type s = size(); + assert(s <= capacity()); + + size_type target = std::max(inlined_capacity(), s + delta); + + // Compute new capacity by repeatedly doubling current capacity + // TODO(user): Check and avoid overflow? + size_type new_capacity = capacity(); + while (new_capacity < target) { + new_capacity <<= 1; + } + + Allocation new_allocation(allocator(), new_capacity); + + UninitializedCopy(std::make_move_iterator(data()), + std::make_move_iterator(data() + s), + new_allocation.buffer()); + + ResetAllocation(new_allocation, s); +} + +template +auto InlinedVector::ShiftRight(const_iterator position, size_type n) + -> std::pair { + iterator start_used = const_cast(position); + iterator start_raw = const_cast(position); + size_type s = size(); + size_type required_size = s + n; + + if (required_size > capacity()) { + // Compute new capacity by repeatedly doubling current capacity + size_type new_capacity = capacity(); + while (new_capacity < required_size) { + new_capacity <<= 1; + } + // Move everyone into the new allocation, leaving a gap of `n` for the + // requested shift. + Allocation new_allocation(allocator(), new_capacity); + size_type index = position - begin(); + UninitializedCopy(std::make_move_iterator(data()), + std::make_move_iterator(data() + index), + new_allocation.buffer()); + UninitializedCopy(std::make_move_iterator(data() + index), + std::make_move_iterator(data() + s), + new_allocation.buffer() + index + n); + ResetAllocation(new_allocation, s); + + // New allocation means our iterator is invalid, so we'll recalculate. + // Since the entire gap is in new space, there's no used space to reuse. + start_raw = begin() + index; + start_used = start_raw; + } else { + // If we had enough space, it's a two-part move. Elements going into + // previously-unoccupied space need an `UninitializedCopy()`. Elements + // going into a previously-occupied space are just a `std::move()`. + iterator pos = const_cast(position); + iterator raw_space = end(); + size_type slots_in_used_space = raw_space - pos; + size_type new_elements_in_used_space = std::min(n, slots_in_used_space); + size_type new_elements_in_raw_space = n - new_elements_in_used_space; + size_type old_elements_in_used_space = + slots_in_used_space - new_elements_in_used_space; + + UninitializedCopy(std::make_move_iterator(pos + old_elements_in_used_space), + std::make_move_iterator(raw_space), + raw_space + new_elements_in_raw_space); + std::move_backward(pos, pos + old_elements_in_used_space, raw_space); + + // If the gap is entirely in raw space, the used space starts where the raw + // space starts, leaving no elements in used space. If the gap is entirely + // in used space, the raw space starts at the end of the gap, leaving all + // elements accounted for within the used space. + start_used = pos; + start_raw = pos + new_elements_in_used_space; + } + tag().add_size(n); + return std::make_pair(start_used, start_raw); +} + +template +void InlinedVector::Destroy(pointer from, pointer to) { + for (pointer cur = from; cur != to; ++cur) { + std::allocator_traits::destroy(allocator(), cur); + } +#ifndef NDEBUG + // Overwrite unused memory with `0xab` so we can catch uninitialized usage. + // Cast to `void*` to tell the compiler that we don't care that we might be + // scribbling on a vtable pointer. + if (from != to) { + auto len = sizeof(value_type) * std::distance(from, to); + std::memset(reinterpret_cast(from), 0xab, len); + } +#endif +} + +template +template +void InlinedVector::AppendRange(Iterator first, Iterator last, + std::forward_iterator_tag) { + auto length = std::distance(first, last); + reserve(size() + length); + if (allocated()) { + UninitializedCopy(first, last, allocated_space() + size()); + tag().set_allocated_size(size() + length); + } else { + UninitializedCopy(first, last, inlined_space() + size()); + tag().set_inline_size(size() + length); + } +} + +template +template +void InlinedVector::AssignRange(Iterator first, Iterator last, + std::input_iterator_tag) { + // Optimized to avoid reallocation. + // Prefer reassignment to copy construction for elements. + iterator out = begin(); + for (; first != last && out != end(); ++first, ++out) { + *out = *first; + } + erase(out, end()); + std::copy(first, last, std::back_inserter(*this)); +} + +template +template +void InlinedVector::AssignRange(Iterator first, Iterator last, + std::forward_iterator_tag) { + auto length = std::distance(first, last); + // Prefer reassignment to copy construction for elements. + if (static_cast(length) <= size()) { + erase(std::copy(first, last, begin()), end()); + return; + } + reserve(length); + iterator out = begin(); + for (; out != end(); ++first, ++out) *out = *first; + if (allocated()) { + UninitializedCopy(first, last, out); + tag().set_allocated_size(length); + } else { + UninitializedCopy(first, last, out); + tag().set_inline_size(length); + } +} + +template +auto InlinedVector::InsertWithCount(const_iterator position, + size_type n, const_reference v) + -> iterator { + assert(position >= begin() && position <= end()); + if (ABSL_PREDICT_FALSE(n == 0)) return const_cast(position); + + value_type copy = v; + std::pair it_pair = ShiftRight(position, n); + std::fill(it_pair.first, it_pair.second, copy); + UninitializedFill(it_pair.second, it_pair.first + n, copy); + + return it_pair.first; +} + +template +template +auto InlinedVector::InsertWithRange(const_iterator position, + InputIterator first, + InputIterator last, + std::input_iterator_tag) + -> iterator { + assert(position >= begin() && position <= end()); + size_type index = position - cbegin(); + size_type i = index; + while (first != last) insert(begin() + i++, *first++); + return begin() + index; +} + +template +template +auto InlinedVector::InsertWithRange(const_iterator position, + ForwardIterator first, + ForwardIterator last, + std::forward_iterator_tag) + -> iterator { + assert(position >= begin() && position <= end()); + if (ABSL_PREDICT_FALSE(first == last)) return const_cast(position); + + auto n = std::distance(first, last); + std::pair it_pair = ShiftRight(position, n); + size_type used_spots = it_pair.second - it_pair.first; + ForwardIterator open_spot = std::next(first, used_spots); + std::copy(first, open_spot, it_pair.first); + UninitializedCopy(open_spot, last, it_pair.second); + return it_pair.first; +} + +} // namespace absl + + +#endif // S2_THIRD_PARTY_ABSL_CONTAINER_INLINED_VECTOR_H_ diff --git a/inst/include/s2/third_party/absl/container/internal/compressed_tuple.h b/inst/include/s2/third_party/absl/container/internal/compressed_tuple.h new file mode 100644 index 0000000..b9b1a1d --- /dev/null +++ b/inst/include/s2/third_party/absl/container/internal/compressed_tuple.h @@ -0,0 +1,191 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Helper class to perform the Empty Base Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the optimization. If all types in Ts are empty +// classes, then CompressedTuple is itself an empty class. +// +// To access the members, use member get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// http://en.cppreference.com/w/cpp/language/ebo + +#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ +#define S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ + +#include +#include +#include + +#include "s2/third_party/absl/utility/utility.h" + +#ifdef _MSC_VER +// We need to mark these classes with this declspec to ensure that +// CompressedTuple happens. +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases) +#else // _MSC_VER +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC +#endif // _MSC_VER + +namespace absl { +namespace container_internal { + +template +class CompressedTuple; + +namespace internal_compressed_tuple { + +template +struct Elem; +template +struct Elem, I> + : std::tuple_element> {}; +template +using ElemT = typename Elem::type; + +// Use the __is_final intrinsic if available. Where it's not available, classes +// declared with the 'final' specifier cannot be used as CompressedTuple +// elements. +// TODO(user): Replace this with std::is_final in C++14. +template +constexpr bool IsFinal() { +#if defined(__clang__) || defined(__GNUC__) + return __is_final(T); +#else + return false; +#endif +} + +template +constexpr bool ShouldUseBase() { + return std::is_class::value && std::is_empty::value && !IsFinal(); +} + +// The storage class provides two specializations: +// - For empty classes, it stores T as a base class. +// - For everything else, it stores T as a member. +template >()> +struct Storage { + using T = ElemT; + T value; + constexpr Storage() = default; + explicit constexpr Storage(T&& v) : value(absl::forward(v)) {} + constexpr const T& get() const& { return value; } + T& get() & { return value; } + constexpr const T&& get() const&& { return absl::move(*this).value; } + T&& get() && { return std::move(*this).value; } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage + : ElemT { + using T = internal_compressed_tuple::ElemT; + constexpr Storage() = default; + explicit constexpr Storage(T&& v) : T(absl::forward(v)) {} + constexpr const T& get() const& { return *this; } + T& get() & { return *this; } + constexpr const T&& get() const&& { return absl::move(*this); } + T&& get() && { return std::move(*this); } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC + CompressedTupleImpl, absl::index_sequence> + // We use the dummy identity function through std::integral_constant to + // convince MSVC of accepting and expanding I in that context. Without it + // you would get: + // error C3548: 'I': parameter pack cannot be used in this context + : Storage, + std::integral_constant::value>... { + constexpr CompressedTupleImpl() = default; + explicit constexpr CompressedTupleImpl(Ts&&... args) + : Storage, I>(absl::forward(args))... {} +}; + +} // namespace internal_compressed_tuple + +// Helper class to perform the Empty Base Class Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the CompressedTuple. If all types in Ts are +// empty classes, then CompressedTuple is itself an empty class. +// +// To access the members, use member .get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// http://en.cppreference.com/w/cpp/language/ebo +template +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple + : private internal_compressed_tuple::CompressedTupleImpl< + CompressedTuple, absl::index_sequence_for> { + private: + template + using ElemT = internal_compressed_tuple::ElemT; + + public: + constexpr CompressedTuple() = default; + explicit constexpr CompressedTuple(Ts... base) + : CompressedTuple::CompressedTupleImpl(absl::forward(base)...) {} + + template + ElemT& get() & { + return internal_compressed_tuple::Storage::get(); + } + + template + constexpr const ElemT& get() const& { + return internal_compressed_tuple::Storage::get(); + } + + template + ElemT&& get() && { + return std::move(*this) + .internal_compressed_tuple::template Storage::get(); + } + + template + constexpr const ElemT&& get() const&& { + return absl::move(*this) + .internal_compressed_tuple::template Storage::get(); + } +}; + +// Explicit specialization for a zero-element tuple +// (needed to avoid ambiguous overloads for the default constructor). +template <> +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; + +} // namespace container_internal +} // namespace absl + +#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC + +#endif // S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ diff --git a/inst/include/s2/third_party/absl/container/internal/container_memory.h b/inst/include/s2/third_party/absl/container/internal/container_memory.h new file mode 100644 index 0000000..ab533fd --- /dev/null +++ b/inst/include/s2/third_party/absl/container/internal/container_memory.h @@ -0,0 +1,424 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ +#define S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ + +#ifdef ADDRESS_SANITIZER +#include +#endif + +#ifdef MEMORY_SANITIZER +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "s2/third_party/absl/memory/memory.h" +#include "s2/third_party/absl/utility/utility.h" + +namespace absl { +namespace container_internal { + +// Allocates at least n bytes aligned to the specified alignment. +// Alignment must be a power of 2. It must be positive. +// +// Note that many allocators don't honor alignment requirements above certain +// threshold (usually either alignof(std::max_align_t) or alignof(void*)). +// Allocate() doesn't apply alignment corrections. If the underlying allocator +// returns insufficiently alignment pointer, that's what you are going to get. +template +void* Allocate(Alloc* alloc, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + struct alignas(Alignment) M {}; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + A mem_alloc(*alloc); + void* p = AT::allocate(mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); + assert(reinterpret_cast(p) % Alignment == 0 && + "allocator does not respect alignment"); + return p; +} + +// The pointer must have been previously obtained by calling +// Allocate(alloc, n). +template +void Deallocate(Alloc* alloc, void* p, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + struct alignas(Alignment) M {}; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + A mem_alloc(*alloc); + AT::deallocate(mem_alloc, static_cast(p), + (n + sizeof(M) - 1) / sizeof(M)); +} + +namespace memory_internal { + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +template +void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, + absl::index_sequence) { + absl::allocator_traits::construct( + *alloc, ptr, std::get(std::forward(t))...); +} + +template +struct WithConstructedImplF { + template + decltype(std::declval()(std::declval())) operator()( + Args&&... args) const { + return std::forward(f)(T(std::forward(args)...)); + } + F&& f; +}; + +template +decltype(std::declval()(std::declval())) WithConstructedImpl( + Tuple&& t, absl::index_sequence, F&& f) { + return WithConstructedImplF{std::forward(f)}( + std::get(std::forward(t))...); +} + +template +auto TupleRefImpl(T&& t, absl::index_sequence) + -> decltype(std::forward_as_tuple(std::get(std::forward(t))...)) { + return std::forward_as_tuple(std::get(std::forward(t))...); +} + +// Returns a tuple of references to the elements of the input tuple. T must be a +// tuple. +template +auto TupleRef(T&& t) -> decltype( + TupleRefImpl(std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>())) { + return TupleRefImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +template +decltype(std::declval()(std::declval(), std::piecewise_construct, + std::declval>(), std::declval())) +DecomposePairImpl(F&& f, std::pair, V> p) { + const auto& key = std::get<0>(p.first); + return std::forward(f)(key, std::piecewise_construct, std::move(p.first), + std::move(p.second)); +} + +} // namespace memory_internal + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +template +void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { + memory_internal::ConstructFromTupleImpl( + alloc, ptr, std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +// Constructs T using the args specified in the tuple and calls F with the +// constructed value. +template +decltype(std::declval()(std::declval())) WithConstructed( + Tuple&& t, F&& f) { + return memory_internal::WithConstructedImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>(), + std::forward(f)); +} + +// Given arguments of an std::pair's consructor, PairArgs() returns a pair of +// tuples with references to the passed arguments. The tuples contain +// constructor arguments for the first and the second elements of the pair. +// +// The following two snippets are equivalent. +// +// 1. std::pair p(args...); +// +// 2. auto a = PairArgs(args...); +// std::pair p(std::piecewise_construct, +// std::move(p.first), std::move(p.second)); +inline std::pair, std::tuple<>> PairArgs() { return {}; } +template +std::pair, std::tuple> PairArgs(F&& f, S&& s) { + return {std::piecewise_construct, std::forward_as_tuple(std::forward(f)), + std::forward_as_tuple(std::forward(s))}; +} +template +std::pair, std::tuple> PairArgs( + const std::pair& p) { + return PairArgs(p.first, p.second); +} +template +std::pair, std::tuple> PairArgs(std::pair&& p) { + return PairArgs(std::forward(p.first), std::forward(p.second)); +} +template +auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) + -> decltype(std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s)))) { + return std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s))); +} + +// A helper function for implementing apply() in map policies. +template +auto DecomposePair(F&& f, Args&&... args) + -> decltype(memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...))) { + return memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...)); +} + +// A helper function for implementing apply() in set policies. +template +decltype(std::declval()(std::declval(), std::declval())) +DecomposeValue(F&& f, Arg&& arg) { + const auto& key = arg; + return std::forward(f)(key, std::forward(arg)); +} + +// Helper functions for asan and msan. +inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { +#ifdef ADDRESS_SANITIZER + ASAN_POISON_MEMORY_REGION(m, s); +#endif +#ifdef MEMORY_SANITIZER + __msan_poison(m, s); +#endif + (void)m; + (void)s; +} + +inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) { +#ifdef ADDRESS_SANITIZER + ASAN_UNPOISON_MEMORY_REGION(m, s); +#endif +#ifdef MEMORY_SANITIZER + __msan_unpoison(m, s); +#endif + (void)m; + (void)s; +} + +template +inline void SanitizerPoisonObject(const T* object) { + SanitizerPoisonMemoryRegion(object, sizeof(T)); +} + +template +inline void SanitizerUnpoisonObject(const T* object) { + SanitizerUnpoisonMemoryRegion(object, sizeof(T)); +} + +namespace memory_internal { + +// If Pair is a standard-layout type, OffsetOf::kFirst and +// OffsetOf::kSecond are equivalent to offsetof(Pair, first) and +// offsetof(Pair, second) respectively. Otherwise they are -1. +// +// The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout +// type, which is non-portable. +template +struct OffsetOf { + static constexpr size_t kFirst = -1; + static constexpr size_t kSecond = -1; +}; + +template +struct OffsetOf::type> { + static constexpr size_t kFirst = offsetof(Pair, first); + static constexpr size_t kSecond = offsetof(Pair, second); +}; + +template +struct IsLayoutCompatible { + private: + struct Pair { + K first; + V second; + }; + + // Is P layout-compatible with Pair? + template + static constexpr bool LayoutCompatible() { + return std::is_standard_layout

() && sizeof(P) == sizeof(Pair) && + alignof(P) == alignof(Pair) && + memory_internal::OffsetOf

::kFirst == + memory_internal::OffsetOf::kFirst && + memory_internal::OffsetOf

::kSecond == + memory_internal::OffsetOf::kSecond; + } + + public: + // Whether pair and pair are layout-compatible. If they are, + // then it is safe to store them in a union and read from either. + static constexpr bool value = std::is_standard_layout() && + std::is_standard_layout() && + memory_internal::OffsetOf::kFirst == 0 && + LayoutCompatible>() && + LayoutCompatible>(); +}; + +} // namespace memory_internal + +// The internal storage type for key-value containers like flat_hash_map. +// +// It is convenient for the value_type of a flat_hash_map to be +// pair; the "const K" prevents accidental modification of the key +// when dealing with the reference returned from find() and similar methods. +// However, this creates other problems; we want to be able to emplace(K, V) +// efficiently with move operations, and similarly be able to move a +// pair in insert(). +// +// The solution is this union, which aliases the const and non-const versions +// of the pair. This also allows flat_hash_map to work, even though +// that has the same efficiency issues with move in emplace() and insert() - +// but people do it anyway. +// +// If kMutableKeys is false, only the value member can be accessed. +// +// If kMutableKeys is true, key can be accessed through all slots while value +// and mutable_value must be accessed only via INITIALIZED slots. Slots are +// created and destroyed via mutable_value so that the key can be moved later. +// +// Accessing one of the union fields while the other is active is safe as +// long as they are layout-compatible, which is guaranteed by the definition of +// kMutableKeys. For C++11, the relevant section of the standard is +// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19) +template +union slot_type { + private: + static void emplace(slot_type* slot) { + // The construction of union doesn't do anything at runtime but it allows us + // to access its members without violating aliasing rules. + new (slot) slot_type; + } + // If pair and pair are layout-compatible, we can accept one + // or the other via slot_type. We are also free to access the key via + // slot_type::key in this case. + using kMutableKeys = + std::integral_constant::value>; + + public: + slot_type() {} + ~slot_type() = delete; + using value_type = std::pair; + using mutable_value_type = std::pair; + + value_type value; + mutable_value_type mutable_value; + K key; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct(*alloc, &slot->mutable_value, + std::forward(args)...); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::forward(args)...); + } + } + + // Construct this slot by moving from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &slot->mutable_value, std::move(other->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::move(other->value)); + } + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + if (kMutableKeys::value) { + absl::allocator_traits::destroy(*alloc, &slot->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &slot->value); + } + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + emplace(new_slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &new_slot->value, + std::move(old_slot->value)); + } + destroy(alloc, old_slot); + } + + template + static void swap(Allocator* alloc, slot_type* a, slot_type* b) { + if (kMutableKeys::value) { + using std::swap; + swap(a->mutable_value, b->mutable_value); + } else { + value_type tmp = std::move(a->value); + absl::allocator_traits::destroy(*alloc, &a->value); + absl::allocator_traits::construct(*alloc, &a->value, + std::move(b->value)); + absl::allocator_traits::destroy(*alloc, &b->value); + absl::allocator_traits::construct(*alloc, &b->value, + std::move(tmp)); + } + } + + template + static void move(Allocator* alloc, slot_type* src, slot_type* dest) { + if (kMutableKeys::value) { + dest->mutable_value = std::move(src->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &dest->value); + absl::allocator_traits::construct(*alloc, &dest->value, + std::move(src->value)); + } + } + + template + static void move(Allocator* alloc, slot_type* first, slot_type* last, + slot_type* result) { + for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) + move(alloc, src, dest); + } +}; + +} // namespace container_internal +} // namespace absl + +#endif // S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ diff --git a/inst/include/s2/third_party/absl/container/internal/layout.h b/inst/include/s2/third_party/absl/container/internal/layout.h new file mode 100644 index 0000000..52d091c --- /dev/null +++ b/inst/include/s2/third_party/absl/container/internal/layout.h @@ -0,0 +1,739 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// MOTIVATION AND TUTORIAL +// +// If you want to put in a single heap allocation N doubles followed by M ints, +// it's easy if N and M are known at compile time. +// +// struct S { +// double a[N]; +// int b[M]; +// }; +// +// S* p = new S; +// +// But what if N and M are known only in run time? Class template Layout to the +// rescue! It's a portable generalization of the technique known as struct hack. +// +// // This object will tell us everything we need to know about the memory +// // layout of double[N] followed by int[M]. It's structurally identical to +// // size_t[2] that stores N and M. It's very cheap to create. +// const Layout layout(N, M); +// +// // Allocate enough memory for both arrays. `AllocSize()` tells us how much +// // memory is needed. We are free to use any allocation function we want as +// // long as it returns aligned memory. +// std::unique_ptr p(new unsigned char[layout.AllocSize()]); +// +// // Obtain the pointer to the array of doubles. +// // Equivalent to `reinterpret_cast(p.get())`. +// // +// // We could have written layout.Pointer<0>(p) instead. If all the types are +// // unique you can use either form, but if some types are repeated you must +// // use the index form. +// double* a = layout.Pointer(p.get()); +// +// // Obtain the pointer to the array of ints. +// // Equivalent to `reinterpret_cast(p.get() + N * 8)`. +// int* b = layout.Pointer(p); +// +// If we are unable to specify sizes of all fields, we can pass as many sizes as +// we can to `Partial()`. In return, it'll allow us to access the fields whose +// locations and sizes can be computed from the provided information. +// `Partial()` comes in handy when the array sizes are embedded into the +// allocation. +// +// // size_t[1] containing N, size_t[1] containing M, double[N], int[M]. +// using L = Layout; +// +// unsigned char* Allocate(size_t n, size_t m) { +// const L layout(1, 1, n, m); +// unsigned char* p = new unsigned char[layout.AllocSize()]; +// *layout.Pointer<0>(p) = n; +// *layout.Pointer<1>(p) = m; +// return p; +// } +// +// void Use(unsigned char* p) { +// // First, extract N and M. +// // Specify that the first array has only one element. Using `prefix` we +// // can access the first two arrays but not more. +// constexpr auto prefix = L::Partial(1); +// size_t n = *prefix.Pointer<0>(p); +// size_t m = *prefix.Pointer<1>(p); +// +// // Now we can get pointers to the payload. +// const L layout(1, 1, n, m); +// double* a = layout.Pointer(p); +// int* b = layout.Pointer(p); +// } +// +// The layout we used above combines fixed-size with dynamically-sized fields. +// This is quite common. Layout is optimized for this use case and generates +// optimal code. All computations that can be performed at compile time are +// indeed performed at compile time. +// +// Efficiency tip: The order of fields matters. In `Layout` try to +// ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no +// padding in between arrays. +// +// You can manually override the alignment of an array by wrapping the type in +// `Aligned`. `Layout<..., Aligned, ...>` has exactly the same API +// and behavior as `Layout<..., T, ...>` except that the first element of the +// array of `T` is aligned to `N` (the rest of the elements follow without +// padding). `N` cannot be less than `alignof(T)`. +// +// `AllocSize()` and `Pointer()` are the most basic methods for dealing with +// memory layouts. Check out the reference or code below to discover more. +// +// EXAMPLE +// +// // Immutable move-only string with sizeof equal to sizeof(void*). The +// // string size and the characters are kept in the same heap allocation. +// class CompactString { +// public: +// CompactString(const char* s = "") { +// const size_t size = strlen(s); +// // size_t[1] followed by char[size + 1]. +// const L layout(1, size + 1); +// p_.reset(new unsigned char[layout.AllocSize()]); +// // If running under ASAN, mark the padding bytes, if any, to catch +// // memory errors. +// layout.PoisonPadding(p_.get()); +// // Store the size in the allocation. +// *layout.Pointer(p_.get()) = size; +// // Store the characters in the allocation. +// memcpy(layout.Pointer(p_.get()), s, size + 1); +// } +// +// size_t size() const { +// // Equivalent to reinterpret_cast(*p). +// return *L::Partial().Pointer(p_.get()); +// } +// +// const char* c_str() const { +// // Equivalent to reinterpret_cast(p.get() + sizeof(size_t)). +// // The argument in Partial(1) specifies that we have size_t[1] in front +// // of the characters. +// return L::Partial(1).Pointer(p_.get()); +// } +// +// private: +// // Our heap allocation contains a size_t followed by an array of chars. +// using L = Layout; +// std::unique_ptr p_; +// }; +// +// int main() { +// CompactString s = "hello"; +// assert(s.size() == 5); +// assert(strcmp(s.c_str(), "hello") == 0); +// } +// +// DOCUMENTATION +// +// The interface exported by this file consists of: +// - class `Layout<>` and its public members. +// - The public members of class `internal_layout::LayoutImpl<>`. That class +// isn't intended to be used directly, and its name and template parameter +// list are internal implementation details, but the class itself provides +// most of the functionality in this file. See comments on its members for +// detailed documentation. +// +// `Layout::Partial(count1,..., countm)` (where `m` <= `n`) returns a +// `LayoutImpl<>` object. `Layout layout(count1,..., countn)` +// creates a `Layout` object, which exposes the same functionality by inheriting +// from `LayoutImpl<>`. + +#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_LAYOUT_H_ +#define S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_LAYOUT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ADDRESS_SANITIZER +#include +#endif + +#include "s2/third_party/absl/meta/type_traits.h" +#include "s2/third_party/absl/strings/str_cat.h" +#include "s2/third_party/absl/types/span.h" +#include "s2/third_party/absl/utility/utility.h" + +#if defined(__GXX_RTTI) +#define ABSL_INTERNAL_HAS_CXA_DEMANGLE +#endif + +#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE +#include +#endif + +namespace absl { +namespace container_internal { + +// A type wrapper that instructs `Layout` to use the specific alignment for the +// array. `Layout<..., Aligned, ...>` has exactly the same API +// and behavior as `Layout<..., T, ...>` except that the first element of the +// array of `T` is aligned to `N` (the rest of the elements follow without +// padding). +// +// Requires: `N >= alignof(T)` and `N` is a power of 2. +template +struct Aligned; + +namespace internal_layout { + +template +struct NotAligned {}; + +template +struct NotAligned> { + static_assert(sizeof(T) == 0, "Aligned cannot be const-qualified"); +}; + +template +using IntToSize = size_t; + +template +using TypeToSize = size_t; + +template +struct Type : NotAligned { + using type = T; +}; + +template +struct Type> { + using type = T; +}; + +template +struct SizeOf : NotAligned, std::integral_constant {}; + +template +struct SizeOf> : std::integral_constant {}; + +// Note: workaround for https://gcc.gnu.org/PR88115 +template +struct AlignOf : NotAligned { + static constexpr size_t value = alignof(T); +}; + +template +struct AlignOf> { + static_assert(N % alignof(T) == 0, + "Custom alignment can't be lower than the type's alignment"); + static constexpr size_t value = N; +}; + +// Does `Ts...` contain `T`? +template +using Contains = absl::disjunction...>; + +template +using CopyConst = + typename std::conditional::value, const To, To>::type; + +// Note: We're not qualifying this with absl:: because it doesn't compile under +// MSVC. +template +using SliceType = Span; + +// This namespace contains no types. It prevents functions defined in it from +// being found by ADL. +namespace adl_barrier { + +template +constexpr size_t Find(Needle, Needle, Ts...) { + static_assert(!Contains(), "Duplicate element type"); + return 0; +} + +template +constexpr size_t Find(Needle, T, Ts...) { + return adl_barrier::Find(Needle(), Ts()...) + 1; +} + +constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); } + +// Returns `q * m` for the smallest `q` such that `q * m >= n`. +// Requires: `m` is a power of two. It's enforced by IsLegalElementType below. +constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } + +constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } + +constexpr size_t Max(size_t a) { return a; } + +template +constexpr size_t Max(size_t a, size_t b, Ts... rest) { + return adl_barrier::Max(b < a ? a : b, rest...); +} + +template +string TypeName() { + string out; + int status = 0; + char* demangled = nullptr; +#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE + demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); +#endif + if (status == 0 && demangled != nullptr) { // Demangling succeeded. + absl::StrAppend(&out, "<", demangled, ">"); + free(demangled); + } else { +#if defined(__GXX_RTTI) || defined(_CPPRTTI) + absl::StrAppend(&out, "<", typeid(T).name(), ">"); +#endif + } + return out; +} + +} // namespace adl_barrier + +template +using EnableIf = typename std::enable_if::type; + +// Can `T` be a template argument of `Layout`? +template +using IsLegalElementType = std::integral_constant< + bool, !std::is_reference::value && !std::is_volatile::value && + !std::is_reference::type>::value && + !std::is_volatile::type>::value && + adl_barrier::IsPow2(AlignOf::value)>; + +template +class LayoutImpl; + +// Public base class of `Layout` and the result type of `Layout::Partial()`. +// +// `Elements...` contains all template arguments of `Layout` that created this +// instance. +// +// `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments +// passed to `Layout::Partial()` or `Layout::Layout()`. +// +// `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is +// `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we +// can compute offsets). +template +class LayoutImpl, absl::index_sequence, + absl::index_sequence> { + private: + static_assert(sizeof...(Elements) > 0, "At least one field is required"); + static_assert(absl::conjunction...>::value, + "Invalid element type (see IsLegalElementType)"); + + enum { + NumTypes = sizeof...(Elements), + NumSizes = sizeof...(SizeSeq), + NumOffsets = sizeof...(OffsetSeq), + }; + + // These are guaranteed by `Layout`. + static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), + "Internal error"); + static_assert(NumTypes > 0, "Internal error"); + + // Returns the index of `T` in `Elements...`. Results in a compilation error + // if `Elements...` doesn't contain exactly one instance of `T`. + template + static constexpr size_t ElementIndex() { + static_assert(Contains, Type::type>...>(), + "Type not found"); + return adl_barrier::Find(Type(), + Type::type>()...); + } + + template + using ElementAlignment = + AlignOf>::type>; + + public: + // Element types of all arrays packed in a tuple. + using ElementTypes = std::tuple::type...>; + + // Element type of the Nth array. + template + using ElementType = typename std::tuple_element::type; + + constexpr explicit LayoutImpl(IntToSize... sizes) + : size_{sizes...} {} + + // Alignment of the layout, equal to the strictest alignment of all elements. + // All pointers passed to the methods of layout must be aligned to this value. + static constexpr size_t Alignment() { + return adl_barrier::Max(AlignOf::value...); + } + + // Offset in bytes of the Nth array. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Offset<0>() == 0); // The ints starts from 0. + // assert(x.Offset<1>() == 16); // The doubles starts from 16. + // + // Requires: `N <= NumSizes && N < sizeof...(Ts)`. + template = 0> + constexpr size_t Offset() const { + return 0; + } + + template = 0> + constexpr size_t Offset() const { + static_assert(N < NumOffsets, "Index out of bounds"); + return adl_barrier::Align( + Offset() + SizeOf>() * size_[N - 1], + ElementAlignment::value); + } + + // Offset in bytes of the array with the specified element type. There must + // be exactly one such array and its zero-based index must be at most + // `NumSizes`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Offset() == 0); // The ints starts from 0. + // assert(x.Offset() == 16); // The doubles starts from 16. + template + constexpr size_t Offset() const { + return Offset()>(); + } + + // Offsets in bytes of all arrays for which the offsets are known. + constexpr std::array Offsets() const { + return {{Offset()...}}; + } + + // The number of elements in the Nth array. This is the Nth argument of + // `Layout::Partial()` or `Layout::Layout()` (zero-based). + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Size<0>() == 3); + // assert(x.Size<1>() == 4); + // + // Requires: `N < NumSizes`. + template + constexpr size_t Size() const { + static_assert(N < NumSizes, "Index out of bounds"); + return size_[N]; + } + + // The number of elements in the array with the specified element type. + // There must be exactly one such array and its zero-based index must be + // at most `NumSizes`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Size() == 3); + // assert(x.Size() == 4); + template + constexpr size_t Size() const { + return Size()>(); + } + + // The number of elements of all arrays for which they are known. + constexpr std::array Sizes() const { + return {{Size()...}}; + } + + // Pointer to the beginning of the Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer<0>(p); + // double* doubles = x.Pointer<1>(p); + // + // Requires: `N <= NumSizes && N < sizeof...(Ts)`. + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst>* Pointer(Char* p) const { + using C = typename std::remove_const::type; + static_assert( + std::is_same() || std::is_same() || + std::is_same(), + "The argument must be a pointer to [const] [signed|unsigned] char"); + // Use a named variable to work around b/33479323. + constexpr size_t alignment = Alignment(); + (void)alignment; + assert(reinterpret_cast(p) % alignment == 0); + return reinterpret_cast>*>(p + Offset()); + } + + // Pointer to the beginning of the array with the specified element type. + // There must be exactly one such array and its zero-based index must be at + // most `NumSizes`. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer(p); + // double* doubles = x.Pointer(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst* Pointer(Char* p) const { + return Pointer()>(p); + } + + // Pointers to all arrays for which pointers are known. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // int* ints; + // double* doubles; + // std::tie(ints, doubles) = x.Pointers(p); + // + // Requires: `p` is aligned to `Alignment()`. + // + // Note: We're not using ElementType alias here because it does not compile + // under MSVC. + template + std::tuple::type>*...> + Pointers(Char* p) const { + return std::tuple>*...>( + Pointer(p)...); + } + + // The Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice<0>(p); + // Span doubles = x.Slice<1>(p); + // + // Requires: `N < NumSizes`. + // Requires: `p` is aligned to `Alignment()`. + template + SliceType>> Slice(Char* p) const { + return SliceType>>(Pointer(p), Size()); + } + + // The array with the specified element type. There must be exactly one + // such array and its zero-based index must be less than `NumSizes`. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice(p); + // Span doubles = x.Slice(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + SliceType> Slice(Char* p) const { + return Slice()>(p); + } + + // All arrays with known sizes. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // Span ints; + // Span doubles; + // std::tie(ints, doubles) = x.Slices(p); + // + // Requires: `p` is aligned to `Alignment()`. + // + // Note: We're not using ElementType alias here because it does not compile + // under MSVC. + template + std::tuple::type>>...> + Slices(Char* p) const { + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed + // in 6.1). + (void)p; + return std::tuple>>...>( + Slice(p)...); + } + + // The size of the allocation that fits all arrays. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes + // + // Requires: `NumSizes == sizeof...(Ts)`. + constexpr size_t AllocSize() const { + static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); + return Offset() + + SizeOf>() * size_[NumTypes - 1]; + } + + // If built with --config=asan, poisons padding bytes (if any) in the + // allocation. The pointer must point to a memory block at least + // `AllocSize()` bytes in length. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // Requires: `p` is aligned to `Alignment()`. + template = 0> + void PoisonPadding(const Char* p) const { + Pointer<0>(p); // verify the requirements on `Char` and `p` + } + + template = 0> + void PoisonPadding(const Char* p) const { + static_assert(N < NumOffsets, "Index out of bounds"); + (void)p; +#ifdef ADDRESS_SANITIZER + PoisonPadding(p); + // The `if` is an optimization. It doesn't affect the observable behaviour. + if (ElementAlignment::value % ElementAlignment::value) { + size_t start = + Offset() + SizeOf>() * size_[N - 1]; + ASAN_POISON_MEMORY_REGION(p + start, Offset() - start); + } +#endif + } + + // Human-readable description of the memory layout. Useful for debugging. + // Slow. + // + // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed + // // by an unknown number of doubles. + // auto x = Layout::Partial(5, 3); + // assert(x.DebugString() == + // "@0(1)[5]; @8(4)[3]; @24(8)"); + // + // Each field is in the following format: @offset(sizeof)[size] ( + // may be missing depending on the target platform). For example, + // @8(4)[3] means that at offset 8 we have an array of ints, where each + // int is 4 bytes, and we have 3 of those ints. The size of the last field may + // be missing (as in the example above). Only fields with known offsets are + // described. Type names may differ across platforms: one compiler might + // produce "unsigned*" where another produces "unsigned int *". + string DebugString() const { + const auto offsets = Offsets(); + const size_t sizes[] = {SizeOf>()...}; + const string types[] = {adl_barrier::TypeName>()...}; + string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); + for (size_t i = 0; i != NumOffsets - 1; ++i) { + absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1], + "(", sizes[i + 1], ")"); + } + // NumSizes is a constant that may be zero. Some compilers cannot see that + // inside the if statement "size_[NumSizes - 1]" must be valid. + int last = static_cast(NumSizes) - 1; + if (NumTypes == NumSizes && last >= 0) { + absl::StrAppend(&res, "[", size_[last], "]"); + } + return res; + } + + private: + // Arguments of `Layout::Partial()` or `Layout::Layout()`. + size_t size_[NumSizes > 0 ? NumSizes : 1]; +}; + +template +using LayoutType = LayoutImpl< + std::tuple, absl::make_index_sequence, + absl::make_index_sequence>; + +} // namespace internal_layout + +// Descriptor of arrays of various types and sizes laid out in memory one after +// another. See the top of the file for documentation. +// +// Check out the public API of internal_layout::LayoutImpl above. The type is +// internal to the library but its methods are public, and they are inherited +// by `Layout`. +template +class Layout : public internal_layout::LayoutType { + public: + static_assert(sizeof...(Ts) > 0, "At least one field is required"); + static_assert( + absl::conjunction...>::value, + "Invalid element type (see IsLegalElementType)"); + + // The result type of `Partial()` with `NumSizes` arguments. + template + using PartialType = internal_layout::LayoutType; + + // `Layout` knows the element types of the arrays we want to lay out in + // memory but not the number of elements in each array. + // `Partial(size1, ..., sizeN)` allows us to specify the latter. The + // resulting immutable object can be used to obtain pointers to the + // individual arrays. + // + // It's allowed to pass fewer array sizes than the number of arrays. E.g., + // if all you need is to the offset of the second array, you only need to + // pass one argument -- the number of elements in the first array. + // + // // int[3] followed by 4 bytes of padding and an unknown number of + // // doubles. + // auto x = Layout::Partial(3); + // // doubles start at byte 16. + // assert(x.Offset<1>() == 16); + // + // If you know the number of elements in all arrays, you can still call + // `Partial()` but it's more convenient to use the constructor of `Layout`. + // + // Layout x(3, 5); + // + // Note: The sizes of the arrays must be specified in number of elements, + // not in bytes. + // + // Requires: `sizeof...(Sizes) <= sizeof...(Ts)`. + // Requires: all arguments are convertible to `size_t`. + template + static constexpr PartialType Partial(Sizes&&... sizes) { + static_assert(sizeof...(Sizes) <= sizeof...(Ts), ""); + return PartialType(absl::forward(sizes)...); + } + + // Creates a layout with the sizes of all arrays specified. If you know + // only the sizes of the first N arrays (where N can be zero), you can use + // `Partial()` defined above. The constructor is essentially equivalent to + // calling `Partial()` and passing in all array sizes; the constructor is + // provided as a convenient abbreviation. + // + // Note: The sizes of the arrays must be specified in number of elements, + // not in bytes. + constexpr explicit Layout(internal_layout::TypeToSize... sizes) + : internal_layout::LayoutType(sizes...) {} +}; + +} // namespace container_internal +} // namespace absl + +#endif // S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_LAYOUT_H_ diff --git a/inst/include/s2/third_party/absl/memory/memory.h b/inst/include/s2/third_party/absl/memory/memory.h new file mode 100644 index 0000000..7a853e8 --- /dev/null +++ b/inst/include/s2/third_party/absl/memory/memory.h @@ -0,0 +1,755 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: memory.h +// ----------------------------------------------------------------------------- +// +// This header file contains utility functions for managing the creation and +// conversion of smart pointers. This file is an extension to the C++ +// standard library header file. + +#ifndef S2_THIRD_PARTY_ABSL_MEMORY_MEMORY_H_ +#define S2_THIRD_PARTY_ABSL_MEMORY_MEMORY_H_ + +#include +#include +#include +#include +#include +#include + +#include "s2/third_party/absl/base/macros.h" +#include "s2/third_party/absl/meta/type_traits.h" + +namespace absl { + +// ----------------------------------------------------------------------------- +// Function Template: WrapUnique() +// ----------------------------------------------------------------------------- +// +// Adopts ownership from a raw pointer and transfers it to the returned +// `std::unique_ptr`, whose type is deduced. Because of this deduction, *do not* +// specify the template type `T` when calling `WrapUnique`. +// +// Example: +// X* NewX(int, int); +// auto x = WrapUnique(NewX(1, 2)); // 'x' is std::unique_ptr. +// +// The purpose of WrapUnique is to automatically deduce the pointer type. If you +// wish to make the type explicit, for readability reasons or because you prefer +// to use a base-class pointer rather than a derived one, just use +// `std::unique_ptr` directly. +// +// Example: +// X* Factory(int, int); +// auto x = std::unique_ptr(Factory(1, 2)); +// - or - +// std::unique_ptr x(Factory(1, 2)); +// +// This has the added advantage of working whether Factory returns a raw +// pointer or a `std::unique_ptr`. +// +// While `absl::WrapUnique` is useful for capturing the output of a raw +// pointer factory, prefer 'absl::make_unique(args...)' over +// 'absl::WrapUnique(new T(args...))'. +// +// auto x = WrapUnique(new X(1, 2)); // works, but nonideal. +// auto x = make_unique(1, 2); // safer, standard, avoids raw 'new'. +// +// Note that `absl::WrapUnique(p)` is valid only if `delete p` is a valid +// expression. In particular, `absl::WrapUnique()` cannot wrap pointers to +// arrays, functions or void, and it must not be used to capture pointers +// obtained from array-new expressions (even though that would compile!). +template +std::unique_ptr WrapUnique(T* ptr) { + static_assert(!std::is_array::value, "array types are unsupported"); + static_assert(std::is_object::value, "non-object types are unsupported"); + return std::unique_ptr(ptr); +} + +namespace memory_internal { + +// Traits to select proper overload and return type for `absl::make_unique<>`. +template +struct MakeUniqueResult { + using scalar = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using array = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using invalid = void; +}; + +} // namespace memory_internal + +// gcc 4.8 has __cplusplus at 201301 but doesn't define make_unique. Other +// supported compilers either just define __cplusplus as 201103 but have +// make_unique (msvc), or have make_unique whenever __cplusplus > 201103 (clang) +#if (__cplusplus > 201103L || defined(_MSC_VER)) && \ + !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8) +using std::make_unique; +#else +// ----------------------------------------------------------------------------- +// Function Template: make_unique() +// ----------------------------------------------------------------------------- +// +// Creates a `std::unique_ptr<>`, while avoiding issues creating temporaries +// during the construction process. `absl::make_unique<>` also avoids redundant +// type declarations, by avoiding the need to explicitly use the `new` operator. +// +// This implementation of `absl::make_unique<>` is designed for C++11 code and +// will be replaced in C++14 by the equivalent `std::make_unique<>` abstraction. +// `absl::make_unique<>` is designed to be 100% compatible with +// `std::make_unique<>` so that the eventual migration will involve a simple +// rename operation. +// +// For more background on why `std::unique_ptr(new T(a,b))` is problematic, +// see Herb Sutter's explanation on +// (Exception-Safe Function Calls)[http://herbsutter.com/gotw/_102/]. +// (In general, reviewers should treat `new T(a,b)` with scrutiny.) +// +// Example usage: +// +// auto p = make_unique(args...); // 'p' is a std::unique_ptr +// auto pa = make_unique(5); // 'pa' is a std::unique_ptr +// +// Three overloads of `absl::make_unique` are required: +// +// - For non-array T: +// +// Allocates a T with `new T(std::forward args...)`, +// forwarding all `args` to T's constructor. +// Returns a `std::unique_ptr` owning that object. +// +// - For an array of unknown bounds T[]: +// +// `absl::make_unique<>` will allocate an array T of type U[] with +// `new U[n]()` and return a `std::unique_ptr` owning that array. +// +// Note that 'U[n]()' is different from 'U[n]', and elements will be +// value-initialized. Note as well that `std::unique_ptr` will perform its +// own destruction of the array elements upon leaving scope, even though +// the array [] does not have a default destructor. +// +// NOTE: an array of unknown bounds T[] may still be (and often will be) +// initialized to have a size, and will still use this overload. E.g: +// +// auto my_array = absl::make_unique(10); +// +// - For an array of known bounds T[N]: +// +// `absl::make_unique<>` is deleted (like with `std::make_unique<>`) as +// this overload is not useful. +// +// NOTE: an array of known bounds T[N] is not considered a useful +// construction, and may cause undefined behavior in templates. E.g: +// +// auto my_array = absl::make_unique(); +// +// In those cases, of course, you can still use the overload above and +// simply initialize it to its desired size: +// +// auto my_array = absl::make_unique(10); + +// `absl::make_unique` overload for non-array types. +template +typename memory_internal::MakeUniqueResult::scalar make_unique( + Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +// `absl::make_unique` overload for an array T[] of unknown bounds. +// The array allocation needs to use the `new T[size]` form and cannot take +// element constructor arguments. The `std::unique_ptr` will manage destructing +// these array elements. +template +typename memory_internal::MakeUniqueResult::array make_unique(size_t n) { + return std::unique_ptr(new typename absl::remove_extent_t[n]()); +} + +// `absl::make_unique` overload for an array T[N] of known bounds. +// This construction will be rejected. +template +typename memory_internal::MakeUniqueResult::invalid make_unique( + Args&&... /* args */) = delete; +#endif + + +// These are make_unique_default_init variants modeled after +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1020r0.html +// Unlike absl::make_unique, values are default initialized rather than value +// initialized. +// +// `absl::make_unique_default_init` overload for non-array types. +template +typename memory_internal::MakeUniqueResult::scalar + make_unique_default_init() { + return std::unique_ptr(new T); +} + +// `absl::make_unique_default_init` overload for an array T[] of unknown bounds. +// The array allocation needs to use the `new T[size]` form and cannot take +// element constructor arguments. The `std::unique_ptr` will manage destructing +// these array elements. +template +typename memory_internal::MakeUniqueResult::array + make_unique_default_init(size_t n) { + return std::unique_ptr(new typename absl::remove_extent_t[n]); +} + +// `absl::make_unique_default_init` overload for an array T[N] of known bounds. +// This construction will be rejected. +template +typename memory_internal::MakeUniqueResult::invalid + make_unique_default_init(Args&&... /* args */) = delete; + + + +// NOTE TO GOOGLERS: +// absl::MakeUnique / gtl::MakeUnique is a legacy spelling. New code should +// prefer absl::make_unique. + +// `absl::MakeUnique` overload for non-array types. +template +typename memory_internal::MakeUniqueResult::scalar + MakeUnique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +// `absl::MakeUnique` overload for an array T[] of unknown bounds. +// The array allocation needs to use the `new T[size]` form and cannot take +// element constructor arguments. The `std::unique_ptr` will manage destructing +// these array elements. +template +typename memory_internal::MakeUniqueResult::array MakeUnique(size_t n) { + return std::unique_ptr(new typename absl::remove_extent_t[n]()); +} + +// `absl::MakeUnique` overload for an array T[N] of known bounds. +// This construction will be rejected. +template +typename memory_internal::MakeUniqueResult::invalid + MakeUnique(Args&&... /* args */) = delete; + +// ----------------------------------------------------------------------------- +// Function Template: RawPtr() +// ----------------------------------------------------------------------------- +// +// Extracts the raw pointer from a pointer-like value `ptr`. `absl::RawPtr` is +// useful within templates that need to handle a complement of raw pointers, +// `std::nullptr_t`, and smart pointers. +template +auto RawPtr(T&& ptr) -> decltype(std::addressof(*ptr)) { + // ptr is a forwarding reference to support Ts with non-const operators. + return (ptr != nullptr) ? std::addressof(*ptr) : nullptr; +} +inline std::nullptr_t RawPtr(std::nullptr_t) { return nullptr; } + +// ----------------------------------------------------------------------------- +// Function Template: ShareUniquePtr() +// ----------------------------------------------------------------------------- +// +// Adopts a `std::unique_ptr` rvalue and returns a `std::shared_ptr` of deduced +// type. Ownership (if any) of the held value is transferred to the returned +// shared pointer. +// +// Example: +// +// auto up = absl::make_unique(10); +// auto sp = absl::ShareUniquePtr(std::move(up)); // shared_ptr +// CHECK_EQ(*sp, 10); +// CHECK(up == nullptr); +// +// Note that this conversion is correct even when T is an array type, and more +// generally it works for *any* deleter of the `unique_ptr` (single-object +// deleter, array deleter, or any custom deleter), since the deleter is adopted +// by the shared pointer as well. The deleter is copied (unless it is a +// reference). +// +// Implements the resolution of [LWG 2415](http://wg21.link/lwg2415), by which a +// null shared pointer does not attempt to call the deleter. +template +std::shared_ptr ShareUniquePtr(std::unique_ptr&& ptr) { + return ptr ? std::shared_ptr(std::move(ptr)) : std::shared_ptr(); +} + +// ----------------------------------------------------------------------------- +// Function Template: WeakenPtr() +// ----------------------------------------------------------------------------- +// +// Creates a weak pointer associated with a given shared pointer. The returned +// value is a `std::weak_ptr` of deduced type. +// +// Example: +// +// auto sp = std::make_shared(10); +// auto wp = absl::WeakenPtr(sp); +// CHECK_EQ(sp.get(), wp.lock().get()); +// sp.reset(); +// CHECK(wp.lock() == nullptr); +// +template +std::weak_ptr WeakenPtr(const std::shared_ptr& ptr) { + return std::weak_ptr(ptr); +} + +namespace memory_internal { + +// ExtractOr::type evaluates to E if possible. Otherwise, D. +template