[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [dferry] /: Arguments::Reader/Writer: add isInsideEmptyArray() and currentSignature().
From: Andreas Hartmetz <ahartmetz () gmail ! com>
Date: 2016-10-17 23:32:34
Message-ID: E1bwHOU-0000V1-CR () code ! kde ! org
[Download RAW message or body]
Git commit e5ebd40e0ebf7123d6106057b332ed29d38c5509 by Andreas Hartmetz.
Committed on 17/10/2016 at 23:22.
Pushed by ahartmetz into branch 'master'.
Arguments::Reader/Writer: add isInsideEmptyArray() and currentSignature().
They will (probably) be needed in QtDBus and they are somewhat useful
in any case.
M +21 -22 applications/analyzer/argumentsmodel.cpp
M +83 -26 serialization/arguments.cpp
M +4 -0 serialization/arguments.h
M +8 -15 tests/serialization/tst_arguments.cpp
http://commits.kde.org/dferry/e5ebd40e0ebf7123d6106057b332ed29d38c5509
diff --git a/applications/analyzer/argumentsmodel.cpp \
b/applications/analyzer/argumentsmodel.cpp index 0dc97b6..4e8dbb4 100644
--- a/applications/analyzer/argumentsmodel.cpp
+++ b/applications/analyzer/argumentsmodel.cpp
@@ -88,7 +88,8 @@ QAbstractItemModel* createArgumentsModel(Message *message)
}
bool isDone = false;
- int emptyNesting = 0;
+ // Cache it, don't call Reader::isInsideEmptyArray() on every data element.
+ bool inEmptyArray = false;
while (!isDone) {
switch(reader.state()) {
@@ -112,63 +113,61 @@ QAbstractItemModel* createArgumentsModel(Message *message)
parent = ascend(parent, model);
break;
case Arguments::BeginArray: {
- const bool hasData = \
reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty);
- parent = descend(parent, hasData ? "Array" : "Array (no elements, \
showing just types)");
- emptyNesting += hasData ? 0 : 1;
+ inEmptyArray = \
!reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); + parent = \
descend(parent, inEmptyArray ? "Array (no elements, showing just types)" : "Array"); \
break; } case Arguments::EndArray:
reader.endArray();
+ inEmptyArray = reader.isInsideEmptyArray();
parent = ascend(parent, model);
- emptyNesting = qMax(emptyNesting - 1, 0);
break;
case Arguments::BeginDict: {
- const bool hasData = \
reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty);
- parent = descend(parent, hasData ? "Dict" : "Dict (no elements, showing \
just types)");
- emptyNesting += hasData ? 0 : 1;
+ inEmptyArray = \
!reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty); + parent = \
descend(parent, inEmptyArray ? "Dict (no elements, showing just types)" : "Dict"); \
break; } case Arguments::EndDict:
reader.endDict();
+ inEmptyArray = reader.isInsideEmptyArray();
parent = ascend(parent, model);
- emptyNesting = qMax(emptyNesting - 1, 0);
break;
case Arguments::Byte:
- addKeyValue(parent, "byte", emptyNesting, reader.readByte());
+ addKeyValue(parent, "byte", inEmptyArray, reader.readByte());
break;
case Arguments::Boolean:
- addKeyValue(parent, "boolean", emptyNesting, reader.readBoolean());
+ addKeyValue(parent, "boolean", inEmptyArray, reader.readBoolean());
break;
case Arguments::Int16:
- addKeyValue(parent, "int16", emptyNesting, reader.readInt16());
+ addKeyValue(parent, "int16", inEmptyArray, reader.readInt16());
break;
case Arguments::Uint16:
- addKeyValue(parent, "uint16", emptyNesting, reader.readUint16());
+ addKeyValue(parent, "uint16", inEmptyArray, reader.readUint16());
break;
case Arguments::Int32:
- addKeyValue(parent, "int32", emptyNesting, reader.readInt32());
+ addKeyValue(parent, "int32", inEmptyArray, reader.readInt32());
break;
case Arguments::Uint32:
- addKeyValue(parent, "uint32", emptyNesting, reader.readUint32());
+ addKeyValue(parent, "uint32", inEmptyArray, reader.readUint32());
break;
case Arguments::Int64:
- addKeyValue(parent, "int64", emptyNesting, reader.readInt64());
+ addKeyValue(parent, "int64", inEmptyArray, reader.readInt64());
break;
case Arguments::Uint64:
- addKeyValue(parent, "uint64", emptyNesting, reader.readUint64());
+ addKeyValue(parent, "uint64", inEmptyArray, reader.readUint64());
break;
case Arguments::Double:
- addKeyValue(parent, "double", emptyNesting, reader.readDouble());
+ addKeyValue(parent, "double", inEmptyArray, reader.readDouble());
break;
case Arguments::String:
- addKeyValue(parent, "string", emptyNesting, reader.readString().ptr);
+ addKeyValue(parent, "string", inEmptyArray, reader.readString().ptr);
break;
case Arguments::ObjectPath:
- addKeyValue(parent, "object path", emptyNesting, \
reader.readObjectPath().ptr); + addKeyValue(parent, "object path", \
inEmptyArray, reader.readObjectPath().ptr); break;
case Arguments::Signature:
- addKeyValue(parent, "type signature", emptyNesting, \
reader.readSignature().ptr); + addKeyValue(parent, "type signature", \
inEmptyArray, reader.readSignature().ptr); break;
case Arguments::UnixFd:
- addKeyValue(parent, "file descriptor", emptyNesting, QVariant());
+ addKeyValue(parent, "file descriptor", inEmptyArray, QVariant());
break;
case Arguments::InvalidData:
case Arguments::NeedMoreData:
diff --git a/serialization/arguments.cpp b/serialization/arguments.cpp
index 0e7c5a5..6c03f83 100644
--- a/serialization/arguments.cpp
+++ b/serialization/arguments.cpp
@@ -507,7 +507,10 @@ std::string Arguments::prettyPrint() const
std::string nestingPrefix;
bool isDone = false;
- int emptyNesting = 0;
+
+ // Cache it, don't call Reader::isInsideEmptyArray() on every data element. This \
isn't really + // a big deal for performance here, but in other situations it is, \
so set a good example :) + bool inEmptyArray = false;
while (!isDone) {
// HACK use nestingPrefix to determine when we're switching from key to \
value - this can be done @@ -548,27 +551,26 @@ std::string Arguments::prettyPrint() \
const
const std::pair<Arguments::IoState, chunk> bytes = \
reader.readPrimitiveArray(); assert(bytes.first == Arguments::Byte);
assert(bytes.second.length > 0);
+ inEmptyArray = reader.isInsideEmptyArray(); // Maybe not necessary, \
but safe
ret << nestingPrefix << "array of bytes [ " << \
uint(bytes.second.ptr[0]); for (uint32 i = 1; i < bytes.second.length; i++) {
ret << ", " << uint(bytes.second.ptr[i]);
}
ret << " ]\n";
} else {
- const bool hasData = \
reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty);
- emptyNesting += hasData ? 0 : 1;
+ inEmptyArray = \
!reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); ret << nestingPrefix << \
"begin array\n"; nestingPrefix += "[ ";
}
break;
case Arguments::EndArray:
reader.endArray();
- emptyNesting = std::max(emptyNesting - 1, 0);
+ inEmptyArray = reader.isInsideEmptyArray();
nestingPrefix.resize(nestingPrefix.size() - 2);
ret << nestingPrefix << "end array\n";
break;
case Arguments::BeginDict: {
- const bool hasData = \
reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty);
- emptyNesting += hasData ? 0 : 1;
+ inEmptyArray = \
!reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty); ret << nestingPrefix << \
"begin dict\n"; nestingPrefix += "{K ";
break; }
@@ -583,14 +585,14 @@ std::string Arguments::prettyPrint() const
#endif
case Arguments::EndDict:
reader.endDict();
- emptyNesting = std::max(emptyNesting - 1, 0);
+ inEmptyArray = reader.isInsideEmptyArray();
nestingPrefix.resize(nestingPrefix.size() - strlen("{V "));
ret << nestingPrefix << "end dict\n";
break;
case Arguments::Boolean: {
bool b = reader.readBoolean();
ret << nestingPrefix << "bool: ";
- if (emptyNesting) {
+ if (inEmptyArray) {
ret << "<nil>";
} else {
ret << (b ? "true" : "false");
@@ -598,37 +600,37 @@ std::string Arguments::prettyPrint() const
ret << '\n';
break; }
case Arguments::Byte:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, int(reader.readByte()), \
"byte"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
int(reader.readByte()), "byte"); break;
case Arguments::Int16:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readInt16(), \
"int16"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readInt16(), "int16"); break;
case Arguments::Uint16:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readUint16(), \
"uint16"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readUint16(), "uint16"); break;
case Arguments::Int32:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readInt32(), \
"int32"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readInt32(), "int32"); break;
case Arguments::Uint32:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readUint32(), \
"uint32"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readUint32(), "uint32"); break;
case Arguments::Int64:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readInt64(), \
"int64"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readInt64(), "int64"); break;
case Arguments::Uint64:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readUint64(), \
"uint64"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readUint64(), "uint64"); break;
case Arguments::Double:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readDouble(), \
"double"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readDouble(), "double"); break;
case Arguments::String:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readString(), \
"string"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readString(), "string"); break;
case Arguments::ObjectPath:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, \
reader.readObjectPath(), "object path"); + printMaybeNil(&ret, \
nestingPrefix, inEmptyArray, reader.readObjectPath(), "object path"); break;
case Arguments::Signature:
- printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readSignature(), \
"type signature"); + printMaybeNil(&ret, nestingPrefix, inEmptyArray, \
reader.readSignature(), "type signature"); break;
case Arguments::UnixFd:
// TODO
@@ -940,6 +942,36 @@ cstring Arguments::Reader::stateString() const
return printableState(m_state);
}
+bool Arguments::Reader::isInsideEmptyArray() const
+{
+ if (d->m_nilArrayNesting > 1) {
+ return true;
+ } else if (d->m_nilArrayNesting == 1) {
+ // Special case 1: in state BeginArray/Dict, when we've just entered the nil \
array according + // to our internal state, the API user cannot know that yet \
-> return false; + // Since this is easily done here, don't complicate the \
rest of the code with a hacky solution. + if (m_state == BeginArray || m_state \
== BeginDict) { + return false;
+ }
+ // Special case 2: in state EndArray/Dict, when we've just left the nil \
array according + // to our internal state, advanceState() and endArray/Dict() \
make sure that + // d->m_nilArrayNesting is only decremented after \
endArray/Dict() has been called. + // Nothing to do for us here, but this is \
the point to explain it. + return true;
+ } else {
+ assert(d->m_nilArrayNesting == 0);
+ return false;
+ }
+}
+
+cstring Arguments::Reader::currentSignature() const
+{
+ // ### not sure if determining length like that is the best way, should probably \
be as similar to + // libdbus-1 as possible (unless we can do much better).
+ return cstring(d->m_signature.ptr,
+ std::max(uint32(0), std::min(d->m_signature.length, \
d->m_signaturePosition))); +}
+
void Arguments::Reader::replaceData(chunk data)
{
VALID_IF(data.length >= d->m_dataPosition, Error::ReplacementDataIsShorter);
@@ -1167,20 +1199,24 @@ void Arguments::Reader::advanceState()
const bool isDict = aggregateInfo.aggregateType == BeginDict;
if (d->m_signaturePosition > aggregateInfo.arr.containedTypeBegin + \
(isDict ? 1 : 0)) { const Private::ArrayInfo &arrayInfo = aggregateInfo.arr;
- bool moreElements = false;
+ bool moreIterations = false;
if (unlikely(d->m_nilArrayNesting)) {
// we're done iterating over it once
// TODO unit-test this
- d->m_nilArrayNesting--;
+ // HACK: decreasing m_nilArrayNesting here would be much \
cleaner! But from the + // user's POV, the nil array has not been \
left before the call to endArray(). + // We will set state \
EndArray / EndDict, so we know that the user "must" call + // \
endArray()/endDict(), so adjust d->m_nilArrayNesting in those methods. + \
//d->m_nilArrayNesting--; } else if (d->m_dataPosition < arrayInfo.dataEnd) {
if (isDict) {
d->m_dataPosition = align(d->m_dataPosition, 8); // align to \
dict entry }
// rewind to start of contained type and read the type info \
there
d->m_signaturePosition = arrayInfo.containedTypeBegin;
- moreElements = true;
+ moreIterations = true;
}
- if (!moreElements) {
+ if (!moreIterations) {
// TODO check that final data position is where it should be \
according to the // serialized array length
m_state = isDict ? EndDict : EndArray;
@@ -1326,7 +1362,8 @@ void Arguments::Reader::advanceState()
VALID_IF(d->m_nesting.beginArray(), Error::MalformedMessageData);
if (firstElementTy.state() == BeginDict) {
d->m_signaturePosition++;
- // TODO check whether the first type is a primitive or string type!
+ // TODO check whether the first type is a primitive or string type! // \
### isn't that already + // checked for the main signature and / or \
variants, though?
// only closed at end of dict - there is no observable difference for \
clients
VALID_IF(d->m_nesting.beginParen(), Error::MalformedMessageData);
}
@@ -1462,7 +1499,11 @@ void Arguments::Reader::skipArray()
void Arguments::Reader::endArray()
{
- advanceStateFrom(EndArray);
+ VALID_IF(m_state == EndArray, Error::ReadWrongType);
+ if (unlikely(d->m_nilArrayNesting)) {
+ d->m_nilArrayNesting--;
+ }
+ advanceState();
}
static bool isAligned(uint32 value, uint32 alignment)
@@ -1562,7 +1603,11 @@ void Arguments::Reader::skipDict()
void Arguments::Reader::endDict()
{
- advanceStateFrom(EndDict);
+ VALID_IF(m_state == EndDict, Error::ReadWrongType);
+ if (unlikely(d->m_nilArrayNesting)) {
+ d->m_nilArrayNesting--;
+ }
+ advanceState();
}
void Arguments::Reader::beginStruct()
@@ -1853,6 +1898,18 @@ cstring Arguments::Writer::stateString() const
return printableState(m_state);
}
+bool Arguments::Writer::isInsideEmptyArray() const
+{
+ return d->m_nilArrayNesting > 0;
+}
+
+cstring Arguments::Writer::currentSignature() const
+{
+ // ### see comment in Reader::currentSignature
+ return cstring(d->m_signature.ptr,
+ std::max(uint32(0), std::min(d->m_signature.length, \
d->m_signaturePosition))); +}
+
void Arguments::Writer::doWritePrimitiveType(uint32 alignAndSize)
{
d->m_dataPosition = align(d->m_dataPosition, alignAndSize);
diff --git a/serialization/arguments.h b/serialization/arguments.h
index d557c8a..cf7719d 100644
--- a/serialization/arguments.h
+++ b/serialization/arguments.h
@@ -169,6 +169,8 @@ public:
IoState state() const { return m_state; }
cstring stateString() const;
+ bool isInsideEmptyArray() const;
+ cstring currentSignature() const; // current signature, either main \
signature or current variant
// HACK call this in NeedMoreData state when more data has been added; this \
replaces m_data
// WARNING: calling replaceData() invalidates copies (if any) of this Reader
void replaceData(chunk data);
@@ -288,6 +290,8 @@ public:
IoState state() const { return m_state; }
cstring stateString() const;
+ bool isInsideEmptyArray() const;
+ cstring currentSignature() const; // current signature, either main \
signature or current variant
enum ArrayOption
{
diff --git a/tests/serialization/tst_arguments.cpp \
b/tests/serialization/tst_arguments.cpp index 3546d04..39dce0b 100644
--- a/tests/serialization/tst_arguments.cpp
+++ b/tests/serialization/tst_arguments.cpp
@@ -70,7 +70,7 @@ static bool stringsEqual(cstring s1, cstring s2)
// 3) skips nil arrays at and below nil array nesting level m_skipNilArraysFromLevel \
with m_skippingReader // It even skips aggregates inside nil arrays as 2) + 3) \
imply. // It checks:
-// a) where nothing is skipped that the aggregate structure and data read is the \
same +// a) where nothing is skipped, that the aggregate structure and data read are \
the same class SkipChecker
{
public:
@@ -284,7 +284,7 @@ static void testReadWithSkip(const Arguments &arg, bool \
debugPrint)
// When using this to iterate over the reader, it will make an exact copy using the \
Writer. // You need to do something only in states where something special should \
happen.
-static void defaultReadToWrite(Arguments::Reader *reader, Arguments::Writer *writer, \
uint32 *emptyNesting) +static void defaultReadToWrite(Arguments::Reader *reader, \
Arguments::Writer *writer) {
switch(reader->state()) {
case Arguments::BeginStruct:
@@ -307,23 +307,19 @@ static void defaultReadToWrite(Arguments::Reader *reader, \
Arguments::Writer *wri
const bool hasData = \
reader->beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); \
writer->beginArray(hasData ? Arguments::Writer::NonEmptyArray
: Arguments::Writer::WriteTypesOfEmptyArray);
- *emptyNesting += hasData ? 0 : 1;
break; }
case Arguments::EndArray:
reader->endArray();
writer->endArray();
- *emptyNesting = std::max(*emptyNesting - 1, 0u);
break;
case Arguments::BeginDict: {
const bool hasData = \
reader->beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty); \
writer->beginDict(hasData ? Arguments::Writer::NonEmptyArray
: Arguments::Writer::WriteTypesOfEmptyArray);
- *emptyNesting += hasData ? 0 : 1;
break; }
case Arguments::EndDict:
reader->endDict();
writer->endDict();
- *emptyNesting = std::max(*emptyNesting - 1, 0u);
break;
case Arguments::Byte:
writer->writeByte(reader->readByte());
@@ -354,7 +350,7 @@ static void defaultReadToWrite(Arguments::Reader *reader, \
Arguments::Writer *wri break;
case Arguments::String: {
cstring s = reader->readString();
- if (*emptyNesting) {
+ if (reader->isInsideEmptyArray()) {
s = cstring("");
} else {
TEST(Arguments::isStringValid(s));
@@ -363,7 +359,7 @@ static void defaultReadToWrite(Arguments::Reader *reader, \
Arguments::Writer *wri break; }
case Arguments::ObjectPath: {
cstring objectPath = reader->readObjectPath();
- if (*emptyNesting) {
+ if (reader->isInsideEmptyArray()) {
objectPath = cstring("/");
} else {
TEST(Arguments::isObjectPathValid(objectPath));
@@ -372,7 +368,7 @@ static void defaultReadToWrite(Arguments::Reader *reader, \
Arguments::Writer *wri break; }
case Arguments::Signature: {
cstring signature = reader->readSignature();
- if (*emptyNesting) {
+ if (reader->isInsideEmptyArray()) {
signature = cstring("");
} else {
TEST(Arguments::isSignatureValid(signature));
@@ -428,7 +424,6 @@ static void doRoundtripWithShortReads(const Arguments &original, \
uint32 dataIncr chunk data = original.data();
chunk shortData;
bool isDone = false;
- uint32 emptyNesting = 0;
while (!isDone) {
TEST(writer.state() != Arguments::InvalidData);
@@ -460,7 +455,7 @@ static void doRoundtripWithShortReads(const Arguments &original, \
uint32 dataIncr reader.replaceData(shortData);
break; }
default:
- defaultReadToWrite(&reader, &writer, &emptyNesting);
+ defaultReadToWrite(&reader, &writer);
break;
}
}
@@ -478,7 +473,6 @@ static void doRoundtripWithReaderCopy(const Arguments &original, \
uint32 dataIncr Arguments::Writer writer;
bool isDone = false;
- uint32 emptyNesting = 0;
uint32 i = 0;
while (!isDone) {
@@ -496,7 +490,7 @@ static void doRoundtripWithReaderCopy(const Arguments &original, \
uint32 dataIncr isDone = true;
break;
default:
- defaultReadToWrite(reader, &writer, &emptyNesting);
+ defaultReadToWrite(reader, &writer);
break;
}
}
@@ -512,7 +506,6 @@ static void doRoundtripWithWriterCopy(const Arguments &original, \
uint32 dataIncr Arguments::Writer *writer = new Arguments::Writer;
bool isDone = false;
- uint32 emptyNesting = 0;
uint32 i = 0;
while (!isDone) {
@@ -530,7 +523,7 @@ static void doRoundtripWithWriterCopy(const Arguments &original, \
uint32 dataIncr isDone = true;
break;
default:
- defaultReadToWrite(&reader, writer, &emptyNesting);
+ defaultReadToWrite(&reader, writer);
break;
}
}
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic