libutil++  1.9.3
 All Classes Functions Variables
Object.cpp
1 /*
2 ** libutil++
3 ** $Id: Object.cpp 1702 2016-08-13 23:26:06Z sella $
4 ** Copyright (c) 2011-2016 Digital Genesis, LLC. All Rights Reserved.
5 ** Released under the LGPL Version 2.1 License.
6 ** http://www.digitalgenesis.com
7 */
8 
9 static const char rcsid[] __attribute__((used)) = "$Id: Object.cpp 1702 2016-08-13 23:26:06Z sella $";
10 
11 #include "Object.h"
12 #include "Variant.h"
13 #include "../util/String.h"
14 #include "../util/CommonMacro.h"
15 
16 #include <stack>
17 
18 using namespace sella::variant;
19 
20 Object::Object() throw () : Nullable(std::make_shared<std::map<std::string, Variant>>()) { }
21 
22 Object::Object(const std::string &value) throw (TypeCastException) : Nullable() {
23  if (value.empty()) {
24  this->value = std::make_shared<std::map<std::string, Variant>>();
25  } else {
26  std::vector<std::string> token = this->tokenize(value, Object::Pattern());
27 
28  parse(token);
29  }
30 
31  this->it = std::static_pointer_cast<std::map<std::string, Variant>>(this->value)->begin();
32 }
33 
34 Object::Object(const std::vector<std::string> &token) throw (TypeCastException) : Nullable() {
35  if (token.empty()) {
36  this->value = std::make_shared<std::map<std::string, Variant>>();
37  } else {
38  parse(token);
39  }
40 
41  this->it = std::static_pointer_cast<std::map<std::string, Variant>>(this->value)->begin();
42 }
43 
44 Object::Object(sella::util::MessagePack &mpack, uint32_t &offset) throw (TypeCastException) : Nullable() {
45  unmsgpack(mpack, offset);
46  this->it = std::static_pointer_cast<std::map<std::string, Variant>>(this->value)->begin();
47 }
48 
49 Object::Object(const std::map<std::string, Variant> &value) throw () : Nullable() {
50  this->value = std::make_shared<std::map<std::string, Variant>>(value);
51  this->it = std::static_pointer_cast<std::map<std::string, Variant>>(this->value)->begin();
52 }
53 
54 Object::Object(std::map<std::string, Variant> &&value) throw () : Nullable() {
55  this->value = std::make_shared<std::map<std::string, Variant>>(std::move(value));
56  this->it = std::static_pointer_cast<std::map<std::string, Variant>>(this->value)->begin();
57 }
58 
59 Object::Object(const Object &other) throw () : Nullable() {
60  if (LIKELY(this != &other)) {
61  if (LIKELY(!other.isNull())) {
62  if (UNLIKELY(isNull())) {
63  this->value = std::make_shared<std::map<std::string, Variant>>(other.get());
64  } else {
65  *std::static_pointer_cast<std::map<std::string, Variant>>(this->value) = *std::static_pointer_cast<std::map<std::string, Variant>>(other.value);
66  }
67  } else {
68  this->value.reset();
69  }
70  this->it = other.it;
71  }
72 }
73 
74 Object::Object(Object &&other) throw () : Nullable() {
75  if (LIKELY(!other.isNull())) {
76  this->value = std::move(other.value);
77  this->it = std::move(other.it);
78  } else {
79  this->value.reset();
80  }
81 }
82 
83 Object::~Object() throw () { }
84 
85 std::map<std::string, Variant>::iterator Object::begin() throw (UnsupportedOperatorException) {
86  return get().begin();
87 }
88 
89 std::map<std::string, Variant>::iterator Object::end() throw (UnsupportedOperatorException) {
90  return get().end();
91 }
92 
93 std::map<std::string, Variant>::const_iterator Object::cbegin() const throw (UnsupportedOperatorException) {
94  return get().cbegin();
95 }
96 
97 std::map<std::string, Variant>::const_iterator Object::cend() const throw (UnsupportedOperatorException) {
98  return get().cend();
99 }
100 
101 bool Object::operator==(const Object &other) const throw () {
102  return (*std::static_pointer_cast<std::map<std::string, Variant>>(this->value) == *std::static_pointer_cast<std::map<std::string, Variant>>(other.value));
103 }
104 
105 bool Object::operator!=(const Object &other) const throw () {
106  return !(*this == other);
107 }
108 
109 Object& Object::operator=(const Object &other) throw () {
110  if (LIKELY(this != &other)) {
111  if (LIKELY(!other.isNull())) {
112  if (UNLIKELY(isNull())) {
113  this->value = std::make_shared<std::map<std::string, Variant>>(other.get());
114  } else {
115  *std::static_pointer_cast<std::map<std::string, Variant>>(this->value) = *std::static_pointer_cast<std::map<std::string, Variant>>(other.value);
116  }
117  } else {
118  this->value.reset();
119  }
120  this->it = other.it;
121  }
122 
123  return *this;
124 }
125 
126 Object& Object::operator=(Object &&other) throw () {
127  if (LIKELY(this != &other)) {
128  if (LIKELY(!other.isNull())) {
129  this->value = std::move(other.value);
130  this->it = std::move(other.it);
131  } else {
132  this->value.reset();
133  }
134  }
135 
136  return *this;
137 }
138 
139 Object& Object::operator+=(const Variant &other) throw () {
140  if (UNLIKELY(isNull())) {
141  this->value = std::make_shared<std::map<std::string, Variant>>();
142  }
143 
144  if (UNLIKELY(this == &(other.get<Object>()))) {
145  return *this;
146  }
147 
148  auto &o = *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
149  auto &o2 = other.get<Object>().get();
150 
151  o.insert(o2.begin(), o2.end());
152 
153  return *this;
154 }
155 
156 Object& Object::operator+=(Variant &&other) throw () {
157  if (UNLIKELY(isNull())) {
158  this->value = std::make_shared<std::map<std::string, Variant>>();
159  }
160 
161  if (UNLIKELY(this == &(other.get<Object>()))) {
162  return *this;
163  }
164 
165  auto &o = *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
166  auto &o2 = other.get<Object>().get();
167 
168  //o.insert(o2.begin(), o2.end());
169  std::move(o2.begin(), o2.end(), std::inserter(o, o.begin()));
170 
171  return *this;
172 }
173 
174 Variant& Object::operator[](const std::string &key) throw () {
175  if (UNLIKELY(isNull())) {
176  this->value = std::make_shared<std::map<std::string, Variant>>();
177  }
178 
179  auto &o = *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
180 
181  return o[key];
182 }
183 
184 Variant& Object::operator[](std::string &&key) throw () {
185  if (UNLIKELY(isNull())) {
186  this->value = std::make_shared<std::map<std::string, Variant>>();
187  }
188 
189  auto &o = *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
190 
191  return o[std::move(key)];
192 }
193 
194 const std::map<std::string, Variant>& Object::get() const throw (UnsupportedOperatorException) {
195  if (UNLIKELY(isNull())) {
196  THROW(UnsupportedOperatorException, "unable to take reference of null value");
197  }
198 
199  return *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
200 }
201 
202 std::map<std::string, Variant>& Object::get() throw (UnsupportedOperatorException) {
203  if (UNLIKELY(isNull())) {
204  THROW(UnsupportedOperatorException, "unable to take reference of null value");
205  }
206 
207  return *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
208 }
209 
210 size_t Object::size() const throw () {
211  if (UNLIKELY(isNull())) {
212  return 0;
213  }
214 
215  return get().size();
216 }
217 
218 bool Object::empty() const throw () {
219  if (UNLIKELY(isNull())) {
220  return true;
221  }
222 
223  return get().empty();
224 }
225 
226 void Object::clear() throw() {
227  if (UNLIKELY(isNull())) {
228  this->value = std::make_shared<std::map<std::string, Variant>>();
229  } else {
230  std::static_pointer_cast<std::map<std::string, Variant>>(this->value)->clear();
231  }
232 }
233 
234 bool Object::erase(const std::string &value) throw () {
235  auto &o = *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
236 
237  it = o.begin();
238 
239  return (o.erase(value) > 0);
240 }
241 
242 std::string Object::str(int type) const throw () {
243  std::string output;
244 
245  switch (type) {
246  case JSON:
247  output = json();
248  break;
249 
250  case CJSON:
251  output = cjson();
252  break;
253 
254  case XML:
255  output = xml();
256  break;
257 
258  case CSV:
259  output = csv();
260  break;
261  }
262 
263  return output;
264 }
265 
266 #if 1
267 void Object::msgpack(sella::util::MessagePack &mpack, uint32_t &offset) const throw (TypeCastException) {
268  uint32_t c;
269 
270  if (UNLIKELY(isNull())) {
271  c = mpack.append(offset);
272 
273  offset += c;
274 
275  if (UNLIKELY(c == 0)) {
276  THROW(TypeCastException, "Failed to append object '%s' at offset %d (buffer length: %d)", str(JSON).c_str(), offset, mpack.length());
277  }
278  } else {
279  const std::map<std::string, Variant> &data = *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
280 
281  c = mpack.append_object(offset, data.size());
282 
283  offset += c;
284 
285  if (UNLIKELY(c == 0)) {
286  THROW(TypeCastException, "Failed to append object '%s' at offset %d (buffer length: %d)", str(JSON).c_str(), offset, mpack.length());
287  }
288 
289  for (auto it = data.begin(), end = data.end(); it != end; ++it) {
290  auto nx = std::next(it);
291 
292  if (nx != end) {
293  PREFETCH(&nx->second.value, PREFETCH_RO, PREFETCH_LOCALITY_MEDIUM);
294  }
295 
296  c = mpack.append(offset, it->first);
297 
298  offset += c;
299 
300  if (UNLIKELY(c == 0)) {
301  THROW(TypeCastException, "Failed to append object '%s' at offset %d (buffer length: %d)", str(JSON).c_str(), offset, mpack.length());
302  }
303 
304  it->second.value->msgpack(mpack, offset);
305  }
306  }
307 }
308 #else
309 uint32_t Object::msgpack(sella::util::MessagePack &mpack, uint32_t &offset) const throw (TypeCastException) {
310  uint32_t c, o = offset;
311 
312  if (UNLIKELY(isNull())) {
313  c = mpack.append(offset);
314 
315  offset += c;
316 
317  return c;
318  }
319 
320  const std::map<std::string, Variant> &data = *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
321 
322  if (UNLIKELY((c = mpack.append_object(offset, data.size())) == 0)) {
323  THROW(TypeCastException, "Failed to append '%s' at offset %d (buffer length: %d)", str(JSON).c_str(), offset, mpack.length());
324  }
325 
326  offset += c;
327 
328  for (auto it = data.begin(), end = data.end(); it != end; ++it) {
329  if (UNLIKELY((c = mpack.append(offset, it->first)) == 0)) {
330  THROW(TypeCastException, "Failed to append '%s' at offset %d (buffer length: %d)", str(JSON).c_str(), offset, mpack.length());
331  }
332 
333  offset += c;
334 
335  if (UNLIKELY(it->second.isNull())) {
336  c = mpack.append(offset);
337 
338  offset += c;
339  } else {
340  int type = it->second.getType();
341 
342  switch (type) {
343  case Boolean::Type:
344  c = mpack.append(offset, it->second.get<Boolean>().get());
345  break;
346 
347  case Signed::Type:
348  c = mpack.append(offset, it->second.get<Signed>().get());
349  break;
350 
351  case Unsigned::Type:
352  c = mpack.append(offset, it->second.get<Unsigned>().get());
353  break;
354 
355  case Double::Type:
356  c = mpack.append(offset, it->second.get<Double>().get());
357  break;
358 
359  case UUID::Type:
360  c = mpack.append(offset, it->second.get<UUID>().get().str());
361  break;
362 
363  case String::Type:
364  c = mpack.append(offset, it->second.get<String>().get());
365  break;
366 
367  case Binary::Type:
368  c = it->second.get<Binary>().msgpack(mpack, offset);
369  break;
370 
371  case Pointer::Type:
372  c = mpack.append(offset, (uint64_t) it->second.get<Pointer>().get());
373  break;
374 
375  case IPAddress::Type:
376  c = mpack.append(offset, it->second.get<IPAddress>().get().str());
377  break;
378 
379  case IPPrefix::Type:
380  c = mpack.append(offset, it->second.get<IPPrefix>().get().str());
381  break;
382 
383  case Interval::Type:
384  c = mpack.append(offset, it->second.get<Interval>().str(JSON));
385  break;
386 
387  case Time::Type:
388  c = mpack.append(offset, it->second.get<Time>().str(JSON));
389  break;
390 
391  case Date::Type:
392  c = mpack.append(offset, it->second.get<Date>().str(JSON));
393  break;
394 
395  case TimeStamp::Type:
396  c = mpack.append(offset, it->second.get<TimeStamp>().str(JSON));
397  break;
398 
399  case Vector::Type:
400  c = it->second.get<Vector>().msgpack(mpack, offset);
401  break;
402 
403  case Object::Type:
404  c = it->second.get<Object>().msgpack(mpack, offset);
405  break;
406  }
407 
408  if (type != Object::Type && type != Vector::Type && type != Binary::Type) {
409  offset += c;
410  }
411  }
412 
413  if (UNLIKELY(c == 0)) {
414  THROW(TypeCastException, "Failed to append '%s' at offset %d (buffer length: %d)", str(JSON).c_str(), offset, mpack.length());
415  }
416  }
417 
418  return (offset - o);
419 }
420 #endif
421 
422 void Object::msgpackBufferSize(size_t &size) const throw () {
423  if (UNLIKELY(isNull())) {
424  size += 1;
425 
426  return;
427  }
428 
429  const std::map<std::string, Variant> &data = *std::static_pointer_cast<std::map<std::string, Variant>>(this->value);
430 
431  for (auto it = data.begin(), end = data.end(); it != end; ++it) {
432  size += 5 + it->first.size();
433  it->second.value->msgpackBufferSize(size);
434  }
435 
436  size += 5;
437 }
438 
439 std::string Object::json() const throw () {
440  if (UNLIKELY(isNull())) {
441  return "null";
442  }
443 
444  std::string output;
445  const std::map<std::string, Variant> &data = this->get();
446 
447  if (data.empty()) {
448  output = "{ }";
449  } else {
450  output = "{ ";
451 
452  for (auto it = data.begin(), end = data.end(); it != end; ++it) {
453  if (it->second.isNull()) {
454  output.append("\"").append(it->first).append("\": ").append("null, ");
455  } else if ((!it->second.isScalar() && !it->second.isNetwork()) || it->second.isNumeric() || it->second.isBoolean()) {
456  output.append("\"").append(it->first).append("\": ").append(it->second.value->str(JSON)).append(", ");
457  } else {
458  output.append("\"").append(it->first).append("\": \"").append(it->second.value->str(JSON)).append("\", ");
459  }
460  }
461 
462  output.erase(output.size() - 2);
463  output.append(" }");
464  }
465 
466  return output;
467 }
468 
469 std::string Object::cjson() const throw () {
470  if (UNLIKELY(isNull())) {
471  return "null";
472  }
473 
474  std::string output;
475  const std::map<std::string, Variant> &data = this->get();
476 
477  if (data.empty()) {
478  output = "{}";
479  } else {
480  output = "{";
481 
482  for (auto it = data.begin(), end = data.end(); it != end; ++it) {
483  if (it->second.isNull()) {
484  output.append("\"").append(it->first).append("\":").append("null,");
485  } else if ((!it->second.isScalar() && !it->second.isNetwork()) || it->second.isNumeric() || it->second.isBoolean()) {
486  output.append("\"").append(it->first).append("\":").append(it->second.value->str(CJSON)).push_back(',');
487  } else {
488  output.append("\"").append(it->first).append("\":\"").append(it->second.value->str(CJSON)).append("\",");
489  }
490  }
491 
492  output.erase(output.size() - 1);
493  output.push_back('}');
494  }
495 
496  return output;
497 }
498 
499 std::string Object::xml() const throw () {
500  if (UNLIKELY(isNull())) {
501  return "<root null=\"true\" />";
502  }
503 
504  std::string output;
505  const std::map<std::string, Variant> &data = this->get();
506 
507  for (auto it = data.begin(), end = data.end(); it != end; ++it) {
508  if (it->second.isNull()) {
509  output.append("<").append(it->first).append(" null=\"true\" />");
510  } else if (it->second.isObject() && it->second.empty()) {
511  output.append("<").append(it->first).append(" />");
512  } else {
513  output.append("<").append(it->first).append(">").append(it->second.value->str(XML)).append("</").append(it->first).append(">");
514  }
515  }
516 
517  return output;
518 }
519 
520 std::string Object::csv() const throw () {
521  if (UNLIKELY(isNull())) {
522  return "\\N";
523  }
524 
525  std::string output;
526  const std::map<std::string, Variant> &data = this->get();
527 
528  if (!data.empty()) {
529  for (auto it = data.begin(), end = data.end(); it != end; ++it) {
530  if (it->second.isNull()) {
531  output.append("\\N,");
532  } else if (it->second.isScalar()) {
533  output.append("\"").append(sella::util::String::escape((it->second.value->str(CSV)))).append("\",");
534  } else {
535  output.append("\"").append(sella::util::String::escape(it->second.value->str(JSON))).append("\",");
536  }
537  }
538 
539  output.erase(output.size() - 1);
540  }
541 
542  return output;
543 }
544 
545 void Object::parse(const std::vector<std::string> &token) throw (TypeCastException) {
546  auto t = token.begin();
547  std::string label;
548  std::stack<int> type;
549  std::shared_ptr<std::map<std::string, Variant>> m = std::make_shared<std::map<std::string, Variant>>();
550 
551  for (auto it = token.begin(), end = token.end(); it != end; ++it) {
552  switch (it->at(0)) {
553  case '[':
554  if (type.empty()) {
555  t = it;
556  }
557  type.push('v');
558  break;
559 
560  case '{':
561  if (type.empty()) {
562  t = it;
563  }
564  type.push('o');
565  break;
566 
567  case ']':
568  if (UNLIKELY(type.empty() || (type.top() != 'v'))) {
569  THROW(TypeCastException, "mismatched vector [ ... ] in text (%s)", t->c_str());
570  }
571  type.pop();
572 
573  if (type.empty()) {
574  m->insert(std::make_pair(label, Variant(std::vector<std::string>(t, it))));
575  label.clear();
576  }
577  break;
578 
579  case '}':
580  if (UNLIKELY(type.empty() || (type.top() != 'o'))) {
581  THROW(TypeCastException, "mismatched object { ... } in text (%s)", t->c_str());
582  }
583  type.pop();
584 
585  if (type.empty()) {
586  m->insert(std::make_pair(label, Variant(std::vector<std::string>(t, it))));
587  label.clear();
588  }
589  break;
590 
591  case ':':
592  case ',':
593  break;
594 
595  case '"':
596  if (type.empty()) {
597  if (label.empty()) {
598  label = std::string(it->begin() + 1, it->end() - 1);
599  } else {
600  std::string value(it->begin() + 1, it->end() - 1);
601  m->insert(std::make_pair(label, Variant(value, Variant::TypeQuoted)));
602  label.clear();
603  }
604  }
605  break;
606 
607  default:
608  if (type.empty()) {
609  m->insert(std::make_pair(label, Variant(*it, Variant::TypeNonQuoted)));
610  label.clear();
611  }
612  }
613  }
614 
615  this->value = m;
616 }
617 
618 void Object::unmsgpack(sella::util::MessagePack &mpack, uint32_t &offset) throw (TypeCastException) {
619  std::shared_ptr<std::map<std::string, Variant>> m = std::make_shared<std::map<std::string, Variant>>();
620 
621  union {
622  bool b;
623  int64_t s;
624  uint64_t u;
625  double d;
626  } u;
627 
628  try {
629  mpack.scan(offset, [this, &mpack, &m, &u](uint32_t i, uint32_t value) {
630  std::string key;
631 
632  if (UNLIKELY(!mpack.decode(key, i))) {
633  THROW(TypeCastException, "Failed to decode object key");
634  }
635 
636  switch (mpack.typeof(value)) {
637  case sella::util::MessagePack::TYPE_NULL:
638  m->insert(std::make_pair(std::move(key), std::move(Variant())));
639  break;
640 
641  case sella::util::MessagePack::TYPE_BOOLEAN:
642  if (LIKELY(mpack.decode(u.b, value))) {
643  m->insert(std::make_pair(std::move(key), std::move(Variant(u.b))));
644  } else {
645  THROW(TypeCastException, "Failed to decode boolean");
646  }
647  break;
648 
649  case sella::util::MessagePack::TYPE_SIGNED:
650  if (LIKELY(mpack.decode(u.s, value))) {
651  m->insert(std::make_pair(std::move(key), std::move(Variant(u.s))));
652  } else {
653  THROW(TypeCastException, "Failed to decode signed");
654  }
655  break;
656 
657  case sella::util::MessagePack::TYPE_UNSIGNED:
658  if (LIKELY(mpack.decode(u.u, value))) {
659  m->insert(std::make_pair(std::move(key), std::move(Variant(u.u))));
660  } else {
661  THROW(TypeCastException, "Failed to decode unsigned");
662  }
663  break;
664 
665  case sella::util::MessagePack::TYPE_FLOAT:
666  if (LIKELY(mpack.decode(u.d, value))) {
667  m->insert(std::make_pair(std::move(key), std::move(Variant(u.d))));
668  } else {
669  THROW(TypeCastException, "Failed to decode double");
670  }
671  break;
672 
673  case sella::util::MessagePack::TYPE_STRING: {
674  std::string str;
675 
676  if (LIKELY(mpack.decode(str, value))) {
677  m->insert(std::make_pair(std::move(key), std::move(Variant(std::move(str)))));
678  } else {
679  THROW(TypeCastException, "Failed to decode string");
680  }
681  break;
682  }
683 
684  case sella::util::MessagePack::TYPE_VECTOR:
685  m->insert(std::make_pair(std::move(key), std::move(Variant(std::move(std::make_shared<Vector>(mpack, value))))));
686  break;
687 
688  case sella::util::MessagePack::TYPE_OBJECT:
689  m->insert(std::make_pair(std::move(key), std::move(Variant(std::move(std::make_shared<Object>(mpack, value))))));
690  break;
691 
692  default:
693  THROW(TypeCastException, "Unsupported type %u in input", mpack.typeof(value));
694  }
695 
696  return true;
697  });
698  } catch (sella::util::MemoryException &e) {
699  RETHROW(TypeCastException, e);
700  } catch (TypeCastException &e) {
701  RETHROW(TypeCastException, e);
702  } catch (std::exception &e) {
703  THROW(TypeCastException, "%s", e.what());
704  }
705 
706  this->value = m;
707 }
708 
709 bool Object::isType(const char *value, size_t size) throw () {
710  return (size > 1 && value[0] == '{' && value[size - 1] == '}');
711 }
712 
713 /*
714 ** vim: noet ts=3 sw=3
715 */