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_line_string.hpp"
18 #include "dse_validate.hpp"
19 
20 #include "string_ref.hpp"
21 
22 #include <assert.h>
23 #include <iomanip>
24 #include <sstream>
25 
26 using namespace datastax;
27 using namespace datastax::internal::core;
28 using namespace datastax::internal::enterprise;
29 
30 extern "C" {
31 
dse_line_string_new()32 DseLineString* dse_line_string_new() { return DseLineString::to(new LineString()); }
33 
dse_line_string_free(DseLineString * line_string)34 void dse_line_string_free(DseLineString* line_string) { delete line_string->from(); }
35 
dse_line_string_reset(DseLineString * line_string)36 void dse_line_string_reset(DseLineString* line_string) { line_string->reset(); }
37 
dse_line_string_reserve(DseLineString * line_string,cass_uint32_t num_points)38 void dse_line_string_reserve(DseLineString* line_string, cass_uint32_t num_points) {
39   line_string->reserve(num_points);
40 }
41 
dse_line_string_add_point(DseLineString * line_string,cass_double_t x,cass_double_t y)42 CassError dse_line_string_add_point(DseLineString* line_string, cass_double_t x, cass_double_t y) {
43   line_string->add_point(x, y);
44   return CASS_OK;
45 }
46 
dse_line_string_finish(DseLineString * line_string)47 CassError dse_line_string_finish(DseLineString* line_string) { return line_string->finish(); }
48 
dse_line_string_iterator_new()49 DseLineStringIterator* dse_line_string_iterator_new() {
50   return DseLineStringIterator::to(new LineStringIterator());
51 }
52 
dse_line_string_iterator_free(DseLineStringIterator * iterator)53 void dse_line_string_iterator_free(DseLineStringIterator* iterator) { delete iterator->from(); }
54 
dse_line_string_iterator_reset(DseLineStringIterator * iterator,const CassValue * value)55 CassError dse_line_string_iterator_reset(DseLineStringIterator* iterator, const CassValue* value) {
56   return iterator->reset_binary(value);
57 }
58 
dse_line_string_iterator_reset_with_wkt_n(DseLineStringIterator * iterator,const char * wkt,size_t wkt_length)59 CassError dse_line_string_iterator_reset_with_wkt_n(DseLineStringIterator* iterator,
60                                                     const char* wkt, size_t wkt_length) {
61   return iterator->reset_text(wkt, wkt_length);
62 }
63 
dse_line_string_iterator_reset_with_wkt(DseLineStringIterator * iterator,const char * wkt)64 CassError dse_line_string_iterator_reset_with_wkt(DseLineStringIterator* iterator,
65                                                   const char* wkt) {
66   return dse_line_string_iterator_reset_with_wkt_n(iterator, wkt, SAFE_STRLEN(wkt));
67 }
68 
dse_line_string_iterator_num_points(const DseLineStringIterator * iterator)69 cass_uint32_t dse_line_string_iterator_num_points(const DseLineStringIterator* iterator) {
70   return iterator->num_points();
71 }
72 
dse_line_string_iterator_next_point(DseLineStringIterator * iterator,cass_double_t * x,cass_double_t * y)73 CassError dse_line_string_iterator_next_point(DseLineStringIterator* iterator, cass_double_t* x,
74                                               cass_double_t* y) {
75   return iterator->next_point(x, y);
76 }
77 
78 } // extern "C"
79 
to_wkt() const80 String LineString::to_wkt() const {
81   // Special case empty line string
82   if (num_points_ == 0) {
83     return "LINESTRING EMPTY";
84   }
85 
86   OStringStream ss;
87   ss.precision(WKT_MAX_DIGITS);
88   ss << "LINESTRING (";
89   const cass_byte_t* pos = bytes_.data() + WKB_HEADER_SIZE + sizeof(cass_uint32_t);
90   for (cass_uint32_t i = 0; i < num_points_; ++i) {
91     if (i > 0) ss << ", ";
92     ss << decode_double(pos, native_byte_order());
93     pos += sizeof(cass_double_t);
94     ss << " ";
95     ss << decode_double(pos, native_byte_order());
96     pos += sizeof(cass_double_t);
97   }
98   ss << ")";
99   return ss.str();
100 }
101 
reset_binary(const CassValue * value)102 CassError LineStringIterator::reset_binary(const CassValue* value) {
103   size_t size = 0;
104   const cass_byte_t* pos = NULL;
105   WkbByteOrder byte_order;
106   cass_uint32_t num_points = 0;
107   CassError rc = CASS_OK;
108 
109   rc = validate_data_type(value, DSE_LINE_STRING_TYPE);
110   if (rc != CASS_OK) return rc;
111 
112   rc = cass_value_get_bytes(value, &pos, &size);
113   if (rc != CASS_OK) return rc;
114 
115   if (size < WKB_LINE_STRING_HEADER_SIZE) {
116     return CASS_ERROR_LIB_NOT_ENOUGH_DATA;
117   }
118   size -= WKB_LINE_STRING_HEADER_SIZE;
119 
120   if (decode_header(pos, &byte_order) != WKB_GEOMETRY_TYPE_LINESTRING) {
121     return CASS_ERROR_LIB_INVALID_DATA;
122   }
123   pos += WKB_HEADER_SIZE;
124 
125   num_points = decode_uint32(pos, byte_order);
126   pos += sizeof(cass_uint32_t);
127 
128   if (size < 2 * num_points * sizeof(cass_double_t)) {
129     return CASS_ERROR_LIB_NOT_ENOUGH_DATA;
130   }
131 
132   num_points_ = num_points;
133   binary_iterator_ = BinaryIterator(pos, pos + size, byte_order);
134   iterator_ = &binary_iterator_;
135 
136   return CASS_OK;
137 }
138 
isnum(int c)139 bool isnum(int c) { return isdigit(c) || c == '+' || c == '-' || c == '.'; }
140 
reset_text(const char * text,size_t size)141 CassError LineStringIterator::reset_text(const char* text, size_t size) {
142   cass_uint32_t num_points = 0;
143   const bool skip_numbers = true;
144   WktLexer lexer(text, size, skip_numbers);
145 
146   if (lexer.next_token() != WktLexer::TK_TYPE_LINESTRING) {
147     return CASS_ERROR_LIB_BAD_PARAMS;
148   }
149 
150   // Validate format and count the number of points
151   WktLexer::Token token = lexer.next_token();
152 
153   // Special case "LINESTRING EMPTY"
154   if (token == WktLexer::TK_EMPTY) {
155     return CASS_OK;
156   }
157 
158   if (token != WktLexer::TK_OPEN_PAREN) {
159     return CASS_ERROR_LIB_BAD_PARAMS;
160   }
161 
162   token = lexer.next_token();
163   while (token != WktLexer::TK_EOF && token != WktLexer::TK_CLOSE_PAREN) {
164     // First number in point
165     if (token != WktLexer::TK_NUMBER) {
166       return CASS_ERROR_LIB_BAD_PARAMS;
167     }
168 
169     // Second number in point
170     token = lexer.next_token();
171     if (token != WktLexer::TK_NUMBER) {
172       return CASS_ERROR_LIB_BAD_PARAMS;
173     }
174 
175     ++num_points;
176 
177     // Check and skip "," token
178     token = lexer.next_token();
179     if (token == WktLexer::TK_COMMA) {
180       token = lexer.next_token();
181       // Verify there are more points
182       if (token != WktLexer::TK_NUMBER) {
183         return CASS_ERROR_LIB_BAD_PARAMS;
184       }
185     }
186   }
187 
188   // Validate closing ")"
189   if (token != WktLexer::TK_CLOSE_PAREN) {
190     return CASS_ERROR_LIB_BAD_PARAMS;
191   }
192 
193   num_points_ = num_points;
194   text_iterator_ = TextIterator(text, size);
195   iterator_ = &text_iterator_;
196 
197   return CASS_OK;
198 }
199 
next_point(cass_double_t * x,cass_double_t * y)200 CassError LineStringIterator::BinaryIterator::next_point(cass_double_t* x, cass_double_t* y) {
201   if (position_ >= points_end_) {
202     return CASS_ERROR_LIB_INVALID_STATE;
203   }
204 
205   *x = decode_double(position_, byte_order_);
206   position_ += sizeof(cass_double_t);
207   *y = decode_double(position_, byte_order_);
208   position_ += sizeof(cass_double_t);
209 
210   return CASS_OK;
211 }
212 
TextIterator(const char * text,size_t size)213 LineStringIterator::TextIterator::TextIterator(const char* text, size_t size)
214     : lexer_(text, size) {
215   WktLexer::Token token;
216   UNUSED_(token);
217   // Skip over "LINESTRING (" tokens
218   token = lexer_.next_token();
219   assert(token == WktLexer::TK_TYPE_LINESTRING);
220   token = lexer_.next_token();
221   assert(token == WktLexer::TK_OPEN_PAREN);
222 }
223 
next_point(cass_double_t * x,cass_double_t * y)224 CassError LineStringIterator::TextIterator::next_point(cass_double_t* x, cass_double_t* y) {
225   WktLexer::Token token;
226 
227   // If we're at the end of the text this will return WktLexer::TK_EOF
228   token = lexer_.next_token();
229   if (token != WktLexer::TK_NUMBER) {
230     return CASS_ERROR_LIB_INVALID_STATE;
231   }
232   *x = lexer_.number();
233 
234   token = lexer_.next_token();
235   assert(token == WktLexer::TK_NUMBER);
236   *y = lexer_.number();
237 
238   // Skip trailing "," or  ")" tokens
239   token = lexer_.next_token();
240   assert(token == WktLexer::TK_COMMA || token == WktLexer::TK_CLOSE_PAREN);
241 
242   return CASS_OK;
243 }
244