knowrob  2.1.0
A Knowledge Base System for Cognition-enabled Robots
bson_pl.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of KnowRob, please consult
3  * https://github.com/knowrob/knowrob for license details.
4  */
5 
6 #include "knowrob/reasoner/mongolog/bson_pl.h"
7 #include "knowrob/storage/mongo/MongoException.h"
8 #include <iostream>
9 
10 // NOTE: returning true would stop further iteration of the document
11 #define APPEND_BSON_PL_PAIR(list,key,value,type) ((PlTail*)list)->append(PlCompound("-", \
12  PlTermv(PlTerm(key), PlCompound(type, PlTerm(value))))) && false
13 
14 static bool bson_visit_double([[maybe_unused]] const bson_iter_t *iter, const char *key, double v_double, void *data) {
15  return APPEND_BSON_PL_PAIR(data,key,v_double,"double");
16 }
17 
18 static bool bson_visit_int32([[maybe_unused]] const bson_iter_t *iter, const char *key, int32_t v_int32, void *data) {
19  return APPEND_BSON_PL_PAIR(data,key,(long)v_int32,"int");
20 }
21 
22 static bool bson_visit_int64([[maybe_unused]] const bson_iter_t *iter, const char *key, int64_t v_int64, void *data) {
23  return APPEND_BSON_PL_PAIR(data,key,(long)v_int64,"int");
24 }
25 
26 static bool bson_visit_oid([[maybe_unused]] const bson_iter_t *iter, const char *key, const bson_oid_t *v_oid, void *data) {
27  char str[25];
28  bson_oid_to_string(v_oid, str);
29  return APPEND_BSON_PL_PAIR(data,key,str,"id");
30 }
31 
32 static bool bson_visit_bool([[maybe_unused]] const bson_iter_t *iter, const char *key, bool v_bool, void *data) {
33  static PlAtom ATOM_true("true");
34  static PlAtom ATOM_false("false");
35 
36  if(v_bool) {
37  return APPEND_BSON_PL_PAIR(data,key,ATOM_true,"bool");
38  }
39  else {
40  return APPEND_BSON_PL_PAIR(data,key,ATOM_false,"bool");
41  }
42 }
43 
44 static bool bson_visit_utf8([[maybe_unused]] const bson_iter_t *iter, [[maybe_unused]] const char *key, size_t v_utf8_len, const char *v_utf8, void *data) {
45  // make sure we have a null-terminated string
46  std::string null_terminated(v_utf8, v_utf8_len);
47  return APPEND_BSON_PL_PAIR(data,key,null_terminated.c_str(),"string");
48 }
49 
50 static bool bson_visit_date_time([[maybe_unused]] const bson_iter_t *iter, const char *key, int64_t msec_since_epoch, void *data) {
51  double sec_since_epoch = ((double)msec_since_epoch)/1000.0;
52  return APPEND_BSON_PL_PAIR(data,key,sec_since_epoch,"time");
53 }
54 
55 static bool bson_visit_decimal128([[maybe_unused]] const bson_iter_t *iter, const bson_decimal128_t *v_decimal128, void *data) {
56  static PlAtom ATOM_Neg_Infinity("-Infinity");
57  static PlAtom ATOM_Pos_Infinity("Infinity");
58 
59  auto *v_pl = (PlTail*)data;
60  // positive infinity
61  if(v_decimal128->high == 0x7800000000000000) {
62  v_pl->append(PlCompound("double", PlTerm(ATOM_Pos_Infinity)));
63  }
64  // negative infinity
65  else if(v_decimal128->high == 0xf800000000000000) {
66  v_pl->append(PlCompound("double", PlTerm(ATOM_Neg_Infinity)));
67  }
68  else {
69  char buffer[BSON_DECIMAL128_STRING];
70  bson_decimal128_to_string(v_decimal128,buffer);
71  setlocale(LC_NUMERIC,"C");
72  v_pl->append(PlCompound("double", PlTerm(atof(buffer))));
73  setlocale(LC_NUMERIC,"");
74  }
75  return false; // NOTE: returning true stops further iteration of the document
76 }
77 
78 static bool bson_visit_decimal128([[maybe_unused]] const bson_iter_t *iter, const char *key, const bson_decimal128_t *v_decimal128, void *data) {
79  static PlAtom ATOM_Neg_Infinity("-Infinity");
80  static PlAtom ATOM_Pos_Infinity("Infinity");
81 
82  auto *out_list = (PlTail*)data;
83  PlTermv v_pl(2);
84  v_pl[0] = PlTerm(key);
85  // positive infinity
86  if(v_decimal128->high == 0x7800000000000000) {
87  v_pl[1] = PlCompound("double", PlTerm(ATOM_Pos_Infinity));
88  }
89  // negative infinity
90  else if(v_decimal128->high == 0xf800000000000000) {
91  v_pl[1] = PlCompound("double", PlTerm(ATOM_Neg_Infinity));
92  }
93  else {
94  char buffer[BSON_DECIMAL128_STRING];
95  bson_decimal128_to_string(v_decimal128,buffer);
96  setlocale(LC_NUMERIC,"C");
97  v_pl[1] = PlCompound("double", PlTerm(atof(buffer)));
98  setlocale(LC_NUMERIC,"");
99  }
100  out_list->append(PlCompound("-",v_pl));
101  return false; // NOTE: returning true stops further iteration of the document
102 }
103 
104 static bool bson_iter_append_array(bson_iter_t *iter, PlTail *pl_array) { // NOLINT(misc-no-recursion)
105  static PlAtom ATOM_true("true");
106  static PlAtom ATOM_false("false");
107 
108  if(BSON_ITER_HOLDS_UTF8(iter)) {
109  pl_array->append(PlCompound("string",
110  PlTerm(bson_iter_utf8(iter, nullptr))));
111  }
112  else if(BSON_ITER_HOLDS_DOCUMENT(iter)) {
113  const uint8_t *data = nullptr;
114  uint32_t len = 0;
115  bson_iter_document(iter, &len, &data);
116  bson_t *doc = bson_new_from_data(data, len);
117  pl_array->append(bson_to_term(doc));
118  bson_destroy(doc);
119  }
120  else if(BSON_ITER_HOLDS_ARRAY(iter)) {
121  const uint8_t *array = nullptr;
122  uint32_t array_len = 0;
123  bson_iter_array(iter, &array_len, &array);
124  bson_t *inner_doc = bson_new_from_data(array, array_len);
125  bson_iter_t inner_iter;
126  PlTerm inner_term;
127  PlTail inner_array(inner_term);
128  if(bson_iter_init(&inner_iter, inner_doc)) {
129  while(bson_iter_next(&inner_iter)) {
130  bson_iter_append_array(&inner_iter,&inner_array);
131  }
132  }
133  inner_array.close();
134  pl_array->append(PlCompound("array",inner_term));
135  bson_destroy(inner_doc);
136  }
137  else if(BSON_ITER_HOLDS_DOUBLE(iter)) {
138  pl_array->append(PlCompound("double",
139  PlTerm(bson_iter_double(iter))));
140  }
141  else if(BSON_ITER_HOLDS_DECIMAL128(iter)) {
142  bson_decimal128_t v_decimal128;
143  bson_iter_decimal128(iter, &v_decimal128);
144  bson_visit_decimal128(iter, &v_decimal128, pl_array);
145  }
146  else if(BSON_ITER_HOLDS_INT32(iter)) {
147  pl_array->append(PlCompound("int",
148  PlTerm((long)bson_iter_int32(iter))));
149  }
150  else if(BSON_ITER_HOLDS_INT64(iter)) {
151  pl_array->append(PlCompound("int",
152  PlTerm((long)bson_iter_int32(iter))));
153  }
154  else if(BSON_ITER_HOLDS_BOOL(iter)) {
155  if(bson_iter_bool(iter)) {
156  pl_array->append(PlCompound("bool", PlTerm(ATOM_true)));
157  }
158  else {
159  pl_array->append(PlCompound("bool", PlTerm(ATOM_false)));
160  }
161  }
162  else if(BSON_ITER_HOLDS_DATE_TIME(iter)) {
163  double sec_since_epoch = ((double)bson_iter_date_time(iter))/1000.0;
164  pl_array->append(PlCompound("time", PlTerm(sec_since_epoch)));
165  }
166  else {
167  bson_type_t iter_t = bson_iter_type(iter);
168  std::cout << "WARN: unsupported array type '" << iter_t << "'" << std::endl;
169  return false;
170  }
171  return true;
172 }
173 
174 static bool bson_visit_array([[maybe_unused]] const bson_iter_t *iter, const char *key, const bson_t *v_array, void *data) {
175  PlTermv av(1);
176  PlTail pl_array(av[0]);
177  bson_iter_t array_iter;
178  if(bson_iter_init(&array_iter, v_array)) {
179  while(bson_iter_next(&array_iter)) {
180  bson_iter_append_array(&array_iter, &pl_array);
181  }
182  }
183  pl_array.close();
184  return APPEND_BSON_PL_PAIR(data,key,av[0],"array");
185 }
186 
187 static bool bson_visit_document([[maybe_unused]] const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data) {
188  auto *out_list = (PlTail*)data;
189  out_list->append(PlCompound("-",
190  PlTermv(PlTerm(key), bson_to_term(v_document))));
191  return false; // NOTE: returning true stops further iteration of the document
192 }
193 
194 static bson_visitor_t get_bson_visitor() {
195  bson_visitor_t visitor;
196  visitor.visit_double = bson_visit_double;
197  visitor.visit_decimal128 = bson_visit_decimal128;
198  visitor.visit_int32 = bson_visit_int32;
199  visitor.visit_int64 = bson_visit_int64;
200  visitor.visit_bool = bson_visit_bool;
201  visitor.visit_oid = bson_visit_oid;
202  visitor.visit_utf8 = bson_visit_utf8;
203 // visitor.visit_timestamp = bson_visit_timestamp;
204  visitor.visit_date_time = bson_visit_date_time;
205  visitor.visit_array = bson_visit_array;
206  visitor.visit_document = bson_visit_document;
207  return visitor;
208 }
209 
210 PlTerm bson_to_term(const bson_t *doc)
211 {
212  static bson_visitor_t visitor = get_bson_visitor();
213  bson_iter_t iter;
214  PlTerm term;
215  PlTail out_list(term);
216  if (bson_iter_init(&iter, doc)) {
217  if(bson_iter_visit_all(&iter, &visitor, &out_list)) {
218  bson_error_t err;
219  bson_set_error(&err,
220  MONGOC_ERROR_BSON,
221  MONGOC_ERROR_BSON_INVALID,
222  "BSON iteration prematurely stopped.");
223  throw knowrob::mongo::MongoException("bson", err);
224  }
225  }
226  out_list.close();
227  return term;
228 }
229 
230 static bool bsonpl_append_typed(bson_t *doc, const char *key, const PlTerm &term, bson_error_t *err) { // NOLINT(misc-no-recursion)
231  static PlAtom ATOM_array("array");
232  static PlAtom ATOM_time("time");
233  static PlAtom ATOM_id("id");
234  static PlAtom ATOM_regex("regex");
235  static PlAtom ATOM_double("double");
236  static PlAtom ATOM_bool("bool");
237  static PlAtom ATOM_string("string");
238  static PlAtom ATOM_integer("integer");
239  static PlAtom ATOM_int("int");
240  static PlAtom ATOM_true("true");
241  static PlAtom ATOM_false("false");
242 
243  const PlAtom type_atom(term.name());
244  const PlTerm &pl_value = term[1];
245  if(type_atom == ATOM_string) {
246  BSON_APPEND_UTF8(doc, key, (char*)pl_value);
247  }
248  else if(type_atom == ATOM_id) {
249  bson_oid_t oid;
250  bson_oid_init_from_string(&oid, (char*)pl_value);
251  BSON_APPEND_OID(doc,key,&oid);
252  }
253  else if(type_atom == ATOM_double) {
254  // NOTE: must use canonical form for "Infinity"
255  bson_decimal128_t dec;
256  bson_decimal128_from_string ((char*)pl_value, &dec);
257  BSON_APPEND_DECIMAL128(doc,key,&dec);
258  }
259  else if(type_atom == ATOM_time) {
260  BSON_APPEND_DATE_TIME(doc,key,(unsigned long long)(1000.0*((double)pl_value)));
261  }
262  else if(type_atom == ATOM_int || type_atom == ATOM_integer) {
263  BSON_APPEND_INT32(doc,key,(int)pl_value);
264  }
265  else if(type_atom == ATOM_bool) {
266  if(pl_value == ATOM_true) {
267  BSON_APPEND_BOOL(doc,key,true);
268  }
269  else if(pl_value == ATOM_false) {
270  BSON_APPEND_BOOL(doc,key,false);
271  }
272  else {
273  BSON_APPEND_BOOL(doc,key,(bool)((int)pl_value));
274  }
275  }
276  else if(type_atom == ATOM_regex) {
277  BSON_APPEND_REGEX(doc,key,(char*)pl_value,"msi");
278  }
279  else if(type_atom == ATOM_array) {
280  PlTail pl_list(pl_value);
281  PlTerm pl_member;
282  bson_t bson_array;
283  int counter=0;
284  bool status=true;
285 
286  BSON_APPEND_ARRAY_BEGIN(doc, key, &bson_array);
287  while(pl_list.next(pl_member)) {
288  const char *index_key;
289  char index_str[16];
290  bson_uint32_to_string(counter, &index_key, index_str, sizeof index_str);
291  if(!bsonpl_append(&bson_array,index_key,pl_member,err)) {
292  status=false;
293  break;
294  }
295  counter+=1;
296  }
297  bson_append_array_end(doc, &bson_array);
298  return status;
299  }
300  else {
301  bson_set_error(err,
302  MONGOC_ERROR_BSON,
303  MONGOC_ERROR_BSON_INVALID,
304  "invalid type: %s", (char*)term
305  );
306  return false;
307  }
308  return true;
309 }
310 
311 bool bsonpl_append(bson_t *doc, const char *key, const PlTerm &term, bson_error_t *err) { // NOLINT(misc-no-recursion)
312  if(PL_is_list((term_t)term)) {
313  // append a document if term is a list
314  bson_t nested_doc;
315  BSON_APPEND_DOCUMENT_BEGIN(doc, key, &nested_doc);
316  if(bsonpl_concat(&nested_doc, term, err)) {
317  bson_append_document_end(doc, &nested_doc);
318  return true;
319  }
320  else {
321  return false;
322  }
323  }
324  else if(std::string_view(term.name()) == "bson_t") {
325  // the term contains a sub-pipeline encoded as bson_t* object which can be concatenated
326  // with the pipeline under construction
327  if(term.arity()!=1) {
328  bson_set_error(err,
329  MONGOC_ERROR_BSON,
330  MONGOC_ERROR_BSON_INVALID,
331  "invalid bson_t term -- arity should be 1: %s", (char*)term
332  );
333  return false;
334  }
335  const PlTerm &bson_value = term[1];
336  auto *sub_bson = (bson_t*)bson_value.operator void *();
337  if(sub_bson) {
338  bool success = bson_concat(doc, sub_bson);
339  bson_destroy(sub_bson);
340  return success;
341  }
342  else {
343  bson_set_error(err,
344  MONGOC_ERROR_BSON,
345  MONGOC_ERROR_BSON_INVALID,
346  "invalid bson_t term -- void pointer is null: %s", (char*)term
347  );
348  return false;
349  }
350  }
351  else {
352  // append typed value otherwise
353  return bsonpl_append_typed(doc,key,term,err);
354  }
355 }
356 
357 bool bsonpl_concat(bson_t *doc, const PlTerm &term, bson_error_t *err) { // NOLINT(misc-no-recursion)
358  // the term given to bson_from_term must be a list
359  PlTail list(term);
360  PlTerm member;
361  if(!list.next(member)) {
362  // the term is a list []
363  return true;
364  }
365  else if(PL_is_list((term_t)member)) {
366  // the term is a list [[..]|_]
367  do {
368  if(!bsonpl_concat(doc,member,err)) {
369  return false;
370  }
371  } while(list.next(member));
372  return true;
373  }
374  else {
375  // the term is a list [key,value]
376  const char *key = (char*)member;
377  list.next(member);
378  return bsonpl_append(doc,key,member,err);
379  }
380 }
PlTerm bson_to_term(const bson_t *doc)
Definition: bson_pl.cpp:210
#define APPEND_BSON_PL_PAIR(list, key, value, type)
Definition: bson_pl.cpp:11
bool bsonpl_concat(bson_t *doc, const PlTerm &term, bson_error_t *err)
Definition: bson_pl.cpp:357
bool bsonpl_append(bson_t *doc, const char *key, const PlTerm &term, bson_error_t *err)
Definition: bson_pl.cpp:311
TermRule & string()
Definition: terms.cpp:63
TermRule & term()
Definition: terms.cpp:136