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
19namespace llvm {
20
21template <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.
33template <typename T, typename Traits = BinaryItemTraits<T>>
34class BinaryItemStream : public BinaryStream {
35public:
36  explicit BinaryItemStream(llvm::support::endianness Endian)
37      : Endian(Endian) {}
38
39  llvm::support::endianness getEndian() const override { return Endian; }
40
41  Error readBytes(uint32_t Offset, uint32_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(uint32_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  uint32_t getLength() override {
70    return ItemEndOffsets.empty() ? 0 : ItemEndOffsets.back();
71  }
72
73private:
74  void computeItemOffsets() {
75    ItemEndOffsets.clear();
76    ItemEndOffsets.reserve(Items.size());
77    uint32_t CurrentOffset = 0;
78    for (const auto &Item : Items) {
79      uint32_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(uint32_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 =
92        std::lower_bound(ItemEndOffsets.begin(), ItemEndOffsets.end(), Offset);
93    size_t Idx = std::distance(ItemEndOffsets.begin(), Iter);
94    assert(Idx < Items.size() && "binary search for offset failed");
95    return Idx;
96  }
97
98  llvm::support::endianness Endian;
99  ArrayRef<T> Items;
100
101  // Sorted vector of offsets to accelerate lookup.
102  std::vector<uint32_t> ItemEndOffsets;
103};
104
105} // end namespace llvm
106
107#endif // LLVM_SUPPORT_BINARYITEMSTREAM_H
108