Skip to content

Commit

Permalink
Merge pull request #410 from silicon-heaven/very-long-int
Browse files Browse the repository at this point in the history
Very long int, ParseException::what(), code cleanup
  • Loading branch information
fvacek authored Feb 12, 2024
2 parents 8bf5e61 + 9738ef7 commit f548d74
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 113 deletions.
61 changes: 32 additions & 29 deletions libshvchainpack/c/cchainpack.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,37 +451,43 @@ static void unpack_uint(ccpcp_unpack_context* unpack_context, int *pbitlen)
uint8_t r = (uint8_t)(*p);
num = (num << 8) + r;
};

unpack_context->item.as.UInt = num;
unpack_context->item.type = CCPCP_ITEM_UINT;
if (bitlen > 64) {
// numbers greater than 64 bits will be returned as UINT MAX
unpack_context->item.as.UInt = UINT64_MAX;
unpack_context->item.type = CCPCP_ITEM_UINT;
}
else {
unpack_context->item.as.UInt = num;
unpack_context->item.type = CCPCP_ITEM_UINT;
}
if(pbitlen)
*pbitlen = bitlen;
}

static void unpack_int(ccpcp_unpack_context* unpack_context, int64_t *pval)
static void unpack_int(ccpcp_unpack_context* unpack_context)
{
int64_t snum = 0;
int bitlen;
unpack_uint(unpack_context, &bitlen);

if (bitlen - 1 >= 64) {
unpack_context->err_no = CCPCP_RC_MALFORMED_INPUT;
}

if(unpack_context->err_no == CCPCP_RC_OK) {
const uint64_t sign_bit_mask = (uint64_t)1 << (bitlen - 1);
uint64_t num = unpack_context->item.as.UInt;
snum = (int64_t)num;

// Note: masked value assignment to bool variable would be undefined on some platforms.

if(num & sign_bit_mask) {
snum &= ~(int64_t)sign_bit_mask;
snum = -snum;
unpack_context->item.type = CCPCP_ITEM_INT;
if (bitlen > 64) {
// numbers greater than 64 bits (with sign) will be returned as INT MAX
unpack_context->item.as.Int = INT64_MAX;
}
else {
const uint64_t sign_bit_mask = (uint64_t)1 << (bitlen - 1);
uint64_t num = unpack_context->item.as.UInt;
snum = (int64_t)num;
// Note: masked value assignment to bool variable would be undefined on some platforms.
if(num & sign_bit_mask) {
snum &= ~(int64_t)sign_bit_mask;
snum = -snum;
}
unpack_context->item.as.Int = snum;
}
}
if(pval)
*pval = snum;
}

void unpack_string(ccpcp_unpack_context* unpack_context)
Expand Down Expand Up @@ -583,10 +589,7 @@ void cchainpack_unpack_next (ccpcp_unpack_context* unpack_context)
break;
}
case CP_Int: {
int64_t n;
unpack_int(unpack_context, &n);
unpack_context->item.type = CCPCP_ITEM_INT;
unpack_context->item.as.Int = n;
unpack_int(unpack_context);
break;
}
case CP_UInt: {
Expand Down Expand Up @@ -616,18 +619,18 @@ void cchainpack_unpack_next (ccpcp_unpack_context* unpack_context)
break;
}
case CP_Decimal: {
int64_t mant;
unpack_int(unpack_context, &mant);
int64_t exp;
unpack_int(unpack_context, &exp);
unpack_int(unpack_context);
int64_t mant = unpack_context->item.as.Int;
unpack_int(unpack_context);
int64_t exp = unpack_context->item.as.Int;
unpack_context->item.type = CCPCP_ITEM_DECIMAL;
unpack_context->item.as.Decimal.mantisa = mant;
unpack_context->item.as.Decimal.exponent = (int)exp;
break;
}
case CP_DateTime: {
int64_t d;
unpack_int(unpack_context, &d);
unpack_int(unpack_context);
int64_t d = unpack_context->item.as.Int;
int8_t offset = 0;
bool has_tz_offset = d & 1;
bool has_not_msec = d & 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class SHVCHAINPACK_DECL_EXPORT ParseException : public std::exception
{
using Super = std::exception;
public:
ParseException(int err_code, const std::string msg, long long pos);
ParseException(int err_code, const std::string &msg, long long pos, const std::string &dump);

const char *what() const noexcept override;
int errCode() const;
Expand Down
2 changes: 2 additions & 0 deletions libshvchainpack/include/shv/chainpack/chainpackreader.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class SHVCHAINPACK_DECL_EXPORT ChainPackReader : public AbstractStreamReader
void parseMetaData(RpcValue::MetaData &meta_data);
void parseMap(RpcValue &val);
void parseIMap(RpcValue &val);

void throwParseException(const std::string &msg = {});
};

} // namespace chainpack
Expand Down
2 changes: 2 additions & 0 deletions libshvchainpack/include/shv/chainpack/cponreader.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class SHVCHAINPACK_DECL_EXPORT CponReader : public AbstractStreamReader
void parseMetaData(RpcValue::MetaData &meta_data);
void parseMap(RpcValue &val);
void parseIMap(RpcValue &val);

void throwParseException(const std::string &msg = {});
};

} // namespace chainpack
Expand Down
6 changes: 5 additions & 1 deletion libshvchainpack/src/chainpack/abstractstreamreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ size_t unpack_underflow_handler(ccpcp_unpack_context *ctx)
return 1;
}

ParseException::ParseException(int err_code, const std::string msg, long long pos)
ParseException::ParseException(int err_code, const std::string &msg, long long pos, const std::string &dump)
: m_errCode(err_code), m_msg(msg), m_pos(pos)
{
m_msg = std::string("Parse error: ") + std::to_string(m_errCode) + " " + ccpcp_error_string(m_errCode)
+ " at pos: " + std::to_string(m_pos)
+ " - " + m_msg
+ " near to:\n" + dump;
}

int ParseException::errCode() const
Expand Down
49 changes: 22 additions & 27 deletions libshvchainpack/src/chainpack/chainpackreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,31 @@
#include <shv/chainpack/cchainpack.h>

#include <iostream>
#include <cmath>
#include <array>

namespace shv::chainpack {

#define PARSE_EXCEPTION(msg) {\
std::array<char, 40> buff; \
auto err_pos = m_in.tellg(); \
auto l = m_in.readsome(buff.data(), buff.size() - 1); \
buff[l] = 0; \
auto buff_data_hex = shv::chainpack::utils::hexDump(buff.data(), l); \
if(exception_aborts) { \
std::clog << __FILE__ << ':' << __LINE__; \
std::clog << ' ' << (msg) << " at pos: " << err_pos << " near to:\n" << buff_data_hex << std::endl; \
abort(); \
} \
else { \
throw ParseException(m_inCtx.err_no, std::string("ChainPack ") + msg + std::string(" at pos: ") + std::to_string(err_pos) + " near to:\n" + buff_data_hex, err_pos); \
} \
}

namespace {
enum {exception_aborts = 0};
}

ChainPackReader::ChainPackReader(std::istream &in)
: Super(in)
{
}

void ChainPackReader::throwParseException(const std::string &msg)
{
std::array<char, 64> buff;
auto err_pos = m_in.tellg();
auto l = m_in.readsome(buff.data(), buff.size() - 1);
buff[l] = 0;
auto dump = shv::chainpack::utils::hexDump(buff.data(), l);

std::string msg2 = m_inCtx.err_msg? m_inCtx.err_msg: "";
if (!msg2.empty() && !msg.empty())
msg2 += " - ";
msg2 += msg;

throw ParseException(m_inCtx.err_no, msg2, err_pos, dump);
}

ChainPackReader &ChainPackReader::operator >>(RpcValue &value)
{
read(value);
Expand All @@ -49,7 +44,7 @@ ChainPackReader::ItemType ChainPackReader::unpackNext()
{
cchainpack_unpack_next(&m_inCtx);
if(m_inCtx.err_no != CCPCP_RC_OK)
PARSE_EXCEPTION("Parse error: " + std::string(m_inCtx.err_msg) + " error code: " + std::to_string(m_inCtx.err_no));
throwParseException();
return m_inCtx.item.type;
}

Expand All @@ -62,7 +57,7 @@ ChainPackReader::ItemType ChainPackReader::peekNext()
{
const char *p = ccpcp_unpack_peek_byte(&m_inCtx);
if(!p)
PARSE_EXCEPTION("Parse error: " + std::string(m_inCtx.err_msg) + " error code: " + std::to_string(m_inCtx.err_no));
throwParseException();
auto sch = static_cast<cchainpack_pack_packing_schema>(static_cast<uint8_t>(*p));
switch(sch) {
case CP_Null: return CCPCP_ITEM_NULL;
Expand Down Expand Up @@ -137,7 +132,7 @@ void ChainPackReader::read(RpcValue &val)
break;
unpackNext();
if(m_inCtx.item.type != CCPCP_ITEM_STRING)
PARSE_EXCEPTION("Unfinished string");
throwParseException("Unfinished string");
}
val = str;
break;
Expand All @@ -151,7 +146,7 @@ void ChainPackReader::read(RpcValue &val)
break;
unpackNext();
if(m_inCtx.item.type != CCPCP_ITEM_BLOB)
PARSE_EXCEPTION("Unfinished blob");
throwParseException("Unfinished blob");
}
val = blob;
break;
Expand Down Expand Up @@ -183,11 +178,11 @@ void ChainPackReader::read(RpcValue &val)
break;
}
default:
PARSE_EXCEPTION("Invalid type.");
throwParseException("Invalid type.");
}
if(!md.isEmpty()) {
if(!val.isValid())
PARSE_EXCEPTION("Attempt to set metadata to invalid RPC value.");
throwParseException("Attempt to set metadata to invalid RPC value.");
val.setMetaData(std::move(md));
}
}
Expand Down
47 changes: 18 additions & 29 deletions libshvchainpack/src/chainpack/cponreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,28 @@

#include <iostream>
#include <fstream>
#include <cmath>
#include <array>

namespace shv::chainpack {

#define PARSE_EXCEPTION(msg) {\
std::array<char, 40> buff; \
auto err_pos = m_in.tellg(); \
auto l = m_in.readsome(buff.data(), buff.size() - 1); \
buff[l] = 0; \
if(exception_aborts) { \
std::clog << __FILE__ << ':' << __LINE__; \
std::clog << ' ' << (msg) << " at pos: " << err_pos << " near to: " << buff.data() << std::endl; \
abort(); \
} \
else { \
throw ParseException(m_inCtx.err_no, std::string("Cpon ") \
+ msg \
+ std::string(" at pos: ") + std::to_string(err_pos) \
+ std::string(" line: ") + std::to_string(m_inCtx.parser_line_no) \
+ " near to: " + buff.data(), err_pos); \
} \
}

namespace {
enum {exception_aborts = 0};
}

CponReader::CponReader(std::istream &in)
: Super(in)
{
}

void CponReader::throwParseException(const std::string &msg)
{
std::array<char, 64> buff;
auto err_pos = m_in.tellg();
auto l = m_in.readsome(buff.data(), buff.size() - 1);
buff[l] = 0;
std::string msg2 = m_inCtx.err_msg? m_inCtx.err_msg: "";
if (!msg2.empty() && !msg.empty())
msg2 += " - ";
msg2 += msg;
throw ParseException(m_inCtx.err_no, msg2, err_pos, std::string(buff.data(), buff.size()));
}

CponReader &CponReader::operator >>(RpcValue &value)
{
read(value);
Expand All @@ -53,7 +42,7 @@ void CponReader::unpackNext()
{
ccpon_unpack_next(&m_inCtx);
if(m_inCtx.err_no != CCPCP_RC_OK)
PARSE_EXCEPTION("Parse error: " + std::to_string(m_inCtx.err_no) + " " + ccpcp_error_string(m_inCtx.err_no) + " - " + std::string(m_inCtx.err_msg));
throwParseException(m_inCtx.err_msg);
}

void CponReader::read(RpcValue &val)
Expand Down Expand Up @@ -96,7 +85,7 @@ void CponReader::read(RpcValue &val)
break;
unpackNext();
if(m_inCtx.item.type != CCPCP_ITEM_BLOB)
PARSE_EXCEPTION("Unfinished blob key");
throwParseException("Unfinished blob key");
}
val = RpcValue(blob);
break;
Expand All @@ -110,7 +99,7 @@ void CponReader::read(RpcValue &val)
break;
unpackNext();
if(m_inCtx.item.type != CCPCP_ITEM_STRING)
PARSE_EXCEPTION("Unfinished string key");
throwParseException("Unfinished string key");
}
val = str;
break;
Expand Down Expand Up @@ -142,11 +131,11 @@ void CponReader::read(RpcValue &val)
break;
}
default:
PARSE_EXCEPTION("Invalid type.");
throwParseException("Invalid type.");
}
if(!md.isEmpty()) {
if(!val.isValid())
PARSE_EXCEPTION(std::string("Attempt to set metadata to invalid RPC value. error - ") + m_inCtx.err_msg);
throwParseException(std::string("Attempt to set metadata to invalid RPC value. error - ") + m_inCtx.err_msg);
val.setMetaData(std::move(md));
}
}
Expand Down
4 changes: 2 additions & 2 deletions libshvchainpack/src/chainpack/rpcmessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ RpcFrame RpcFrame::fromChainPack(std::string &&frame_data)
RpcValue::MetaData meta;
rd.read(meta);
if(meta.isEmpty())
throw ParseException(CCPCP_RC_MALFORMED_INPUT, "Metadata missing", -1);
throw ParseException(CCPCP_RC_MALFORMED_INPUT, "Metadata missing", -1, {});
auto pos = in.tellg();
if(pos < 0)
throw ParseException(CCPCP_RC_MALFORMED_INPUT, "Metadata missing", -1);
throw ParseException(CCPCP_RC_MALFORMED_INPUT, "Metadata missing", -1, {});
auto data = std::string(frame_data, pos);
return RpcFrame(std::move(meta), std::move(data));
}
Expand Down
3 changes: 1 addition & 2 deletions libshvchainpack/tests/test_chainpack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,6 @@ DOCTEST_TEST_CASE("ChainPack")
DOCTEST_SUBCASE("fuzzing tests")
{
std::array<uint8_t, 15> input{0x8b, 0x8b, 0x0, 0x8d, 0xf6, 0xff, 0xff, 0xff, 0xff, 0x0, 0x8b, 0x0, 0x8e, 0x0, 0x0};
REQUIRE_THROWS_WITH_AS(shv::chainpack::RpcValue::fromChainPack({reinterpret_cast<const char*>(input.data()), input.size()}), "ChainPack Parse error: error code: 4 at pos: 15 near to:\n", ParseException);
REQUIRE_THROWS_WITH_AS(shv::chainpack::RpcValue::fromChainPack({reinterpret_cast<const char*>(input.data()), input.size()}), "Parse error: 3 BUFFER_UNDERFLOW at pos: -1 - near to:\n", ParseException);
}

}
Loading

0 comments on commit f548d74

Please sign in to comment.