Coverage Report

Created: 2026-09-11 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/s2shape_index.cc
Line
Count
Source
1
// Copyright 2012 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/s2shape_index.h"
19
20
#include <cstdint>
21
#include <limits>
22
23
#include "absl/base/optimization.h"
24
#include "absl/log/absl_check.h"
25
#include "s2/util/coding/coder.h"
26
#include "s2/util/coding/varint.h"
27
#include "s2/util/gtl/compact_array.h"
28
29
0
bool S2ClippedShape::ContainsEdge(int id) const {
30
  // Linear search is fast because the number of edges per shape is typically
31
  // very small (less than 10).
32
0
  for (int e = 0; e < num_edges(); ++e) {
33
0
    if (edge(e) == id) return true;
34
0
  }
35
0
  return false;
36
0
}
37
38
0
S2ShapeIndexCell::~S2ShapeIndexCell() {
39
  // Free memory for all shapes owned by this cell.
40
0
  for (S2ClippedShape& s : shapes_)
41
0
    s.Destruct();
42
0
  shapes_.clear();
43
0
}
44
45
const S2ClippedShape*
46
0
S2ShapeIndexCell::find_clipped(int shape_id) const {
47
  // Linear search is fine because the number of shapes per cell is typically
48
  // very small (most often 1), and is large only for pathological inputs
49
  // (e.g. very deeply nested loops).
50
0
  for (const auto& s : shapes_) {
51
0
    if (s.shape_id() == shape_id) return &s;
52
0
  }
53
0
  return nullptr;
54
0
}
55
56
// Allocate room for "n" additional clipped shapes in the cell, and return a
57
// pointer to the first new clipped shape.  Expects that all new clipped
58
// shapes will have a larger shape id than any current shape, and that shapes
59
// will be added in increasing shape id order.
60
0
S2ClippedShape* S2ShapeIndexCell::add_shapes(int n) {
61
0
  if (n > 0) {
62
0
    int size = shapes_.size();
63
0
    shapes_.resize(size + n);
64
0
    return &shapes_[size];
65
0
  }
66
0
  return nullptr;
67
0
}
68
69
0
void S2ShapeIndexCell::Encode(int num_shape_ids, Encoder* encoder) const {
70
  // The encoding is designed to be especially compact in certain common
71
  // situations:
72
  //
73
  // 1. The S2ShapeIndex contains exactly one shape.
74
  //
75
  // 2. The S2ShapeIndex contains more than one shape, but a particular index
76
  //    cell contains only one shape (num_clipped == 1).
77
  //
78
  // 3. The edge ids for a given shape in a cell form a contiguous range.
79
  //
80
  // The details were optimized by constructing an S2ShapeIndex for each
81
  // feature in Google's geographic repository and measuring their total
82
  // encoded size.  The MutableS2ShapeIndex encoding (of which this function
83
  // is just one part) uses an average of 1.88 bytes per vertex for features
84
  // consisting of polygons or polylines.
85
  //
86
  // Note that this code does not bother handling num_shapes >= 2**28 or
87
  // num_edges >= 2**29.  This could be fixed using varint64 in a few more
88
  // places, but if a single cell contains this many shapes or edges then we
89
  // have bigger problems than just the encoding format :)
90
0
  if (num_shape_ids == 1) {
91
    // If the entire S2ShapeIndex contains just one shape, then we don't need
92
    // to encode any shape ids.  This is a very important and common case.
93
0
    ABSL_DCHECK_EQ(num_clipped(), 1);  // Index invariant: no empty cells.
94
0
    const S2ClippedShape& clipped = this->clipped(0);
95
0
    ABSL_DCHECK_EQ(clipped.shape_id(), 0);
96
0
    int n = clipped.num_edges();
97
0
    encoder->Ensure(Varint::kMax64 + n * Varint::kMax32);
98
0
    if (n >= 2 && n <= 17 && clipped.edge(n - 1) - clipped.edge(0) == n - 1) {
99
      // The cell contains a contiguous range of edges (*most common case*).
100
      // If the starting edge id is small then we can encode the cell in one
101
      // byte.  (The n == 0 and n == 1 cases are encoded compactly below.)
102
      // This encoding uses a 1-bit tag because it is by far the most common.
103
      //
104
      // Encoding: bit 0: 0
105
      //           bit 1: contains_center
106
      //           bits 2-5: (num_edges - 2)
107
      //           bits 6+: edge_id
108
0
      encoder->put_varint64(static_cast<uint64_t>(clipped.edge(0)) << 6 |
109
0
                            (n - 2) << 2 | clipped.contains_center() << 1 | 0);
110
0
    } else if (n == 1) {
111
      // The cell contains only one edge.  For edge ids up to 15, we can
112
      // encode the cell in a single byte.
113
      //
114
      // Encoding: bits 0-1: 1
115
      //           bit 2: contains_center
116
      //           bits 3+: edge_id
117
0
      encoder->put_varint64(static_cast<uint64_t>(clipped.edge(0)) << 3 |
118
0
                            clipped.contains_center() << 2 | 1);
119
0
    } else {
120
      // General case (including n == 0, which is encoded compactly here).
121
      //
122
      // Encoding: bits 0-1: 3
123
      //           bit 2: contains_center
124
      //           bits 3+: num_edges
125
0
      encoder->put_varint64(static_cast<uint64_t>(n) << 3 |
126
0
                            clipped.contains_center() << 2 | 3);
127
0
      EncodeEdges(clipped, encoder);
128
0
    }
129
0
  } else {
130
0
    if (num_clipped() > 1) {
131
      // The cell contains more than one shape.  The tag for this encoding
132
      // must be distinguishable from the cases encoded below.  We can afford
133
      // to use a 3-bit tag because num_clipped is generally small.
134
0
      encoder->Ensure(Varint::kMax32);
135
0
      encoder->put_varint32((num_clipped() << 3) | 3);
136
0
    }
137
    // The shape ids are delta-encoded.
138
0
    int shape_id_base = 0;
139
0
    for (int j = 0; j < num_clipped(); ++j) {
140
0
      const S2ClippedShape& clipped = this->clipped(j);
141
0
      ABSL_DCHECK_GE(clipped.shape_id(), shape_id_base);
142
0
      int shape_delta = clipped.shape_id() - shape_id_base;
143
0
      shape_id_base = clipped.shape_id() + 1;
144
145
      // Like the code above except that we also need to encode shape_id(s).
146
      // Because of this some choices are slightly different.
147
0
      int n = clipped.num_edges();
148
0
      encoder->Ensure((n + 2) * Varint::kMax32);
149
0
      if (n >= 1 && n <= 16 && clipped.edge(n - 1) - clipped.edge(0) == n - 1) {
150
        // The clipped shape has a contiguous range of up to 16 edges.  This
151
        // encoding uses a 1-bit tag because it is by far the most common.
152
        //
153
        // Encoding: bit 0: 0
154
        //           bit 1: contains_center
155
        //           bits 2+: edge_id
156
        // Next value: bits 0-3: (num_edges - 1)
157
        //             bits 4+: shape_delta
158
0
        encoder->put_varint32(clipped.edge(0) << 2 |
159
0
                              clipped.contains_center() << 1 | 0);
160
0
        encoder->put_varint32(shape_delta << 4 | (n - 1));
161
0
      } else if (n == 0) {
162
        // Special encoding for clipped shapes with no edges.  Such shapes are
163
        // common in polygon interiors.  This encoding uses a 3-bit tag in
164
        // order to leave more bits available for the other encodings.
165
        //
166
        // NOTE(ericv): When num_clipped > 1, this tag could be 2 bits
167
        // (because the tag used to indicate num_clipped > 1 can't appear).
168
        // Alternatively, that tag can be considered reserved for future use.
169
        //
170
        // Encoding: bits 0-2: 7
171
        //           bit 3: contains_center
172
        //           bits 4+: shape_delta
173
0
        encoder->put_varint32(shape_delta << 4 |
174
0
                              clipped.contains_center() << 3 | 7);
175
0
      } else {
176
        // General case.  This encoding uses a 2-bit tag, and the first value
177
        // typically is encoded into one byte.
178
        //
179
        // Encoding: bits 0-1: 1
180
        //           bit 2: contains_center
181
        //           bits 3+: (num_edges - 1)
182
        // Next value: shape_delta
183
0
        encoder->put_varint32((n - 1) << 3 |
184
0
                              clipped.contains_center() << 2 | 1);
185
0
        encoder->put_varint32(shape_delta);
186
0
        EncodeEdges(clipped, encoder);
187
0
      }
188
0
    }
189
0
  }
190
0
}
191
192
0
bool S2ShapeIndexCell::Decode(int num_shape_ids, Decoder* decoder) {
193
  // This function inverts the encodings documented above.
194
0
  if (num_shape_ids == 1) {
195
    // Entire S2ShapeIndex contains only one shape.
196
0
    S2ClippedShape* clipped = add_shapes(1);
197
0
    uint64_t header = 0;
198
0
    if (!decoder->get_varint64(&header)) return false;
199
0
    if ((header & 1) == 0) {
200
      // The cell contains a contiguous range of edges.
201
0
      int num_edges = ((header >> 2) & 15) + 2;
202
0
      clipped->Init(0 /*shape_id*/, num_edges);
203
0
      clipped->set_contains_center((header & 2) != 0);
204
      // Edge ids are int32, but we use int64 to guard against overflow.
205
0
      const int64_t edge_id = header >> 6;
206
0
      if (ABSL_PREDICT_FALSE(edge_id + num_edges >
207
0
                             std::numeric_limits<int32_t>::max())) {
208
0
        return false;
209
0
      }
210
0
      for (int i = 0; i < num_edges; ++i) {
211
        // Range check for `edge_id + i` is performed above.
212
0
        clipped->set_edge(i, static_cast<int>(edge_id + i));
213
0
      }
214
0
      return true;
215
0
    }
216
0
    if ((header & 2) == 0) {
217
      // The cell contains a single edge.
218
0
      clipped->Init(0 /*shape_id*/, 1 /*num_edges*/);
219
0
      clipped->set_contains_center((header & 4) != 0);
220
0
      const int64_t edge_id = header >> 3;
221
0
      if (ABSL_PREDICT_FALSE(edge_id > std::numeric_limits<int32_t>::max())) {
222
0
        return false;
223
0
      }
224
0
      clipped->set_edge(0, edge_id);
225
0
      return true;
226
0
    }
227
    // The cell contains some other combination of edges.
228
0
    int num_edges = header >> 3;
229
0
    clipped->Init(0 /*shape_id*/, num_edges);
230
0
    clipped->set_contains_center((header & 4) != 0);
231
0
    return DecodeEdges(num_edges, clipped, decoder);
232
0
  }
233
  // S2ShapeIndex contains more than one shape.
234
0
  uint32_t header = 0;
235
0
  if (!decoder->get_varint32(&header)) return false;
236
0
  int num_clipped = 1;
237
0
  if ((header & 7) == 3) {
238
    // This cell contains more than one shape.
239
0
    num_clipped = header >> 3;
240
0
    if (!decoder->get_varint32(&header)) return false;
241
0
  }
242
0
  int64_t shape_id = 0;  // Shape id is 32-bit, but we guard against overflow.
243
0
  S2ClippedShape* clipped = add_shapes(num_clipped);
244
0
  for (int j = 0; j < num_clipped; ++j, ++clipped, ++shape_id) {
245
    // Ensure we don't overflow a 32 bit shape id.
246
0
    if (shape_id >= std::numeric_limits<int>::max()) {
247
0
      return false;
248
0
    }
249
250
0
    if (j > 0 && !decoder->get_varint32(&header)) return false;
251
0
    if ((header & 1) == 0) {
252
      // The clipped shape contains a contiguous range of edges.
253
0
      uint32_t shape_id_count = 0;
254
0
      if (!decoder->get_varint32(&shape_id_count)) return false;
255
0
      shape_id += shape_id_count >> 4;
256
0
      int num_edges = (shape_id_count & 15) + 1;
257
0
      clipped->Init(shape_id, num_edges);
258
0
      clipped->set_contains_center((header & 2) != 0);
259
0
      const int64_t edge_id = header >> 2;
260
0
      if (ABSL_PREDICT_FALSE(edge_id + num_edges >
261
0
                             std::numeric_limits<int32_t>::max())) {
262
0
        return false;
263
0
      }
264
0
      for (int i = 0; i < num_edges; ++i) {
265
0
        clipped->set_edge(i, static_cast<int>(edge_id + i));
266
0
      }
267
0
    } else if ((header & 7) == 7) {
268
      // The clipped shape has no edges.
269
0
      shape_id += header >> 4;
270
0
      clipped->Init(shape_id, 0);
271
0
      clipped->set_contains_center((header & 8) != 0);
272
0
    } else {
273
      // The clipped shape contains some other combination of edges.
274
0
      if ((header & 3) != 1) {
275
0
        return false;
276
0
      }
277
278
0
      uint32_t shape_delta = 0;
279
0
      if (!decoder->get_varint32(&shape_delta)) return false;
280
0
      shape_id += shape_delta;
281
0
      int num_edges = (header >> 3) + 1;
282
0
      clipped->Init(shape_id, num_edges);
283
0
      clipped->set_contains_center((header & 4) != 0);
284
0
      if (!DecodeEdges(num_edges, clipped, decoder)) return false;
285
0
    }
286
0
  }
287
0
  return true;
288
0
}
289
290
inline void S2ShapeIndexCell::EncodeEdges(const S2ClippedShape& clipped,
291
0
                                          Encoder* encoder) {
292
  // Each entry is an (edge_id, count) pair representing a contiguous range of
293
  // edges.  The edge ids are delta-encoded such that 0 represents the minimum
294
  // valid next edge id.
295
  //
296
  // Encoding: if bits 0-2 < 7: encodes (count - 1)
297
  //            - bits 3+: edge delta
298
  //           if bits 0-2 == 7:
299
  //            - bits 3+ encode (count - 8)
300
  //            - Next value is edge delta
301
  //
302
  // No count is encoded for the last edge (saving 3 bits).
303
0
  int edge_id_base = 0;
304
0
  int num_edges = clipped.num_edges();
305
0
  for (int i = 0; i < num_edges; ++i) {
306
0
    int edge_id = clipped.edge(i);
307
0
    ABSL_DCHECK_GE(edge_id, edge_id_base);
308
0
    int delta = edge_id - edge_id_base;
309
0
    if (i + 1 == num_edges) {
310
      // This is the last edge; no need to encode an edge count.
311
0
      encoder->put_varint32(delta);
312
0
    } else {
313
      // Count the edges in this contiguous range.
314
0
      int count = 1;
315
0
      for (; i + 1 < num_edges && clipped.edge(i + 1) == edge_id + count; ++i) {
316
0
        ++count;
317
0
      }
318
0
      if (count < 8) {
319
        // Count is encoded in low 3 bits of delta.
320
0
        encoder->put_varint32(delta << 3 | (count - 1));
321
0
      } else {
322
        // Count and delta are encoded separately.
323
0
        encoder->put_varint32((count - 8) << 3 | 7);
324
0
        encoder->put_varint32(delta);
325
0
      }
326
0
      edge_id_base = edge_id + count;
327
0
    }
328
0
  }
329
0
}
330
331
inline bool S2ShapeIndexCell::DecodeEdges(int num_edges,
332
                                          S2ClippedShape* clipped,
333
0
                                          Decoder* decoder) {
334
  // This function inverts the encodings documented above.
335
0
  int64_t edge_id = 0;  // Edge id is 32-bit, but we guard against overflow.
336
0
  for (int i = 0; i < num_edges; ) {
337
0
    uint32_t delta = 0;
338
0
    if (!decoder->get_varint32(&delta)) return false;
339
0
    if (i + 1 == num_edges) {
340
      // The last edge is encoded without an edge count.
341
0
      edge_id += delta;
342
0
      if (ABSL_PREDICT_FALSE(edge_id > std::numeric_limits<int32_t>::max())) {
343
0
        return false;
344
0
      }
345
0
      clipped->set_edge(i++, edge_id);
346
0
    } else {
347
      // Otherwise decode the count and edge delta.
348
0
      uint32_t count = (delta & 7) + 1;
349
0
      delta >>= 3;
350
0
      if (count == 8) {
351
0
        count = delta + 8;
352
0
        if (!decoder->get_varint32(&delta)) return false;
353
0
      }
354
355
      // Guard against overflowing edge memory for bad inputs.
356
0
      if (ABSL_PREDICT_FALSE(i + count > static_cast<uint32_t>(num_edges))) {
357
0
        return false;
358
0
      }
359
360
0
      edge_id += delta;
361
      // Guard against overflowing edge memory for bad inputs.
362
0
      if (ABSL_PREDICT_FALSE(edge_id + count >
363
0
                             std::numeric_limits<int32_t>::max())) {
364
0
        return false;
365
0
      }
366
0
      for (; count > 0; --count, ++i, ++edge_id) {
367
0
        clipped->set_edge(i, edge_id);
368
0
      }
369
0
    }
370
0
  }
371
0
  return true;
372
0
}