Skip to content
Merged
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
109 changes: 100 additions & 9 deletions data/include/join/cbor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,89 @@ namespace join
/**
* @brief CBOR serialization mode.
*/
enum class CborWriteMode
enum class CborWriteMode : uint32_t
{
Definite, /**< containers written with their length. */
Indefinite, /**< containers written with an indefinite length. */
Definite = 0, /**< containers written with their length. */
Indefinite = 1L << 0, /**< containers written with an indefinite length. */
ValidateEncoding = 1L << 1, /**< validate UTF-8 encoding of text strings. */
};

/**
* @brief perform binary AND on CborWriteMode.
* @param a bitset.
* @param b other bitset.
* @return bitset result of binary AND on CborWriteMode.
*/
constexpr CborWriteMode operator& (CborWriteMode a, CborWriteMode b) noexcept
{
return CborWriteMode (static_cast<uint32_t> (a) & static_cast<uint32_t> (b));
}

/**
* @brief perform binary OR on CborWriteMode.
* @param a bitset.
* @param b other bitset.
* @return bitset result of binary OR on CborWriteMode.
*/
constexpr CborWriteMode operator| (CborWriteMode a, CborWriteMode b) noexcept
{
return CborWriteMode (static_cast<uint32_t> (a) | static_cast<uint32_t> (b));
}

/**
* @brief perform binary XOR on CborWriteMode.
* @param a bitset.
* @param b other bitset.
* @return bitset result of binary XOR on CborWriteMode.
*/
constexpr CborWriteMode operator^ (CborWriteMode a, CborWriteMode b) noexcept
{
return CborWriteMode (static_cast<uint32_t> (a) ^ static_cast<uint32_t> (b));
}

/**
* @brief perform binary NOT on CborWriteMode.
* @param a bitset.
* @return bitset result of binary NOT on CborWriteMode.
*/
constexpr CborWriteMode operator~(CborWriteMode a) noexcept
{
return CborWriteMode (~static_cast<uint32_t> (a));
}

/**
* @brief perform binary AND assignment on CborWriteMode.
* @param a bitset.
* @param b other bitset.
* @return reference to updated bitset.
*/
constexpr const CborWriteMode& operator&= (CborWriteMode& a, CborWriteMode b) noexcept
{
return a = a & b;
}

/**
* @brief perform binary OR assignment on CborWriteMode.
* @param a bitset.
* @param b other bitset.
* @return reference to updated bitset.
*/
constexpr const CborWriteMode& operator|= (CborWriteMode& a, CborWriteMode b) noexcept
{
return a = a | b;
}

/**
* @brief perform binary XOR assignment on CborWriteMode.
* @param a bitset.
* @param b other bitset.
* @return reference to updated bitset.
*/
constexpr const CborWriteMode& operator^= (CborWriteMode& a, CborWriteMode b) noexcept
{
return a = a ^ b;
}

/**
* @brief CBOR writer class.
*/
Expand Down Expand Up @@ -223,6 +300,16 @@ namespace join
*/
int setString (std::string_view value)
{
if constexpr ((WriteMode & CborWriteMode::ValidateEncoding) != CborWriteMode::Definite)
{
if (JOIN_UNLIKELY (utf8::validate (value.data (), value.data () + value.size ()) == -1))
{
join::lastError = make_error_code (SaxErrc::InvalidEncoding);
reset ();
return -1;
}
}

writeHead (0x60, value.size ());
append (value.data (), value.size ());
endValue ();
Expand All @@ -249,7 +336,7 @@ namespace join
*/
int startArray ()
{
static_assert (WriteMode == CborWriteMode::Indefinite, "definite length containers require a size");
static_assert (_indefinite, "definite length containers require a size");
append (0x9f);
++_depth;
return 0;
Expand All @@ -262,7 +349,7 @@ namespace join
*/
int startArray ([[maybe_unused]] uint32_t size)
{
if constexpr (WriteMode == CborWriteMode::Indefinite)
if constexpr (_indefinite)
{
append (0x9f);
}
Expand All @@ -286,7 +373,7 @@ namespace join
reset ();
return -1;
}
if constexpr (WriteMode == CborWriteMode::Indefinite)
if constexpr (_indefinite)
{
append (0xff);
}
Expand All @@ -301,7 +388,7 @@ namespace join
*/
int startObject ()
{
static_assert (WriteMode == CborWriteMode::Indefinite, "definite length containers require a size");
static_assert (_indefinite, "definite length containers require a size");
append (0xbf);
++_depth;
return 0;
Expand All @@ -314,7 +401,7 @@ namespace join
*/
int startObject ([[maybe_unused]] uint32_t size)
{
if constexpr (WriteMode == CborWriteMode::Indefinite)
if constexpr (_indefinite)
{
append (0xbf);
}
Expand Down Expand Up @@ -444,7 +531,7 @@ namespace join
reset ();
return -1;
}
if constexpr (WriteMode == CborWriteMode::Indefinite)
if constexpr (_indefinite)
{
append (0xff);
}
Expand Down Expand Up @@ -577,6 +664,10 @@ namespace join

/// number of open containers.
size_t _depth = 0;

private:
/// containers written with an indefinite length.
static constexpr bool _indefinite = (WriteMode & CborWriteMode::Indefinite) != CborWriteMode::Definite;
};

/// CBOR writer with definite length containers.
Expand Down
22 changes: 22 additions & 0 deletions data/tests/cbor_stream_writer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,21 @@ TEST (CborStreamWriter, setString)
stream.str ("");
EXPECT_EQ (cborStreamWriter.setString (std::string (65536, 'x')), 0);
EXPECT_EQ (stream.str (), std::string ({'\x7A', '\x00', '\x01', '\x00', '\x00'}) + std::string (65536, 'x'));

stream.str ("");
EXPECT_EQ (cborStreamWriter.setString ("\xff"), 0);
EXPECT_EQ (stream.str (), std::string ({'\x61', '\xFF'}));

join::BasicCborWriter<join::CborWriteMode::Indefinite | join::CborWriteMode::ValidateEncoding> validator (stream);

stream.str ("");
EXPECT_EQ (validator.setString ("caf\xc3\xa9"), 0);
EXPECT_EQ (stream.str (), std::string ({'\x65', 'c', 'a', 'f', '\xC3', '\xA9'}));

stream.str ("");
EXPECT_EQ (validator.setString ("\xff"), -1);
EXPECT_EQ (join::lastError, join::SaxErrc::InvalidEncoding);
EXPECT_TRUE (stream.str ().empty ());
}

/**
Expand Down Expand Up @@ -445,6 +460,13 @@ TEST (CborStreamWriter, setKey)

EXPECT_EQ (key (std::span<const uint8_t> (bytes, sizeof (bytes)), -1), 0);
EXPECT_EQ (stream.str ().substr (1), std::string ({'\x42', '\x00', '\xFF'}));

join::BasicCborWriter<join::CborWriteMode::Indefinite | join::CborWriteMode::ValidateEncoding> validator (stream);
stream.str ("");
validator.startObject (1);
EXPECT_EQ (validator.setKey (std::string_view ("\xff")), -1);
EXPECT_EQ (join::lastError, join::SaxErrc::InvalidEncoding);
EXPECT_TRUE (stream.str ().empty ());
}

/**
Expand Down
22 changes: 22 additions & 0 deletions data/tests/cbor_writer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,21 @@ TEST (CborWriter, setString)
stream.str ("");
EXPECT_EQ (cborWriter.setString (std::string (65536, 'x')), 0);
EXPECT_EQ (stream.str (), std::string ({'\x7A', '\x00', '\x01', '\x00', '\x00'}) + std::string (65536, 'x'));

stream.str ("");
EXPECT_EQ (cborWriter.setString ("\xff"), 0);
EXPECT_EQ (stream.str (), std::string ({'\x61', '\xFF'}));

join::BasicCborWriter<join::CborWriteMode::ValidateEncoding> validator (stream);

stream.str ("");
EXPECT_EQ (validator.setString ("caf\xc3\xa9"), 0);
EXPECT_EQ (stream.str (), std::string ({'\x65', 'c', 'a', 'f', '\xC3', '\xA9'}));

stream.str ("");
EXPECT_EQ (validator.setString ("\xff"), -1);
EXPECT_EQ (join::lastError, join::SaxErrc::InvalidEncoding);
EXPECT_TRUE (stream.str ().empty ());
}

/**
Expand Down Expand Up @@ -435,6 +450,13 @@ TEST (CborWriter, setKey)

EXPECT_EQ (key (std::span<const uint8_t> (bytes, sizeof (bytes)), -1), 0);
EXPECT_EQ (stream.str ().substr (1), std::string ({'\x42', '\x00', '\xFF'}));

join::BasicCborWriter<join::CborWriteMode::ValidateEncoding> validator (stream);
stream.str ("");
validator.startObject (1);
EXPECT_EQ (validator.setKey (std::string_view ("\xff")), -1);
EXPECT_EQ (join::lastError, join::SaxErrc::InvalidEncoding);
EXPECT_TRUE (stream.str ().empty ());
}

/**
Expand Down
Loading