//===-- ListRecordBuilder.cpp ---------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/SmallString.h"
#include "llvm/DebugInfo/CodeView/ListRecordBuilder.h"
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"

using namespace llvm;
using namespace codeview;

ListRecordBuilder::ListRecordBuilder(TypeRecordKind Kind)
    : Kind(Kind), Builder(Kind) {}

void ListRecordBuilder::writeListContinuation(const ListContinuationRecord &R) {
  TypeRecordBuilder &Builder = getBuilder();

  assert(getLastContinuationSize() < 65535 - 8 && "continuation won't fit");

  Builder.writeTypeRecordKind(TypeRecordKind::ListContinuation);
  Builder.writeUInt16(0);
  Builder.writeTypeIndex(R.getContinuationIndex());

  // End the current segment manually so that nothing comes after the
  // continuation.
  ContinuationOffsets.push_back(Builder.size());
  SubrecordStart = Builder.size();
}

void ListRecordBuilder::finishSubRecord() {
  // The type table inserts a 16 bit size field before each list, so factor that
  // into our alignment padding.
  uint32_t Remainder =
      (Builder.size() + 2 * (ContinuationOffsets.size() + 1)) % 4;
  if (Remainder != 0) {
    for (int32_t PaddingBytesLeft = 4 - Remainder; PaddingBytesLeft > 0;
         --PaddingBytesLeft) {
      Builder.writeUInt8(LF_PAD0 + PaddingBytesLeft);
    }
  }

  // Check if this subrecord makes the current segment not fit in 64K minus the
  // space for a continuation record (8 bytes). If the segment does not fit,
  // back up and insert a continuation record, sliding the current subrecord
  // down.
  if (getLastContinuationSize() > 65535 - 8) {
    assert(SubrecordStart != 0 && "can't slide from the start!");
    SmallString<128> SubrecordCopy(
        Builder.str().slice(SubrecordStart, Builder.size()));
    assert(SubrecordCopy.size() < 65530 && "subrecord is too large to slide!");
    Builder.truncate(SubrecordStart);

    // Write a placeholder continuation record.
    Builder.writeTypeRecordKind(TypeRecordKind::ListContinuation);
    Builder.writeUInt16(0);
    Builder.writeUInt32(0);
    ContinuationOffsets.push_back(Builder.size());
    assert(Builder.size() == SubrecordStart + 8 && "wrong continuation size");
    assert(getLastContinuationSize() < 65535 && "segment too big");

    // Start a new list record of the appropriate kind, and copy the previous
    // subrecord into place.
    Builder.writeTypeRecordKind(Kind);
    Builder.writeBytes(SubrecordCopy);
  }

  SubrecordStart = Builder.size();
}

TypeIndex ListRecordBuilder::writeListRecord(TypeTableBuilder &Table) {
  // Get the continuation segments as a reversed vector of StringRefs for
  // convenience.
  SmallVector<StringRef, 1> Segments;
  StringRef Data = str();
  size_t LastEnd = 0;
  for (size_t SegEnd : ContinuationOffsets) {
    Segments.push_back(Data.slice(LastEnd, SegEnd));
    LastEnd = SegEnd;
  }
  Segments.push_back(Data.slice(LastEnd, Builder.size()));

  // Pop the last record off and emit it directly.
  StringRef LastRec = Segments.pop_back_val();
  TypeIndex ContinuationIndex = Table.writeRecord(LastRec);

  // Emit each record with a continuation in reverse order, so that each one
  // references the previous record.
  for (StringRef Rec : reverse(Segments)) {
    assert(*reinterpret_cast<const ulittle16_t *>(Rec.data()) ==
           unsigned(Kind));
    ulittle32_t *ContinuationPtr =
        reinterpret_cast<ulittle32_t *>(const_cast<char *>(Rec.end())) - 1;
    *ContinuationPtr = ContinuationIndex.getIndex();
    ContinuationIndex = Table.writeRecord(Rec);
  }
  return ContinuationIndex;
}
