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