Skip to content

Commit ac7e1c3

Browse files
authored
Merge pull request #1469 from joto/wkb
Refactoring of WKB code
2 parents b081512 + e378060 commit ac7e1c3

File tree

1 file changed

+108
-61
lines changed

1 file changed

+108
-61
lines changed

src/wkb.hpp

Lines changed: 108 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -43,49 +43,66 @@ enum wkb_byte_order_type_t : uint8_t
4343
#endif
4444
};
4545

46+
template <typename T>
47+
static void str_push(std::string *str, T data)
48+
{
49+
assert(str);
50+
str->append(reinterpret_cast<char const *const>(&data), sizeof(T));
51+
}
52+
4653
/**
47-
* Writer for EWKB data suitable for postgres.
54+
* Add a EWKB header to the string.
4855
*
49-
* Code has been largely derived from osmium::geom::WKBFactoryImpl.
56+
* \pre \code str != nullptr \endcode
5057
*/
51-
class writer_t
58+
inline std::size_t write_header(std::string *str, geometry_type type,
59+
uint32_t srid)
5260
{
61+
assert(str);
5362

54-
std::string m_data;
55-
int m_srid;
63+
str_push(str, Endian);
64+
str_push(str, type | wkb_srid);
65+
str_push(str, srid);
5666

57-
size_t m_geometry_size_offset = 0;
58-
size_t m_multigeometry_size_offset = 0;
59-
size_t m_ring_size_offset = 0;
67+
return str->size();
68+
}
6069

61-
size_t header(std::string &str, geometry_type type, bool add_length) const
62-
{
63-
str_push(str, Endian);
64-
str_push(str, type | wkb_srid);
65-
str_push(str, m_srid);
70+
/**
71+
* Add a EWKB header to the string and add a dummy placeholder length of 0.
72+
* This can later be replaced by the real length.
73+
*
74+
* \pre \code str != nullptr \endcode
75+
*/
76+
inline std::size_t write_header_with_length(std::string *str,
77+
geometry_type type, uint32_t srid)
78+
{
79+
auto const offset = write_header(str, type, srid);
80+
str_push(str, static_cast<uint32_t>(0));
81+
return offset;
82+
}
6683

67-
std::size_t const offset = str.size();
68-
if (add_length) {
69-
str_push(str, static_cast<uint32_t>(0));
70-
}
71-
return offset;
72-
}
84+
/// Create EWKB Point geometry.
85+
inline std::string create_point(double x, double y, uint32_t srid = 4326)
86+
{
87+
std::string data;
88+
data.reserve(25); // Point geometries are always 25 bytes
7389

74-
void set_size(size_t const offset, size_t const size)
75-
{
76-
uint32_t s = static_cast<uint32_t>(size);
77-
std::copy_n(reinterpret_cast<char *>(&s), sizeof(uint32_t),
78-
&m_data[offset]);
79-
}
90+
write_header(&data, wkb_point, srid);
91+
str_push(&data, x);
92+
str_push(&data, y);
8093

81-
template <typename T>
82-
inline static void str_push(std::string &str, T data)
83-
{
84-
str.append(reinterpret_cast<char const *>(&data), sizeof(T));
85-
}
94+
return data;
95+
}
8696

97+
/**
98+
* Writer for EWKB data suitable for postgres.
99+
*
100+
* Code has been largely derived from osmium::geom::WKBFactoryImpl.
101+
*/
102+
class writer_t
103+
{
87104
public:
88-
explicit writer_t(int srid) : m_srid(srid) {}
105+
explicit writer_t(uint32_t srid) : m_srid(srid) {}
89106

90107
void add_sub_geometry(std::string const &part)
91108
{
@@ -96,31 +113,27 @@ class writer_t
96113
void add_location(osmium::geom::Coordinates const &xy)
97114
{
98115
assert(!m_data.empty());
99-
str_push(m_data, xy.x);
100-
str_push(m_data, xy.y);
116+
str_push(&m_data, xy.x);
117+
str_push(&m_data, xy.y);
101118
}
102119

103120
/* Point */
104121

105122
std::string make_point(osmium::geom::Coordinates const &xy) const
106123
{
107-
std::string data;
108-
header(data, wkb_point, false);
109-
str_push(data, xy.x);
110-
str_push(data, xy.y);
111-
112-
return data;
124+
return create_point(xy.x, xy.y, m_srid);
113125
}
114126

115127
/* LineString */
116128

117129
void linestring_start()
118130
{
119131
assert(m_data.empty());
120-
m_geometry_size_offset = header(m_data, wkb_line, true);
132+
m_geometry_size_offset =
133+
write_header_with_length(&m_data, wkb_line, m_srid);
121134
}
122135

123-
std::string linestring_finish(size_t num_points)
136+
std::string linestring_finish(std::size_t num_points)
124137
{
125138
set_size(m_geometry_size_offset, num_points);
126139
std::string data;
@@ -136,10 +149,11 @@ class writer_t
136149
void multilinestring_start()
137150
{
138151
assert(m_data.empty());
139-
m_multigeometry_size_offset = header(m_data, wkb_multi_line, true);
152+
m_multigeometry_size_offset =
153+
write_header_with_length(&m_data, wkb_multi_line, m_srid);
140154
}
141155

142-
std::string multilinestring_finish(size_t num_lines)
156+
std::string multilinestring_finish(std::size_t num_lines)
143157
{
144158
set_size(m_multigeometry_size_offset, num_lines);
145159
std::string data;
@@ -155,21 +169,22 @@ class writer_t
155169
void polygon_start()
156170
{
157171
assert(m_data.empty());
158-
m_geometry_size_offset = header(m_data, wkb_polygon, true);
172+
m_geometry_size_offset =
173+
write_header_with_length(&m_data, wkb_polygon, m_srid);
159174
}
160175

161176
void polygon_ring_start()
162177
{
163178
m_ring_size_offset = m_data.size();
164-
str_push(m_data, static_cast<uint32_t>(0));
179+
str_push(&m_data, static_cast<uint32_t>(0));
165180
}
166181

167-
void polygon_ring_finish(size_t num_points)
182+
void polygon_ring_finish(std::size_t num_points)
168183
{
169184
set_size(m_ring_size_offset, num_points);
170185
}
171186

172-
std::string polygon_finish(size_t num_rings)
187+
std::string polygon_finish(std::size_t num_rings)
173188
{
174189
set_size(m_geometry_size_offset, num_rings);
175190
std::string data;
@@ -185,10 +200,11 @@ class writer_t
185200
void multipolygon_start()
186201
{
187202
assert(m_data.empty());
188-
m_multigeometry_size_offset = header(m_data, wkb_multi_polygon, true);
203+
m_multigeometry_size_offset =
204+
write_header_with_length(&m_data, wkb_multi_polygon, m_srid);
189205
}
190206

191-
std::string multipolygon_finish(size_t num_polygons)
207+
std::string multipolygon_finish(std::size_t num_polygons)
192208
{
193209
set_size(m_multigeometry_size_offset, num_polygons);
194210
std::string data;
@@ -198,7 +214,24 @@ class writer_t
198214

199215
return data;
200216
}
201-
};
217+
218+
private:
219+
void set_size(std::size_t offset, std::size_t size)
220+
{
221+
assert(m_data.size() >= offset + sizeof(uint32_t));
222+
auto const s = static_cast<uint32_t>(size);
223+
std::memcpy(&m_data[offset], reinterpret_cast<char const *>(&s),
224+
sizeof(uint32_t));
225+
}
226+
227+
std::string m_data;
228+
229+
std::size_t m_geometry_size_offset = 0;
230+
std::size_t m_multigeometry_size_offset = 0;
231+
std::size_t m_ring_size_offset = 0;
232+
233+
uint32_t m_srid;
234+
}; // class writer_t
202235

203236
/**
204237
* Class that allows to iterate over the elements of a ewkb geometry.
@@ -245,20 +278,20 @@ class parser_t
245278
return out;
246279
}
247280

248-
explicit parser_t(char const *wkb) : m_wkb(wkb), m_pos(0) {}
249-
explicit parser_t(std::string const &wkb) : m_wkb(wkb.c_str()), m_pos(0) {}
281+
explicit parser_t(std::string const &wkb) noexcept : m_wkb(&wkb) {}
250282

251-
size_t save_pos() const { return m_pos; }
252-
void rewind(size_t pos) { m_pos = pos; }
283+
std::size_t save_pos() const noexcept { return m_pos; }
253284

254-
int read_header()
285+
void rewind(std::size_t pos) noexcept { m_pos = pos; }
286+
287+
uint32_t read_header()
255288
{
256-
m_pos += sizeof(uint8_t); // skip endianess
289+
m_pos += sizeof(uint8_t); // skip endianess marker
257290

258291
auto const type = read_data<uint32_t>();
259292

260293
if (type & wkb_srid) {
261-
m_pos += sizeof(int); // skip srid
294+
m_pos += sizeof(uint32_t); // skip SRID
262295
}
263296

264297
return type & 0xffU;
@@ -274,7 +307,12 @@ class parser_t
274307
return osmium::geom::Coordinates{x, y};
275308
}
276309

277-
void skip_points(size_t num) { m_pos += sizeof(double) * 2 * num; }
310+
void skip_points(std::size_t num)
311+
{
312+
auto const length = sizeof(double) * 2 * num;
313+
check_available(length);
314+
m_pos += length;
315+
}
278316

279317
template <typename PROJ>
280318
double get_area(PROJ *proj = nullptr)
@@ -355,18 +393,27 @@ class parser_t
355393
return std::abs(total) * 0.5;
356394
}
357395

396+
void check_available(std::size_t length)
397+
{
398+
if (m_pos + length > m_wkb->size()) {
399+
throw std::runtime_error{"Invalid EWKB geometry found"};
400+
}
401+
}
402+
358403
template <typename T>
359404
T read_data()
360405
{
406+
check_available(sizeof(T));
407+
361408
T data;
362-
memcpy(&data, m_wkb + m_pos, sizeof(T));
409+
std::memcpy(&data, m_wkb->data() + m_pos, sizeof(T));
363410
m_pos += sizeof(T);
364411

365412
return data;
366413
}
367414

368-
char const *m_wkb;
369-
size_t m_pos;
415+
std::string const *m_wkb;
416+
std::size_t m_pos = 0;
370417
};
371418

372419
} // namespace ewkb

0 commit comments

Comments
 (0)