Coverage Report

Created: 2026-08-31 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/s2lax_polygon_shape.cc
Line
Count
Source
1
// Copyright 2013 Google Inc. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
//
15
16
// Author: ericv@google.com (Eric Veach)
17
18
#include "s2/s2lax_polygon_shape.h"
19
20
#include <algorithm>
21
#include <atomic>
22
#include <cstdint>
23
#include <cstdlib>
24
#include <limits>
25
#include <memory>
26
#include <utility>
27
#include <vector>
28
29
#include "absl/base/nullability.h"
30
#include "absl/log/absl_check.h"
31
#include "absl/memory/memory.h"
32
#include "absl/strings/string_view.h"
33
#include "absl/types/span.h"
34
35
#include "s2/encoded_s2point_vector.h"
36
#include "s2/encoded_uint_vector.h"
37
#include "s2/s2coder.h"
38
#include "s2/s2error.h"
39
#include "s2/s2loop.h"
40
#include "s2/s2point.h"
41
#include "s2/s2point_array.h"
42
#include "s2/s2polygon.h"
43
#include "s2/s2shape.h"
44
#include "s2/s2shapeutil_get_reference_point.h"
45
#include "s2/util/coding/coder.h"
46
#include "s2/util/coding/varint.h"
47
48
using absl::MakeSpan;
49
using absl::Span;
50
using ::s2internal::MakeS2PointArrayForOverwrite;
51
using std::unique_ptr;
52
using std::vector;
53
using ChainPosition = S2Shape::ChainPosition;
54
55
namespace {
56
template <typename T>
57
3.93k
unique_ptr<T> make_unique_for_overwrite(size_t n) {
58
  // We only need to support this one variant.
59
3.93k
  static_assert(std::is_array<T>::value, "T must be an array type");
60
3.93k
  return unique_ptr<T>(new typename absl::remove_extent_t<T>[n]);
61
3.93k
}
62
}  // namespace
63
64
65
// When adding a new encoding, be aware that old binaries will not be able
66
// to decode it.
67
static const unsigned char kCurrentEncodingVersionNumber = 1;
68
69
S2LaxPolygonShape::S2LaxPolygonShape(
70
14.2k
    absl::Span<const S2LaxPolygonShape::Loop> loops) {
71
14.2k
  Init(loops);
72
14.2k
}
73
74
0
S2LaxPolygonShape::S2LaxPolygonShape(Span<const Span<const S2Point>> loops) {
75
0
  Init(loops);
76
0
}
77
78
0
S2LaxPolygonShape::S2LaxPolygonShape(const S2Polygon& polygon) {
79
0
  Init(polygon);
80
0
}
81
82
S2LaxPolygonShape::S2LaxPolygonShape(S2LaxPolygonShape&& b) noexcept
83
0
    : num_loops_(std::exchange(b.num_loops_, 0)),
84
0
      prev_loop_(b.prev_loop_.exchange(0, std::memory_order_relaxed)),
85
0
      num_vertices_(std::exchange(b.num_vertices_, 0)),
86
0
      vertices_(std::move(b.vertices_)),
87
0
      loop_starts_(std::move(b.loop_starts_)) {}
88
89
S2LaxPolygonShape& S2LaxPolygonShape::operator=(
90
0
    S2LaxPolygonShape&& b) noexcept {
91
0
  using std::memory_order_relaxed;
92
93
0
  num_loops_ = std::exchange(b.num_loops_, 0);
94
0
  prev_loop_.store(b.prev_loop_.exchange(0, memory_order_relaxed),
95
0
                   memory_order_relaxed);
96
0
  num_vertices_ = std::exchange(b.num_vertices_, 0);
97
0
  vertices_ = std::move(b.vertices_);
98
0
  loop_starts_ = std::move(b.loop_starts_);
99
0
  return *this;
100
0
}
101
102
14.2k
void S2LaxPolygonShape::Init(absl::Span<const S2LaxPolygonShape::Loop> loops) {
103
14.2k
  vector<Span<const S2Point>> spans;
104
14.2k
  spans.reserve(loops.size());
105
3.03M
  for (const S2LaxPolygonShape::Loop& loop : loops) {
106
3.03M
    spans.emplace_back(loop);
107
3.03M
  }
108
14.2k
  Init(spans);
109
14.2k
}
110
111
0
void S2LaxPolygonShape::Init(const S2Polygon& polygon) {
112
0
  vector<Span<const S2Point>> spans;
113
0
  for (int i = 0; i < polygon.num_loops(); ++i) {
114
0
    const S2Loop* loop = polygon.loop(i);
115
0
    if (loop->is_full()) {
116
0
      spans.emplace_back();  // Empty span.
117
0
    } else {
118
0
      spans.emplace_back(&loop->vertex(0), loop->num_vertices());
119
0
    }
120
0
  }
121
0
  Init(spans);
122
123
  // S2Polygon and S2LaxPolygonShape holes are oriented oppositely, so we need
124
  // to reverse the orientation of any loops representing holes.
125
0
  for (int i = 0; i < polygon.num_loops(); ++i) {
126
0
    if (polygon.loop(i)->is_hole()) {
127
0
      S2Point* v0 = &vertices_[loop_starts_[i]];
128
0
      std::reverse(v0, v0 + num_loop_vertices(i));
129
0
    }
130
0
  }
131
0
}
132
133
14.2k
void S2LaxPolygonShape::Init(Span<const Span<const S2Point>> loops) {
134
14.2k
  num_loops_ = loops.size();
135
14.2k
  if (num_loops_ == 0) {
136
2.63k
    num_vertices_ = 0;
137
11.5k
  } else if (num_loops_ == 1) {
138
7.63k
    num_vertices_ = loops[0].size();
139
7.63k
    vertices_ = MakeS2PointArrayForOverwrite(num_vertices_);
140
7.63k
    std::copy_n(loops[0].data(), num_vertices_, vertices_.get());
141
7.63k
  } else {
142
    // Don't use make_unique<> here in order to avoid zero initialization.
143
3.93k
    loop_starts_ = make_unique_for_overwrite<uint32_t[]>(num_loops_ + 1);
144
3.93k
    num_vertices_ = 0;
145
3.02M
    for (int i = 0; i < num_loops_; ++i) {
146
3.02M
      loop_starts_[i] = num_vertices_;
147
3.02M
      num_vertices_ += loops[i].size();
148
3.02M
    }
149
3.93k
    loop_starts_[num_loops_] = num_vertices_;
150
3.93k
    vertices_ = MakeS2PointArrayForOverwrite(num_vertices_);
151
3.02M
    for (int i = 0; i < num_loops_; ++i) {
152
3.02M
      std::copy_n(loops[i].data(), loops[i].size(),
153
3.02M
                  &vertices_[loop_starts_[i]]);
154
3.02M
    }
155
3.93k
  }
156
14.2k
}
157
158
0
int S2LaxPolygonShape::num_loop_vertices(int i) const {
159
0
  ABSL_DCHECK_LT(i, num_loops());
160
0
  if (num_loops() == 1) {
161
0
    return num_vertices_;
162
0
  } else {
163
0
    return loop_starts_[i + 1] - loop_starts_[i];
164
0
  }
165
0
}
166
167
0
const S2Point& S2LaxPolygonShape::loop_vertex(int i, int j) const {
168
0
  ABSL_DCHECK_LT(i, num_loops());
169
0
  ABSL_DCHECK_LT(j, num_loop_vertices(i));
170
0
  if (i == 0) {
171
0
    return vertices_[j];
172
0
  } else {
173
0
    return vertices_[loop_starts_[i] + j];
174
0
  }
175
0
}
176
177
void S2LaxPolygonShape::Encode(Encoder* encoder,
178
0
                               s2coding::CodingHint hint) const {
179
0
  encoder->Ensure(1 + Varint::kMax32);
180
0
  encoder->put8(kCurrentEncodingVersionNumber);
181
0
  encoder->put_varint32(num_loops());
182
0
  s2coding::EncodeS2PointVector(MakeSpan(vertices_.get(), num_vertices()),
183
0
                                hint, encoder);
184
0
  if (num_loops() > 1) {
185
0
    s2coding::EncodeUintVector<uint32_t>(
186
0
        MakeSpan(loop_starts_.get(), num_loops() + 1), encoder);
187
0
  }
188
0
}
189
190
0
bool S2LaxPolygonShape::Init(Decoder* decoder, S2Error* absl_nullable error) {
191
0
  const auto Error = [&error](absl::string_view message) {
192
0
    if (error != nullptr) {
193
0
      *error = S2Error::DataLoss(message);
194
0
    }
195
0
    return false;
196
0
  };
197
198
0
  if (decoder->avail() < 1) {
199
0
    return Error("Insufficient data in decoder");
200
0
  }
201
202
0
  uint8_t version = decoder->get8();
203
0
  if (version != kCurrentEncodingVersionNumber) {
204
0
    return Error("Bad version number in byte string");
205
0
  }
206
207
0
  uint32_t num_loops;
208
0
  if (!decoder->get_varint32(&num_loops)) {
209
0
    return Error("Failed to decode number of loops");
210
0
  }
211
  // `loop_starts_` is indexed by an integer, and contains an extra entry,
212
  // so we limit num_loops to INT32_MAX - 1.
213
0
  if (num_loops > std::numeric_limits<int32_t>::max() - 1u) {
214
0
    return Error("Number of loops too large");
215
0
  }
216
217
0
  num_loops_ = num_loops;
218
0
  s2coding::EncodedS2PointVector vertices;
219
0
  if (!vertices.Init(decoder)) {
220
0
    return Error("Failed to decode vertices");
221
0
  }
222
223
0
  if (num_loops_ == 0) {
224
0
    num_vertices_ = 0;
225
0
  } else {
226
0
    num_vertices_ = vertices.size();
227
0
    vertices_ = MakeS2PointArrayForOverwrite(num_vertices_);
228
229
    // Load the polygon vertices from the encoded s2point vector.
230
0
    if (error == nullptr) {
231
0
      vertices.Decode(absl::MakeSpan(vertices_.get(), num_vertices_));
232
0
    } else {
233
0
      vertices.Decode(absl::MakeSpan(vertices_.get(), num_vertices_), *error);
234
0
      if (!error->ok()) {
235
0
        return false;
236
0
      }
237
0
    }
238
239
0
    if (num_loops_ > 1) {
240
0
      s2coding::EncodedUintVector<uint32_t> loop_starts;
241
0
      if (!loop_starts.Init(decoder) || loop_starts.size() != num_loops_ + 1) {
242
0
        return Error("Failed to decode loop offsets");
243
0
      }
244
245
0
      loop_starts_ = make_unique_for_overwrite<uint32_t[]>(loop_starts.size());
246
0
      for (size_t i = 0; i < loop_starts.size(); ++i) {
247
0
        loop_starts_[i] = loop_starts[i];
248
0
      }
249
0
    }
250
0
  }
251
252
0
  return true;
253
0
}
254
255
0
S2Shape::Edge S2LaxPolygonShape::edge(int e) const {
256
  // Method names are fully specified to enable inlining.
257
0
  ChainPosition pos = S2LaxPolygonShape::chain_position(e);
258
0
  return S2LaxPolygonShape::chain_edge(pos.chain_id, pos.offset);
259
0
}
260
261
0
S2Shape::ReferencePoint S2LaxPolygonShape::GetReferencePoint() const {
262
0
  return s2shapeutil::GetReferencePoint(*this);
263
0
}
264
265
0
S2Shape::Chain S2LaxPolygonShape::chain(int i) const {
266
0
  ABSL_DCHECK_LT(i, num_loops());
267
0
  if (num_loops() == 1) {
268
0
    return Chain(0, num_vertices_);
269
0
  } else {
270
0
    int start = loop_starts_[i];
271
0
    return Chain(start, loop_starts_[i + 1] - start);
272
0
  }
273
0
}
274
275
EncodedS2LaxPolygonShape::EncodedS2LaxPolygonShape(
276
    EncodedS2LaxPolygonShape&& b) noexcept
277
0
    : num_loops_(std::exchange(b.num_loops_, 0)),
278
0
      prev_loop_(b.prev_loop_.exchange(0, std::memory_order_relaxed)),
279
0
      vertices_(std::move(b.vertices_)),
280
0
      loop_starts_(std::move(b.loop_starts_)) {}
281
282
EncodedS2LaxPolygonShape& EncodedS2LaxPolygonShape::operator=(
283
0
    EncodedS2LaxPolygonShape&& b) noexcept {
284
0
  num_loops_ = std::exchange(b.num_loops_, 0);
285
0
  prev_loop_.store(b.prev_loop_.exchange(0, std::memory_order_relaxed),
286
0
                   std::memory_order_relaxed);
287
0
  vertices_ = std::move(b.vertices_);
288
0
  loop_starts_ = std::move(b.loop_starts_);
289
0
  return *this;
290
0
}
291
292
0
bool EncodedS2LaxPolygonShape::Init(Decoder* decoder) {
293
0
  if (decoder->avail() < 1) return false;
294
0
  uint8_t version = decoder->get8();
295
0
  if (version != kCurrentEncodingVersionNumber) return false;
296
297
0
  uint32_t num_loops;
298
0
  if (!decoder->get_varint32(&num_loops)) return false;
299
  // `loop_starts_` is indexed by an integer, and contains an extra entry,
300
  // so we limit num_loops to INT32_MAX - 1.
301
0
  if (num_loops > std::numeric_limits<int32_t>::max() - 1u) return false;
302
0
  num_loops_ = num_loops;
303
304
0
  if (!vertices_.Init(decoder)) return false;
305
306
0
  if (num_loops_ > 1) {
307
0
    if (!loop_starts_.Init(decoder)) return false;
308
0
  }
309
0
  return true;
310
0
}
311
312
// The encoding must be identical to S2LaxPolygonShape::Encode().
313
void EncodedS2LaxPolygonShape::Encode(Encoder* encoder,
314
0
                                      s2coding::CodingHint) const {
315
0
  encoder->Ensure(1 + Varint::kMax32);
316
0
  encoder->put8(kCurrentEncodingVersionNumber);
317
0
  encoder->put_varint32(num_loops_);
318
0
  vertices_.Encode(encoder);
319
0
  if (num_loops_ > 1) {
320
0
    loop_starts_.Encode(encoder);
321
0
  }
322
0
}
323
324
0
int EncodedS2LaxPolygonShape::num_vertices() const {
325
0
  if (num_loops() <= 1) {
326
0
    return vertices_.size();
327
0
  } else {
328
0
    return loop_starts_[num_loops()];
329
0
  }
330
0
}
331
332
0
int EncodedS2LaxPolygonShape::num_loop_vertices(int i) const {
333
0
  ABSL_DCHECK_LT(i, num_loops());
334
0
  if (num_loops() == 1) {
335
0
    return vertices_.size();
336
0
  } else {
337
0
    return loop_starts_[i + 1] - loop_starts_[i];
338
0
  }
339
0
}
340
341
0
S2Point EncodedS2LaxPolygonShape::loop_vertex(int i, int j) const {
342
0
  ABSL_DCHECK_LT(i, num_loops());
343
0
  ABSL_DCHECK_LT(j, num_loop_vertices(i));
344
0
  if (num_loops() == 1) {
345
0
    return vertices_[j];
346
0
  } else {
347
0
    return vertices_[loop_starts_[i] + j];
348
0
  }
349
0
}
350
351
0
S2Shape::Edge EncodedS2LaxPolygonShape::edge(int e) const {
352
0
  ABSL_DCHECK_LT(e, num_edges());
353
0
  size_t e1 = e + 1;
354
0
  if (num_loops() == 1) {
355
0
    if (e1 == vertices_.size()) { e1 = 0; }
356
0
    return Edge(vertices_[e], vertices_[e1]);
357
0
  } else {
358
    // Method names are fully specified to enable inlining.
359
0
    ChainPosition pos = EncodedS2LaxPolygonShape::chain_position(e);
360
0
    return EncodedS2LaxPolygonShape::chain_edge(pos.chain_id, pos.offset);
361
0
  }
362
0
}
363
364
0
S2Shape::ReferencePoint EncodedS2LaxPolygonShape::GetReferencePoint() const {
365
0
  return s2shapeutil::GetReferencePoint(*this);
366
0
}
367
368
0
S2Shape::Chain EncodedS2LaxPolygonShape::chain(int i) const {
369
0
  ABSL_DCHECK_LT(i, num_loops());
370
0
  if (num_loops() == 1) {
371
0
    return Chain(0, vertices_.size());
372
0
  } else {
373
0
    int start = loop_starts_[i];
374
0
    return Chain(start, loop_starts_[i + 1] - start);
375
0
  }
376
0
}