Add support for signed/bool to BufferReader/Writer (#27637)
* Add support for signed/bool to BufferReader/Writer
Problem:
- Safe buffer reader/writer doesn't support signed values
- Safe buffer reader/writer doesn't support bools
- Fixes #27629
This PR:
- Adds signed and bool support to BufferWriter and to Reader
- Adds missing unit tests
- Makes it clear that low-level reads are not to be used directly
Testing done:
- Integration tests pass
- New unit tests added and pass
- Existing unit tests all pass
* Restyled by clang-format
---------
Co-authored-by: tennessee.carmelveilleux@gmail.com <tennessee@google.com>
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/src/lib/core/CHIPEncoding.h b/src/lib/core/CHIPEncoding.h
index c1a6783..09079b7 100644
--- a/src/lib/core/CHIPEncoding.h
+++ b/src/lib/core/CHIPEncoding.h
@@ -216,6 +216,13 @@
template <typename T>
inline T HostSwap(T v);
+// For completeness of the set, we have identity.
+template <>
+inline uint8_t HostSwap<uint8_t>(uint8_t v)
+{
+ return v;
+}
+
/**
* This conditionally performs, as necessary for the target system, a
* byte order swap by value of the specified 16-bit value, presumed to
diff --git a/src/lib/support/BufferReader.cpp b/src/lib/support/BufferReader.cpp
index 3e9ede6..0d21477 100644
--- a/src/lib/support/BufferReader.cpp
+++ b/src/lib/support/BufferReader.cpp
@@ -17,40 +17,46 @@
#include "BufferReader.h"
+#include <lib/core/CHIPEncoding.h>
+
#include <string.h>
+#include <type_traits>
namespace chip {
namespace Encoding {
namespace LittleEndian {
namespace {
-// These helper methods return void and put the value being read into an
+
+// This helper methods return void and put the value being read into an
// outparam because that allows us to easily overload on the type of the
// thing being read.
-void ReadHelper(const uint8_t *& p, uint8_t * dest)
+void ReadHelper(const uint8_t * p, bool * dest)
{
- *dest = Read8(p);
+ *dest = (*p != 0);
}
-void ReadHelper(const uint8_t *& p, uint16_t * dest)
+
+template <typename T>
+void ReadHelper(const uint8_t * p, T * dest)
{
- *dest = Read16(p);
+ std::make_unsigned_t<T> result;
+ memcpy(&result, p, sizeof(result));
+ result = chip::Encoding::LittleEndian::HostSwap(result);
+
+ *dest = static_cast<T>(result);
}
-void ReadHelper(const uint8_t *& p, uint32_t * dest)
-{
- *dest = Read32(p);
-}
-void ReadHelper(const uint8_t *& p, uint64_t * dest)
-{
- *dest = Read64(p);
-}
+
} // anonymous namespace
template <typename T>
-void Reader::RawRead(T * retval)
+void Reader::RawReadLowLevelBeCareful(T * retval)
{
static_assert(CHAR_BIT == 8, "Our various sizeof checks rely on bytes and octets being the same thing");
+ static_assert((-1 & 3) == 3, "LittleEndian::BufferReader only works with 2's complement architectures.");
- static constexpr size_t data_size = sizeof(T);
+ VerifyOrReturn(IsSuccess());
+
+ constexpr size_t data_size = sizeof(T);
if (mAvailable < data_size)
{
@@ -61,6 +67,8 @@
}
ReadHelper(mReadPtr, retval);
+ mReadPtr += data_size;
+
mAvailable = static_cast<uint16_t>(mAvailable - data_size);
}
@@ -84,10 +92,15 @@
}
// Explicit Read instantiations for the data types we want to support.
-template void Reader::RawRead(uint8_t *);
-template void Reader::RawRead(uint16_t *);
-template void Reader::RawRead(uint32_t *);
-template void Reader::RawRead(uint64_t *);
+template void Reader::RawReadLowLevelBeCareful(bool *);
+template void Reader::RawReadLowLevelBeCareful(int8_t *);
+template void Reader::RawReadLowLevelBeCareful(int16_t *);
+template void Reader::RawReadLowLevelBeCareful(int32_t *);
+template void Reader::RawReadLowLevelBeCareful(int64_t *);
+template void Reader::RawReadLowLevelBeCareful(uint8_t *);
+template void Reader::RawReadLowLevelBeCareful(uint16_t *);
+template void Reader::RawReadLowLevelBeCareful(uint32_t *);
+template void Reader::RawReadLowLevelBeCareful(uint64_t *);
} // namespace LittleEndian
} // namespace Encoding
diff --git a/src/lib/support/BufferReader.h b/src/lib/support/BufferReader.h
index bbf21f5..14c251b 100644
--- a/src/lib/support/BufferReader.h
+++ b/src/lib/support/BufferReader.h
@@ -91,6 +91,28 @@
CHIP_ERROR StatusCode() const { return mStatus; }
/**
+ * @return false if the reader is in error, true if the reader is OK.
+ */
+ bool IsSuccess() const { return StatusCode() == CHIP_NO_ERROR; }
+
+ /**
+ * Read a bool, assuming single byte storage.
+ *
+ * @param [out] dest Where the 8-bit integer goes.
+ *
+ * @note The read can put the reader in a failed-status state if there are
+ * not enough octets available. Callers must either continue to do
+ * more reads on the return value or check its status to see whether
+ * the sequence of reads that has been performed succeeded.
+ */
+ CHECK_RETURN_VALUE
+ Reader & ReadBool(bool * dest)
+ {
+ RawReadLowLevelBeCareful(dest);
+ return *this;
+ }
+
+ /**
* Read a single 8-bit unsigned integer.
*
* @param [out] dest Where the 8-bit integer goes.
@@ -103,7 +125,7 @@
CHECK_RETURN_VALUE
Reader & Read8(uint8_t * dest)
{
- RawRead(dest);
+ RawReadLowLevelBeCareful(dest);
return *this;
}
@@ -120,7 +142,7 @@
CHECK_RETURN_VALUE
Reader & Read16(uint16_t * dest)
{
- RawRead(dest);
+ RawReadLowLevelBeCareful(dest);
return *this;
}
@@ -137,7 +159,7 @@
CHECK_RETURN_VALUE
Reader & Read32(uint32_t * dest)
{
- RawRead(dest);
+ RawReadLowLevelBeCareful(dest);
return *this;
}
@@ -154,7 +176,75 @@
CHECK_RETURN_VALUE
Reader & Read64(uint64_t * dest)
{
- RawRead(dest);
+ RawReadLowLevelBeCareful(dest);
+ return *this;
+ }
+
+ /**
+ * Read a single 8-bit signed integer.
+ *
+ * @param [out] dest Where the 8-bit integer goes.
+ *
+ * @note The read can put the reader in a failed-status state if there are
+ * not enough octets available. Callers must either continue to do
+ * more reads on the return value or check its status to see whether
+ * the sequence of reads that has been performed succeeded.
+ */
+ CHECK_RETURN_VALUE
+ Reader & ReadSigned8(int8_t * dest)
+ {
+ RawReadLowLevelBeCareful(dest);
+ return *this;
+ }
+
+ /**
+ * Read a single 16-bit signed integer.
+ *
+ * @param [out] dest Where the 16-bit integer goes.
+ *
+ * @note The read can put the reader in a failed-status state if there are
+ * not enough octets available. Callers must either continue to do
+ * more reads on the return value or check its status to see whether
+ * the sequence of reads that has been performed succeeded.
+ */
+ CHECK_RETURN_VALUE
+ Reader & ReadSigned16(int16_t * dest)
+ {
+ RawReadLowLevelBeCareful(dest);
+ return *this;
+ }
+
+ /**
+ * Read a single 32-bit signed integer.
+ *
+ * @param [out] dest Where the 32-bit integer goes.
+ *
+ * @note The read can put the reader in a failed-status state if there are
+ * not enough octets available. Callers must either continue to do
+ * more reads on the return value or check its status to see whether
+ * the sequence of reads that has been performed succeeded.
+ */
+ CHECK_RETURN_VALUE
+ Reader & ReadSigned32(int32_t * dest)
+ {
+ RawReadLowLevelBeCareful(dest);
+ return *this;
+ }
+
+ /**
+ * Read a single 64-bit signed integer.
+ *
+ * @param [out] dest Where the 64-bit integer goes.
+ *
+ * @note The read can put the reader in a failed-status state if there are
+ * not enough octets available. Callers must either continue to do
+ * more reads on the return value or check its status to see whether
+ * the sequence of reads that has been performed succeeded.
+ */
+ CHECK_RETURN_VALUE
+ Reader & ReadSigned64(int64_t * dest)
+ {
+ RawReadLowLevelBeCareful(dest);
return *this;
}
@@ -180,7 +270,7 @@
* delegate to this one.
*/
template <typename T>
- void RawRead(T * retval);
+ void RawReadLowLevelBeCareful(T * retval);
/**
* Advance the Reader forward by the specified number of octets.
diff --git a/src/lib/support/BufferWriter.cpp b/src/lib/support/BufferWriter.cpp
index e939470..c606cba 100644
--- a/src/lib/support/BufferWriter.cpp
+++ b/src/lib/support/BufferWriter.cpp
@@ -60,6 +60,17 @@
return *this;
}
+LittleEndian::BufferWriter & LittleEndian::BufferWriter::EndianPutSigned(int64_t x, size_t size)
+{
+ while (size > 0)
+ {
+ Put(static_cast<uint8_t>(x & 0xff));
+ x >>= 8;
+ size--;
+ }
+ return *this;
+}
+
BigEndian::BufferWriter & BigEndian::BufferWriter::EndianPut(uint64_t x, size_t size)
{
while (size-- > 0)
@@ -69,5 +80,14 @@
return *this;
}
+BigEndian::BufferWriter & BigEndian::BufferWriter::EndianPutSigned(int64_t x, size_t size)
+{
+ while (size-- > 0)
+ {
+ Put(static_cast<uint8_t>((x >> (size * 8)) & 0xff));
+ }
+ return *this;
+}
+
} // namespace Encoding
} // namespace chip
diff --git a/src/lib/support/BufferWriter.h b/src/lib/support/BufferWriter.h
index a8afee2..72f50b3 100644
--- a/src/lib/support/BufferWriter.h
+++ b/src/lib/support/BufferWriter.h
@@ -104,13 +104,18 @@
Derived & Put(uint8_t c) { return static_cast<Derived &>(BufferWriter::Put(c)); }
Derived & Skip(size_t len) { return static_cast<Derived &>(BufferWriter::Skip(len)); }
- // write an integer into a buffer, in an endian specific way
+ // write an integer into a buffer, in an endian-specific way
Derived & Put8(uint8_t c) { return static_cast<Derived *>(this)->Put(c); }
Derived & Put16(uint16_t x) { return static_cast<Derived *>(this)->EndianPut(x, sizeof(x)); }
Derived & Put32(uint32_t x) { return static_cast<Derived *>(this)->EndianPut(x, sizeof(x)); }
Derived & Put64(uint64_t x) { return static_cast<Derived *>(this)->EndianPut(x, sizeof(x)); }
+ Derived & PutSigned8(int8_t x) { return static_cast<Derived *>(this)->EndianPutSigned(x, sizeof(x)); }
+ Derived & PutSigned16(int16_t x) { return static_cast<Derived *>(this)->EndianPutSigned(x, sizeof(x)); }
+ Derived & PutSigned32(int32_t x) { return static_cast<Derived *>(this)->EndianPutSigned(x, sizeof(x)); }
+ Derived & PutSigned64(int64_t x) { return static_cast<Derived *>(this)->EndianPutSigned(x, sizeof(x)); }
+
protected:
EndianBufferWriterBase(uint8_t * buf, size_t len) : BufferWriter(buf, len) {}
EndianBufferWriterBase(MutableByteSpan buf) : BufferWriter(buf.data(), buf.size()) {}
@@ -123,11 +128,15 @@
class BufferWriter : public EndianBufferWriterBase<BufferWriter>
{
public:
- BufferWriter(uint8_t * buf, size_t len) : EndianBufferWriterBase<BufferWriter>(buf, len) {}
+ BufferWriter(uint8_t * buf, size_t len) : EndianBufferWriterBase<BufferWriter>(buf, len)
+ {
+ static_assert((-1 & 3) == 3, "LittleEndian::BufferWriter only works with 2's complement architectures.");
+ }
BufferWriter(MutableByteSpan buf) : EndianBufferWriterBase<BufferWriter>(buf) {}
BufferWriter(const BufferWriter & other) = default;
BufferWriter & operator=(const BufferWriter & other) = default;
BufferWriter & EndianPut(uint64_t x, size_t size);
+ BufferWriter & EndianPutSigned(int64_t x, size_t size);
};
} // namespace LittleEndian
@@ -137,11 +146,15 @@
class BufferWriter : public EndianBufferWriterBase<BufferWriter>
{
public:
- BufferWriter(uint8_t * buf, size_t len) : EndianBufferWriterBase<BufferWriter>(buf, len) {}
+ BufferWriter(uint8_t * buf, size_t len) : EndianBufferWriterBase<BufferWriter>(buf, len)
+ {
+ static_assert((-1 & 3) == 3, "BigEndian::BufferWriter only works with 2's complement architectures.");
+ }
BufferWriter(MutableByteSpan buf) : EndianBufferWriterBase<BufferWriter>(buf) {}
BufferWriter(const BufferWriter & other) = default;
BufferWriter & operator=(const BufferWriter & other) = default;
BufferWriter & EndianPut(uint64_t x, size_t size);
+ BufferWriter & EndianPutSigned(int64_t x, size_t size);
};
} // namespace BigEndian
diff --git a/src/lib/support/tests/TestBufferReader.cpp b/src/lib/support/tests/TestBufferReader.cpp
index ea501ee..9af64c9 100644
--- a/src/lib/support/tests/TestBufferReader.cpp
+++ b/src/lib/support/tests/TestBufferReader.cpp
@@ -133,12 +133,139 @@
NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
}
+static void TestBufferReader_LittleEndianScalars(nlTestSuite * inSuite, void * inContext)
+{
+ const uint8_t test_buf1[10] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 };
+
+ // Unsigned 8 bits reads
+ {
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf1 } };
+ uint8_t val1 = 0;
+ uint8_t val2 = 0;
+ NL_TEST_ASSERT(inSuite, reader.Read8(&val1).Read8(&val2).IsSuccess());
+ NL_TEST_ASSERT(inSuite, val1 == 0xfe);
+ NL_TEST_ASSERT(inSuite, val2 == 0xff);
+ }
+
+ // Unsigned 16 bits reads
+ {
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf1 } };
+ uint16_t val1 = 0;
+ uint16_t val2 = 0;
+ NL_TEST_ASSERT(inSuite, reader.Read16(&val1).Read16(&val2).IsSuccess());
+ NL_TEST_ASSERT(inSuite, val1 == 0xfffe);
+ NL_TEST_ASSERT(inSuite, val2 == 0xffff);
+ }
+
+ // Unsigned 32 bits reads
+ {
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf1 } };
+ uint32_t val1 = 0;
+ uint32_t val2 = 0;
+ NL_TEST_ASSERT(inSuite, reader.Read32(&val1).Read32(&val2).IsSuccess());
+ NL_TEST_ASSERT(inSuite, val1 == static_cast<uint32_t>(0xfffffffeUL));
+ NL_TEST_ASSERT(inSuite, val2 == static_cast<uint32_t>(0xffffffffUL));
+ }
+
+ // Unsigned 32 bits reads, unaligned
+ {
+ uint8_t test_buf2[10] = { 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 };
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf2 } };
+
+ uint32_t val1 = 0;
+ uint32_t val2 = 0;
+ NL_TEST_ASSERT(inSuite, reader.Skip(1).Read32(&val1).Read32(&val2).IsSuccess());
+ NL_TEST_ASSERT(inSuite, reader.Remaining() == 1);
+ NL_TEST_ASSERT(inSuite, val1 == static_cast<uint32_t>(0xfffffffeUL));
+ NL_TEST_ASSERT(inSuite, val2 == static_cast<uint32_t>(0xffffffffUL));
+ }
+
+ // Unsigned 64 bits read
+ {
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf1 } };
+ uint64_t val = 0;
+ NL_TEST_ASSERT(inSuite, reader.Read64(&val).IsSuccess());
+ NL_TEST_ASSERT(inSuite, reader.Remaining() == 2);
+ NL_TEST_ASSERT(inSuite, val == static_cast<uint64_t>(0xfffffffffffffffeULL));
+ }
+
+ // Signed 8 bits reads
+ {
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf1 } };
+ int8_t val1 = 0;
+ int8_t val2 = 0;
+ NL_TEST_ASSERT(inSuite, reader.ReadSigned8(&val1).ReadSigned8(&val2).IsSuccess());
+ NL_TEST_ASSERT(inSuite, val1 == -2);
+ NL_TEST_ASSERT(inSuite, val2 == -1);
+ }
+
+ // Signed 16 bits reads
+ {
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf1 } };
+ int16_t val1 = 0;
+ int16_t val2 = 0;
+ NL_TEST_ASSERT(inSuite, reader.ReadSigned16(&val1).ReadSigned16(&val2).IsSuccess());
+ NL_TEST_ASSERT(inSuite, val1 == -2);
+ NL_TEST_ASSERT(inSuite, val2 == -1);
+ }
+
+ // Signed 32 bits reads
+ {
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf1 } };
+ int32_t val1 = 0;
+ int32_t val2 = 0;
+ NL_TEST_ASSERT(inSuite, reader.ReadSigned32(&val1).ReadSigned32(&val2).IsSuccess());
+ NL_TEST_ASSERT(inSuite, val1 == -2);
+ NL_TEST_ASSERT(inSuite, val2 == -1);
+ }
+
+ // Signed 32 bits reads, unaligned
+ {
+ uint8_t test_buf2[10] = { 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 };
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf2 } };
+
+ int32_t val1 = 0;
+ int32_t val2 = 0;
+ NL_TEST_ASSERT(inSuite, reader.Skip(1).ReadSigned32(&val1).ReadSigned32(&val2).IsSuccess());
+ NL_TEST_ASSERT(inSuite, reader.Remaining() == 1);
+ NL_TEST_ASSERT(inSuite, val1 == static_cast<int32_t>(-2L));
+ NL_TEST_ASSERT(inSuite, val2 == static_cast<int32_t>(-1L));
+ }
+
+ // Signed 64 bits read
+ {
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf1 } };
+ int64_t val = 0;
+ NL_TEST_ASSERT(inSuite, reader.ReadSigned64(&val).IsSuccess());
+ NL_TEST_ASSERT(inSuite, reader.Remaining() == 2);
+ NL_TEST_ASSERT(inSuite, val == static_cast<int64_t>(-2LL));
+ }
+
+ // Bools
+ {
+ uint8_t test_buf2[5] = { 0x00, 0xff, 0x01, 0x04, 0x07 };
+ chip::Encoding::LittleEndian::Reader reader{ ByteSpan{ test_buf2 } };
+ bool val1 = true;
+ bool val2 = false;
+ bool val3 = false;
+
+ NL_TEST_ASSERT(inSuite, reader.ReadBool(&val1).ReadBool(&val2).ReadBool(&val3).IsSuccess());
+ NL_TEST_ASSERT(inSuite, reader.Remaining() == 2);
+ NL_TEST_ASSERT(inSuite, val1 == false);
+ NL_TEST_ASSERT(inSuite, val2 == true);
+ NL_TEST_ASSERT(inSuite, val3 == true);
+ }
+}
+
#define NL_TEST_DEF_FN(fn) NL_TEST_DEF("Test " #fn, fn)
/**
* Test Suite. It lists all the test functions.
*/
-static const nlTest sTests[] = { NL_TEST_DEF_FN(TestBufferReader_Basic), NL_TEST_DEF_FN(TestBufferReader_BasicSpan),
- NL_TEST_DEF_FN(TestBufferReader_Saturation), NL_TEST_DEF_FN(TestBufferReader_Skip),
+static const nlTest sTests[] = { NL_TEST_DEF_FN(TestBufferReader_Basic),
+ NL_TEST_DEF_FN(TestBufferReader_BasicSpan),
+ NL_TEST_DEF_FN(TestBufferReader_Saturation),
+ NL_TEST_DEF_FN(TestBufferReader_Skip),
+ NL_TEST_DEF("Test Little-endian buffer Reader scalar reads", TestBufferReader_LittleEndianScalars),
NL_TEST_SENTINEL() };
int TestBufferReader()
diff --git a/src/lib/support/tests/TestBufferWriter.cpp b/src/lib/support/tests/TestBufferWriter.cpp
index 53a5012..2e8d187 100644
--- a/src/lib/support/tests/TestBufferWriter.cpp
+++ b/src/lib/support/tests/TestBufferWriter.cpp
@@ -196,6 +196,42 @@
bb.EndianPut(0x0102030405060708u, 3);
NL_TEST_ASSERT(inSuite, bb.expect("\x08\x07\x06", 3, 0));
}
+
+ {
+ BWTest<LittleEndian::BufferWriter> bb(4);
+ bb.PutSigned8(static_cast<int8_t>(-6));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xfa", 1, 3));
+ }
+
+ {
+ BWTest<LittleEndian::BufferWriter> bb(4);
+ bb.PutSigned16(static_cast<int16_t>(-2));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xfe\xff", 2, 2));
+ }
+
+ {
+ BWTest<LittleEndian::BufferWriter> bb(4);
+ bb.PutSigned32(static_cast<int32_t>(-2));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xfe\xff\xff\xff", 4, 0));
+ }
+
+ {
+ BWTest<LittleEndian::BufferWriter> bb(8);
+ bb.PutSigned64(static_cast<int64_t>(-2));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xfe\xff\xff\xff\xff\xff\xff\xff", 8, 0));
+ }
+
+ {
+ BWTest<LittleEndian::BufferWriter> bb(7);
+ bb.PutSigned64(static_cast<int64_t>(-2));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xfe\xff\xff\xff\xff\xff\xff", 8, 0));
+ }
+
+ {
+ BWTest<LittleEndian::BufferWriter> bb(9);
+ bb.PutSigned64(static_cast<int64_t>(9223372036854775807LL));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xff\xff\xff\xff\xff\xff\xff\x7f", 8, 1));
+ }
}
void TestPutBigEndian(nlTestSuite * inSuite, void * inContext)
@@ -223,6 +259,42 @@
bb.EndianPut(0x0102030405060708u, 3);
NL_TEST_ASSERT(inSuite, bb.expect("\x06\x07\x08", 3, 0));
}
+
+ {
+ BWTest<BigEndian::BufferWriter> bb(4);
+ bb.PutSigned8(static_cast<int8_t>(-6));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xfa", 1, 3));
+ }
+
+ {
+ BWTest<BigEndian::BufferWriter> bb(4);
+ bb.PutSigned16(static_cast<int16_t>(-2));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xff\xfe", 2, 2));
+ }
+
+ {
+ BWTest<BigEndian::BufferWriter> bb(4);
+ bb.PutSigned32(static_cast<int32_t>(-2));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xff\xff\xff\xfe", 4, 0));
+ }
+
+ {
+ BWTest<BigEndian::BufferWriter> bb(8);
+ bb.PutSigned64(static_cast<int64_t>(-2));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xff\xff\xff\xff\xff\xff\xff\xfe", 8, 0));
+ }
+
+ {
+ BWTest<BigEndian::BufferWriter> bb(7);
+ bb.PutSigned64(static_cast<int64_t>(-2));
+ NL_TEST_ASSERT(inSuite, bb.expect("\xff\xff\xff\xff\xff\xff\xff", 8, 0));
+ }
+
+ {
+ BWTest<BigEndian::BufferWriter> bb(9);
+ bb.PutSigned64(static_cast<int64_t>(9223372036854775807LL));
+ NL_TEST_ASSERT(inSuite, bb.expect("\x7f\xff\xff\xff\xff\xff\xff\xff", 8, 1));
+ }
}
const nlTest sTests[] = {