Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
MODMED
modmedLog
Commits
fe68c6dc
Commit
fe68c6dc
authored
Jun 19, 2018
by
EXT Arnaud Clère
Browse files
WIP: test Read+Write between buffers, generic and specific data structures
parent
9650ab4b
Changes
3
Hide whitespace changes
Inline
Side-by-side
tests/QBind/QBind.pro
View file @
fe68c6dc
...
...
@@ -27,4 +27,5 @@ SOURCES += \
main
.
cpp
HEADERS
+=
\
QCborWriter
.
hpp
QCborWriter
.
hpp
\
QJsonReader
.
hpp
tests/QBind/QJsonReader.hpp
0 → 100644
View file @
fe68c6dc
/****************************************************************************
* **
* ** Copyright (C) 2017 MinMaxMedical.
* ** All rights reserved.
* ** Contact: MinMaxMedical <InCAS@MinMaxMedical.com>
* **
* ** This file is part of the modmedLog module.
* **
* ** $QT_BEGIN_LICENSE:BSD$
* ** You may use this file under the terms of the BSD license as follows:
* **
* ** "Redistribution and use in source and binary forms, with or without
* ** modification, are permitted provided that the following conditions are
* ** met:
* ** * Redistributions of source code must retain the above copyright
* ** notice, this list of conditions and the following disclaimer.
* ** * Redistributions in binary form must reproduce the above copyright
* ** notice, this list of conditions and the following disclaimer in
* ** the documentation and/or other materials provided with the
* ** distribution.
* ** * Neither the name of MinMaxMedical S.A.S. and its Subsidiary(-ies) nor
* ** the names of its contributors may be used to endorse or promote
* ** products derived from this software without specific prior written
* ** permission.
* **
* ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
* ** $QT_END_LICENSE$
* **
* ****************************************************************************/
#pragma once
#include <type_traits>
#include <QtCore/qiodevice.h>
class
QJsonReader
{
class
Impl
;
Q_DISABLE_COPY
(
QJsonReader
)
// but not swap
public:
Q_ENABLE_SWAP
(
QJsonReader
,
std
::
swap
(
m
,
o
.
m
);)
using
TResult
=
QJsonReader
;
using
TValue
=
QJsonReader
::
Impl
*
;
using
TSequence
=
QJsonReader
::
Impl
*
;
static
const
BindMode
Mode
=
BindMode
::
Read
;
QJsonReader
(
QIODevice
*
io
)
:
m
(
new
Impl
(
io
))
{}
~
QJsonReader
()
{
if
(
m
)
delete
m
;
}
Val
<
QJsonReader
>
begin
()
{
auto
beforeMove
=
m
;
return
std
::
move
(
Val
<
QJsonReader
>
(
beforeMove
,
std
::
move
(
*
this
)));
}
private:
Impl
*
m
=
nullptr
;
class
Impl
{
struct
Level
{
bool
isFirst
;
const
char
*
end
;
};
QIODevice
*
io
=
nullptr
;
QStack
<
Level
>
levels
=
QStack
<
Level
>
();
//!< minimal dynamic context to implement out()
char
nextChar
=
'\0'
;
//!< minimal dynamic context to parse
public:
Impl
(
QIODevice
*
io
)
:
io
(
io
)
{}
protected:
// static "interface" implementation
template
<
class
T_
>
friend
class
Val
;
Impl
*
sequence
()
{
levels
.
push
(
Level
{
true
,
"]"
});
return
get
(
'['
,
"ntf-.0123456789[{
\"
"
)
?
this
:
nullptr
;
}
bool
null
()
{
if
(
!
get
(
'n'
,
"[{
\"
ntf-.0123456789"
))
return
false
;
if
(
nextChar
==
'u'
)
getChar
();
else
{
next
(
"[{
\"
"
);
return
false
;
}
if
(
nextChar
==
'l'
)
getChar
();
else
{
next
(
"[{
\"
"
);
return
false
;
}
if
(
nextChar
==
'l'
)
getChar
();
else
{
next
(
"[{
\"
"
);
return
false
;
}
return
true
;
}
bool
bind
(
QByteArray
&
s
)
{
if
(
!
get
(
'"'
))
{
return
false
;
}
s
.
clear
();
for
(
char
read
=
getCharInString
();
read
!=
'\0'
;
read
=
getCharInString
())
{
s
.
push_back
(
read
);
}
return
get
(
'"'
);
}
// Natively supported overloads
bool
bind
(
double
&
n
)
{
qlonglong
ll
=
0
;
double
d
=
0
;
// roughly:
// m_stream >> ll;
bool
isNegative
=
false
;
if
(
nextChar
==
'-'
)
{
getChar
();
isNegative
=
true
;
}
int
digit
=
getDigit
();
if
(
digit
<
0
||
9
<
digit
)
{
return
false
;
// do not accept no digit otherwise we may accept an empty mantissa or string!
}
do
{
// TODO detect overflow
ll
=
ll
*
10
+
digit
;
d
=
d
*
10
+
digit
;
digit
=
getDigit
();
}
while
(
0
<=
digit
&&
digit
<=
9
);
// accept many leading '0' which is more permissive than JSON
if
(
nextChar
==
'.'
)
{
// roughly
// d = ll;
// m_stream >> decimal // except it would eat exponent
// d += decimal
getChar
();
double
decimal
=
.1
;
for
(
int
digit
=
getDigit
();
0
<=
digit
&&
digit
<=
9
;
digit
=
getDigit
(),
decimal
/=
10
)
{
d
+=
decimal
*
digit
;
}
}
if
(
nextChar
==
'e'
||
nextChar
==
'E'
)
{
getChar
();
qlonglong
exponent
=
0
;
bool
isNegativeExponent
=
false
;
if
(
nextChar
==
'-'
)
{
getChar
();
isNegativeExponent
=
true
;
}
if
(
nextChar
==
'+'
)
{
getChar
();
}
for
(
int
digit
=
getDigit
();
0
<=
digit
&&
digit
<=
9
;
digit
=
getDigit
())
{
exponent
=
exponent
*
10
+
digit
;
// TODO detect overflow
}
// TODO detect overflow
d
*=
10
^
(
isNegativeExponent
?
-
exponent
:
exponent
);
}
// TODO if (!next(",]}") {}
n
=
isNegative
?
-
d
:
d
;
return
true
;
}
// This dispatch would be more simple with C++17 constexpr if
template
<
typename
T
>
void
bind
(
T
t
)
{
QBind
<
TResult
,
T
>::
bind
(
Val
<
TResult
>
(
this
,
TResult
()),
t
);
// Val<TResult> prevents QBind<TResult,T>() from getting out() of an outer Seq for instance
}
// static "interface" implementation
template
<
class
T_
>
friend
class
Seq
;
Impl
*
item
()
{
Level
&
level
=
levels
.
last
();
if
(
!
level
.
isFirst
&&
!
get
(
','
,
level
.
end
))
{
return
nullptr
;
}
level
.
isFirst
=
false
;
return
this
;
}
Impl
*
out
()
{
auto
level
=
levels
.
pop
();
while
(
get
(
','
,
level
.
end
))
{
skipItem
();
}
return
get
(
*
level
.
end
)
?
this
:
nullptr
;
}
private:
int
getDigit
()
{
return
'0'
<=
nextChar
&&
nextChar
<=
'9'
?
(
getChar
())
-
'0'
:
-
1
;
}
char
getCharInString
()
{
if
(
nextChar
==
'\0'
||
nextChar
==
'"'
)
{
return
'\0'
;
}
if
(
nextChar
==
'\\'
)
{
getChar
();
bool
isHex
;
unsigned
hex
;
switch
(
getChar
())
{
case
'\\'
:
return
'\\'
;
case
'"'
:
return
'"'
;
case
'b'
:
return
'\b'
;
case
'f'
:
return
'\f'
;
case
'n'
:
return
'\n'
;
case
'r'
:
return
'\r'
;
case
't'
:
return
'\t'
;
case
'/'
:
return
'/'
;
case
'u'
:
{
QByteArray
read
;
read
.
push_back
(
getChar
());
read
.
push_back
(
getChar
());
read
.
push_back
(
getChar
());
read
.
push_back
(
getChar
());
hex
=
read
.
toUInt
(
&
isHex
,
16
);
if
(
isHex
)
{
return
hex
;
}
return
'?'
;
}
default
:
return
'?'
;
}
}
else
{
return
getChar
();
}
}
char
getChar
()
{
if
(
!
io
->
getChar
(
&
nextChar
))
{
nextChar
=
'\0'
;
}
return
nextChar
;
}
char
next
(
const
char
*
ends
)
{
while
(
nextChar
!=
'\0'
&&
(
nextChar
<=
' '
||
!
strchr
(
ends
,
nextChar
)))
{
getChar
();
}
return
nextChar
;
}
bool
get
(
char
expected
)
{
return
getChar
()
==
expected
;
}
bool
get
(
char
expected
,
const
char
*
others
)
{
QByteArray
valid
(
others
);
valid
.
append
(
expected
);
if
(
next
(
valid
)
!=
expected
)
{
return
false
;
}
return
get
(
expected
);
}
bool
skipItem
()
{
QByteArray
ba
;
double
d
;
return
(
sequence
()
&&
out
()
)
||
bind
(
ba
)
||
bind
(
d
)
||
null
()
;
}
};
};
tests/QBind/main.cpp
View file @
fe68c6dc
...
...
@@ -68,6 +68,7 @@ public:
Seq
<
T_
>
sequence
()
{
auto
val
=
Q_LIKELY
(
m_val
)
?
m_val
->
sequence
()
:
nullptr
;
return
Seq
<
T_
>
(
val
,
std
::
move
(
m_out
));
}
/**/
T_
null
()
{
if
(
Q_LIKELY
(
m_val
)
)
m_val
->
null
()
;
return
std
::
move
(
m_out
)
;
}
template
<
typename
T
>
T_
bind
(
T
t
)
{
if
(
Q_LIKELY
(
m_val
)
)
m_val
->
bind
(
t
)
;
return
std
::
move
(
m_out
)
;
}
private:
...
...
@@ -114,7 +115,7 @@ struct QBind
}
};
// QBind partial specializations (generic on TResult,
not always
on TResult::Mode)
// QBind partial specializations (generic on TResult,
usually not
on TResult::Mode
for dynamically-sized or builtin types
)
#include <QtCore/qstring.h>
...
...
@@ -143,12 +144,35 @@ struct QBind<TResult, std::nullptr_t> { static TResult bind(Val<TResult> value,
return
value
.
null
();
}};
// QBind specialization for generic Val<TSrc>
#include <QtCore/qjsonvalue.h>
template
<
class
TResult
>
struct
QBind
<
TResult
,
QJsonValue
>
{
static
TResult
bind
(
Val
<
TResult
>
dst
,
QJsonValue
src
)
{
static_assert
(
TResult
::
Mode
==
Write
,
"cannot write to TResult"
);
if
(
src
.
isNull
())
return
dst
.
null
();
if
(
src
.
isArray
())
return
dst
.
sequence
().
bind
(
src
.
toArray
());
if
(
src
.
isDouble
())
return
dst
.
bind
(
src
.
toDouble
());
if
(
src
.
isString
())
return
dst
.
bind
(
src
.
toString
());
}};
#include <QtCore/qjsonarray.h>
template
<
class
TResult
>
struct
QBind
<
TResult
,
QJsonArray
>
{
static
TResult
bind
(
Val
<
TResult
>
dst
,
QJsonArray
src
)
{
static_assert
(
TResult
::
Mode
==
Write
,
"cannot write to TResult"
);
auto
dstSequence
(
dst
.
sequence
());
for
(
auto
&&
item
:
src
)
{
dstSequence
=
dstSequence
.
bind
(
item
);
}
return
dstSequence
;
}};
// QBind specialization for generic Val<TSource>
template
<
class
T
Ds
t
,
class
TSrc
>
struct
QBind
<
T
Ds
t
,
Val
<
TSrc
>>
{
static
T
Ds
t
bind
(
Val
<
T
Ds
t
>
dst
,
Val
<
TSrc
>
src
)
{
static_assert
(
TSrc
::
Mode
==
Read
,
"cannot read from TSrc"
);
static_assert
(
T
Ds
t
::
Mode
==
Write
,
"cannot write to T
Ds
t"
);
template
<
class
T
Resul
t
,
class
TS
ou
rc
e
>
struct
QBind
<
T
Resul
t
,
Val
<
TS
ou
rc
e
>>
{
static
T
Resul
t
bind
(
Val
<
T
Resul
t
>
dst
,
Val
<
TS
ou
rc
e
>
src
)
{
static_assert
(
TS
ou
rc
e
::
Mode
==
Read
,
"cannot read from TS
ou
rc
e
"
);
static_assert
(
T
Resul
t
::
Mode
==
Write
,
"cannot write to T
Resul
t"
);
auto
srcNull
(
src
.
null
());
if
(
srcNull
)
...
...
@@ -280,11 +304,28 @@ private:
// Tests and Benchmarks
// NB: It is not possible to use QBENCHMARK to item qDebug because it installs a specific handler
struct
Person
{
QString
firstName
;
QString
lastName
;
double
height
;
template
<
class
TResult
>
TResult
bind
(
Val
<
TResult
>
value
)
{
return
value
.
sequence
()
.
bind
(
firstName
)
.
bind
(
lastName
)
.
bind
(
height
)
;
// automagically closes all opened structures
}
};
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qfile.h>
#include <QtCore/qbuffer.h>
#include <QtCore/qdebug.h>
#include <QtCore/qjsonvalue.h>
#include "QJsonReader.hpp"
#include "QCborWriter.hpp"
#define GROUP(group) { \
...
...
@@ -420,7 +461,34 @@ int main()
STOP
(
"QWritable>Cbor"
,
QString
::
fromUtf8
(
b
.
buffer
().
toHex
()));
}
GROUP_STOP
#if 0
GROUP("Read+Write")
{
// Person p;
// START {
// b.seek(0); b.buffer()="[\"John\",\"Doe\",42]";
// QJsonReader(&b).begin().bind(p);
// }
// STOP("Json>struct",QString("[%1,%2,%3]").arg(p.firstName,p.lastName,QString::number(p.height)))
// START {
// b.seek(0); b.buffer().clear();
// QJsonWriter(&b).begin().bind(p);
// }
// STOP("struct>Json",b.buffer())
// QJsonValue v;
// START {
// b.seek(0); b.buffer().clear();
// QJsonReader(&b).begin().bind(v);
// }
// STOP("Json>JsonValue",v.toString());
// START {
// b.seek(0); b.buffer().clear();
// QCborWriter(&b).begin().bind(v);
// }
// STOP("JsonValue>Cbor",QString::fromUtf8(b.buffer().toHex()));
}
GROUP_STOP
#endif
return
fclose
(
samples
);
return
fclose
(
results
);
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment