<commit_before>//===- SystemZInstrInfo.cpp - SystemZ Instruction Information --------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the SystemZ implementation of the TargetInstrInfo class.
//
//===----------------------------------------------------------------------===//

#include "SystemZ.h"
#include "SystemZInstrInfo.h"
#include "SystemZMachineFunctionInfo.h"
#include "SystemZTargetMachine.h"
#include "SystemZGenInstrInfo.inc"
#include "llvm/Function.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/PseudoSourceValue.h"

using namespace llvm;

SystemZInstrInfo::SystemZInstrInfo(SystemZTargetMachine &tm)
  : TargetInstrInfoImpl(SystemZInsts, array_lengthof(SystemZInsts)),
    RI(tm, *this), TM(tm) {
  // Fill the spill offsets map
  static const unsigned SpillOffsTab[][2] = {
    { SystemZ::R2D,  0x10 },
    { SystemZ::R3D,  0x18 },
    { SystemZ::R4D,  0x20 },
    { SystemZ::R5D,  0x28 },
    { SystemZ::R6D,  0x30 },
    { SystemZ::R7D,  0x38 },
    { SystemZ::R8D,  0x40 },
    { SystemZ::R9D,  0x48 },
    { SystemZ::R10D, 0x50 },
    { SystemZ::R11D, 0x58 },
    { SystemZ::R12D, 0x60 },
    { SystemZ::R13D, 0x68 },
    { SystemZ::R14D, 0x70 },
    { SystemZ::R15D, 0x78 }
  };

  RegSpillOffsets.grow(SystemZ::NUM_TARGET_REGS);

  for (unsigned i = 0, e = array_lengthof(SpillOffsTab); i != e; ++i)
    RegSpillOffsets[SpillOffsTab[i][0]] = SpillOffsTab[i][1];
}

void SystemZInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
                                          MachineBasicBlock::iterator MI,
                                    unsigned SrcReg, bool isKill, int FrameIdx,
                                    const TargetRegisterClass *RC) const {
  assert(0 && "Cannot store this register to stack slot!");
}

void SystemZInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
                                           MachineBasicBlock::iterator MI,
                                           unsigned DestReg, int FrameIdx,
                                           const TargetRegisterClass *RC) const{
  assert(0 && "Cannot store this register to stack slot!");
}

bool SystemZInstrInfo::copyRegToReg(MachineBasicBlock &MBB,
                                    MachineBasicBlock::iterator I,
                                    unsigned DestReg, unsigned SrcReg,
                                    const TargetRegisterClass *DestRC,
                                    const TargetRegisterClass *SrcRC) const {
  DebugLoc DL = DebugLoc::getUnknownLoc();
  if (I != MBB.end()) DL = I->getDebugLoc();

  // Determine if DstRC and SrcRC have a common superclass.
  const TargetRegisterClass *CommonRC = DestRC;
  if (DestRC == SrcRC)
    /* Same regclass for source and dest */;
  else if (CommonRC->hasSuperClass(SrcRC))
    CommonRC = SrcRC;
  else if (!CommonRC->hasSubClass(SrcRC))
    CommonRC = 0;

  if (CommonRC) {
    if (CommonRC == &SystemZ::GR64RegClass ||
        CommonRC == &SystemZ::ADDR64RegClass) {
      BuildMI(MBB, I, DL, get(SystemZ::MOV64rr), DestReg).addReg(SrcReg);
    } else if (CommonRC == &SystemZ::GR32RegClass ||
               CommonRC == &SystemZ::ADDR32RegClass) {
      BuildMI(MBB, I, DL, get(SystemZ::MOV32rr), DestReg).addReg(SrcReg);
    } else if (CommonRC == &SystemZ::GR64PRegClass) {
      BuildMI(MBB, I, DL, get(SystemZ::MOV64rrP), DestReg).addReg(SrcReg);
    } else if (CommonRC == &SystemZ::GR128RegClass) {
      BuildMI(MBB, I, DL, get(SystemZ::MOV128rr), DestReg).addReg(SrcReg);
    } else {
      return false;
    }

    return true;
  }

  if ((SrcRC == &SystemZ::GR64RegClass &&
       DestRC == &SystemZ::ADDR64RegClass) ||
      (DestRC == &SystemZ::GR64RegClass &&
       SrcRC == &SystemZ::ADDR64RegClass)) {
    BuildMI(MBB, I, DL, get(SystemZ::MOV64rr), DestReg).addReg(SrcReg);
    return true;
  } else if ((SrcRC == &SystemZ::GR32RegClass &&
              DestRC == &SystemZ::ADDR32RegClass) ||
             (DestRC == &SystemZ::GR32RegClass &&
              SrcRC == &SystemZ::ADDR32RegClass)) {
    BuildMI(MBB, I, DL, get(SystemZ::MOV32rr), DestReg).addReg(SrcReg);
    return true;
  }

  return false;
}

bool
SystemZInstrInfo::isMoveInstr(const MachineInstr& MI,
                              unsigned &SrcReg, unsigned &DstReg,
                              unsigned &SrcSubIdx, unsigned &DstSubIdx) const {
  SrcSubIdx = DstSubIdx = 0; // No sub-registers yet.

  switch (MI.getOpcode()) {
  default:
    return false;
  case SystemZ::MOV32rr:
  case SystemZ::MOV64rr:
  case SystemZ::MOV64rrP:
  case SystemZ::MOV128rr:
    assert(MI.getNumOperands() >= 2 &&
           MI.getOperand(0).isReg() &&
           MI.getOperand(1).isReg() &&
           "invalid register-register move instruction");
    SrcReg = MI.getOperand(1).getReg();
    DstReg = MI.getOperand(0).getReg();
    return true;
  }
}

bool
SystemZInstrInfo::spillCalleeSavedRegisters(MachineBasicBlock &MBB,
                                           MachineBasicBlock::iterator MI,
                                const std::vector<CalleeSavedInfo> &CSI) const {
  DebugLoc DL = DebugLoc::getUnknownLoc();
  if (MI != MBB.end()) DL = MI->getDebugLoc();

  MachineFunction &MF = *MBB.getParent();
  SystemZMachineFunctionInfo *MFI = MF.getInfo<SystemZMachineFunctionInfo>();
  MFI->setCalleeSavedFrameSize(CSI.size() * 8);

  // Scan the callee-saved and find the bounds of register spill area.
  unsigned LowReg = 0, HighReg = 0, StartOffset = -1U, EndOffset = 0;
  for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
    unsigned Reg = CSI[i].getReg();
    unsigned Offset = RegSpillOffsets[Reg];
    if (StartOffset > Offset) {
      LowReg = Reg; StartOffset = Offset;
    }
    if (EndOffset < Offset) {
      HighReg = Reg; EndOffset = RegSpillOffsets[Reg];
    }
  }

  // Save information for epilogue inserter.
  MFI->setLowReg(LowReg); MFI->setHighReg(HighReg);

  // Build a store instruction. Use STORE MULTIPLE instruction if there are many
  // registers to store, otherwise - just STORE.
  MachineInstrBuilder MIB =
    BuildMI(MBB, MI, DL, get((LowReg == HighReg ?
                              SystemZ::MOV64mr : SystemZ::MOV64mrm)));

  // Add store operands.
  MIB.addReg(SystemZ::R15D).addImm(StartOffset);
  if (LowReg == HighReg)
    MIB.addReg(0);
  MIB.addReg(LowReg, RegState::Kill);
  if (LowReg != HighReg)
    MIB.addReg(HighReg, RegState::Kill);

  // Do a second scan adding regs as being killed by instruction
  for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
    unsigned Reg = CSI[i].getReg();
    // Add the callee-saved register as live-in. It's killed at the spill.
    MBB.addLiveIn(Reg);
    if (Reg != LowReg && Reg != HighReg)
      MIB.addReg(Reg, RegState::ImplicitKill);
  }

  return true;
}

bool
SystemZInstrInfo::restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
                                             MachineBasicBlock::iterator MI,
                                const std::vector<CalleeSavedInfo> &CSI) const {
  if (CSI.empty())
    return false;

  DebugLoc DL = DebugLoc::getUnknownLoc();
  if (MI != MBB.end()) DL = MI->getDebugLoc();

  MachineFunction &MF = *MBB.getParent();
  const TargetRegisterInfo *RegInfo= MF.getTarget().getRegisterInfo();
  SystemZMachineFunctionInfo *MFI = MF.getInfo<SystemZMachineFunctionInfo>();

  unsigned LowReg = MFI->getLowReg(), HighReg = MFI->getHighReg();
  unsigned StartOffset = RegSpillOffsets[LowReg];

  // Build a load instruction. Use LOAD MULTIPLE instruction if there are many
  // registers to load, otherwise - just LOAD.
  MachineInstrBuilder MIB =
    BuildMI(MBB, MI, DL, get((LowReg == HighReg ?
                              SystemZ::MOV64rm : SystemZ::MOV64rmm)));
  // Add store operands.
  MIB.addReg(LowReg, RegState::Define);
  if (LowReg != HighReg)
    MIB.addReg(HighReg, RegState::Define);

  MIB.addReg((RegInfo->hasFP(MF) ? SystemZ::R11D : SystemZ::R15D));
  MIB.addImm(StartOffset);
  if (LowReg == HighReg)
    MIB.addReg(0);

  // Do a second scan adding regs as being defined by instruction
  for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
    unsigned Reg = CSI[i].getReg();
    if (Reg != LowReg && Reg != HighReg)
      MIB.addReg(Reg, RegState::ImplicitDefine);
  }

  return true;
}

unsigned
SystemZInstrInfo::InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB,
                              MachineBasicBlock *FBB,
                            const SmallVectorImpl<MachineOperand> &Cond) const {
  assert(0 && "Implement branches!");

  return 0;
}

const TargetInstrDesc&
SystemZInstrInfo::getBrCond(SystemZCC::CondCodes CC) const {
  unsigned Opc;
  switch (CC) {
  default:
    assert(0 && "Unknown condition code!");
  case SystemZCC::E:
    Opc = SystemZ::JE;
    break;
  case SystemZCC::NE:
    Opc = SystemZ::JNE;
    break;
  case SystemZCC::H:
    Opc = SystemZ::JH;
    break;
  case SystemZCC::L:
    Opc = SystemZ::JL;
    break;
  case SystemZCC::HE:
    Opc = SystemZ::JHE;
    break;
  case SystemZCC::LE:
    Opc = SystemZ::JLE;
    break;
  }

  return get(Opc);
}
<commit_msg>Implement InsertBranch() hook<commit_after>//===- SystemZInstrInfo.cpp - SystemZ Instruction Information --------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the SystemZ implementation of the TargetInstrInfo class.
//
//===----------------------------------------------------------------------===//

#include "SystemZ.h"
#include "SystemZInstrInfo.h"
#include "SystemZMachineFunctionInfo.h"
#include "SystemZTargetMachine.h"
#include "SystemZGenInstrInfo.inc"
#include "llvm/Function.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/PseudoSourceValue.h"

using namespace llvm;

SystemZInstrInfo::SystemZInstrInfo(SystemZTargetMachine &tm)
  : TargetInstrInfoImpl(SystemZInsts, array_lengthof(SystemZInsts)),
    RI(tm, *this), TM(tm) {
  // Fill the spill offsets map
  static const unsigned SpillOffsTab[][2] = {
    { SystemZ::R2D,  0x10 },
    { SystemZ::R3D,  0x18 },
    { SystemZ::R4D,  0x20 },
    { SystemZ::R5D,  0x28 },
    { SystemZ::R6D,  0x30 },
    { SystemZ::R7D,  0x38 },
    { SystemZ::R8D,  0x40 },
    { SystemZ::R9D,  0x48 },
    { SystemZ::R10D, 0x50 },
    { SystemZ::R11D, 0x58 },
    { SystemZ::R12D, 0x60 },
    { SystemZ::R13D, 0x68 },
    { SystemZ::R14D, 0x70 },
    { SystemZ::R15D, 0x78 }
  };

  RegSpillOffsets.grow(SystemZ::NUM_TARGET_REGS);

  for (unsigned i = 0, e = array_lengthof(SpillOffsTab); i != e; ++i)
    RegSpillOffsets[SpillOffsTab[i][0]] = SpillOffsTab[i][1];
}

void SystemZInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
                                          MachineBasicBlock::iterator MI,
                                    unsigned SrcReg, bool isKill, int FrameIdx,
                                    const TargetRegisterClass *RC) const {
  assert(0 && "Cannot store this register to stack slot!");
}

void SystemZInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
                                           MachineBasicBlock::iterator MI,
                                           unsigned DestReg, int FrameIdx,
                                           const TargetRegisterClass *RC) const{
  assert(0 && "Cannot store this register to stack slot!");
}

bool SystemZInstrInfo::copyRegToReg(MachineBasicBlock &MBB,
                                    MachineBasicBlock::iterator I,
                                    unsigned DestReg, unsigned SrcReg,
                                    const TargetRegisterClass *DestRC,
                                    const TargetRegisterClass *SrcRC) const {
  DebugLoc DL = DebugLoc::getUnknownLoc();
  if (I != MBB.end()) DL = I->getDebugLoc();

  // Determine if DstRC and SrcRC have a common superclass.
  const TargetRegisterClass *CommonRC = DestRC;
  if (DestRC == SrcRC)
    /* Same regclass for source and dest */;
  else if (CommonRC->hasSuperClass(SrcRC))
    CommonRC = SrcRC;
  else if (!CommonRC->hasSubClass(SrcRC))
    CommonRC = 0;

  if (CommonRC) {
    if (CommonRC == &SystemZ::GR64RegClass ||
        CommonRC == &SystemZ::ADDR64RegClass) {
      BuildMI(MBB, I, DL, get(SystemZ::MOV64rr), DestReg).addReg(SrcReg);
    } else if (CommonRC == &SystemZ::GR32RegClass ||
               CommonRC == &SystemZ::ADDR32RegClass) {
      BuildMI(MBB, I, DL, get(SystemZ::MOV32rr), DestReg).addReg(SrcReg);
    } else if (CommonRC == &SystemZ::GR64PRegClass) {
      BuildMI(MBB, I, DL, get(SystemZ::MOV64rrP), DestReg).addReg(SrcReg);
    } else if (CommonRC == &SystemZ::GR128RegClass) {
      BuildMI(MBB, I, DL, get(SystemZ::MOV128rr), DestReg).addReg(SrcReg);
    } else {
      return false;
    }

    return true;
  }

  if ((SrcRC == &SystemZ::GR64RegClass &&
       DestRC == &SystemZ::ADDR64RegClass) ||
      (DestRC == &SystemZ::GR64RegClass &&
       SrcRC == &SystemZ::ADDR64RegClass)) {
    BuildMI(MBB, I, DL, get(SystemZ::MOV64rr), DestReg).addReg(SrcReg);
    return true;
  } else if ((SrcRC == &SystemZ::GR32RegClass &&
              DestRC == &SystemZ::ADDR32RegClass) ||
             (DestRC == &SystemZ::GR32RegClass &&
              SrcRC == &SystemZ::ADDR32RegClass)) {
    BuildMI(MBB, I, DL, get(SystemZ::MOV32rr), DestReg).addReg(SrcReg);
    return true;
  }

  return false;
}

bool
SystemZInstrInfo::isMoveInstr(const MachineInstr& MI,
                              unsigned &SrcReg, unsigned &DstReg,
                              unsigned &SrcSubIdx, unsigned &DstSubIdx) const {
  SrcSubIdx = DstSubIdx = 0; // No sub-registers yet.

  switch (MI.getOpcode()) {
  default:
    return false;
  case SystemZ::MOV32rr:
  case SystemZ::MOV64rr:
  case SystemZ::MOV64rrP:
  case SystemZ::MOV128rr:
    assert(MI.getNumOperands() >= 2 &&
           MI.getOperand(0).isReg() &&
           MI.getOperand(1).isReg() &&
           "invalid register-register move instruction");
    SrcReg = MI.getOperand(1).getReg();
    DstReg = MI.getOperand(0).getReg();
    return true;
  }
}

bool
SystemZInstrInfo::spillCalleeSavedRegisters(MachineBasicBlock &MBB,
                                           MachineBasicBlock::iterator MI,
                                const std::vector<CalleeSavedInfo> &CSI) const {
  DebugLoc DL = DebugLoc::getUnknownLoc();
  if (MI != MBB.end()) DL = MI->getDebugLoc();

  MachineFunction &MF = *MBB.getParent();
  SystemZMachineFunctionInfo *MFI = MF.getInfo<SystemZMachineFunctionInfo>();
  MFI->setCalleeSavedFrameSize(CSI.size() * 8);

  // Scan the callee-saved and find the bounds of register spill area.
  unsigned LowReg = 0, HighReg = 0, StartOffset = -1U, EndOffset = 0;
  for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
    unsigned Reg = CSI[i].getReg();
    unsigned Offset = RegSpillOffsets[Reg];
    if (StartOffset > Offset) {
      LowReg = Reg; StartOffset = Offset;
    }
    if (EndOffset < Offset) {
      HighReg = Reg; EndOffset = RegSpillOffsets[Reg];
    }
  }

  // Save information for epilogue inserter.
  MFI->setLowReg(LowReg); MFI->setHighReg(HighReg);

  // Build a store instruction. Use STORE MULTIPLE instruction if there are many
  // registers to store, otherwise - just STORE.
  MachineInstrBuilder MIB =
    BuildMI(MBB, MI, DL, get((LowReg == HighReg ?
                              SystemZ::MOV64mr : SystemZ::MOV64mrm)));

  // Add store operands.
  MIB.addReg(SystemZ::R15D).addImm(StartOffset);
  if (LowReg == HighReg)
    MIB.addReg(0);
  MIB.addReg(LowReg, RegState::Kill);
  if (LowReg != HighReg)
    MIB.addReg(HighReg, RegState::Kill);

  // Do a second scan adding regs as being killed by instruction
  for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
    unsigned Reg = CSI[i].getReg();
    // Add the callee-saved register as live-in. It's killed at the spill.
    MBB.addLiveIn(Reg);
    if (Reg != LowReg && Reg != HighReg)
      MIB.addReg(Reg, RegState::ImplicitKill);
  }

  return true;
}

bool
SystemZInstrInfo::restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
                                             MachineBasicBlock::iterator MI,
                                const std::vector<CalleeSavedInfo> &CSI) const {
  if (CSI.empty())
    return false;

  DebugLoc DL = DebugLoc::getUnknownLoc();
  if (MI != MBB.end()) DL = MI->getDebugLoc();

  MachineFunction &MF = *MBB.getParent();
  const TargetRegisterInfo *RegInfo= MF.getTarget().getRegisterInfo();
  SystemZMachineFunctionInfo *MFI = MF.getInfo<SystemZMachineFunctionInfo>();

  unsigned LowReg = MFI->getLowReg(), HighReg = MFI->getHighReg();
  unsigned StartOffset = RegSpillOffsets[LowReg];

  // Build a load instruction. Use LOAD MULTIPLE instruction if there are many
  // registers to load, otherwise - just LOAD.
  MachineInstrBuilder MIB =
    BuildMI(MBB, MI, DL, get((LowReg == HighReg ?
                              SystemZ::MOV64rm : SystemZ::MOV64rmm)));
  // Add store operands.
  MIB.addReg(LowReg, RegState::Define);
  if (LowReg != HighReg)
    MIB.addReg(HighReg, RegState::Define);

  MIB.addReg((RegInfo->hasFP(MF) ? SystemZ::R11D : SystemZ::R15D));
  MIB.addImm(StartOffset);
  if (LowReg == HighReg)
    MIB.addReg(0);

  // Do a second scan adding regs as being defined by instruction
  for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
    unsigned Reg = CSI[i].getReg();
    if (Reg != LowReg && Reg != HighReg)
      MIB.addReg(Reg, RegState::ImplicitDefine);
  }

  return true;
}

unsigned
SystemZInstrInfo::InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB,
                              MachineBasicBlock *FBB,
                            const SmallVectorImpl<MachineOperand> &Cond) const {
  // FIXME this should probably have a DebugLoc operand
  DebugLoc dl = DebugLoc::getUnknownLoc();
  // Shouldn't be a fall through.
  assert(TBB && "InsertBranch must not be told to insert a fallthrough");
  assert((Cond.size() == 1 || Cond.size() == 0) &&
         "SystemZ branch conditions have one component!");

  if (Cond.empty()) {
    // Unconditional branch?
    assert(!FBB && "Unconditional branch with multiple successors!");
    BuildMI(&MBB, dl, get(SystemZ::JMP)).addMBB(TBB);
    return 1;
  }

  // Conditional branch.
  unsigned Count = 0;
  SystemZCC::CondCodes CC = (SystemZCC::CondCodes)Cond[0].getImm();
  BuildMI(&MBB, dl, getBrCond(CC)).addMBB(TBB);
  ++Count;

  if (FBB) {
    // Two-way Conditional branch. Insert the second branch.
    BuildMI(&MBB, dl, get(SystemZ::JMP)).addMBB(FBB);
    ++Count;
  }
  return Count;
}

const TargetInstrDesc&
SystemZInstrInfo::getBrCond(SystemZCC::CondCodes CC) const {
  unsigned Opc;
  switch (CC) {
  default:
    assert(0 && "Unknown condition code!");
  case SystemZCC::E:
    Opc = SystemZ::JE;
    break;
  case SystemZCC::NE:
    Opc = SystemZ::JNE;
    break;
  case SystemZCC::H:
    Opc = SystemZ::JH;
    break;
  case SystemZCC::L:
    Opc = SystemZ::JL;
    break;
  case SystemZCC::HE:
    Opc = SystemZ::JHE;
    break;
  case SystemZCC::LE:
    Opc = SystemZ::JLE;
    break;
  }

  return get(Opc);
}
<|endoftext|>


<commit_before>//===-- llvm/Target/TargetLoweringObjectFile.cpp - Object File Info -------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements classes used to handle lowerings specific to common
// object file formats.
//
//===----------------------------------------------------------------------===//

#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Mangler.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetLowering.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;

//===----------------------------------------------------------------------===//
//                              Generic Code
//===----------------------------------------------------------------------===//

/// Initialize - this method must be called before any actual lowering is
/// done.  This specifies the current context for codegen, and gives the
/// lowering implementations a chance to set up their default sections.
void TargetLoweringObjectFile::Initialize(MCContext &ctx,
                                          const TargetMachine &TM) {
  Ctx = &ctx;
  DL = TM.getDataLayout();
  InitMCObjectFileInfo(TM.getTargetTriple(),
                       TM.getRelocationModel(), TM.getCodeModel(), *Ctx);
}

TargetLoweringObjectFile::~TargetLoweringObjectFile() {
}

static bool isSuitableForBSS(const GlobalVariable *GV, bool NoZerosInBSS) {
  const Constant *C = GV->getInitializer();

  // Must have zero initializer.
  if (!C->isNullValue())
    return false;

  // Leave constant zeros in readonly constant sections, so they can be shared.
  if (GV->isConstant())
    return false;

  // If the global has an explicit section specified, don't put it in BSS.
  if (!GV->getSection().empty())
    return false;

  // If -nozero-initialized-in-bss is specified, don't ever use BSS.
  if (NoZerosInBSS)
    return false;

  // Otherwise, put it in BSS!
  return true;
}

/// IsNullTerminatedString - Return true if the specified constant (which is
/// known to have a type that is an array of 1/2/4 byte elements) ends with a
/// nul value and contains no other nuls in it.  Note that this is more general
/// than ConstantDataSequential::isString because we allow 2 & 4 byte strings.
static bool IsNullTerminatedString(const Constant *C) {
  // First check: is we have constant array terminated with zero
  if (const ConstantDataSequential *CDS = dyn_cast<ConstantDataSequential>(C)) {
    unsigned NumElts = CDS->getNumElements();
    assert(NumElts != 0 && "Can't have an empty CDS");

    if (CDS->getElementAsInteger(NumElts-1) != 0)
      return false; // Not null terminated.

    // Verify that the null doesn't occur anywhere else in the string.
    for (unsigned i = 0; i != NumElts-1; ++i)
      if (CDS->getElementAsInteger(i) == 0)
        return false;
    return true;
  }

  // Another possibility: [1 x i8] zeroinitializer
  if (isa<ConstantAggregateZero>(C))
    return cast<ArrayType>(C->getType())->getNumElements() == 1;

  return false;
}

MCSymbol *TargetLoweringObjectFile::getSymbolWithGlobalValueBase(
    const GlobalValue *GV, StringRef Suffix, Mangler &Mang,
    const TargetMachine &TM) const {
  assert(!Suffix.empty());

  SmallString<60> NameStr;
  NameStr += DL->getPrivateGlobalPrefix();
  TM.getNameWithPrefix(NameStr, GV, Mang);
  NameStr.append(Suffix.begin(), Suffix.end());
  return Ctx->GetOrCreateSymbol(NameStr.str());
}

MCSymbol *TargetLoweringObjectFile::getCFIPersonalitySymbol(
    const GlobalValue *GV, Mangler &Mang, const TargetMachine &TM,
    MachineModuleInfo *MMI) const {
  return TM.getSymbol(GV, Mang);
}

void TargetLoweringObjectFile::emitPersonalityValue(MCStreamer &Streamer,
                                                    const TargetMachine &TM,
                                                    const MCSymbol *Sym) const {
}

/// getKindForGlobal - This is a top-level target-independent classifier for
/// a global variable.  Given an global variable and information from TM, it
/// classifies the global in a variety of ways that make various target
/// implementations simpler.  The target implementation is free to ignore this
/// extra info of course.
SectionKind TargetLoweringObjectFile::getKindForGlobal(const GlobalValue *GV,
                                                       const TargetMachine &TM){
  assert(!GV->isDeclaration() && !GV->hasAvailableExternallyLinkage() &&
         "Can only be used for global definitions");

  Reloc::Model ReloModel = TM.getRelocationModel();

  // Early exit - functions should be always in text sections.
  const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV);
  if (GVar == 0)
    return SectionKind::getText();

  // Handle thread-local data first.
  if (GVar->isThreadLocal()) {
    if (isSuitableForBSS(GVar, TM.Options.NoZerosInBSS))
      return SectionKind::getThreadBSS();
    return SectionKind::getThreadData();
  }

  // Variables with common linkage always get classified as common.
  if (GVar->hasCommonLinkage())
    return SectionKind::getCommon();

  // Variable can be easily put to BSS section.
  if (isSuitableForBSS(GVar, TM.Options.NoZerosInBSS)) {
    if (GVar->hasLocalLinkage())
      return SectionKind::getBSSLocal();
    else if (GVar->hasExternalLinkage())
      return SectionKind::getBSSExtern();
    return SectionKind::getBSS();
  }

  const Constant *C = GVar->getInitializer();

  // If the global is marked constant, we can put it into a mergable section,
  // a mergable string section, or general .data if it contains relocations.
  if (GVar->isConstant()) {
    // If the initializer for the global contains something that requires a
    // relocation, then we may have to drop this into a writable data section
    // even though it is marked const.
    switch (C->getRelocationInfo()) {
    case Constant::NoRelocation:
      // If the global is required to have a unique address, it can't be put
      // into a mergable section: just drop it into the general read-only
      // section instead.
      if (!GVar->hasUnnamedAddr())
        return SectionKind::getReadOnly();

      // If initializer is a null-terminated string, put it in a "cstring"
      // section of the right width.
      if (ArrayType *ATy = dyn_cast<ArrayType>(C->getType())) {
        if (IntegerType *ITy =
              dyn_cast<IntegerType>(ATy->getElementType())) {
          if ((ITy->getBitWidth() == 8 || ITy->getBitWidth() == 16 ||
               ITy->getBitWidth() == 32) &&
              IsNullTerminatedString(C)) {
            if (ITy->getBitWidth() == 8)
              return SectionKind::getMergeable1ByteCString();
            if (ITy->getBitWidth() == 16)
              return SectionKind::getMergeable2ByteCString();

            assert(ITy->getBitWidth() == 32 && "Unknown width");
            return SectionKind::getMergeable4ByteCString();
          }
        }
      }

      // Otherwise, just drop it into a mergable constant section.  If we have
      // a section for this size, use it, otherwise use the arbitrary sized
      // mergable section.
      switch (TM.getDataLayout()->getTypeAllocSize(C->getType())) {
      case 4:  return SectionKind::getMergeableConst4();
      case 8:  return SectionKind::getMergeableConst8();
      case 16: return SectionKind::getMergeableConst16();
      default: return SectionKind::getMergeableConst();
      }

    case Constant::LocalRelocation:
      // In static relocation model, the linker will resolve all addresses, so
      // the relocation entries will actually be constants by the time the app
      // starts up.  However, we can't put this into a mergable section, because
      // the linker doesn't take relocations into consideration when it tries to
      // merge entries in the section.
      if (ReloModel == Reloc::Static)
        return SectionKind::getReadOnly();

      // Otherwise, the dynamic linker needs to fix it up, put it in the
      // writable data.rel.local section.
      return SectionKind::getReadOnlyWithRelLocal();

    case Constant::GlobalRelocations:
      // In static relocation model, the linker will resolve all addresses, so
      // the relocation entries will actually be constants by the time the app
      // starts up.  However, we can't put this into a mergable section, because
      // the linker doesn't take relocations into consideration when it tries to
      // merge entries in the section.
      if (ReloModel == Reloc::Static)
        return SectionKind::getReadOnly();

      // Otherwise, the dynamic linker needs to fix it up, put it in the
      // writable data.rel section.
      return SectionKind::getReadOnlyWithRel();
    }
  }

  // Okay, this isn't a constant.  If the initializer for the global is going
  // to require a runtime relocation by the dynamic linker, put it into a more
  // specific section to improve startup time of the app.  This coalesces these
  // globals together onto fewer pages, improving the locality of the dynamic
  // linker.
  if (ReloModel == Reloc::Static)
    return SectionKind::getDataNoRel();

  switch (C->getRelocationInfo()) {
  case Constant::NoRelocation:
    return SectionKind::getDataNoRel();
  case Constant::LocalRelocation:
    return SectionKind::getDataRelLocal();
  case Constant::GlobalRelocations:
    return SectionKind::getDataRel();
  }
  llvm_unreachable("Invalid relocation");
}

/// SectionForGlobal - This method computes the appropriate section to emit
/// the specified global variable or function definition.  This should not
/// be passed external (or available externally) globals.
const MCSection *TargetLoweringObjectFile::
SectionForGlobal(const GlobalValue *GV, SectionKind Kind, Mangler &Mang,
                 const TargetMachine &TM) const {
  // Select section name.
  if (GV->hasSection())
    return getExplicitSectionGlobal(GV, Kind, Mang, TM);

  // Use default section depending on the 'type' of global
  return SelectSectionForGlobal(GV, Kind, Mang, TM);
}

bool TargetLoweringObjectFile::isSectionAtomizableBySymbols(
    const MCSection &Section) const {
  return false;
}

// Lame default implementation. Calculate the section name for global.
const MCSection *
TargetLoweringObjectFile::SelectSectionForGlobal(const GlobalValue *GV,
                                                 SectionKind Kind,
                                                 Mangler &Mang,
                                                 const TargetMachine &TM) const{
  assert(!Kind.isThreadLocal() && "Doesn't support TLS");

  if (Kind.isText())
    return getTextSection();

  if (Kind.isBSS() && BSSSection != 0)
    return BSSSection;

  if (Kind.isReadOnly() && ReadOnlySection != 0)
    return ReadOnlySection;

  return getDataSection();
}

/// getSectionForConstant - Given a mergable constant with the
/// specified size and relocation information, return a section that it
/// should be placed in.
const MCSection *
TargetLoweringObjectFile::getSectionForConstant(SectionKind Kind) const {
  if (Kind.isReadOnly() && ReadOnlySection != 0)
    return ReadOnlySection;

  return DataSection;
}

/// getTTypeGlobalReference - Return an MCExpr to use for a
/// reference to the specified global variable from exception
/// handling information.
const MCExpr *TargetLoweringObjectFile::getTTypeGlobalReference(
    const GlobalValue *GV, unsigned Encoding, Mangler &Mang,
    const TargetMachine &TM, MachineModuleInfo *MMI,
    MCStreamer &Streamer) const {
  const MCSymbolRefExpr *Ref =
      MCSymbolRefExpr::Create(TM.getSymbol(GV, Mang), getContext());

  return getTTypeReference(Ref, Encoding, Streamer);
}

const MCExpr *TargetLoweringObjectFile::
getTTypeReference(const MCSymbolRefExpr *Sym, unsigned Encoding,
                  MCStreamer &Streamer) const {
  switch (Encoding & 0x70) {
  default:
    report_fatal_error("We do not support this DWARF encoding yet!");
  case dwarf::DW_EH_PE_absptr:
    // Do nothing special
    return Sym;
  case dwarf::DW_EH_PE_pcrel: {
    // Emit a label to the streamer for the current position.  This gives us
    // .-foo addressing.
    MCSymbol *PCSym = getContext().CreateTempSymbol();
    Streamer.EmitLabel(PCSym);
    const MCExpr *PC = MCSymbolRefExpr::Create(PCSym, getContext());
    return MCBinaryExpr::CreateSub(Sym, PC, getContext());
  }
  }
}

const MCExpr *TargetLoweringObjectFile::getDebugThreadLocalSymbol(const MCSymbol *Sym) const {
  // FIXME: It's not clear what, if any, default this should have - perhaps a
  // null return could mean 'no location' & we should just do that here.
  return MCSymbolRefExpr::Create(Sym, *Ctx);
}
<commit_msg>Target: whitespace<commit_after>//===-- llvm/Target/TargetLoweringObjectFile.cpp - Object File Info -------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements classes used to handle lowerings specific to common
// object file formats.
//
//===----------------------------------------------------------------------===//

#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Mangler.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetLowering.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;

//===----------------------------------------------------------------------===//
//                              Generic Code
//===----------------------------------------------------------------------===//

/// Initialize - this method must be called before any actual lowering is
/// done.  This specifies the current context for codegen, and gives the
/// lowering implementations a chance to set up their default sections.
void TargetLoweringObjectFile::Initialize(MCContext &ctx,
                                          const TargetMachine &TM) {
  Ctx = &ctx;
  DL = TM.getDataLayout();
  InitMCObjectFileInfo(TM.getTargetTriple(),
                       TM.getRelocationModel(), TM.getCodeModel(), *Ctx);
}

TargetLoweringObjectFile::~TargetLoweringObjectFile() {
}

static bool isSuitableForBSS(const GlobalVariable *GV, bool NoZerosInBSS) {
  const Constant *C = GV->getInitializer();

  // Must have zero initializer.
  if (!C->isNullValue())
    return false;

  // Leave constant zeros in readonly constant sections, so they can be shared.
  if (GV->isConstant())
    return false;

  // If the global has an explicit section specified, don't put it in BSS.
  if (!GV->getSection().empty())
    return false;

  // If -nozero-initialized-in-bss is specified, don't ever use BSS.
  if (NoZerosInBSS)
    return false;

  // Otherwise, put it in BSS!
  return true;
}

/// IsNullTerminatedString - Return true if the specified constant (which is
/// known to have a type that is an array of 1/2/4 byte elements) ends with a
/// nul value and contains no other nuls in it.  Note that this is more general
/// than ConstantDataSequential::isString because we allow 2 & 4 byte strings.
static bool IsNullTerminatedString(const Constant *C) {
  // First check: is we have constant array terminated with zero
  if (const ConstantDataSequential *CDS = dyn_cast<ConstantDataSequential>(C)) {
    unsigned NumElts = CDS->getNumElements();
    assert(NumElts != 0 && "Can't have an empty CDS");

    if (CDS->getElementAsInteger(NumElts-1) != 0)
      return false; // Not null terminated.

    // Verify that the null doesn't occur anywhere else in the string.
    for (unsigned i = 0; i != NumElts-1; ++i)
      if (CDS->getElementAsInteger(i) == 0)
        return false;
    return true;
  }

  // Another possibility: [1 x i8] zeroinitializer
  if (isa<ConstantAggregateZero>(C))
    return cast<ArrayType>(C->getType())->getNumElements() == 1;

  return false;
}

MCSymbol *TargetLoweringObjectFile::getSymbolWithGlobalValueBase(
    const GlobalValue *GV, StringRef Suffix, Mangler &Mang,
    const TargetMachine &TM) const {
  assert(!Suffix.empty());

  SmallString<60> NameStr;
  NameStr += DL->getPrivateGlobalPrefix();
  TM.getNameWithPrefix(NameStr, GV, Mang);
  NameStr.append(Suffix.begin(), Suffix.end());
  return Ctx->GetOrCreateSymbol(NameStr.str());
}

MCSymbol *TargetLoweringObjectFile::getCFIPersonalitySymbol(
    const GlobalValue *GV, Mangler &Mang, const TargetMachine &TM,
    MachineModuleInfo *MMI) const {
  return TM.getSymbol(GV, Mang);
}

void TargetLoweringObjectFile::emitPersonalityValue(MCStreamer &Streamer,
                                                    const TargetMachine &TM,
                                                    const MCSymbol *Sym) const {
}

/// getKindForGlobal - This is a top-level target-independent classifier for
/// a global variable.  Given an global variable and information from TM, it
/// classifies the global in a variety of ways that make various target
/// implementations simpler.  The target implementation is free to ignore this
/// extra info of course.
SectionKind TargetLoweringObjectFile::getKindForGlobal(const GlobalValue *GV,
                                                       const TargetMachine &TM){
  assert(!GV->isDeclaration() && !GV->hasAvailableExternallyLinkage() &&
         "Can only be used for global definitions");

  Reloc::Model ReloModel = TM.getRelocationModel();

  // Early exit - functions should be always in text sections.
  const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV);
  if (GVar == 0)
    return SectionKind::getText();

  // Handle thread-local data first.
  if (GVar->isThreadLocal()) {
    if (isSuitableForBSS(GVar, TM.Options.NoZerosInBSS))
      return SectionKind::getThreadBSS();
    return SectionKind::getThreadData();
  }

  // Variables with common linkage always get classified as common.
  if (GVar->hasCommonLinkage())
    return SectionKind::getCommon();

  // Variable can be easily put to BSS section.
  if (isSuitableForBSS(GVar, TM.Options.NoZerosInBSS)) {
    if (GVar->hasLocalLinkage())
      return SectionKind::getBSSLocal();
    else if (GVar->hasExternalLinkage())
      return SectionKind::getBSSExtern();
    return SectionKind::getBSS();
  }

  const Constant *C = GVar->getInitializer();

  // If the global is marked constant, we can put it into a mergable section,
  // a mergable string section, or general .data if it contains relocations.
  if (GVar->isConstant()) {
    // If the initializer for the global contains something that requires a
    // relocation, then we may have to drop this into a writable data section
    // even though it is marked const.
    switch (C->getRelocationInfo()) {
    case Constant::NoRelocation:
      // If the global is required to have a unique address, it can't be put
      // into a mergable section: just drop it into the general read-only
      // section instead.
      if (!GVar->hasUnnamedAddr())
        return SectionKind::getReadOnly();

      // If initializer is a null-terminated string, put it in a "cstring"
      // section of the right width.
      if (ArrayType *ATy = dyn_cast<ArrayType>(C->getType())) {
        if (IntegerType *ITy =
              dyn_cast<IntegerType>(ATy->getElementType())) {
          if ((ITy->getBitWidth() == 8 || ITy->getBitWidth() == 16 ||
               ITy->getBitWidth() == 32) &&
              IsNullTerminatedString(C)) {
            if (ITy->getBitWidth() == 8)
              return SectionKind::getMergeable1ByteCString();
            if (ITy->getBitWidth() == 16)
              return SectionKind::getMergeable2ByteCString();

            assert(ITy->getBitWidth() == 32 && "Unknown width");
            return SectionKind::getMergeable4ByteCString();
          }
        }
      }

      // Otherwise, just drop it into a mergable constant section.  If we have
      // a section for this size, use it, otherwise use the arbitrary sized
      // mergable section.
      switch (TM.getDataLayout()->getTypeAllocSize(C->getType())) {
      case 4:  return SectionKind::getMergeableConst4();
      case 8:  return SectionKind::getMergeableConst8();
      case 16: return SectionKind::getMergeableConst16();
      default: return SectionKind::getMergeableConst();
      }

    case Constant::LocalRelocation:
      // In static relocation model, the linker will resolve all addresses, so
      // the relocation entries will actually be constants by the time the app
      // starts up.  However, we can't put this into a mergable section, because
      // the linker doesn't take relocations into consideration when it tries to
      // merge entries in the section.
      if (ReloModel == Reloc::Static)
        return SectionKind::getReadOnly();

      // Otherwise, the dynamic linker needs to fix it up, put it in the
      // writable data.rel.local section.
      return SectionKind::getReadOnlyWithRelLocal();

    case Constant::GlobalRelocations:
      // In static relocation model, the linker will resolve all addresses, so
      // the relocation entries will actually be constants by the time the app
      // starts up.  However, we can't put this into a mergable section, because
      // the linker doesn't take relocations into consideration when it tries to
      // merge entries in the section.
      if (ReloModel == Reloc::Static)
        return SectionKind::getReadOnly();

      // Otherwise, the dynamic linker needs to fix it up, put it in the
      // writable data.rel section.
      return SectionKind::getReadOnlyWithRel();
    }
  }

  // Okay, this isn't a constant.  If the initializer for the global is going
  // to require a runtime relocation by the dynamic linker, put it into a more
  // specific section to improve startup time of the app.  This coalesces these
  // globals together onto fewer pages, improving the locality of the dynamic
  // linker.
  if (ReloModel == Reloc::Static)
    return SectionKind::getDataNoRel();

  switch (C->getRelocationInfo()) {
  case Constant::NoRelocation:
    return SectionKind::getDataNoRel();
  case Constant::LocalRelocation:
    return SectionKind::getDataRelLocal();
  case Constant::GlobalRelocations:
    return SectionKind::getDataRel();
  }
  llvm_unreachable("Invalid relocation");
}

/// SectionForGlobal - This method computes the appropriate section to emit
/// the specified global variable or function definition.  This should not
/// be passed external (or available externally) globals.
const MCSection *TargetLoweringObjectFile::
SectionForGlobal(const GlobalValue *GV, SectionKind Kind, Mangler &Mang,
                 const TargetMachine &TM) const {
  // Select section name.
  if (GV->hasSection())
    return getExplicitSectionGlobal(GV, Kind, Mang, TM);

  // Use default section depending on the 'type' of global
  return SelectSectionForGlobal(GV, Kind, Mang, TM);
}

bool TargetLoweringObjectFile::isSectionAtomizableBySymbols(
    const MCSection &Section) const {
  return false;
}

// Lame default implementation. Calculate the section name for global.
const MCSection *
TargetLoweringObjectFile::SelectSectionForGlobal(const GlobalValue *GV,
                                                 SectionKind Kind,
                                                 Mangler &Mang,
                                                 const TargetMachine &TM) const{
  assert(!Kind.isThreadLocal() && "Doesn't support TLS");

  if (Kind.isText())
    return getTextSection();

  if (Kind.isBSS() && BSSSection != 0)
    return BSSSection;

  if (Kind.isReadOnly() && ReadOnlySection != 0)
    return ReadOnlySection;

  return getDataSection();
}

/// getSectionForConstant - Given a mergable constant with the
/// specified size and relocation information, return a section that it
/// should be placed in.
const MCSection *
TargetLoweringObjectFile::getSectionForConstant(SectionKind Kind) const {
  if (Kind.isReadOnly() && ReadOnlySection != 0)
    return ReadOnlySection;

  return DataSection;
}

/// getTTypeGlobalReference - Return an MCExpr to use for a
/// reference to the specified global variable from exception
/// handling information.
const MCExpr *TargetLoweringObjectFile::getTTypeGlobalReference(
    const GlobalValue *GV, unsigned Encoding, Mangler &Mang,
    const TargetMachine &TM, MachineModuleInfo *MMI,
    MCStreamer &Streamer) const {
  const MCSymbolRefExpr *Ref =
      MCSymbolRefExpr::Create(TM.getSymbol(GV, Mang), getContext());

  return getTTypeReference(Ref, Encoding, Streamer);
}

const MCExpr *TargetLoweringObjectFile::
getTTypeReference(const MCSymbolRefExpr *Sym, unsigned Encoding,
                  MCStreamer &Streamer) const {
  switch (Encoding & 0x70) {
  default:
    report_fatal_error("We do not support this DWARF encoding yet!");
  case dwarf::DW_EH_PE_absptr:
    // Do nothing special
    return Sym;
  case dwarf::DW_EH_PE_pcrel: {
    // Emit a label to the streamer for the current position.  This gives us
    // .-foo addressing.
    MCSymbol *PCSym = getContext().CreateTempSymbol();
    Streamer.EmitLabel(PCSym);
    const MCExpr *PC = MCSymbolRefExpr::Create(PCSym, getContext());
    return MCBinaryExpr::CreateSub(Sym, PC, getContext());
  }
  }
}

const MCExpr *TargetLoweringObjectFile::getDebugThreadLocalSymbol(const MCSymbol *Sym) const {
  // FIXME: It's not clear what, if any, default this should have - perhaps a
  // null return could mean 'no location' & we should just do that here.
  return MCSymbolRefExpr::Create(Sym, *Ctx);
}
<|endoftext|>


<commit_before>#include <lms/internal/file_monitor.h>

#ifdef __APPLE__
    // TODO
#else
    #include <unistd.h>
#endif

//http://www.ibm.com/developerworks/linux/library/l-ubuntu-inotify/index.html

namespace lms {
namespace extra {

FileMonitor::FileMonitor() {
#ifdef __APPLE__
    // TODO
#else
    fd = inotify_init1(IN_NONBLOCK);
    //fd = inotify_init();
#endif
}

FileMonitor::~FileMonitor() {
#ifdef __APPLE__
    // TODO
#else
    close(fd);
#endif
}

FileMonitor::operator bool () const {
#ifdef __APPLE__
    // TODO
    return true;
#else
    return fd != -1;
#endif
}

bool FileMonitor::watch(const std::string &path) {
#ifdef __APPLE__
    time_t lastMod;
    if(lastModified(path, lastMod)) {
        files.push_back(std::make_pair(path, lastMod));
        return true;
    }
    return false;
#else
    int wd = inotify_add_watch(fd, path.c_str(), MASK);
    return wd != -1;
#endif
}

void FileMonitor::unwatchAll() {
#ifdef __APPLE__
    files.clear();
#else
    close(fd);
    fd = inotify_init1(IN_NONBLOCK);
#endif
}

bool FileMonitor::hasChangedFiles() {
#ifdef __APPLE__
    bool changes = false;
    for(File& file : files) {
        time_t newLastMod;
        if(lastModified(file.first, newLastMod)) {
            if(newLastModified != file.second) {
                changes = true;
                file.second = newLastModified;
            }
        }
    }
    return changes;
#else
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    int retval = select(fd + 1, &rfds, NULL, NULL, &tv);

    if(retval == -1) {
        perror("hasChangedFiles");
    } else if(retval == 0) {
        //printf("select returned 0\n");
    } else if (retval > 0) {
        if(FD_ISSET(fd, &rfds)) {
            char buf[BUF_SIZE];
            int len = 0;
            inotify_event *evt;
            char *p;

            while((len = read(fd, buf, BUF_SIZE)) > 0) {
                if(len == 0) {
                    printf("read returned 0\n");
                }

                for (p = buf; p < buf + len; ) {
                    evt = (inotify_event *) p;

                    if(checkEvent(evt)) {
                        return true;
                    }

                    p += sizeof(inotify_event) + evt->len;
                }
            }
        }
    }

    return false;
#endif
}

#ifdef __APPLE__
bool FileMonitor::lastModified(const std::string& path, time_t& t) {
    struct stat fileStats;
    if(stat(path.c_str(), &fileStats) == 0) {
        t = fileStats.st_mtime;
        return true;
    }

    return false;
}
#endif

#ifndef __APPLE__
bool FileMonitor::checkEvent(inotify_event *evt) {
    // check for either MODIFY, MOVE_SELF or DELETE
    return (evt->mask & MASK) || (evt->mask & IN_IGNORED);
}
#endif

}  // namespace extra
}  // namespace lms
<commit_msg>Fix OS X filemonitor<commit_after>#include <lms/internal/file_monitor.h>

#ifdef __APPLE__
    // TODO
#else
    #include <unistd.h>
#endif

//http://www.ibm.com/developerworks/linux/library/l-ubuntu-inotify/index.html

namespace lms {
namespace extra {

FileMonitor::FileMonitor() {
#ifdef __APPLE__
    // TODO
#else
    fd = inotify_init1(IN_NONBLOCK);
    //fd = inotify_init();
#endif
}

FileMonitor::~FileMonitor() {
#ifdef __APPLE__
    // TODO
#else
    close(fd);
#endif
}

FileMonitor::operator bool () const {
#ifdef __APPLE__
    // TODO
    return true;
#else
    return fd != -1;
#endif
}

bool FileMonitor::watch(const std::string &path) {
#ifdef __APPLE__
    time_t lastMod;
    if(lastModified(path, lastMod)) {
        files.push_back(std::make_pair(path, lastMod));
        return true;
    }
    return false;
#else
    int wd = inotify_add_watch(fd, path.c_str(), MASK);
    return wd != -1;
#endif
}

void FileMonitor::unwatchAll() {
#ifdef __APPLE__
    files.clear();
#else
    close(fd);
    fd = inotify_init1(IN_NONBLOCK);
#endif
}

bool FileMonitor::hasChangedFiles() {
#ifdef __APPLE__
    bool changes = false;
    for(File& file : files) {
        time_t newLastMod;
        if(lastModified(file.first, newLastMod)) {
            if(newLastMod != file.second) {
                changes = true;
                file.second = newLastMod;
            }
        }
    }
    return changes;
#else
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    int retval = select(fd + 1, &rfds, NULL, NULL, &tv);

    if(retval == -1) {
        perror("hasChangedFiles");
    } else if(retval == 0) {
        //printf("select returned 0\n");
    } else if (retval > 0) {
        if(FD_ISSET(fd, &rfds)) {
            char buf[BUF_SIZE];
            int len = 0;
            inotify_event *evt;
            char *p;

            while((len = read(fd, buf, BUF_SIZE)) > 0) {
                if(len == 0) {
                    printf("read returned 0\n");
                }

                for (p = buf; p < buf + len; ) {
                    evt = (inotify_event *) p;

                    if(checkEvent(evt)) {
                        return true;
                    }

                    p += sizeof(inotify_event) + evt->len;
                }
            }
        }
    }

    return false;
#endif
}

#ifdef __APPLE__
bool FileMonitor::lastModified(const std::string& path, time_t& t) {
    struct stat fileStats;
    if(stat(path.c_str(), &fileStats) == 0) {
        t = fileStats.st_mtime;
        return true;
    }

    return false;
}
#endif

#ifndef __APPLE__
bool FileMonitor::checkEvent(inotify_event *evt) {
    // check for either MODIFY, MOVE_SELF or DELETE
    return (evt->mask & MASK) || (evt->mask & IN_IGNORED);
}
#endif

}  // namespace extra
}  // namespace lms
<|endoftext|>


<commit_before>/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#if 0
#include "base/EncodingUtils.h"
#endif

#include "base/util/StringBuffer.h"
#include "spdm/constants.h"
#include "push/CTPConfig.h"
#include "base/globalsdef.h"

USE_NAMESPACE

#define CTP_QUEUE_PUSH                  "queuePush"
#define CTP_RETRY                       "ctpRetry"
#define CTP_MAX_RETRY_TIMEOUT           "maxCtpRetry"
#define CTP_CMD_TIMEOUT                 "ctpCmdTimeout"
#define CTP_CONN_TIMEOUT                "ctpConnTimeout"
#define CTP_PORT                        "ctpPort"
#define CTP_READY                       "ctpReady"
#define PROPERTY_CTP_SERVER             "ctpServer"
#define PROPERTY_NOTIFY_TIMEOUT         "notifyTimeout"

CTPConfig::CTPConfig(const char* application_uri)
                    : DMTClientConfig(application_uri) {
}

CTPConfig::~CTPConfig() {}

char* CTPConfig::decodePassword(const char* /* password */) {

    char* decripted = NULL;
#if 0
    // Encryption not supported yet!!! TODO FIXME
    if (password && strlen(password) > 0) {
         decripted = decryptData(password);
    }

    if (decripted == NULL || getLastErrorCode() == 801) {
        decripted = new char[1];
        decripted[0] = 0;
        setError(ERR_NONE, "");
    }
#endif
    return decripted;
}

StringBuffer CTPConfig::encodePassword(const char* /* password */) {

    StringBuffer buffer("");
#if 0
    // Encryption not supported yet!!! TODO FIXME
    if (password && strlen(password) > 0) {
        char* encoded = encryptData(password);
        buffer = encoded;
    }
#endif
    return buffer;
}

void CTPConfig::readCTPConfig() {

    if (!open()) {
        LOG.error("Impossible read the ctp configuration. exit");
        return;
    }

    if (readAccessConfig(*syncMLNode)) {
        setUsername(accessConfig.getUsername());
        // urlTo is loaded from 'ctpServer' key
        // nonce is read calling 'readAccessConfig()'
    }

    bool passEncoded = false;
    // TODO we should stringbuffer instead of malloc/new strings
    char* passDecoded;
    if (passEncoded) {
#if 0
        // FIXME TODO
        passDecoded = decodePassword(accessConfig.getPassword());
        accessConfig.setPassword(passDecoded);
#endif
    } else {
        passDecoded = stringdup(accessConfig.getPassword());
        accessConfig.setPassword(passDecoded);
    }
    delete [] passDecoded;

    if (readDeviceConfig(*syncMLNode)) {
        setDeviceId(deviceConfig.getDevID());

    }

    // now read the single CTP properties
    ManagementNode* node;
    char nodeName[DIM_MANAGEMENT_PATH];
    nodeName[0] = 0;
    sprintf(nodeName, "%s%s", rootContext, CONTEXT_PUSH_CTP);

    node = dmt->readManagementNode(nodeName);
    if (node)
    {
        char* tmp;
        tmp = node->readPropertyValue(PROPERTY_PUSH_NOTIFICATION);
        if (tmp) {
            setPush(atoi(tmp));
        } else {
            setPush(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(PROPERTY_POLLING_NOTIFICATION);
        if (tmp) {
            setPolling(atoi(tmp));
        } else {
            setPolling(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_QUEUE_PUSH);
        if (tmp) {
            setQueuePush(atoi(tmp) == 0 ? false : true);
        } else {
            setQueuePush(false);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(PROPERTY_NOTIFY_TIMEOUT);
        if (tmp) {
            setNotifyTimeout(atoi(tmp));
        } else {
            setNotifyTimeout(180);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_RETRY);
        if (tmp) {
            setCtpRetry(atoi(tmp));
        } else {
            setCtpRetry(5);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_MAX_RETRY_TIMEOUT);
        if (tmp) {
            setMaxCtpRetry(atoi(tmp));
        } else {
            setMaxCtpRetry(900); // 15 min
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_CMD_TIMEOUT);
        if (tmp) {
            setCtpCmdTimeout(atoi(tmp));
        } else {
            setCtpCmdTimeout(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_CONN_TIMEOUT);
        if (tmp) {
            setCtpConnTimeout(atoi(tmp));
        } else {
            setCtpConnTimeout(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_PORT);
        if (tmp) {
            setCtpPort(atoi(tmp));
        } else {
            setCtpPort(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_READY);
        if (tmp) {
            setCtpReady(atoi(tmp));
        } else {
            setCtpReady(5);
        }
        delete [] tmp;

        // server url is loaded from regstry key 'ctpServer'
        tmp = node->readPropertyValue(PROPERTY_CTP_SERVER);
        if (tmp && strlen(tmp)>0) {
            // *** workaround for prefix: remove this when not necessary! ***
            StringBuffer url = checkPrefix(tmp);
            LOG.debug("urlTo = %s", url.c_str());
            setUrlTo(url);
        } else {
            // If 'ctpServer' reg value is empty, extract the url from 'syncUrl'
            setUrlTo(getHostName(accessConfig.getSyncURL()));
        }
        delete [] tmp;

        delete node;
        node = NULL;
    }

    close();
    return;

}

void CTPConfig::saveCTPConfig() {

    LOG.debug("saveCTPConfig");

    ManagementNode* node = NULL;
    if (!open()) {
        return;
    }

    bool passwordEncoded = false;

    StringBuffer buffer;
    if (passwordEncoded) {
        buffer = encodePassword(accessConfig.getPassword());
    } else {
        buffer = accessConfig.getPassword();
    }
    accessConfig.setPassword(buffer.c_str());
    // Save the nonce: save AuthConfig properties
    char nodeName[DIM_MANAGEMENT_PATH];
    sprintf(nodeName, "%s%s%s", APPLICATION_URI, CONTEXT_SPDS_SYNCML, CONTEXT_AUTH);
    node = dmt->readManagementNode(nodeName);
    if (node) {
        saveAuthConfig(*syncMLNode, *node);
        delete node;
    }

    //char* passDecoded;
    if (passwordEncoded) {
#if 0
        // TODO FIXME
        passDecoded = decodePassword(accessConfig.getPassword());
        accessConfig.setPassword(passDecoded);
        delete [] passDecoded;
#endif
    } else {
        // Nothing to do: the password is already in clear text.
    }
    close();
}

StringBuffer CTPConfig::getHostName(StringBuffer syncUrl) {

    size_t start, end;
    StringBuffer host;
    // Extract the hostName from syncUrl: "http://<hostName>:8080/funambol/ds"
    start = syncUrl.find("://", 0);
    if (start != StringBuffer::npos) {
        start += 3;
    } else {
        // try to extract hostname from <hostName>:8080/funambol/ds"
        start = 0;
    }

    end = syncUrl.find(":", start);       // stop if ":" or "/" found
    if (end == StringBuffer::npos) {
        end = syncUrl.find("/", start);
        if (end == StringBuffer::npos) {  // so the url is only <hostName>
            end = syncUrl.length();
        }
    }

    if (end > start) {
        host = syncUrl.substr(start, end-start);
    }

    return host;
}

int CTPConfig::getHostPort(StringBuffer syncUrl) {

    size_t start, endSlash, endColon, urlLength;
    StringBuffer hostPort = "";
    urlLength = syncUrl.length();
    int port = 0;
    // Extract the port from syncUrl: "http://<hostName>:8080/funambol/ds"
    start = syncUrl.find("://", 0);
    if (start != StringBuffer::npos) {
        start += 3;
    } else {
        // try to extract the port from <hostName>:8080/funambol/ds"
        start = 0;
    }

    endSlash = syncUrl.find("/", start);       // stop if ":" or "/" found
    endColon = syncUrl.find(":", start);       // stop if ":" or "/" found

    if (endSlash == StringBuffer::npos && endColon == StringBuffer::npos) {
        // there is no port
        // hostname
    } else if (endSlash != StringBuffer::npos && endColon == StringBuffer::npos) {
        // there is no port
        // hostname/funambol
    } else if (endSlash == StringBuffer::npos && endColon != StringBuffer::npos) {
        // there is port
        // hostname:8080
        hostPort = syncUrl.substr(endColon + 1, urlLength - start);
    } else {
        if (endSlash > endColon) {
            // there is port
            // hostname:8080/funambol
            hostPort = syncUrl.substr(endColon + 1, endSlash - endColon - 1);
        }
    }

    if (hostPort != "") {
        port = atoi(hostPort.c_str());
    }

    return port;
}

/// If passed 'url' contains "prefix:" the value is appended
/// next to the host name: <prefix><hostName> is returned
/// (hostName is retrieved from SyncUrl)
/// Otherwise the passed 'url' is returned as StringBuffer.
StringBuffer CTPConfig::checkPrefix(char* url) {

    StringBuffer ctpUrl = url;
    const StringBuffer prefix = "prefix:";

    size_t pos = ctpUrl.find(prefix, 0);
    if (pos == StringBuffer::npos) {
        // Not found: keep the url passed
        return ctpUrl;
    }

    // Go after the ":"
    pos += prefix.length();
    if (pos != StringBuffer::npos) {
        // Add the prefix
        ctpUrl = ctpUrl.substr(pos, ctpUrl.length());
        // Append the syncUrl
        ctpUrl += getHostName(accessConfig.getSyncURL());
    }

    return ctpUrl;
}

<commit_msg>When saving the config, all props are saved to disk. Internal members 'username' and 'deviceID' have been removed, to avoid confusion: we use the params from the main config. So get methods are now proxy methods to those params in the main config.<commit_after>/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#if 0
#include "base/EncodingUtils.h"
#endif

#include "base/util/StringBuffer.h"
#include "spdm/constants.h"
#include "push/CTPConfig.h"
#include "base/globalsdef.h"

USE_NAMESPACE

#define CTP_QUEUE_PUSH                  "queuePush"
#define CTP_RETRY                       "ctpRetry"
#define CTP_MAX_RETRY_TIMEOUT           "maxCtpRetry"
#define CTP_CMD_TIMEOUT                 "ctpCmdTimeout"
#define CTP_CONN_TIMEOUT                "ctpConnTimeout"
#define CTP_PORT                        "ctpPort"
#define CTP_READY                       "ctpReady"
#define PROPERTY_CTP_SERVER             "ctpServer"
#define PROPERTY_NOTIFY_TIMEOUT         "notifyTimeout"

CTPConfig::CTPConfig(const char* application_uri)
                    : DMTClientConfig(application_uri) {
}

CTPConfig::~CTPConfig() {}

char* CTPConfig::decodePassword(const char* /* password */) {

    char* decripted = NULL;
#if 0
    // Encryption not supported yet!!! TODO FIXME
    if (password && strlen(password) > 0) {
         decripted = decryptData(password);
    }

    if (decripted == NULL || getLastErrorCode() == 801) {
        decripted = new char[1];
        decripted[0] = 0;
        setError(ERR_NONE, "");
    }
#endif
    return decripted;
}

StringBuffer CTPConfig::encodePassword(const char* /* password */) {

    StringBuffer buffer("");
#if 0
    // Encryption not supported yet!!! TODO FIXME
    if (password && strlen(password) > 0) {
        char* encoded = encryptData(password);
        buffer = encoded;
    }
#endif
    return buffer;
}

void CTPConfig::readCTPConfig() {

    if (!open()) {
        LOG.error("Impossible read the ctp configuration. exit");
        return;
    }

    readAccessConfig(*syncMLNode);      // nonce is read with this call
    readDeviceConfig(*syncMLNode);      // devID is read with this call

    bool passEncoded = false;
#if 0
    // TODO: decode password if encoded!
    passDecoded = decodePassword(accessConfig.getPassword());
    accessConfig.setPassword(passDecoded);
#endif

    //
    // Read the single CTP properties
    //
    ManagementNode* node;
    char nodeName[DIM_MANAGEMENT_PATH];
    nodeName[0] = 0;
    sprintf(nodeName, "%s%s", rootContext, CONTEXT_PUSH_CTP);

    node = dmt->readManagementNode(nodeName);
    if (node)
    {
        char* tmp;
        tmp = node->readPropertyValue(PROPERTY_PUSH_NOTIFICATION);
        if (tmp) {
            setPush(atoi(tmp));
        } else {
            setPush(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(PROPERTY_POLLING_NOTIFICATION);
        if (tmp) {
            setPolling(atoi(tmp));
        } else {
            setPolling(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_QUEUE_PUSH);
        if (tmp) {
            setQueuePush(atoi(tmp) == 0 ? false : true);
        } else {
            setQueuePush(false);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(PROPERTY_NOTIFY_TIMEOUT);
        if (tmp) {
            setNotifyTimeout(atoi(tmp));
        } else {
            setNotifyTimeout(180);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_RETRY);
        if (tmp) {
            setCtpRetry(atoi(tmp));
        } else {
            setCtpRetry(5);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_MAX_RETRY_TIMEOUT);
        if (tmp) {
            setMaxCtpRetry(atoi(tmp));
        } else {
            setMaxCtpRetry(900); // 15 min
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_CMD_TIMEOUT);
        if (tmp) {
            setCtpCmdTimeout(atoi(tmp));
        } else {
            setCtpCmdTimeout(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_CONN_TIMEOUT);
        if (tmp) {
            setCtpConnTimeout(atoi(tmp));
        } else {
            setCtpConnTimeout(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_PORT);
        if (tmp) {
            setCtpPort(atoi(tmp));
        } else {
            setCtpPort(0);
        }
        delete [] tmp;

        tmp = node->readPropertyValue(CTP_READY);
        if (tmp) {
            setCtpReady(atoi(tmp));
        } else {
            setCtpReady(5);
        }
        delete [] tmp;

        // server url is loaded from regstry key 'ctpServer'
        tmp = node->readPropertyValue(PROPERTY_CTP_SERVER);
        if (tmp && strlen(tmp)>0) {
            // *** workaround for prefix: remove this when not necessary! ***
            StringBuffer url = checkPrefix(tmp);
            LOG.debug("urlTo = %s", url.c_str());
            setUrlTo(url);
        } else {
            // If 'ctpServer' reg value is empty, extract the url from 'syncUrl'
            setUrlTo(getHostName(accessConfig.getSyncURL()));
        }
        delete [] tmp;

        delete node;
        node = NULL;
    }

    close();
}

void CTPConfig::saveCTPConfig() {

    LOG.debug("saveCTPConfig");

    ManagementNode* node = NULL;
    if (!open()) {
        return;
    }

    // TODO: encode password if necessary
    bool passwordEncoded = false;
    if (passwordEncoded) {
        StringBuffer buffer;
        buffer = encodePassword(accessConfig.getPassword());
        accessConfig.setPassword(buffer.c_str());
    }

    // Save the nonce: save AuthConfig properties
    StringBuffer nodeName("");
    nodeName.sprintf("%s%s%s", APPLICATION_URI, CONTEXT_SPDS_SYNCML, CONTEXT_AUTH);
    node = dmt->readManagementNode(nodeName.c_str());
    if (node) {
        saveAuthConfig(*syncMLNode, *node);
        delete node;
    }

    if (passwordEncoded) {
#if 0
        // TODO FIXME
        char* passDecoded = decodePassword(accessConfig.getPassword());
        accessConfig.setPassword(passDecoded);
        delete [] passDecoded;
#endif
    } else {
        // Nothing to do: the password is already in clear text.
    }

    //
    // Save the single CTP properties
    //
    nodeName.sprintf("%s%s", APPLICATION_URI, CONTEXT_PUSH_CTP);
    node = dmt->readManagementNode(nodeName.c_str());
    if (node)
    {
        // NOTE: Leave it empty: so it's generated each time we call readCTPConfig()
        // So when we change the ServerURL, the 'urlTo' value is changed accordingly.
        node->setPropertyValue(PROPERTY_CTP_SERVER, "");

        StringBuffer value("");
        value.sprintf("%ld", ctpCmdTimeout);
        node->setPropertyValue(CTP_CMD_TIMEOUT, value.c_str());

        value.sprintf("%ld", ctpConnTimeout);
        node->setPropertyValue(CTP_CONN_TIMEOUT, value.c_str());

        value.sprintf("%ld", ctpPort);
        node->setPropertyValue(CTP_PORT, value.c_str());

        value.sprintf("%ld", ctpReady);
        node->setPropertyValue(CTP_READY, value.c_str());

        value.sprintf("%ld", ctpRetry);
        node->setPropertyValue(CTP_RETRY, value.c_str());

        value.sprintf("%ld", maxCtpRetry);
        node->setPropertyValue(CTP_MAX_RETRY_TIMEOUT, value.c_str());

        value.sprintf("%ld", notifyTimeout);
        node->setPropertyValue(PROPERTY_NOTIFY_TIMEOUT, value.c_str());

        value.sprintf("%ld", maxCtpRetry);
        node->setPropertyValue(CTP_MAX_RETRY_TIMEOUT, value.c_str());

        /* -- Not used now --
        value.sprintf("%d", push);
        node->setPropertyValue(PROPERTY_PUSH_NOTIFICATION, value.c_str());

        value.sprintf("%d", polling);
        node->setPropertyValue(PROPERTY_POLLING_NOTIFICATION, value.c_str());

        value.sprintf("%d", queuePush);
        node->setPropertyValue(CTP_QUEUE_PUSH, value.c_str());
        */

        delete node;
    }

    close();
}

StringBuffer CTPConfig::getHostName(StringBuffer syncUrl) {

    size_t start, end;
    StringBuffer host;
    // Extract the hostName from syncUrl: "http://<hostName>:8080/funambol/ds"
    start = syncUrl.find("://", 0);
    if (start != StringBuffer::npos) {
        start += 3;
    } else {
        // try to extract hostname from <hostName>:8080/funambol/ds"
        start = 0;
    }

    end = syncUrl.find(":", start);       // stop if ":" or "/" found
    if (end == StringBuffer::npos) {
        end = syncUrl.find("/", start);
        if (end == StringBuffer::npos) {  // so the url is only <hostName>
            end = syncUrl.length();
        }
    }

    if (end > start) {
        host = syncUrl.substr(start, end-start);
    }

    return host;
}

int CTPConfig::getHostPort(StringBuffer syncUrl) {

    size_t start, endSlash, endColon, urlLength;
    StringBuffer hostPort = "";
    urlLength = syncUrl.length();
    int port = 0;
    // Extract the port from syncUrl: "http://<hostName>:8080/funambol/ds"
    start = syncUrl.find("://", 0);
    if (start != StringBuffer::npos) {
        start += 3;
    } else {
        // try to extract the port from <hostName>:8080/funambol/ds"
        start = 0;
    }

    endSlash = syncUrl.find("/", start);       // stop if ":" or "/" found
    endColon = syncUrl.find(":", start);       // stop if ":" or "/" found

    if (endSlash == StringBuffer::npos && endColon == StringBuffer::npos) {
        // there is no port
        // hostname
    } else if (endSlash != StringBuffer::npos && endColon == StringBuffer::npos) {
        // there is no port
        // hostname/funambol
    } else if (endSlash == StringBuffer::npos && endColon != StringBuffer::npos) {
        // there is port
        // hostname:8080
        hostPort = syncUrl.substr(endColon + 1, urlLength - start);
    } else {
        if (endSlash > endColon) {
            // there is port
            // hostname:8080/funambol
            hostPort = syncUrl.substr(endColon + 1, endSlash - endColon - 1);
        }
    }

    if (hostPort != "") {
        port = atoi(hostPort.c_str());
    }

    return port;
}

/// If passed 'url' contains "prefix:" the value is appended
/// next to the host name: <prefix><hostName> is returned
/// (hostName is retrieved from SyncUrl)
/// Otherwise the passed 'url' is returned as StringBuffer.
StringBuffer CTPConfig::checkPrefix(char* url) {

    StringBuffer ctpUrl = url;
    const StringBuffer prefix = "prefix:";

    size_t pos = ctpUrl.find(prefix, 0);
    if (pos == StringBuffer::npos) {
        // Not found: keep the url passed
        return ctpUrl;
    }

    // Go after the ":"
    pos += prefix.length();
    if (pos != StringBuffer::npos) {
        // Add the prefix
        ctpUrl = ctpUrl.substr(pos, ctpUrl.length());
        // Append the syncUrl
        ctpUrl += getHostName(accessConfig.getSyncURL());
    }

    return ctpUrl;
}

<|endoftext|>


<commit_before>// Copyright (c) 2016, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-pinocchio.
// hpp-pinocchio is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-pinocchio is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-pinocchio. If not, see <http://www.gnu.org/licenses/>.

#include "hpp/pinocchio/center-of-mass-computation.hh"

#include <algorithm>
#include <boost/foreach.hpp>
#include <boost/serialization/vector.hpp>

#include <pinocchio/algorithm/center-of-mass.hpp>
#include <pinocchio/algorithm/copy.hpp>

#include <hpp/util/exception-factory.hh>
#include <hpp/util/serialization.hh>

#include "hpp/pinocchio/joint.hh"
#include <hpp/pinocchio/joint-collection.hh>
#include "hpp/pinocchio/device.hh"
#include "hpp/pinocchio/serialization.hh"

namespace hpp {
  namespace pinocchio {
    CenterOfMassComputationPtr_t CenterOfMassComputation::create (
        const DevicePtr_t& d)
    {
      return CenterOfMassComputationPtr_t (new CenterOfMassComputation (d));
    }

    void CenterOfMassComputation::compute (DeviceData& d, const Computation_t& flag)
    {
      const Model& model = robot_->model();

      bool computeCOM = (flag & COM);
      bool computeJac = (flag & JACOBIAN);
      assert(computeCOM && "This does nothing");
      assert (!(computeJac && !computeCOM)); // JACOBIAN => COM

      Data& data = *d.data_;

      data.mass[0] = 0;
      if(computeCOM) data.com[0].setZero();
      if(computeJac) data.Jcom.setZero();

      // Propagate COM initialization on all joints.
      // Could be done only on subtree, with minor gain (possibly loss because of
      // more difficult branch prediction). I dont recommend to implement it.
      for(JointIndex jid=1;jid<JointIndex(model.joints.size());++jid)
        {
          const double &            mass  = model.inertias[jid].mass ();
          data.mass[jid] = mass;
          data.com [jid] = mass*data.oMi[jid].act (model.inertias[jid].lever());
        }

      // Nullify non-subtree com and mass.
      std::size_t root = 0;
      for(std::size_t jid=1; jid<model.joints.size(); ++jid )
        {
          const std::size_t& rootId = roots_[root];
          if(jid == rootId)
            {
              jid = (std::size_t)data.lastChild[rootId];
              root ++;
            }
          else
            {
              data.mass[jid] = 0;
              data.com [jid] .setZero();
            }
        }

      // TODO as of now, it is not possible to access the template parameter
      // JointCollectionTpl of Model so we use the default one.
      typedef ::pinocchio::JacobianCenterOfMassBackwardStep<Model::Scalar,
              Model::Options, JointCollectionTpl
#if PINOCCHIO_VERSION_AT_LEAST(2,1,6)
              ,Data::Matrix3x
#endif
                > Pass;

      // Assume root is sorted from smallest id.
      // Nasty cast below, from (u-long) size_t to int.
      for( int root=int(roots_.size()-1); root>=0; --root )
      {
        std::size_t rootId = roots_[(std::size_t)root];

        // Backward loop on descendents of joint rootId.
        for( JointIndex jid = (JointIndex)data.lastChild[rootId];jid>=rootId;--jid )
          {
            if(computeJac)
              Pass::run(model.joints[jid],data.joints[jid],
                      Pass::ArgsType(model,data,
#if PINOCCHIO_VERSION_AT_LEAST(2,1,6)
                        data.Jcom,
#endif
                        false));
            else
              {
                assert(computeCOM);
                const JointIndex & parent = model.parents[jid];
                data.com [parent] += data.com [jid];
                data.mass[parent] += data.mass[jid];
              }

            //std::cout << data.oMi [jid] << std::endl;
            //std::cout << data.mass[jid] << std::endl;
            //std::cout << data.com [jid].transpose() << std::endl;
          } // end for jid

        // Backward loop on ancestors of joint rootId
        JointIndex jid = model.parents[rootId]; // loop variable
        rootId = (root>0) ? roots_[(std::size_t)(root-1)] : 0;      // root of previous subtree in roots_
        while (jid>rootId)                           // stop when meeting the next subtree
          {
            const JointIndex & parent = model.parents[jid];
            if(computeJac)
              Pass::run(model.joints[jid],data.joints[jid],
                      Pass::ArgsType(model,data,
#if PINOCCHIO_VERSION_AT_LEAST(2,1,6)
                        data.Jcom,
#endif
                        false));
            else
              {
                assert(computeCOM);
                data.com [parent] += data.com [jid];
                data.mass[parent] += data.mass[jid];
              }
            jid = parent;
          } // end while

      } // end for root in roots_

      if(computeCOM) data.com[0]  /= data.mass[0];
      if(computeJac) data.Jcom    /= data.mass[0];
    }

    CenterOfMassComputation::CenterOfMassComputation (const DevicePtr_t& d) :
      robot_(d), roots_ ()
    { assert (d->modelPtr()); }

    void CenterOfMassComputation::add (const JointPtr_t& j)
    {
      const Data& data = robot_->data();
      JointIndex jid = j->index();
      BOOST_FOREACH( const JointIndex rootId,  roots_ )
        {
          assert (std::size_t(rootId)<robot_->model().joints.size());
          // Assert that the new root is not in already-recorded subtrees.
          if( (jid >= rootId) && (data.lastChild[rootId] >= int(jid)) )
            // We are doing something stupid. Should we throw an error
            // or just return silently ?
            HPP_THROW(std::invalid_argument, "Joint " << j->name()
                << " (" << jid << ") is already in a subtree ["
                << rootId << ", " << data.lastChild[rootId] << "]");
        }

      roots_.push_back(jid);
    }

    CenterOfMassComputation::~CenterOfMassComputation ()
    {}

    template<class Archive>
    void CenterOfMassComputation::serialize(Archive & ar, const unsigned int version)
    {
      (void) version;
      ar & BOOST_SERIALIZATION_NVP(robot_);
      ar & BOOST_SERIALIZATION_NVP(roots_);
    }

    HPP_SERIALIZATION_IMPLEMENT(CenterOfMassComputation);
  }  //  namespace pinocchio
}  //  namespace hpp
<commit_msg>[CenterOfMassComputation] Fix invalid memory access.<commit_after>// Copyright (c) 2016, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-pinocchio.
// hpp-pinocchio is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-pinocchio is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-pinocchio. If not, see <http://www.gnu.org/licenses/>.

#include "hpp/pinocchio/center-of-mass-computation.hh"

#include <algorithm>
#include <boost/foreach.hpp>
#include <boost/serialization/vector.hpp>

#include <pinocchio/algorithm/center-of-mass.hpp>
#include <pinocchio/algorithm/copy.hpp>

#include <hpp/util/exception-factory.hh>
#include <hpp/util/serialization.hh>

#include "hpp/pinocchio/joint.hh"
#include <hpp/pinocchio/joint-collection.hh>
#include "hpp/pinocchio/device.hh"
#include "hpp/pinocchio/serialization.hh"

namespace hpp {
  namespace pinocchio {
    CenterOfMassComputationPtr_t CenterOfMassComputation::create (
        const DevicePtr_t& d)
    {
      return CenterOfMassComputationPtr_t (new CenterOfMassComputation (d));
    }

    void CenterOfMassComputation::compute (DeviceData& d, const Computation_t& flag)
    {
      const Model& model = robot_->model();

      bool computeCOM = (flag & COM);
      bool computeJac = (flag & JACOBIAN);
      assert(computeCOM && "This does nothing");
      assert (!(computeJac && !computeCOM)); // JACOBIAN => COM

      Data& data = *d.data_;

      data.mass[0] = 0;
      if(computeCOM) data.com[0].setZero();
      if(computeJac) data.Jcom.setZero();

      // Propagate COM initialization on all joints.
      // Could be done only on subtree, with minor gain (possibly loss because of
      // more difficult branch prediction). I dont recommend to implement it.
      for(JointIndex jid=1;jid<JointIndex(model.joints.size());++jid)
        {
          const double &            mass  = model.inertias[jid].mass ();
          data.mass[jid] = mass;
          data.com [jid] = mass*data.oMi[jid].act (model.inertias[jid].lever());
        }

      // Nullify non-subtree com and mass.
      std::size_t root = 0;
      std::size_t rootId = roots_[root];
      for(std::size_t jid=1; jid<model.joints.size(); ++jid )
        {
          if(jid == rootId)
            {
              jid = (std::size_t)data.lastChild[rootId];
              root ++;
	      if (root < roots_.size())
		rootId = roots_[root];
	      else
		// After last root, make sure that all joints set to 0.
		rootId = model.joints.size();
            }
          else
            {
              data.mass[jid] = 0;
              data.com [jid] .setZero();
            }
        }

      // TODO as of now, it is not possible to access the template parameter
      // JointCollectionTpl of Model so we use the default one.
      typedef ::pinocchio::JacobianCenterOfMassBackwardStep<Model::Scalar,
              Model::Options, JointCollectionTpl
#if PINOCCHIO_VERSION_AT_LEAST(2,1,6)
              ,Data::Matrix3x
#endif
                > Pass;

      // Assume root is sorted from smallest id.
      // Nasty cast below, from (u-long) size_t to int.
      for( int root=int(roots_.size()-1); root>=0; --root )
      {
        std::size_t rootId = roots_[(std::size_t)root];

        // Backward loop on descendents of joint rootId.
        for( JointIndex jid = (JointIndex)data.lastChild[rootId];jid>=rootId;--jid )
          {
            if(computeJac)
              Pass::run(model.joints[jid],data.joints[jid],
                      Pass::ArgsType(model,data,
#if PINOCCHIO_VERSION_AT_LEAST(2,1,6)
                        data.Jcom,
#endif
                        false));
            else
              {
                assert(computeCOM);
                const JointIndex & parent = model.parents[jid];
                data.com [parent] += data.com [jid];
                data.mass[parent] += data.mass[jid];
              }

            //std::cout << data.oMi [jid] << std::endl;
            //std::cout << data.mass[jid] << std::endl;
            //std::cout << data.com [jid].transpose() << std::endl;
          } // end for jid

        // Backward loop on ancestors of joint rootId
        JointIndex jid = model.parents[rootId]; // loop variable
        rootId = (root>0) ? roots_[(std::size_t)(root-1)] : 0;      // root of previous subtree in roots_
        while (jid>rootId)                           // stop when meeting the next subtree
          {
            const JointIndex & parent = model.parents[jid];
            if(computeJac)
              Pass::run(model.joints[jid],data.joints[jid],
                      Pass::ArgsType(model,data,
#if PINOCCHIO_VERSION_AT_LEAST(2,1,6)
                        data.Jcom,
#endif
                        false));
            else
              {
                assert(computeCOM);
                data.com [parent] += data.com [jid];
                data.mass[parent] += data.mass[jid];
              }
            jid = parent;
          } // end while

      } // end for root in roots_

      if(computeCOM) data.com[0]  /= data.mass[0];
      if(computeJac) data.Jcom    /= data.mass[0];
    }

    CenterOfMassComputation::CenterOfMassComputation (const DevicePtr_t& d) :
      robot_(d), roots_ ()
    { assert (d->modelPtr()); }

    void CenterOfMassComputation::add (const JointPtr_t& j)
    {
      const Data& data = robot_->data();
      JointIndex jid = j->index();
      BOOST_FOREACH( const JointIndex rootId,  roots_ )
        {
          assert (std::size_t(rootId)<robot_->model().joints.size());
          // Assert that the new root is not in already-recorded subtrees.
          if( (jid >= rootId) && (data.lastChild[rootId] >= int(jid)) )
            // We are doing something stupid. Should we throw an error
            // or just return silently ?
            HPP_THROW(std::invalid_argument, "Joint " << j->name()
                << " (" << jid << ") is already in a subtree ["
                << rootId << ", " << data.lastChild[rootId] << "]");
        }

      roots_.push_back(jid);
    }

    CenterOfMassComputation::~CenterOfMassComputation ()
    {}

    template<class Archive>
    void CenterOfMassComputation::serialize(Archive & ar, const unsigned int version)
    {
      (void) version;
      ar & BOOST_SERIALIZATION_NVP(robot_);
      ar & BOOST_SERIALIZATION_NVP(roots_);
    }

    HPP_SERIALIZATION_IMPLEMENT(CenterOfMassComputation);
  }  //  namespace pinocchio
}  //  namespace hpp
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 David Wicks, sansumbrella.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "choreograph/Phrase.hpp"

///
/// \file
/// Retime Phrases take another Phrase and reinterpret the time at which
/// that Phrase's value is computed.
///

namespace choreograph
{

///
/// LoopPhrase repeats an existing Phrase N times.
///
template<typename T>
class LoopPhrase : public Phrase<T>
{
public:
  /// Create a Phrase that loops \a source \a numLoops times.
  LoopPhrase( const PhraseRef<T> &source, float numLoops, Time inflectionPoint = 0.0f ):
    Phrase<T>( source->getDuration() * numLoops ),
    _source( source ),
    _inflection_point( inflectionPoint )
  {}

  T getValue( Time atTime ) const override { return _source->getValueWrapped( atTime, _inflection_point ); }
  T getStartValue() const override { return _source->getStartValue(); }
  T getEndValue() const override { return _source->getValueWrapped( this->getDuration() ); }
private:
  PhraseRef<T>  _source;
  Time          _inflection_point;
};

///
/// PingPongPhrase repeats an existing Phrase N times,
/// playing forward from start to end, then playing
/// in reverse from end to start.
///
template<typename T>
class PingPongPhrase : public Phrase<T>
{
public:
  /// Create a Phrase that loops \a source \a numLoops times.
  PingPongPhrase( const PhraseRef<T> &source, float numLoops, Time inflectionPoint = 0.0f ):
    Phrase<T>( source->getDuration() * numLoops ),
    _source( source ),
    _inflection_point( inflectionPoint )
  {}

  T getValue( Time atTime ) const override {
    bool forward = (int)(atTime / _source->getDuration()) % 2 == 0;
    Time insetTime = std::fmod( atTime, _source->getDuration() );
    if( forward ) {
      return _source->getValue( insetTime );
    }
    else {
      return _source->getValue( _source->getDuration() - insetTime );
    }
  }
  T getStartValue() const override { return _source->getStartValue(); }
  T getEndValue() const override { return getValue( this->getDuration() ); }
private:
  PhraseRef<T>  _source;
  Time          _inflection_point;
};

///
/// ReversePhrase plays an existing Phrase in reverse.
///
template<typename T>
class ReversePhrase : public Phrase<T>
{
public:
  /// Create a Phrase that loops \a source \a numLoops times.
  ReversePhrase( const PhraseRef<T> &source ):
  Phrase<T>( source->getDuration() ),
  _source( source )
  {}

  T getValue( Time atTime ) const override { return _source->getValue( _source->getDuration() - atTime ); }
  T getStartValue() const override { return _source->getEndValue(); }
  T getEndValue() const override { return _source->getStartValue(); }
private:
  PhraseRef<T>  _source;
};

} // namespace choreograph
<commit_msg>Initial ClipPhrase implementation.<commit_after>/*
 * Copyright (c) 2014 David Wicks, sansumbrella.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "choreograph/Phrase.hpp"

///
/// \file
/// Retime Phrases take another Phrase and reinterpret the time at which
/// that Phrase's value is computed.
///

namespace choreograph
{

///
/// LoopPhrase repeats an existing Phrase N times.
///
template<typename T>
class LoopPhrase : public Phrase<T>
{
public:
  /// Create a Phrase that loops \a source \a numLoops times.
  LoopPhrase( const PhraseRef<T> &source, float numLoops, Time inflectionPoint = 0.0f ):
    Phrase<T>( source->getDuration() * numLoops ),
    _source( source ),
    _inflection_point( inflectionPoint )
  {}

  T getValue( Time atTime ) const override { return _source->getValueWrapped( atTime, _inflection_point ); }
  T getStartValue() const override { return _source->getStartValue(); }
  T getEndValue() const override { return _source->getValueWrapped( this->getDuration() ); }
private:
  PhraseRef<T>  _source;
  Time          _inflection_point;
};

///
/// PingPongPhrase repeats an existing Phrase N times,
/// playing forward from start to end, then playing
/// in reverse from end to start.
///
template<typename T>
class PingPongPhrase : public Phrase<T>
{
public:
  /// Create a Phrase that loops \a source \a numLoops times.
  PingPongPhrase( const PhraseRef<T> &source, float numLoops, Time inflectionPoint = 0.0f ):
    Phrase<T>( source->getDuration() * numLoops ),
    _source( source ),
    _inflection_point( inflectionPoint )
  {}

  T getValue( Time atTime ) const override {
    bool forward = (int)(atTime / _source->getDuration()) % 2 == 0;
    Time insetTime = std::fmod( atTime, _source->getDuration() );
    if( forward ) {
      return _source->getValue( insetTime );
    }
    else {
      return _source->getValue( _source->getDuration() - insetTime );
    }
  }
  T getStartValue() const override { return _source->getStartValue(); }
  T getEndValue() const override { return getValue( this->getDuration() ); }
private:
  PhraseRef<T>  _source;
  Time          _inflection_point;
};

///
/// ReversePhrase plays an existing Phrase in reverse.
///
template<typename T>
class ReversePhrase : public Phrase<T>
{
public:
  /// Create a Phrase that loops \a source \a numLoops times.
  ReversePhrase( const PhraseRef<T> &source ):
  Phrase<T>( source->getDuration() ),
  _source( source )
  {}

  T getValue( Time atTime ) const override { return _source->getValue( _source->getDuration() - atTime ); }
  T getStartValue() const override { return _source->getEndValue(); }
  T getEndValue() const override { return _source->getStartValue(); }
private:
  PhraseRef<T>  _source;
};

///
/// ClipPhrase cuts of an existing Phrase at some point in time.
/// Note that if the clip time is after the end of the source, the
/// source will continue interpolating past the end of its range.
///
template<typename T>
class ClipPhrase : public Phrase<T>
{
public:
  ClipPhrase( const PhraseRef<T> &source, float end ):
    Phrase<T>( end ),
    _source( source )
  {}

  T getValue( Time atTime ) const override { return _source->getValue( atTime ); }
  T getStartValue() const override { return _source->getStartValue(); }
  T getEndValue() const override { return _source->getValue( this->getDuration() ); }
private:
  PhraseRef<T>  _source;
};

} // namespace choreograph
<|endoftext|>