<commit_before>// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

#if !defined(CPPLINQ_LINQ_SELECT_HPP)
#define CPPLINQ_LINQ_SELECT_HPP
#pragma once

namespace cpplinq
{
    template <class Collection, class Selector>
    class linq_select
    {
        typedef typename Collection::cursor
            inner_cursor;
    public:
        struct cursor {
            typedef typename util::result_of<Selector(typename inner_cursor::element_type)>::type
                reference_type;
            typedef typename std::remove_reference<reference_type>::type
                element_type;
            typedef typename inner_cursor::cursor_category
                cursor_category;

            cursor(const inner_cursor& cur, Selector sel) : cur(cur), sel(std::move(sel)) {}

            void forget() { cur.forget(); }
            bool empty() const { return cur.empty(); }
            void inc() { cur.inc(); }
            reference_type get() const { return sel(cur.get()); }

            bool atbegin() const { return cur.atbegin(); }
            void dec() { cur.dec(); }

            void skip(size_t n) { cur.skip(n); }
            size_t position() const { return cur.position(); }
            size_t size() const { return cur.size(); }
        private:
            inner_cursor    cur;
            Selector        sel;
        };

        linq_select(const Collection& c, Selector sel) : c(c), sel(sel) {}

        cursor get_cursor() const { return cursor(c.get_cursor(), sel); }

    private:
        Collection c;
        Selector sel;
    };

}

#endif // defined(CPPLINQ_LINQ_SELECT_HPP)
<commit_msg>Update linq_select.hpp<commit_after>// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

#if !defined(CPPLINQ_LINQ_SELECT_HPP)
#define CPPLINQ_LINQ_SELECT_HPP
#pragma once

#include <cstddef>

namespace cpplinq
{
    template <class Collection, class Selector>
    class linq_select
    {
        typedef typename Collection::cursor
            inner_cursor;
    public:
        struct cursor {
            typedef typename util::result_of<Selector(typename inner_cursor::element_type)>::type
                reference_type;
            typedef typename std::remove_reference<reference_type>::type
                element_type;
            typedef typename inner_cursor::cursor_category
                cursor_category;

            cursor(const inner_cursor& cur, Selector sel) : cur(cur), sel(std::move(sel)) {}

            void forget() { cur.forget(); }
            bool empty() const { return cur.empty(); }
            void inc() { cur.inc(); }
            reference_type get() const { return sel(cur.get()); }

            bool atbegin() const { return cur.atbegin(); }
            void dec() { cur.dec(); }

            void skip(std::size_t n) { cur.skip(n); }
            std::size_t position() const { return cur.position(); }
            std::size_t size() const { return cur.size(); }
        private:
            inner_cursor    cur;
            Selector        sel;
        };

        linq_select(const Collection& c, Selector sel) : c(c), sel(sel) {}

        cursor get_cursor() const { return cursor(c.get_cursor(), sel); }

    private:
        Collection c;
        Selector sel;
    };

}

#endif // defined(CPPLINQ_LINQ_SELECT_HPP)
<|endoftext|>


<commit_before>// RaceDetector.cpp (Oclgrind)
// Copyright (c) 2013-2015, James Price and Simon McIntosh-Smith,
// University of Bristol. All rights reserved.
//
// This program is provided under a three-clause BSD license. For full
// license terms please see the LICENSE file distributed with this
// source code.

#include "core/common.h"

#include "core/Context.h"
#include "core/KernelInvocation.h"
#include "core/Memory.h"
#include "core/WorkGroup.h"
#include "core/WorkItem.h"

#include "RaceDetector.h"

using namespace oclgrind;
using namespace std;

THREAD_LOCAL RaceDetector::WorkerState RaceDetector::m_state = {NULL};

#define STATE(workgroup) ((*m_state.groups)[workgroup])

// Use a bank of mutexes to reduce unnecessary synchronisation
#define NUM_GLOBAL_MUTEXES 4096 // Must be power of two
#define GLOBAL_MUTEX(buffer,offset) \
  m_globalMutexes[buffer][offset & (NUM_GLOBAL_MUTEXES-1)]

RaceDetector::RaceDetector(const Context *context)
 : Plugin(context)
{
  m_kernelInvocation = NULL;

  m_allowUniformWrites = !checkEnv("OCLGRIND_UNIFORM_WRITES");
}

void RaceDetector::kernelBegin(const KernelInvocation *kernelInvocation)
{
  m_kernelInvocation = kernelInvocation;
}

void RaceDetector::kernelEnd(const KernelInvocation *kernelInvocation)
{
  // Clear all global memory accesses
  for (auto buffer  = m_globalAccesses.begin();
            buffer != m_globalAccesses.end();
            buffer++)
  {
    size_t sz = buffer->second.size();
    buffer->second.clear();
    buffer->second.resize(sz);
  }

  m_kernelInvocation = NULL;
}

void RaceDetector::memoryAllocated(const Memory *memory, size_t address,
                                   size_t size, cl_mem_flags flags,
                                   const uint8_t *initData)
{
  if (memory->getAddressSpace() == AddrSpaceGlobal)
  {
    m_globalAccesses[EXTRACT_BUFFER(address)].resize(size);
    m_globalMutexes[EXTRACT_BUFFER(address)] = new mutex[NUM_GLOBAL_MUTEXES];
  }
}

void RaceDetector::memoryAtomicLoad(const Memory *memory,
                                    const WorkItem *workItem,
                                    AtomicOp op, size_t address, size_t size)
{
  registerAccess(memory, workItem->getWorkGroup(), workItem,
                 address, size, true);
}

void RaceDetector::memoryAtomicStore(const Memory *memory,
                                     const WorkItem *workItem,
                                     AtomicOp op, size_t address, size_t size)
{
  registerAccess(memory, workItem->getWorkGroup(), workItem,
                 address, size, true,
                 (const uint8_t*)memory->getPointer(address));
}

void RaceDetector::memoryDeallocated(const Memory *memory, size_t address)
{
  if (memory->getAddressSpace() == AddrSpaceGlobal)
  {
    m_globalAccesses.erase(EXTRACT_BUFFER(address));

    delete[] m_globalMutexes[EXTRACT_BUFFER(address)];
    m_globalMutexes.erase(EXTRACT_BUFFER(address));
  }
}

void RaceDetector::memoryLoad(const Memory *memory, const WorkItem *workItem,
                              size_t address, size_t size)
{
  registerAccess(memory, workItem->getWorkGroup(), workItem,
                 address, size, false, NULL);
}

void RaceDetector::memoryLoad(const Memory *memory, const WorkGroup *workGroup,
                              size_t address, size_t size)
{
  registerAccess(memory, workGroup, NULL, address, size, false);
}

void RaceDetector::memoryStore(const Memory *memory, const WorkItem *workItem,
                               size_t address, size_t size,
                               const uint8_t *storeData)
{
  registerAccess(memory, workItem->getWorkGroup(), workItem,
                 address, size, false, storeData);
}

void RaceDetector::memoryStore(const Memory *memory, const WorkGroup *workGroup,
                               size_t address, size_t size,
                               const uint8_t *storeData)
{
  registerAccess(memory, workGroup, NULL,
                 address, size, false, storeData);
}

void RaceDetector::workGroupBarrier(const WorkGroup *workGroup, uint32_t flags)
{
  if (flags & CLK_LOCAL_MEM_FENCE)
  {
    syncWorkItems(workGroup->getLocalMemory(),
                  STATE(workGroup), STATE(workGroup).wiLocal);
  }
  if (flags & CLK_GLOBAL_MEM_FENCE)
  {
    syncWorkItems(m_context->getGlobalMemory(),
                  STATE(workGroup), STATE(workGroup).wiGlobal);
  }
}

void RaceDetector::workGroupBegin(const WorkGroup *workGroup)
{
  // Create worker state if haven't already
  if (!m_state.groups)
  {
    m_state.groups = new unordered_map<const WorkGroup*,WorkGroupState>;
  }

  // Initialize work-group state
  WorkGroupState& state = STATE(workGroup);
  Size3 wgsize = workGroup->getGroupSize();
  state.numWorkItems = wgsize.x*wgsize.y*wgsize.z;
  state.wiGlobal.resize(state.numWorkItems+1);
  state.wiLocal.resize(state.numWorkItems+1);
}

void RaceDetector::workGroupComplete(const WorkGroup *workGroup)
{
  WorkGroupState& state = STATE(workGroup);

  syncWorkItems(workGroup->getLocalMemory(), state, state.wiLocal);
  syncWorkItems(m_context->getGlobalMemory(), state, state.wiGlobal);

  // Merge global accesses across kernel invocation
  Size3 group = workGroup->getGroupID();
  for (auto record  = state.wgGlobal.begin();
            record != state.wgGlobal.end();
            record++)
  {
    size_t address = record->first;
    size_t buffer = EXTRACT_BUFFER(address);
    size_t offset = EXTRACT_OFFSET(address);

    lock_guard<mutex> lock(GLOBAL_MUTEX(buffer, offset));

    AccessRecord& a = record->second;
    AccessRecord& b = m_globalAccesses[buffer][offset];

    // Check for races with previous accesses
    if (getAccessWorkGroup(b.store) != group && check(a.load,  b.store))
      logRace(m_context->getGlobalMemory(), address, a.load, b.store);
    if (getAccessWorkGroup(b.load) != group && check(a.store, b.load))
      logRace(m_context->getGlobalMemory(), address, a.store, b.load);
    if (getAccessWorkGroup(b.store) != group && check(a.store, b.store))
      logRace(m_context->getGlobalMemory(), address, a.store, b.store);

    // Insert accesses
    if (a.load.isSet())
      insert(b, a.load);
    if (a.store.isSet())
      insert(b, a.store);
  }
  state.wgGlobal.clear();

  // Clean-up work-group state
  m_state.groups->erase(workGroup);
  if (m_state.groups->empty())
  {
    delete m_state.groups;
    m_state.groups = NULL;
  }
}

bool RaceDetector::check(const MemoryAccess& a,
                         const MemoryAccess& b) const
{
  // Ensure both accesses are valid
  if (!a.isSet() || !b.isSet())
    return false;

  // No race if same work-item
  if (a.isWorkItem() && b.isWorkItem() && (a.getEntity() == b.getEntity()))
    return false;

  // No race if both operations are atomics
  if (a.isAtomic() && b.isAtomic())
    return false;

  // Potential race if at least one store
  if (a.isStore() || b.isStore())
  {
    // Read-write race if one is a load
    if (a.isLoad() || b.isLoad())
      return true;

    // Write-write race if not uniform
    if (!m_allowUniformWrites || (a.getStoreData() != b.getStoreData()))
      return true;
  }

  return false;
}

Size3 RaceDetector::getAccessWorkGroup(const MemoryAccess& access) const
{
  if (access.isWorkItem())
  {
    Size3 wi(access.getEntity(), m_kernelInvocation->getGlobalSize());
    Size3 wgsize = m_kernelInvocation->getLocalSize();
    return Size3(wi.x/wgsize.x, wi.y/wgsize.y, wi.z/wgsize.z);
  }
  else
  {
    return Size3(access.getEntity(), m_kernelInvocation->getLocalSize());
  }
}

void RaceDetector::insert(AccessRecord& record,
                          const MemoryAccess& access) const
{
  if (access.isLoad())
  {
    if (!record.load.isSet() || record.load.isAtomic())
      record.load = access;
  }
  else if (access.isStore())
  {
    if (!record.store.isSet() || record.store.isAtomic())
      record.store = access;
  }
}

void RaceDetector::logRace(const Memory *memory, size_t address,
                           const MemoryAccess& first,
                           const MemoryAccess& second) const
{
  const char *raceType;
  if (first.isLoad() || second.isLoad())
    raceType = "Read-write";
  else
    raceType = "Write-write";

  Context::Message msg(ERROR, m_context);
  msg << raceType << " data race at "
      << getAddressSpaceName(memory->getAddressSpace())
      << " memory address 0x" << hex << address << endl
      << msg.INDENT
      << "Kernel: " << msg.CURRENT_KERNEL << endl
      << endl
      << "First entity:  ";

  if (first.isWorkItem())
  {
    Size3 wgsize = m_kernelInvocation->getLocalSize();
    Size3 global(first.getEntity(), m_kernelInvocation->getGlobalSize());
    Size3 local(global.x%wgsize.x, global.y%wgsize.y, global.z%wgsize.z);
    Size3 group(global.x/wgsize.x, global.y/wgsize.y, global.z/wgsize.z);
    msg << "Global" << global << " Local" << local << " Group" << group;
  }
  else
  {
    msg << "Group"
        << Size3(first.getEntity(), m_kernelInvocation->getLocalSize());
  }

  msg << endl << first.getInstruction() << endl
      << endl
      << "Second entity: ";

  // Show details of other entity involved in race
  if (second.isWorkItem())
  {
    Size3 wgsize = m_kernelInvocation->getLocalSize();
    Size3 global(second.getEntity(), m_kernelInvocation->getGlobalSize());
    Size3 local(global.x%wgsize.x, global.y%wgsize.y, global.z%wgsize.z);
    Size3 group(global.x/wgsize.x, global.y/wgsize.y, global.z/wgsize.z);
    msg << "Global" << global << " Local" << local << " Group" << group;
  }
  else
  {
    msg << "Group"
        << Size3(second.getEntity(), m_kernelInvocation->getLocalSize());
  }
  msg << endl << second.getInstruction() << endl;
  msg.send();
}

void RaceDetector::registerAccess(const Memory *memory,
                                  const WorkGroup *workGroup,
                                  const WorkItem *workItem,
                                  size_t address, size_t size, bool atomic,
                                  const uint8_t *storeData)
{
  unsigned addrSpace = memory->getAddressSpace();
  if (addrSpace == AddrSpacePrivate ||
      addrSpace == AddrSpaceConstant)
    return;
  if (!memory->isAddressValid(address, size))
    return;

  // Construct access
  MemoryAccess access(workGroup, workItem, storeData != NULL, atomic);

  size_t index;
  if (workItem)
  {
    Size3 wgsize = workGroup->getGroupSize();
    Size3 lid = workItem->getLocalID();
    index = lid.x + (lid.y + lid.z*wgsize.y)*wgsize.x;
  }
  else
  {
    index = STATE(workGroup).wiLocal.size() - 1;
  }

  AccessMap& accesess = (addrSpace == AddrSpaceGlobal) ?
    STATE(workGroup).wiGlobal[index] :
    STATE(workGroup).wiLocal[index];

  for (size_t i = 0; i < size; i++)
  {
    if (storeData)
      access.setStoreData(storeData[i]);

    insert(accesess[address+i], access);
  }
}

void RaceDetector::syncWorkItems(const Memory *memory,
                                 WorkGroupState& state,
                                 vector<AccessMap>& accesses)
{
  AccessMap wgAccesses;

  for (size_t i = 0; i < state.numWorkItems + 1; i++)
  {
    for (auto record  = accesses[i].begin();
              record != accesses[i].end();
              record++)
    {
      size_t address = record->first;

      AccessRecord& a = record->second;
      AccessRecord& b = wgAccesses[address];

      if (check(a.load,  b.store))
        logRace(memory, address, a.load, b.store);
      if (check(a.store, b.load))
        logRace(memory, address, a.store, b.load);
      if (check(a.store, b.store))
        logRace(memory, address, a.store, b.store);

      if (a.load.isSet())
      {
        insert(b, a.load);
        if (memory->getAddressSpace() == AddrSpaceGlobal)
          insert(state.wgGlobal[address], a.load);
      }
      if (a.store.isSet())
      {
        insert(b, a.store);
        if (memory->getAddressSpace() == AddrSpaceGlobal)
          insert(state.wgGlobal[address], a.store);
      }
    }

    accesses[i].clear();
  }
}

RaceDetector::MemoryAccess::MemoryAccess()
{
  this->info = 0;
  this->instruction = NULL;
}

RaceDetector::MemoryAccess::MemoryAccess(const WorkGroup *workGroup,
                                         const WorkItem *workItem,
                                         bool store, bool atomic)
{
  this->info = 0;

  this->info |= 1 << SET_BIT;
  this->info |= store << STORE_BIT;
  this->info |= atomic << ATOMIC_BIT;

  if (workItem)
  {
    this->entity = workItem->getGlobalIndex();
    this->instruction = workItem->getCurrentInstruction();
  }
  else
  {
    this->info |= (1<<WG_BIT);
    this->entity = workGroup->getGroupIndex();
    this->instruction = NULL; // TODO?
  }
}

void RaceDetector::MemoryAccess::clear()
{
  this->info = 0;
  this->instruction = NULL;
}

bool RaceDetector::MemoryAccess::isSet() const
{
  return this->info & (1<<SET_BIT);
}

bool RaceDetector::MemoryAccess::isAtomic() const
{
  return this->info & (1<<ATOMIC_BIT);
}

bool RaceDetector::MemoryAccess::isLoad() const
{
  return !isStore();
}

bool RaceDetector::MemoryAccess::isStore() const
{
  return this->info & (1<<STORE_BIT);
}

bool RaceDetector::MemoryAccess::isWorkGroup() const
{
  return this->info & (1<<WG_BIT);
}

bool RaceDetector::MemoryAccess::isWorkItem() const
{
  return !isWorkGroup();
}

bool RaceDetector::MemoryAccess::hasWorkGroupSync() const
{
  return this->info & (1<<WG_SYNC_BIT);
}

void RaceDetector::MemoryAccess::setWorkGroupSync()
{
  this->info |= (1<<WG_SYNC_BIT);
}

size_t RaceDetector::MemoryAccess::getEntity() const
{
  return this->entity;
}

const llvm::Instruction* RaceDetector::MemoryAccess::getInstruction() const
{
  return this->instruction;
}

uint8_t RaceDetector::MemoryAccess::getStoreData() const
{
  return this->storeData;
}

void RaceDetector::MemoryAccess::setStoreData(uint8_t data)
{
  this->storeData = data;
}
<commit_msg>DRD: Reordered race vs same WG checks on global accesses.<commit_after>// RaceDetector.cpp (Oclgrind)
// Copyright (c) 2013-2015, James Price and Simon McIntosh-Smith,
// University of Bristol. All rights reserved.
//
// This program is provided under a three-clause BSD license. For full
// license terms please see the LICENSE file distributed with this
// source code.

#include "core/common.h"

#include "core/Context.h"
#include "core/KernelInvocation.h"
#include "core/Memory.h"
#include "core/WorkGroup.h"
#include "core/WorkItem.h"

#include "RaceDetector.h"

using namespace oclgrind;
using namespace std;

THREAD_LOCAL RaceDetector::WorkerState RaceDetector::m_state = {NULL};

#define STATE(workgroup) ((*m_state.groups)[workgroup])

// Use a bank of mutexes to reduce unnecessary synchronisation
#define NUM_GLOBAL_MUTEXES 4096 // Must be power of two
#define GLOBAL_MUTEX(buffer,offset) \
  m_globalMutexes[buffer][offset & (NUM_GLOBAL_MUTEXES-1)]

RaceDetector::RaceDetector(const Context *context)
 : Plugin(context)
{
  m_kernelInvocation = NULL;

  m_allowUniformWrites = !checkEnv("OCLGRIND_UNIFORM_WRITES");
}

void RaceDetector::kernelBegin(const KernelInvocation *kernelInvocation)
{
  m_kernelInvocation = kernelInvocation;
}

void RaceDetector::kernelEnd(const KernelInvocation *kernelInvocation)
{
  // Clear all global memory accesses
  for (auto buffer  = m_globalAccesses.begin();
            buffer != m_globalAccesses.end();
            buffer++)
  {
    size_t sz = buffer->second.size();
    buffer->second.clear();
    buffer->second.resize(sz);
  }

  m_kernelInvocation = NULL;
}

void RaceDetector::memoryAllocated(const Memory *memory, size_t address,
                                   size_t size, cl_mem_flags flags,
                                   const uint8_t *initData)
{
  if (memory->getAddressSpace() == AddrSpaceGlobal)
  {
    m_globalAccesses[EXTRACT_BUFFER(address)].resize(size);
    m_globalMutexes[EXTRACT_BUFFER(address)] = new mutex[NUM_GLOBAL_MUTEXES];
  }
}

void RaceDetector::memoryAtomicLoad(const Memory *memory,
                                    const WorkItem *workItem,
                                    AtomicOp op, size_t address, size_t size)
{
  registerAccess(memory, workItem->getWorkGroup(), workItem,
                 address, size, true);
}

void RaceDetector::memoryAtomicStore(const Memory *memory,
                                     const WorkItem *workItem,
                                     AtomicOp op, size_t address, size_t size)
{
  registerAccess(memory, workItem->getWorkGroup(), workItem,
                 address, size, true,
                 (const uint8_t*)memory->getPointer(address));
}

void RaceDetector::memoryDeallocated(const Memory *memory, size_t address)
{
  if (memory->getAddressSpace() == AddrSpaceGlobal)
  {
    m_globalAccesses.erase(EXTRACT_BUFFER(address));

    delete[] m_globalMutexes[EXTRACT_BUFFER(address)];
    m_globalMutexes.erase(EXTRACT_BUFFER(address));
  }
}

void RaceDetector::memoryLoad(const Memory *memory, const WorkItem *workItem,
                              size_t address, size_t size)
{
  registerAccess(memory, workItem->getWorkGroup(), workItem,
                 address, size, false, NULL);
}

void RaceDetector::memoryLoad(const Memory *memory, const WorkGroup *workGroup,
                              size_t address, size_t size)
{
  registerAccess(memory, workGroup, NULL, address, size, false);
}

void RaceDetector::memoryStore(const Memory *memory, const WorkItem *workItem,
                               size_t address, size_t size,
                               const uint8_t *storeData)
{
  registerAccess(memory, workItem->getWorkGroup(), workItem,
                 address, size, false, storeData);
}

void RaceDetector::memoryStore(const Memory *memory, const WorkGroup *workGroup,
                               size_t address, size_t size,
                               const uint8_t *storeData)
{
  registerAccess(memory, workGroup, NULL,
                 address, size, false, storeData);
}

void RaceDetector::workGroupBarrier(const WorkGroup *workGroup, uint32_t flags)
{
  if (flags & CLK_LOCAL_MEM_FENCE)
  {
    syncWorkItems(workGroup->getLocalMemory(),
                  STATE(workGroup), STATE(workGroup).wiLocal);
  }
  if (flags & CLK_GLOBAL_MEM_FENCE)
  {
    syncWorkItems(m_context->getGlobalMemory(),
                  STATE(workGroup), STATE(workGroup).wiGlobal);
  }
}

void RaceDetector::workGroupBegin(const WorkGroup *workGroup)
{
  // Create worker state if haven't already
  if (!m_state.groups)
  {
    m_state.groups = new unordered_map<const WorkGroup*,WorkGroupState>;
  }

  // Initialize work-group state
  WorkGroupState& state = STATE(workGroup);
  Size3 wgsize = workGroup->getGroupSize();
  state.numWorkItems = wgsize.x*wgsize.y*wgsize.z;
  state.wiGlobal.resize(state.numWorkItems+1);
  state.wiLocal.resize(state.numWorkItems+1);
}

void RaceDetector::workGroupComplete(const WorkGroup *workGroup)
{
  WorkGroupState& state = STATE(workGroup);

  syncWorkItems(workGroup->getLocalMemory(), state, state.wiLocal);
  syncWorkItems(m_context->getGlobalMemory(), state, state.wiGlobal);

  // Merge global accesses across kernel invocation
  Size3 group = workGroup->getGroupID();
  for (auto record  = state.wgGlobal.begin();
            record != state.wgGlobal.end();
            record++)
  {
    size_t address = record->first;
    size_t buffer = EXTRACT_BUFFER(address);
    size_t offset = EXTRACT_OFFSET(address);

    lock_guard<mutex> lock(GLOBAL_MUTEX(buffer, offset));

    AccessRecord& a = record->second;
    AccessRecord& b = m_globalAccesses[buffer][offset];

    // Check for races with previous accesses
    if (check(a.load,  b.store) && getAccessWorkGroup(b.store) != group)
      logRace(m_context->getGlobalMemory(), address, a.load, b.store);
    if (check(a.store, b.load) && getAccessWorkGroup(b.load) != group)
      logRace(m_context->getGlobalMemory(), address, a.store, b.load);
    if (check(a.store, b.store) && getAccessWorkGroup(b.store) != group)
      logRace(m_context->getGlobalMemory(), address, a.store, b.store);

    // Insert accesses
    if (a.load.isSet())
      insert(b, a.load);
    if (a.store.isSet())
      insert(b, a.store);
  }
  state.wgGlobal.clear();

  // Clean-up work-group state
  m_state.groups->erase(workGroup);
  if (m_state.groups->empty())
  {
    delete m_state.groups;
    m_state.groups = NULL;
  }
}

bool RaceDetector::check(const MemoryAccess& a,
                         const MemoryAccess& b) const
{
  // Ensure both accesses are valid
  if (!a.isSet() || !b.isSet())
    return false;

  // No race if same work-item
  if (a.isWorkItem() && b.isWorkItem() && (a.getEntity() == b.getEntity()))
    return false;

  // No race if both operations are atomics
  if (a.isAtomic() && b.isAtomic())
    return false;

  // Potential race if at least one store
  if (a.isStore() || b.isStore())
  {
    // Read-write race if one is a load
    if (a.isLoad() || b.isLoad())
      return true;

    // Write-write race if not uniform
    if (!m_allowUniformWrites || (a.getStoreData() != b.getStoreData()))
      return true;
  }

  return false;
}

Size3 RaceDetector::getAccessWorkGroup(const MemoryAccess& access) const
{
  if (access.isWorkItem())
  {
    Size3 wi(access.getEntity(), m_kernelInvocation->getGlobalSize());
    Size3 wgsize = m_kernelInvocation->getLocalSize();
    return Size3(wi.x/wgsize.x, wi.y/wgsize.y, wi.z/wgsize.z);
  }
  else
  {
    return Size3(access.getEntity(), m_kernelInvocation->getLocalSize());
  }
}

void RaceDetector::insert(AccessRecord& record,
                          const MemoryAccess& access) const
{
  if (access.isLoad())
  {
    if (!record.load.isSet() || record.load.isAtomic())
      record.load = access;
  }
  else if (access.isStore())
  {
    if (!record.store.isSet() || record.store.isAtomic())
      record.store = access;
  }
}

void RaceDetector::logRace(const Memory *memory, size_t address,
                           const MemoryAccess& first,
                           const MemoryAccess& second) const
{
  const char *raceType;
  if (first.isLoad() || second.isLoad())
    raceType = "Read-write";
  else
    raceType = "Write-write";

  Context::Message msg(ERROR, m_context);
  msg << raceType << " data race at "
      << getAddressSpaceName(memory->getAddressSpace())
      << " memory address 0x" << hex << address << endl
      << msg.INDENT
      << "Kernel: " << msg.CURRENT_KERNEL << endl
      << endl
      << "First entity:  ";

  if (first.isWorkItem())
  {
    Size3 wgsize = m_kernelInvocation->getLocalSize();
    Size3 global(first.getEntity(), m_kernelInvocation->getGlobalSize());
    Size3 local(global.x%wgsize.x, global.y%wgsize.y, global.z%wgsize.z);
    Size3 group(global.x/wgsize.x, global.y/wgsize.y, global.z/wgsize.z);
    msg << "Global" << global << " Local" << local << " Group" << group;
  }
  else
  {
    msg << "Group"
        << Size3(first.getEntity(), m_kernelInvocation->getLocalSize());
  }

  msg << endl << first.getInstruction() << endl
      << endl
      << "Second entity: ";

  // Show details of other entity involved in race
  if (second.isWorkItem())
  {
    Size3 wgsize = m_kernelInvocation->getLocalSize();
    Size3 global(second.getEntity(), m_kernelInvocation->getGlobalSize());
    Size3 local(global.x%wgsize.x, global.y%wgsize.y, global.z%wgsize.z);
    Size3 group(global.x/wgsize.x, global.y/wgsize.y, global.z/wgsize.z);
    msg << "Global" << global << " Local" << local << " Group" << group;
  }
  else
  {
    msg << "Group"
        << Size3(second.getEntity(), m_kernelInvocation->getLocalSize());
  }
  msg << endl << second.getInstruction() << endl;
  msg.send();
}

void RaceDetector::registerAccess(const Memory *memory,
                                  const WorkGroup *workGroup,
                                  const WorkItem *workItem,
                                  size_t address, size_t size, bool atomic,
                                  const uint8_t *storeData)
{
  unsigned addrSpace = memory->getAddressSpace();
  if (addrSpace == AddrSpacePrivate ||
      addrSpace == AddrSpaceConstant)
    return;
  if (!memory->isAddressValid(address, size))
    return;

  // Construct access
  MemoryAccess access(workGroup, workItem, storeData != NULL, atomic);

  size_t index;
  if (workItem)
  {
    Size3 wgsize = workGroup->getGroupSize();
    Size3 lid = workItem->getLocalID();
    index = lid.x + (lid.y + lid.z*wgsize.y)*wgsize.x;
  }
  else
  {
    index = STATE(workGroup).wiLocal.size() - 1;
  }

  AccessMap& accesess = (addrSpace == AddrSpaceGlobal) ?
    STATE(workGroup).wiGlobal[index] :
    STATE(workGroup).wiLocal[index];

  for (size_t i = 0; i < size; i++)
  {
    if (storeData)
      access.setStoreData(storeData[i]);

    insert(accesess[address+i], access);
  }
}

void RaceDetector::syncWorkItems(const Memory *memory,
                                 WorkGroupState& state,
                                 vector<AccessMap>& accesses)
{
  AccessMap wgAccesses;

  for (size_t i = 0; i < state.numWorkItems + 1; i++)
  {
    for (auto record  = accesses[i].begin();
              record != accesses[i].end();
              record++)
    {
      size_t address = record->first;

      AccessRecord& a = record->second;
      AccessRecord& b = wgAccesses[address];

      if (check(a.load,  b.store))
        logRace(memory, address, a.load, b.store);
      if (check(a.store, b.load))
        logRace(memory, address, a.store, b.load);
      if (check(a.store, b.store))
        logRace(memory, address, a.store, b.store);

      if (a.load.isSet())
      {
        insert(b, a.load);
        if (memory->getAddressSpace() == AddrSpaceGlobal)
          insert(state.wgGlobal[address], a.load);
      }
      if (a.store.isSet())
      {
        insert(b, a.store);
        if (memory->getAddressSpace() == AddrSpaceGlobal)
          insert(state.wgGlobal[address], a.store);
      }
    }

    accesses[i].clear();
  }
}

RaceDetector::MemoryAccess::MemoryAccess()
{
  this->info = 0;
  this->instruction = NULL;
}

RaceDetector::MemoryAccess::MemoryAccess(const WorkGroup *workGroup,
                                         const WorkItem *workItem,
                                         bool store, bool atomic)
{
  this->info = 0;

  this->info |= 1 << SET_BIT;
  this->info |= store << STORE_BIT;
  this->info |= atomic << ATOMIC_BIT;

  if (workItem)
  {
    this->entity = workItem->getGlobalIndex();
    this->instruction = workItem->getCurrentInstruction();
  }
  else
  {
    this->info |= (1<<WG_BIT);
    this->entity = workGroup->getGroupIndex();
    this->instruction = NULL; // TODO?
  }
}

void RaceDetector::MemoryAccess::clear()
{
  this->info = 0;
  this->instruction = NULL;
}

bool RaceDetector::MemoryAccess::isSet() const
{
  return this->info & (1<<SET_BIT);
}

bool RaceDetector::MemoryAccess::isAtomic() const
{
  return this->info & (1<<ATOMIC_BIT);
}

bool RaceDetector::MemoryAccess::isLoad() const
{
  return !isStore();
}

bool RaceDetector::MemoryAccess::isStore() const
{
  return this->info & (1<<STORE_BIT);
}

bool RaceDetector::MemoryAccess::isWorkGroup() const
{
  return this->info & (1<<WG_BIT);
}

bool RaceDetector::MemoryAccess::isWorkItem() const
{
  return !isWorkGroup();
}

bool RaceDetector::MemoryAccess::hasWorkGroupSync() const
{
  return this->info & (1<<WG_SYNC_BIT);
}

void RaceDetector::MemoryAccess::setWorkGroupSync()
{
  this->info |= (1<<WG_SYNC_BIT);
}

size_t RaceDetector::MemoryAccess::getEntity() const
{
  return this->entity;
}

const llvm::Instruction* RaceDetector::MemoryAccess::getInstruction() const
{
  return this->instruction;
}

uint8_t RaceDetector::MemoryAccess::getStoreData() const
{
  return this->storeData;
}

void RaceDetector::MemoryAccess::setStoreData(uint8_t data)
{
  this->storeData = data;
}
<|endoftext|>


<commit_before>#pragma once

#include <eosio/chain/types.hpp>
#include <eosio/chain/webassembly/common.hpp>
#include <eosio/chain/webassembly/eos-vm.hpp>

#include <eosio/vm/backend.hpp>
#include <eosio/vm/host_function.hpp>

namespace eosio { namespace chain { namespace webassembly {
   namespace detail {
      template <typename T, std::size_t A>
      constexpr std::integral_constant<bool, A != 0> is_legacy_ptr(legacy_ptr<T, A>);
      template <typename T>
      constexpr std::false_type is_legacy_ptr(T);
      template <typename T, std::size_t A>
      constexpr std::integral_constant<bool, A != 0> is_legacy_array_ptr(legacy_array_ptr<T, A>);
      template <typename T>
      constexpr std::false_type is_legacy_array_ptr(T);
      template <typename T>
      constexpr std::true_type is_unvalidated_ptr(unvalidated_ptr<T>);
      template <typename T>
      constexpr std::false_type is_unvalidated_ptr(T);

      template <typename T>
      struct is_whitelisted_legacy_type {
         static constexpr bool value = std::is_same_v<float128_t, T> ||
                                       std::is_same_v<null_terminated_ptr, T> ||
                                       std::is_same_v<decltype(is_legacy_ptr(std::declval<T>())), std::true_type> ||
                                       std::is_same_v<decltype(is_legacy_array_ptr(std::declval<T>())), std::true_type> ||
                                       std::is_same_v<decltype(is_unvalidated_ptr(std::declval<T>())), std::true_type> ||
                                       std::is_same_v<name, T> ||
                                       std::is_arithmetic_v<T>;
      };
      template <typename T>
      struct is_whitelisted_type {
         static constexpr bool value = std::is_arithmetic_v<std::decay_t<T>> ||
                                       std::is_same_v<std::decay_t<T>, name> ||
                                       std::is_pointer_v<T>    ||
                                       std::is_lvalue_reference_v<T> ||
                                       std::is_same_v<std::decay_t<T>, float128_t> ||
                                       eosio::vm::is_span_type_v<std::decay_t<T>>;
      };
   }

   template <typename T>
   inline static constexpr bool is_whitelisted_type_v = detail::is_whitelisted_type<T>::value;

   template <typename T>
   inline static constexpr bool is_whitelisted_legacy_type_v = detail::is_whitelisted_legacy_type<T>::value;

   template <typename... Ts>
   inline static constexpr bool are_whitelisted_types_v = true; //(... && detail::is_whitelisted_type<Ts>::value);

   template <typename... Ts>
   inline static constexpr bool are_whitelisted_legacy_types_v = (... && detail::is_whitelisted_legacy_type<Ts>::value);

   template <typename T, typename U>
   inline static bool is_aliasing(const T& a, const U& b) {
      std::uintptr_t a_ui = reinterpret_cast<std::uintptr_t>(a.data());
      std::uintptr_t b_ui = reinterpret_cast<std::uintptr_t>(b.data());
      return a_ui < b_ui ? a_ui + a.size() >= b_ui : b_ui + b.size() >= a_ui;
   }
   inline static bool is_nan( const float32_t f ) {
      return f32_is_nan( f );
   }
   inline static bool is_nan( const float64_t f ) {
      return f64_is_nan( f );
   }
   inline static bool is_nan( const float128_t& f ) {
      return f128_is_nan( f );
   }

   EOS_VM_PRECONDITION(early_validate_pointers,
         EOS_VM_INVOKE_ON_ALL([&](auto&& arg, auto&&... rest) {
            using namespace eosio::vm;
            using arg_t = std::decay_t<decltype(arg)>;
            if constexpr (is_reference_proxy_type_v<arg_t>) {
               if constexpr (is_span_type_v<dependent_type_t<arg_t>>) {
                  using dep_t = dependent_type_t<arg_t>;
                  const dep_t& s = arg;
                  EOS_ASSERT( s.size() <= std::numeric_limits<wasm_size_t>::max() / (wasm_size_t)sizeof(dependent_type_t<dep_t>),
                        wasm_execution_error, "length will overflow" );
                  volatile auto check = *(reinterpret_cast<const char*>(arg.original_ptr) + s.size_bytes() - 1);
                  ignore_unused_variable_warning(check);
               } else {
                  EOS_ASSERT(arg.original_ptr != ctx.get_interface().get_memory(), wasm_execution_error, "references cannot be created for null pointers");
               }
            } else if constexpr (vm::is_reference_type_v<arg_t>) {
               EOS_ASSERT(arg.value != ctx.get_interface().get_memory(), wasm_execution_error, "references cannot be created for null pointers");
            }
         }));

   EOS_VM_PRECONDITION(context_free_check,
         EOS_VM_INVOKE_ONCE([&](auto&&...) {
            EOS_ASSERT(ctx.get_host().get_context().is_context_free(), unaccessible_api, "this API may only be called from context_free apply");
         }));

   EOS_VM_PRECONDITION(context_aware_check,
         EOS_VM_INVOKE_ONCE([&](auto&&...) {
            EOS_ASSERT(!ctx.get_host().get_context().is_context_free(), unaccessible_api, "only context free api's can be used in this context");
         }));

   EOS_VM_PRECONDITION(privileged_check,
         EOS_VM_INVOKE_ONCE([&](auto&&...) {
            EOS_ASSERT(ctx.get_host().get_context().is_privileged(), unaccessible_api,
                       "${code} does not have permission to call this API", ("code", ctx.get_host().get_context().get_receiver()));
         }));

   EOS_VM_PRECONDITION(alias_check,
         EOS_VM_INVOKE_ON_ALL(([&](auto&& arg, auto&&... rest) {
            using namespace eosio::vm;
            using arg_t = std::decay_t<decltype(arg)>;
            if constexpr (is_span_type_v<arg_t>) {
               // check alignment while we are here
               EOS_ASSERT( reinterpret_cast<std::uintptr_t>(arg.data()) % alignof(dependent_type_t<arg_t>) == 0,
                     wasm_exception, "memory not aligned" );
               eosio::vm::invoke_on<false, eosio::vm::invoke_on_all_t>([&](auto&& narg, auto&&... nrest) {
                  using nested_arg_t = std::decay_t<decltype(arg)>;
                  if constexpr (eosio::vm::is_span_type_v<nested_arg_t>)
                     EOS_ASSERT(!is_aliasing(arg, narg), wasm_exception, "arrays not allowed to alias");
               }, rest...);
            }
         })));

   template<typename T>
   inline constexpr bool should_check_nan_v =
      std::is_same_v<T, float32_t> || std::is_same_v<T, float64_t> || std::is_same_v<T, float128_t>;

   template<typename T>
   struct remove_reference_proxy {
      using type = T;
   };
   template<typename T, std::size_t A>
   struct remove_reference_proxy<vm::reference_proxy<T, A>> {
      using type = T;
   };
   template<typename T>
   struct remove_reference_proxy<vm::reference<T>> {
      using type = T;
   };

   EOS_VM_PRECONDITION(is_nan_check,
         EOS_VM_INVOKE_ON_ALL([&](auto&& arg, auto&&... rest) {
            if constexpr (should_check_nan_v<std::remove_cv_t<typename remove_reference_proxy<std::decay_t<decltype(arg)>>::type>>) {
               EOS_ASSERT(!webassembly::is_nan(arg), transaction_exception, "NaN is not an allowed value for a secondary key");
            }
         }));

   EOS_VM_PRECONDITION(legacy_static_check_wl_args,
         EOS_VM_INVOKE_ONCE([&](auto&&... args) {
            static_assert( are_whitelisted_legacy_types_v<std::decay_t<decltype(args)>...>, "legacy whitelisted type violation");
         }));

   EOS_VM_PRECONDITION(static_check_wl_args,
         EOS_VM_INVOKE_ONCE([&](auto&&... args) {
            static_assert( are_whitelisted_types_v<std::decay_t<decltype(args)>...>, "whitelisted type violation");
         }));
}}} // ns eosio::chain::webassembly
<commit_msg>Remove uses vm::reference.  It has no alignment guarantees and is unusable for nodeos.<commit_after>#pragma once

#include <eosio/chain/types.hpp>
#include <eosio/chain/webassembly/common.hpp>
#include <eosio/chain/webassembly/eos-vm.hpp>

#include <eosio/vm/backend.hpp>
#include <eosio/vm/host_function.hpp>

namespace eosio { namespace chain { namespace webassembly {
   namespace detail {
      template <typename T, std::size_t A>
      constexpr std::integral_constant<bool, A != 0> is_legacy_ptr(legacy_ptr<T, A>);
      template <typename T>
      constexpr std::false_type is_legacy_ptr(T);
      template <typename T, std::size_t A>
      constexpr std::integral_constant<bool, A != 0> is_legacy_array_ptr(legacy_array_ptr<T, A>);
      template <typename T>
      constexpr std::false_type is_legacy_array_ptr(T);
      template <typename T>
      constexpr std::true_type is_unvalidated_ptr(unvalidated_ptr<T>);
      template <typename T>
      constexpr std::false_type is_unvalidated_ptr(T);

      template <typename T>
      struct is_whitelisted_legacy_type {
         static constexpr bool value = std::is_same_v<float128_t, T> ||
                                       std::is_same_v<null_terminated_ptr, T> ||
                                       std::is_same_v<decltype(is_legacy_ptr(std::declval<T>())), std::true_type> ||
                                       std::is_same_v<decltype(is_legacy_array_ptr(std::declval<T>())), std::true_type> ||
                                       std::is_same_v<decltype(is_unvalidated_ptr(std::declval<T>())), std::true_type> ||
                                       std::is_same_v<name, T> ||
                                       std::is_arithmetic_v<T>;
      };
      template <typename T>
      struct is_whitelisted_type {
         static constexpr bool value = std::is_arithmetic_v<std::decay_t<T>> ||
                                       std::is_same_v<std::decay_t<T>, name> ||
                                       std::is_pointer_v<T>    ||
                                       std::is_lvalue_reference_v<T> ||
                                       std::is_same_v<std::decay_t<T>, float128_t> ||
                                       eosio::vm::is_span_type_v<std::decay_t<T>>;
      };
   }

   template <typename T>
   inline static constexpr bool is_whitelisted_type_v = detail::is_whitelisted_type<T>::value;

   template <typename T>
   inline static constexpr bool is_whitelisted_legacy_type_v = detail::is_whitelisted_legacy_type<T>::value;

   template <typename... Ts>
   inline static constexpr bool are_whitelisted_types_v = true; //(... && detail::is_whitelisted_type<Ts>::value);

   template <typename... Ts>
   inline static constexpr bool are_whitelisted_legacy_types_v = (... && detail::is_whitelisted_legacy_type<Ts>::value);

   template <typename T, typename U>
   inline static bool is_aliasing(const T& a, const U& b) {
      std::uintptr_t a_ui = reinterpret_cast<std::uintptr_t>(a.data());
      std::uintptr_t b_ui = reinterpret_cast<std::uintptr_t>(b.data());
      return a_ui < b_ui ? a_ui + a.size() >= b_ui : b_ui + b.size() >= a_ui;
   }
   inline static bool is_nan( const float32_t f ) {
      return f32_is_nan( f );
   }
   inline static bool is_nan( const float64_t f ) {
      return f64_is_nan( f );
   }
   inline static bool is_nan( const float128_t& f ) {
      return f128_is_nan( f );
   }

   EOS_VM_PRECONDITION(early_validate_pointers,
         EOS_VM_INVOKE_ON_ALL([&](auto&& arg, auto&&... rest) {
            using namespace eosio::vm;
            using arg_t = std::decay_t<decltype(arg)>;
            if constexpr (is_reference_proxy_type_v<arg_t>) {
               if constexpr (is_span_type_v<dependent_type_t<arg_t>>) {
                  using dep_t = dependent_type_t<arg_t>;
                  const dep_t& s = arg;
                  EOS_ASSERT( s.size() <= std::numeric_limits<wasm_size_t>::max() / (wasm_size_t)sizeof(dependent_type_t<dep_t>),
                        wasm_execution_error, "length will overflow" );
                  volatile auto check = *(reinterpret_cast<const char*>(arg.original_ptr) + s.size_bytes() - 1);
                  ignore_unused_variable_warning(check);
               } else {
                  EOS_ASSERT(arg.original_ptr != ctx.get_interface().get_memory(), wasm_execution_error, "references cannot be created for null pointers");
               }
            }
         }));

   EOS_VM_PRECONDITION(context_free_check,
         EOS_VM_INVOKE_ONCE([&](auto&&...) {
            EOS_ASSERT(ctx.get_host().get_context().is_context_free(), unaccessible_api, "this API may only be called from context_free apply");
         }));

   EOS_VM_PRECONDITION(context_aware_check,
         EOS_VM_INVOKE_ONCE([&](auto&&...) {
            EOS_ASSERT(!ctx.get_host().get_context().is_context_free(), unaccessible_api, "only context free api's can be used in this context");
         }));

   EOS_VM_PRECONDITION(privileged_check,
         EOS_VM_INVOKE_ONCE([&](auto&&...) {
            EOS_ASSERT(ctx.get_host().get_context().is_privileged(), unaccessible_api,
                       "${code} does not have permission to call this API", ("code", ctx.get_host().get_context().get_receiver()));
         }));

   EOS_VM_PRECONDITION(alias_check,
         EOS_VM_INVOKE_ON_ALL(([&](auto&& arg, auto&&... rest) {
            using namespace eosio::vm;
            using arg_t = std::decay_t<decltype(arg)>;
            if constexpr (is_span_type_v<arg_t>) {
               // check alignment while we are here
               EOS_ASSERT( reinterpret_cast<std::uintptr_t>(arg.data()) % alignof(dependent_type_t<arg_t>) == 0,
                     wasm_exception, "memory not aligned" );
               eosio::vm::invoke_on<false, eosio::vm::invoke_on_all_t>([&](auto&& narg, auto&&... nrest) {
                  using nested_arg_t = std::decay_t<decltype(arg)>;
                  if constexpr (eosio::vm::is_span_type_v<nested_arg_t>)
                     EOS_ASSERT(!is_aliasing(arg, narg), wasm_exception, "arrays not allowed to alias");
               }, rest...);
            }
         })));

   template<typename T>
   inline constexpr bool should_check_nan_v =
      std::is_same_v<T, float32_t> || std::is_same_v<T, float64_t> || std::is_same_v<T, float128_t>;

   template<typename T>
   struct remove_reference_proxy {
      using type = T;
   };
   template<typename T, std::size_t A>
   struct remove_reference_proxy<vm::reference_proxy<T, A>> {
      using type = T;
   };

   EOS_VM_PRECONDITION(is_nan_check,
         EOS_VM_INVOKE_ON_ALL([&](auto&& arg, auto&&... rest) {
            if constexpr (should_check_nan_v<std::remove_cv_t<typename remove_reference_proxy<std::decay_t<decltype(arg)>>::type>>) {
               EOS_ASSERT(!webassembly::is_nan(arg), transaction_exception, "NaN is not an allowed value for a secondary key");
            }
         }));

   EOS_VM_PRECONDITION(legacy_static_check_wl_args,
         EOS_VM_INVOKE_ONCE([&](auto&&... args) {
            static_assert( are_whitelisted_legacy_types_v<std::decay_t<decltype(args)>...>, "legacy whitelisted type violation");
         }));

   EOS_VM_PRECONDITION(static_check_wl_args,
         EOS_VM_INVOKE_ONCE([&](auto&&... args) {
            static_assert( are_whitelisted_types_v<std::decay_t<decltype(args)>...>, "whitelisted type violation");
         }));
}}} // ns eosio::chain::webassembly
<|endoftext|>


<commit_before>/**
 * @file
 * @brief Parser
 */
#include <iostream>
#include "parameter_parser.h"
#include "parameters.h"

taylortrack::utils::Parameters taylortrack::utils::ParameterParser::parse_streamer(int argc,const char **argv) {
  char *end;
  Parameters parameters;

  for(int i = 1; i < argc; i++) {
    if(argv[i][0] == '-') {
      switch(argv[i][1]) {
        // stream buffer size
        case 's':
          if(++i >= argc) {
            parameters.valid = false;
          } else {
            parameters.size =  (int) strtol(argv[i], &end, 10);
          }
          break;
        // Output port
        case 'o':
          if(++i >= argc) {
            parameters.valid = false;
          } else {
            parameters.outport = argv[i];
          }
          break;
        // Input port
        case 'i':
          if(++i >= argc) {
            parameters.valid = false;
          } else {
            parameters.inport = argv[i];
          }
          break;

        default:
          parameters.valid = false;
      }
    }else {
      parameters.file = argv[i];
      parameters.valid = true;
    }
  }
  return parameters;
} // LCOV_EXCL_BR_LINE
<commit_msg>fixed potentially not initialized variable warning<commit_after>/**
 * @file
 * @brief Parser
 */
#include <iostream>
#include "parameter_parser.h"
#include "parameters.h"

taylortrack::utils::Parameters taylortrack::utils::ParameterParser::parse_streamer(int argc,const char **argv) {
  char *end;
  Parameters parameters = utils::Parameters();

  for(int i = 1; i < argc; i++) {
    if(argv[i][0] == '-') {
      switch(argv[i][1]) {
        // stream buffer size
        case 's':
          if(++i >= argc) {
            parameters.valid = false;
          } else {
            parameters.size =  (int) strtol(argv[i], &end, 10);
          }
          break;
        // Output port
        case 'o':
          if(++i >= argc) {
            parameters.valid = false;
          } else {
            parameters.outport = argv[i];
          }
          break;
        // Input port
        case 'i':
          if(++i >= argc) {
            parameters.valid = false;
          } else {
            parameters.inport = argv[i];
          }
          break;

        default:
          parameters.valid = false;
      }
    }else {
      parameters.file = argv[i];
      parameters.valid = true;
    }
  }
  return parameters;
} // LCOV_EXCL_BR_LINE
<|endoftext|>


<commit_before>/********************************************************************
  Software License Agreement (BSD License)

  Copyright (c) 2016, Syler Wagner.
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
  2. 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.
  3. Neither the name of the copyright holder nor the names of its
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

  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 OWNER 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.
 ********************************************************************/

/**
 * utm_fcu_tf_broadcaster.cpp
 * Broadcast transform between local_origin and fcu.
 *
 * @author  Syler Wagner      <syler@mit.edu>

 * @date    2016-07-18  syler   creation
 *
 * This node listens for an initial odometry message on the
 * /mavros/global_position/local topic and then broadcasts a
 * transform between local_origin and fcu.
 **/

#include <utm_fcu_tf_broadcaster.h>
#include <tf/transform_datatypes.h>

//$ TODO: use mavros.get_namespace() instead of hardcoding

LocalOriginToFCUBroadcaster::LocalOriginToFCUBroadcaster(ros::NodeHandle *n) {
  _n = n;

  _n->param<std::string>("frame_id", _frame_id, "fcu");
  _n->param<std::string>("mavros_ns", _ns, "mavros");

  ROS_WARN("Starting utm_fcu_tf_broadcaster with mavros namespace '%s'", _ns.c_str());

  // _n->param<std::string>("frame_id", _frame_id, "fcu");

  //$ transform is not initialized at startup
  _transform_initialized = false;
  _t_latest_error = ros::Time::now();
  _message_count = 0;

  ros::NodeHandle mavros_nh(_ns); //$ node handle for mavros global position topic

  mavros_nh.param<std::string>("local_position/frame_id", _frame_id, "fcu");
  mavros_nh.param<std::string>("global_position/frame_id", _utm_frame_id, "utm");

  ROS_WARN("Trying to initialize transform between %s and %s", _utm_frame_id.c_str(), _frame_id.c_str());

  //$ UTM odometry subscriber
  _odom_sub = mavros_nh.subscribe("global_position/local", 1, &LocalOriginToFCUBroadcaster::odometryCallback, this);
}

LocalOriginToFCUBroadcaster::~LocalOriginToFCUBroadcaster() {}

bool LocalOriginToFCUBroadcaster::verifyOdometry(const nav_msgs::Odometry::ConstPtr& odom) {
  /*$
    The first few messages on /mavros/global_position/local are usually invalid and will usually be something like
      x:  166021.443179
      y:  0.000000
      z:  0.210000
    so the ones with bogus values need to be filtered out.
    */

    ros::Duration time_since_first_callback;

    if (_message_count == 1) {
      _t_first_message = ros::Time::now();
      ROS_WARN("Got first message on %s/global_position/local", _ns.c_str());
    }
    else {
      time_since_first_callback = ros::Time::now() - _t_first_message;
    }

    if (odom->pose.pose.position.y < 1e-6) {
      ROS_ERROR("UTM messages on %s/global_position/local are invalid. Wait a second...", _ns.c_str());
      return false;
    }
    else if (time_since_first_callback < ros::Duration(1)) {
      return false;
    }
    else {
      return true;
    }
}

/*$
 * The odometryCallback() function initializes the transform between
 * local_origin and fcu. The data from the /mavros/global_position/local
 * topic is only read once.
 */
void LocalOriginToFCUBroadcaster::odometryCallback(const nav_msgs::Odometry::ConstPtr& odom) {

  _message_count += 1;

  if (_transform_initialized == false) {
    if (verifyOdometry(odom)) {
      _odom_trans.transform.translation.x = odom->pose.pose.position.x;
      _odom_trans.transform.translation.y = odom->pose.pose.position.y;
      _odom_trans.transform.translation.z = odom->pose.pose.position.z;
      //$ TODO: I don't think the rotation is necessary
      geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(0);
      _odom_trans.transform.rotation = odom_quat;

      ROS_WARN("Transform between %s and %s initialized!", _utm_frame_id.c_str(), _frame_id.c_str());
      ROS_WARN("Translation: [x: %4.1f, y: %4.1f, z:  %4.1f]", _odom_trans.transform.translation.x, _odom_trans.transform.translation.y, _odom_trans.transform.translation.z);
      ROS_WARN("Rotation: [x: %4.1f, y: %4.1f, z:  %4.1f, w:  %4.1f]", _odom_trans.transform.rotation.x, _odom_trans.transform.rotation.y, _odom_trans.transform.rotation.z, _odom_trans.transform.rotation.w);

      _transform_initialized = true;
    }
  }
}

/*$
 * Send the transform between local_origin and fcu after it has been initialized.
 */
void  LocalOriginToFCUBroadcaster::sendTransform() {
  if (_transform_initialized) {
    _odom_trans.header.stamp = ros::Time::now();
    _odom_trans.header.frame_id = _utm_frame_id;
    _odom_trans.child_frame_id = _frame_id;
    _odom_broadcaster.sendTransform(_odom_trans);
  }
  else {
    ros::Duration time_since_last_error_message = ros::Time::now() - _t_latest_error;
    if (time_since_last_error_message > ros::Duration(5)) {
      _t_latest_error = ros::Time::now();
      ROS_ERROR("No UTM odometry messages received from UAV. Are you in the air yet?");
    }
  }
}

int main(int argc, char** argv) {
  ros::init(argc, argv, "utm_fcu_tf_broadcaster");

  ros::NodeHandle* n = new ros::NodeHandle("~");
  LocalOriginToFCUBroadcaster broadcaster(n);

  int rate;
  std::string frame_id;

  n->param("rated", rate, 100);

  ros::Rate r(rate);

  while (n->ok()) {
    ros::spinOnce();
    broadcaster.sendTransform();
    r.sleep();
  }
}
<commit_msg>Fixed typo in utm_fcu_tf_broadcaster<commit_after>/********************************************************************
  Software License Agreement (BSD License)

  Copyright (c) 2016, Syler Wagner.
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
  2. 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.
  3. Neither the name of the copyright holder nor the names of its
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

  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 OWNER 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.
 ********************************************************************/

/**
 * utm_fcu_tf_broadcaster.cpp
 * Broadcast transform between local_origin and fcu.
 *
 * @author  Syler Wagner      <syler@mit.edu>

 * @date    2016-07-18  syler   creation
 *
 * This node listens for an initial odometry message on the
 * /mavros/global_position/local topic and then broadcasts a
 * transform between local_origin and fcu.
 **/

#include <utm_fcu_tf_broadcaster.h>
#include <tf/transform_datatypes.h>

//$ TODO: use mavros.get_namespace() instead of hardcoding

LocalOriginToFCUBroadcaster::LocalOriginToFCUBroadcaster(ros::NodeHandle *n) {
  _n = n;

  _n->param<std::string>("frame_id", _frame_id, "fcu");
  _n->param<std::string>("mavros_ns", _ns, "mavros");

  ROS_WARN("Starting utm_fcu_tf_broadcaster with mavros namespace '%s'", _ns.c_str());

  // _n->param<std::string>("frame_id", _frame_id, "fcu");

  //$ transform is not initialized at startup
  _transform_initialized = false;
  _t_latest_error = ros::Time::now();
  _message_count = 0;

  ros::NodeHandle mavros_nh(_ns); //$ node handle for mavros global position topic

  mavros_nh.param<std::string>("local_position/frame_id", _frame_id, "fcu");
  mavros_nh.param<std::string>("global_position/frame_id", _utm_frame_id, "utm");

  ROS_WARN("Trying to initialize transform between %s and %s", _utm_frame_id.c_str(), _frame_id.c_str());

  //$ UTM odometry subscriber
  _odom_sub = mavros_nh.subscribe("global_position/local", 1, &LocalOriginToFCUBroadcaster::odometryCallback, this);
}

LocalOriginToFCUBroadcaster::~LocalOriginToFCUBroadcaster() {}

bool LocalOriginToFCUBroadcaster::verifyOdometry(const nav_msgs::Odometry::ConstPtr& odom) {
  /*$
    The first few messages on /mavros/global_position/local are usually invalid and will usually be something like
      x:  166021.443179
      y:  0.000000
      z:  0.210000
    so the ones with bogus values need to be filtered out.
    */

    ros::Duration time_since_first_callback;

    if (_message_count == 1) {
      _t_first_message = ros::Time::now();
      ROS_WARN("Got first message on %s/global_position/local", _ns.c_str());
    }
    else {
      time_since_first_callback = ros::Time::now() - _t_first_message;
    }

    if (odom->pose.pose.position.y < 1e-6) {
      ROS_ERROR("UTM messages on %s/global_position/local are invalid. Wait a second...", _ns.c_str());
      return false;
    }
    else if (time_since_first_callback < ros::Duration(1)) {
      return false;
    }
    else {
      return true;
    }
}

/*$
 * The odometryCallback() function initializes the transform between
 * local_origin and fcu. The data from the /mavros/global_position/local
 * topic is only read once.
 */
void LocalOriginToFCUBroadcaster::odometryCallback(const nav_msgs::Odometry::ConstPtr& odom) {

  _message_count += 1;

  if (_transform_initialized == false) {
    if (verifyOdometry(odom)) {
      _odom_trans.transform.translation.x = odom->pose.pose.position.x;
      _odom_trans.transform.translation.y = odom->pose.pose.position.y;
      _odom_trans.transform.translation.z = odom->pose.pose.position.z;
      //$ TODO: I don't think the rotation is necessary
      geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(0);
      _odom_trans.transform.rotation = odom_quat;

      ROS_WARN("Transform between %s and %s initialized!", _utm_frame_id.c_str(), _frame_id.c_str());
      ROS_WARN("Translation: [x: %4.1f, y: %4.1f, z:  %4.1f]", _odom_trans.transform.translation.x, _odom_trans.transform.translation.y, _odom_trans.transform.translation.z);
      ROS_WARN("Rotation: [x: %4.1f, y: %4.1f, z:  %4.1f, w:  %4.1f]", _odom_trans.transform.rotation.x, _odom_trans.transform.rotation.y, _odom_trans.transform.rotation.z, _odom_trans.transform.rotation.w);

      _transform_initialized = true;
    }
  }
}

/*$
 * Send the transform between local_origin and fcu after it has been initialized.
 */
void  LocalOriginToFCUBroadcaster::sendTransform() {
  if (_transform_initialized) {
    _odom_trans.header.stamp = ros::Time::now();
    _odom_trans.header.frame_id = _utm_frame_id;
    _odom_trans.child_frame_id = _frame_id;
    _odom_broadcaster.sendTransform(_odom_trans);
  }
  else {
    ros::Duration time_since_last_error_message = ros::Time::now() - _t_latest_error;
    if (time_since_last_error_message > ros::Duration(5)) {
      _t_latest_error = ros::Time::now();
      ROS_ERROR("No UTM odometry messages received from UAV. Are you in the air yet?");
    }
  }
}

int main(int argc, char** argv) {
  ros::init(argc, argv, "utm_fcu_tf_broadcaster");

  ros::NodeHandle* n = new ros::NodeHandle("~");
  LocalOriginToFCUBroadcaster broadcaster(n);

  int rate;
  std::string frame_id;

  n->param("rate", rate, 100);

  ros::Rate r(rate);

  while (n->ok()) {
    ros::spinOnce();
    broadcaster.sendTransform();
    r.sleep();
  }
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qversitdocument.h"
#include "qversitdocument_p.h"
#include "qmobilityglobal.h"

#include <QTextCodec>

QTM_BEGIN_NAMESPACE

/*!
  \class QVersitDocument
  \brief The QVersitDocument class is a container for a list of versit properties.
  \ingroup versit
  \inmodule QtVersit

  A vCard is represented in abstract form as a QVersitDocument that consists of a number of
  properties such as a name (N), a telephone number (TEL) and an email address (EMAIL), for
  instance.  Each of these properties is stored as an instance of a QVersitProperty in a
  QVersitDocument.

  In addition to the list of properties, QVersitDocument also records the type of the Versit
  document in two ways.  The VersitType enum describes the format in which the document is to be
  serialized by QVersitWriter (or the format from which it was read by QVersitReader), and should
  not be used to infer any semantics about the document data.  The componentType field is a string
  corresponding directly to the value of the BEGIN line in a document.  For example, for a vCard,
  this will always be the string "VCARD"; for an iCalendar, it could be "VCALENDAR", "VEVENT",
  "VTODO", "VJOURNAL", "VALARM" or "VTIMEZONE".

  As well as properties, a QVersitDocument can hold other documents.  For iCalendar, this is how
  a single VCALENDAR document can compose documents of type VEVENT, VTODO, etc.

  For example, for the following iCalendar:
  \code
  BEGIN:VCALENDAR
  VERSION:2.0
  BEGIN:VEVENT
  SUMMARY:Christmas
  DTSTART:20001225
  END:VEVENT
  END:VCALENDAR
  \endcode

  This can be represented as a QVersitDocument of with componentType VCALENDAR and versitType
  ICalendar20Type.  It contains no properties (note: the VERSION property is not stored explicitly
  as a property) and one sub-document.  The sub-document has componentType VEVENT and versitType
  ICalendar20Type, and contains two properties.

  QVersitDocument supports implicit sharing.

  \sa QVersitProperty
 */

/*!
  \enum QVersitDocument::VersitType
  This enum describes a Versit document serialization format and version.
  \value InvalidType No type specified or a document with an invalid type was parsed
  \value VCard21Type vCard version 2.1
  \value VCard30Type vCard version 3.0
  \value VCard40Type vCard version 4.0
  \value ICalendar20Type iCalendar version 2.0
 */

/*! Constructs a new empty document */
QVersitDocument::QVersitDocument() : d(new QVersitDocumentPrivate())
{
}

/*! Constructs a new empty document with the type set to \a type */
QVersitDocument::QVersitDocument(VersitType type) : d(new QVersitDocumentPrivate())
{
    d->mVersitType = type;
}

/*! Constructs a document that is a copy of \a other */
QVersitDocument::QVersitDocument(const QVersitDocument& other) : d(other.d)
{
}

/*! Frees the memory used by the document */
QVersitDocument::~QVersitDocument()
{
}

/*! Assigns this document to \a other */
QVersitDocument& QVersitDocument::operator=(const QVersitDocument& other)
{
    if (this != &other)
        d = other.d;
    return *this;
}

/*! Returns true if this is equal to \a other; false otherwise. */
bool QVersitDocument::operator==(const QVersitDocument& other) const
{
    return d->mVersitType == other.d->mVersitType &&
            d->mProperties == other.d->mProperties &&
            d->mSubDocuments == other.d->mSubDocuments &&
            d->mComponentType == other.d->mComponentType;
}

/*! Returns true if this is not equal to \a other; false otherwise. */
bool QVersitDocument::operator!=(const QVersitDocument& other) const
{
    return !(*this == other);
}

/*! Returns the hash value for \a key. */
uint qHash(const QVersitDocument &key)
{
    int hash = QT_PREPEND_NAMESPACE(qHash)(key.type());
    hash += QT_PREPEND_NAMESPACE(qHash)(key.componentType());
    foreach (const QVersitProperty& property, key.properties()) {
        hash += qHash(property);
    }
    foreach (const QVersitDocument& nested, key.subDocuments()) {
        hash += qHash(nested);
    }
    return hash;
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QVersitDocument& document)
{
    dbg.nospace() << "QVersitDocument(" << document.type() << ", " << document.componentType() << ')';
    foreach (const QVersitProperty& property, document.properties()) {
        dbg.space() << '\n' << property;
    }
    foreach (const QVersitDocument& nested, document.subDocuments()) {
        dbg.space() << '\n' << nested;
    }
    return dbg.maybeSpace();
}
#endif

/*!
 * Sets the versit document type to \a type.  This determines the format in which the document is
 * to be serialized.
 */
void QVersitDocument::setType(VersitType type)
{
    d->mVersitType = type;
}

/*!
 * Gets the versit document type.
 */
QVersitDocument::VersitType QVersitDocument::type() const
{
    return d->mVersitType;
}

/*!
 * Sets the versit component type to \a componentType (eg. VCARD, VCALENDAR, VEVENT, etc.)
 */
void QVersitDocument::setComponentType(QString componentType)
{
    d->mComponentType = componentType;
}

/*!
 * Gets the versit component type
 */
QString QVersitDocument::componentType() const
{
    return d->mComponentType;
}

/*!
 * Add \a property to the list of contained versit properties.
 * The property is appended as the last property of the list.
 */
void QVersitDocument::addProperty(const QVersitProperty& property)
{
    d->mProperties.append(property);
}

/*!
 * Removes the property \a property from the versit document.
 */
void QVersitDocument::removeProperty(const QVersitProperty& property)
{
    d->mProperties.removeAll(property);
}

/*!
 * Removes all the properties with the given \a name from the versit document.
 */
void QVersitDocument::removeProperties(const QString& name)
{
    for (int i=d->mProperties.count()-1; i >=0; i--) {
        if (d->mProperties[i].name() == name) {
            d->mProperties.removeAt(i);
        }
    }
}

/*!
 * Sets the list of properties to \a properties.  Logically, all of the existing properties are
 * removed and all of the supplied \a properties are added.
 */
void QVersitDocument::setProperties(const QList<QVersitProperty>& properties)
{
    d->mProperties = properties;
}

/*!
 * Gets the list of the contained versit properties.
 * Note that the actual properties cannot be modified using the copy.
 */
QList<QVersitProperty> QVersitDocument::properties() const
{
    return d->mProperties;
}

/*!
 * Adds \a subdocument to the Versit document.
 */
void QVersitDocument::addSubDocument(const QVersitDocument& subdocument)
{
    d->mSubDocuments.append(subdocument);
}

/*!
 * Removes the \a subdocument from the versit document.
 */
void QVersitDocument::removeSubDocument(const QVersitDocument& subdocument)
{
    d->mSubDocuments.removeAll(subdocument);
}

/*!
 * Sets the list of subdocuments to \a documents.
 */
void QVersitDocument::setSubDocuments(const QList<QVersitDocument>& documents)
{
    d->mSubDocuments = documents;
}

/*!
 * Returns the list of subdocuments contained within this Versit document.
 */
QList<QVersitDocument> QVersitDocument::subDocuments() const
{
    return d->mSubDocuments;
}

/*!
 * Returns true if the document is empty.
 */
bool QVersitDocument::isEmpty() const
{
    return d->mProperties.isEmpty()
        && d->mSubDocuments.isEmpty()
        && d->mVersitType == QVersitDocument::InvalidType;
}

/*!
 * Clears the document, removing all properties and metadata
 * and resetting the codec to the default.
 */
void QVersitDocument::clear()
{
    d->mProperties.clear();
    d->mSubDocuments.clear();
    d->mVersitType = QVersitDocument::InvalidType;
    d->mComponentType.clear();
}

QTM_END_NAMESPACE
<commit_msg>A minor documentation fix.<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qversitdocument.h"
#include "qversitdocument_p.h"
#include "qmobilityglobal.h"

#include <QTextCodec>

QTM_BEGIN_NAMESPACE

/*!
  \class QVersitDocument
  \brief The QVersitDocument class is a container for a list of versit properties.
  \ingroup versit
  \inmodule QtVersit

  A vCard is represented in abstract form as a QVersitDocument that consists of a number of
  properties such as a name (N), a telephone number (TEL) and an email address (EMAIL), for
  instance.  Each of these properties is stored as an instance of a QVersitProperty in a
  QVersitDocument.

  In addition to the list of properties, QVersitDocument also records the type of the Versit
  document in two ways.  The VersitType enum describes the format in which the document is to be
  serialized by QVersitWriter (or the format from which it was read by QVersitReader), and should
  not be used to infer any semantics about the document data.  The componentType field is a string
  corresponding directly to the value of the BEGIN line in a document.  For example, for a vCard,
  this will always be the string "VCARD"; for an iCalendar, it could be "VCALENDAR", "VEVENT",
  "VTODO", "VJOURNAL", "VALARM" or "VTIMEZONE".

  As well as properties, a QVersitDocument can hold other documents.  For iCalendar, this is how
  a single VCALENDAR document can compose documents of type VEVENT, VTODO, etc.

  For example, for the following iCalendar:
  \code
  BEGIN:VCALENDAR
  VERSION:2.0
  BEGIN:VEVENT
  SUMMARY:Christmas
  DTSTART:20001225
  END:VEVENT
  END:VCALENDAR
  \endcode

  This can be represented as a QVersitDocument of with componentType VCALENDAR and versitType
  ICalendar20Type.  It contains no properties (note: the VERSION property is not stored explicitly
  as a property) and one sub-document.  The sub-document has componentType VEVENT and versitType
  ICalendar20Type, and contains two properties.

  QVersitDocument supports implicit sharing.

  \sa QVersitProperty
 */

/*!
  \enum QVersitDocument::VersitType
  This enum describes a Versit document serialization format and version.
  \value InvalidType No type specified or a document with an invalid type was parsed
  \value VCard21Type vCard version 2.1
  \value VCard30Type vCard version 3.0
  \value VCard40Type vCard version 4.0
  \value ICalendar20Type iCalendar version 2.0
 */

/*! Constructs a new empty document */
QVersitDocument::QVersitDocument() : d(new QVersitDocumentPrivate())
{
}

/*! Constructs a new empty document with the type set to \a type */
QVersitDocument::QVersitDocument(VersitType type) : d(new QVersitDocumentPrivate())
{
    d->mVersitType = type;
}

/*! Constructs a document that is a copy of \a other */
QVersitDocument::QVersitDocument(const QVersitDocument& other) : d(other.d)
{
}

/*! Frees the memory used by the document */
QVersitDocument::~QVersitDocument()
{
}

/*! Assigns this document to \a other */
QVersitDocument& QVersitDocument::operator=(const QVersitDocument& other)
{
    if (this != &other)
        d = other.d;
    return *this;
}

/*! Returns true if this is equal to \a other; false otherwise. */
bool QVersitDocument::operator==(const QVersitDocument& other) const
{
    return d->mVersitType == other.d->mVersitType &&
            d->mProperties == other.d->mProperties &&
            d->mSubDocuments == other.d->mSubDocuments &&
            d->mComponentType == other.d->mComponentType;
}

/*! Returns true if this is not equal to \a other; false otherwise. */
bool QVersitDocument::operator!=(const QVersitDocument& other) const
{
    return !(*this == other);
}

/*! Returns the hash value for \a key. */
uint qHash(const QVersitDocument &key)
{
    int hash = QT_PREPEND_NAMESPACE(qHash)(key.type());
    hash += QT_PREPEND_NAMESPACE(qHash)(key.componentType());
    foreach (const QVersitProperty& property, key.properties()) {
        hash += qHash(property);
    }
    foreach (const QVersitDocument& nested, key.subDocuments()) {
        hash += qHash(nested);
    }
    return hash;
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QVersitDocument& document)
{
    dbg.nospace() << "QVersitDocument(" << document.type() << ", " << document.componentType() << ')';
    foreach (const QVersitProperty& property, document.properties()) {
        dbg.space() << '\n' << property;
    }
    foreach (const QVersitDocument& nested, document.subDocuments()) {
        dbg.space() << '\n' << nested;
    }
    return dbg.maybeSpace();
}
#endif

/*!
 * Sets the versit document type to \a type.  This determines the format in which the document is
 * to be serialized.
 */
void QVersitDocument::setType(VersitType type)
{
    d->mVersitType = type;
}

/*!
 * Gets the versit document type.
 */
QVersitDocument::VersitType QVersitDocument::type() const
{
    return d->mVersitType;
}

/*!
 * Sets the versit component type to \a componentType (eg. VCARD, VCALENDAR, VEVENT, etc.)
 */
void QVersitDocument::setComponentType(QString componentType)
{
    d->mComponentType = componentType;
}

/*!
 * Gets the versit component type
 */
QString QVersitDocument::componentType() const
{
    return d->mComponentType;
}

/*!
 * Add \a property to the list of contained versit properties.
 * The property is appended as the last property of the list.
 */
void QVersitDocument::addProperty(const QVersitProperty& property)
{
    d->mProperties.append(property);
}

/*!
 * Removes the property \a property from the versit document.
 */
void QVersitDocument::removeProperty(const QVersitProperty& property)
{
    d->mProperties.removeAll(property);
}

/*!
 * Removes all the properties with the given \a name from the versit document.
 */
void QVersitDocument::removeProperties(const QString& name)
{
    for (int i=d->mProperties.count()-1; i >=0; i--) {
        if (d->mProperties[i].name() == name) {
            d->mProperties.removeAt(i);
        }
    }
}

/*!
 * Sets the list of properties to \a properties.  Logically, all of the existing properties are
 * removed and all of the supplied \a properties are added.
 */
void QVersitDocument::setProperties(const QList<QVersitProperty>& properties)
{
    d->mProperties = properties;
}

/*!
 * Gets the list of the contained versit properties.
 * Note that the actual properties cannot be modified using the copy.
 */
QList<QVersitProperty> QVersitDocument::properties() const
{
    return d->mProperties;
}

/*!
 * Adds \a subdocument to the Versit document.
 */
void QVersitDocument::addSubDocument(const QVersitDocument& subdocument)
{
    d->mSubDocuments.append(subdocument);
}

/*!
 * Removes the \a subdocument from the versit document.
 */
void QVersitDocument::removeSubDocument(const QVersitDocument& subdocument)
{
    d->mSubDocuments.removeAll(subdocument);
}

/*!
 * Sets the list of subdocuments to \a documents.
 */
void QVersitDocument::setSubDocuments(const QList<QVersitDocument>& documents)
{
    d->mSubDocuments = documents;
}

/*!
 * Returns the list of subdocuments contained within this Versit document.
 */
QList<QVersitDocument> QVersitDocument::subDocuments() const
{
    return d->mSubDocuments;
}

/*!
 * Returns true if the document is empty.
 */
bool QVersitDocument::isEmpty() const
{
    return d->mProperties.isEmpty()
        && d->mSubDocuments.isEmpty()
        && d->mVersitType == QVersitDocument::InvalidType;
}

/*!
 * Clears the document, removing all properties, sub-documents and metadata.
 */
void QVersitDocument::clear()
{
    d->mProperties.clear();
    d->mSubDocuments.clear();
    d->mVersitType = QVersitDocument::InvalidType;
    d->mComponentType.clear();
}

QTM_END_NAMESPACE
<|endoftext|>