LoadResult DiagLoader::readMetaBlock(llvm::BitstreamCursor &Stream) {
  if (Stream.EnterSubBlock(lfort::serialized_diags::BLOCK_META)) {
    reportInvalidFile("Malformed metadata block");
    return Failure;
  }

  bool versionChecked = false;
  
  while (true) {
    unsigned blockOrCode = 0;
    StreamResult Res = readToNextRecordOrBlock(Stream, "Metadata Block",
                                               blockOrCode);
    
    switch(Res) {
      case Read_EndOfStream:
        llvm_unreachable("EndOfStream handled by readToNextRecordOrBlock");
      case Read_Failure:
        return Failure;
      case Read_Record:
        break;
      case Read_BlockBegin:
        if (Stream.SkipBlock()) {
          reportInvalidFile("Malformed metadata block");
          return Failure;
        }
      case Read_BlockEnd:
        if (!versionChecked) {
          reportInvalidFile("Diagnostics file does not contain version"
                            " information");
          return Failure;
        }
        return Success;
    }
    
    RecordData Record;
    const char *Blob;
    unsigned BlobLen;
    unsigned recordID = Stream.ReadRecord(blockOrCode, Record, &Blob, &BlobLen);
    
    if (recordID == serialized_diags::RECORD_VERSION) {
      if (Record.size() < 1) {
        reportInvalidFile("malformed VERSION identifier in diagnostics file");
        return Failure;
      }
      if (Record[0] > MaxSupportedVersion) {
        reportInvalidFile("diagnosics file is a newer version than the one "
                          "supported");
        return Failure;
      }
      versionChecked = true;
    }
  }
}
LoadResult DiagLoader::readDiagnosticBlock(llvm::BitstreamCursor &Stream,
                                           CXDiagnosticSetImpl &Diags,
                                           CXLoadedDiagnosticSetImpl &TopDiags){

  if (Stream.EnterSubBlock(lfort::serialized_diags::BLOCK_DIAG)) {
    reportInvalidFile("malformed diagnostic block");
    return Failure;
  }
  
  OwningPtr<CXLoadedDiagnostic> D(new CXLoadedDiagnostic());
  RecordData Record;
  
  while (true) {
    unsigned blockOrCode = 0;
    StreamResult Res = readToNextRecordOrBlock(Stream, "Diagnostic Block",
                                               blockOrCode);
    switch (Res) {
      case Read_EndOfStream:
        llvm_unreachable("EndOfStream handled in readToNextRecordOrBlock");
      case Read_Failure:
        return Failure;
      case Read_BlockBegin: {
        // The only blocks we care about are subdiagnostics.
        if (blockOrCode != serialized_diags::BLOCK_DIAG) {
          if (!Stream.SkipBlock()) {
            reportInvalidFile("Invalid subblock in Diagnostics block");
            return Failure;
          }
        } else if (readDiagnosticBlock(Stream, D->getChildDiagnostics(),
                                       TopDiags)) {
          return Failure;
        }

        continue;
      }
      case Read_BlockEnd:
        Diags.appendDiagnostic(D.take());        
        return Success;
      case Read_Record:
        break;
    }
    
    // Read the record.
    Record.clear();
    const char *BlobStart = 0;
    unsigned BlobLen = 0;
    unsigned recID = Stream.ReadRecord(blockOrCode, Record,
                                       BlobStart, BlobLen);
    
    if (recID < serialized_diags::RECORD_FIRST ||
        recID > serialized_diags::RECORD_LAST)
      continue;
    
    switch ((serialized_diags::RecordIDs)recID) {  
      case serialized_diags::RECORD_VERSION:
        continue;
      case serialized_diags::RECORD_CATEGORY:
        if (readString(TopDiags, TopDiags.Categories, "category", Record,
                       BlobStart, BlobLen,
                       /* allowEmptyString */ true))
          return Failure;
        continue;
      
      case serialized_diags::RECORD_DIAG_FLAG:
        if (readString(TopDiags, TopDiags.WarningFlags, "warning flag", Record,
                       BlobStart, BlobLen))
          return Failure;
        continue;
        
      case serialized_diags::RECORD_FILENAME: {
        if (readString(TopDiags, TopDiags.FileNames, "filename", Record,
                       BlobStart, BlobLen))
          return Failure;

        if (Record.size() < 3) {
          reportInvalidFile("Invalid file entry");
          return Failure;
        }
        
        const FileEntry *FE =
          TopDiags.FakeFiles.getVirtualFile(TopDiags.FileNames[Record[0]],
                                            /* size */ Record[1],
                                            /* time */ Record[2]);
        
        TopDiags.Files[Record[0]] = FE;
        continue;
      }

      case serialized_diags::RECORD_SOURCE_RANGE: {
        CXSourceRange SR;
        if (readRange(TopDiags, Record, 0, SR))
          return Failure;
        D->Ranges.push_back(SR);
        continue;
      }
      
      case serialized_diags::RECORD_FIXIT: {
        CXSourceRange SR;
        if (readRange(TopDiags, Record, 0, SR))
          return Failure;
        llvm::StringRef RetStr;
        if (readString(TopDiags, RetStr, "FIXIT", Record, BlobStart, BlobLen,
                       /* allowEmptyString */ true))
          return Failure;
        D->FixIts.push_back(std::make_pair(SR, createCXString(RetStr, false)));
        continue;
      }
        
      case serialized_diags::RECORD_DIAG: {
        D->severity = Record[0];
        unsigned offset = 1;
        if (readLocation(TopDiags, Record, offset, D->DiagLoc))
          return Failure;
        D->category = Record[offset++];
        unsigned diagFlag = Record[offset++];
        D->DiagOption = diagFlag ? TopDiags.WarningFlags[diagFlag] : "";
        D->CategoryText = D->category ? TopDiags.Categories[D->category] : "";
        D->Spelling = TopDiags.makeString(BlobStart, BlobLen);
        continue;
      }
    }
  }
}