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