1 /*
2 Copyright (c) DataStax, Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #include "dse_polygon.hpp"
18 #include "dse_validate.hpp"
19
20 #include <assert.h>
21 #include <iomanip>
22 #include <sstream>
23
24 using namespace datastax;
25 using namespace datastax::internal;
26 using namespace datastax::internal::core;
27 using namespace datastax::internal::enterprise;
28
29 extern "C" {
30
dse_polygon_new()31 DsePolygon* dse_polygon_new() { return DsePolygon::to(new enterprise::Polygon()); }
32
dse_polygon_free(DsePolygon * polygon)33 void dse_polygon_free(DsePolygon* polygon) { delete polygon->from(); }
34
dse_polygon_reset(DsePolygon * polygon)35 void dse_polygon_reset(DsePolygon* polygon) { polygon->reset(); }
36
dse_polygon_reserve(DsePolygon * polygon,cass_uint32_t num_rings,cass_uint32_t total_num_points)37 void dse_polygon_reserve(DsePolygon* polygon, cass_uint32_t num_rings,
38 cass_uint32_t total_num_points) {
39 polygon->reserve(num_rings, total_num_points);
40 }
41
dse_polygon_start_ring(DsePolygon * polygon)42 CassError dse_polygon_start_ring(DsePolygon* polygon) { return polygon->start_ring(); }
43
dse_polygon_add_point(DsePolygon * polygon,cass_double_t x,cass_double_t y)44 CassError dse_polygon_add_point(DsePolygon* polygon, cass_double_t x, cass_double_t y) {
45 polygon->add_point(x, y);
46 return CASS_OK;
47 }
48
dse_polygon_finish(DsePolygon * polygon)49 CassError dse_polygon_finish(DsePolygon* polygon) { return polygon->finish(); }
50
dse_polygon_iterator_new()51 DsePolygonIterator* dse_polygon_iterator_new() {
52 return DsePolygonIterator::to(new PolygonIterator());
53 }
54
dse_polygon_iterator_reset(DsePolygonIterator * iterator,const CassValue * value)55 CassError dse_polygon_iterator_reset(DsePolygonIterator* iterator, const CassValue* value) {
56 return iterator->reset_binary(value);
57 }
58
dse_polygon_iterator_reset_with_wkt_n(DsePolygonIterator * iterator,const char * wkt,size_t wkt_length)59 CassError dse_polygon_iterator_reset_with_wkt_n(DsePolygonIterator* iterator, const char* wkt,
60 size_t wkt_length) {
61 return iterator->reset_text(wkt, wkt_length);
62 }
63
dse_polygon_iterator_reset_with_wkt(DsePolygonIterator * iterator,const char * wkt)64 CassError dse_polygon_iterator_reset_with_wkt(DsePolygonIterator* iterator, const char* wkt) {
65 return dse_polygon_iterator_reset_with_wkt_n(iterator, wkt, SAFE_STRLEN(wkt));
66 }
67
dse_polygon_iterator_free(DsePolygonIterator * iterator)68 void dse_polygon_iterator_free(DsePolygonIterator* iterator) { delete iterator->from(); }
69
dse_polygon_iterator_num_rings(const DsePolygonIterator * iterator)70 cass_uint32_t dse_polygon_iterator_num_rings(const DsePolygonIterator* iterator) {
71 return iterator->num_rings();
72 }
73
dse_polygon_iterator_next_num_points(DsePolygonIterator * iterator,cass_uint32_t * num_points)74 CassError dse_polygon_iterator_next_num_points(DsePolygonIterator* iterator,
75 cass_uint32_t* num_points) {
76 return iterator->next_num_points(num_points);
77 }
78
dse_polygon_iterator_next_point(DsePolygonIterator * iterator,cass_double_t * x,cass_double_t * y)79 CassError dse_polygon_iterator_next_point(DsePolygonIterator* iterator, cass_double_t* x,
80 cass_double_t* y) {
81 return iterator->next_point(x, y);
82 }
83
84 } // extern "C"
85
to_wkt() const86 String Polygon::to_wkt() const {
87 // Special case empty polygon
88 if (num_rings_ == 0) {
89 return "POLYGON EMPTY";
90 }
91
92 OStringStream ss;
93 ss.precision(WKT_MAX_DIGITS);
94 ss << "POLYGON (";
95 const cass_byte_t* pos = bytes_.data() + WKB_HEADER_SIZE + sizeof(cass_uint32_t);
96 for (cass_uint32_t i = 0; i < num_rings_; ++i) {
97 if (i > 0) ss << ", ";
98 ss << "(";
99 cass_uint32_t num_points = decode_uint32(pos, native_byte_order());
100 pos += sizeof(cass_uint32_t);
101 for (cass_uint32_t j = 0; j < num_points; ++j) {
102 if (j > 0) ss << ", ";
103 ss << decode_double(pos, native_byte_order());
104 pos += sizeof(cass_double_t);
105 ss << " ";
106 ss << decode_double(pos, native_byte_order());
107 pos += sizeof(cass_double_t);
108 }
109 ss << ")";
110 }
111 ss << ")";
112 return ss.str();
113 }
114
reset_binary(const CassValue * value)115 CassError PolygonIterator::reset_binary(const CassValue* value) {
116 size_t size;
117 const cass_byte_t* pos;
118 WkbByteOrder byte_order;
119 cass_uint32_t num_rings;
120
121 CassError rc = validate_data_type(value, DSE_POLYGON_TYPE);
122 if (rc != CASS_OK) return rc;
123
124 rc = cass_value_get_bytes(value, &pos, &size);
125 if (rc != CASS_OK) return rc;
126
127 if (size < WKB_POLYGON_HEADER_SIZE) {
128 return CASS_ERROR_LIB_NOT_ENOUGH_DATA;
129 }
130 size -= WKB_POLYGON_HEADER_SIZE;
131
132 if (decode_header(pos, &byte_order) != WKB_GEOMETRY_TYPE_POLYGON) {
133 return CASS_ERROR_LIB_INVALID_DATA;
134 }
135 pos += WKB_HEADER_SIZE;
136
137 num_rings = decode_uint32(pos, byte_order);
138 pos += sizeof(cass_uint32_t);
139
140 const cass_byte_t* rings = pos;
141 const cass_byte_t* rings_end = pos + size;
142
143 for (cass_uint32_t i = 0; i < num_rings; ++i) {
144 cass_uint32_t num_points;
145
146 if (size < sizeof(cass_uint32_t)) {
147 return CASS_ERROR_LIB_NOT_ENOUGH_DATA;
148 }
149 size -= sizeof(cass_uint32_t);
150
151 num_points = decode_uint32(pos, byte_order);
152 pos += sizeof(cass_uint32_t);
153
154 if (size < 2 * num_points * sizeof(cass_double_t)) {
155 return CASS_ERROR_LIB_NOT_ENOUGH_DATA;
156 }
157 size -= 2 * num_points * sizeof(cass_double_t);
158 }
159
160 num_rings_ = num_rings;
161 binary_iterator_ = BinaryIterator(rings, rings_end, byte_order);
162 iterator_ = &binary_iterator_;
163
164 return CASS_OK;
165 }
166
reset_text(const char * text,size_t size)167 CassError PolygonIterator::reset_text(const char* text, size_t size) {
168 cass_uint32_t num_rings = 0;
169 const bool skip_numbers = true;
170 WktLexer lexer(text, size, skip_numbers);
171
172 if (lexer.next_token() != WktLexer::TK_TYPE_POLYGON) {
173 return CASS_ERROR_LIB_BAD_PARAMS;
174 }
175
176 // Validate format and count the number of rings
177 WktLexer::Token token = lexer.next_token();
178
179 // Special case "POLYGON EMPTY"
180 if (token == WktLexer::TK_EMPTY) {
181 return CASS_OK;
182 }
183
184 if (token != WktLexer::TK_OPEN_PAREN) {
185 return CASS_ERROR_LIB_BAD_PARAMS;
186 }
187
188 token = lexer.next_token();
189 while (token != WktLexer::TK_EOF && token != WktLexer::TK_CLOSE_PAREN) {
190 // Start ring
191 if (token != WktLexer::TK_OPEN_PAREN) {
192 return CASS_ERROR_LIB_BAD_PARAMS;
193 }
194
195 // Consume points in ring
196 token = lexer.next_token();
197 while (token != WktLexer::TK_EOF && token != WktLexer::TK_CLOSE_PAREN) {
198 // First number in point
199 if (token != WktLexer::TK_NUMBER) {
200 return CASS_ERROR_LIB_BAD_PARAMS;
201 }
202
203 // Second number in point
204 token = lexer.next_token();
205 if (token != WktLexer::TK_NUMBER) {
206 return CASS_ERROR_LIB_BAD_PARAMS;
207 }
208
209 // Check and skip "," token
210 token = lexer.next_token();
211 if (token == WktLexer::TK_COMMA) {
212 token = lexer.next_token();
213 // Verify there are more points
214 if (token != WktLexer::TK_NUMBER) {
215 return CASS_ERROR_LIB_BAD_PARAMS;
216 }
217 }
218 }
219
220 // End ring
221 if (token != WktLexer::TK_CLOSE_PAREN) {
222 return CASS_ERROR_LIB_BAD_PARAMS;
223 }
224
225 ++num_rings;
226
227 // Check and skip "," token
228 token = lexer.next_token();
229 if (token == WktLexer::TK_COMMA) {
230 token = lexer.next_token();
231 // Verify there are more rings
232 if (token != WktLexer::TK_OPEN_PAREN) {
233 return CASS_ERROR_LIB_BAD_PARAMS;
234 }
235 }
236 }
237
238 // Validate closing ")"
239 if (token != WktLexer::TK_CLOSE_PAREN) {
240 return CASS_ERROR_LIB_BAD_PARAMS;
241 }
242
243 num_rings_ = num_rings;
244 text_iterator_ = TextIterator(text, size);
245 iterator_ = &text_iterator_;
246
247 return CASS_OK;
248 }
249
next_num_points(cass_uint32_t * num_points)250 CassError PolygonIterator::BinaryIterator::next_num_points(cass_uint32_t* num_points) {
251 if (state_ != STATE_NUM_POINTS) {
252 return CASS_ERROR_LIB_INVALID_STATE;
253 }
254
255 uint32_t n = *num_points = decode_uint32(position_, byte_order_);
256 position_ += sizeof(cass_uint32_t);
257 points_end_ = position_ + n * 2 * sizeof(cass_double_t);
258 state_ = STATE_POINTS;
259
260 return CASS_OK;
261 }
262
next_point(cass_double_t * x,cass_double_t * y)263 CassError PolygonIterator::BinaryIterator::next_point(cass_double_t* x, cass_double_t* y) {
264 if (state_ != STATE_POINTS) {
265 return CASS_ERROR_LIB_INVALID_STATE;
266 }
267
268 *x = decode_double(position_, byte_order_);
269 position_ += sizeof(cass_double_t);
270 *y = decode_double(position_, byte_order_);
271 position_ += sizeof(cass_double_t);
272 if (position_ >= rings_end_) {
273 state_ = STATE_DONE;
274 } else if (position_ >= points_end_) {
275 state_ = STATE_NUM_POINTS;
276 }
277
278 return CASS_OK;
279 }
280
TextIterator(const char * text,size_t size)281 PolygonIterator::TextIterator::TextIterator(const char* text, size_t size)
282 : state_(STATE_NUM_POINTS)
283 , lexer_(text, size) {
284 WktLexer::Token token;
285 UNUSED_(token);
286 // Skip over "POLYGON (" tokens
287 token = lexer_.next_token();
288 assert(token == WktLexer::TK_TYPE_POLYGON);
289 token = lexer_.next_token();
290 assert(token == WktLexer::TK_OPEN_PAREN);
291 }
292
next_num_points(cass_uint32_t * num_points)293 CassError PolygonIterator::TextIterator::next_num_points(cass_uint32_t* num_points) {
294 WktLexer::Token token;
295 token = lexer_.next_token();
296
297 *num_points = 0;
298
299 // If we're at the end of the text this will return WktLexer::TK_EOF
300 if (state_ != STATE_NUM_POINTS || token != WktLexer::TK_OPEN_PAREN) {
301 return CASS_ERROR_LIB_INVALID_STATE;
302 }
303
304 const bool skip_numbers = true;
305 WktLexer temp(lexer_, skip_numbers); // Read forward using a temp lexer
306
307 token = temp.next_token();
308 while (token != WktLexer::TK_EOF && token != WktLexer::TK_CLOSE_PAREN) {
309 // First number in point
310 assert(token == WktLexer::TK_NUMBER);
311
312 // Second number in point
313 token = temp.next_token();
314 assert(token == WktLexer::TK_NUMBER);
315
316 ++(*num_points);
317
318 // Check and skip "," token
319 token = temp.next_token();
320 if (token == WktLexer::TK_COMMA) {
321 token = temp.next_token();
322 assert(token == WktLexer::TK_NUMBER);
323 }
324 }
325
326 assert(token == WktLexer::TK_CLOSE_PAREN);
327 state_ = STATE_POINTS;
328
329 return CASS_OK;
330 }
331
next_point(cass_double_t * x,cass_double_t * y)332 CassError PolygonIterator::TextIterator::next_point(cass_double_t* x, cass_double_t* y) {
333 WktLexer::Token token;
334
335 if (state_ != STATE_POINTS) {
336 return CASS_ERROR_LIB_INVALID_STATE;
337 }
338
339 // First number in point
340 token = lexer_.next_token();
341 assert(token == WktLexer::TK_NUMBER);
342 *x = lexer_.number();
343
344 // Second number in point
345 token = lexer_.next_token();
346 assert(token == WktLexer::TK_NUMBER);
347 *y = lexer_.number();
348
349 token = lexer_.next_token();
350 if (token == WktLexer::TK_CLOSE_PAREN) {
351 // Done with this ring
352 token = lexer_.next_token();
353 if (token == WktLexer::TK_CLOSE_PAREN) {
354 // Done with last ring
355 state_ = STATE_DONE;
356 } else {
357 // More rings
358 assert(token == WktLexer::TK_COMMA);
359 state_ = STATE_NUM_POINTS;
360 }
361 } else {
362 // More points
363 assert(token == WktLexer::TK_COMMA);
364 }
365
366 return CASS_OK;
367 }
368