clayborg created this revision. clayborg added reviewers: labath, JDevlieghere, aadsm, wallace. Herald added a subscriber: mgorny. clayborg requested review of this revision. Herald added a project: LLDB. Herald added a subscriber: lldb-commits.
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. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D115073 Files: 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 lldb/unittests/Utility/DataEncoderTest.cpp
Index: lldb/unittests/Utility/DataEncoderTest.cpp =================================================================== --- /dev/null +++ lldb/unittests/Utility/DataEncoderTest.cpp @@ -0,0 +1,605 @@ +//===-- 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 std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderLittle, + addr_size); + offset = encoder.PutU8(offset, 11); + ASSERT_EQ(offset, 1U); + expected = {11, 2, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU8(offset, 12); + ASSERT_EQ(offset, 2U); + expected = {11, 12, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU8(offset, 13); + ASSERT_EQ(offset, 3U); + expected = {11, 12, 13, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU8(offset, 14); + ASSERT_EQ(offset, 4U); + expected = {11, 12, 13, 14, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + // 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(buffer.size(), 15), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, AppendUnsignedLittle) { + const uint32_t addr_size = 4; + std::vector<uint8_t> expected; + DataEncoder encoder(lldb::eByteOrderLittle, addr_size); + ASSERT_EQ(encoder.AppendU8(0x11), true); + ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11})); + ASSERT_EQ(encoder.AppendU16(0x2233), true); + ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11, 0x33, 0x22})); + ASSERT_EQ(encoder.AppendU32(0x44556677), true); + ASSERT_EQ(encoder.GetData(), + ArrayRef<uint8_t>({0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44})); + ASSERT_EQ(encoder.AppendU64(0x8899AABBCCDDEEFF), true); + ASSERT_EQ(encoder.GetData(), + ArrayRef<uint8_t>({0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44, + 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88})); + ASSERT_EQ(encoder.AppendU64(0x8899AABBCCDDEEFF), true); +} + +TEST(DataEncoderTest, AppendUnsignedBig) { + const uint32_t addr_size = 4; + std::vector<uint8_t> expected; + DataEncoder encoder(lldb::eByteOrderBig, addr_size); + ASSERT_EQ(encoder.AppendU8(0x11), true); + ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11})); + ASSERT_EQ(encoder.AppendU16(0x2233), true); + ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11, 0x22, 0x33})); + ASSERT_EQ(encoder.AppendU32(0x44556677), true); + ASSERT_EQ(encoder.GetData(), + ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77})); + ASSERT_EQ(encoder.AppendU64(0x8899AABBCCDDEEFF), true); + 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); + ASSERT_EQ(encoder.AppendAddress(0x11223344), true); + ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11})); + ASSERT_EQ(encoder.AppendAddress(0x55), true); + 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); + ASSERT_EQ(encoder.AppendAddress(0x11223344), true); + ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({0x11, 0x22, 0x33, 0x44})); + ASSERT_EQ(encoder.AppendAddress(0x55), true); + 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); + ASSERT_EQ(encoder.AppendAddress(0x11223344), true); + ASSERT_EQ(encoder.GetData(), + ArrayRef<uint8_t>({0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00})); + ASSERT_EQ(encoder.AppendAddress(0x5566778899AABBCC), true); + 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); + ASSERT_EQ(encoder.AppendAddress(0x11223344), true); + ASSERT_EQ(encoder.GetData(), + ArrayRef<uint8_t>({0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44})); + ASSERT_EQ(encoder.AppendAddress(0x5566778899AABBCC), true); + 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 + ASSERT_EQ(encoder.AppendData(StringRef()), true); + ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({})); + // Make sure empty StringRef appends nothing + ASSERT_EQ(encoder.AppendData(StringRef("")), true); + ASSERT_EQ(encoder.GetData(), ArrayRef<uint8_t>({})); + // Append some bytes that contains a NULL character + ASSERT_EQ(encoder.AppendData(StringRef("\x11\x00\x22", 3)), true); + 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 + ASSERT_EQ(encoder.AppendCString(StringRef()), true); + 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. + ASSERT_EQ(encoder.AppendCString(StringRef("")), true); + 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. + ASSERT_EQ(encoder.AppendCString(StringRef("\0", 1)), true); + 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 + ASSERT_EQ(encoder.AppendCString(StringRef("hello")), true); + 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 + ASSERT_EQ(encoder.AppendCString(StringRef("world", 6)), true); + 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 std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderLittle, + addr_size); + offset = encoder.PutU16(offset, 11); + ASSERT_EQ(offset, 2U); + expected = {11, 0, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU16(offset, 12); + ASSERT_EQ(offset, 4U); + expected = {11, 0, 12, 0, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU16(offset, 13); + ASSERT_EQ(offset, 6U); + expected = {11, 0, 12, 0, 13, 0, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU16(offset, 14); + ASSERT_EQ(offset, 8U); + expected = {11, 0, 12, 0, 13, 0, 14, 0}; + ASSERT_EQ(buffer, expected); + // 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(buffer.size(), 15), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutU16Big) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderBig, + addr_size); + offset = encoder.PutU16(offset, 11); + ASSERT_EQ(offset, 2U); + expected = {0, 11, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU16(offset, 12); + ASSERT_EQ(offset, 4U); + expected = {0, 11, 0, 12, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU16(offset, 13); + ASSERT_EQ(offset, 6U); + expected = {0, 11, 0, 12, 0, 13, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU16(offset, 14); + ASSERT_EQ(offset, 8U); + expected = {0, 11, 0, 12, 0, 13, 0, 14}; + ASSERT_EQ(buffer, expected); + // 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(buffer.size(), 15), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutU32Little) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderLittle, + addr_size); + offset = encoder.PutU32(offset, 11); + ASSERT_EQ(offset, 4U); + expected = {11, 0, 0, 0, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU32(offset, 12); + ASSERT_EQ(offset, 8u); + expected = {11, 0, 0, 0, 12, 0, 0, 0}; + ASSERT_EQ(buffer, expected); + // 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(buffer.size(), 15), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutU32Big) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderBig, + addr_size); + offset = encoder.PutU32(offset, 11); + ASSERT_EQ(offset, 4U); + expected = {0, 0, 0, 11, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutU32(offset, 12); + ASSERT_EQ(offset, 8U); + expected = {0, 0, 0, 11, 0, 0, 0, 12}; + ASSERT_EQ(buffer, expected); + // 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(buffer.size(), 15), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutU64Little) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderLittle, + addr_size); + offset = encoder.PutU64(offset, 11); + ASSERT_EQ(offset, 8U); + expected = {11, 0, 0, 0, 0, 0, 0, 0}; + ASSERT_EQ(buffer, expected); + // 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(buffer.size(), 15), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutU64Big) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderBig, + addr_size); + offset = encoder.PutU64(offset, 11); + ASSERT_EQ(offset, 8U); + expected = {0, 0, 0, 0, 0, 0, 0, 11}; + ASSERT_EQ(buffer, expected); + // 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(buffer.size(), 15), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutUnsignedLittle) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.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); + expected = {0x88, 2, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + + // Put only the least significant 2 byte2 from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 2, 0x1122334455667788ULL); + ASSERT_EQ(offset, 2U); + expected = {0x88, 0x77, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + + // Put only the least significant 4 bytes from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 4, 0x1122334455667788ULL); + ASSERT_EQ(offset, 4U); + expected = {0x88, 0x77, 0x66, 0x55, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + + // Put the full uint64_t value into the encoder + offset = encoder.PutUnsigned(0, 8, 0x1122334455667788ULL); + ASSERT_EQ(offset, 8U); + expected = {0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11}; + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutUnsignedBig) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + uint32_t offset = 0; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.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); + expected = {0x88, 2, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + + // Put only the least significant 2 byte2 from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 2, 0x1122334455667788ULL); + ASSERT_EQ(offset, 2U); + expected = {0x77, 0x88, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + + // Put only the least significant 4 bytes from the uint64_t into the encoder + offset = encoder.PutUnsigned(0, 4, 0x1122334455667788ULL); + ASSERT_EQ(offset, 4U); + expected = {0x55, 0x66, 0x77, 0x88, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + + // Put the full uint64_t value into the encoder + offset = encoder.PutUnsigned(0, 8, 0x1122334455667788ULL); + ASSERT_EQ(offset, 8U); + expected = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutData) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + // Reset buffer + buffer = init; + char one_byte[] = {11}; + char two_bytes[] = {12, 13}; + char to_many_bytes[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + DataEncoder encoder(buffer.data(), buffer.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(buffer, init); + // Test putting zero bytes from a valid array + offset = encoder.PutData(offset, one_byte, 0); + ASSERT_EQ(offset, 0U); + ASSERT_EQ(buffer, init); + // Test putting one byte from a valid array + offset = encoder.PutData(offset, one_byte, sizeof(one_byte)); + ASSERT_EQ(offset, 1U); + expected = {11, 2, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + + offset = encoder.PutData(offset, two_bytes, sizeof(two_bytes)); + ASSERT_EQ(offset, 3U); + expected = {11, 12, 13, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + + offset = encoder.PutData(0, to_many_bytes, sizeof(to_many_bytes)); + ASSERT_EQ(offset, UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutCString) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderLittle, + addr_size); + // Test putting invalid string pointer + ASSERT_EQ(encoder.PutCString(0, nullptr), UINT32_MAX); + ASSERT_EQ(buffer, init); + // Test putting an empty string + uint32_t offset = 0; + offset = encoder.PutCString(offset, ""); + ASSERT_EQ(offset, 1U); + expected = {'\0', 2, 3, 4, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + // Test putting valid C string + offset = encoder.PutCString(offset, "hello"); + ASSERT_EQ(offset, 7U); + expected = {'\0', 'h', 'e', 'l', 'l', 'o', '\0', 8}; + ASSERT_EQ(buffer, expected); + // 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(buffer, expected); +} + +TEST(DataEncoderTest, PutAddressLittle4) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderLittle, + addr_size); + uint32_t offset = 0; + offset = encoder.PutAddress(offset, 0x11223344); + ASSERT_EQ(offset, addr_size); + expected = {0x44, 0x33, 0x22, 0x11, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutAddress(offset, 0x55667788); + ASSERT_EQ(offset, addr_size*2); + expected = {0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55}; + ASSERT_EQ(buffer, expected); + // 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(buffer, expected); + ASSERT_EQ(encoder.PutAddress(addr_size+2, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(addr_size+3, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(addr_size+4, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutAddressBig4) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 4; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderBig, + addr_size); + uint32_t offset = 0; + offset = encoder.PutAddress(offset, 0x11223344); + ASSERT_EQ(offset, addr_size); + expected = {0x11, 0x22, 0x33, 0x44, 5, 6, 7, 8}; + ASSERT_EQ(buffer, expected); + offset = encoder.PutAddress(offset, 0x55667788); + ASSERT_EQ(offset, addr_size*2); + expected = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + ASSERT_EQ(buffer, expected); + // 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(buffer, expected); + ASSERT_EQ(encoder.PutAddress(addr_size+2, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(addr_size+3, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(addr_size+4, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutAddressLittle8) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 8; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderLittle, + addr_size); + uint32_t offset = 0; + offset = encoder.PutAddress(offset, 0x11223344); + ASSERT_EQ(offset, addr_size); + expected = {0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}; + ASSERT_EQ(buffer, expected); + // 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(buffer, expected); + ASSERT_EQ(encoder.PutAddress(2, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(3, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(4, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(5, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(6, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(7, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(8, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} + +TEST(DataEncoderTest, PutAddressBig8) { + const std::vector<uint8_t> init = {1, 2, 3, 4, 5, 6, 7, 8}; + const uint32_t addr_size = 8; + + std::vector<uint8_t> buffer; + std::vector<uint8_t> expected; + // Reset buffer + buffer = init; + DataEncoder encoder(buffer.data(), buffer.size(), lldb::eByteOrderBig, + addr_size); + uint32_t offset = 0; + offset = encoder.PutAddress(offset, 0x1122334455667788); + ASSERT_EQ(offset, addr_size); + expected = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + ASSERT_EQ(buffer, expected); + // 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(buffer, expected); + ASSERT_EQ(encoder.PutAddress(2, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(3, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(4, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(5, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(6, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(7, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); + ASSERT_EQ(encoder.PutAddress(8, 0x10203040), UINT32_MAX); + ASSERT_EQ(buffer, expected); +} Index: lldb/unittests/Utility/CMakeLists.txt =================================================================== --- lldb/unittests/Utility/CMakeLists.txt +++ lldb/unittests/Utility/CMakeLists.txt @@ -6,6 +6,7 @@ BroadcasterTest.cpp ConstStringTest.cpp CompletionRequestTest.cpp + DataEncoderTest.cpp DataExtractorTest.cpp EnvironmentTest.cpp EventTest.cpp Index: lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp =================================================================== --- lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp +++ lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp @@ -33,15 +33,12 @@ 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); } + auto buffer_sp = encoder.GetDataBuffer(); return llvm::MemoryBuffer::getMemBufferCopy( llvm::toStringRef(buffer_sp->GetData()), ""); } Index: lldb/source/Utility/DataEncoder.cpp =================================================================== --- lldb/source/Utility/DataEncoder.cpp +++ 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" @@ -35,71 +35,13 @@ 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) +DataEncoder::DataEncoder(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_addr_size(addr_size), m_data_sp(new DataBufferHeap()) { } DataEncoder::~DataEncoder() = default; -// 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(); - - return new_size; -} - // Extract a single unsigned char from the binary data and update the offset // pointed to by "offset_ptr". // @@ -178,7 +120,15 @@ } uint32_t DataEncoder::PutAddress(uint32_t offset, lldb::addr_t addr) { - return PutUnsigned(offset, m_addr_size, addr); + switch (m_addr_size) { + case 4: + return PutU32(offset, addr); + case 8: + return PutU64(offset, addr); + default: + llvm_unreachable("PutAddress unhandled case!"); + } + return UINT32_MAX; } uint32_t DataEncoder::PutCString(uint32_t offset, const char *cstr) { @@ -186,3 +136,85 @@ return PutData(offset, cstr, strlen(cstr) + 1); return UINT32_MAX; } + +void DataEncoder::UpdateMemberVariables() { + m_start = m_data_sp->GetBytes(); + m_end = m_start ? m_start + m_data_sp->GetByteSize() : nullptr; +} + +bool DataEncoder::AppendU8(uint8_t value) { + if (!IsDynamic()) + return UINT32_MAX; + m_data_sp->AppendData(&value, sizeof(value)); + UpdateMemberVariables(); + return true; +} + +bool DataEncoder::AppendU16(uint16_t value) { + if (!IsDynamic()) + return UINT32_MAX; + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + UpdateMemberVariables(); + PutU16(offset, value); + return true; +} + +bool DataEncoder::AppendU32(uint32_t value) { + if (!IsDynamic()) + return UINT32_MAX; + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + UpdateMemberVariables(); + PutU32(offset, value); + return true; +} + +bool DataEncoder::AppendU64(uint64_t value) { + if (!IsDynamic()) + return UINT32_MAX; + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + UpdateMemberVariables(); + PutU64(offset, value); + return true; +} + +bool DataEncoder::AppendAddress(lldb::addr_t addr) { + switch (m_addr_size) { + case 4: + return AppendU32(addr); + case 8: + return AppendU64(addr); + default: + llvm_unreachable("AppendAddress unhandled case!"); + } + return false; +} + +bool DataEncoder::AppendData(llvm::StringRef data) { + if (!IsDynamic()) + return UINT32_MAX; + const char *bytes = data.data(); + const size_t length = data.size(); + if (bytes && length > 0) { + m_data_sp->AppendData(bytes, length); + UpdateMemberVariables(); + } + return true; +} + +bool DataEncoder::AppendCString(llvm::StringRef data) { + if (!IsDynamic()) + return UINT32_MAX; + 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') + return AppendU8(0); + UpdateMemberVariables(); + } + return true; +} Index: lldb/source/Plugins/Platform/Android/AdbClient.cpp =================================================================== --- lldb/source/Plugins/Platform/Android/AdbClient.cpp +++ lldb/source/Plugins/Platform/Android/AdbClient.cpp @@ -584,11 +584,10 @@ 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); + auto data_sp = encoder.GetDataBuffer(); Status error; ConnectionStatus status; m_conn->Write(data_sp->GetBytes(), kSyncPacketLen, status, &error); Index: lldb/source/Expression/DWARFExpression.cpp =================================================================== --- lldb/source/Expression/DWARFExpression.cpp +++ lldb/source/Expression/DWARFExpression.cpp @@ -470,7 +470,7 @@ 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 Index: lldb/include/lldb/lldb-forward.h =================================================================== --- lldb/include/lldb/lldb-forward.h +++ lldb/include/lldb/lldb-forward.h @@ -65,6 +65,7 @@ class DWARFDataExtractor; class DWARFExpression; class DataBuffer; +class DataBufferHeap; class DataEncoder; class DataExtractor; class Debugger; Index: lldb/include/lldb/Utility/DataEncoder.h =================================================================== --- lldb/include/lldb/Utility/DataEncoder.h +++ 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,10 +29,26 @@ /// 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. If the data buffer is caller owned, the data within that +/// buffer can be modified with the methods that start with "Put". This allows +/// you to modify an existing buffer as long as the modification are within the +/// buffer bounds. With caller owned data, none of the "Append" methods will +/// work and they will return false and not append anything to the buffer since +/// the object doesn't own the data and can't grow the buffer. Caller owned data +/// is created by calling the following constructor: +/// +/// \see DataEncoder(void *data, uint32_t data_length, +/// lldb::ByteOrder byte_order, +/// uint8_t addr_size) +/// +/// DataEncoder objects can also be used to create binary data that is owned by +/// this object using a DataBufferHeap. In this case the "Append" methods can +/// append data and grow the buffer that is needed to contain the new data, and +/// then the "Put" methods can be used to fixup any data within the owned data. +/// Object owned data is created when you use the following constructor: /// -/// \see DataBuffer +/// \see DataEncoder(lldb::ByteOrder byte_order, uint8_t addr_size); + class DataEncoder { public: /// Default constructor. @@ -37,10 +56,14 @@ /// Initialize all members to a default empty state. DataEncoder(); - /// Construct with a buffer that is owned by the caller. + /// Construct an ancoder that points to caller owned data. /// - /// 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 with methods that start with "Put". + /// Any "Put" methods must specify an offset that allows the data to be modified + /// within the existing buffer bounds. Calls to "Append" methods will return + /// false as appending is now allowed for caller owned buffers since we can't + /// dynamically size caller owned bufers. /// /// \param[in] data /// A pointer to caller owned data. @@ -49,30 +72,27 @@ /// 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 of the data that we will be encoding. /// /// \param[in] addr_size - /// A new address byte size value. + /// A size of an address in bytes. \see PutAddress DataEncoder(void *data, uint32_t data_length, lldb::ByteOrder byte_order, uint8_t addr_size); - /// Construct with shared data. - /// - /// 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. + /// Construct an encoder that owns a dynamic memory buffer that can + /// dynamically grow as data is appended. /// - /// \param[in] data_sp - /// A shared pointer to data. + /// A dynamic memory buffer will be created that can grow as data is encoded. + /// Any of the member functions that start with "Append" can be used to append + /// data to this buffer. The member functions that start with "Put" can be + /// used to modify data that already exists in the buffer. /// /// \param[in] byte_order - /// A byte order of the data that we are extracting from. + /// A byte order of the data that we will be encoding. /// /// \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 /// @@ -81,12 +101,6 @@ /// 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 @@ -111,6 +125,84 @@ /// 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. + /// + /// \return + /// Return true if the data was successfully appended, false if the buffer + /// is not dynamic. + bool AppendU8(uint8_t value); + bool AppendU16(uint16_t value); + bool AppendU32(uint32_t value); + bool 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. + /// + /// \return + /// Return true if the data was successfully appended, false if the buffer + /// is not dynamic. + bool 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. + /// + /// This function will only work if the object returns true to + /// DataEncoder::IsDynamic(), otherwise UINT32_MAX will be returned to + /// indicate failure. + /// + /// \param data + /// A string reference that contains bytes to append. + /// + /// \return + /// Return true if the data was successfully appended, false if the buffer + /// is not dynamic. + bool 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. + /// + /// \return + /// Return true if the data was successfully appended, false if the buffer + /// is not dynamic. + bool AppendCString(llvm::StringRef data); + /// Encode an arbitrary number of bytes. /// /// \param[in] offset @@ -165,11 +257,41 @@ /// NULL will be returned. uint32_t PutCString(uint32_t offset, const char *cstr); + /// Get a shared copy of the growable buffer maintained by this object. + /// + /// This value will be empty if the DataEncoder object was constructed with + /// pointer to data that this object doesn't own. + /// + /// \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 object points to. + /// + /// This value will always return the data that this object points to even if + /// the object was constructed with pointers to data it doesn't own or if it + /// was constructed with the dynamic data buffer that can change as data is + /// appended. + /// + /// \return + /// A array reference to the data that this object points to. + llvm::ArrayRef<uint8_t> GetData() { + return llvm::ArrayRef<uint8_t>(m_start, GetByteSize()); + } + 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); + /// Return if this object has a dynamic buffer that can be appended to. + /// + /// The buffer is dynamic if the "m_data_sp" is valid. "m_data_sp" is only set + /// by constructors mentioned below. + /// + /// @see DataEncoder(lldb::ByteOrder byte_order, uint8_t addr_size); + bool IsDynamic() const { + return (bool)m_data_sp; + } uint32_t BytesLeft(uint32_t offset) const { const uint32_t size = GetByteSize(); @@ -187,30 +309,12 @@ 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. + /// Update the m_start and m_end after appending 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); + /// Any of the member functions that append data to the end of the owned + /// data should call this function after appending data to update the required + /// member variables. + void UpdateMemberVariables(); /// Test the validity of \a offset. /// @@ -238,9 +342,8 @@ /// addresses uint8_t m_addr_size; - /// The shared pointer to data that can - /// be shared among multiple instances - mutable lldb::DataBufferSP m_data_sp; + /// The shared pointer to data that can grow as data is added + mutable std::shared_ptr<lldb_private::DataBufferHeap> m_data_sp; DataEncoder(const DataEncoder &) = delete; const DataEncoder &operator=(const DataEncoder &) = delete;
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits