summaryrefslogtreecommitdiff
path: root/include/llvm/Support/StreamableMemoryObject.h
blob: 1eb4c32dbc44b16780aee389bb8d0a99524414aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//===- StreamableMemoryObject.h - Streamable data interface -----*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//


#ifndef LLVM_SUPPORT_STREAMABLEMEMORYOBJECT_H
#define LLVM_SUPPORT_STREAMABLEMEMORYOBJECT_H

#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataStream.h"
#include "llvm/Support/MemoryObject.h"
#include <vector>

namespace llvm {

/// StreamableMemoryObject - Interface to data which might be streamed.
/// Streamability has 2 important implications/restrictions. First, the data
/// might not yet exist in memory when the request is made. This just means
/// that readByte/readBytes might have to block or do some work to get it.
/// More significantly, the exact size of the object might not be known until
/// it has all been fetched. This means that to return the right result,
/// getExtent must also wait for all the data to arrive; therefore it should
/// not be called on objects which are actually streamed (this would defeat
/// the purpose of streaming). Instead, isValidAddress and isObjectEnd can be
/// used to test addresses without knowing the exact size of the stream.
/// Finally, getPointer can be used instead of readBytes to avoid extra copying.
class StreamableMemoryObject : public MemoryObject {
 public:
  /// Destructor      - Override as necessary.
  virtual ~StreamableMemoryObject();

  /// getBase         - Returns the lowest valid address in the region.
  ///
  /// @result         - The lowest valid address.
  virtual uint64_t getBase() const override = 0;

  /// getExtent       - Returns the size of the region in bytes.  (The region is
  ///                   contiguous, so the highest valid address of the region
  ///                   is getBase() + getExtent() - 1).
  ///                   May block until all bytes in the stream have been read
  ///
  /// @result         - The size of the region.
  virtual uint64_t getExtent() const override = 0;

  /// readByte        - Tries to read a single byte from the region.
  ///                   May block until (address - base) bytes have been read
  /// @param address  - The address of the byte, in the same space as getBase().
  /// @param ptr      - A pointer to a byte to be filled in.  Must be non-NULL.
  /// @result         - 0 if successful; -1 if not.  Failure may be due to a
  ///                   bounds violation or an implementation-specific error.
  virtual int readByte(uint64_t address, uint8_t *ptr) const override = 0;

  /// readBytes       - Tries to read a contiguous range of bytes from the
  ///                   region, up to the end of the region.
  ///                   May block until (address - base + size) bytes have
  ///                   been read. Additionally, StreamableMemoryObjects will
  ///                   not do partial reads - if size bytes cannot be read,
  ///                   readBytes will fail.
  ///
  /// @param address  - The address of the first byte, in the same space as
  ///                   getBase().
  /// @param size     - The number of bytes to copy.
  /// @param buf      - A pointer to a buffer to be filled in.  Must be non-NULL
  ///                   and large enough to hold size bytes.
  /// @result         - 0 if successful; -1 if not.  Failure may be due to a
  ///                   bounds violation or an implementation-specific error.
  virtual int readBytes(uint64_t address,
                        uint64_t size,
                        uint8_t *buf) const override = 0;

  /// getPointer  - Ensures that the requested data is in memory, and returns
  ///               A pointer to it. More efficient than using readBytes if the
  ///               data is already in memory.
  ///               May block until (address - base + size) bytes have been read
  /// @param address - address of the byte, in the same space as getBase()
  /// @param size    - amount of data that must be available on return
  /// @result        - valid pointer to the requested data
  virtual const uint8_t *getPointer(uint64_t address, uint64_t size) const = 0;

  /// isValidAddress - Returns true if the address is within the object
  ///                  (i.e. between base and base + extent - 1 inclusive)
  ///                  May block until (address - base) bytes have been read
  /// @param address - address of the byte, in the same space as getBase()
  /// @result        - true if the address may be read with readByte()
  virtual bool isValidAddress(uint64_t address) const = 0;

  /// isObjectEnd    - Returns true if the address is one past the end of the
  ///                  object (i.e. if it is equal to base + extent)
  ///                  May block until (address - base) bytes have been read
  /// @param address - address of the byte, in the same space as getBase()
  /// @result        - true if the address is equal to base + extent
  virtual bool isObjectEnd(uint64_t address) const = 0;
};

/// StreamingMemoryObject - interface to data which is actually streamed from
/// a DataStreamer. In addition to inherited members, it has the
/// dropLeadingBytes and setKnownObjectSize methods which are not applicable
/// to non-streamed objects.
class StreamingMemoryObject : public StreamableMemoryObject {
public:
  StreamingMemoryObject(DataStreamer *streamer);
  virtual uint64_t getBase() const override { return 0; }
  virtual uint64_t getExtent() const override;
  virtual int readByte(uint64_t address, uint8_t *ptr) const override;
  virtual int readBytes(uint64_t address,
                        uint64_t size,
                        uint8_t *buf) const override;
  virtual const uint8_t *getPointer(uint64_t address,
                                    uint64_t size) const override {
    // This could be fixed by ensuring the bytes are fetched and making a copy,
    // requiring that the bitcode size be known, or otherwise ensuring that
    // the memory doesn't go away/get reallocated, but it's
    // not currently necessary. Users that need the pointer don't stream.
    assert(0 && "getPointer in streaming memory objects not allowed");
    return NULL;
  }
  virtual bool isValidAddress(uint64_t address) const override;
  virtual bool isObjectEnd(uint64_t address) const override;

  /// Drop s bytes from the front of the stream, pushing the positions of the
  /// remaining bytes down by s. This is used to skip past the bitcode header,
  /// since we don't know a priori if it's present, and we can't put bytes
  /// back into the stream once we've read them.
  bool dropLeadingBytes(size_t s);

  /// If the data object size is known in advance, many of the operations can
  /// be made more efficient, so this method should be called before reading
  /// starts (although it can be called anytime).
  void setKnownObjectSize(size_t size);

private:
  const static uint32_t kChunkSize = 4096 * 4;
  mutable std::vector<unsigned char> Bytes;
  OwningPtr<DataStreamer> Streamer;
  mutable size_t BytesRead;   // Bytes read from stream
  size_t BytesSkipped;// Bytes skipped at start of stream (e.g. wrapper/header)
  mutable size_t ObjectSize; // 0 if unknown, set if wrapper seen or EOF reached
  mutable bool EOFReached;

  // Fetch enough bytes such that Pos can be read or EOF is reached
  // (i.e. BytesRead > Pos). Return true if Pos can be read.
  // Unlike most of the functions in BitcodeReader, returns true on success.
  // Most of the requests will be small, but we fetch at kChunkSize bytes
  // at a time to avoid making too many potentially expensive GetBytes calls
  bool fetchToPos(size_t Pos) const {
    if (EOFReached) return Pos < ObjectSize;
    while (Pos >= BytesRead) {
      Bytes.resize(BytesRead + BytesSkipped + kChunkSize);
      size_t bytes = Streamer->GetBytes(&Bytes[BytesRead + BytesSkipped],
                                        kChunkSize);
      BytesRead += bytes;
      if (bytes < kChunkSize) {
        if (ObjectSize && BytesRead < Pos)
          assert(0 && "Unexpected short read fetching bitcode");
        if (BytesRead <= Pos) { // reached EOF/ran out of bytes
          ObjectSize = BytesRead;
          EOFReached = true;
          return false;
        }
      }
    }
    return true;
  }

  StreamingMemoryObject(const StreamingMemoryObject&) LLVM_DELETED_FUNCTION;
  void operator=(const StreamingMemoryObject&) LLVM_DELETED_FUNCTION;
};

StreamableMemoryObject *getNonStreamedMemoryObject(
    const unsigned char *Start, const unsigned char *End);

}
#endif  // STREAMABLEMEMORYOBJECT_H_