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::endianness Endian) : Endian(Endian) {} 37 38 llvm::endianness getEndian() const override { return Endian; } 39 40 Error readBytes(uint64_t Offset, uint64_t Size, 41 ArrayRef<uint8_t> &Buffer) override { 42 auto ExpectedIndex = translateOffsetIndex(Offset); 43 if (!ExpectedIndex) 44 return ExpectedIndex.takeError(); 45 const auto &Item = Items[*ExpectedIndex]; 46 if (auto EC = checkOffsetForRead(Offset, Size)) 47 return EC; 48 if (Size > Traits::length(Item)) 49 return make_error<BinaryStreamError>(stream_error_code::stream_too_short); 50 Buffer = Traits::bytes(Item).take_front(Size); 51 return Error::success(); 52 } 53 54 Error readLongestContiguousChunk(uint64_t Offset, 55 ArrayRef<uint8_t> &Buffer) override { 56 auto ExpectedIndex = translateOffsetIndex(Offset); 57 if (!ExpectedIndex) 58 return ExpectedIndex.takeError(); 59 Buffer = Traits::bytes(Items[*ExpectedIndex]); 60 return Error::success(); 61 } 62 63 void setItems(ArrayRef<T> ItemArray) { 64 Items = ItemArray; 65 computeItemOffsets(); 66 } 67 68 uint64_t getLength() override { 69 return ItemEndOffsets.empty() ? 0 : ItemEndOffsets.back(); 70 } 71 72 private: 73 void computeItemOffsets() { 74 ItemEndOffsets.clear(); 75 ItemEndOffsets.reserve(Items.size()); 76 uint64_t CurrentOffset = 0; 77 for (const auto &Item : Items) { 78 uint64_t Len = Traits::length(Item); 79 assert(Len > 0 && "no empty items"); 80 CurrentOffset += Len; 81 ItemEndOffsets.push_back(CurrentOffset); 82 } 83 } 84 85 Expected<uint32_t> translateOffsetIndex(uint64_t Offset) { 86 // Make sure the offset is somewhere in our items array. 87 if (Offset >= getLength()) 88 return make_error<BinaryStreamError>(stream_error_code::stream_too_short); 89 ++Offset; 90 auto Iter = llvm::lower_bound(ItemEndOffsets, Offset); 91 size_t Idx = std::distance(ItemEndOffsets.begin(), Iter); 92 assert(Idx < Items.size() && "binary search for offset failed"); 93 return Idx; 94 } 95 96 llvm::endianness Endian; 97 ArrayRef<T> Items; 98 99 // Sorted vector of offsets to accelerate lookup. 100 std::vector<uint64_t> ItemEndOffsets; 101 }; 102 103 } // end namespace llvm 104 105 #endif // LLVM_SUPPORT_BINARYITEMSTREAM_H 106