[Lldb-commits] [lldb] 244258e - Modify DataEncoder to be able to encode data in an object owned buffer.

Greg Clayton via lldb-commits lldb-commits at lists.llvm.org
Tue Dec 7 09:45:04 PST 2021


Author: Greg Clayton
Date: 2021-12-07T09:44:57-08:00
New Revision: 244258e35acc92b3293e91ddecee6a8c8613ec20

URL: https://github.com/llvm/llvm-project/commit/244258e35acc92b3293e91ddecee6a8c8613ec20
DIFF: https://github.com/llvm/llvm-project/commit/244258e35acc92b3293e91ddecee6a8c8613ec20.diff

LOG: Modify DataEncoder to be able to encode data in an object owned buffer.

DataEncoder was previously made to modify data within an existing buffer. As the code progressed, new clients started using DataEncoder to create binary data. In these cases the use of this class was possibly, but only if you knew exactly how large your buffer would be ahead of time. This patchs adds the ability for DataEncoder to own a buffer that can be dynamically resized as data is appended to the buffer.

Change in this patch:
- Allow a DataEncoder object to be created that owns a DataBufferHeap object that can dynamically grow as data is appended
- Add new methods that start with "Append" to append data to the buffer and grow it as needed
- Adds full testing of the API to assure modifications don't regress any functionality
- Has two constructors: one that uses caller owned data and one that creates an object with object owned data
- "Append" methods only work if the object owns it own data
- Removes the ability to specify a shared memory buffer as no one was using this functionality. This allows us to switch to a case where the object owns its own data in a DataBufferHeap that can be resized as data is added

"Put" methods work on both caller and object owned data.
"Append" methods work on only object owned data where we can grow the buffer. These methods will return false if called on a DataEncoder object that has caller owned data.

The main reason for these modifications is to be able to use the DateEncoder objects instead of llvm::gsym::FileWriter in https://reviews.llvm.org/D113789. This patch wants to add the ability to create symbol table caching to LLDB and the code needs to build binary caches and save them to disk.

Reviewed By: labath

Differential Revision: https://reviews.llvm.org/D115073

Added: 
    lldb/unittests/Utility/DataEncoderTest.cpp

Modified: 
    lldb/include/lldb/Utility/DataEncoder.h
    lldb/include/lldb/lldb-forward.h
    lldb/source/Expression/DWARFExpression.cpp
    lldb/source/Plugins/Platform/Android/AdbClient.cpp
    lldb/source/Utility/DataEncoder.cpp
    lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp
    lldb/unittests/Utility/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Utility/DataEncoder.h b/lldb/include/lldb/Utility/DataEncoder.h
index b944c09d5c47f..ed3e448b23049 100644
--- 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 <cstddef>
 #include <cstdint>
 
@@ -26,21 +29,34 @@ namespace lldb_private {
 /// 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 @@ class DataEncoder {
   ///     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 @@ class DataEncoder {
   ///     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 @@ class DataEncoder {
   /// 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 @@ class DataEncoder {
 
   /// 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 @@ class DataEncoder {
   ///     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<lldb_private::DataBufferHeap> 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<uint8_t> GetData() const;
+
+private:
   uint32_t BytesLeft(uint32_t offset) const {
     const uint32_t size = GetByteSize();
     if (size > offset)
@@ -187,31 +260,6 @@ class DataEncoder {
     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 @@ class DataEncoder {
   ///
   /// \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<lldb_private::DataBufferHeap> 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
index 482da17bfacb8..23ddfa912fb14 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -65,6 +65,7 @@ class DWARFCallFrameInfo;
 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
index a10546c1deaeb..890d8b5d3107a 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -460,22 +460,19 @@ bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) {
       // 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<DataBufferHeap> 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 @@ bool DWARFExpression::LinkThreadLocalStorage(
   // 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<DataBufferHeap> 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 @@ bool DWARFExpression::LinkThreadLocalStorage(
   // 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 @@ bool DWARFExpression::MatchesOperand(StackFrame &frame,
     return MatchRegOp(*reg)(operand);
   }
 }
-

diff  --git a/lldb/source/Plugins/Platform/Android/AdbClient.cpp b/lldb/source/Plugins/Platform/Android/AdbClient.cpp
index afd869c6739c5..9af921f4843cf 100644
--- a/lldb/source/Plugins/Platform/Android/AdbClient.cpp
+++ b/lldb/source/Plugins/Platform/Android/AdbClient.cpp
@@ -584,14 +584,13 @@ AdbClient::SyncService::~SyncService() = default;
 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<uint8_t> 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
index e88cd23c1d84f..6e0b3ab91549c 100644
--- 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;
 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<uint8_t *>(data)),
-      m_end(static_cast<uint8_t *>(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<uint8_t> DataEncoder::GetData() const {
+  return llvm::ArrayRef<uint8_t>(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::PutU8(uint32_t offset, uint8_t value) {
 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::PutU16(uint32_t offset, uint16_t value) {
 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::PutU32(uint32_t offset, uint32_t value) {
 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 @@ uint32_t DataEncoder::PutData(uint32_t offset, const void *src,
     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 @@ uint32_t DataEncoder::PutCString(uint32_t offset, const char *cstr) {
     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
index 19163aeac6948..36f91e1163787 100644
--- a/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp
+++ b/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp
@@ -33,14 +33,10 @@ class MockProcessELF : public MockProcess<NativeProcessELF> {
 std::unique_ptr<llvm::MemoryBuffer> CreateAuxvData(
     MockProcessELF &process,
     llvm::ArrayRef<std::pair<AuxVector::EntryType, uint32_t>> 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
index 3dd910412e645..d7d84aa2cde3f 100644
--- a/lldb/unittests/Utility/CMakeLists.txt
+++ b/lldb/unittests/Utility/CMakeLists.txt
@@ -6,6 +6,7 @@ add_lldb_unittest(UtilityTests
   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
index 0000000000000..4c3797e2371e0
--- /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 <vector>
+using namespace lldb_private;
+using namespace llvm;
+
+TEST(DataEncoderTest, PutU8) {
+  const ArrayRef<uint8_t> 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<uint8_t>({11, 2, 3, 4, 5, 6, 7, 8}));
+  offset = encoder.PutU8(offset, 12);
+  ASSERT_EQ(offset, 2U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({11, 12, 3, 4, 5, 6, 7, 8}));
+  offset = encoder.PutU8(offset, 13);
+  ASSERT_EQ(offset, 3U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({11, 12, 13, 4, 5, 6, 7, 8}));
+  offset = encoder.PutU8(offset, 14);
+  ASSERT_EQ(offset, 4U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({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<uint8_t>({11, 12, 13, 14, 5, 6, 7, 8}));
+}
+
+TEST(DataEncoderTest, AppendUnsignedLittle) {
+  const uint32_t addr_size = 4;
+  std::vector<uint8_t> expected;
+  DataEncoder encoder(lldb::eByteOrderLittle, addr_size);
+  encoder.AppendU8(0x11);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11}));
+  encoder.AppendU16(0x2233);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11, 0x33, 0x22}));
+  encoder.AppendU32(0x44556677);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44}));
+  encoder.AppendU64(0x8899AABBCCDDEEFF);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({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<uint8_t> expected;
+  DataEncoder encoder(lldb::eByteOrderBig, addr_size);
+  encoder.AppendU8(0x11);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11}));
+  encoder.AppendU16(0x2233);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11, 0x22, 0x33}));
+  encoder.AppendU32(0x44556677);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}));
+  encoder.AppendU64(0x8899AABBCCDDEEFF);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({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<uint8_t> expected;
+  DataEncoder encoder(lldb::eByteOrderLittle, addr_size);
+  encoder.AppendAddress(0x11223344);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11}));
+  encoder.AppendAddress(0x55);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x55, 0x00, 0x00, 0x00}));
+}
+
+TEST(DataEncoderTest, AppendAddress4Big) {
+  const uint32_t addr_size = 4;
+  std::vector<uint8_t> expected;
+  DataEncoder encoder(lldb::eByteOrderBig, addr_size);
+  encoder.AppendAddress(0x11223344);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44}));
+  encoder.AppendAddress(0x55);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x00, 0x55}));
+}
+
+TEST(DataEncoderTest, AppendAddress8Little) {
+  const uint32_t addr_size = 8;
+  std::vector<uint8_t> expected;
+  DataEncoder encoder(lldb::eByteOrderLittle, addr_size);
+  encoder.AppendAddress(0x11223344);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+  encoder.AppendAddress(0x5566778899AABBCC);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({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<uint8_t> expected;
+  DataEncoder encoder(lldb::eByteOrderBig, addr_size);
+  encoder.AppendAddress(0x11223344);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44}));
+  encoder.AppendAddress(0x5566778899AABBCC);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({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<uint8_t> expected;
+  DataEncoder encoder(lldb::eByteOrderBig, addr_size);
+  // Make sure default constructed StringRef appends nothing
+  encoder.AppendData(StringRef());
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({}));
+  // Make sure empty StringRef appends nothing
+  encoder.AppendData(StringRef(""));
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({}));
+  // Append some bytes that contains a NULL character
+  encoder.AppendData(StringRef("\x11\x00\x22", 3));
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11, 0x00, 0x22}));
+}
+
+TEST(DataEncoderTest, AppendCString) {
+  const uint32_t addr_size = 4;
+  std::vector<uint8_t> expected;
+  DataEncoder encoder(lldb::eByteOrderBig, addr_size);
+  // Make sure default constructed StringRef appends nothing
+  encoder.AppendCString(StringRef());
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({}));
+  // 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<uint8_t>({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<uint8_t>({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<uint8_t>({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<uint8_t>({0x00, 0x00, 'h', 'e', 'l', 'l', 'o', 0x00,
+                               'w', 'o', 'r', 'l', 'd', '\0'}));
+}
+
+TEST(DataEncoderTest, PutU16Little) {
+  const ArrayRef<uint8_t> 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<uint8_t>({11, 0, 3, 4, 5, 6, 7, 8}));
+  offset = encoder.PutU16(offset, 12);
+  ASSERT_EQ(offset, 4U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({11, 0, 12, 0, 5, 6, 7, 8}));
+  offset = encoder.PutU16(offset, 13);
+  ASSERT_EQ(offset, 6U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({11, 0, 12, 0, 13, 0, 7, 8}));
+  offset = encoder.PutU16(offset, 14);
+  ASSERT_EQ(offset, 8U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({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<uint8_t>({11, 0, 12, 0, 13, 0, 14, 0}));
+}
+
+TEST(DataEncoderTest, PutU16Big) {
+  const ArrayRef<uint8_t> 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<uint8_t>({0, 11, 3, 4, 5, 6, 7, 8}));
+  offset = encoder.PutU16(offset, 12);
+  ASSERT_EQ(offset, 4U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0, 11, 0, 12, 5, 6, 7, 8}));
+  offset = encoder.PutU16(offset, 13);
+  ASSERT_EQ(offset, 6U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0, 11, 0, 12, 0, 13, 7, 8}));
+  offset = encoder.PutU16(offset, 14);
+  ASSERT_EQ(offset, 8U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({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<uint8_t>({0, 11, 0, 12, 0, 13, 0, 14}));
+}
+
+TEST(DataEncoderTest, PutU32Little) {
+  const ArrayRef<uint8_t> 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<uint8_t>({11, 0, 0, 0, 5, 6, 7, 8}));
+  offset = encoder.PutU32(offset, 12);
+  ASSERT_EQ(offset, 8u);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({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<uint8_t>({11, 0, 0, 0, 12, 0, 0, 0}));
+}
+
+TEST(DataEncoderTest, PutU32Big) {
+  const ArrayRef<uint8_t> 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<uint8_t>({0, 0, 0, 11, 5, 6, 7, 8}));
+  offset = encoder.PutU32(offset, 12);
+  ASSERT_EQ(offset, 8U);
+  ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({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<uint8_t>({0, 0, 0, 11, 0, 0, 0, 12}));
+}
+
+TEST(DataEncoderTest, PutU64Little) {
+  const ArrayRef<uint8_t> 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<uint8_t>({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<uint8_t>({11, 0, 0, 0, 0, 0, 0, 0}));
+}
+
+TEST(DataEncoderTest, PutU64Big) {
+  const ArrayRef<uint8_t> 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<uint8_t>({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<uint8_t>({0, 0, 0, 0, 0, 0, 0, 11}));
+}
+
+TEST(DataEncoderTest, PutUnsignedLittle) {
+  const ArrayRef<uint8_t> 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<uint8_t>({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<uint8_t>({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<uint8_t>({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<uint8_t>({0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11}));
+}
+
+TEST(DataEncoderTest, PutUnsignedBig) {
+  const ArrayRef<uint8_t> 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<uint8_t>({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<uint8_t>({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<uint8_t>({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<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+}
+
+TEST(DataEncoderTest, PutData) {
+  const ArrayRef<uint8_t> 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<uint8_t>({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<uint8_t>({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<uint8_t>({11, 12, 13, 4, 5, 6, 7, 8}));
+}
+
+TEST(DataEncoderTest, PutCString) {
+  const ArrayRef<uint8_t> 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<uint8_t>({'\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<uint8_t>({'\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<uint8_t>({'\0', 'h', 'e', 'l', 'l', 'o', '\0', 8}));
+}
+
+TEST(DataEncoderTest, PutAddressLittle4) {
+  const ArrayRef<uint8_t> 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<uint8_t>({0x44, 0x33, 0x22, 0x11, 5, 6, 7, 8}));
+  offset = encoder.PutAddress(offset, 0x55667788);
+  ASSERT_EQ(offset, addr_size*2);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({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<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55}));
+  ASSERT_EQ(encoder.PutAddress(addr_size+2, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55}));
+  ASSERT_EQ(encoder.PutAddress(addr_size+3, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55}));
+  ASSERT_EQ(encoder.PutAddress(addr_size+4, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55}));
+}
+
+TEST(DataEncoderTest, PutAddressBig4) {
+  const ArrayRef<uint8_t> 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<uint8_t>({0x11, 0x22, 0x33, 0x44, 5, 6, 7, 8}));
+  offset = encoder.PutAddress(offset, 0x55667788);
+  ASSERT_EQ(offset, addr_size*2);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({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<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(addr_size+2, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(addr_size+3, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(addr_size+4, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+}
+
+TEST(DataEncoderTest, PutAddressLittle8) {
+  const ArrayRef<uint8_t> 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<uint8_t>({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<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+  ASSERT_EQ(encoder.PutAddress(2, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+  ASSERT_EQ(encoder.PutAddress(3, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+  ASSERT_EQ(encoder.PutAddress(4, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+  ASSERT_EQ(encoder.PutAddress(5, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+  ASSERT_EQ(encoder.PutAddress(6, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+  ASSERT_EQ(encoder.PutAddress(7, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+  ASSERT_EQ(encoder.PutAddress(8, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}));
+}
+
+TEST(DataEncoderTest, PutAddressBig8) {
+  const ArrayRef<uint8_t> 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<uint8_t>({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<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(2, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(3, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(4, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(5, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(6, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(7, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+  ASSERT_EQ(encoder.PutAddress(8, 0x10203040), UINT32_MAX);
+  ASSERT_EQ(encoder.GetData(),
+            ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}));
+}


        


More information about the lldb-commits mailing list