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