<commit_before>/*
 * Copyright (c) 2008 James Molloy, Jörg Pfähler, Matthew Iselin
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <utilities/RequestQueue.h>
#include <processor/Processor.h>
#include <panic.h>
#include <Log.h>

#include <utilities/assert.h>

RequestQueue::RequestQueue() :
  m_RequestQueueSize(0),
  m_Stop(false), m_RequestQueueMutex(false), m_pThread(0)
{
    for (size_t i = 0; i < REQUEST_QUEUE_NUM_PRIORITIES; i++)
        m_pRequestQueue[i] = 0;
}

RequestQueue::~RequestQueue()
{
}

void RequestQueue::initialise()
{
  // Start the worker thread.
#ifdef THREADS
  m_pThread = new Thread(Processor::information().getCurrentThread()->getParent(),
                       reinterpret_cast<Thread::ThreadStartFunc> (&trampoline),
                       reinterpret_cast<void*> (this));
#endif
}

void RequestQueue::destroy()
{
  // Cause the worker thread to stop.
  m_Stop = true;
  // Post to the queue length semaphore to ensure the worker thread wakes up.
  m_RequestQueueSize.release();
}

uint64_t RequestQueue::addRequest(size_t priority, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4,
                                  uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)
{
  // Create a new request object.
  Request *pReq = new Request();
  pReq->p1 = p1; pReq->p2 = p2; pReq->p3 = p3; pReq->p4 = p4; pReq->p5 = p5; pReq->p6 = p6; pReq->p7 = p7; pReq->p8 = p8;
  pReq->isAsync = false;
  pReq->next = 0;
  pReq->bReject = false;
  pReq->pThread = Processor::information().getCurrentThread();
  pReq->pThread->addRequest(pReq);

  // Add to the request queue.
  m_RequestQueueMutex.acquire();

  if (m_pRequestQueue[priority] == 0)
    m_pRequestQueue[priority] = pReq;
  else
  {
    Request *p = m_pRequestQueue[priority];
    while (p->next != 0)
      p = p->next;
    p->next = pReq;
  }

  // Increment the number of items on the request queue.
  m_RequestQueueSize.release();

  m_RequestQueueMutex.release();

  // We are waiting on the worker thread - mark the thread as such.
  Thread *pThread = Processor::information().getCurrentThread();
  pThread->setBlockingThread(m_pThread);

  // Wait for the request to be satisfied. This should sleep the thread.
  pReq->mutex.acquire();

  // Don't use the Thread object if it may be already freed
  if(!pReq->bReject)
      pThread->setBlockingThread(0);

  if(pReq->bReject || pThread->wasInterrupted() || pThread->getUnwindState() == Thread::Exit)
  {
      // The request was interrupted somehow. We cannot assume that pReq's
      // contents are valid, so just return zero. The caller may have to redo
      // their request.
      // By releasing here, the worker thread can detect that the request was
      // interrupted and clean up by itself.
      NOTICE("RequestQueue::addRequest - interrupted");
      pReq->mutex.release();
      return 0;
  }

  // Grab the result.
  uintptr_t ret = pReq->ret;

  // Delete the request structure.
  pReq->pThread->removeRequest(pReq);
  delete pReq;

  return ret;
}

uint64_t RequestQueue::addAsyncRequest(size_t priority, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4,
                                       uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)
{
  // Create a new request object.
  Request *pReq = new Request();
  pReq->p1 = p1; pReq->p2 = p2; pReq->p3 = p3; pReq->p4 = p4; pReq->p5 = p5; pReq->p6 = p6; pReq->p7 = p7; pReq->p8 = p8;
  pReq->isAsync = true;
  pReq->next = 0;
  pReq->bReject = false;
  pReq->pThread = Processor::information().getCurrentThread();
  pReq->pThread->addRequest(pReq);

  // Add to the request queue.
  m_RequestQueueMutex.acquire();

  if (m_pRequestQueue[priority] == 0)
    m_pRequestQueue[priority] = pReq;
  else
  {
    Request *p = m_pRequestQueue[priority];
    while (p->next != 0)
    {
      if(p == pReq)
      {
        return 0;
      }
      p = p->next;
    }
    if(p != pReq)
      p->next = pReq;
    else
    {
      return 0;
    }
  }

  assert_heap_ptr_valid(pReq);

  // Increment the number of items on the request queue.
  m_RequestQueueSize.release();

  // If the queue is too long, make this block.
  if (m_RequestQueueSize.getValue() > REQUEST_QUEUE_MAX_QUEUE_SZ)
  {
      pReq->isAsync = false;

      m_RequestQueueMutex.release();

      // We are waiting on the worker thread - mark the thread as such.
      Thread *pThread = Processor::information().getCurrentThread();
      pThread->setBlockingThread(m_pThread);

      // Wait for the request to be satisfied. This should sleep the thread.
      pReq->mutex.acquire();

      pThread->setBlockingThread(0);

      if(pReq->bReject || pThread->wasInterrupted() || pThread->getUnwindState() == Thread::Exit)
      {
          // The request was interrupted somehow. We cannot assume that pReq's
          // contents are valid, so just return zero. The caller may have to redo
          // their request.
          // By releasing here, the worker thread can detect that the request was
          // interrupted and clean up by itself.
          NOTICE("RequestQueue::addRequest - interrupted");
          pReq->pThread->removeRequest(pReq);
          pReq->mutex.release();
          return 0;
      }

      // Delete the request structure.
      pReq->pThread->removeRequest(pReq);
      delete pReq;
  }
  else
      m_RequestQueueMutex.release();

  return 0;
}

int RequestQueue::trampoline(void *p)
{
  RequestQueue *pRQ = reinterpret_cast<RequestQueue*> (p);
  return pRQ->work();
}

int RequestQueue::work()
{
  while (true)
  {
    // Sleep on the queue length semaphore - wake when there's something to do.
    m_RequestQueueSize.acquire();

    // Check why we were woken - is m_Stop set? If so, quit.
    if (m_Stop)
      return 0;

    // Get the first request from the queue.
    m_RequestQueueMutex.acquire();

    // Get the most important queue with data in.
    /// \todo Stop possible starvation here.
    size_t priority = 0;
    for (priority = 0; priority < REQUEST_QUEUE_NUM_PRIORITIES-1; priority++)
        if (m_pRequestQueue[priority])
            break;

    Request *pReq = m_pRequestQueue[priority];
    // Quick sanity check:
    if (pReq == 0)
    {
        if(Processor::information().getCurrentThread()->getUnwindState() == Thread::ReleaseBlockingThread)
            continue;
        ERROR("Unwind state: " << (size_t)Processor::information().getCurrentThread()->getUnwindState());
        FATAL("RequestQueue: Worker thread woken but no requests pending!");
    }
    m_pRequestQueue[priority] = pReq->next;

    m_RequestQueueMutex.release();

    // Verify that it's still valid to run the request
    if(pReq->bReject)
    {
        WARNING("RequestQueue: request rejected");
        if(pReq->isAsync)
            delete pReq;
        continue;
    }

    // Perform the request.
    pReq->ret = executeRequest(pReq->p1, pReq->p2, pReq->p3, pReq->p4, pReq->p5, pReq->p6, pReq->p7, pReq->p8);
    if(pReq->mutex.tryAcquire())
    {
        // Something's gone wrong - the calling thread has released the Mutex. Destroy the request
        // and grab the next request from the queue. The calling thread has long since stopped
        // caring about whether we're done or not.
        NOTICE("RequestQueue::work - caller interrupted");
        pReq->pThread->removeRequest(pReq);
        continue;
    }
    switch (Processor::information().getCurrentThread()->getUnwindState())
    {
        case Thread::Continue:
            break;
        case Thread::Exit:
            return 0;
        case Thread::ReleaseBlockingThread:
            Processor::information().getCurrentThread()->setUnwindState(Thread::Continue);
            break;
    }

    bool bAsync = pReq->isAsync;

    // Request finished - post the request's mutex to wake the calling thread.
    pReq->mutex.release();

    // If the request was asynchronous, destroy the request structure.
    if (bAsync)
    {
        pReq->pThread->removeRequest(pReq);
        delete pReq;
    }
  }
  return 0;
}
<commit_msg>Work towards fixing the rare breakpoint when dumping a large file. This merely checks to see if a request has been rejected before it acquires the request mutex.<commit_after>/*
 * Copyright (c) 2008 James Molloy, Jörg Pfähler, Matthew Iselin
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <utilities/RequestQueue.h>
#include <processor/Processor.h>
#include <panic.h>
#include <Log.h>

#include <utilities/assert.h>

RequestQueue::RequestQueue() :
  m_RequestQueueSize(0),
  m_Stop(false), m_RequestQueueMutex(false), m_pThread(0)
{
    for (size_t i = 0; i < REQUEST_QUEUE_NUM_PRIORITIES; i++)
        m_pRequestQueue[i] = 0;
}

RequestQueue::~RequestQueue()
{
}

void RequestQueue::initialise()
{
  // Start the worker thread.
#ifdef THREADS
  m_pThread = new Thread(Processor::information().getCurrentThread()->getParent(),
                       reinterpret_cast<Thread::ThreadStartFunc> (&trampoline),
                       reinterpret_cast<void*> (this));
#endif
}

void RequestQueue::destroy()
{
  // Cause the worker thread to stop.
  m_Stop = true;
  // Post to the queue length semaphore to ensure the worker thread wakes up.
  m_RequestQueueSize.release();
}

uint64_t RequestQueue::addRequest(size_t priority, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4,
                                  uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)
{
  // Create a new request object.
  Request *pReq = new Request();
  pReq->p1 = p1; pReq->p2 = p2; pReq->p3 = p3; pReq->p4 = p4; pReq->p5 = p5; pReq->p6 = p6; pReq->p7 = p7; pReq->p8 = p8;
  pReq->isAsync = false;
  pReq->next = 0;
  pReq->bReject = false;
  pReq->pThread = Processor::information().getCurrentThread();
  pReq->pThread->addRequest(pReq);

  // Add to the request queue.
  m_RequestQueueMutex.acquire();

  if (m_pRequestQueue[priority] == 0)
    m_pRequestQueue[priority] = pReq;
  else
  {
    Request *p = m_pRequestQueue[priority];
    while (p->next != 0)
      p = p->next;
    p->next = pReq;
  }

  // Increment the number of items on the request queue.
  m_RequestQueueSize.release();

  m_RequestQueueMutex.release();

  // We are waiting on the worker thread - mark the thread as such.
  Thread *pThread = Processor::information().getCurrentThread();
  pThread->setBlockingThread(m_pThread);

  if(pReq->bReject)
  {
      // Hmm, in the time the RequestQueueMutex was being acquired, we got
      // pre-empted, and then an unexpected exit event happened. The request
      // is to be rejected, so don't acquire the mutex at all.
      delete pReq;
      return 0;
  }

  // Wait for the request to be satisfied. This should sleep the thread.
  pReq->mutex.acquire();

  // Don't use the Thread object if it may be already freed
  if(!pReq->bReject)
      pThread->setBlockingThread(0);

  if(pReq->bReject || pThread->wasInterrupted() || pThread->getUnwindState() == Thread::Exit)
  {
      // The request was interrupted somehow. We cannot assume that pReq's
      // contents are valid, so just return zero. The caller may have to redo
      // their request.
      // By releasing here, the worker thread can detect that the request was
      // interrupted and clean up by itself.
      NOTICE("RequestQueue::addRequest - interrupted");
      if(pReq->bReject)
          delete pReq; // Safe to delete, unexpected exit condition
      pReq->mutex.release();
      return 0;
  }

  // Grab the result.
  uintptr_t ret = pReq->ret;

  // Delete the request structure.
  pReq->pThread->removeRequest(pReq);
  delete pReq;

  return ret;
}

uint64_t RequestQueue::addAsyncRequest(size_t priority, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4,
                                       uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)
{
  // Create a new request object.
  Request *pReq = new Request();
  pReq->p1 = p1; pReq->p2 = p2; pReq->p3 = p3; pReq->p4 = p4; pReq->p5 = p5; pReq->p6 = p6; pReq->p7 = p7; pReq->p8 = p8;
  pReq->isAsync = true;
  pReq->next = 0;
  pReq->bReject = false;
  pReq->pThread = Processor::information().getCurrentThread();
  pReq->pThread->addRequest(pReq);

  // Add to the request queue.
  m_RequestQueueMutex.acquire();

  if (m_pRequestQueue[priority] == 0)
    m_pRequestQueue[priority] = pReq;
  else
  {
    Request *p = m_pRequestQueue[priority];
    while (p->next != 0)
    {
      if(p == pReq)
      {
        return 0;
      }
      p = p->next;
    }
    if(p != pReq)
      p->next = pReq;
    else
    {
      return 0;
    }
  }

  assert_heap_ptr_valid(pReq);

  // Increment the number of items on the request queue.
  m_RequestQueueSize.release();

  // If the queue is too long, make this block.
  if (m_RequestQueueSize.getValue() > REQUEST_QUEUE_MAX_QUEUE_SZ)
  {
      pReq->isAsync = false;

      m_RequestQueueMutex.release();

      // We are waiting on the worker thread - mark the thread as such.
      Thread *pThread = Processor::information().getCurrentThread();
      pThread->setBlockingThread(m_pThread);

      // Wait for the request to be satisfied. This should sleep the thread.
      pReq->mutex.acquire();

      pThread->setBlockingThread(0);

      if(pReq->bReject || pThread->wasInterrupted() || pThread->getUnwindState() == Thread::Exit)
      {
          // The request was interrupted somehow. We cannot assume that pReq's
          // contents are valid, so just return zero. The caller may have to redo
          // their request.
          // By releasing here, the worker thread can detect that the request was
          // interrupted and clean up by itself.
          NOTICE("RequestQueue::addRequest - interrupted");
          pReq->pThread->removeRequest(pReq);
          pReq->mutex.release();
          return 0;
      }

      // Delete the request structure.
      pReq->pThread->removeRequest(pReq);
      delete pReq;
  }
  else
      m_RequestQueueMutex.release();

  return 0;
}

int RequestQueue::trampoline(void *p)
{
  RequestQueue *pRQ = reinterpret_cast<RequestQueue*> (p);
  return pRQ->work();
}

int RequestQueue::work()
{
  while (true)
  {
    // Sleep on the queue length semaphore - wake when there's something to do.
    m_RequestQueueSize.acquire();

    // Check why we were woken - is m_Stop set? If so, quit.
    if (m_Stop)
      return 0;

    // Get the first request from the queue.
    m_RequestQueueMutex.acquire();

    // Get the most important queue with data in.
    /// \todo Stop possible starvation here.
    size_t priority = 0;
    for (priority = 0; priority < REQUEST_QUEUE_NUM_PRIORITIES-1; priority++)
        if (m_pRequestQueue[priority])
            break;

    Request *pReq = m_pRequestQueue[priority];
    // Quick sanity check:
    if (pReq == 0)
    {
        if(Processor::information().getCurrentThread()->getUnwindState() == Thread::ReleaseBlockingThread)
            continue;
        ERROR("Unwind state: " << (size_t)Processor::information().getCurrentThread()->getUnwindState());
        FATAL("RequestQueue: Worker thread woken but no requests pending!");
    }
    m_pRequestQueue[priority] = pReq->next;

    m_RequestQueueMutex.release();

    // Verify that it's still valid to run the request
    if(pReq->bReject)
    {
        WARNING("RequestQueue: request rejected");
        if(pReq->isAsync)
            delete pReq;
        continue;
    }

    // Perform the request.
    pReq->ret = executeRequest(pReq->p1, pReq->p2, pReq->p3, pReq->p4, pReq->p5, pReq->p6, pReq->p7, pReq->p8);
    if(pReq->mutex.tryAcquire())
    {
        // Something's gone wrong - the calling thread has released the Mutex. Destroy the request
        // and grab the next request from the queue. The calling thread has long since stopped
        // caring about whether we're done or not.
        NOTICE("RequestQueue::work - caller interrupted");
        pReq->pThread->removeRequest(pReq);
        continue;
    }
    switch (Processor::information().getCurrentThread()->getUnwindState())
    {
        case Thread::Continue:
            break;
        case Thread::Exit:
            return 0;
        case Thread::ReleaseBlockingThread:
            Processor::information().getCurrentThread()->setUnwindState(Thread::Continue);
            break;
    }

    bool bAsync = pReq->isAsync;

    // Request finished - post the request's mutex to wake the calling thread.
    pReq->mutex.release();

    // If the request was asynchronous, destroy the request structure.
    if (bAsync)
    {
        pReq->pThread->removeRequest(pReq);
        delete pReq;
    }
  }
  return 0;
}
<|endoftext|>


<commit_before>/**
 ** \file  libport/escape.cc
 ** \brief Implementation for libport/escape.hh.
 **/

#include <ios>
#include <iomanip>
#include <cctype>
#include "libport/escape.hh"

namespace libport
{
  std::ostream&
  Escape::print (std::ostream& ostr) const
  {
    return escape_ (ostr, pobj_str_);
  }

  std::ostream&
  Escape::escape_ (std::ostream& o, const std::string& es) const
  {
    // For some reason yet to be found, when we use the locale for
    // std::isprint, Valgrind goes berzerk.  So we no longer do the
    // following:
    //
    // static std::locale locale ("");
    //
    // if (std::isprint (*p, locale))
    std::ios_base::fmtflags flags = o.flags (std::ios_base::hex);
    char fill = o.fill ('0');

    for (std::string::const_iterator p = es.begin (); p != es.end (); ++p)
      switch (*p)
      {
	case '\b': o << "\\b"; break;
	case '\f': o << "\\f"; break;
	case '\n': o << "\\n"; break;
	case '\r': o << "\\r"; break;
	case '\t': o << "\\t"; break;
	case '\v': o << "\\v"; break;

	case '\\': o << "\\\\"; break;
	case '\"': o << "\\\""; break;
	default:
	  if (std::isprint (*p))
	    o << *p;
	  else
	    o << "\\x" << std::setw (2) << (int) (unsigned char) *p;
      }

    o.fill (fill);
    o.flags (flags);
    return o;
  }
}
<commit_msg>Improve escaping<commit_after>/**
 ** \file  libport/escape.cc
 ** \brief Implementation for libport/escape.hh.
 **/

#include <ios>
#include <iomanip>
#include <cctype>
#include "libport/escape.hh"

namespace libport
{
  std::ostream&
  Escape::print (std::ostream& ostr) const
  {
    return escape_ (ostr, pobj_str_);
  }

  std::ostream&
  Escape::escape_ (std::ostream& o, const std::string& es) const
  {
    // For some reason yet to be found, when we use the locale for
    // std::isprint, Valgrind goes berzerk.  So we no longer do the
    // following:
    //
    // static std::locale locale ("");
    //
    // if (std::isprint (*p, locale))
    std::ios_base::fmtflags flags = o.flags (std::ios_base::hex);
    char fill = o.fill ('0');

    for (std::string::const_iterator p = es.begin (); p != es.end (); ++p)
      switch (*p)
      {
	case '\b': o << "\\b"; break;
	case '\f': o << "\\f"; break;
	case '\n': o << "\\n"; break;
	case '\r': o << "\\r"; break;
	case '\t': o << "\\t"; break;
	case '\v': o << "\\v"; break;

	case '\\': o << "\\\\"; break;
	case '\"': o << "\\\""; break;
	case '\'': o << "\\\'"; break;
	default:
	  if (std::isprint (*p))
	    o << *p;
	  else
	    o << "\\x" << std::setw (2) << (int) (unsigned char) *p;
      }

    o.fill (fill);
    o.flags (flags);
    return o;
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// For loading files, we make use of overlapped i/o to ensure that reading from
// the filesystem (e.g., a network filesystem) does not block the calling
// thread.  An alternative approach would be to use a background thread or pool
// of threads, but it seems better to leverage the operating system's ability
// to do background file reads for us.
//
// Since overlapped reads require a 'static' buffer for the duration of the
// asynchronous read, the URLRequestFileJob keeps a buffer as a member var.  In
// URLRequestFileJob::Read, data is simply copied from the object's buffer into
// the given buffer.  If there is no data to copy, the URLRequestFileJob
// attempts to read more from the file to fill its buffer.  If reading from the
// file does not complete synchronously, then the URLRequestFileJob waits for a
// signal from the OS that the overlapped read has completed.  It does so by
// leveraging the MessageLoop::WatchObject API.

#include "net/url_request/url_request_file_job.h"

#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/platform_file.h"
#include "base/string_util.h"
#include "base/synchronization/lock.h"
#include "base/threading/worker_pool.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "googleurl/src/gurl.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_file_dir_job.h"

namespace net {

#if defined(OS_WIN)
class URLRequestFileJob::AsyncResolver
    : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> {
 public:
  explicit AsyncResolver(URLRequestFileJob* owner)
      : owner_(owner), owner_loop_(MessageLoop::current()) {
  }

  void Resolve(const FilePath& file_path) {
    base::PlatformFileInfo file_info;
    bool exists = file_util::GetFileInfo(file_path, &file_info);
    base::AutoLock locked(lock_);
    if (owner_loop_) {
      owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
          this, &AsyncResolver::ReturnResults, exists, file_info));
    }
  }

  void Cancel() {
    owner_ = NULL;

    base::AutoLock locked(lock_);
    owner_loop_ = NULL;
  }

 private:
  friend class base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver>;

  ~AsyncResolver() {}

  void ReturnResults(bool exists, const base::PlatformFileInfo& file_info) {
    if (owner_)
      owner_->DidResolve(exists, file_info);
  }

  URLRequestFileJob* owner_;

  base::Lock lock_;
  MessageLoop* owner_loop_;
};
#endif

URLRequestFileJob::URLRequestFileJob(URLRequest* request,
                                     const FilePath& file_path)
    : URLRequestJob(request),
      file_path_(file_path),
      ALLOW_THIS_IN_INITIALIZER_LIST(
          io_callback_(this, &URLRequestFileJob::DidRead)),
      is_directory_(false),
      remaining_bytes_(0),
      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
}

// static
URLRequestJob* URLRequestFileJob::Factory(URLRequest* request,
                                          const std::string& scheme) {

  FilePath file_path;
  const bool is_file = FileURLToFilePath(request->url(), &file_path);

#if defined(OS_CHROMEOS)
  // Check file access.
  if (AccessDisabled(file_path))
    return new URLRequestErrorJob(request, ERR_ACCESS_DENIED);
#endif

  // We need to decide whether to create URLRequestFileJob for file access or
  // URLRequestFileDirJob for directory access. To avoid accessing the
  // filesystem, we only look at the path string here.
  // The code in the URLRequestFileJob::Start() method discovers that a path,
  // which doesn't end with a slash, should really be treated as a directory,
  // and it then redirects to the URLRequestFileDirJob.
  if (is_file &&
      file_util::EndsWithSeparator(file_path) &&
      file_path.IsAbsolute())
    return new URLRequestFileDirJob(request, file_path);

  // Use a regular file request job for all non-directories (including invalid
  // file names).
  return new URLRequestFileJob(request, file_path);
}

#if defined(OS_CHROMEOS)
static const char* const kLocalAccessWhiteList[] = {
  "/home/chronos/user/Downloads",
  "/media",
  "/opt/oem",
  "/usr/share/chromeos-assets",
  "/tmp",
  "/var/log",
};

// static
bool URLRequestFileJob::AccessDisabled(const FilePath& file_path) {
  if (URLRequest::IsFileAccessAllowed()) {  // for tests.
    return false;
  }

  for (size_t i = 0; i < arraysize(kLocalAccessWhiteList); ++i) {
    const FilePath white_listed_path(kLocalAccessWhiteList[i]);
    // FilePath::operator== should probably handle trailing seperators.
    if (white_listed_path == file_path.StripTrailingSeparators() ||
        white_listed_path.IsParent(file_path)) {
      return false;
    }
  }
  return true;
}
#endif

void URLRequestFileJob::Start() {
#if defined(OS_WIN)
  // Resolve UNC paths on a background thread.
  if (!file_path_.value().compare(0, 2, L"\\\\")) {
    DCHECK(!async_resolver_);
    async_resolver_ = new AsyncResolver(this);
    base::WorkerPool::PostTask(FROM_HERE, NewRunnableMethod(
        async_resolver_.get(), &AsyncResolver::Resolve, file_path_), true);
    return;
  }
#endif

  // URL requests should not block on the disk!
  //   http://code.google.com/p/chromium/issues/detail?id=59849
  bool exists;
  base::PlatformFileInfo file_info;
  {
    base::ThreadRestrictions::ScopedAllowIO allow_io;
    exists = file_util::GetFileInfo(file_path_, &file_info);
  }

  // Continue asynchronously.
  MessageLoop::current()->PostTask(
      FROM_HERE,
      method_factory_.NewRunnableMethod(
          &URLRequestFileJob::DidResolve, exists, file_info));
}

void URLRequestFileJob::Kill() {
  stream_.Close();

#if defined(OS_WIN)
  if (async_resolver_) {
    async_resolver_->Cancel();
    async_resolver_ = NULL;
  }
#endif

  URLRequestJob::Kill();
  method_factory_.RevokeAll();
}

bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
                                    int *bytes_read) {
  DCHECK_NE(dest_size, 0);
  DCHECK(bytes_read);
  DCHECK_GE(remaining_bytes_, 0);

  if (remaining_bytes_ < dest_size)
    dest_size = static_cast<int>(remaining_bytes_);

  // If we should copy zero bytes because |remaining_bytes_| is zero, short
  // circuit here.
  if (!dest_size) {
    *bytes_read = 0;
    return true;
  }

  int rv = stream_.Read(dest->data(), dest_size, &io_callback_);
  if (rv >= 0) {
    // Data is immediately available.
    *bytes_read = rv;
    remaining_bytes_ -= rv;
    DCHECK_GE(remaining_bytes_, 0);
    return true;
  }

  // Otherwise, a read error occured.  We may just need to wait...
  if (rv == ERR_IO_PENDING) {
    SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
  } else {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
  }
  return false;
}

bool URLRequestFileJob::IsRedirectResponse(GURL* location,
                                           int* http_status_code) {
  if (is_directory_) {
    // This happens when we discovered the file is a directory, so needs a
    // slash at the end of the path.
    std::string new_path = request_->url().path();
    new_path.push_back('/');
    GURL::Replacements replacements;
    replacements.SetPathStr(new_path);

    *location = request_->url().ReplaceComponents(replacements);
    *http_status_code = 301;  // simulate a permanent redirect
    return true;
  }

#if defined(OS_WIN)
  // Follow a Windows shortcut.
  // We just resolve .lnk file, ignore others.
  if (!LowerCaseEqualsASCII(file_path_.Extension(), ".lnk"))
    return false;

  FilePath new_path = file_path_;
  bool resolved;
  resolved = file_util::ResolveShortcut(&new_path);

  // If shortcut is not resolved succesfully, do not redirect.
  if (!resolved)
    return false;

  *location = FilePathToFileURL(new_path);
  *http_status_code = 301;
  return true;
#else
  return false;
#endif
}

Filter* URLRequestFileJob::SetupFilter() const {
  // Bug 9936 - .svgz files needs to be decompressed.
  return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")
      ? Filter::GZipFactory() : NULL;
}

bool URLRequestFileJob::GetMimeType(std::string* mime_type) const {
  // URL requests should not block on the disk!  On Windows this goes to the
  // registry.
  //   http://code.google.com/p/chromium/issues/detail?id=59849
  base::ThreadRestrictions::ScopedAllowIO allow_io;
  DCHECK(request_);
  return GetMimeTypeFromFile(file_path_, mime_type);
}

void URLRequestFileJob::SetExtraRequestHeaders(
    const HttpRequestHeaders& headers) {
  std::string range_header;
  if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) {
    // We only care about "Range" header here.
    std::vector<HttpByteRange> ranges;
    if (HttpUtil::ParseRangeHeader(range_header, &ranges)) {
      if (ranges.size() == 1) {
        byte_range_ = ranges[0];
      } else {
        // We don't support multiple range requests in one single URL request,
        // because we need to do multipart encoding here.
        // TODO(hclam): decide whether we want to support multiple range
        // requests.
        NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
                                    ERR_REQUEST_RANGE_NOT_SATISFIABLE));
      }
    }
  }
}

URLRequestFileJob::~URLRequestFileJob() {
#if defined(OS_WIN)
  DCHECK(!async_resolver_);
#endif
}

void URLRequestFileJob::DidResolve(
    bool exists, const base::PlatformFileInfo& file_info) {
#if defined(OS_WIN)
  async_resolver_ = NULL;
#endif

  // We may have been orphaned...
  if (!request_)
    return;

  is_directory_ = file_info.is_directory;

  int rv = OK;
  // We use URLRequestFileJob to handle files as well as directories without
  // trailing slash.
  // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
  // we will append trailing slash and redirect to FileDirJob.
  // A special case is "\" on Windows. We should resolve as invalid.
  // However, Windows resolves "\" to "C:\", thus reports it as existent.
  // So what happens is we append it with trailing slash and redirect it to
  // FileDirJob where it is resolved as invalid.
  if (!exists) {
    rv = ERR_FILE_NOT_FOUND;
  } else if (!is_directory_) {
    // URL requests should not block on the disk!
    //   http://code.google.com/p/chromium/issues/detail?id=59849
    base::ThreadRestrictions::ScopedAllowIO allow_io;

    int flags = base::PLATFORM_FILE_OPEN |
                base::PLATFORM_FILE_READ |
                base::PLATFORM_FILE_ASYNC;
    rv = stream_.Open(file_path_, flags);
  }

  if (rv != OK) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
    return;
  }

  if (!byte_range_.ComputeBounds(file_info.size)) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
               ERR_REQUEST_RANGE_NOT_SATISFIABLE));
    return;
  }

  remaining_bytes_ = byte_range_.last_byte_position() -
                     byte_range_.first_byte_position() + 1;
  DCHECK_GE(remaining_bytes_, 0);

  // Do the seek at the beginning of the request.
  if (remaining_bytes_ > 0 &&
      byte_range_.first_byte_position() != 0 &&
      byte_range_.first_byte_position() !=
          stream_.Seek(FROM_BEGIN, byte_range_.first_byte_position())) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
               ERR_REQUEST_RANGE_NOT_SATISFIABLE));
    return;
  }

  set_expected_content_size(remaining_bytes_);
  NotifyHeadersComplete();
}

void URLRequestFileJob::DidRead(int result) {
  if (result > 0) {
    SetStatus(URLRequestStatus());  // Clear the IO_PENDING status
  } else if (result == 0) {
    NotifyDone(URLRequestStatus());
  } else {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
  }

  remaining_bytes_ -= result;
  DCHECK_GE(remaining_bytes_, 0);

  NotifyReadComplete(result);
}

}  // namespace net
<commit_msg>Add another AllowIO exception to URLRequestFileJob. Without this, playback of local-file videos FATALs w/ a violation message, but only if the file isn't small enough to have been cached by a previous operation that had an AllowIO in effect.  Oy!<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// For loading files, we make use of overlapped i/o to ensure that reading from
// the filesystem (e.g., a network filesystem) does not block the calling
// thread.  An alternative approach would be to use a background thread or pool
// of threads, but it seems better to leverage the operating system's ability
// to do background file reads for us.
//
// Since overlapped reads require a 'static' buffer for the duration of the
// asynchronous read, the URLRequestFileJob keeps a buffer as a member var.  In
// URLRequestFileJob::Read, data is simply copied from the object's buffer into
// the given buffer.  If there is no data to copy, the URLRequestFileJob
// attempts to read more from the file to fill its buffer.  If reading from the
// file does not complete synchronously, then the URLRequestFileJob waits for a
// signal from the OS that the overlapped read has completed.  It does so by
// leveraging the MessageLoop::WatchObject API.

#include "net/url_request/url_request_file_job.h"

#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/platform_file.h"
#include "base/string_util.h"
#include "base/synchronization/lock.h"
#include "base/threading/worker_pool.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "googleurl/src/gurl.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_file_dir_job.h"

namespace net {

#if defined(OS_WIN)
class URLRequestFileJob::AsyncResolver
    : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> {
 public:
  explicit AsyncResolver(URLRequestFileJob* owner)
      : owner_(owner), owner_loop_(MessageLoop::current()) {
  }

  void Resolve(const FilePath& file_path) {
    base::PlatformFileInfo file_info;
    bool exists = file_util::GetFileInfo(file_path, &file_info);
    base::AutoLock locked(lock_);
    if (owner_loop_) {
      owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
          this, &AsyncResolver::ReturnResults, exists, file_info));
    }
  }

  void Cancel() {
    owner_ = NULL;

    base::AutoLock locked(lock_);
    owner_loop_ = NULL;
  }

 private:
  friend class base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver>;

  ~AsyncResolver() {}

  void ReturnResults(bool exists, const base::PlatformFileInfo& file_info) {
    if (owner_)
      owner_->DidResolve(exists, file_info);
  }

  URLRequestFileJob* owner_;

  base::Lock lock_;
  MessageLoop* owner_loop_;
};
#endif

URLRequestFileJob::URLRequestFileJob(URLRequest* request,
                                     const FilePath& file_path)
    : URLRequestJob(request),
      file_path_(file_path),
      ALLOW_THIS_IN_INITIALIZER_LIST(
          io_callback_(this, &URLRequestFileJob::DidRead)),
      is_directory_(false),
      remaining_bytes_(0),
      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
}

// static
URLRequestJob* URLRequestFileJob::Factory(URLRequest* request,
                                          const std::string& scheme) {

  FilePath file_path;
  const bool is_file = FileURLToFilePath(request->url(), &file_path);

#if defined(OS_CHROMEOS)
  // Check file access.
  if (AccessDisabled(file_path))
    return new URLRequestErrorJob(request, ERR_ACCESS_DENIED);
#endif

  // We need to decide whether to create URLRequestFileJob for file access or
  // URLRequestFileDirJob for directory access. To avoid accessing the
  // filesystem, we only look at the path string here.
  // The code in the URLRequestFileJob::Start() method discovers that a path,
  // which doesn't end with a slash, should really be treated as a directory,
  // and it then redirects to the URLRequestFileDirJob.
  if (is_file &&
      file_util::EndsWithSeparator(file_path) &&
      file_path.IsAbsolute())
    return new URLRequestFileDirJob(request, file_path);

  // Use a regular file request job for all non-directories (including invalid
  // file names).
  return new URLRequestFileJob(request, file_path);
}

#if defined(OS_CHROMEOS)
static const char* const kLocalAccessWhiteList[] = {
  "/home/chronos/user/Downloads",
  "/media",
  "/opt/oem",
  "/usr/share/chromeos-assets",
  "/tmp",
  "/var/log",
};

// static
bool URLRequestFileJob::AccessDisabled(const FilePath& file_path) {
  if (URLRequest::IsFileAccessAllowed()) {  // for tests.
    return false;
  }

  for (size_t i = 0; i < arraysize(kLocalAccessWhiteList); ++i) {
    const FilePath white_listed_path(kLocalAccessWhiteList[i]);
    // FilePath::operator== should probably handle trailing seperators.
    if (white_listed_path == file_path.StripTrailingSeparators() ||
        white_listed_path.IsParent(file_path)) {
      return false;
    }
  }
  return true;
}
#endif

void URLRequestFileJob::Start() {
#if defined(OS_WIN)
  // Resolve UNC paths on a background thread.
  if (!file_path_.value().compare(0, 2, L"\\\\")) {
    DCHECK(!async_resolver_);
    async_resolver_ = new AsyncResolver(this);
    base::WorkerPool::PostTask(FROM_HERE, NewRunnableMethod(
        async_resolver_.get(), &AsyncResolver::Resolve, file_path_), true);
    return;
  }
#endif

  // URL requests should not block on the disk!
  //   http://code.google.com/p/chromium/issues/detail?id=59849
  bool exists;
  base::PlatformFileInfo file_info;
  {
    base::ThreadRestrictions::ScopedAllowIO allow_io;
    exists = file_util::GetFileInfo(file_path_, &file_info);
  }

  // Continue asynchronously.
  MessageLoop::current()->PostTask(
      FROM_HERE,
      method_factory_.NewRunnableMethod(
          &URLRequestFileJob::DidResolve, exists, file_info));
}

void URLRequestFileJob::Kill() {
  stream_.Close();

#if defined(OS_WIN)
  if (async_resolver_) {
    async_resolver_->Cancel();
    async_resolver_ = NULL;
  }
#endif

  URLRequestJob::Kill();
  method_factory_.RevokeAll();
}

bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
                                    int *bytes_read) {
  DCHECK_NE(dest_size, 0);
  DCHECK(bytes_read);
  DCHECK_GE(remaining_bytes_, 0);

  if (remaining_bytes_ < dest_size)
    dest_size = static_cast<int>(remaining_bytes_);

  // If we should copy zero bytes because |remaining_bytes_| is zero, short
  // circuit here.
  if (!dest_size) {
    *bytes_read = 0;
    return true;
  }

  int rv = stream_.Read(dest->data(), dest_size, &io_callback_);
  if (rv >= 0) {
    // Data is immediately available.
    *bytes_read = rv;
    remaining_bytes_ -= rv;
    DCHECK_GE(remaining_bytes_, 0);
    return true;
  }

  // Otherwise, a read error occured.  We may just need to wait...
  if (rv == ERR_IO_PENDING) {
    SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
  } else {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
  }
  return false;
}

bool URLRequestFileJob::IsRedirectResponse(GURL* location,
                                           int* http_status_code) {
  if (is_directory_) {
    // This happens when we discovered the file is a directory, so needs a
    // slash at the end of the path.
    std::string new_path = request_->url().path();
    new_path.push_back('/');
    GURL::Replacements replacements;
    replacements.SetPathStr(new_path);

    *location = request_->url().ReplaceComponents(replacements);
    *http_status_code = 301;  // simulate a permanent redirect
    return true;
  }

#if defined(OS_WIN)
  // Follow a Windows shortcut.
  // We just resolve .lnk file, ignore others.
  if (!LowerCaseEqualsASCII(file_path_.Extension(), ".lnk"))
    return false;

  FilePath new_path = file_path_;
  bool resolved;
  resolved = file_util::ResolveShortcut(&new_path);

  // If shortcut is not resolved succesfully, do not redirect.
  if (!resolved)
    return false;

  *location = FilePathToFileURL(new_path);
  *http_status_code = 301;
  return true;
#else
  return false;
#endif
}

Filter* URLRequestFileJob::SetupFilter() const {
  // Bug 9936 - .svgz files needs to be decompressed.
  return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")
      ? Filter::GZipFactory() : NULL;
}

bool URLRequestFileJob::GetMimeType(std::string* mime_type) const {
  // URL requests should not block on the disk!  On Windows this goes to the
  // registry.
  //   http://code.google.com/p/chromium/issues/detail?id=59849
  base::ThreadRestrictions::ScopedAllowIO allow_io;
  DCHECK(request_);
  return GetMimeTypeFromFile(file_path_, mime_type);
}

void URLRequestFileJob::SetExtraRequestHeaders(
    const HttpRequestHeaders& headers) {
  std::string range_header;
  if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) {
    // We only care about "Range" header here.
    std::vector<HttpByteRange> ranges;
    if (HttpUtil::ParseRangeHeader(range_header, &ranges)) {
      if (ranges.size() == 1) {
        byte_range_ = ranges[0];
      } else {
        // We don't support multiple range requests in one single URL request,
        // because we need to do multipart encoding here.
        // TODO(hclam): decide whether we want to support multiple range
        // requests.
        NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
                                    ERR_REQUEST_RANGE_NOT_SATISFIABLE));
      }
    }
  }
}

URLRequestFileJob::~URLRequestFileJob() {
#if defined(OS_WIN)
  DCHECK(!async_resolver_);
#endif
}

void URLRequestFileJob::DidResolve(
    bool exists, const base::PlatformFileInfo& file_info) {
#if defined(OS_WIN)
  async_resolver_ = NULL;
#endif

  // We may have been orphaned...
  if (!request_)
    return;

  is_directory_ = file_info.is_directory;

  int rv = OK;
  // We use URLRequestFileJob to handle files as well as directories without
  // trailing slash.
  // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
  // we will append trailing slash and redirect to FileDirJob.
  // A special case is "\" on Windows. We should resolve as invalid.
  // However, Windows resolves "\" to "C:\", thus reports it as existent.
  // So what happens is we append it with trailing slash and redirect it to
  // FileDirJob where it is resolved as invalid.
  if (!exists) {
    rv = ERR_FILE_NOT_FOUND;
  } else if (!is_directory_) {
    // URL requests should not block on the disk!
    //   http://code.google.com/p/chromium/issues/detail?id=59849
    base::ThreadRestrictions::ScopedAllowIO allow_io;

    int flags = base::PLATFORM_FILE_OPEN |
                base::PLATFORM_FILE_READ |
                base::PLATFORM_FILE_ASYNC;
    rv = stream_.Open(file_path_, flags);
  }

  if (rv != OK) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
    return;
  }

  if (!byte_range_.ComputeBounds(file_info.size)) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
               ERR_REQUEST_RANGE_NOT_SATISFIABLE));
    return;
  }

  remaining_bytes_ = byte_range_.last_byte_position() -
                     byte_range_.first_byte_position() + 1;
  DCHECK_GE(remaining_bytes_, 0);

  // URL requests should not block on the disk!
  //   http://code.google.com/p/chromium/issues/detail?id=59849
  {
    base::ThreadRestrictions::ScopedAllowIO allow_io;
    // Do the seek at the beginning of the request.
    if (remaining_bytes_ > 0 &&
        byte_range_.first_byte_position() != 0 &&
        byte_range_.first_byte_position() !=
        stream_.Seek(FROM_BEGIN, byte_range_.first_byte_position())) {
      NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
                                  ERR_REQUEST_RANGE_NOT_SATISFIABLE));
      return;
    }
  }

  set_expected_content_size(remaining_bytes_);
  NotifyHeadersComplete();
}

void URLRequestFileJob::DidRead(int result) {
  if (result > 0) {
    SetStatus(URLRequestStatus());  // Clear the IO_PENDING status
  } else if (result == 0) {
    NotifyDone(URLRequestStatus());
  } else {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
  }

  remaining_bytes_ -= result;
  DCHECK_GE(remaining_bytes_, 0);

  NotifyReadComplete(result);
}

}  // namespace net
<|endoftext|>


<commit_before>// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

//https://github.com/onnx/onnx/blob/master/docs/Operators.md#Scatter
#include "core/common/common.h"
#include "core/framework/op_kernel.h"
#include "core/providers/common.h"

namespace onnxruntime {

class Scatter final : public OpKernel {
 public:
  Scatter(const OpKernelInfo& info) : OpKernel(info) {
    ORT_ENFORCE(info.GetAttr<int64_t>("axis", &axis_).IsOK(),
                "Missing/Invalid 'axis' attribute value");
  }
  ~Scatter() = default;
  Status Compute(OpKernelContext* context) const override;

 private:
  int64_t axis_;
};

ONNX_CPU_OPERATOR_KERNEL(
    Scatter,
    9,
    KernelDefBuilder()
        .MayInplace(0, 0)
        .TypeConstraint("T", DataTypeImpl::AllTensorTypes())
        .TypeConstraint("Tind", std::vector<MLDataType>{DataTypeImpl::GetTensorType<int32_t>(), DataTypeImpl::GetTensorType<int64_t>()}),
    Scatter);

template <class Tin>
Status CopyScatterData(const Tensor* data_input, const Tensor* indices_input, const Tensor* updates_input,
                       const int64_t axis, Tensor* data_output) {
  const TensorShape& input_data_shape = data_input->Shape();
  const Tin* indices_data = indices_input->template Data<Tin>();
  const auto num_indices = indices_input->Shape().Size();
  for (int64_t i = 0; i < num_indices; ++i) {
    Tin idx = indices_data[i];
    if (idx < 0 || idx >= input_data_shape[axis]) {
      return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "indices element out of data bounds, idx=", idx,
                             " data_dim=", input_data_shape[axis]);
    }
  }

  const auto input_elements = input_data_shape.Size();
  const auto element_bytes = data_input->DataType()->Size();
  const auto total_input_bytes = data_input->Size();

  const uint8_t* src_base = reinterpret_cast<const uint8_t*>(data_input->DataRaw());
  uint8_t* dst_base = reinterpret_cast<uint8_t*>(data_output->MutableDataRaw());
  const bool is_string_type = data_input->DataType() == DataTypeImpl::GetType<std::string>();

  // We allow runtime to re-use input for output. If input/output Tensor* are the same
  // we do not copy
  if (src_base != dst_base) {
    if (is_string_type) {
      const std::string* str_begin = data_input->template Data<std::string>();
      const std::string* str_end = str_begin + input_elements;
      std::string* dst = data_output->template MutableData<std::string>();
      std::copy(str_begin, str_end, dst);
    } else {
      memcpy(dst_base, src_base, total_input_bytes);
    }
  }

  // Now poke updates

  const auto& upd_shape = updates_input->Shape();
  const auto num_dims = input_data_shape.NumDimensions();
  assert(num_dims > 0);

  // Allocate and zero out counts. The input/output is of the same rank as
  // indices/updates but the actual dimensions of indices/updates must be less or equal
  // than that of input/output because we can update no more elements than
  // the input contains. As we walk through the indices/updates
  // we maintain dimension count as we will need to use it
  // to compute output offset but using input/output dim values.
  // We treat the whole array as a number where each element having
  // different cardinality according to the upd_shape dimensions.
  // As each counter reaches its max (upd_shape) it resets to zero
  // and we carry to the more significant dim (right to left)
  std::vector<int64_t> dim_counters(num_dims);

  // This vector contains number of elements under the dimension.
  // For example, for the dimensions of [4, 2, 3] the vector
  // would contain [6, 3, 1] since for each count of dim 1 it
  // contains 3 elements of dim 2.
  // For each count of dim 0 we would have 2x3=6 elements.
  // The last value is always 1.
  // We use it to compute output element offset. For a given value of
  // counters we multiple each counter value per corresponding entry of dim_block_size value
  // and add up resulting the output element offset. However, for dimensions
  // that are equal to the specified axis value we take indices_data[index]
  // instead of the counter value.
  // E.g. for 3-dim and axis=0
  //    output[indices[i][j][k]][j][k] = updates[i][j][k]
  // for axis 1
  //    output[i][indices[i][j][k]][k] = updates[i][j][k]
  // and so on
  std::vector<int64_t> dim_block_size(num_dims);

  dim_block_size.back() = 1;
  if (num_dims > 1) {
    // We start at num_dims - 2 because we already pre-populated
    // the last element above
    for (int64_t i = int64_t(num_dims - 2); i >= 0; --i) {
      dim_block_size[i] = input_data_shape[i + 1] * dim_block_size[i + 1];
    }
  }

  const uint8_t* update_data = reinterpret_cast<const uint8_t*>(updates_input->DataRaw());
  // For every update we compute the destination offset and copy it there
  for (int64_t index = 0; index < num_indices;) {
    const Tin axis_idx = indices_data[index];

    // Compute the offset
    // See comments above for dim_block_size
    size_t dst_offset = 0;
    for (size_t i = 0; i < num_dims; ++i) {
      if (i == size_t(axis)) {
        // replace the counter with the update index for this dim
        dst_offset += axis_idx * dim_block_size[i];
      } else {
        dst_offset += dim_counters[i] * dim_block_size[i];
      }
    }

    const size_t dst_offset_bytes = dst_offset * element_bytes;
    assert(dst_offset_bytes < total_input_bytes);
    if (is_string_type) {
      reinterpret_cast<std::string*>(dst_base)[dst_offset] =
          reinterpret_cast<const std::string*>(update_data)[index];
    } else {
      // Copy an element
      auto src_offset_bytes = index * element_bytes;
      memcpy(dst_base + dst_offset_bytes, update_data + src_offset_bytes, element_bytes);
    }

    if (++index == num_indices) {
      break;
    }
    // Increment counters
    // See comments for dim_counters above
    for (int64_t i = int64_t(num_dims - 1); i >= 0; --i) {
      auto v = ++dim_counters[i];
      assert(v <= upd_shape[i]);
      if (v < upd_shape[i]) {
        // No carry, done
        break;
      }
      // No carry for the most significant dim
      assert(i > 0);
      dim_counters[i] = 0;
    }
  }
  return Status::OK();
}

Status Scatter::Compute(OpKernelContext* context) const {
  const auto* data_input = context->Input<Tensor>(0);
  const auto& input_data_shape = data_input->Shape();
  const auto axis = HandleNegativeAxis(axis_, input_data_shape.NumDimensions());

  const auto* indices_input = context->Input<Tensor>(1);
  const auto* updates_input = context->Input<Tensor>(2);

  if (data_input->DataType() != updates_input->DataType()) {
    return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "data type is different from updates type");
  }

  auto& indices_dims = indices_input->Shape().GetDims();
  auto& updates_dims = updates_input->Shape().GetDims();
  if (indices_dims.size() != updates_dims.size()) {
    return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
                           "Indices and updates must have the same rank");
  }

  for (size_t i = 0; i < indices_dims.size(); ++i) {
    if (indices_dims[i] != updates_dims[i]) {
      return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Indices vs updates dimensions differs at position=", i,
                             " ", indices_dims[i], " vs ", updates_dims[i]);
    }
  }

  // According to the spec the rank of ind/upd shall be the same as input(data)
  // and we also want to make sure that the dimensions of the of the ind/upd do not
  // exceed that of the input
  auto& input_dims = input_data_shape.GetDims();
  if (input_dims.size() != indices_dims.size()) {
    return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Indices must have the same rank as Input. Indices rank=",
                           indices_dims.size(), ". Input rank=", input_dims.size());
  }

  for (size_t i = 0; i < input_dims.size(); ++i) {
    if (input_dims[i] < indices_dims[i]) {
      return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Indices dim=", indices_dims[i], " at pos=", i,
                             " is greater than input dim=", input_dims[i]);
    }
  }

  auto* data_output = context->Output(0, input_data_shape);

  MLDataType Tind_type = indices_input->DataType();
  if (Tind_type == DataTypeImpl::GetType<int32_t>()) {
    return CopyScatterData<int32_t>(data_input, indices_input, updates_input, axis, data_output);
  } else if (Tind_type == DataTypeImpl::GetType<int64_t>()) {
    return CopyScatterData<int64_t>(data_input, indices_input, updates_input, axis, data_output);
  }
  return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Expecting indices to be either int32_t or int64_t");
}

}  // namespace onnxruntime
<commit_msg>Improve the performance of Scatter (#991)<commit_after>// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

//https://github.com/onnx/onnx/blob/master/docs/Operators.md#Scatter
#include "core/common/common.h"
#include "core/framework/op_kernel.h"
#include "core/providers/common.h"

namespace onnxruntime {

class Scatter final : public OpKernel {
 public:
  Scatter(const OpKernelInfo& info) : OpKernel(info) {
    ORT_ENFORCE(info.GetAttr<int64_t>("axis", &axis_).IsOK(),
                "Missing/Invalid 'axis' attribute value");
  }
  ~Scatter() = default;
  Status Compute(OpKernelContext* context) const override;

 private:
  int64_t axis_;
};

ONNX_CPU_OPERATOR_KERNEL(
    Scatter,
    9,
    KernelDefBuilder()
        .MayInplace(0, 0)
        .TypeConstraint("T", DataTypeImpl::AllTensorTypes())
        .TypeConstraint("Tind", std::vector<MLDataType>{DataTypeImpl::GetTensorType<int32_t>(), DataTypeImpl::GetTensorType<int64_t>()}),
    Scatter);

template <class Tin, class Tdata>
Status CopyScatterData(const Tensor* data_input, const Tensor* indices_input, const Tensor* updates_input,
                       const int64_t axis, Tensor* data_output) {
  const TensorShape& input_data_shape = data_input->Shape();
  const Tin* indices_data = indices_input->template Data<Tin>();
  const auto num_indices = indices_input->Shape().Size();
  for (int64_t i = 0; i < num_indices; ++i) {
    Tin idx = indices_data[i];
    if (idx < 0 || idx >= input_data_shape[axis]) {
      return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "indices element out of data bounds, idx=", idx,
                             " data_dim=", input_data_shape[axis]);
    }
  }

  const auto input_elements = input_data_shape.Size();
  const auto total_input_bytes = data_input->Size();

  const Tdata* src_base = static_cast<const Tdata*>(data_input->DataRaw());
  Tdata* dst_base = static_cast<Tdata*>(data_output->MutableDataRaw());
  bool is_string_type = data_input->DataType() == DataTypeImpl::GetType<std::string>();

  // We allow runtime to re-use input for output. If input/output Tensor* are the same
  // we do not copy
  if (src_base != dst_base) {
    if (is_string_type) {
      const std::string* str_begin = data_input->template Data<std::string>();
      const std::string* str_end = str_begin + input_elements;
      std::string* dst = data_output->template MutableData<std::string>();
      std::copy(str_begin, str_end, dst);
    } else {
      memcpy(dst_base, src_base, total_input_bytes);
    }
  }

  // Now poke updates

  const auto& upd_shape = updates_input->Shape();
  const auto num_dims = input_data_shape.NumDimensions();
  assert(num_dims > 0);

  // Allocate and zero out counts. The input/output is of the same rank as
  // indices/updates but the actual dimensions of indices/updates must be less or equal
  // than that of input/output because we can update no more elements than
  // the input contains. As we walk through the indices/updates
  // we maintain dimension count as we will need to use it
  // to compute output offset but using input/output dim values.
  // We treat the whole array as a number where each element having
  // different cardinality according to the upd_shape dimensions.
  // As each counter reaches its max (upd_shape) it resets to zero
  // and we carry to the more significant dim (right to left)
  std::vector<int64_t> dim_counters(num_dims);

  // This vector contains number of elements under the dimension.
  // For example, for the dimensions of [4, 2, 3] the vector
  // would contain [6, 3, 1] since for each count of dim 1 it
  // contains 3 elements of dim 2.
  // For each count of dim 0 we would have 2x3=6 elements.
  // The last value is always 1.
  // We use it to compute output element offset. For a given value of
  // counters we multiple each counter value per corresponding entry of dim_block_size value
  // and add up resulting the output element offset. However, for dimensions
  // that are equal to the specified axis value we take indices_data[index]
  // instead of the counter value.
  // E.g. for 3-dim and axis=0
  //    output[indices[i][j][k]][j][k] = updates[i][j][k]
  // for axis 1
  //    output[i][indices[i][j][k]][k] = updates[i][j][k]
  // and so on
  std::vector<int64_t> dim_block_size(num_dims);

  dim_block_size.back() = 1;
  if (num_dims > 1) {
    // We start at num_dims - 2 because we already pre-populated
    // the last element above
    for (int64_t i = int64_t(num_dims - 2); i >= 0; --i) {
      dim_block_size[i] = input_data_shape[i + 1] * dim_block_size[i + 1];
    }
  }

  const Tdata* update_data = static_cast<const Tdata*>(updates_input->DataRaw());
  // For every update we compute the destination offset and copy it there
  for (int64_t index = 0; index < num_indices;) {
    const Tin axis_idx = indices_data[index];

    // Compute the offset
    // See comments above for dim_block_size
    size_t dst_offset = 0;
    for (size_t i = 0; i < num_dims; ++i) {
      if (i == size_t(axis)) {
        // replace the counter with the update index for this dim
        dst_offset += axis_idx * dim_block_size[i];
      } else {
        dst_offset += dim_counters[i] * dim_block_size[i];
      }
    }

    dst_base[dst_offset] = update_data[index];

    if (++index == num_indices) {
      break;
    }
    // Increment counters
    // See comments for dim_counters above
    for (int64_t i = int64_t(num_dims - 1); i >= 0; --i) {
      auto v = ++dim_counters[i];
      assert(v <= upd_shape[i]);
      if (v < upd_shape[i]) {
        // No carry, done
        break;
      }
      // No carry for the most significant dim
      assert(i > 0);
      dim_counters[i] = 0;
    }
  }
  return Status::OK();
}

#define DispatchOnIndexTypeAndTensorType(index_type, tensor_type, retval, function, ...) \
  if (tensor_type == DataTypeImpl::GetType<float>())                                     \
    retval = function<index_type, float>(__VA_ARGS__);                                   \
  else if (tensor_type == DataTypeImpl::GetType<double>())                               \
    retval = function<index_type, double>(__VA_ARGS__);                                  \
  else if (tensor_type == DataTypeImpl::GetType<int8_t>())                               \
    retval = function<index_type, int8_t>(__VA_ARGS__);                                  \
  else if (tensor_type == DataTypeImpl::GetType<int16_t>())                              \
    retval = function<index_type, int16_t>(__VA_ARGS__);                                 \
  else if (tensor_type == DataTypeImpl::GetType<int32_t>())                              \
    retval = function<index_type, int32_t>(__VA_ARGS__);                                 \
  else if (tensor_type == DataTypeImpl::GetType<int64_t>())                              \
    retval = function<index_type, int64_t>(__VA_ARGS__);                                 \
  else if (tensor_type == DataTypeImpl::GetType<uint8_t>())                              \
    retval = function<index_type, uint8_t>(__VA_ARGS__);                                 \
  else if (tensor_type == DataTypeImpl::GetType<uint16_t>())                             \
    retval = function<index_type, uint16_t>(__VA_ARGS__);                                \
  else if (tensor_type == DataTypeImpl::GetType<uint32_t>())                             \
    retval = function<index_type, uint32_t>(__VA_ARGS__);                                \
  else if (tensor_type == DataTypeImpl::GetType<uint64_t>())                             \
    retval = function<index_type, uint64_t>(__VA_ARGS__);                                \
  else if (tensor_type == DataTypeImpl::GetType<bool>())                                 \
    retval = function<index_type, bool>(__VA_ARGS__);                                    \
  else if (tensor_type == DataTypeImpl::GetType<MLFloat16>())                            \
    retval = function<index_type, MLFloat16>(__VA_ARGS__);                               \
  else if (tensor_type == DataTypeImpl::GetType<BFloat16>())                             \
    retval = function<index_type, BFloat16>(__VA_ARGS__);                                \
  else if (tensor_type == DataTypeImpl::GetType<std::string>())                          \
    retval = function<index_type, std::string>(__VA_ARGS__);                             \
  else                                                                                \
    ORT_ENFORCE(false, "Unknown tensor type of ", tensor_type)

Status Scatter::Compute(OpKernelContext* context) const {
  const auto* data_input = context->Input<Tensor>(0);
  const auto& input_data_shape = data_input->Shape();
  const auto axis = HandleNegativeAxis(axis_, input_data_shape.NumDimensions());

  const auto* indices_input = context->Input<Tensor>(1);
  const auto* updates_input = context->Input<Tensor>(2);

  if (data_input->DataType() != updates_input->DataType()) {
    return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "data type is different from updates type");
  }

  auto& indices_dims = indices_input->Shape().GetDims();
  auto& updates_dims = updates_input->Shape().GetDims();
  if (indices_dims.size() != updates_dims.size()) {
    return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
                           "Indices and updates must have the same rank");
  }

  for (size_t i = 0; i < indices_dims.size(); ++i) {
    if (indices_dims[i] != updates_dims[i]) {
      return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Indices vs updates dimensions differs at position=", i,
                             " ", indices_dims[i], " vs ", updates_dims[i]);
    }
  }

  // According to the spec the rank of ind/upd shall be the same as input(data)
  // and we also want to make sure that the dimensions of the of the ind/upd do not
  // exceed that of the input
  auto& input_dims = input_data_shape.GetDims();
  if (input_dims.size() != indices_dims.size()) {
    return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Indices must have the same rank as Input. Indices rank=",
                           indices_dims.size(), ". Input rank=", input_dims.size());
  }

  for (size_t i = 0; i < input_dims.size(); ++i) {
    if (input_dims[i] < indices_dims[i]) {
      return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Indices dim=", indices_dims[i], " at pos=", i,
                             " is greater than input dim=", input_dims[i]);
    }
  }

  auto* data_output = context->Output(0, input_data_shape);

  MLDataType Tind_type = indices_input->DataType();
  MLDataType Tdata_type = data_input->DataType();
  Status status;
  if (Tind_type == DataTypeImpl::GetType<int32_t>()) {
    DispatchOnIndexTypeAndTensorType(int32_t, Tdata_type, status, CopyScatterData, data_input, indices_input, updates_input, axis, data_output);
  } else if (Tind_type == DataTypeImpl::GetType<int64_t>()) {
    DispatchOnIndexTypeAndTensorType(int64_t, Tdata_type, status, CopyScatterData, data_input, indices_input, updates_input, axis, data_output);
  } else {
    return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Expecting indices to be either int32_t or int64_t");
  }
  return status;
}

}  // namespace onnxruntime
<|endoftext|>


<commit_before>#include <tiramisu/tiramisu.h>
#include "configure.h"

using namespace tiramisu;

expr mixf(expr x, expr y, expr a)
{
    return x + (y - x) * a;
}

int main()
{
    init("resize_conv_block");

    // -------------------------------------------------------
    // Layer I
    // -------------------------------------------------------
    var o_x("o_x", 0, IMG_WIDTH), o_y("o_y", 0, IMG_HEIGHT), fin("fin", 0, FIn), n("n", 0, BATCH_SIZE);
    var x("x", 0, N), y("y", 0, N);

    var x_pad("x_pad", 0, N + 2), y_pad("y_pad", 0, N + 2);
    var k_x("k_x", 0, K_X), k_y("k_y", 0, K_Y), fout("fout", 0, FOut);

    var fout_b("fout_b", 0, FOUT_NB_BLOCKS), ffout("ffout", 0, FOUT_BLOCKING);

    input c_input("c_input", {n, o_y, o_x, fin}, p_float32);
    input conv_filter("conv_filter", {fout_b, k_y, k_x, fin, ffout}, p_float32);
    input conv_bias("conv_bias", {fout_b, ffout}, p_float32);

    // Resize computation
    expr o_r((cast(p_float32, y_pad) + 0.5f) * (cast(p_float32, IMG_HEIGHT) / cast(p_float32, N + 2)) - 0.5f);
    expr o_c((cast(p_float32, x_pad) + 0.5f) * (cast(p_float32, IMG_WIDTH) / cast(p_float32, N + 2)) - 0.5f);

    expr r_coeff(expr(o_r) - expr(o_floor, o_r));
    expr c_coeff(expr(o_c) - expr(o_floor, o_c));

    expr A00_r(cast(p_int32, expr(o_floor, o_r)));
    expr A00_c(cast(p_int32, expr(o_floor, o_c)));

    computation resize(
        "resize",
        {n, y_pad, x_pad, fin},
        mixf(
            mixf(
                c_input(n, A00_r, A00_c, fin),
                c_input(n, A00_r + 1, A00_c, fin),
                r_coeff
            ),

            mixf(
                c_input(n, A00_r, A00_c + 1, fin),
                c_input(n, A00_r + 1, A00_c + 1, fin),
                r_coeff
            ),

            c_coeff
        )
    );

    // Convolution computation
    computation init_output("init_output", {n, fout_b, y, x, ffout}, conv_bias(fout_b, ffout));
    computation conv(
        "conv",
        {n, fout_b, y, x, k_y, k_x, fin, ffout},
        init_output(n, fout_b, y, x, ffout) + conv_filter(fout_b, k_y, k_x, fin, ffout)*resize(n, y + k_y, x + k_x, fin)
    );

    // -------------------------------------------------------
    // Layer II
    // -------------------------------------------------------
    var y_b("y_b", 0, Y_NB_BLOCKS), x_b("x_b", 0, X_NB_BLOCKS);

    // Loop through weights to load them into cache
    computation prefetch_weights(
        "prefetch_weights",
        {n, fout_b, y_b, x_b, k_y, k_x, fin, ffout},
        conv_filter(fout_b, k_y, k_x, fin, ffout),
        SCHEDULE_PREFETCH_WEIGHTS
    );

    if (N >= 224) {
        var xx, yy;

        init_output.tile(y, x, Y_BLOCKING, X_BLOCKING);
        conv.tile(y, x, Y_BLOCKING, X_BLOCKING, y_b, x_b, yy, xx);

        // n, fout_b, y_b, x_b, yy, xx, k_y, k_x, fin, ffout
        conv.interchange(xx, k_y);
        conv.interchange(xx, k_x);
        // n, fout_b, y_b, x_b, yy, k_y, k_x, xx, fin, ffout
        conv.interchange(yy, k_y);
        conv.interchange(yy, k_x);
        // n, fout_b, y_b, x_b, k_y, k_x, yy, xx, fin, ffout

        resize.then(init_output, n)
              .then(prefetch_weights, x_b)
              .then(conv, x_b);
    }

    else {
        // n, fout_b, y, x, k_y, k_x, fin, ffout
        conv.interchange(x, k_y);

        var xx;
        conv.split(x, X_BLOCKING, x_b, xx);
        conv.interchange(xx, k_x);
        // n, fout_b, y, k_y, x_b, k_x, xx, fin, ffout

        resize.then(init_output, n)
              .then(conv, y);
    }

    conv.tag_parallel_level(n);

    init_output.vectorize(ffout, FOUT_BLOCKING);
    conv.vectorize(ffout, FOUT_BLOCKING);

    // -------------------------------------------------------
    // Layer III
    // -------------------------------------------------------
    buffer resized_buf("input_resized_buf", {BATCH_SIZE, N + 2, N + 2, FIn}, p_float32, a_input);
    buffer output_buf("output_buf", {BATCH_SIZE, FOUT_NB_BLOCKS, N, N, FOUT_BLOCKING}, p_float32, a_output);

    buffer prefetch_w_buf("prefetch_w_buf", {1}, p_float32, a_temporary);
    if (SCHEDULE_PREFETCH_WEIGHTS)
        prefetch_weights.store_in(&prefetch_w_buf, {});

    resize.store_in(&resized_buf, {n, y_pad, x_pad, fin});

    init_output.store_in(&output_buf);
    conv.store_in(&output_buf, {n, fout_b, y, x, ffout});

    // -------------------------------------------------------
    // Code Generation
    // -------------------------------------------------------
    codegen({
        c_input.get_buffer(),
        conv_filter.get_buffer(),
        conv_bias.get_buffer(),
        &resized_buf,
        &output_buf
    }, "resize_conv_tiramisu.o");

    return 0;
}
<commit_msg>Resize-Conv : fuse resize with conv<commit_after>#include <tiramisu/tiramisu.h>
#include "configure.h"

using namespace tiramisu;

expr mixf(expr x, expr y, expr a)
{
    return x + (y - x) * a;
}

int main()
{
    init("resize_conv_block");

    // -------------------------------------------------------
    // Layer I
    // -------------------------------------------------------
    var o_x("o_x", 0, IMG_WIDTH), o_y("o_y", 0, IMG_HEIGHT), fin("fin", 0, FIn), n("n", 0, BATCH_SIZE);
    var x("x", 0, N), y("y", 0, N);

    var x_pad("x_pad", 0, N + 2), y_pad("y_pad", 0, N + 2);
    var k_x("k_x", 0, K_X), k_y("k_y", 0, K_Y), fout("fout", 0, FOut);

    var fout_b("fout_b", 0, FOUT_NB_BLOCKS), ffout("ffout", 0, FOUT_BLOCKING);

    input c_input("c_input", {n, o_y, o_x, fin}, p_float32);
    input conv_filter("conv_filter", {fout_b, k_y, k_x, fin, ffout}, p_float32);
    input conv_bias("conv_bias", {fout_b, ffout}, p_float32);

    // Resize computation
    // Compute for y = 2, ..., N + 1
    expr o_r((cast(p_float32, y + 2) + 0.5f) * (cast(p_float32, IMG_HEIGHT) / cast(p_float32, N + 2)) - 0.5f);
    expr o_c((cast(p_float32, x_pad) + 0.5f) * (cast(p_float32, IMG_WIDTH) / cast(p_float32, N + 2)) - 0.5f);

    expr r_coeff(expr(o_r) - expr(o_floor, o_r));
    expr c_coeff(expr(o_c) - expr(o_floor, o_c));

    expr A00_r(cast(p_int32, expr(o_floor, o_r)));
    expr A00_c(cast(p_int32, expr(o_floor, o_c)));

    computation resize(
        "resize",
        {n, y, x_pad, fin},
        mixf(
            mixf(
                c_input(n, A00_r, A00_c, fin),
                c_input(n, A00_r + 1, A00_c, fin),
                r_coeff
            ),

            mixf(
                c_input(n, A00_r, A00_c + 1, fin),
                c_input(n, A00_r + 1, A00_c + 1, fin),
                r_coeff
            ),

            c_coeff
        )
    );

    // Start to compute resize for y = 0, 1 to fuse resize with convolution
    var y_prelude("y_prelude", 0, 2);

    expr o_r_prelude((cast(p_float32, y_prelude) + 0.5f) * (cast(p_float32, IMG_HEIGHT) / cast(p_float32, N + 2)) - 0.5f);
    expr r_coeff_prelude(expr(o_r_prelude) - expr(o_floor, o_r_prelude));
    expr A00_r_prelude(cast(p_int32, expr(o_floor, o_r_prelude)));

    computation resize_prelude(
        "resize_prelude",
        {n, y_prelude, x_pad, fin},
        mixf(
            mixf(
                c_input(n, A00_r_prelude, A00_c, fin),
                c_input(n, A00_r_prelude + 1, A00_c, fin),
                r_coeff_prelude
            ),

            mixf(
                c_input(n, A00_r_prelude, A00_c + 1, fin),
                c_input(n, A00_r_prelude + 1, A00_c + 1, fin),
                r_coeff_prelude
            ),

            c_coeff
        )
    );

    // Convolution computation
    view resized_view("resized_view", {n, y_pad, x_pad, fin}, p_float32);
    computation init_output("init_output", {n, fout_b, y, x, ffout}, conv_bias(fout_b, ffout));
    computation conv(
        "conv",
        {n, fout_b, y, x, k_y, k_x, fin, ffout},
        init_output(n, fout_b, y, x, ffout) + conv_filter(fout_b, k_y, k_x, fin, ffout)*resized_view(n, y + k_y, x + k_x, fin)
    );

    // -------------------------------------------------------
    // Layer II
    // -------------------------------------------------------
    var y_b("y_b", 0, Y_NB_BLOCKS), x_b("x_b", 0, X_NB_BLOCKS);

    // Loop through weights to load them into cache
    computation prefetch_weights(
        "prefetch_weights",
        {n, fout_b, y_b, x_b, k_y, k_x, fin, ffout},
        conv_filter(fout_b, k_y, k_x, fin, ffout),
        SCHEDULE_PREFETCH_WEIGHTS
    );

    if (N >= 224) {
        var xx, yy;

        resize.split(y, Y_BLOCKING, y_b, yy);
        init_output.tile(y, x, Y_BLOCKING, X_BLOCKING, y_b, x_b, yy, xx);
        conv.tile(y, x, Y_BLOCKING, X_BLOCKING, y_b, x_b, yy, xx);

        // n, fout_b, y_b, x_b, yy, xx, k_y, k_x, fin, ffout
        conv.interchange(xx, k_y);
        conv.interchange(xx, k_x);
        // n, fout_b, y_b, x_b, yy, k_y, k_x, xx, fin, ffout
        conv.interchange(yy, k_y);
        conv.interchange(yy, k_x);
        // n, fout_b, y_b, x_b, k_y, k_x, yy, xx, fin, ffout

        // Interchange fout_b and y_b in order to fuse resize with convolution
        prefetch_weights.interchange(fout_b, y_b);
        init_output.interchange(fout_b, y_b);
        conv.interchange(fout_b, y_b);

        resize_prelude.then(resize, n)
                      .then(init_output, y_b)
                      .then(prefetch_weights, x_b)
                      .then(conv, x_b);
    }

    else {
        // n, fout_b, y, x, k_y, k_x, fin, ffout
        conv.interchange(x, k_y);

        var xx;
        conv.split(x, X_BLOCKING, x_b, xx);
        conv.interchange(xx, k_x);
        // n, fout_b, y, k_y, x_b, k_x, xx, fin, ffout

        // Interchange fout_b and y in order to fuse resize with convolution
        init_output.interchange(fout_b, y);
        conv.interchange(fout_b, y);

        resize_prelude.then(resize, n)
                      .then(init_output, y)
                      .then(conv, fout_b);
    }

    conv.tag_parallel_level(n);

    resize.tag_unroll_level(fin);
    resize.vectorize(x_pad, 8);

    init_output.vectorize(ffout, FOUT_BLOCKING);
    conv.vectorize(ffout, FOUT_BLOCKING);

    // -------------------------------------------------------
    // Layer III
    // -------------------------------------------------------
    buffer resized_buf("input_resized_buf", {BATCH_SIZE, N + 2, N + 2, FIn}, p_float32, a_input);
    buffer output_buf("output_buf", {BATCH_SIZE, FOUT_NB_BLOCKS, N, N, FOUT_BLOCKING}, p_float32, a_output);

    buffer prefetch_w_buf("prefetch_w_buf", {1}, p_float32, a_temporary);
    if (SCHEDULE_PREFETCH_WEIGHTS)
        prefetch_weights.store_in(&prefetch_w_buf, {});

    resize_prelude.store_in(&resized_buf, {n, y_prelude, x_pad, fin});
    resize.store_in(&resized_buf, {n, y + 2, x_pad, fin});
    resized_view.store_in(&resized_buf);

    init_output.store_in(&output_buf);
    conv.store_in(&output_buf, {n, fout_b, y, x, ffout});

    // -------------------------------------------------------
    // Code Generation
    // -------------------------------------------------------
    codegen({
        c_input.get_buffer(),
        conv_filter.get_buffer(),
        conv_bias.get_buffer(),
        &resized_buf,
        &output_buf
    }, "resize_conv_tiramisu.o");

    return 0;
}
<|endoftext|>


<commit_before>#include "Tilemap.hpp"
#include "TransformGroup.hpp" // GroupedDrawable
#include "sharedPtrConverter.hpp"
#include "container.hpp"
#include "MapInfo.hpp"

static char const libname[] = "Tilemap";
#include "ExportThis.hpp"
#include "Geometry.hpp"

static unsigned Tilemap_get(jd::Tilemap const& map, sf::Vector3i p)
{
    if (!map.isValidPosition(p))
        throw "invalid tile position (@get)";
    return map[static_cast<jd::Vector3u>(p)];
}

static void Tilemap_set(jd::Tilemap& map, sf::Vector3i p, unsigned tid)
{
    if (!map.isValidPosition(p))
        throw "invalid tile position (@set)";
    map.set(static_cast<jd::Vector3u>(p), tid);
}

static void init(LuaVm& vm)
{
    vm.initLib("SfGraphics");

    using namespace luabind;
    class_<PropertyMap> cPropertyMap("StringTable");
    exportAssocContainer<false>(cPropertyMap);

    class_<MapObjectGroup::Map> cObjectGroupMap("ObjectGroupTable");
    exportAssocContainer<true>(cObjectGroupMap);

    class_<std::vector<PropertyMap>> cPropertyMapVec("StringTableList");
    exportRandomAccessContainer<true>(cPropertyMapVec);

    class_<std::vector<sf::Vector2f>> cPointVec("PointList");
    exportRandomAccessContainer<false>(cPointVec);

    class_<std::vector<MapObject>> cMapObjectVec("ObjectList");
    exportRandomAccessContainer<true>(cMapObjectVec);

    LHMODULE [
        namespace_("mapInfo") [
            cPropertyMap,
            cObjectGroupMap,
            cPropertyMapVec,
            cMapObjectVec,
            cPointVec,

#           define LHCURCLASS MapInfo
            class_<LHCURCLASS>("Map")
                .def(constructor<LHCURCLASS const&>())
                .LHPROPRW(tileProperties)
                .LHPROPRW(layerProperties)
                .LHPROPRW(objectGroups),
#           undef LHCURCLASS

#           define LHCURCLASS MapObject
            class_<LHCURCLASS>("Object")
                .def(constructor<LHCURCLASS const&>())
                .LHPROPRW(name)
                .LHPROPRW(type)
                .LHPROPRW(position)
                .LHPROPRW(tileId)
                .LHPROPRW(objectType)
                .LHPROPRW(relativePoints)
                .LHPROPG(absolutePoints)
                .enum_("t")[
                    value("RECT", MapObject::rect),
                    value("TILE", MapObject::tile),
                    value("LINE", MapObject::line),
                    value("POLY", MapObject::poly)
                ]
                .LHPROPRW(properties),
#           undef LHCURCLASS

#           define LHCURCLASS MapObjectGroup
            class_<LHCURCLASS>("ObjectGroup")
                .def(constructor<LHCURCLASS const&>())
                .LHPROPRW(name)
                .LHPROPRW(objects)
                .LHPROPRW(properties)
#           undef LHCURCLASS

        ],

#   define LHCURCLASS jd::Tileset
        class_<LHCURCLASS>("Tileset")
            .def(constructor<
                sf::Vector2u,
                // HACK: For some reason, ConstPtr is not recognized by luabind:
                ResourceTraits<sf::Texture>::Ptr>())
            .def("texturePosition", &LHCURCLASS::position)
            .LHPROPG(size)
            .LHPROPG(texture),
#   undef LHCURCLASS
        class_<jd::Tilemap, bases<sf::Drawable, sf::Transformable>>("@Tilemap@"),
#   define LHCURCLASS GroupedDrawable<jd::Tilemap>
		class_<LHCURCLASS, bases<TransformGroup::AutoEntry, jd::Tilemap>>("Tilemap")
            .def(constructor<>())
            .def(constructor<TransformGroup&>())
            .def(constructor<LHCURCLASS const&>())
            .property("group", &LHCURCLASS::group, &LHCURCLASS::setGroup)
            .LHMEMFN(isValidPosition)
            .def("get", &Tilemap_get)
            .def("set", &Tilemap_set)
            .property("bounds", &LHCURCLASS::getGlobalBounds)
            .property("localBounds", &LHCURCLASS::getLocalBounds)
            .property("tileset", &LHCURCLASS::tileset, &LHCURCLASS::setTileset)
            .property("size", &LHCURCLASS::size, &LHCURCLASS::setSize)
            .LHMEMFN(localTilePos)
            .def("tilePos", &LHCURCLASS::globalTilePos)
            .LHMEMFN(tilePosFromLocal)
            .LHMEMFN(tilePosFromGlobal)
            .def("tileRect", &LHCURCLASS::globalTileRect)
            .LHMEMFN(localTileRect)
            .def("loadFromFile", &loadTilemap)
#   undef LHCURCLASS
    ];
}<commit_msg>Tilemap lexp: Export MapObject::size<commit_after>#include "Tilemap.hpp"
#include "TransformGroup.hpp" // GroupedDrawable
#include "sharedPtrConverter.hpp"
#include "container.hpp"
#include "MapInfo.hpp"

static char const libname[] = "Tilemap";
#include "ExportThis.hpp"
#include "Geometry.hpp"

static unsigned Tilemap_get(jd::Tilemap const& map, sf::Vector3i p)
{
    if (!map.isValidPosition(p))
        throw "invalid tile position (@get)";
    return map[static_cast<jd::Vector3u>(p)];
}

static void Tilemap_set(jd::Tilemap& map, sf::Vector3i p, unsigned tid)
{
    if (!map.isValidPosition(p))
        throw "invalid tile position (@set)";
    map.set(static_cast<jd::Vector3u>(p), tid);
}

static void init(LuaVm& vm)
{
    vm.initLib("SfGraphics");

    using namespace luabind;
    class_<PropertyMap> cPropertyMap("StringTable");
    exportAssocContainer<false>(cPropertyMap);

    class_<MapObjectGroup::Map> cObjectGroupMap("ObjectGroupTable");
    exportAssocContainer<true>(cObjectGroupMap);

    class_<std::vector<PropertyMap>> cPropertyMapVec("StringTableList");
    exportRandomAccessContainer<true>(cPropertyMapVec);

    class_<std::vector<sf::Vector2f>> cPointVec("PointList");
    exportRandomAccessContainer<false>(cPointVec);

    class_<std::vector<MapObject>> cMapObjectVec("ObjectList");
    exportRandomAccessContainer<true>(cMapObjectVec);

    LHMODULE [
        namespace_("mapInfo") [
            cPropertyMap,
            cObjectGroupMap,
            cPropertyMapVec,
            cMapObjectVec,
            cPointVec,

#           define LHCURCLASS MapInfo
            class_<LHCURCLASS>("Map")
                .def(constructor<LHCURCLASS const&>())
                .LHPROPRW(tileProperties)
                .LHPROPRW(layerProperties)
                .LHPROPRW(objectGroups),
#           undef LHCURCLASS

#           define LHCURCLASS MapObject
            class_<LHCURCLASS>("Object")
                .def(constructor<LHCURCLASS const&>())
                .LHPROPRW(name)
                .LHPROPRW(type)
                .LHPROPRW(position)
                .LHPROPRW(size)
                .LHPROPRW(tileId)
                .LHPROPRW(objectType)
                .LHPROPRW(relativePoints)
                .LHPROPG(absolutePoints)
                .enum_("t")[
                    value("RECT", MapObject::rect),
                    value("TILE", MapObject::tile),
                    value("LINE", MapObject::line),
                    value("POLY", MapObject::poly)
                ]
                .LHPROPRW(properties),
#           undef LHCURCLASS

#           define LHCURCLASS MapObjectGroup
            class_<LHCURCLASS>("ObjectGroup")
                .def(constructor<LHCURCLASS const&>())
                .LHPROPRW(name)
                .LHPROPRW(objects)
                .LHPROPRW(properties)
#           undef LHCURCLASS

        ],

#   define LHCURCLASS jd::Tileset
        class_<LHCURCLASS>("Tileset")
            .def(constructor<
                sf::Vector2u,
                // HACK: For some reason, ConstPtr is not recognized by luabind:
                ResourceTraits<sf::Texture>::Ptr>())
            .def("texturePosition", &LHCURCLASS::position)
            .LHPROPG(size)
            .LHPROPG(texture),
#   undef LHCURCLASS
        class_<jd::Tilemap, bases<sf::Drawable, sf::Transformable>>("@Tilemap@"),
#   define LHCURCLASS GroupedDrawable<jd::Tilemap>
		class_<LHCURCLASS, bases<TransformGroup::AutoEntry, jd::Tilemap>>("Tilemap")
            .def(constructor<>())
            .def(constructor<TransformGroup&>())
            .def(constructor<LHCURCLASS const&>())
            .property("group", &LHCURCLASS::group, &LHCURCLASS::setGroup)
            .LHMEMFN(isValidPosition)
            .def("get", &Tilemap_get)
            .def("set", &Tilemap_set)
            .property("bounds", &LHCURCLASS::getGlobalBounds)
            .property("localBounds", &LHCURCLASS::getLocalBounds)
            .property("tileset", &LHCURCLASS::tileset, &LHCURCLASS::setTileset)
            .property("size", &LHCURCLASS::size, &LHCURCLASS::setSize)
            .LHMEMFN(localTilePos)
            .def("tilePos", &LHCURCLASS::globalTilePos)
            .LHMEMFN(tilePosFromLocal)
            .LHMEMFN(tilePosFromGlobal)
            .def("tileRect", &LHCURCLASS::globalTileRect)
            .LHMEMFN(localTileRect)
            .def("loadFromFile", &loadTilemap)
#   undef LHCURCLASS
    ];
}<|endoftext|>


<commit_before>#include "ExeFactory.h"

#include "pe/DOSExe.h"
#include "pe/PEFile.h"

std::map<ExeFactory::exe_type, ExeBuilder*> ExeFactory::builders;

void ExeFactory::init()
{
    if (builders.size() > 0) {
        return; // already initialized
    }
    builders[MZ] = new DOSExeBuilder();
    builders[PE] = new PEFileBuilder();
}

void ExeFactory::destroy()
{
    std::map<exe_type, ExeBuilder*>::iterator itr;
    for (itr = builders.begin(); itr != builders.end(); itr++) {
        ExeBuilder* builder = itr->second;
        delete builder;
    }
    builders.clear();
}

ExeFactory::exe_type ExeFactory::findMatching(AbstractByteBuffer *buf)
{
    if (!buf) return NONE;

    std::map<exe_type, ExeBuilder*>::iterator itr;
    for (itr = builders.begin(); itr != builders.end(); itr++) {

        ExeBuilder* builder = itr->second;
        if (builder == NULL) continue;

        if (builder->signatureMatches(buf)) {
            return itr->first;
        }
    }
    return NONE;
}

Executable* ExeFactory::build(AbstractByteBuffer *buf, exe_type type)
{
    if (builders.find(type) == builders.end()) return NULL;

    ExeBuilder* builder = builders[type];
    if (builder == NULL) return NULL;

    return builder->build(buf);
}

QString ExeFactory::getTypeName(exe_type type)
{
    if (builders.find(type) == builders.end()) return "Not supported";

    ExeBuilder* builder = builders[type];
    if (builder == NULL) return "Not supported";

    return builder->typeName();
}
<commit_msg>[FEATURE] In ExeFactory: autoinit builders<commit_after>#include "ExeFactory.h"

#include "pe/DOSExe.h"
#include "pe/PEFile.h"

std::map<ExeFactory::exe_type, ExeBuilder*> ExeFactory::builders;

void ExeFactory::init()
{
    if (builders.size() > 0) {
        return; // already initialized
    }
    builders[MZ] = new DOSExeBuilder();
    builders[PE] = new PEFileBuilder();
}

void ExeFactory::destroy()
{
    std::map<exe_type, ExeBuilder*>::iterator itr;
    for (itr = builders.begin(); itr != builders.end(); itr++) {
        ExeBuilder* builder = itr->second;
        delete builder;
    }
    builders.clear();
}

ExeFactory::exe_type ExeFactory::findMatching(AbstractByteBuffer *buf)
{
    if (!buf) return NONE;

    ExeFactory::init(); //ensue that the builders are initialized

    std::map<exe_type, ExeBuilder*>::iterator itr;
    for (itr = builders.begin(); itr != builders.end(); itr++) {

        ExeBuilder* builder = itr->second;
        if (builder == NULL) continue;

        if (builder->signatureMatches(buf)) {
            return itr->first;
        }
    }
    return NONE;
}

Executable* ExeFactory::build(AbstractByteBuffer *buf, exe_type type)
{
    ExeFactory::init(); //ensue that the builders are initialized

    if (builders.find(type) == builders.end()) {
        return NULL;
    }
    ExeBuilder* builder = builders[type];
    if (!builder) return NULL;

    return builder->build(buf);
}

QString ExeFactory::getTypeName(exe_type type)
{
    ExeFactory::init(); //ensue that the builders are initialized

    if (builders.find(type) == builders.end()) return "Not supported";

    ExeBuilder* builder = builders[type];
    if (builder == NULL) return "Not supported";

    return builder->typeName();
}
<|endoftext|>


<commit_before>#include <iostream>

#include "scsi/config.h"
#include "scsi/base.h"

# define C0           2.99792458e8

// Good practice to enclose all local definitions in an anonymous namespace
// equivalent of static functions in plain C.
namespace {

//! [StateDef]
struct State1D : public StateBase
{
    double x,  // transverse position
           xv; // transverse veclocity
//! [StateDef]

    virtual void assign(const StateBase& other)
    {
        StateBase::assign(other);
        const State1D& ST = static_cast<const State1D&>(other);
        x = ST.x;
        xv= ST.xv;
    }

    virtual void show(std::ostream &strm) const
    {
        strm<<pos<<" ["<<x<<", "<<xv<<"]\n";
    }

    // allow introspection of state variable by eg. Python
    virtual bool getArray(unsigned idx, ArrayInfo& Info)
    {
        if(idx==0) {
            Info.name = "x";
            Info.ptr = &x;
            // remaining defaults ok for scalar double
        } else if(idx==1) {
            Info.name = "xv";
            Info.ptr = &xv;
        } else {
            return StateBase::getArray(idx-2, Info); // check w/ base class for any remaining
        }
        return true;
    }

    virtual StateBase* clone() const {
        return new State1D(*this, clone_tag());
    }

    virtual ~State1D() {}

    // initialize state from config
    //! [StateInit]
    State1D(const Config& c)
        :StateBase(c)
        ,x(c.get<double>("x", 0.0))   // m
        ,xv(c.get<double>("xv", 0.0)) // m/s
    {}
    //! [StateInit]
    State1D(const State1D& O, clone_tag t)
        :StateBase(O, t)
        ,x(O.x)
        ,xv(O.xv)
    {}
};

//! [ElemSrcDef]
struct Element1DSource : public ElementVoid
{
    State1D initial;
//! [ElemSrcDef]

//! [ElemSrcInit]
    Element1DSource(const Config& c)
        :ElementVoid(c)
        ,initial(c)
    {}
//! [ElemSrcInit]

//! [ElemSrcAdvance]
    virtual void advance(StateBase& s) const
    {
        s.assign(initial);
        s.pos += length; // source element ususaly has zero length, but not required
    }
//! [ElemSrcAdvance]

    virtual void assign(const ElementVoid* other )
    {
        ElementVoid::assign(other);
        const Element1DSource *O = static_cast<const Element1DSource*>(other);
        initial.assign(O->initial);
    }

    virtual const char* type_name() const { return "source"; }
};

//! [ElemGenericDef]
struct Element1DGeneric : public ElementVoid
{
    double xa, // transverse acceleration
           tt; // logitudinal transit time
//! [ElemGenericDef]

//! [ElemGenericInit]
    Element1DGeneric(const Config& c)
        :ElementVoid(c)
        ,xa(c.get<double>("A", 0.0)) // m/s2
    {
        // length will never change, so only need to compute transit time once
        tt = length/C0; // sec.
    }
//! [ElemGenericInit]

//! [ElemGenericAdvance]
    virtual void advance(StateBase& s) const
    {
        State1D &ST = static_cast<State1D&>(s); // safe since sim_type=Simple1D will only use State1D

        ST.pos += length;
        ST.xv  += xa*tt;
        ST.x   += xa*tt*tt;
    }
//! [ElemGenericAdvance]

    virtual void assign(const ElementVoid* other )
    {
        ElementVoid::assign(other);
        const Element1DGeneric *O = static_cast<const Element1DGeneric*>(other);
        xa = O->xa;
        tt = O->tt;
    }

    virtual const char* type_name() const { return "generic"; }
};

} // end anon namespace

//! [register]
void register1D()
{
    Machine::registerState<State1D>("Simple1D");

    Machine::registerElement<Element1DSource>("Simple1D", "source");
    Machine::registerElement<Element1DGeneric>("Simple1D", "generic");
}
//! [register]

//! [main]
static const char lattice[] = ""
"sim_type = \"Simple1D\";\n"
"src   : source, x = 1e-5, xv = 1e-6;\n"
"elem1 : generic, A = 1,  L = 10;\n"
"elem2 : generic, A = -2, L = 10;\n"
"example : LINE = (src, elem1, elem2);\n"
;

int main(int argc, char *argv[])
{
    try {
        register1D();

        std::auto_ptr<Config> conf;
        {
            GLPSParser parser;
            conf.reset(parser.parse_byte(lattice, sizeof(lattice)-1));
        }

        /* conf now contains (using python notation)
         * {"sim_type":"Simple1D",
         *  "elements": [
         *    {"type":"source",  "name":"src", "x":1e-5, "xv":1e-6},
         *    {"type":"generic", "name":"elem1", "A":1.0,  "L":10.0},
         *    {"type":"generic", "name":"elem2", "A":-2.0, "L":10.0},
         *  ],
         * }
         */

        Machine mymachine(*conf);
        /* During Machine construction
         * sim_type=Simple1D with type=source selects Element1DSource, which is constructed once
         *
         * sim_type=Simple1D with type=source selects Element1DGeneric, which is constructed twice.
         *  first with A=1, then a second time with A=-2
         *
         * mymachine now has 3 elements.  mymachine.size()==3
         */

        mymachine.set_trace(&std::cout); // print intermediates

        std::auto_ptr<StateBase> thestate(mymachine.allocState());

        // propagate through the source element to initialize the state based on
        // values of "x" and "xv" from the lattice
        mymachine.propagate(thestate.get(), 0, 1);

        std::cout<<"Source state "<<*thestate<<"\n";

        // propagate through remaining elements
        mymachine.propagate(thestate.get(), 1);

        std::cout<<"Final state "<<*thestate<<"\n";

        Machine::registeryCleanup();

        return 0;
    } catch(std::exception& e) {
        std::cerr<<"Error: "<<e.what()<<"\n";
        return 1;
    }
}
//! [main]
<commit_msg>update example<commit_after>#include <iostream>

#include "scsi/config.h"
#include "scsi/base.h"

# define C0           2.99792458e8

// Good practice to enclose all local definitions in an anonymous namespace
// equivalent of static functions in plain C.
namespace {

//! [StateDef]
struct State1D : public StateBase
{
    double x,  // transverse position
           xv; // transverse veclocity
//! [StateDef]

    virtual void assign(const StateBase& other)
    {
        StateBase::assign(other);
        const State1D& ST = static_cast<const State1D&>(other);
        x = ST.x;
        xv= ST.xv;
    }

    virtual void show(std::ostream &strm) const
    {
        strm<<pos<<" ["<<x<<", "<<xv<<"]\n";
    }

    // allow introspection of state variable by eg. Python
    virtual bool getArray(unsigned idx, ArrayInfo& Info)
    {
        if(idx==0) {
            Info.name = "x";
            Info.ptr = &x;
            // remaining defaults ok for scalar double
        } else if(idx==1) {
            Info.name = "xv";
            Info.ptr = &xv;
        } else {
            return StateBase::getArray(idx-2, Info); // check w/ base class for any remaining
        }
        return true;
    }

    virtual StateBase* clone() const {
        return new State1D(*this, clone_tag());
    }

    virtual ~State1D() {}

    // initialize state from config
    //! [StateInit]
    State1D(const Config& c)
        :StateBase(c)
        ,x(c.get<double>("x", 0.0))   // m
        ,xv(c.get<double>("xv", 0.0)) // m/s
    {}
    //! [StateInit]
    State1D(const State1D& O, clone_tag t)
        :StateBase(O, t)
        ,x(O.x)
        ,xv(O.xv)
    {}
};

//! [ElemSrcDef]
struct Element1DSource : public ElementVoid
{
    State1D initial;
//! [ElemSrcDef]

//! [ElemSrcInit]
    Element1DSource(const Config& c)
        :ElementVoid(c)
        ,initial(c)
    {}
//! [ElemSrcInit]

//! [ElemSrcAdvance]
    virtual void advance(StateBase& s)
    {
        s.assign(initial);
        s.pos += length; // source element ususaly has zero length, but not required
    }
//! [ElemSrcAdvance]

    virtual void assign(const ElementVoid* other )
    {
        ElementVoid::assign(other);
        const Element1DSource *O = static_cast<const Element1DSource*>(other);
        initial.assign(O->initial);
    }

    virtual const char* type_name() const { return "source"; }
};

//! [ElemGenericDef]
struct Element1DGeneric : public ElementVoid
{
    double xa, // transverse acceleration
           tt; // logitudinal transit time
//! [ElemGenericDef]

//! [ElemGenericInit]
    Element1DGeneric(const Config& c)
        :ElementVoid(c)
        ,xa(c.get<double>("A", 0.0)) // m/s2
    {
        // length will never change, so only need to compute transit time once
        tt = length/C0; // sec.
    }
//! [ElemGenericInit]

//! [ElemGenericAdvance]
    virtual void advance(StateBase& s)
    {
        State1D &ST = static_cast<State1D&>(s); // safe since sim_type=Simple1D will only use State1D

        ST.pos += length;
        ST.xv  += xa*tt;
        ST.x   += xa*tt*tt;
    }
//! [ElemGenericAdvance]

    virtual void assign(const ElementVoid* other )
    {
        ElementVoid::assign(other);
        const Element1DGeneric *O = static_cast<const Element1DGeneric*>(other);
        xa = O->xa;
        tt = O->tt;
    }

    virtual const char* type_name() const { return "generic"; }
};

} // end anon namespace

//! [register]
void register1D()
{
    Machine::registerState<State1D>("Simple1D");

    Machine::registerElement<Element1DSource>("Simple1D", "source");
    Machine::registerElement<Element1DGeneric>("Simple1D", "generic");
}
//! [register]

//! [main]
static const char lattice[] = ""
"sim_type = \"Simple1D\";\n"
"src   : source, x = 1e-5, xv = 1e-6;\n"
"elem1 : generic, A = 1,  L = 10;\n"
"elem2 : generic, A = -2, L = 10;\n"
"example : LINE = (src, elem1, elem2);\n"
;

int main(int argc, char *argv[])
{
    try {
        register1D();

        std::auto_ptr<Config> conf;
        {
            GLPSParser parser;
            conf.reset(parser.parse_byte(lattice, sizeof(lattice)-1));
        }

        /* conf now contains (using python notation)
         * {"sim_type":"Simple1D",
         *  "elements": [
         *    {"type":"source",  "name":"src", "x":1e-5, "xv":1e-6},
         *    {"type":"generic", "name":"elem1", "A":1.0,  "L":10.0},
         *    {"type":"generic", "name":"elem2", "A":-2.0, "L":10.0},
         *  ],
         * }
         */

        Machine mymachine(*conf);
        /* During Machine construction
         * sim_type=Simple1D with type=source selects Element1DSource, which is constructed once
         *
         * sim_type=Simple1D with type=source selects Element1DGeneric, which is constructed twice.
         *  first with A=1, then a second time with A=-2
         *
         * mymachine now has 3 elements.  mymachine.size()==3
         */

        mymachine.set_trace(&std::cout); // print intermediates

        std::auto_ptr<StateBase> thestate(mymachine.allocState());

        // propagate through the source element to initialize the state based on
        // values of "x" and "xv" from the lattice
        mymachine.propagate(thestate.get(), 0, 1);

        std::cout<<"Source state "<<*thestate<<"\n";

        // propagate through remaining elements
        mymachine.propagate(thestate.get(), 1);

        std::cout<<"Final state "<<*thestate<<"\n";

        Machine::registeryCleanup();

        return 0;
    } catch(std::exception& e) {
        std::cerr<<"Error: "<<e.what()<<"\n";
        return 1;
    }
}
//! [main]
<|endoftext|>