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 s = new StoreVocab32();
151 } else if (ch == '{') {
152 s = new StoreBlob();
153 } else {
154 s = ss = new StoreString("");
155 }
156 if (s != nullptr) {
157 s->fromStringNested(str);
158
159 // Traditionally all int are read as 32 bit integers, but 64 bit
160 // integers will not fit.
161 // Therefore the value is read as 64 bit, but if the value would fit
162 // a 32 bit integer, it is cast to a 32 bit integer.
163 if (s->isInt64()
164 && s->asInt64() >= std::numeric_limits<int32_t>::min()
165 && s->asInt64() <= std::numeric_limits<int32_t>::max()) {
166 Storable* s_i32 = new StoreInt32(s->asInt32());
167 delete s;
168 s = s_i32;
169 s_i32 = nullptr;
170 }
171
172 if (ss != nullptr) {
173 if (str.length() == 0 || str[0] != '\"') {
174 std::string val = ss->asString();
175 if (val == "true") {
176 delete s;
177 s = new StoreVocab32(static_cast<int>('1'));
178 } else if (val == "false") {
179 delete s;
180 s = new StoreVocab32(0);
181 }
182 }
183 }
184 add(s);
185 }
186 ss = nullptr;
187 }
188}
189
190void BottleImpl::fromString(const std::string& line)
191{
192 clear();
193 dirty = true;
194 std::string arg;
195 bool quoted = false;
196 bool back = false;
197 bool begun = false;
198 int nested = 0;
199 int nestedAlt = 0;
200 std::string nline = line + " ";
201
202 for (char ch : nline) {
203 if (back) {
204 arg += ch;
205 back = false;
206 } else {
207 if (!begun) {
208 if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r') {
209 begun = true;
210 }
211 }
212 if (begun) {
213 if (ch == '\"') {
214 quoted = !quoted;
215 }
216 if (!quoted) {
217 if (ch == '(') {
218 nested++;
219 }
220 if (ch == ')') {
221 nested--;
222 }
223 if (ch == '{') {
224 nestedAlt++;
225 }
226 if (ch == '}') {
227 nestedAlt--;
228 }
229 }
230 if (ch == '\\') {
231 back = true;
232 arg += ch;
233 } else {
234 if ((!quoted) && (ch == ',' || ch == ' ' || ch == '\t' ||
235 ch == '\n' || ch == '\r') &&
236 (nestedAlt == 0) && (nested == 0)) {
237 if (!arg.empty()) {
238 if (arg == "null") {
239 add(new StoreVocab32(yarp::os::createVocab32('n', 'u', 'l', 'l')));
240 } else {
241 smartAdd(arg);
242 }
243 }
244 arg = "";
245 begun = false;
246 } else {
247 arg += ch;
248 }
249 }
250 }
251 }
252 }
253}
254
255bool BottleImpl::isComplete(const char* txt)
256{
257 bool quoted = false;
258 bool back = false;
259 bool begun = false;
260 int nested = 0;
261 int nestedAlt = 0;
262 std::string nline = txt;
263 nline += " ";
264
265 for (char ch : nline) {
266 if (back) {
267 back = false;
268 } else {
269 if (!begun) {
270 if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r') {
271 begun = true;
272 }
273 }
274 if (begun) {
275 if (ch == '\"') {
276 quoted = !quoted;
277 }
278 if (!quoted) {
279 if (ch == '(') {
280 nested++;
281 }
282 if (ch == ')') {
283 nested--;
284 }
285 if (ch == '{') {
286 nestedAlt++;
287 }
288 if (ch == '}') {
289 nestedAlt--;
290 }
291 }
292 if (ch == '\\') {
293 back = true;
294 // arg += ch;
295 } else {
296 if ((!quoted) &&
297 (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') &&
298 (nestedAlt == 0) && (nested == 0)) {
299 // smartAdd(arg);
300 begun = false;
301 } else {
302 // arg += ch;
303 }
304 }
305 }
306 }
307 }
308 return nested == 0 && nestedAlt == 0 && !quoted;
309}
310
311
312std::string BottleImpl::toString() const
313{
314 std::string result;
315 for (unsigned int i = 0; i < content.size(); i++) {
316 if (i > 0) {
317 result += " ";
318 }
319 Storable& s = *content[i];
320 result += s.toStringNested();
321 }
322 return result;
323}
324
326{
327 return content.size();
328}
329
330
332{
333 if (reader.isError()) {
334 return false;
335 }
336 std::int32_t id = speciality;
337 yCTrace(BOTTLEIMPL, "READING, nest flag is %d", nested);
338 if (id == 0) {
339 id = reader.expectInt32();
340 yCTrace(BOTTLEIMPL, "READ subcode %" PRId32, id);
341 } else {
342 yCTrace(BOTTLEIMPL, "READ skipped subcode %" PRId32, speciality);
343 }
345 if (storable == nullptr) {
346 yCError(BOTTLEIMPL, "Reader failed, unrecognized object code %" PRId32, id);
347 return false;
348 }
349 storable->readRaw(reader);
350 add(storable);
351 return true;
352}
353
354
355void BottleImpl::fromBinary(const char* text, size_t len)
356{
357 std::string wrapper(text, len);
359 sis.add(wrapper);
361 Route route;
362 reader.reset(sis, nullptr, route, len, false);
363 read(reader);
364}
365
366
368{
369 std::string wrapper(data.get(), data.length());
371 sis.add(wrapper);
373 Route route;
374 reader.reset(sis, nullptr, route, data.length(), false);
375
376 clear();
377 dirty = true; // for clarity
378
379 if (!nested) {
380 clear();
381 specialize(0);
382
383 std::int32_t code = reader.expectInt32();
384 if (reader.isError()) {
385 return false;
386 }
387 yCTrace(BOTTLEIMPL, "READ got top level code %" PRId32, code);
388 code = code & UNIT_MASK;
389 if (code != 0) {
390 specialize(code);
391 }
392 }
393 std::int32_t len = reader.expectInt32();
394 if (reader.isError()) {
395 return false;
396 }
397 yCTrace(BOTTLEIMPL, "READ bottle length %d", len);
398 for (int i = 0; i < len; i++) {
399 bool ok = fromBytes(reader);
400 if (!ok) {
401 return false;
402 }
403 }
404
405 return true;
406}
407
409{
410 synch();
411 yCAssert(BOTTLEIMPL, data.length() == byteCount());
412 memcpy(data.get(), getBytes(), byteCount());
413}
414
415
416const char* BottleImpl::getBytes() const
417{
418 yCTrace(BOTTLEIMPL, "am I nested? %d", nested);
419 synch();
420 return &data[0];
421}
422
424{
425 synch();
426 return data.size();
427}
428
430{
431 synch();
432}
433
435{
436 // could simplify this if knew lengths of blocks up front
437 if (writer.isTextMode()) {
438 writer.appendText(toString());
439 } else {
440 synch();
441 writer.appendBlock(getBytes(), byteCount());
442 }
443 return !writer.isError();
444}
445
446
448{
449 bool result = false;
450
451 if (reader.isTextMode()) {
452 std::string str = reader.expectText();
453 if (reader.isError()) {
454 return false;
455 }
456 bool done = (str.length() <= 0);
457 while (!done) {
458 if (str[str.length() - 1] == '\\') {
459 str = str.substr(0, str.length() - 1);
460 str += reader.expectText();
461 if (reader.isError()) {
462 return false;
463 }
464 } else {
465 if (isComplete(str.c_str())) {
466 done = true;
467 } else {
468 str += "\n";
469 str += reader.expectText();
470 if (reader.isError()) {
471 return false;
472 }
473 }
474 }
475 }
476 fromString(str);
477 result = true;
478 } else {
479 if (!nested) {
480 // no byte length any more to facilitate nesting
481 // reader.expectInt32(); // the bottle byte ct; ignored
482
483 clear();
484 specialize(0);
485
486 std::int32_t code = reader.expectInt32();
487 if (reader.isError()) {
488 return false;
489 }
490 yCTrace(BOTTLEIMPL, "READ got top level code %" PRId32, code);
491 code = code & UNIT_MASK;
492 if (code != 0) {
493 specialize(code);
494 }
495 }
496
497 result = true;
498 clear();
499 dirty = true; // for clarity
500
501 std::int32_t len = 0;
502 len = reader.expectInt32();
503 if (reader.isError()) {
504 return false;
505 }
506 yCTrace(BOTTLEIMPL, "READ got length %d", len);
507 for (int i = 0; i < len; i++) {
508 bool ok = fromBytes(reader);
509 if (!ok) {
510 return false;
511 }
512 }
513 }
514 return result;
515}
516
517void BottleImpl::synch() const
518{
519 const_cast<BottleImpl*>(this)->synch();
520}
521
522void BottleImpl::synch()
523{
524 if (dirty) {
525 if (!nested) {
526 subCode();
527 yCTrace(BOTTLEIMPL, "bottle code %" PRId32, StoreList::code + subCode());
528 }
529 data.clear();
530 BufferedConnectionWriter writer;
531 if (!nested) {
532 writer.appendInt32(StoreList::code + speciality);
533 yCTrace(BOTTLEIMPL, "wrote bottle code %" PRId32, StoreList::code + speciality);
534 }
535 yCTrace(BOTTLEIMPL, "bottle length %zd", size());
536 writer.appendInt32(static_cast<std::int32_t>(size()));
537 for (auto s : content) {
538 if (speciality == 0) {
539 yCTrace(BOTTLEIMPL, "subcode %" PRId32, s->getCode());
540 writer.appendInt32(s->getCode());
541 } else {
542 yCTrace(BOTTLEIMPL, "skipped subcode %" PRId32, s->getCode());
543 yCAssert(BOTTLEIMPL, speciality == s->getCode());
544 }
545 if (s->isList()) {
546 s->asList()->implementation->setNested(true);
547 }
548 s->writeRaw(writer);
549 }
550 data.resize(writer.dataSize(), ' ');
551 MemoryOutputStream m(&data[0]);
552 writer.write(m);
553 dirty = false;
554 }
555}
556
557
558void BottleImpl::specialize(std::int32_t subCode)
559{
560 speciality = subCode;
561}
562
563
565{
566 return speciality;
567}
568
569void BottleImpl::setNested(bool nested)
570{
571 this->nested = nested;
572}
573
574
576{
577 return subCoder(*this);
578}
579
581{
582 return index < size();
583}
584
585bool BottleImpl::isInt8(int index)
586{
587 return (checkIndex(index) ? content[index]->isInt8() : false);
588}
589
590bool BottleImpl::isInt16(int index)
591{
592 return (checkIndex(index) ? content[index]->isInt16() : false);
593}
594
595bool BottleImpl::isInt32(int index)
596{
597 return (checkIndex(index) ? content[index]->isInt32() : false);
598}
599
600bool BottleImpl::isInt64(int index)
601{
602 return (checkIndex(index) ? content[index]->isInt64() : false);
603}
604
606{
607 return (checkIndex(index) ? content[index]->isFloat32() : false);
608}
609
611{
612 return (checkIndex(index) ? content[index]->isFloat64() : false);
613}
614
615bool BottleImpl::isString(int index)
616{
617 return (checkIndex(index) ? content[index]->isString() : false);
618}
619
620bool BottleImpl::isList(int index)
621{
622 return (checkIndex(index) ? content[index]->isList() : false);
623}
624
626{
627 Storable* stb = nullptr;
628 if (size() == 0) {
629 stb = new StoreNull();
630 } else {
631 stb = content[size() - 1];
632 content.pop_back();
633 dirty = true;
634 }
635 yCAssert(BOTTLEIMPL, stb != nullptr);
636 return stb;
637}
638
640{
641 return (checkIndex(index) ? *(content[index]) : getNull());
642}
643
645{
646 auto* lst = new StoreList();
647 add(lst);
648 return lst->internal();
649}
650
652{
653 auto* lst = new StoreDict();
654 add(lst);
655 return lst->internal();
656}
657
659{
660
661 if (len == 0 || alt->size() == 0) {
662 clear();
663 return;
664 }
665
666 // Handle copying to the same object just a subset of the bottle
667 const BottleImpl* src = alt;
668 BottleImpl tmp(nullptr);
669 if (alt == this) {
670 tmp.fromString(toString());
671 src = &tmp;
672 }
673
674 clear();
675
676 const size_t last = src->size() - 1;
677 for (size_t i = 0; (i < len) && (first + i <= last); ++i) {
678 add(src->get(first + i).cloneStorable());
679 }
680}
681
683{
684 if (ro) {
685 yCFatal(BOTTLEIMPL, "Attempted to modify the null bottle");
686 }
687 if (invalid) {
688 invalid = false;
689 }
690}
691
692Value& BottleImpl::findGroupBit(const std::string& key) const
693{
694 for (size_t i = 0; i < size(); i++) {
695 Value* org = &(get(static_cast<int>(i)));
696 Value* cursor = org;
697 if (cursor->isList()) {
698 cursor = &(cursor->asList()->get(0));
699 }
700 if (key == cursor->toString()) {
701 return *org;
702 }
703 }
704 // return invalid object
705 return get(-1);
706}
707
708Value& BottleImpl::findBit(const std::string& key) const
709{
710 for (size_t i = 0; i < size(); i++) {
711 Value* org = &(get(static_cast<int>(i)));
712 Value* cursor = org;
713 bool nested = false;
714 if (cursor->isList()) {
715 Bottle* bot = cursor->asList();
716 cursor = &(bot->get(0));
717 nested = true;
718 }
719 if (key == cursor->toString()) {
720 if (nested) {
721 return org->asList()->get(1);
722 }
723 return get(static_cast<int>(i + 1));
724 }
725 }
726 // return invalid object
727 return get(-1);
728}
#define UNIT_MASK
Definition Storable.h:18
A simple collection of objects that can be described and transmitted in a portable way.
Definition Bottle.h:64
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition Bottle.cpp:246
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:43
virtual bool isList() const
Checks if value is a list.
Definition Value.cpp:162
virtual Bottle * asList() const
Get list value.
Definition Value.cpp:240
std::string toString() const override
Return a standard text representation of the content of the object.
Definition Value.cpp:356
virtual std::int32_t getCode() const
Get standard type code of value.
Definition Value.cpp:374
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:156
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:42
virtual std::string toStringNested() const
Create string representation, including any syntax that should wrap it such as braces or parentheses.
Definition Storable.h:259
std::int64_t asInt64() const override
Get 64-bit integer value.
Definition Storable.h:136
bool isList() const override
Checks if value is a list.
Definition Storable.h:171
virtual bool writeRaw(ConnectionWriter &connection) const =0
static Storable * createByCode(std::int32_t id)
Definition Storable.cpp:64
virtual void fromStringNested(const std::string &src)
Initialize from a string representation.
Definition Storable.h:248
virtual Storable * cloneStorable() const
Typed synonym for clone()
Definition Storable.h:57
yarp::os::Bottle * asList() const override
Get list value.
Definition Storable.h:176
bool isInt64() const override
Checks if value is a 64-bit integer.
Definition Storable.h:131
std::int32_t asInt32() const override
Get 32-bit integer value.
Definition Storable.h:126
Key/value pairs.
Definition Storable.h:1103
A nested list of items.
Definition Storable.h:1038
static const std::int32_t code
Definition Storable.h:1065
std::string asString() const override
Get string value.
Definition Storable.h:956
A vocabulary item.
Definition Storable.h:829
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:1162
constexpr yarp::conf::vocab32_t createVocab32(char a, char b=0, char c=0, char d=0)
Create a vocab from chars.
Definition Vocab.h:27