blob: 4d27013ce36850d1585e8924d9e70bdfb6c661f2 [file] [log] [blame]
Andrew Scull5e1ddfa2018-08-14 10:06:54 +01001//===- BinaryItemStream.h ---------------------------------------*- C++ -*-===//
2//
Andrew Walbran16937d02019-10-22 13:54:20 +01003// 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
Andrew Scull5e1ddfa2018-08-14 10:06:54 +01006//
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;
Olivier Deprezf4ef2d02021-04-20 13:36:24 +020091 auto Iter = llvm::lower_bound(ItemEndOffsets, Offset);
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010092 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<uint32_t> ItemEndOffsets;
102};
103
104} // end namespace llvm
105
106#endif // LLVM_SUPPORT_BINARYITEMSTREAM_H