blob: d324f1a4fdbac5ec29e21c3d06adb92104c9e76f [file] [log] [blame]
//===--- BitstreamVisitor.h - Helper for reading a bitstream --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H
#define LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H
#include "llvm/Bitcode/BitstreamReader.h"
#include "clang/Basic/LLVM.h"
#include "clang/Serialization/ASTReader.h"
#include <string>
namespace clang {
namespace index {
namespace store {
/// Helper class that saves the current stream position and
/// then restores it when destroyed.
struct SavedStreamPosition {
explicit SavedStreamPosition(llvm::BitstreamCursor &Cursor)
: Cursor(Cursor), Offset(Cursor.GetCurrentBitNo()) { }
~SavedStreamPosition() {
Cursor.JumpToBit(Offset);
}
private:
llvm::BitstreamCursor &Cursor;
uint64_t Offset;
};
enum class StreamVisit {
Continue,
Skip,
Abort
};
template <typename ImplClass>
class BitstreamVisitor {
SmallVector<unsigned, 4> BlockStack;
protected:
llvm::BitstreamCursor &Stream;
Optional<llvm::BitstreamBlockInfo> BlockInfo;
std::string *Error;
public:
BitstreamVisitor(llvm::BitstreamCursor &Stream)
: Stream(Stream) {}
StreamVisit visitBlock(unsigned ID) {
return StreamVisit::Continue;
}
bool visit(std::string &Error) {
this->Error = &Error;
ASTReader::RecordData Record;
while (1) {
llvm::BitstreamEntry Entry = Stream.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd);
switch (Entry.Kind) {
case llvm::BitstreamEntry::Error:
Error = "malformed serialization";
return false;
case llvm::BitstreamEntry::EndBlock:
if (BlockStack.empty())
return true;
BlockStack.pop_back();
if (Stream.ReadBlockEnd()) {
Error = "malformed serialization";
return false;
}
if (Stream.AtEndOfStream())
return true;
break;
case llvm::BitstreamEntry::SubBlock: {
if (Entry.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) {
BlockInfo = Stream.ReadBlockInfoBlock();
if (!BlockInfo) {
Error = "malformed BlockInfoBlock";
return false;
}
Stream.setBlockInfo(&*BlockInfo);
break;
}
StreamVisit Ret = static_cast<ImplClass*>(this)->visitBlock(Entry.ID);
switch (Ret) {
case StreamVisit::Continue:
if (Stream.EnterSubBlock(Entry.ID)) {
Error = "malformed block record";
return false;
}
readBlockAbbrevs(Stream);
BlockStack.push_back(Entry.ID);
break;
case StreamVisit::Skip:
if (Stream.SkipBlock()) {
Error = "malformed serialization";
return false;
}
if (Stream.AtEndOfStream())
return true;
break;
case StreamVisit::Abort:
return false;
}
break;
}
case llvm::BitstreamEntry::Record: {
Record.clear();
StringRef Blob;
unsigned RecID = Stream.readRecord(Entry.ID, Record, &Blob);
unsigned BlockID = BlockStack.empty() ? 0 : BlockStack.back();
StreamVisit Ret = static_cast<ImplClass*>(this)->visitRecord(BlockID, RecID, Record, Blob);
switch (Ret) {
case StreamVisit::Continue:
break;
case StreamVisit::Skip:
Stream.skipRecord(Entry.ID);
break;
case StreamVisit::Abort:
return false;
}
break;
}
}
}
}
static void readBlockAbbrevs(llvm::BitstreamCursor &Cursor) {
while (true) {
uint64_t Offset = Cursor.GetCurrentBitNo();
unsigned Code = Cursor.ReadCode();
// We expect all abbrevs to be at the start of the block.
if (Code != llvm::bitc::DEFINE_ABBREV) {
Cursor.JumpToBit(Offset);
return;
}
Cursor.ReadAbbrevRecord();
}
}
};
} // end namespace store
} // end namespace index
} // end namespace clang
#endif