Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add user encode/decode #10

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 91 additions & 1 deletion src/dataencode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,96 @@ void from_wire_type_value(Buffer& buf, TypeStore& ctxt, Value& val)
from_wire_full(buf, ctxt, val);
}

}} // namespace pvxs::impl
} // namespace impl

namespace xcode {

void encodeType(std::vector<uint8_t>& buf, const Value& prototype, bool be)
{
VectorOutBuf out(be, buf);
to_wire(out, Value::Helper::desc(prototype));
if(!out.good())
throw std::runtime_error("Encode error");

// trim unused space
buf.resize(out.consumed());
}

void encodeFull(std::vector<uint8_t>& buf, const Value& value, bool be)
{
if(!value)
throw std::invalid_argument("Can't encode empty Value");

VectorOutBuf out(be, buf);
to_wire_full(out, value);
if(!out.good())
throw std::runtime_error("Encode error");

// trim unused space
buf.resize(out.consumed());
}

void encodeValid(std::vector<uint8_t>& buf, const Value& value, bool be)
{
if(value.type()!=TypeCode::Struct)
throw std::invalid_argument("partial/valid encode requires Struct");

VectorOutBuf out(be, buf);
to_wire_valid(out, value);
if(!out.good())
throw std::runtime_error("Encode error");

// trim unused space
buf.resize(out.consumed());
}

Value decodeType(const uint8_t*& buf, const uint8_t *bufend, bool be)
{
// buf not actually modified
FixedBuf inp(be, const_cast<uint8_t*>(buf), bufend-buf);
TypeStore cache;
Value ret;
from_wire_type(inp, cache, ret);
if(!inp.good())
throw std::runtime_error("Decode error");

buf = inp.save();
return ret;
}

void decodeFull(Value& dest, const uint8_t*& buf, const uint8_t *bufend, bool be)
{
if(!dest)
throw std::invalid_argument("Can't decode into empty Value");

// buf not actually modified
FixedBuf inp(be, const_cast<uint8_t*>(buf), bufend-buf);
TypeStore cache;
Value ret;
from_wire_full(inp, cache, dest);
if(!inp.good())
throw std::runtime_error("Decode error");

buf = inp.save();
}

void decodeValid(Value& dest, const uint8_t*& buf, const uint8_t *bufend, bool be)
{
if(dest.type()!=TypeCode::Struct)
throw std::logic_error("partial/valid decode requires Struct");

// buf not actually modified
FixedBuf inp(be, const_cast<uint8_t*>(buf), bufend-buf);
TypeStore cache;
Value ret;
from_wire_valid(inp, cache, dest);
if(!inp.good())
throw std::runtime_error("Decode error");

buf = inp.save();
}


}} // namespace pvxs::xcode

#endif // DATAENCODE_H
2 changes: 1 addition & 1 deletion src/pvaproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class PVXS_API VectorOutBuf : public Buffer
public:
// note: vector::data() is not constexpr in c++11
VectorOutBuf(bool be, std::vector<uint8_t>& b)
:base_type(be, b.data(), b.size())
:base_type(be, b.data()+b.size(), 0u)
,backing(b)
{}
virtual ~VectorOutBuf();
Expand Down
47 changes: 47 additions & 0 deletions src/pvxs/data.h
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,53 @@ std::ostream& operator<<(std::ostream& strm, const Value& val)
return strm<<val.format();
}

//! Encoding and decoding Value as a byte array
namespace xcode {

/** Encode type definition to byte array
*/
PVXS_API
void encodeType(std::vector<uint8_t>& buf, const Value& prototype, bool be=true);

/** Encode full (for structures) Value to byte array.
*
* Provided Value must not be empty.
*/
PVXS_API
void encodeFull(std::vector<uint8_t>& buf, const Value& value, bool be=true);

/** Encode a bit-mask and only valid Value fields to byte array.
*
* Provided Value must be a Struct
*/
PVXS_API
void encodeValid(std::vector<uint8_t>& buf, const Value& value, bool be=true);

/** Decode type definition from byte array.
*
* Returns a prototype (empty) Value
*/
PVXS_API
Value decodeType(const uint8_t*& buf, const uint8_t* bufend, bool be=true);

/** Decode all fields and store in destination Value
*
* Caller must ensure that the destination Value matches
* the type definition used to encode field values.
*/
PVXS_API
void decodeFull(Value &dest, const uint8_t*& buf, const uint8_t *bufend, bool be=true);

/** Decode a bit-mask and only value fields and store in destination Value
*
* Caller must ensure that the destination Value matches
* the type definition used to encode field values.
*/
PVXS_API
void decodeValid(Value& dest, const uint8_t*& buf, const uint8_t *bufend, bool be=true);

} // namespace xcode

} // namespace pvxs

#endif // PVXS_DATA_H
2 changes: 2 additions & 0 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ void Server::Pvt::onSearch(const UDPManager::Search& msg)
if(nreply==0 && !msg.mustReply)
return;

searchReply.clear();
VectorOutBuf M(true, searchReply);

M.skip(8); // fill in header after body length known
Expand Down Expand Up @@ -495,6 +496,7 @@ void Server::Pvt::doBeacons(short evt)
{
log_debug_printf(serversetup, "Server beacon timer expires\n%s", "");

beaconMsg.clear();
VectorOutBuf M(true, beaconMsg);
M.skip(8); // fill in header after body length known

Expand Down
2 changes: 1 addition & 1 deletion src/serverconn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ ServerConn::ServerConn(ServIface* iface, evutil_socket_t sock, struct sockaddr *

auto tx = bufferevent_get_output(bev.get());

std::vector<uint8_t> buf(128);
std::vector<uint8_t> buf;

// queue connection validation message
{
Expand Down
4 changes: 2 additions & 2 deletions test/testudp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ void testSearch(bool be, std::initializer_list<const char*> names)
});
sub->start();

std::vector<uint8_t> msg(1024, 0);
std::vector<uint8_t> msg;
VectorOutBuf M(be, msg);

M.skip(8); // placeholder for header
Expand All @@ -132,7 +132,7 @@ void testSearch(bool be, std::initializer_list<const char*> names)
to_wire(M, name);
}

auto pktlen = M.save()-msg.data();
auto pktlen = M.consumed();

FixedBuf H(be, msg.data(), 8);
to_wire(H, Header{CMD_SEARCH, 0, uint32_t(pktlen-8)});
Expand Down
96 changes: 95 additions & 1 deletion test/testxcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,11 +948,104 @@ void testEmptyRequest()
"[0] struct parent=[0] [0:1)\n")<<"\nActual descs2\n"<<descs2.data();
}

void testUserXCode()
{
testDiag("%s", __func__);

auto top = TypeDef(TypeCode::Struct, {
members::UInt32("value"),
members::UInt32("other"),
}).create();

{
std::vector<uint8_t> buf;
xcode::encodeType(buf, top);
testBytes(buf, "\x80\x00\x02\x05value\x26\x05other\x26");
}

{
std::vector<uint8_t> buf;
xcode::encodeType(buf, top);
xcode::encodeValid(buf, top);
testBytes(buf, "\x80\x00\x02\x05value\x26\x05other\x26\x00");
}

top["value"] = 0xdeadbeef;

{
std::vector<uint8_t> buf;
xcode::encodeType(buf, top);
xcode::encodeValid(buf, top);
testBytes(buf, "\x80\x00\x02\x05value\x26\x05other\x26\x01\x02\xde\xad\xbe\xef");
}

{
std::vector<uint8_t> buf;
xcode::encodeType(buf, top);
xcode::encodeFull(buf, top);
testBytes(buf, "\x80\x00\x02\x05value\x26\x05other\x26\xde\xad\xbe\xef\x00\x00\x00\x00");
}

{
const char raw[] = "\x80\x00\x02\x05value\x26\x05other\x26";
auto pos = (const uint8_t*)raw;
auto end = sizeof(raw)-1u+(const uint8_t*)raw;
auto top2 = xcode::decodeType(pos, end);
testEq(pos, end);
testFalse(top.equalInst(top2));
testTrue(top.equalType(top2))<<"top: "<<top<<"\n top2: "<<top2;
}

{
const char raw[] = "\x80\x00\x02\x05value\x26\x05other\x26\x01\x02\xde\xad\xbe\xef";
auto pos = (const uint8_t*)raw;
auto end = sizeof(raw)-1u+(const uint8_t*)raw;
auto top2 = xcode::decodeType(pos, end);
xcode::decodeValid(top2, pos, end);
testEq(pos, end);
testFalse(top.equalInst(top2));
testTrue(top.equalType(top2))<<"top: "<<top<<"\n top2: "<<top2;
testTrue(top2["value"].isMarked());
testFalse(top2["other"].isMarked());
testEq(top2["value"].as<uint32_t>(), 0xdeadbeef);
}

{
const char raw[] = "\x80\x00\x02\x05value\x26\x05other\x26\xde\xad\xbe\xef\x00\x00\x00\x00";
auto pos = (const uint8_t*)raw;
auto end = sizeof(raw)-1u+(const uint8_t*)raw;
auto top2 = xcode::decodeType(pos, end);
xcode::decodeFull(top2, pos, end);
testEq(pos, end);
testFalse(top.equalInst(top2));
testTrue(top.equalType(top2))<<"top: "<<top<<"\n top2: "<<top2;
testTrue(top2["value"].isMarked());
testTrue(top2["other"].isMarked());
testEq(top2["value"].as<uint32_t>(), 0xdeadbeef);
testEq(top2["other"].as<uint32_t>(), 0u);
}

testThrows<std::runtime_error>([](){
const char raw[] = "\x80\x00\x02\x05va";
auto pos = (const uint8_t*)raw;
auto end = sizeof(raw)-1u+(const uint8_t*)raw;
auto top2 = xcode::decodeType(pos, end);
})<<"Truncated type";

testThrows<std::runtime_error>([](){
const char raw[] = "\x80\x00\x02\x05value\x26\x05other\x26\xde\xad\xbe";
auto pos = (const uint8_t*)raw;
auto end = sizeof(raw)-1u+(const uint8_t*)raw;
auto top2 = xcode::decodeType(pos, end);
xcode::decodeFull(top2, pos, end);
})<<"Truncated full Value";
}

} // namespace

MAIN(testxcode)
{
testPlan(116);
testPlan(138);
testSetup();
testSerialize1();
testDeserialize1();
Expand All @@ -965,5 +1058,6 @@ MAIN(testxcode)
testXCodeNTScalar();
testXCodeNTNDArray();
testEmptyRequest();
testUserXCode();
return testDone();
}