diff --git a/lldb/include/lldb/Utility/DataEncoder.h b/lldb/include/lldb/Utility/DataEncoder.h --- a/lldb/include/lldb/Utility/DataEncoder.h +++ b/lldb/include/lldb/Utility/DataEncoder.h @@ -16,6 +16,9 @@ #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + #include #include @@ -26,21 +29,34 @@ /// An binary data encoding class. /// /// DataEncoder is a class that can encode binary data (swapping if needed) to -/// a data buffer. The data buffer can be caller owned, or can be shared data -/// that can be shared between multiple DataEncoder or DataEncoder instances. +/// a data buffer. The DataEncoder can be constructed with data that will be +/// copied into the internally owned buffer. This allows data to be modified +/// in the internal buffer. The DataEncoder object can also be constructed with +/// just a byte order and address size and data can be appended to the +/// internally owned buffer. +/// +/// Clients can get a shared pointer to the data buffer when done modifying or +/// creating the data to keep the data around after the lifetime of a +/// DataEncoder object. \see GetDataBuffer /// -/// \see DataBuffer +/// Client can get a reference to the object owned data as an array by calling +/// the GetData method. \see GetData class DataEncoder { public: /// Default constructor. /// - /// Initialize all members to a default empty state. + /// Initialize all members to a default empty state and create a empty memory + /// buffer that can be appended to. The ByteOrder and address size will be set + /// to match the current host system. DataEncoder(); - /// Construct with a buffer that is owned by the caller. + /// Construct an encoder that copies the specified data into the object owned + /// data buffer. /// - /// This constructor allows us to use data that is owned by the caller. The - /// data must stay around as long as this object is valid. + /// This constructor is designed to be used when you have a data buffer and + /// want to modify values within the buffer. A copy of the data will be made + /// in the internally owned buffer and that data can be fixed up and appended + /// to. /// /// \param[in] data /// A pointer to caller owned data. @@ -49,54 +65,37 @@ /// The length in bytes of \a data. /// /// \param[in] byte_order - /// A byte order of the data that we are extracting from. + /// A byte order for the data that will be encoded. /// /// \param[in] addr_size - /// A new address byte size value. - DataEncoder(void *data, uint32_t data_length, lldb::ByteOrder byte_order, - uint8_t addr_size); + /// A size of an address in bytes. \see PutAddress, AppendAddress + DataEncoder(const void *data, uint32_t data_length, + lldb::ByteOrder byte_order, uint8_t addr_size); - /// Construct with shared data. + /// Construct an encoder that owns a heap based memory buffer. /// - /// Copies the data shared pointer which adds a reference to the contained - /// in \a data_sp. The shared data reference is reference counted to ensure - /// the data lives as long as anyone still has a valid shared pointer to the - /// data in \a data_sp. - /// - /// \param[in] data_sp - /// A shared pointer to data. + /// This allows clients to create binary data from scratch by appending values + /// with the methods that start with "Append". /// /// \param[in] byte_order - /// A byte order of the data that we are extracting from. + /// A byte order for the data that will be encoded. /// /// \param[in] addr_size - /// A new address byte size value. - DataEncoder(const lldb::DataBufferSP &data_sp, lldb::ByteOrder byte_order, - uint8_t addr_size); + /// A size of an address in bytes. \see PutAddress, AppendAddress + DataEncoder(lldb::ByteOrder byte_order, uint8_t addr_size); - /// Destructor - /// - /// If this object contains a valid shared data reference, the reference - /// count on the data will be decremented, and if zero, the data will be - /// freed. ~DataEncoder(); - /// Clears the object state. - /// - /// Clears the object contents back to a default invalid state, and release - /// any references to shared data that this object may contain. - void Clear(); - /// Encode an unsigned integer of size \a byte_size to \a offset. /// /// Encode a single integer value at \a offset and return the offset that /// follows the newly encoded integer when the data is successfully encoded - /// into the existing data. There must be enough room in the data, else - /// UINT32_MAX will be returned to indicate that encoding failed. + /// into the existing data. There must be enough room in the existing data, + /// else UINT32_MAX will be returned to indicate that encoding failed. /// /// \param[in] offset - /// The offset within the contained data at which to put the - /// encoded integer. + /// The offset within the contained data at which to put the encoded + /// integer. /// /// \param[in] byte_size /// The size in byte of the integer to encode. @@ -111,6 +110,64 @@ /// was successfully encoded, UINT32_MAX if the encoding failed. uint32_t PutUnsigned(uint32_t offset, uint32_t byte_size, uint64_t value); + /// Encode an unsigned integer at offset \a offset. + /// + /// Encode a single unsigned integer value at \a offset and return the offset + /// that follows the newly encoded integer when the data is successfully + /// encoded into the existing data. There must be enough room in the data, + /// else UINT32_MAX will be returned to indicate that encoding failed. + /// + /// \param[in] offset + /// The offset within the contained data at which to put the encoded + /// integer. + /// + /// \param[in] value + /// The integer value to write. + /// + /// \return + /// The next offset in the bytes of this data if the integer was + /// successfully encoded, UINT32_MAX if the encoding failed. + uint32_t PutU8(uint32_t offset, uint8_t value); + uint32_t PutU16(uint32_t offset, uint16_t value); + uint32_t PutU32(uint32_t offset, uint32_t value); + uint32_t PutU64(uint32_t offset, uint64_t value); + + /// Append a unsigned integer to the end of the owned data. + /// + /// \param value + /// A unsigned integer value to append. + void AppendU8(uint8_t value); + void AppendU16(uint16_t value); + void AppendU32(uint32_t value); + void AppendU64(uint64_t value); + + /// Append an address sized integer to the end of the owned data. + /// + /// \param addr + /// A unsigned integer address value to append. The size of the address + /// will be determined by the address size specified in the constructor. + void AppendAddress(lldb::addr_t addr); + + /// Append a bytes to the end of the owned data. + /// + /// Append the bytes contained in the string reference. This function will + /// not append a NULL termination character for a C string. Use the + /// AppendCString function for this purpose. + /// + /// \param data + /// A string reference that contains bytes to append. + void AppendData(llvm::StringRef data); + + /// Append a C string to the end of the owned data. + /// + /// Append the bytes contained in the string reference along with an extra + /// NULL termination character if the StringRef bytes doesn't include one as + /// the last byte. + /// + /// \param data + /// A string reference that contains bytes to append. + void AppendCString(llvm::StringRef data); + /// Encode an arbitrary number of bytes. /// /// \param[in] offset @@ -131,11 +188,10 @@ /// Encode an address in the existing buffer at \a offset bytes into the /// buffer. /// - /// Encode a single address (honoring the m_addr_size member) to the data - /// and return the next offset where subsequent data would go. pointed to by - /// \a offset_ptr. The size of the extracted address comes from the \a - /// m_addr_size member variable and should be set correctly prior to - /// extracting any address values. + /// Encode a single address to the data and return the next offset where + /// subsequent data would go. The size of the address comes from the \a + /// m_addr_size member variable and should be set correctly prior to encoding + /// any address values. /// /// \param[in] offset /// The offset where to encode the address. @@ -150,7 +206,10 @@ /// Put a C string to \a offset. /// - /// Encodes a C string into the existing data including the terminating + /// Encodes a C string into the existing data including the terminating. If + /// there is not enough room in the buffer to fit the entire C string and the + /// NULL terminator in the existing buffer bounds, then this function will + /// fail. /// /// \param[in] offset /// The offset where to encode the string. @@ -159,18 +218,32 @@ /// The string to encode. /// /// \return - /// A pointer to the C string value in the data. If the offset - /// pointed to by \a offset_ptr is out of bounds, or if the - /// offset plus the length of the C string is out of bounds, - /// NULL will be returned. + /// The next valid offset within data if the put operation was successful, + /// else UINT32_MAX to indicate the put failed. uint32_t PutCString(uint32_t offset, const char *cstr); -private: - uint32_t PutU8(uint32_t offset, uint8_t value); - uint32_t PutU16(uint32_t offset, uint16_t value); - uint32_t PutU32(uint32_t offset, uint32_t value); - uint32_t PutU64(uint32_t offset, uint64_t value); + /// Get a shared copy of the heap based memory buffer owned by this object. + /// + /// This allows a data encoder to be used to create a data buffer that can + /// be extracted and used elsewhere after this object is destroyed. + /// + /// \return + /// A shared pointer to the DataBufferHeap that contains the data that was + /// encoded into this object. + std::shared_ptr GetDataBuffer() { + return m_data_sp; + } + /// Get a access to the bytes that this references. + /// + /// This value will always return the data that this object references even if + /// the object was constructed with caller owned data. + /// + /// \return + /// A array reference to the data that this object references. + llvm::ArrayRef GetData() const; + +private: uint32_t BytesLeft(uint32_t offset) const { const uint32_t size = GetByteSize(); if (size > offset) @@ -187,31 +260,6 @@ return length <= BytesLeft(offset); } - /// Adopt a subset of shared data in \a data_sp. - /// - /// Copies the data shared pointer which adds a reference to the contained - /// in \a data_sp. The shared data reference is reference counted to ensure - /// the data lives as long as anyone still has a valid shared pointer to the - /// data in \a data_sp. The byte order and address byte size settings remain - /// the same. If \a offset is not a valid offset in \a data_sp, then no - /// reference to the shared data will be added. If there are not \a length - /// bytes available in \a data starting at \a offset, the length will be - /// truncated to contains as many bytes as possible. - /// - /// \param[in] data_sp - /// A shared pointer to data. - /// - /// \param[in] offset - /// The offset into \a data_sp at which the subset starts. - /// - /// \param[in] length - /// The length in bytes of the subset of \a data_sp. - /// - /// \return - /// The number of bytes that this object now contains. - uint32_t SetData(const lldb::DataBufferSP &data_sp, uint32_t offset = 0, - uint32_t length = UINT32_MAX); - /// Test the validity of \a offset. /// /// \return @@ -223,25 +271,17 @@ /// /// \return /// The total number of bytes of data this object refers to. - size_t GetByteSize() const { return m_end - m_start; } - - /// A pointer to the first byte of data. - uint8_t *m_start = nullptr; + size_t GetByteSize() const; - /// A pointer to the byte that is past the end of the data. - uint8_t *m_end = nullptr; + /// The shared pointer to data that can grow as data is added + std::shared_ptr m_data_sp; - /// The byte order of the data we are extracting from. + /// The byte order of the data we are encoding to. lldb::ByteOrder m_byte_order; - /// The address size to use when extracting pointers or - /// addresses + /// The address size to use when encoding pointers or addresses. uint8_t m_addr_size; - /// The shared pointer to data that can - /// be shared among multiple instances - mutable lldb::DataBufferSP m_data_sp; - DataEncoder(const DataEncoder &) = delete; const DataEncoder &operator=(const DataEncoder &) = delete; }; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -65,6 +65,7 @@ class DWARFDataExtractor; class DWARFExpression; class DataBuffer; +class DataBufferHeap; class DataEncoder; class DataExtractor; class Debugger; diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -460,22 +460,19 @@ // first, then modify it, and if all goes well, we then replace the data // for this expression - // So first we copy the data into a heap buffer - std::unique_ptr head_data_up( - new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize())); - - // Make en encoder so we can write the address into the buffer using the - // correct byte order (endianness) - DataEncoder encoder(head_data_up->GetBytes(), head_data_up->GetByteSize(), + // Make en encoder that contains a copy of the location expression data + // so we can write the address into the buffer using the correct byte + // order. + DataEncoder encoder(m_data.GetDataStart(), m_data.GetByteSize(), m_data.GetByteOrder(), addr_byte_size); // Replace the address in the new buffer - if (encoder.PutUnsigned(offset, addr_byte_size, file_addr) == UINT32_MAX) + if (encoder.PutAddress(offset, file_addr) == UINT32_MAX) return false; // All went well, so now we can reset the data using a shared pointer to // the heap data so "m_data" will now correctly manage the heap data. - m_data.SetData(DataBufferSP(head_data_up.release())); + m_data.SetData(encoder.GetDataBuffer()); return true; } else { const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); @@ -521,15 +518,11 @@ // We have to make a copy of the data as we don't know if this data is from a // read only memory mapped buffer, so we duplicate all of the data first, // then modify it, and if all goes well, we then replace the data for this - // expression - - // So first we copy the data into a heap buffer - std::shared_ptr heap_data_sp( - new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize())); + // expression. - // Make en encoder so we can write the address into the buffer using the - // correct byte order (endianness) - DataEncoder encoder(heap_data_sp->GetBytes(), heap_data_sp->GetByteSize(), + // Make en encoder that contains a copy of the location expression data so we + // can write the address into the buffer using the correct byte order. + DataEncoder encoder(m_data.GetDataStart(), m_data.GetByteSize(), m_data.GetByteOrder(), addr_byte_size); lldb::offset_t offset = 0; @@ -603,7 +596,7 @@ // and read the // TLS data m_module_wp = new_module_sp; - m_data.SetData(heap_data_sp); + m_data.SetData(encoder.GetDataBuffer()); return true; } @@ -2817,4 +2810,3 @@ return MatchRegOp(*reg)(operand); } } - diff --git a/lldb/source/Plugins/Platform/Android/AdbClient.cpp b/lldb/source/Plugins/Platform/Android/AdbClient.cpp --- a/lldb/source/Plugins/Platform/Android/AdbClient.cpp +++ b/lldb/source/Plugins/Platform/Android/AdbClient.cpp @@ -584,14 +584,13 @@ Status AdbClient::SyncService::SendSyncRequest(const char *request_id, const uint32_t data_len, const void *data) { - const DataBufferSP data_sp(new DataBufferHeap(kSyncPacketLen, 0)); - DataEncoder encoder(data_sp, eByteOrderLittle, sizeof(void *)); - auto offset = encoder.PutData(0, request_id, strlen(request_id)); - encoder.PutUnsigned(offset, 4, data_len); - + DataEncoder encoder(eByteOrderLittle, sizeof(void *)); + encoder.AppendData(llvm::StringRef(request_id)); + encoder.AppendU32(data_len); + llvm::ArrayRef bytes = encoder.GetData(); Status error; ConnectionStatus status; - m_conn->Write(data_sp->GetBytes(), kSyncPacketLen, status, &error); + m_conn->Write(bytes.data(), kSyncPacketLen, status, &error); if (error.Fail()) return error; diff --git a/lldb/source/Utility/DataEncoder.cpp b/lldb/source/Utility/DataEncoder.cpp --- a/lldb/source/Utility/DataEncoder.cpp +++ b/lldb/source/Utility/DataEncoder.cpp @@ -8,7 +8,7 @@ #include "lldb/Utility/DataEncoder.h" -#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Endian.h" #include "llvm/Support/Endian.h" @@ -22,91 +22,34 @@ using namespace lldb_private; using namespace llvm::support::endian; -// Default constructor. DataEncoder::DataEncoder() - : m_byte_order(endian::InlHostByteOrder()), m_addr_size(sizeof(void *)), - m_data_sp() {} + : m_data_sp(new DataBufferHeap()), m_byte_order(endian::InlHostByteOrder()), + m_addr_size(sizeof(void *)) {} -// This constructor allows us to use data that is owned by someone else. The -// data must stay around as long as this object is valid. -DataEncoder::DataEncoder(void *data, uint32_t length, ByteOrder endian, +DataEncoder::DataEncoder(const void *data, uint32_t length, ByteOrder endian, uint8_t addr_size) - : m_start(static_cast(data)), - m_end(static_cast(data) + length), m_byte_order(endian), - m_addr_size(addr_size), m_data_sp() {} - -// Make a shared pointer reference to the shared data in "data_sp" and set the -// endian swapping setting to "swap", and the address size to "addr_size". The -// shared data reference will ensure the data lives as long as any DataEncoder -// objects exist that have a reference to this data. -DataEncoder::DataEncoder(const DataBufferSP &data_sp, ByteOrder endian, - uint8_t addr_size) - : m_start(nullptr), m_end(nullptr), m_byte_order(endian), - m_addr_size(addr_size), m_data_sp() { - SetData(data_sp); -} + : m_data_sp(new DataBufferHeap(data, length)), m_byte_order(endian), + m_addr_size(addr_size) {} -DataEncoder::~DataEncoder() = default; +DataEncoder::DataEncoder(ByteOrder endian, uint8_t addr_size) + : m_data_sp(new DataBufferHeap()), m_byte_order(endian), + m_addr_size(addr_size) {} -// Clears the object contents back to a default invalid state, and release any -// references to shared data that this object may contain. -void DataEncoder::Clear() { - m_start = nullptr; - m_end = nullptr; - m_byte_order = endian::InlHostByteOrder(); - m_addr_size = sizeof(void *); - m_data_sp.reset(); -} - -// Assign the data for this object to be a subrange of the shared data in -// "data_sp" starting "data_offset" bytes into "data_sp" and ending -// "data_length" bytes later. If "data_offset" is not a valid offset into -// "data_sp", then this object will contain no bytes. If "data_offset" is -// within "data_sp" yet "data_length" is too large, the length will be capped -// at the number of bytes remaining in "data_sp". A ref counted pointer to the -// data in "data_sp" will be made in this object IF the number of bytes this -// object refers to in greater than zero (if at least one byte was available -// starting at "data_offset") to ensure the data stays around as long as it is -// needed. The address size and endian swap settings will remain unchanged from -// their current settings. -uint32_t DataEncoder::SetData(const DataBufferSP &data_sp, uint32_t data_offset, - uint32_t data_length) { - m_start = m_end = nullptr; - - if (data_length > 0) { - m_data_sp = data_sp; - if (data_sp) { - const size_t data_size = data_sp->GetByteSize(); - if (data_offset < data_size) { - m_start = data_sp->GetBytes() + data_offset; - const size_t bytes_left = data_size - data_offset; - // Cap the length of we asked for too many - if (data_length <= bytes_left) - m_end = m_start + data_length; // We got all the bytes we wanted - else - m_end = m_start + bytes_left; // Not all the bytes requested were - // available in the shared data - } - } - } - - uint32_t new_size = GetByteSize(); - - // Don't hold a shared pointer to the data buffer if we don't share any valid - // bytes in the shared buffer. - if (new_size == 0) - m_data_sp.reset(); +DataEncoder::~DataEncoder() = default; - return new_size; +llvm::ArrayRef DataEncoder::GetData() const { + return llvm::ArrayRef(m_data_sp->GetBytes(), GetByteSize()); } +size_t DataEncoder::GetByteSize() const { return m_data_sp->GetByteSize(); } + // Extract a single unsigned char from the binary data and update the offset // pointed to by "offset_ptr". // // RETURNS the byte that was extracted, or zero on failure. uint32_t DataEncoder::PutU8(uint32_t offset, uint8_t value) { if (ValidOffset(offset)) { - m_start[offset] = value; + m_data_sp->GetBytes()[offset] = value; return offset + 1; } return UINT32_MAX; @@ -115,9 +58,9 @@ uint32_t DataEncoder::PutU16(uint32_t offset, uint16_t value) { if (ValidOffsetForDataOfSize(offset, sizeof(value))) { if (m_byte_order != endian::InlHostByteOrder()) - write16be(m_start + offset, value); + write16be(m_data_sp->GetBytes() + offset, value); else - write16le(m_start + offset, value); + write16le(m_data_sp->GetBytes() + offset, value); return offset + sizeof(value); } @@ -127,9 +70,9 @@ uint32_t DataEncoder::PutU32(uint32_t offset, uint32_t value) { if (ValidOffsetForDataOfSize(offset, sizeof(value))) { if (m_byte_order != endian::InlHostByteOrder()) - write32be(m_start + offset, value); + write32be(m_data_sp->GetBytes() + offset, value); else - write32le(m_start + offset, value); + write32le(m_data_sp->GetBytes() + offset, value); return offset + sizeof(value); } @@ -139,9 +82,9 @@ uint32_t DataEncoder::PutU64(uint32_t offset, uint64_t value) { if (ValidOffsetForDataOfSize(offset, sizeof(value))) { if (m_byte_order != endian::InlHostByteOrder()) - write64be(m_start + offset, value); + write64be(m_data_sp->GetBytes() + offset, value); else - write64le(m_start + offset, value); + write64le(m_data_sp->GetBytes() + offset, value); return offset + sizeof(value); } @@ -171,7 +114,7 @@ return offset; if (ValidOffsetForDataOfSize(offset, src_len)) { - memcpy(m_start + offset, src, src_len); + memcpy(m_data_sp->GetBytes() + offset, src, src_len); return offset + src_len; } return UINT32_MAX; @@ -186,3 +129,56 @@ return PutData(offset, cstr, strlen(cstr) + 1); return UINT32_MAX; } + +void DataEncoder::AppendU8(uint8_t value) { + m_data_sp->AppendData(&value, sizeof(value)); +} + +void DataEncoder::AppendU16(uint16_t value) { + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + PutU16(offset, value); +} + +void DataEncoder::AppendU32(uint32_t value) { + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + PutU32(offset, value); +} + +void DataEncoder::AppendU64(uint64_t value) { + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + PutU64(offset, value); +} + +void DataEncoder::AppendAddress(lldb::addr_t addr) { + switch (m_addr_size) { + case 4: + AppendU32(addr); + break; + case 8: + AppendU64(addr); + break; + default: + llvm_unreachable("AppendAddress unhandled case!"); + } +} + +void DataEncoder::AppendData(llvm::StringRef data) { + const char *bytes = data.data(); + const size_t length = data.size(); + if (bytes && length > 0) + m_data_sp->AppendData(bytes, length); +} + +void DataEncoder::AppendCString(llvm::StringRef data) { + const char *bytes = data.data(); + const size_t length = data.size(); + if (bytes) { + if (length > 0) + m_data_sp->AppendData(bytes, length); + if (length == 0 || bytes[length - 1] != '\0') + AppendU8(0); + } +} diff --git a/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp b/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp --- a/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp +++ b/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp @@ -33,14 +33,10 @@ std::unique_ptr CreateAuxvData( MockProcessELF &process, llvm::ArrayRef> auxv_data) { - auto addr_size = process.GetAddressByteSize(); - DataBufferSP buffer_sp( - new DataBufferHeap(auxv_data.size() * addr_size * 2, 0)); - DataEncoder encoder(buffer_sp, process.GetByteOrder(), addr_size); - uint32_t offset = 0; + DataEncoder encoder(process.GetByteOrder(), process.GetAddressByteSize()); for (auto &pair : auxv_data) { - offset = encoder.PutAddress(offset, pair.first); - offset = encoder.PutAddress(offset, pair.second); + encoder.AppendAddress(pair.first); + encoder.AppendAddress(pair.second); } return llvm::MemoryBuffer::getMemBufferCopy( llvm::toStringRef(buffer_sp->GetData()), ""); diff --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt --- a/lldb/unittests/Utility/CMakeLists.txt +++ b/lldb/unittests/Utility/CMakeLists.txt @@ -6,6 +6,7 @@ BroadcasterTest.cpp ConstStringTest.cpp CompletionRequestTest.cpp + DataEncoderTest.cpp DataExtractorTest.cpp EnvironmentTest.cpp EventTest.cpp diff --git a/lldb/unittests/Utility/DataEncoderTest.cpp b/lldb/unittests/Utility/DataEncoderTest.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/Utility/DataEncoderTest.cpp @@ -0,0 +1,534 @@ +//===-- DataEncoderTest.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Utility/DataEncoder.h" +#include "llvm/ADT/ArrayRef.h" +#include +using namespace lldb_private; +using namespace llvm; + +TEST(DataEncoderTest, PutU8) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + offset = encoder.PutU8(offset, 11); + ASSERT_EQ(offset, 1U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 2, 3, 4, 5, 6, 7, 8})); + offset = encoder.PutU8(offset, 12); + ASSERT_EQ(offset, 2U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 12, 3, 4, 5, 6, 7, 8})); + offset = encoder.PutU8(offset, 13); + ASSERT_EQ(offset, 3U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 12, 13, 4, 5, 6, 7, 8})); + offset = encoder.PutU8(offset, 14); + ASSERT_EQ(offset, 4U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 12, 13, 14, 5, 6, 7, 8})); + // Check that putting a number to an invalid offset doesn't work and returns + // an error offset and doesn't modify the buffer. + ASSERT_EQ(encoder.PutU8(init.size(), 15), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 12, 13, 14, 5, 6, 7, 8})); +} + +TEST(DataEncoderTest, AppendUnsignedLittle) { + const uint32_t addr_size = 4; + std::vector expected; + DataEncoder encoder(lldb::eByteOrderLittle, addr_size); + encoder.AppendU8(0x11); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x11})); + encoder.AppendU16(0x2233); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x11, 0x33, 0x22})); + encoder.AppendU32(0x44556677); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44})); + encoder.AppendU64(0x8899AABBCCDDEEFF); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44, + 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88})); + encoder.AppendU64(0x8899AABBCCDDEEFF); +} + +TEST(DataEncoderTest, AppendUnsignedBig) { + const uint32_t addr_size = 4; + std::vector expected; + DataEncoder encoder(lldb::eByteOrderBig, addr_size); + encoder.AppendU8(0x11); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x11})); + encoder.AppendU16(0x2233); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x11, 0x22, 0x33})); + encoder.AppendU32(0x44556677); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77})); + encoder.AppendU64(0x8899AABBCCDDEEFF); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF})); +} + +TEST(DataEncoderTest, AppendAddress4Little) { + const uint32_t addr_size = 4; + std::vector expected; + DataEncoder encoder(lldb::eByteOrderLittle, addr_size); + encoder.AppendAddress(0x11223344); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x44, 0x33, 0x22, 0x11})); + encoder.AppendAddress(0x55); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x55, 0x00, 0x00, 0x00})); +} + +TEST(DataEncoderTest, AppendAddress4Big) { + const uint32_t addr_size = 4; + std::vector expected; + DataEncoder encoder(lldb::eByteOrderBig, addr_size); + encoder.AppendAddress(0x11223344); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x11, 0x22, 0x33, 0x44})); + encoder.AppendAddress(0x55); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x00, 0x55})); +} + +TEST(DataEncoderTest, AppendAddress8Little) { + const uint32_t addr_size = 8; + std::vector expected; + DataEncoder encoder(lldb::eByteOrderLittle, addr_size); + encoder.AppendAddress(0x11223344); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + encoder.AppendAddress(0x5566778899AABBCC); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, + 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55})); +} + +TEST(DataEncoderTest, AppendAddress8Big) { + const uint32_t addr_size = 8; + std::vector expected; + DataEncoder encoder(lldb::eByteOrderBig, addr_size); + encoder.AppendAddress(0x11223344); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44})); + encoder.AppendAddress(0x5566778899AABBCC); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC})); +} + +TEST(DataEncoderTest, AppendData) { + const uint32_t addr_size = 4; + std::vector expected; + DataEncoder encoder(lldb::eByteOrderBig, addr_size); + // Make sure default constructed StringRef appends nothing + encoder.AppendData(StringRef()); + ASSERT_EQ(encoder.GetData(), ArrayRef({})); + // Make sure empty StringRef appends nothing + encoder.AppendData(StringRef("")); + ASSERT_EQ(encoder.GetData(), ArrayRef({})); + // Append some bytes that contains a NULL character + encoder.AppendData(StringRef("\x11\x00\x22", 3)); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x11, 0x00, 0x22})); +} + +TEST(DataEncoderTest, AppendCString) { + const uint32_t addr_size = 4; + std::vector expected; + DataEncoder encoder(lldb::eByteOrderBig, addr_size); + // Make sure default constructed StringRef appends nothing + encoder.AppendCString(StringRef()); + ASSERT_EQ(encoder.GetData(), ArrayRef({})); + // Make sure empty StringRef appends a NULL character since the StringRef + // doesn't contain a NULL in the referenced string. + encoder.AppendCString(StringRef("")); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x00})); + // Make sure empty StringRef appends only one NULL character if StringRef + // does contain a NULL in the referenced string. + encoder.AppendCString(StringRef("\0", 1)); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x00, 0x00})); + // Append a string where the StringRef doesn't contain a NULL termination + // and verify the NULL terminate gets added + encoder.AppendCString(StringRef("hello")); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x00, 0x00, 'h', 'e', 'l', 'l', 'o', 0x00})); + // Append a string where the StringRef does contain a NULL termination and + // verify only one NULL is added + encoder.AppendCString(StringRef("world", 6)); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x00, 0x00, 'h', 'e', 'l', 'l', 'o', 0x00, + 'w', 'o', 'r', 'l', 'd', '\0'})); +} + +TEST(DataEncoderTest, PutU16Little) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + offset = encoder.PutU16(offset, 11); + ASSERT_EQ(offset, 2U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 3, 4, 5, 6, 7, 8})); + offset = encoder.PutU16(offset, 12); + ASSERT_EQ(offset, 4U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 12, 0, 5, 6, 7, 8})); + offset = encoder.PutU16(offset, 13); + ASSERT_EQ(offset, 6U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 12, 0, 13, 0, 7, 8})); + offset = encoder.PutU16(offset, 14); + ASSERT_EQ(offset, 8U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 12, 0, 13, 0, 14, 0})); + // Check that putting a number to an invalid offset doesn't work and returns + // an error offset and doesn't modify the buffer. + ASSERT_EQ(encoder.PutU16(init.size(), 15), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 12, 0, 13, 0, 14, 0})); +} + +TEST(DataEncoderTest, PutU16Big) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderBig, + addr_size); + offset = encoder.PutU16(offset, 11); + ASSERT_EQ(offset, 2U); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 11, 3, 4, 5, 6, 7, 8})); + offset = encoder.PutU16(offset, 12); + ASSERT_EQ(offset, 4U); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 11, 0, 12, 5, 6, 7, 8})); + offset = encoder.PutU16(offset, 13); + ASSERT_EQ(offset, 6U); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 11, 0, 12, 0, 13, 7, 8})); + offset = encoder.PutU16(offset, 14); + ASSERT_EQ(offset, 8U); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 11, 0, 12, 0, 13, 0, 14})); + // Check that putting a number to an invalid offset doesn't work and returns + // an error offset and doesn't modify the buffer. + ASSERT_EQ(encoder.PutU16(init.size(), 15), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 11, 0, 12, 0, 13, 0, 14})); +} + +TEST(DataEncoderTest, PutU32Little) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + offset = encoder.PutU32(offset, 11); + ASSERT_EQ(offset, 4U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 0, 0, 5, 6, 7, 8})); + offset = encoder.PutU32(offset, 12); + ASSERT_EQ(offset, 8u); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 0, 0, 12, 0, 0, 0})); + // Check that putting a number to an invalid offset doesn't work and returns + // an error offset and doesn't modify the buffer. + ASSERT_EQ(encoder.PutU32(init.size(), 15), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 0, 0, 12, 0, 0, 0})); +} + +TEST(DataEncoderTest, PutU32Big) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderBig, + addr_size); + offset = encoder.PutU32(offset, 11); + ASSERT_EQ(offset, 4U); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 0, 0, 11, 5, 6, 7, 8})); + offset = encoder.PutU32(offset, 12); + ASSERT_EQ(offset, 8U); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 0, 0, 11, 0, 0, 0, 12})); + // Check that putting a number to an invalid offset doesn't work and returns + // an error offset and doesn't modify the buffer. + ASSERT_EQ(encoder.PutU32(init.size(), 15), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 0, 0, 11, 0, 0, 0, 12})); +} + +TEST(DataEncoderTest, PutU64Little) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + offset = encoder.PutU64(offset, 11); + ASSERT_EQ(offset, 8U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 0, 0, 0, 0, 0, 0})); + // Check that putting a number to an invalid offset doesn't work and returns + // an error offset and doesn't modify the buffer. + ASSERT_EQ(encoder.PutU64(init.size(), 15), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 0, 0, 0, 0, 0, 0, 0})); +} + +TEST(DataEncoderTest, PutU64Big) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderBig, + addr_size); + offset = encoder.PutU64(offset, 11); + ASSERT_EQ(offset, 8U); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 0, 0, 0, 0, 0, 0, 11})); + // Check that putting a number to an invalid offset doesn't work and returns + // an error offset and doesn't modify the buffer. + ASSERT_EQ(encoder.PutU64(init.size(), 15), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), ArrayRef({0, 0, 0, 0, 0, 0, 0, 11})); +} + +TEST(DataEncoderTest, PutUnsignedLittle) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + // Put only the least significant byte from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 1, 0x1122334455667788ULL); + ASSERT_EQ(offset, 1U); + ASSERT_EQ(encoder.GetData(), ArrayRef({0x88, 2, 3, 4, 5, 6, 7, 8})); + + // Put only the least significant 2 byte2 from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 2, 0x1122334455667788ULL); + ASSERT_EQ(offset, 2U); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x88, 0x77, 3, 4, 5, 6, 7, 8})); + + // Put only the least significant 4 bytes from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 4, 0x1122334455667788ULL); + ASSERT_EQ(offset, 4U); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x88, 0x77, 0x66, 0x55, 5, 6, 7, 8})); + + // Put the full uint64_t value into the encoder + offset = encoder.PutUnsigned(0, 8, 0x1122334455667788ULL); + ASSERT_EQ(offset, 8U); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11})); +} + +TEST(DataEncoderTest, PutUnsignedBig) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + uint32_t offset = 0; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderBig, + addr_size); + // Put only the least significant byte from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 1, 0x1122334455667788ULL); + ASSERT_EQ(offset, 1U); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x88, 2, 3, 4, 5, 6, 7, 8})); + + // Put only the least significant 2 byte2 from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 2, 0x1122334455667788ULL); + ASSERT_EQ(offset, 2U); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x77, 0x88, 3, 4, 5, 6, 7, 8})); + + // Put only the least significant 4 bytes from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 4, 0x1122334455667788ULL); + ASSERT_EQ(offset, 4U); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x55, 0x66, 0x77, 0x88, 5, 6, 7, 8})); + + // Put the full uint64_t value into the encoder + offset = encoder.PutUnsigned(0, 8, 0x1122334455667788ULL); + ASSERT_EQ(offset, 8U); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); +} + +TEST(DataEncoderTest, PutData) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + char one_byte[] = {11}; + char two_bytes[] = {12, 13}; + char to_many_bytes[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + uint32_t offset = 0; + // Test putting zero bytes from a invalid array (NULL) + offset = encoder.PutData(offset, nullptr, 0); + ASSERT_EQ(offset, 0U); + ASSERT_EQ(encoder.GetData(), init); + // Test putting zero bytes from a valid array + offset = encoder.PutData(offset, one_byte, 0); + ASSERT_EQ(offset, 0U); + ASSERT_EQ(encoder.GetData(), init); + // Test putting one byte from a valid array + offset = encoder.PutData(offset, one_byte, sizeof(one_byte)); + ASSERT_EQ(offset, 1U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 2, 3, 4, 5, 6, 7, 8})); + offset = encoder.PutData(offset, two_bytes, sizeof(two_bytes)); + ASSERT_EQ(offset, 3U); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 12, 13, 4, 5, 6, 7, 8})); + offset = encoder.PutData(0, to_many_bytes, sizeof(to_many_bytes)); + ASSERT_EQ(offset, UINT32_MAX); + ASSERT_EQ(encoder.GetData(), ArrayRef({11, 12, 13, 4, 5, 6, 7, 8})); +} + +TEST(DataEncoderTest, PutCString) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + // Test putting invalid string pointer + ASSERT_EQ(encoder.PutCString(0, nullptr), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), init); + // Test putting an empty string + uint32_t offset = 0; + offset = encoder.PutCString(offset, ""); + ASSERT_EQ(offset, 1U); + ASSERT_EQ(encoder.GetData(), ArrayRef({'\0', 2, 3, 4, 5, 6, 7, 8})); + // Test putting valid C string + offset = encoder.PutCString(offset, "hello"); + ASSERT_EQ(offset, 7U); + ASSERT_EQ(encoder.GetData(), + ArrayRef({'\0', 'h', 'e', 'l', 'l', 'o', '\0', 8})); + // Test putting valid C string but where it won't fit in existing data and + // make sure data stay unchanged. + offset = encoder.PutCString(offset, "world"); + ASSERT_EQ(offset, UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({'\0', 'h', 'e', 'l', 'l', 'o', '\0', 8})); +} + +TEST(DataEncoderTest, PutAddressLittle4) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + uint32_t offset = 0; + offset = encoder.PutAddress(offset, 0x11223344); + ASSERT_EQ(offset, addr_size); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 5, 6, 7, 8})); + offset = encoder.PutAddress(offset, 0x55667788); + ASSERT_EQ(offset, addr_size*2); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55})); + // Make sure we can put an address when it won't fit in the existing buffer + // and that the buffer doesn't get modified. + ASSERT_EQ(encoder.PutAddress(addr_size+1, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55})); + ASSERT_EQ(encoder.PutAddress(addr_size+2, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55})); + ASSERT_EQ(encoder.PutAddress(addr_size+3, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55})); + ASSERT_EQ(encoder.PutAddress(addr_size+4, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55})); +} + +TEST(DataEncoderTest, PutAddressBig4) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderBig, + addr_size); + uint32_t offset = 0; + offset = encoder.PutAddress(offset, 0x11223344); + ASSERT_EQ(offset, addr_size); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 5, 6, 7, 8})); + offset = encoder.PutAddress(offset, 0x55667788); + ASSERT_EQ(offset, addr_size*2); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + // Make sure we can put an address when it won't fit in the existing buffer + // and that the buffer doesn't get modified. + ASSERT_EQ(encoder.PutAddress(addr_size+1, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(addr_size+2, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(addr_size+3, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(addr_size+4, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); +} + +TEST(DataEncoderTest, PutAddressLittle8) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 8; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderLittle, + addr_size); + uint32_t offset = 0; + offset = encoder.PutAddress(offset, 0x11223344); + ASSERT_EQ(offset, addr_size); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + // Make sure we can put an address when it won't fit in the existing buffer + // and that the buffer doesn't get modified. + ASSERT_EQ(encoder.PutAddress(1, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + ASSERT_EQ(encoder.PutAddress(2, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + ASSERT_EQ(encoder.PutAddress(3, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + ASSERT_EQ(encoder.PutAddress(4, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + ASSERT_EQ(encoder.PutAddress(5, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + ASSERT_EQ(encoder.PutAddress(6, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + ASSERT_EQ(encoder.PutAddress(7, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + ASSERT_EQ(encoder.PutAddress(8, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); +} + +TEST(DataEncoderTest, PutAddressBig8) { + const ArrayRef init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 8; + DataEncoder encoder(init.data(), init.size(), lldb::eByteOrderBig, + addr_size); + uint32_t offset = 0; + offset = encoder.PutAddress(offset, 0x1122334455667788); + ASSERT_EQ(offset, addr_size); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + // Make sure we can put an address when it won't fit in the existing buffer + // and that the buffer doesn't get modified. + ASSERT_EQ(encoder.PutAddress(1, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(2, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(3, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(4, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(5, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(6, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(7, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); + ASSERT_EQ(encoder.PutAddress(8, 0x10203040), UINT32_MAX); + ASSERT_EQ(encoder.GetData(), + ArrayRef({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})); +}