1 //===- BinaryItemStream.h ---------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_SUPPORT_BINARYITEMSTREAM_H 10 #define LLVM_SUPPORT_BINARYITEMSTREAM_H 11 12 #include "llvm/ADT/ArrayRef.h" 13 #include "llvm/Support/BinaryStream.h" 14 #include "llvm/Support/BinaryStreamError.h" 15 #include "llvm/Support/Error.h" 16 #include <cstddef> 17 #include <cstdint> 18 19 namespace llvm { 20 21 template <typename T> struct BinaryItemTraits { 22 static size_t length(const T &Item) = delete; 23 static ArrayRef<uint8_t> bytes(const T &Item) = delete; 24 }; 25 26 /// BinaryItemStream represents a sequence of objects stored in some kind of 27 /// external container but for which it is useful to view as a stream of 28 /// contiguous bytes. An example of this might be if you have a collection of 29 /// records and you serialize each one into a buffer, and store these serialized 30 /// records in a container. The pointers themselves are not laid out 31 /// contiguously in memory, but we may wish to read from or write to these 32 /// records as if they were. 33 template <typename T, typename Traits = BinaryItemTraits<T>> 34 class BinaryItemStream : public BinaryStream { 35 public: 36 explicit BinaryItemStream(llvm::support::endianness Endian) 37 : Endian(Endian) {} 38 39 llvm::support::endianness getEndian() const override { return Endian; } 40 41 Error readBytes(uint64_t Offset, uint64_t Size, 42 ArrayRef<uint8_t> &Buffer) override { 43 auto ExpectedIndex = translateOffsetIndex(Offset); 44 if (!ExpectedIndex) 45 return ExpectedIndex.takeError(); 46 const auto &Item = Items[*ExpectedIndex]; 47 if (auto EC = checkOffsetForRead(Offset, Size)) 48 return EC; 49 if (Size > Traits::length(Item)) 50 return make_error<BinaryStreamError>(stream_error_code::stream_too_short); 51 Buffer = Traits::bytes(Item).take_front(Size); 52 return Error::success(); 53 } 54 55 Error readLongestContiguousChunk(uint64_t Offset, 56 ArrayRef<uint8_t> &Buffer) override { 57 auto ExpectedIndex = translateOffsetIndex(Offset); 58 if (!ExpectedIndex) 59 return ExpectedIndex.takeError(); 60 Buffer = Traits::bytes(Items[*ExpectedIndex]); 61 return Error::success(); 62 } 63 64 void setItems(ArrayRef<T> ItemArray) { 65 Items = ItemArray; 66 computeItemOffsets(); 67 } 68 69 uint64_t getLength() override { 70 return ItemEndOffsets.empty() ? 0 : ItemEndOffsets.back(); 71 } 72 73 private: 74 void computeItemOffsets() { 75 ItemEndOffsets.clear(); 76 ItemEndOffsets.reserve(Items.size()); 77 uint64_t CurrentOffset = 0; 78 for (const auto &Item : Items) { 79 uint64_t Len = Traits::length(Item); 80 assert(Len > 0 && "no empty items"); 81 CurrentOffset += Len; 82 ItemEndOffsets.push_back(CurrentOffset); 83 } 84 } 85 86 Expected<uint32_t> translateOffsetIndex(uint64_t Offset) { 87 // Make sure the offset is somewhere in our items array. 88 if (Offset >= getLength()) 89 return make_error<BinaryStreamError>(stream_error_code::stream_too_short); 90 ++Offset; 91 auto Iter = llvm::lower_bound(ItemEndOffsets, Offset); 92 size_t Idx = std::distance(ItemEndOffsets.begin(), Iter); 93 assert(Idx < Items.size() && "binary search for offset failed"); 94 return Idx; 95 } 96 97 llvm::support::endianness Endian; 98 ArrayRef<T> Items; 99 100 // Sorted vector of offsets to accelerate lookup. 101 std::vector<uint64_t> ItemEndOffsets; 102 }; 103 104 } // end namespace llvm 105 106 #endif // LLVM_SUPPORT_BINARYITEMSTREAM_H 107