YARP
Yet Another Robot Platform
 
Loading...
Searching...
No Matches
BottleImpl.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3 * SPDX-FileCopyrightText: 2006-2010 RobotCub Consortium
4 * SPDX-FileCopyrightText: 2006, 2008 Arjan Gijsberts
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
9
10#include <yarp/conf/numeric.h>
11
17
18#include <limits>
19
21using yarp::os::Bytes;
25using yarp::os::Value;
28
29namespace {
30YARP_OS_LOG_COMPONENT(BOTTLEIMPL, "yarp.os.impl.BottleImpl")
31} // namespace
32
34 parent(nullptr),
35 invalid(false),
36 ro(false),
37 speciality(0),
38 nested(false),
39 dirty(true)
40{
41}
42
44 parent(parent),
45 invalid(false),
46 ro(false),
47 speciality(0),
48 nested(false),
49 dirty(true)
50{
51}
52
53
58
59
60void BottleImpl::add(Storable* s)
61{
62 content.push_back(s);
63 dirty = true;
64}
65
66
68{
69 for (auto& i : content) {
70 delete i;
71 }
72 content.clear();
73 dirty = true;
74}
75
76void BottleImpl::smartAdd(const std::string& str)
77{
78 if (str.length() > 0) {
79 char ch = str[0];
80 Storable* s = nullptr;
81 StoreString* ss = nullptr;
82 bool numberLike = true;
83 bool preamble = true;
84 bool hexActive = false;
85 size_t hexStart = 0;
86 int periodCount = 0;
87 int signCount = 0;
88 bool hasPeriodOrE = false;
89 for (size_t i = 0; i < str.length(); i++) {
90 if (str == "inf" || str == "-inf" || str == "nan") {
91 hasPeriodOrE = true;
92 break;
93 }
94 char ch2 = str[i];
95 if (ch2 == '.') {
96 hasPeriodOrE = true;
98 if (periodCount > 1) {
99 numberLike = false;
100 }
101 }
102 if (!hexActive && (ch2 == 'e' || ch2 == 'E')) {
103 hasPeriodOrE = true;
104 }
105 if (preamble) {
106 if (ch2 == 'x' || ch2 == 'X') {
107 hexActive = true;
108 hexStart = i;
109 continue;
110 }
111 }
112 if (preamble) {
113 if (ch2 == '0' || ch2 == '+' || ch2 == '-') {
114 if (ch2 == '+' || ch2 == '-') {
115 signCount++;
116 if (signCount > 1) {
117 numberLike = false;
118 }
119 }
120 continue;
121 }
122 }
123 preamble = false;
124 if (!((ch2 >= '0' && ch2 <= '9') || ch2 == '.' || ch2 == 'e' ||
125 ch2 == 'E' || ch2 == '+' || ch2 == '-' ||
126 (hexActive && ((ch2 >= 'a' && ch2 <= 'f') ||
127 (ch2 >= 'A' && ch2 <= 'F'))))) {
128 numberLike = false;
129 break;
130 }
131 }
132 if (hexActive) {
133 if (static_cast<int>(str.length()) - (hexStart + 1) > 8) {
134 // we can only deal with 32bit hexadecimal
135 numberLike = false;
136 }
137 }
138
139 if (numberLike &&
140 ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.' || ch == 'i' /* inf */ || ch == 'n' /* nan */) &&
141 (ch != '.' || str.length() > 1)) {
142 if (!hasPeriodOrE) {
143 s = new StoreInt64(0);
144 } else {
145 s = new StoreFloat64(0);
146 }
147 } else if (ch == '(') {
148 s = new StoreList();
149 } else if (ch == '[') {
150 if (str.length() <=6) //6 is 4 + `[` + `]`
151 {s = new StoreVocab32();}
152 else
153 {s = new StoreVocab64();}
154 } else if (ch == '{') {
155 s = new StoreBlob();
156 } else {
157 s = ss = new StoreString("");
158 }
159 if (s != nullptr) {
160 s->fromStringNested(str);
161
162 // Traditionally all int are read as 32 bit integers, but 64 bit
163 // integers will not fit.
164 // Therefore the value is read as 64 bit, but if the value would fit
165 // a 32 bit integer, it is cast to a 32 bit integer.
166 if (s->isInt64()
167 && s->asInt64() >= std::numeric_limits<int32_t>::min()
168 && s->asInt64() <= std::numeric_limits<int32_t>::max()) {
169 Storable* s_i32 = new StoreInt32(s->asInt32());
170 delete s;
171 s = s_i32;
172 s_i32 = nullptr;
173 }
174
175 if (ss != nullptr) {
176 if (str.length() == 0 || str[0] != '\"') {
177 std::string val = ss->asString();
178 if (val == "true") {
179 delete s;
180 s = new StoreVocab32(static_cast<int>('1'));
181 } else if (val == "false") {
182 delete s;
183 s = new StoreVocab32(0);
184 }
185 }
186 }
187 add(s);
188 }
189 ss = nullptr;
190 }
191}
192
193void BottleImpl::fromString(const std::string& line)
194{
195 clear();
196 dirty = true;
197 std::string arg;
198 bool quoted = false;
199 bool back = false;
200 bool begun = false;
201 int nested = 0;
202 int nestedAlt = 0;
203 std::string nline = line + " ";
204
205 for (char ch : nline) {
206 if (back) {
207 arg += ch;
208 back = false;
209 } else {
210 if (!begun) {
211 if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r') {
212 begun = true;
213 }
214 }
215 if (begun) {
216 if (ch == '\"') {
217 quoted = !quoted;
218 }
219 if (!quoted) {
220 if (ch == '(') {
221 nested++;
222 }
223 if (ch == ')') {
224 nested--;
225 }
226 if (ch == '{') {
227 nestedAlt++;
228 }
229 if (ch == '}') {
230 nestedAlt--;
231 }
232 }
233 if (ch == '\\') {
234 back = true;
235 arg += ch;
236 } else {
237 if ((!quoted) && (ch == ',' || ch == ' ' || ch == '\t' ||
238 ch == '\n' || ch == '\r') &&
239 (nestedAlt == 0) && (nested == 0)) {
240 if (!arg.empty()) {
241 if (arg == "null") {
242 add(new StoreVocab32(yarp::os::createVocab32('n', 'u', 'l', 'l')));
243 } else {
244 smartAdd(arg);
245 }
246 }
247 arg = "";
248 begun = false;
249 } else {
250 arg += ch;
251 }
252 }
253 }
254 }
255 }
256}
257
258bool BottleImpl::isComplete(const char* txt)
259{
260 bool quoted = false;
261 bool back = false;
262 bool begun = false;
263 int nested = 0;
264 int nestedAlt = 0;
265 std::string nline = txt;
266 nline += " ";
267
268 for (char ch : nline) {
269 if (back) {
270 back = false;
271 } else {
272 if (!begun) {
273 if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r') {
274 begun = true;
275 }
276 }
277 if (begun) {
278 if (ch == '\"') {
279 quoted = !quoted;
280 }
281 if (!quoted) {
282 if (ch == '(') {
283 nested++;
284 }
285 if (ch == ')') {
286 nested--;
287 }
288 if (ch == '{') {
289 nestedAlt++;
290 }
291 if (ch == '}') {
292 nestedAlt--;
293 }
294 }
295 if (ch == '\\') {
296 back = true;
297 // arg += ch;
298 } else {
299 if ((!quoted) &&
300 (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') &&
301 (nestedAlt == 0) && (nested == 0)) {
302 // smartAdd(arg);
303 begun = false;
304 } else {
305 // arg += ch;
306 }
307 }
308 }
309 }
310 }
311 return nested == 0 && nestedAlt == 0 && !quoted;
312}
313
314
315std::string BottleImpl::toString() const
316{
317 std::string result;
318 for (unsigned int i = 0; i < content.size(); i++) {
319 if (i > 0) {
320 result += " ";
321 }
322 Storable& s = *content[i];
323 result += s.toStringNested();
324 }
325 return result;
326}
327
329{
330 return content.size();
331}
332
333
335{
336 if (reader.isError()) {
337 return false;
338 }
339 std::int32_t id = speciality;
340 yCTrace(BOTTLEIMPL, "READING, nest flag is %d", nested);
341 if (id == 0) {
342 id = reader.expectInt32();
343 yCTrace(BOTTLEIMPL, "READ subcode %" PRId32, id);
344 } else {
345 yCTrace(BOTTLEIMPL, "READ skipped subcode %" PRId32, speciality);
346 }
348 if (storable == nullptr) {
349 yCError(BOTTLEIMPL, "Reader failed, unrecognized object code %" PRId32, id);
350 return false;
351 }
352 storable->readRaw(reader);
353 add(storable);
354 return true;
355}
356
357
358void BottleImpl::fromBinary(const char* text, size_t len)
359{
360 std::string wrapper(text, len);
362 sis.add(wrapper);
364 Route route;
365 reader.reset(sis, nullptr, route, len, false);
366 read(reader);
367}
368
369
371{
372 std::string wrapper(data.get(), data.length());
374 sis.add(wrapper);
376 Route route;
377 reader.reset(sis, nullptr, route, data.length(), false);
378
379 clear();
380 dirty = true; // for clarity
381
382 if (!nested) {
383 clear();
384 specialize(0);
385
386 std::int32_t code = reader.expectInt32();
387 if (reader.isError()) {
388 return false;
389 }
390 yCTrace(BOTTLEIMPL, "READ got top level code %" PRId32, code);
391 code = code & UNIT_MASK;
392 if (code != 0) {
393 specialize(code);
394 }
395 }
396 std::int32_t len = reader.expectInt32();
397 if (reader.isError()) {
398 return false;
399 }
400 yCTrace(BOTTLEIMPL, "READ bottle length %d", len);
401 for (int i = 0; i < len; i++) {
402 bool ok = fromBytes(reader);
403 if (!ok) {
404 return false;
405 }
406 }
407
408 return true;
409}
410
412{
413 synch();
414 yCAssert(BOTTLEIMPL, data.length() == byteCount());
415 memcpy(data.get(), getBytes(), byteCount());
416}
417
418
419const char* BottleImpl::getBytes() const
420{
421 yCTrace(BOTTLEIMPL, "am I nested? %d", nested);
422 synch();
423 return &data[0];
424}
425
427{
428 synch();
429 return data.size();
430}
431
433{
434 synch();
435}
436
438{
439 // could simplify this if knew lengths of blocks up front
440 if (writer.isTextMode()) {
441 writer.appendText(toString());
442 } else {
443 synch();
444 writer.appendBlock(getBytes(), byteCount());
445 }
446 return !writer.isError();
447}
448
449
451{
452 bool result = false;
453
454 if (reader.isTextMode()) {
455 std::string str = reader.expectText();
456 if (reader.isError()) {
457 return false;
458 }
459 bool done = (str.length() <= 0);
460 while (!done) {
461 if (str[str.length() - 1] == '\\') {
462 str = str.substr(0, str.length() - 1);
463 str += reader.expectText();
464 if (reader.isError()) {
465 return false;
466 }
467 } else {
468 if (isComplete(str.c_str())) {
469 done = true;
470 } else {
471 str += "\n";
472 str += reader.expectText();
473 if (reader.isError()) {
474 return false;
475 }
476 }
477 }
478 }
479 fromString(str);
480 result = true;
481 } else {
482 if (!nested) {
483 // no byte length any more to facilitate nesting
484 // reader.expectInt32(); // the bottle byte ct; ignored
485
486 clear();
487 specialize(0);
488
489 std::int32_t code = reader.expectInt32();
490 if (reader.isError()) {
491 return false;
492 }
493 yCTrace(BOTTLEIMPL, "READ got top level code %" PRId32, code);
494 code = code & UNIT_MASK;
495 if (code != 0) {
496 specialize(code);
497 }
498 }
499
500 result = true;
501 clear();
502 dirty = true; // for clarity
503
504 std::int32_t len = 0;
505 len = reader.expectInt32();
506 if (reader.isError()) {
507 return false;
508 }
509 yCTrace(BOTTLEIMPL, "READ got length %d", len);
510 for (int i = 0; i < len; i++) {
511 bool ok = fromBytes(reader);
512 if (!ok) {
513 return false;
514 }
515 }
516 }
517 return result;
518}
519
520void BottleImpl::synch() const
521{
522 const_cast<BottleImpl*>(this)->synch();
523}
524
525void BottleImpl::synch()
526{
527 if (dirty) {
528 if (!nested) {
529 subCode();
530 yCTrace(BOTTLEIMPL, "bottle code %" PRId32, StoreList::code + subCode());
531 }
532 data.clear();
533 BufferedConnectionWriter writer;
534 if (!nested) {
535 writer.appendInt32(StoreList::code + speciality);
536 yCTrace(BOTTLEIMPL, "wrote bottle code %" PRId32, StoreList::code + speciality);
537 }
538 yCTrace(BOTTLEIMPL, "bottle length %zd", size());
539 writer.appendInt32(static_cast<std::int32_t>(size()));
540 for (auto s : content) {
541 if (speciality == 0) {
542 yCTrace(BOTTLEIMPL, "subcode %" PRId32, s->getCode());
543 writer.appendInt32(s->getCode());
544 } else {
545 yCTrace(BOTTLEIMPL, "skipped subcode %" PRId32, s->getCode());
546 yCAssert(BOTTLEIMPL, speciality == s->getCode());
547 }
548 if (s->isList()) {
549 s->asList()->implementation->setNested(true);
550 }
551 s->writeRaw(writer);
552 }
553 data.resize(writer.dataSize(), ' ');
554 MemoryOutputStream m(&data[0]);
555 writer.write(m);
556 dirty = false;
557 }
558}
559
560
561void BottleImpl::specialize(std::int32_t subCode)
562{
563 speciality = subCode;
564}
565
566
568{
569 return speciality;
570}
571
572void BottleImpl::setNested(bool nested)
573{
574 this->nested = nested;
575}
576
577
579{
580 return subCoder(*this);
581}
582
584{
585 return index < size();
586}
587
588bool BottleImpl::isInt8(int index)
589{
590 return (checkIndex(index) ? content[index]->isInt8() : false);
591}
592
593bool BottleImpl::isInt16(int index)
594{
595 return (checkIndex(index) ? content[index]->isInt16() : false);
596}
597
598bool BottleImpl::isInt32(int index)
599{
600 return (checkIndex(index) ? content[index]->isInt32() : false);
601}
602
603bool BottleImpl::isInt64(int index)
604{
605 return (checkIndex(index) ? content[index]->isInt64() : false);
606}
607
609{
610 return (checkIndex(index) ? content[index]->isFloat32() : false);
611}
612
614{
615 return (checkIndex(index) ? content[index]->isFloat64() : false);
616}
617
618bool BottleImpl::isString(int index)
619{
620 return (checkIndex(index) ? content[index]->isString() : false);
621}
622
623bool BottleImpl::isList(int index)
624{
625 return (checkIndex(index) ? content[index]->isList() : false);
626}
627
629{
630 Storable* stb = nullptr;
631 if (size() == 0) {
632 stb = new StoreNull();
633 } else {
634 stb = content[size() - 1];
635 content.pop_back();
636 dirty = true;
637 }
638 yCAssert(BOTTLEIMPL, stb != nullptr);
639 return stb;
640}
641
643{
644 return (checkIndex(index) ? *(content[index]) : getNull());
645}
646
648{
649 auto* lst = new StoreList();
650 add(lst);
651 return lst->internal();
652}
653
655{
656 auto* lst = new StoreDict();
657 add(lst);
658 return lst->internal();
659}
660
662{
663
664 if (len == 0 || alt->size() == 0) {
665 clear();
666 return;
667 }
668
669 // Handle copying to the same object just a subset of the bottle
670 const BottleImpl* src = alt;
671 BottleImpl tmp(nullptr);
672 if (alt == this) {
673 tmp.fromString(toString());
674 src = &tmp;
675 }
676
677 clear();
678
679 const size_t last = src->size() - 1;
680 for (size_t i = 0; (i < len) && (first + i <= last); ++i) {
681 add(src->get(first + i).cloneStorable());
682 }
683}
684
686{
687 if (ro) {
688 yCFatal(BOTTLEIMPL, "Attempted to modify the null bottle");
689 }
690 if (invalid) {
691 invalid = false;
692 }
693}
694
695Value& BottleImpl::findGroupBit(const std::string& key) const
696{
697 for (size_t i = 0; i < size(); i++) {
698 Value* org = &(get(static_cast<int>(i)));
699 Value* cursor = org;
700 if (cursor->isList()) {
701 cursor = &(cursor->asList()->get(0));
702 }
703 if (key == cursor->toString()) {
704 return *org;
705 }
706 }
707 // return invalid object
708 return get(-1);
709}
710
711Value& BottleImpl::findBit(const std::string& key) const
712{
713 for (size_t i = 0; i < size(); i++) {
714 Value* org = &(get(static_cast<int>(i)));
715 Value* cursor = org;
716 bool nested = false;
717 if (cursor->isList()) {
718 Bottle* bot = cursor->asList();
719 cursor = &(bot->get(0));
720 nested = true;
721 }
722 if (key == cursor->toString()) {
723 if (nested) {
724 return org->asList()->get(1);
725 }
726 return get(static_cast<int>(i + 1));
727 }
728 }
729 // return invalid object
730 return get(-1);
731}
#define UNIT_MASK
Definition Storable.h:19
A simple collection of objects that can be described and transmitted in a portable way.
Definition Bottle.h:65
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition Bottle.cpp:252
A mini-server for performing network communication in the background.
A simple abstraction for a block of bytes.
Definition Bytes.h:24
size_t length() const
Definition Bytes.cpp:22
const char * get() const
Definition Bytes.cpp:27
An interface for reading from a network connection.
virtual bool isTextMode() const =0
Check if the connection is text mode.
virtual std::int32_t expectInt32()=0
Read a 32-bit integer from the network connection.
virtual std::string expectText(const char terminatingChar='\n')=0
Read some text from the network connection.
virtual bool isError() const =0
An interface for writing to a network connection.
virtual bool isError() const =0
virtual bool isTextMode() const =0
Check if the connection is text mode.
virtual void appendText(const std::string &str, const char terminate='\n')=0
Send a terminated string to the network connection.
virtual void appendBlock(const char *data, size_t len)=0
Send a block of data to the network connection.
A class for storing options and configuration information.
Definition Property.h:33
Information about a connection between two ports.
Definition Route.h:28
A base class for nested structures that can be searched.
Definition Searchable.h:31
An InputStream that reads from a string.
void add(const std::string &txt)
A single value (typically within a Bottle).
Definition Value.h:44
virtual bool isList() const
Checks if value is a list.
Definition Value.cpp:162
virtual Bottle * asList() const
Get list value.
Definition Value.cpp:252
std::string toString() const override
Return a standard text representation of the content of the object.
Definition Value.cpp:368
virtual std::int32_t getCode() const
Get standard type code of value.
Definition Value.cpp:386
A flexible data format for holding a bunch of numbers and strings.
Definition BottleImpl.h:34
bool fromBytes(const yarp::os::Bytes &data)
Storable & get(size_type index) const
yarp::os::Bottle & addList()
static StoreNull & getNull()
Definition BottleImpl.h:161
const char * getBytes() const
bool write(ConnectionWriter &writer) const
Value & findGroupBit(const std::string &key) const
bool checkIndex(size_type index) const
void toBytes(yarp::os::Bytes &data)
Value & findBit(const std::string &key) const
void copyRange(const BottleImpl *alt, size_type first=0, size_type len=npos)
yarp::os::Property & addDict()
static bool isComplete(const char *txt)
bool read(ConnectionReader &reader)
std::string toString() const
void fromBinary(const char *text, size_t len)
void setNested(bool nested)
void specialize(std::int32_t subCode)
void fromString(const std::string &line)
A single item in a Bottle.
Definition Storable.h:44
virtual std::string toStringNested() const
Create string representation, including any syntax that should wrap it such as braces or parentheses.
Definition Storable.h:271
std::int64_t asInt64() const override
Get 64-bit integer value.
Definition Storable.h:138
bool isList() const override
Checks if value is a list.
Definition Storable.h:173
virtual bool writeRaw(ConnectionWriter &connection) const =0
static Storable * createByCode(std::int32_t id)
Definition Storable.cpp:66
virtual void fromStringNested(const std::string &src)
Initialize from a string representation.
Definition Storable.h:260
virtual Storable * cloneStorable() const
Typed synonym for clone()
Definition Storable.h:59
yarp::os::Bottle * asList() const override
Get list value.
Definition Storable.h:178
bool isInt64() const override
Checks if value is a 64-bit integer.
Definition Storable.h:133
std::int32_t asInt32() const override
Get 32-bit integer value.
Definition Storable.h:128
Key/value pairs.
Definition Storable.h:1201
A nested list of items.
Definition Storable.h:1136
static const std::int32_t code
Definition Storable.h:1163
std::string asString() const override
Get string value.
Definition Storable.h:1054
A 32 bit vocabulary item.
Definition Storable.h:841
Lets Readable objects read from the underlying InputStream associated with the connection between two...
void reset(yarp::os::InputStream &in, TwoWayStream *str, const Route &route, size_t len, bool textMode, bool bareMode=false)
std::int32_t expectInt32() override
Read a 32-bit integer from the network connection.
#define yCError(component,...)
#define yCAssert(component, x)
#define yCTrace(component,...)
#define yCFatal(component,...)
#define YARP_OS_LOG_COMPONENT(name, name_string)
std::int32_t subCoder(T &content)
Definition Storable.h:1260
constexpr yarp::conf::vocab32_t createVocab32(char a, char b=0, char c=0, char d=0)
Create a vocab from chars.
Definition Vocab32.h:27