<commit_before>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "core/posix.hh"
#include "core/vla.hh"
#include "core/reactor.hh"
#include "core/future-util.hh"
#include "core/stream.hh"
#include "core/circular_buffer.hh"
#include "core/align.hh"
#include <atomic>
#include <list>
#include <queue>
#include <fcntl.h>
#include <linux/if_tun.h>
#include "ip.hh"

#include <xen/xen.h>

#include <xen/memory.h>
#include <xen/sys/gntalloc.h>

#include "core/xen/xenstore.hh"
#include "core/xen/evtchn.hh"

#include "xenfront.hh"

using namespace net;

using phys = uint64_t;

class xenfront_net_device : public net::device {
private:
    bool _userspace;
    stream<packet> _rx_stream;
    net::hw_features _hw_features;

    std::string _device_str;
    xenstore* _xenstore = xenstore::instance();
    unsigned _otherend;
    std::string _backend;
    gntalloc *_gntalloc;
    evtchn   *_evtchn;
    int _tx_evtchn;
    int _rx_evtchn;

    front_ring<tx> _tx_ring;
    front_ring<rx> _rx_ring;

    grant_head *_tx_refs;
    grant_head *_rx_refs;

    std::list<std::pair<std::string, std::string>> _features;

    ethernet_address _hw_address;

    int bind_tx_evtchn();
    int bind_rx_evtchn();

    future<> alloc_rx_references(unsigned refs);
    future<> queue_rx_packet();

    std::string path(std::string s) { return _device_str + "/" + s; }

public:
    explicit xenfront_net_device(boost::program_options::variables_map opts, bool userspace);
    ~xenfront_net_device();
    virtual subscription<packet> receive(std::function<future<> (packet)> next) override;
    virtual future<> send(packet p) override;

    ethernet_address hw_address();
    net::hw_features hw_features();
};

subscription<packet>
xenfront_net_device::receive(std::function<future<> (packet)> next) {
    auto sub = _rx_stream.listen(std::move(next));
    keep_doing([this] () {
        return _evtchn->pending(_rx_evtchn).then([this] {
            return queue_rx_packet();
        });
    });

    return std::move(sub);
}

future<>
xenfront_net_device::send(packet _p) {

    uint32_t frag = 0;
    // There doesn't seem to be a way to tell xen, when using the userspace
    // drivers, to map a particular page. Therefore, the only alternative
    // here is to copy. All pages shared must come from the gntalloc mmap.
    //
    // A better solution could be to change the packet allocation path to
    // use a pre-determined page for data.
    //
    // In-kernel should be fine
    return _tx_ring.free_idx().then([this, p = std::move(_p), frag] (uint32_t idx)  {

        auto req_prod = _tx_ring._sring->req_prod;

        auto f = p.frag(frag);

        auto ref = _tx_refs->new_ref(f.base, f.size);

        _tx_ring.entries[idx] = ref;

        auto req = &_tx_ring._sring->_ring[idx].req;
        req->gref = ref.first;
        req->offset = 0;
        req->flags = 0;
        req->id = idx;
        req->size = f.size;

        _tx_ring.req_prod_pvt = idx;
        _tx_ring._sring->req_prod = req_prod + 1;

        _tx_ring._sring->req_event++;
        if ((frag + 1) == p.nr_frags()) {
            printf("NOTIFY!!\n");
            _evtchn->notify(_tx_evtchn);
            return make_ready_future<>();
        } else {
            return make_ready_future<>();
        }
    });

    // FIXME: Don't forget to clear all grant refs when frontend closes. Or is it automatic?
}

#define rmb() asm volatile("lfence":::"memory");
#define wmb() asm volatile("":::"memory");

// FIXME: This is totally wrong, just coded so we can gt started with sending
template <typename T>
future<uint32_t> front_ring<T>::free_idx() {
    static uint32_t idx = 0;
    return make_ready_future<uint32_t>(idx++);
}

future<> xenfront_net_device::queue_rx_packet() {

    printf("Got at queue\n");
    auto rsp_cons = _rx_ring.rsp_cons;
    rmb();
    auto rsp_prod = _rx_ring._sring->rsp_prod;

    while (rsp_cons < rsp_prod) {
        auto rsp = _rx_ring[rsp_cons].rsp;
        auto entry = _rx_ring.entries[rsp.id];

        rsp_cons++;
        _rx_ring.rsp_cons = rsp_cons;

        if (rsp.status < 0) {
            printf("Packet error: Handle it\n");
            continue;
        }
        auto rsp_size = rsp.status;

        packet p(static_cast<char *>(entry.second) + rsp.offset, rsp_size);
        _rx_stream.produce(std::move(p));

        auto req_prod = _rx_ring._sring->req_prod;
        if (req_prod >= _rx_ring.req_prod_pvt) {
            printf("Allocate more\n"); // FIXME: This is futurized as well,
        }

        _rx_ring._sring->rsp_event = rsp_cons + 1;
        _rx_ring._sring->req_prod  = rsp_cons + 1;

        rsp_prod = _rx_ring._sring->rsp_prod;
        // FIXME: END GRANT. FIXME: ALLOCATE MORE MEMORY
    }

    // FIXME: Queue_rx maybe should not be a future then
    return make_ready_future<>();
}

future<> xenfront_net_device::alloc_rx_references(unsigned refs) {
    auto req_prod = _rx_ring.req_prod_pvt;
    rmb();

    for (auto i = req_prod; (i < _rx_ring.nr_ents) && (i < refs); ++i) {
        _rx_ring.entries[i] = _rx_refs->new_ref();

        // This is how the backend knows where to put data.
        auto req =  &_rx_ring._sring->_ring[i].req;
        req->id = i;
        req->gref = _rx_ring.entries[i].first;
        ++req_prod;
    }

    _rx_ring.req_prod_pvt = req_prod;
    wmb();
    _rx_ring._sring->req_prod = req_prod;
    /* ready */
    _evtchn->notify(_rx_evtchn);
    return make_ready_future();
}

ethernet_address xenfront_net_device::hw_address() {
    return _hw_address;
}

net::hw_features xenfront_net_device::hw_features() {
    return _hw_features;
}

int xenfront_net_device::bind_tx_evtchn() {
    return _evtchn->bind();
}

int xenfront_net_device::bind_rx_evtchn() {

    auto split = _xenstore->read<bool>(_backend + "/feature-split-event-channels");
    if (split) {
        return _evtchn->bind();
    }
    return _tx_evtchn;
}

xenfront_net_device::xenfront_net_device(boost::program_options::variables_map opts, bool userspace)
    : _userspace(userspace)
    , _rx_stream()
    , _device_str("device/vif/" + std::to_string(opts["vif"].as<unsigned>()))
    , _otherend(std::stoi(_xenstore->read(path("backend-id"))))
    , _backend(_xenstore->read(path("backend")))
    , _gntalloc(gntalloc::instance(_userspace, _otherend))
    , _evtchn(evtchn::instance(_userspace, _otherend))
    , _tx_evtchn(bind_tx_evtchn())
    , _rx_evtchn(bind_rx_evtchn())
    , _tx_ring(_gntalloc->alloc_ref())
    , _rx_ring(_gntalloc->alloc_ref())
    , _tx_refs(_gntalloc->alloc_ref(front_ring<tx>::nr_ents, false))
    , _rx_refs(_gntalloc->alloc_ref(front_ring<rx>::nr_ents, true))
    , _hw_address(net::parse_ethernet_address(_xenstore->read(path("mac")))) {

    _rx_stream.started();

    auto all_features = _xenstore->ls(_backend);

    // FIXME: Also have to remove what xenstar don't support,
    // but ATM we should support it all
    all_features.remove_if([](std::string& el) {
        return !!el.compare(0, 8, "feature-") && !!el.compare(0, 8, "request-");
    });

    for (auto&s : all_features) {
        auto value = _xenstore->read(_backend + "/" + s);
        _features.push_back(std::make_pair(s, value));
    }

    {
        auto t = xenstore::xenstore_transaction();

        for (auto& f: _features) {
            _xenstore->write(path(f.first), f.second, t);
        }

        _xenstore->write<int>(path("event-channel-tx"), _tx_evtchn, t);
        _xenstore->write<int>(path("event-channel-rx"), _rx_evtchn, t);
        _xenstore->write<int>(path("tx-ring-ref"), _tx_ring.ref, t);
        _xenstore->write<int>(path("rx-ring-ref"), _rx_ring.ref, t);
        _xenstore->write<int>(path("state"), 4, t);
    }

    alloc_rx_references(_rx_ring.nr_ents);
}

xenfront_net_device::~xenfront_net_device() {

    {
        auto t = xenstore::xenstore_transaction();

        for (auto& f: _features) {
            _xenstore->remove(path(f.first), t);
        }
        _xenstore->remove(path("event-channel-tx"), t);
        _xenstore->remove(path("event-channel-rx"), t);
        _xenstore->remove(path("tx-ring-ref"), t);
        _xenstore->remove(path("rx-ring-ref"), t);
        _xenstore->write<int>(path("state"), 6, t);
    }

    _xenstore->write<int>(path("state"), 1);
}

boost::program_options::options_description
get_xenfront_net_options_description() {

    boost::program_options::options_description opts(
            "xenfront net options");
    opts.add_options()
        ("vif",
                boost::program_options::value<unsigned>()->default_value(0),
                "vif number to hijack")
        ;
    return opts;
}

std::unique_ptr<net::device> create_xenfront_net_device(boost::program_options::variables_map opts, bool userspace) {
    auto ptr = std::make_unique<xenfront_net_device>(opts, userspace);
    // This assumes only one device per cpu. Will need to be fixed when
    // this assumption will no longer hold.
    dev = ptr.get();
    return std::move(ptr);
}
<commit_msg>xen: linearize packet before transmitting<commit_after>/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "core/posix.hh"
#include "core/vla.hh"
#include "core/reactor.hh"
#include "core/future-util.hh"
#include "core/stream.hh"
#include "core/circular_buffer.hh"
#include "core/align.hh"
#include <atomic>
#include <list>
#include <queue>
#include <fcntl.h>
#include <linux/if_tun.h>
#include "ip.hh"

#include <xen/xen.h>

#include <xen/memory.h>
#include <xen/sys/gntalloc.h>

#include "core/xen/xenstore.hh"
#include "core/xen/evtchn.hh"

#include "xenfront.hh"

using namespace net;

using phys = uint64_t;

class xenfront_net_device : public net::device {
private:
    bool _userspace;
    stream<packet> _rx_stream;
    net::hw_features _hw_features;

    std::string _device_str;
    xenstore* _xenstore = xenstore::instance();
    unsigned _otherend;
    std::string _backend;
    gntalloc *_gntalloc;
    evtchn   *_evtchn;
    int _tx_evtchn;
    int _rx_evtchn;

    front_ring<tx> _tx_ring;
    front_ring<rx> _rx_ring;

    grant_head *_tx_refs;
    grant_head *_rx_refs;

    std::list<std::pair<std::string, std::string>> _features;

    ethernet_address _hw_address;

    int bind_tx_evtchn();
    int bind_rx_evtchn();

    future<> alloc_rx_references(unsigned refs);
    future<> queue_rx_packet();

    std::string path(std::string s) { return _device_str + "/" + s; }

public:
    explicit xenfront_net_device(boost::program_options::variables_map opts, bool userspace);
    ~xenfront_net_device();
    virtual subscription<packet> receive(std::function<future<> (packet)> next) override;
    virtual future<> send(packet p) override;

    ethernet_address hw_address();
    net::hw_features hw_features();
};

subscription<packet>
xenfront_net_device::receive(std::function<future<> (packet)> next) {
    auto sub = _rx_stream.listen(std::move(next));
    keep_doing([this] () {
        return _evtchn->pending(_rx_evtchn).then([this] {
            return queue_rx_packet();
        });
    });

    return std::move(sub);
}

future<>
xenfront_net_device::send(packet _p) {

    uint32_t frag = 0;
    // There doesn't seem to be a way to tell xen, when using the userspace
    // drivers, to map a particular page. Therefore, the only alternative
    // here is to copy. All pages shared must come from the gntalloc mmap.
    //
    // A better solution could be to change the packet allocation path to
    // use a pre-determined page for data.
    //
    // In-kernel should be fine

    // FIXME: negotiate and use scatter/gather
    _p.linearize();

    return _tx_ring.free_idx().then([this, p = std::move(_p), frag] (uint32_t idx)  {

        auto req_prod = _tx_ring._sring->req_prod;

        auto f = p.frag(frag);

        auto ref = _tx_refs->new_ref(f.base, f.size);

        _tx_ring.entries[idx] = ref;

        auto req = &_tx_ring._sring->_ring[idx].req;
        req->gref = ref.first;
        req->offset = 0;
        req->flags = 0;
        req->id = idx;
        req->size = f.size;

        _tx_ring.req_prod_pvt = idx;
        _tx_ring._sring->req_prod = req_prod + 1;

        _tx_ring._sring->req_event++;
        if ((frag + 1) == p.nr_frags()) {
            printf("NOTIFY!!\n");
            _evtchn->notify(_tx_evtchn);
            return make_ready_future<>();
        } else {
            return make_ready_future<>();
        }
    });

    // FIXME: Don't forget to clear all grant refs when frontend closes. Or is it automatic?
}

#define rmb() asm volatile("lfence":::"memory");
#define wmb() asm volatile("":::"memory");

// FIXME: This is totally wrong, just coded so we can gt started with sending
template <typename T>
future<uint32_t> front_ring<T>::free_idx() {
    static uint32_t idx = 0;
    return make_ready_future<uint32_t>(idx++);
}

future<> xenfront_net_device::queue_rx_packet() {

    printf("Got at queue\n");
    auto rsp_cons = _rx_ring.rsp_cons;
    rmb();
    auto rsp_prod = _rx_ring._sring->rsp_prod;

    while (rsp_cons < rsp_prod) {
        auto rsp = _rx_ring[rsp_cons].rsp;
        auto entry = _rx_ring.entries[rsp.id];

        rsp_cons++;
        _rx_ring.rsp_cons = rsp_cons;

        if (rsp.status < 0) {
            printf("Packet error: Handle it\n");
            continue;
        }
        auto rsp_size = rsp.status;

        packet p(static_cast<char *>(entry.second) + rsp.offset, rsp_size);
        _rx_stream.produce(std::move(p));

        auto req_prod = _rx_ring._sring->req_prod;
        if (req_prod >= _rx_ring.req_prod_pvt) {
            printf("Allocate more\n"); // FIXME: This is futurized as well,
        }

        _rx_ring._sring->rsp_event = rsp_cons + 1;
        _rx_ring._sring->req_prod  = rsp_cons + 1;

        rsp_prod = _rx_ring._sring->rsp_prod;
        // FIXME: END GRANT. FIXME: ALLOCATE MORE MEMORY
    }

    // FIXME: Queue_rx maybe should not be a future then
    return make_ready_future<>();
}

future<> xenfront_net_device::alloc_rx_references(unsigned refs) {
    auto req_prod = _rx_ring.req_prod_pvt;
    rmb();

    for (auto i = req_prod; (i < _rx_ring.nr_ents) && (i < refs); ++i) {
        _rx_ring.entries[i] = _rx_refs->new_ref();

        // This is how the backend knows where to put data.
        auto req =  &_rx_ring._sring->_ring[i].req;
        req->id = i;
        req->gref = _rx_ring.entries[i].first;
        ++req_prod;
    }

    _rx_ring.req_prod_pvt = req_prod;
    wmb();
    _rx_ring._sring->req_prod = req_prod;
    /* ready */
    _evtchn->notify(_rx_evtchn);
    return make_ready_future();
}

ethernet_address xenfront_net_device::hw_address() {
    return _hw_address;
}

net::hw_features xenfront_net_device::hw_features() {
    return _hw_features;
}

int xenfront_net_device::bind_tx_evtchn() {
    return _evtchn->bind();
}

int xenfront_net_device::bind_rx_evtchn() {

    auto split = _xenstore->read<bool>(_backend + "/feature-split-event-channels");
    if (split) {
        return _evtchn->bind();
    }
    return _tx_evtchn;
}

xenfront_net_device::xenfront_net_device(boost::program_options::variables_map opts, bool userspace)
    : _userspace(userspace)
    , _rx_stream()
    , _device_str("device/vif/" + std::to_string(opts["vif"].as<unsigned>()))
    , _otherend(std::stoi(_xenstore->read(path("backend-id"))))
    , _backend(_xenstore->read(path("backend")))
    , _gntalloc(gntalloc::instance(_userspace, _otherend))
    , _evtchn(evtchn::instance(_userspace, _otherend))
    , _tx_evtchn(bind_tx_evtchn())
    , _rx_evtchn(bind_rx_evtchn())
    , _tx_ring(_gntalloc->alloc_ref())
    , _rx_ring(_gntalloc->alloc_ref())
    , _tx_refs(_gntalloc->alloc_ref(front_ring<tx>::nr_ents, false))
    , _rx_refs(_gntalloc->alloc_ref(front_ring<rx>::nr_ents, true))
    , _hw_address(net::parse_ethernet_address(_xenstore->read(path("mac")))) {

    _rx_stream.started();

    auto all_features = _xenstore->ls(_backend);

    // FIXME: Also have to remove what xenstar don't support,
    // but ATM we should support it all
    all_features.remove_if([](std::string& el) {
        return !!el.compare(0, 8, "feature-") && !!el.compare(0, 8, "request-");
    });

    for (auto&s : all_features) {
        auto value = _xenstore->read(_backend + "/" + s);
        _features.push_back(std::make_pair(s, value));
    }

    {
        auto t = xenstore::xenstore_transaction();

        for (auto& f: _features) {
            _xenstore->write(path(f.first), f.second, t);
        }

        _xenstore->write<int>(path("event-channel-tx"), _tx_evtchn, t);
        _xenstore->write<int>(path("event-channel-rx"), _rx_evtchn, t);
        _xenstore->write<int>(path("tx-ring-ref"), _tx_ring.ref, t);
        _xenstore->write<int>(path("rx-ring-ref"), _rx_ring.ref, t);
        _xenstore->write<int>(path("state"), 4, t);
    }

    alloc_rx_references(_rx_ring.nr_ents);
}

xenfront_net_device::~xenfront_net_device() {

    {
        auto t = xenstore::xenstore_transaction();

        for (auto& f: _features) {
            _xenstore->remove(path(f.first), t);
        }
        _xenstore->remove(path("event-channel-tx"), t);
        _xenstore->remove(path("event-channel-rx"), t);
        _xenstore->remove(path("tx-ring-ref"), t);
        _xenstore->remove(path("rx-ring-ref"), t);
        _xenstore->write<int>(path("state"), 6, t);
    }

    _xenstore->write<int>(path("state"), 1);
}

boost::program_options::options_description
get_xenfront_net_options_description() {

    boost::program_options::options_description opts(
            "xenfront net options");
    opts.add_options()
        ("vif",
                boost::program_options::value<unsigned>()->default_value(0),
                "vif number to hijack")
        ;
    return opts;
}

std::unique_ptr<net::device> create_xenfront_net_device(boost::program_options::variables_map opts, bool userspace) {
    auto ptr = std::make_unique<xenfront_net_device>(opts, userspace);
    // This assumes only one device per cpu. Will need to be fixed when
    // this assumption will no longer hold.
    dev = ptr.get();
    return std::move(ptr);
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <cmake.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Dates.h>
#include <Filter.h>
#include <i18n.h>
#include <text.h>
#include <util.h>

extern Context context;

////////////////////////////////////////////////////////////////////////////////
// Const iterator that can be derefenced into a Task by domSource.
static Task dummy;
Task& contextTask = dummy;

////////////////////////////////////////////////////////////////////////////////
bool domSource (const std::string& identifier, Variant& value)
{
  if (context.dom.get (identifier, contextTask, value))
  {
    value.source (identifier);
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
Filter::Filter ()
: _startCount (0)
, _endCount (0)
{
}

////////////////////////////////////////////////////////////////////////////////
Filter::~Filter ()
{
}

////////////////////////////////////////////////////////////////////////////////
// Take an input set of tasks and filter into a subset.
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
{
  context.timer_filter.start ();
  _startCount = (int) input.size ();

  if (context.config.getBoolean ("debug"))
  {
    Tree* t = context.parser.tree ();
    if (t)
      context.debug (t->dump ());
  }

  std::string filterExpr = context.parser.getFilterExpression ();
  if (filterExpr.length ())
  {
    Eval eval;
    eval.addSource (namedDates);
    eval.addSource (domSource);

    // Debug output from Eval during compilation is useful.  During evaluation
    // it is mostly noise.
    eval.debug (context.config.getBoolean ("debug"));
    eval.compileExpression (filterExpr);
    eval.ambiguity (false);
    eval.debug (false);

    std::vector <Task>::const_iterator task;
    for (task = input.begin (); task != input.end (); ++task)
    {
      // Set up context for any DOM references.
      contextTask = *task;

      Variant var;
      eval.evaluateCompiledExpression (var);
      if (var.get_bool ())
        output.push_back (*task);
    }
  }
  else
    output = input;

  _endCount = (int) output.size ();
  context.debug (format ("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount));
  context.timer_filter.stop ();
}

////////////////////////////////////////////////////////////////////////////////
// Take the set of all tasks and filter into a subset.
void Filter::subset (std::vector <Task>& output)
{
  context.timer_filter.start ();

  if (context.config.getBoolean ("debug"))
  {
    Tree* t = context.parser.tree ();
    if (t)
      context.debug (t->dump ());
  }

  bool shortcut = false;
  std::string filterExpr = context.parser.getFilterExpression ();
  if (filterExpr.length ())
  {
    context.timer_filter.stop ();
    const std::vector <Task>& pending = context.tdb2.pending.get_tasks ();
    context.timer_filter.start ();
    _startCount = (int) pending.size ();

    Eval eval;
    eval.addSource (namedDates);
    eval.addSource (domSource);

    // Debug output from Eval during compilation is useful.  During evaluation
    // it is mostly noise.
    eval.debug (context.config.getBoolean ("debug"));
    eval.compileExpression (filterExpr);
    eval.ambiguity (false);
    eval.debug (false);

    output.clear ();
    std::vector <Task>::const_iterator task;

    for (task = pending.begin (); task != pending.end (); ++task)
    {
      // Set up context for any DOM references.
      contextTask = *task;

      Variant var;
      eval.evaluateCompiledExpression (var);
      if (var.get_bool ())
        output.push_back (*task);
    }

    shortcut = pendingOnly ();
    if (! shortcut)
    {
      context.timer_filter.stop ();
      const std::vector <Task>& completed = context.tdb2.completed.get_tasks (); // TODO Optional
      context.timer_filter.start ();
      _startCount += (int) completed.size ();

      for (task = completed.begin (); task != completed.end (); ++task)
      {
        // Set up context for any DOM references.
        contextTask = *task;

        Variant var;
        eval.evaluateCompiledExpression (var);
        if (var.get_bool ())
          output.push_back (*task);
      }
    }
  }
  else
  {
    safety ();

    context.timer_filter.stop ();
    const std::vector <Task>& pending   = context.tdb2.pending.get_tasks ();
    const std::vector <Task>& completed = context.tdb2.completed.get_tasks ();
    context.timer_filter.start ();

    std::vector <Task>::const_iterator task;
    for (task = pending.begin (); task != pending.end (); ++task)
      output.push_back (*task);

    for (task = completed.begin (); task != completed.end (); ++task)
      output.push_back (*task);
  }

  _endCount = (int) output.size ();
  context.debug (format ("Filtered {1} tasks --> {2} tasks [{3}]", _startCount, _endCount, (shortcut ? "pending only" : "all tasks")));
  context.timer_filter.stop ();
}

////////////////////////////////////////////////////////////////////////////////
// If the filter contains the restriction "status:pending", as the first filter
// term, then completed.data does not need to be loaded.
bool Filter::pendingOnly ()
{
  Tree* tree = context.parser.tree ();

  // To skip loading completed.data, there should be:
  // - 'status:pending'
  // - no 'or' operators
  // - no 'xor' operators
  int countStatus  = 0;
  int countPending = 0;
  int countId      = 0;
  int countOr      = 0;
  int countXor     = 0;
  std::vector <Tree*>::iterator i;
  for (i = tree->_branches.begin (); i != tree->_branches.end (); ++i)
  {
    if ((*i)->hasTag ("FILTER"))
    {
      if ((*i)->hasTag ("ID"))                                                ++countId;
      if ((*i)->hasTag ("OP") && (*i)->attribute ("raw") == "or")             ++countOr;
      if ((*i)->hasTag ("OP") && (*i)->attribute ("raw") == "xor")            ++countXor;
      if ((*i)->hasTag ("ATTRIBUTE") && (*i)->attribute ("name") == "status") ++countStatus;
      if ((*i)->hasTag ("ATTRIBUTE") && (*i)->attribute ("raw") == "pending") ++countPending;
    }
  }

  // Use of 'or' or 'xor' means the potential for alternation.
  if (countOr || countXor)
    return false;

  // If the filter does not contain id or status terms, read both files.
  if (countId == 0 && countStatus == 0)
    return false;

  // Only one 'status == pending' is allowed.
  if (countStatus == 1 && countPending != 1)
    return false;

  return true;
}

////////////////////////////////////////////////////////////////////////////////
// Disaster avoidance mechanism. If a WRITECMD has no filter, then it can cause
// all tasks to be modified. This is usually not intended.
void Filter::safety ()
{
  Tree* tree = context.parser.tree ();
  std::vector <Tree*>::iterator i;
  for (i = tree->_branches.begin (); i != tree->_branches.end (); ++i)
  {
    if ((*i)->hasTag ("WRITECMD"))
    {
      if (context.parser.getFilterExpression () == "")
      {
        // If user is willing to be asked, this can be avoided.
        if (context.config.getBoolean ("confirmation") &&
            confirm (STRING_TASK_SAFETY_VALVE))
          return;

        // Sounds the alarm.
        throw std::string (STRING_TASK_SAFETY_FAIL);
      }

      // Nothing to see here. Move along.
      return;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
<commit_msg>Filter<commit_after>////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <cmake.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Dates.h>
#include <Filter.h>
#include <i18n.h>
#include <text.h>
#include <util.h>

extern Context context;

////////////////////////////////////////////////////////////////////////////////
// Const iterator that can be derefenced into a Task by domSource.
static Task dummy;
Task& contextTask = dummy;

////////////////////////////////////////////////////////////////////////////////
bool domSource (const std::string& identifier, Variant& value)
{
  if (context.dom.get (identifier, contextTask, value))
  {
    value.source (identifier);
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
Filter::Filter ()
: _startCount (0)
, _endCount (0)
{
}

////////////////////////////////////////////////////////////////////////////////
Filter::~Filter ()
{
}

////////////////////////////////////////////////////////////////////////////////
// Take an input set of tasks and filter into a subset.
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
{
  context.timer_filter.start ();
  _startCount = (int) input.size ();

  if (context.config.getBoolean ("debug"))
  {
    Tree* t = context.parser.tree ();
    if (t)
      context.debug (t->dump ());
  }

  std::string filterExpr = context.parser.getFilterExpression ();
  if (filterExpr.length ())
  {
    Eval eval;
    eval.ambiguity (false);
    eval.addSource (namedDates);
    eval.addSource (domSource);

    // Debug output from Eval during compilation is useful.  During evaluation
    // it is mostly noise.
    eval.debug (context.config.getBoolean ("debug"));
    eval.compileExpression (filterExpr);
    eval.debug (false);

    std::vector <Task>::const_iterator task;
    for (task = input.begin (); task != input.end (); ++task)
    {
      // Set up context for any DOM references.
      contextTask = *task;

      Variant var;
      eval.evaluateCompiledExpression (var);
      if (var.get_bool ())
        output.push_back (*task);
    }
  }
  else
    output = input;

  _endCount = (int) output.size ();
  context.debug (format ("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount));
  context.timer_filter.stop ();
}

////////////////////////////////////////////////////////////////////////////////
// Take the set of all tasks and filter into a subset.
void Filter::subset (std::vector <Task>& output)
{
  context.timer_filter.start ();

  if (context.config.getBoolean ("debug"))
  {
    Tree* t = context.parser.tree ();
    if (t)
      context.debug (t->dump ());
  }

  bool shortcut = false;
  std::string filterExpr = context.parser.getFilterExpression ();
  if (filterExpr.length ())
  {
    context.timer_filter.stop ();
    const std::vector <Task>& pending = context.tdb2.pending.get_tasks ();
    context.timer_filter.start ();
    _startCount = (int) pending.size ();

    Eval eval;
    eval.ambiguity (false);
    eval.addSource (namedDates);
    eval.addSource (domSource);

    // Debug output from Eval during compilation is useful.  During evaluation
    // it is mostly noise.
    eval.debug (context.config.getBoolean ("debug"));
    eval.compileExpression (filterExpr);
    eval.debug (false);

    output.clear ();
    std::vector <Task>::const_iterator task;

    for (task = pending.begin (); task != pending.end (); ++task)
    {
      // Set up context for any DOM references.
      contextTask = *task;

      Variant var;
      eval.evaluateCompiledExpression (var);
      if (var.get_bool ())
        output.push_back (*task);
    }

    shortcut = pendingOnly ();
    if (! shortcut)
    {
      context.timer_filter.stop ();
      const std::vector <Task>& completed = context.tdb2.completed.get_tasks (); // TODO Optional
      context.timer_filter.start ();
      _startCount += (int) completed.size ();

      for (task = completed.begin (); task != completed.end (); ++task)
      {
        // Set up context for any DOM references.
        contextTask = *task;

        Variant var;
        eval.evaluateCompiledExpression (var);
        if (var.get_bool ())
          output.push_back (*task);
      }
    }
  }
  else
  {
    safety ();

    context.timer_filter.stop ();
    const std::vector <Task>& pending   = context.tdb2.pending.get_tasks ();
    const std::vector <Task>& completed = context.tdb2.completed.get_tasks ();
    context.timer_filter.start ();

    std::vector <Task>::const_iterator task;
    for (task = pending.begin (); task != pending.end (); ++task)
      output.push_back (*task);

    for (task = completed.begin (); task != completed.end (); ++task)
      output.push_back (*task);
  }

  _endCount = (int) output.size ();
  context.debug (format ("Filtered {1} tasks --> {2} tasks [{3}]", _startCount, _endCount, (shortcut ? "pending only" : "all tasks")));
  context.timer_filter.stop ();
}

////////////////////////////////////////////////////////////////////////////////
// If the filter contains the restriction "status:pending", as the first filter
// term, then completed.data does not need to be loaded.
bool Filter::pendingOnly ()
{
  Tree* tree = context.parser.tree ();

  // To skip loading completed.data, there should be:
  // - 'status:pending'
  // - no 'or' operators
  // - no 'xor' operators
  int countStatus  = 0;
  int countPending = 0;
  int countId      = 0;
  int countOr      = 0;
  int countXor     = 0;
  std::vector <Tree*>::iterator i;
  for (i = tree->_branches.begin (); i != tree->_branches.end (); ++i)
  {
    if ((*i)->hasTag ("FILTER"))
    {
      if ((*i)->hasTag ("ID"))                                                ++countId;
      if ((*i)->hasTag ("OP") && (*i)->attribute ("raw") == "or")             ++countOr;
      if ((*i)->hasTag ("OP") && (*i)->attribute ("raw") == "xor")            ++countXor;
      if ((*i)->hasTag ("ATTRIBUTE") && (*i)->attribute ("name") == "status") ++countStatus;
      if ((*i)->hasTag ("ATTRIBUTE") && (*i)->attribute ("raw") == "pending") ++countPending;
    }
  }

  // Use of 'or' or 'xor' means the potential for alternation.
  if (countOr || countXor)
    return false;

  // If the filter does not contain id or status terms, read both files.
  if (countId == 0 && countStatus == 0)
    return false;

  // Only one 'status == pending' is allowed.
  if (countStatus == 1 && countPending != 1)
    return false;

  return true;
}

////////////////////////////////////////////////////////////////////////////////
// Disaster avoidance mechanism. If a WRITECMD has no filter, then it can cause
// all tasks to be modified. This is usually not intended.
void Filter::safety ()
{
  Tree* tree = context.parser.tree ();
  std::vector <Tree*>::iterator i;
  for (i = tree->_branches.begin (); i != tree->_branches.end (); ++i)
  {
    if ((*i)->hasTag ("WRITECMD"))
    {
      if (context.parser.getFilterExpression () == "")
      {
        // If user is willing to be asked, this can be avoided.
        if (context.config.getBoolean ("confirmation") &&
            confirm (STRING_TASK_SAFETY_VALVE))
          return;

        // Sounds the alarm.
        throw std::string (STRING_TASK_SAFETY_FAIL);
      }

      // Nothing to see here. Move along.
      return;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>/*
 * MRustC - Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * trans/mangling_v2.cpp
 * - Name mangling (encoding of rust paths into symbols)
 */
#include <debug.hpp>
#include <string_view.hpp>
#include <hir/hir.hpp>  // ABI_RUST
#include <hir/type.hpp>

class Mangler
{
    ::std::ostream& m_os;
public:
    Mangler(::std::ostream& os):
        m_os(os)
    {
    }

    // Item names:
    // - These can have a single '#' in them (either leading or in the middle)
    // TODO: Some other invalid characters can appear:
    // - '-' (crate names)
    void fmt_name(const RcString& s)
    {
        this->fmt_name(s.c_str());
    }
    void fmt_name(const char* const s)
    {
        size_t size = strlen(s);
        const char* hash_pos = nullptr;
        // - Search the string for the '#' character
        for(const auto* p = s; *p; p++)
        {
            if( p == s ) {
                ASSERT_BUG(Span(), !isdigit(*p), "Leading digit not valid in '" << s << "'");
            }

            if( isalnum(*p) ) {
            }
            else if( *p == '_' ) {
            }
            else if( *p == '#' || *p == '-' ) { // HACK: Treat '-' and '#' as the same in encoding
                // Multiple hash characters? abort/error
                ASSERT_BUG(Span(), hash_pos == nullptr, "Multiple '#' characters in '" << s << "'");
                hash_pos = p;
            }
            else {
                BUG(Span(), "Encounteded invalid character '" << *p << "' in '" << s << "'");
            }
        }

        // If there's a hash, then prefix with a letter indicating its location?
        // - Using a 3 character overhead currently (but a letter could work really well)
        if( hash_pos != nullptr )
        {
            auto pre_hash_len = static_cast<int>(hash_pos - s);
#if 0
            assert(pre_hash_len < 26);
            // <posletter> <full_len> <body1> <body2>
            m_os << 'a' + pre_hash_len;
            m_os << size - 1;
            m_os << ::stdx::string_view(s, s + pre_hash_len);
            m_os << hash_pos + 1;;
#else
            // 'h' <len1> <body1> <len2> <body2>
            m_os << "h";
            m_os << pre_hash_len;
            m_os << ::stdx::string_view(s, s + pre_hash_len);
            m_os << size - pre_hash_len - 1;
            m_os << hash_pos + 1;;
#endif
        }
        else
        {
            m_os << size;
            m_os << s;
        }
    }
    // SimplePath : <ncomp> 'c' [<RcString> ...]
    void fmt_simple_path(const ::HIR::SimplePath& sp)
    {
        m_os << sp.m_components.size();
        m_os << "c";    // Needed to separate the component count from the crate name
        this->fmt_name(sp.m_crate_name);
        for(const auto& c : sp.m_components)
        {
            this->fmt_name(c);
        }
    }

    // PathParams : <ntys> 'g' [<TypeRef> ...]
    void fmt_path_params(const ::HIR::PathParams& pp)
    {
        // Type Parameter count
        m_os << pp.m_types.size();
        m_os << "g";
        for(const auto& ty : pp.m_types)
        {
            fmt_type(ty);
        }
    }
    // GenericPath : <SimplePath> <PathParams>
    void fmt_generic_path(const ::HIR::GenericPath& gp)
    {
        this->fmt_simple_path(gp.m_path);
        this->fmt_path_params(gp.m_params);
    }

    void fmt_path(const ::HIR::Path& p)
    {
        // Path type
        // - Generic: starts with `G`
        // - Inherent: Starts with `I`
        // - Trait: Starts with `Q` (qualified)
        // - bare type: Starts with `T` (see Trans_MangleType)
        TU_MATCH_HDRA( (p.m_data), {)
        TU_ARMA(Generic, e) {
            m_os << "G";
            this->fmt_generic_path(e);
            }
        TU_ARMA(UfcsInherent, e) {
            m_os << "I";
            this->fmt_type(*e.type);
            this->fmt_name(e.item);
            this->fmt_path_params(e.params);
            }
        TU_ARMA(UfcsKnown, e) {
            m_os << "Q";
            this->fmt_type(*e.type);
            this->fmt_generic_path(e.trait);
            this->fmt_name(e.item);
            this->fmt_path_params(e.params);
            }
        TU_ARMA(UfcsUnknown, e)
            BUG(Span(), "Non-encodable path " << p);
        }
    }

    // Type
    // - Tuple: 'T' <nelem> [<TypeRef> ...]
    // - Slice: 'S' <TypeRef>
    // - Array: 'A' <size> <TypeRef>
    // - Path: 'N' <Path>
    // - TraitObject: 'D' <data:GenericPath> <nmarker> [markers: <GenericPath> ...] <naty> [<TypeRef> ...]    TODO: Does this need to include the ATY name?
    // - Borrow: 'B' ('s'|'u'|'o') <TypeRef>
    // - RawPointer: 'P' ('s'|'u'|'o') <TypeRef>
    // - Function: 'F' <abi:RcString> <nargs> [args: <TypeRef> ...] <ret:TypeRef>
    // - Primitives::
    //   - u8  : 'C' 'a'
    //   - i8  : 'C' 'b'
    //   - u16 : 'C' 'c'
    //   - i16 : 'C' 'd'
    //   - u32 : 'C' 'e'
    //   - i32 : 'C' 'f'
    //   - u64 : 'C' 'g'
    //   - i64 : 'C' 'h'
    //   - u128: 'C' 'i'
    //   - i128: 'C' 'j'
    //   --
    //   - f32  : 'C' 'n'
    //   - f64  : 'C' 'o'
    //   --
    //   - usize: 'C' 'u'
    //   - isize: 'C' 'v'
    //   - bool : 'C' 'x'
    //   - char : 'C' 'x'
    //   - str  : 'C' 'y'
    // - Diverge: 'C' 'z'
    void fmt_type(const ::HIR::TypeRef& ty)
    {
        TU_MATCH_HDRA( (ty.m_data), { )
        case ::HIR::TypeRef::Data::TAG_Infer:
        case ::HIR::TypeRef::Data::TAG_Generic:
        case ::HIR::TypeRef::Data::TAG_ErasedType:
        case ::HIR::TypeRef::Data::TAG_Closure:
            BUG(Span(), "Non-encodable type " << ty);
        TU_ARMA(Tuple, e) {
            m_os << "T" << e.size();
            for(const auto& sty : e)
                this->fmt_type(sty);
            }
        TU_ARMA(Slice, e) {
            m_os << "S";
            this->fmt_type(*e.inner);
            }
        TU_ARMA(Array, e) {
            m_os << "A" << e.size_val;
            this->fmt_type(*e.inner);
            }
        TU_ARMA(Path, e) {
            m_os << "N";
            this->fmt_path(e.path);
            }
        TU_ARMA(TraitObject, e) {
            // - TraitObject: 'D' <data:GenericPath> <naty> [<TypeRef> ...] <nmarker> [markers: <GenericPath> ...]
            m_os << "D";
            this->fmt_generic_path(e.m_trait.m_path);
            m_os << e.m_trait.m_type_bounds.size();
            // HACK: Assume all TraitObject types have the same aty set (std::map is deterministic)
            for(const auto& aty : e.m_trait.m_type_bounds)
                this->fmt_type(aty.second);
            m_os << e.m_markers.size();
            for(const auto& p : e.m_markers)
                this->fmt_generic_path(p);
            }
        TU_ARMA(Function, e) {
            // - Function: 'F' <abi:RcString> <nargs> [args: <TypeRef> ...] <ret:TypeRef>
            m_os << "F";
            m_os << (e.is_unsafe ? "u" : "");    // Optional allowed, next is a number
            if( e.m_abi != ABI_RUST )
            {
                m_os << "e";
                this->fmt_name(e.m_abi.c_str());
            }
            m_os << e.m_arg_types.size();
            for(const auto& t : e.m_arg_types)
                this->fmt_type(t);
            this->fmt_type(*e.m_rettype);
            }
        TU_ARMA(Borrow, e) {
            m_os << "B";
            switch(e.type)
            {
            case ::HIR::BorrowType::Shared: m_os << "s"; break;
            case ::HIR::BorrowType::Unique: m_os << "u"; break;
            case ::HIR::BorrowType::Owned:  m_os << "o"; break;
            }
            this->fmt_type(*e.inner);
            }
        TU_ARMA(Pointer, e) {
            m_os << "P";
            switch(e.type)
            {
            case ::HIR::BorrowType::Shared: m_os << "s"; break;
            case ::HIR::BorrowType::Unique: m_os << "u"; break;
            case ::HIR::BorrowType::Owned:  m_os << "o"; break;
            }
            this->fmt_type(*e.inner);
            }
        TU_ARMA(Primitive, e) {
            switch(e)
            {
            case ::HIR::CoreType::U8  : m_os << 'C' << 'a'; break;
            case ::HIR::CoreType::I8  : m_os << 'C' << 'b'; break;
            case ::HIR::CoreType::U16 : m_os << 'C' << 'c'; break;
            case ::HIR::CoreType::I16 : m_os << 'C' << 'd'; break;
            case ::HIR::CoreType::U32 : m_os << 'C' << 'e'; break;
            case ::HIR::CoreType::I32 : m_os << 'C' << 'f'; break;
            case ::HIR::CoreType::U64 : m_os << 'C' << 'g'; break;
            case ::HIR::CoreType::I64 : m_os << 'C' << 'h'; break;
            case ::HIR::CoreType::U128: m_os << 'C' << 'i'; break;
            case ::HIR::CoreType::I128: m_os << 'C' << 'j'; break;
            case ::HIR::CoreType::F32 : m_os << 'C' << 'n'; break;
            case ::HIR::CoreType::F64 : m_os << 'C' << 'o'; break;
            case ::HIR::CoreType::Usize: m_os << 'C' << 'u'; break;
            case ::HIR::CoreType::Isize: m_os << 'C' << 'v'; break;
            case ::HIR::CoreType::Bool: m_os << 'C' << 'w'; break;
            case ::HIR::CoreType::Char: m_os << 'C' << 'x'; break;
            case ::HIR::CoreType::Str : m_os << 'C' << 'y'; break;
            }
            }
        TU_ARMA(Diverge, _e) {
            m_os << 'C' << 'z';
            }
        }
    }
};

::FmtLambda Trans_ManglePath(const ::HIR::Path& p)
{
    return FMT_CB(os, os << "ZR"; Mangler(os).fmt_path(p));
}
::FmtLambda Trans_MangleSimplePath(const ::HIR::SimplePath& p)
{
    return FMT_CB(os, os << "ZRG"; Mangler(os).fmt_simple_path(p); Mangler(os).fmt_path_params({}););
}
::FmtLambda Trans_MangleGenericPath(const ::HIR::GenericPath& p)
{
    return FMT_CB(os, os << "ZRG"; Mangler(os).fmt_generic_path(p));
}
::FmtLambda Trans_MangleType(const ::HIR::TypeRef& p)
{
    return FMT_CB(os, os << "ZRT"; Mangler(os).fmt_type(p));
}

::FmtLambda Trans_Mangle(const ::HIR::SimplePath& path) { return Trans_MangleSimplePath(path); }
::FmtLambda Trans_Mangle(const ::HIR::GenericPath& path) { return Trans_MangleGenericPath(path); }
::FmtLambda Trans_Mangle(const ::HIR::Path& path) { return Trans_ManglePath(path); }
::FmtLambda Trans_Mangle(const ::HIR::TypeRef& ty) { return Trans_MangleType(ty); }
<commit_msg>Trans Mangling - Emit symbols longer than 128 bytes as hashes (to allow building larger crates on MSVC)<commit_after>/*
 * MRustC - Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * trans/mangling_v2.cpp
 * - Name mangling (encoding of rust paths into symbols)
 */
#include <debug.hpp>
#include <string_view.hpp>
#include <hir/hir.hpp>  // ABI_RUST
#include <hir/type.hpp>

class Mangler
{
    ::std::ostream& m_os;
public:
    Mangler(::std::ostream& os):
        m_os(os)
    {
    }

    // Item names:
    // - These can have a single '#' in them (either leading or in the middle)
    // TODO: Some other invalid characters can appear:
    // - '-' (crate names)
    void fmt_name(const RcString& s)
    {
        this->fmt_name(s.c_str());
    }
    void fmt_name(const char* const s)
    {
        size_t size = strlen(s);
        const char* hash_pos = nullptr;
        // - Search the string for the '#' character
        for(const auto* p = s; *p; p++)
        {
            if( p == s ) {
                ASSERT_BUG(Span(), !isdigit(*p), "Leading digit not valid in '" << s << "'");
            }

            if( isalnum(*p) ) {
            }
            else if( *p == '_' ) {
            }
            else if( *p == '#' || *p == '-' ) { // HACK: Treat '-' and '#' as the same in encoding
                // Multiple hash characters? abort/error
                ASSERT_BUG(Span(), hash_pos == nullptr, "Multiple '#' characters in '" << s << "'");
                hash_pos = p;
            }
            else {
                BUG(Span(), "Encounteded invalid character '" << *p << "' in '" << s << "'");
            }
        }

        // If there's a hash, then prefix with a letter indicating its location?
        // - Using a 3 character overhead currently (but a letter could work really well)
        if( hash_pos != nullptr )
        {
            auto pre_hash_len = static_cast<int>(hash_pos - s);
#if 0
            assert(pre_hash_len < 26);
            // <posletter> <full_len> <body1> <body2>
            m_os << 'a' + pre_hash_len;
            m_os << size - 1;
            m_os << ::stdx::string_view(s, s + pre_hash_len);
            m_os << hash_pos + 1;;
#else
            // 'h' <len1> <body1> <len2> <body2>
            m_os << "h";
            m_os << pre_hash_len;
            m_os << ::stdx::string_view(s, s + pre_hash_len);
            m_os << size - pre_hash_len - 1;
            m_os << hash_pos + 1;;
#endif
        }
        else
        {
            m_os << size;
            m_os << s;
        }
    }
    // SimplePath : <ncomp> 'c' [<RcString> ...]
    void fmt_simple_path(const ::HIR::SimplePath& sp)
    {
        m_os << sp.m_components.size();
        m_os << "c";    // Needed to separate the component count from the crate name
        this->fmt_name(sp.m_crate_name);
        for(const auto& c : sp.m_components)
        {
            this->fmt_name(c);
        }
    }

    // PathParams : <ntys> 'g' [<TypeRef> ...]
    void fmt_path_params(const ::HIR::PathParams& pp)
    {
        // Type Parameter count
        m_os << pp.m_types.size();
        m_os << "g";
        for(const auto& ty : pp.m_types)
        {
            fmt_type(ty);
        }
    }
    // GenericPath : <SimplePath> <PathParams>
    void fmt_generic_path(const ::HIR::GenericPath& gp)
    {
        this->fmt_simple_path(gp.m_path);
        this->fmt_path_params(gp.m_params);
    }

    void fmt_path(const ::HIR::Path& p)
    {
        // Path type
        // - Generic: starts with `G`
        // - Inherent: Starts with `I`
        // - Trait: Starts with `Q` (qualified)
        // - bare type: Starts with `T` (see Trans_MangleType)
        TU_MATCH_HDRA( (p.m_data), {)
        TU_ARMA(Generic, e) {
            m_os << "G";
            this->fmt_generic_path(e);
            }
        TU_ARMA(UfcsInherent, e) {
            m_os << "I";
            this->fmt_type(*e.type);
            this->fmt_name(e.item);
            this->fmt_path_params(e.params);
            }
        TU_ARMA(UfcsKnown, e) {
            m_os << "Q";
            this->fmt_type(*e.type);
            this->fmt_generic_path(e.trait);
            this->fmt_name(e.item);
            this->fmt_path_params(e.params);
            }
        TU_ARMA(UfcsUnknown, e)
            BUG(Span(), "Non-encodable path " << p);
        }
    }

    // Type
    // - Tuple: 'T' <nelem> [<TypeRef> ...]
    // - Slice: 'S' <TypeRef>
    // - Array: 'A' <size> <TypeRef>
    // - Path: 'N' <Path>
    // - TraitObject: 'D' <data:GenericPath> <nmarker> [markers: <GenericPath> ...] <naty> [<TypeRef> ...]    TODO: Does this need to include the ATY name?
    // - Borrow: 'B' ('s'|'u'|'o') <TypeRef>
    // - RawPointer: 'P' ('s'|'u'|'o') <TypeRef>
    // - Function: 'F' <abi:RcString> <nargs> [args: <TypeRef> ...] <ret:TypeRef>
    // - Primitives::
    //   - u8  : 'C' 'a'
    //   - i8  : 'C' 'b'
    //   - u16 : 'C' 'c'
    //   - i16 : 'C' 'd'
    //   - u32 : 'C' 'e'
    //   - i32 : 'C' 'f'
    //   - u64 : 'C' 'g'
    //   - i64 : 'C' 'h'
    //   - u128: 'C' 'i'
    //   - i128: 'C' 'j'
    //   --
    //   - f32  : 'C' 'n'
    //   - f64  : 'C' 'o'
    //   --
    //   - usize: 'C' 'u'
    //   - isize: 'C' 'v'
    //   - bool : 'C' 'x'
    //   - char : 'C' 'x'
    //   - str  : 'C' 'y'
    // - Diverge: 'C' 'z'
    void fmt_type(const ::HIR::TypeRef& ty)
    {
        TU_MATCH_HDRA( (ty.m_data), { )
        case ::HIR::TypeRef::Data::TAG_Infer:
        case ::HIR::TypeRef::Data::TAG_Generic:
        case ::HIR::TypeRef::Data::TAG_ErasedType:
        case ::HIR::TypeRef::Data::TAG_Closure:
            BUG(Span(), "Non-encodable type " << ty);
        TU_ARMA(Tuple, e) {
            m_os << "T" << e.size();
            for(const auto& sty : e)
                this->fmt_type(sty);
            }
        TU_ARMA(Slice, e) {
            m_os << "S";
            this->fmt_type(*e.inner);
            }
        TU_ARMA(Array, e) {
            m_os << "A" << e.size_val;
            this->fmt_type(*e.inner);
            }
        TU_ARMA(Path, e) {
            m_os << "N";
            this->fmt_path(e.path);
            }
        TU_ARMA(TraitObject, e) {
            // - TraitObject: 'D' <data:GenericPath> <naty> [<TypeRef> ...] <nmarker> [markers: <GenericPath> ...]
            m_os << "D";
            this->fmt_generic_path(e.m_trait.m_path);
            m_os << e.m_trait.m_type_bounds.size();
            // HACK: Assume all TraitObject types have the same aty set (std::map is deterministic)
            for(const auto& aty : e.m_trait.m_type_bounds)
                this->fmt_type(aty.second);
            m_os << e.m_markers.size();
            for(const auto& p : e.m_markers)
                this->fmt_generic_path(p);
            }
        TU_ARMA(Function, e) {
            // - Function: 'F' <abi:RcString> <nargs> [args: <TypeRef> ...] <ret:TypeRef>
            m_os << "F";
            m_os << (e.is_unsafe ? "u" : "");    // Optional allowed, next is a number
            if( e.m_abi != ABI_RUST )
            {
                m_os << "e";
                this->fmt_name(e.m_abi.c_str());
            }
            m_os << e.m_arg_types.size();
            for(const auto& t : e.m_arg_types)
                this->fmt_type(t);
            this->fmt_type(*e.m_rettype);
            }
        TU_ARMA(Borrow, e) {
            m_os << "B";
            switch(e.type)
            {
            case ::HIR::BorrowType::Shared: m_os << "s"; break;
            case ::HIR::BorrowType::Unique: m_os << "u"; break;
            case ::HIR::BorrowType::Owned:  m_os << "o"; break;
            }
            this->fmt_type(*e.inner);
            }
        TU_ARMA(Pointer, e) {
            m_os << "P";
            switch(e.type)
            {
            case ::HIR::BorrowType::Shared: m_os << "s"; break;
            case ::HIR::BorrowType::Unique: m_os << "u"; break;
            case ::HIR::BorrowType::Owned:  m_os << "o"; break;
            }
            this->fmt_type(*e.inner);
            }
        TU_ARMA(Primitive, e) {
            switch(e)
            {
            case ::HIR::CoreType::U8  : m_os << 'C' << 'a'; break;
            case ::HIR::CoreType::I8  : m_os << 'C' << 'b'; break;
            case ::HIR::CoreType::U16 : m_os << 'C' << 'c'; break;
            case ::HIR::CoreType::I16 : m_os << 'C' << 'd'; break;
            case ::HIR::CoreType::U32 : m_os << 'C' << 'e'; break;
            case ::HIR::CoreType::I32 : m_os << 'C' << 'f'; break;
            case ::HIR::CoreType::U64 : m_os << 'C' << 'g'; break;
            case ::HIR::CoreType::I64 : m_os << 'C' << 'h'; break;
            case ::HIR::CoreType::U128: m_os << 'C' << 'i'; break;
            case ::HIR::CoreType::I128: m_os << 'C' << 'j'; break;
            case ::HIR::CoreType::F32 : m_os << 'C' << 'n'; break;
            case ::HIR::CoreType::F64 : m_os << 'C' << 'o'; break;
            case ::HIR::CoreType::Usize: m_os << 'C' << 'u'; break;
            case ::HIR::CoreType::Isize: m_os << 'C' << 'v'; break;
            case ::HIR::CoreType::Bool: m_os << 'C' << 'w'; break;
            case ::HIR::CoreType::Char: m_os << 'C' << 'x'; break;
            case ::HIR::CoreType::Str : m_os << 'C' << 'y'; break;
            }
            }
        TU_ARMA(Diverge, _e) {
            m_os << 'C' << 'z';
            }
        }
    }
};

::FmtLambda Trans_ManglePath(const ::HIR::Path& p)
{
    return FMT_CB(os, os << "ZR"; Mangler(os).fmt_path(p));
}
::FmtLambda Trans_MangleSimplePath(const ::HIR::SimplePath& p)
{
    return FMT_CB(os, os << "ZRG"; Mangler(os).fmt_simple_path(p); Mangler(os).fmt_path_params({}););
}
::FmtLambda Trans_MangleGenericPath(const ::HIR::GenericPath& p)
{
    return FMT_CB(os, os << "ZRG"; Mangler(os).fmt_generic_path(p));
}
::FmtLambda Trans_MangleTypeRef(const ::HIR::TypeRef& p)
{
    return FMT_CB(os, os << "ZRT"; Mangler(os).fmt_type(p));
}

namespace {
    ::FmtLambda max_len(::FmtLambda v) {
        std::stringstream   ss;
        ss << v;
        auto s = ss.str();
        static const size_t MAX_LEN = 128;
        if( s.size() > 128 ) {
            size_t hash = ::std::hash<std::string>()(s);
            ss.str("");
            ss << s.substr(0, MAX_LEN-9) << "$" << ::std::hex << hash;
            DEBUG("Over-long symbol '" << s << "' -> '" << ss.str() << "'");
            s = ss.str();
        }
        else {
        }
        return ::FmtLambda([=](::std::ostream& os){
            os << s;
            });
    }
}
// TODO: If the mangled name exceeds a limit, stop emitting the real name and start hashing the rest.
#define DO_MANGLE(ty) ::FmtLambda Trans_Mangle(const ::HIR::ty& v) { \
    return max_len(Trans_Mangle##ty(v)); \
}
DO_MANGLE(SimplePath)
DO_MANGLE(GenericPath)
DO_MANGLE(Path)
DO_MANGLE(TypeRef)
<|endoftext|>


<commit_before>// Copyright (c) 2013 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.

#include "components/autofill/browser/autocheckout_manager.h"

#include "base/basictypes.h"
#include "base/bind.h"
#include "base/utf_string_conversions.h"
#include "components/autofill/browser/autofill_country.h"
#include "components/autofill/browser/autofill_field.h"
#include "components/autofill/browser/autofill_manager.h"
#include "components/autofill/browser/autofill_profile.h"
#include "components/autofill/browser/credit_card.h"
#include "components/autofill/browser/field_types.h"
#include "components/autofill/browser/form_structure.h"
#include "components/autofill/common/autocheckout_status.h"
#include "components/autofill/common/autofill_messages.h"
#include "components/autofill/common/form_data.h"
#include "components/autofill/common/form_field_data.h"
#include "components/autofill/common/web_element_descriptor.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/ssl_status.h"
#include "googleurl/src/gurl.h"
#include "ui/gfx/rect.h"

using content::RenderViewHost;
using content::SSLStatus;
using content::WebContents;

namespace {

// Build FormFieldData based on the supplied |autocomplete_attribute|. Will
// fill rest of properties with default values.
FormFieldData BuildField(const std::string& autocomplete_attribute) {
  FormFieldData field;
  field.name = string16();
  field.value = string16();
  field.autocomplete_attribute = autocomplete_attribute;
  field.form_control_type = "text";
  return field;
}

// Build Autocheckout specific form data to be consumed by
// AutofillDialogController to show the Autocheckout specific UI.
FormData BuildAutocheckoutFormData() {
  FormData formdata;
  formdata.fields.push_back(BuildField("name"));
  formdata.fields.push_back(BuildField("tel"));
  formdata.fields.push_back(BuildField("email"));
  formdata.fields.push_back(BuildField("cc-name"));
  formdata.fields.push_back(BuildField("cc-number"));
  formdata.fields.push_back(BuildField("cc-exp-month"));
  formdata.fields.push_back(BuildField("cc-exp-year"));
  formdata.fields.push_back(BuildField("cc-csc"));
  formdata.fields.push_back(BuildField("billing street-address"));
  formdata.fields.push_back(BuildField("billing locality"));
  formdata.fields.push_back(BuildField("billing region"));
  formdata.fields.push_back(BuildField("billing country"));
  formdata.fields.push_back(BuildField("billing postal-code"));
  formdata.fields.push_back(BuildField("shipping street-address"));
  formdata.fields.push_back(BuildField("shipping locality"));
  formdata.fields.push_back(BuildField("shipping region"));
  formdata.fields.push_back(BuildField("shipping country"));
  formdata.fields.push_back(BuildField("shipping postal-code"));
  return formdata;
}

}  // namespace

namespace autofill {

AutocheckoutManager::AutocheckoutManager(AutofillManager* autofill_manager)
    : autofill_manager_(autofill_manager),
      autocheckout_offered_(false),
      is_autocheckout_bubble_showing_(false),
      in_autocheckout_flow_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
}

AutocheckoutManager::~AutocheckoutManager() {
}

void AutocheckoutManager::FillForms() {
  // |page_meta_data_| should have been set by OnLoadedPageMetaData.
  DCHECK(page_meta_data_);

  // Fill the forms on the page with data given by user.
  std::vector<FormData> filled_forms;
  const std::vector<FormStructure*>& form_structures =
    autofill_manager_->GetFormStructures();
  for (std::vector<FormStructure*>::const_iterator iter =
           form_structures.begin(); iter != form_structures.end(); ++iter) {
    const FormStructure& form_structure = **iter;
    FormData form = form_structure.ToFormData();
    DCHECK_EQ(form_structure.field_count(), form.fields.size());

    for (size_t i = 0; i < form_structure.field_count(); ++i) {
      const AutofillField* field = form_structure.field(i);
      SetValue(*field, &form.fields[i]);
    }

    filled_forms.push_back(form);
  }

  // Send filled forms along with proceed descriptor to renderer.
  RenderViewHost* host =
      autofill_manager_->GetWebContents()->GetRenderViewHost();
  if (!host)
    return;

  host->Send(new AutofillMsg_FillFormsAndClick(
      host->GetRoutingID(),
      filled_forms,
      *page_meta_data_->proceed_element_descriptor));
}

void AutocheckoutManager::OnLoadedPageMetaData(
    scoped_ptr<AutocheckoutPageMetaData> page_meta_data) {
  scoped_ptr<AutocheckoutPageMetaData> old_meta_data =
      page_meta_data_.Pass();
  page_meta_data_ = page_meta_data.Pass();

  // On the first page of an Autocheckout flow, when this function is called the
  // user won't have opted into the flow yet.
  if (!in_autocheckout_flow_)
    return;

  // Missing Autofill server results.
  if (!page_meta_data_) {
    in_autocheckout_flow_ = false;
  } else if (page_meta_data_->IsStartOfAutofillableFlow()) {
    // Not possible unless Autocheckout failed to proceed.
    in_autocheckout_flow_ = false;
  } else if (!page_meta_data_->IsInAutofillableFlow()) {
    // Missing Autocheckout meta data in the Autofill server results.
    in_autocheckout_flow_ = false;
  } else if (page_meta_data_->current_page_number <=
                 old_meta_data->current_page_number) {
    // Not possible unless Autocheckout failed to proceed.
    in_autocheckout_flow_ = false;
  }

  // Encountered an error during the Autocheckout flow.
  if (!in_autocheckout_flow_) {
    // TODO(ahutter): SendAutocheckoutStatus of the error.
    autofill_manager_->delegate()->OnAutocheckoutError();
    return;
  }

  // Add 1.0 since page numbers are 0-indexed.
  autofill_manager_->delegate()->UpdateProgressBar(
      (1.0 + page_meta_data_->current_page_number) /
          page_meta_data_->total_pages);
  FillForms();
  // If the current page is the last page in the flow, close the dialog.
  if (page_meta_data_->IsEndOfAutofillableFlow()) {
    // TODO(ahutter): SendAutocheckoutStatus of SUCCESS.
    autofill_manager_->delegate()->HideRequestAutocompleteDialog();
    in_autocheckout_flow_ = false;
  }
}

void AutocheckoutManager::OnFormsSeen() {
  autocheckout_offered_ = false;
}

void AutocheckoutManager::MaybeShowAutocheckoutBubble(
    const GURL& frame_url,
    const content::SSLStatus& ssl_status,
    const gfx::NativeView& native_view,
    const gfx::RectF& bounding_box) {
  if (autocheckout_offered_ ||
      is_autocheckout_bubble_showing_ ||
      !IsStartOfAutofillableFlow())
    return;

  base::Callback<void(bool)> callback = base::Bind(
      &AutocheckoutManager::MaybeShowAutocheckoutDialog,
      weak_ptr_factory_.GetWeakPtr(),
      frame_url,
      ssl_status);
  autofill_manager_->delegate()->ShowAutocheckoutBubble(
      bounding_box,
      native_view,
      callback);
  is_autocheckout_bubble_showing_ = true;
  autocheckout_offered_ = true;
}

void AutocheckoutManager::MaybeShowAutocheckoutDialog(
    const GURL& frame_url,
    const SSLStatus& ssl_status,
    bool show_dialog) {
  is_autocheckout_bubble_showing_ = false;
  if (!show_dialog)
    return;

  base::Callback<void(const FormStructure*)> callback =
      base::Bind(&AutocheckoutManager::ReturnAutocheckoutData,
                 weak_ptr_factory_.GetWeakPtr());
  autofill_manager_->ShowRequestAutocompleteDialog(
      BuildAutocheckoutFormData(), frame_url, ssl_status,
      DIALOG_TYPE_AUTOCHECKOUT, callback);
}

bool AutocheckoutManager::IsStartOfAutofillableFlow() const {
  return page_meta_data_ && page_meta_data_->IsStartOfAutofillableFlow();
}

bool AutocheckoutManager::IsInAutofillableFlow() const {
  return page_meta_data_ && page_meta_data_->IsInAutofillableFlow();
}

void AutocheckoutManager::ReturnAutocheckoutData(const FormStructure* result) {
  if (!result)
    return;

  in_autocheckout_flow_ = true;

  profile_.reset(new AutofillProfile());
  credit_card_.reset(new CreditCard());

  for (size_t i = 0; i < result->field_count(); ++i) {
    AutofillFieldType type = result->field(i)->type();
    if (type == CREDIT_CARD_VERIFICATION_CODE) {
      // TODO(ramankk): CVV is not handled by CreditCard, not sure how to
      // handle it yet.
      cvv_ = result->field(i)->value;
      continue;
    }
    if (AutofillType(type).group() == AutofillType::CREDIT_CARD) {
      credit_card_->SetRawInfo(result->field(i)->type(),
                               result->field(i)->value);
    } else {
      profile_->SetRawInfo(result->field(i)->type(), result->field(i)->value);
    }
  }

  // Add 1.0 since page numbers are 0-indexed.
  autofill_manager_->delegate()->UpdateProgressBar(
      (1.0 + page_meta_data_->current_page_number) /
          page_meta_data_->total_pages);
  FillForms();

  // If the current page is the last page in the flow, close the dialog.
  if (page_meta_data_->IsEndOfAutofillableFlow()) {
    // TODO(ahutter): SendAutocheckoutStatus of SUCCESS.
    autofill_manager_->delegate()->HideRequestAutocompleteDialog();
    in_autocheckout_flow_ = false;
  }
}

void AutocheckoutManager::SetValue(const AutofillField& field,
                                   FormFieldData* field_to_fill) {
  AutofillFieldType type = field.type();

  if (type == FIELD_WITH_DEFAULT_VALUE) {
    DCHECK(field.is_checkable);
    // For a form with radio buttons, like:
    // <form>
    //   <input type="radio" name="sex" value="male">Male<br>
    //   <input type="radio" name="sex" value="female">Female
    // </form>
    // If the default value specified at the server is "female", then
    // Autofill server responds back with following field mappings
    //   (fieldtype: FIELD_WITH_DEFAULT_VALUE, value: "female")
    //   (fieldtype: FIELD_WITH_DEFAULT_VALUE, value: "female")
    // Note that, the field mapping is repeated twice to respond to both the
    // input elements with the same name/signature in the form.
    string16 default_value = UTF8ToUTF16(field.default_value());
    // Mark the field checked if server says the default value of the field
    // to be this field's value.
    field_to_fill->is_checked = (field.value == default_value);
    return;
  }

  // Handle verification code directly.
  if (type == CREDIT_CARD_VERIFICATION_CODE) {
    field_to_fill->value = cvv_;
    return;
  }

  // TODO(ramankk): Handle variants in a better fashion, need to distinguish
  // between shipping and billing address.
  if (AutofillType(type).group() == AutofillType::CREDIT_CARD)
    credit_card_->FillFormField(field, 0, field_to_fill);
  else
    profile_->FillFormField(field, 0, field_to_fill);
}

}  // namespace autofill
<commit_msg>requestAutocomplete dialog wasn't filling out shipping name<commit_after>// Copyright (c) 2013 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.

#include "components/autofill/browser/autocheckout_manager.h"

#include "base/basictypes.h"
#include "base/bind.h"
#include "base/utf_string_conversions.h"
#include "components/autofill/browser/autofill_country.h"
#include "components/autofill/browser/autofill_field.h"
#include "components/autofill/browser/autofill_manager.h"
#include "components/autofill/browser/autofill_profile.h"
#include "components/autofill/browser/credit_card.h"
#include "components/autofill/browser/field_types.h"
#include "components/autofill/browser/form_structure.h"
#include "components/autofill/common/autocheckout_status.h"
#include "components/autofill/common/autofill_messages.h"
#include "components/autofill/common/form_data.h"
#include "components/autofill/common/form_field_data.h"
#include "components/autofill/common/web_element_descriptor.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/ssl_status.h"
#include "googleurl/src/gurl.h"
#include "ui/gfx/rect.h"

using content::RenderViewHost;
using content::SSLStatus;
using content::WebContents;

namespace {

// Build FormFieldData based on the supplied |autocomplete_attribute|. Will
// fill rest of properties with default values.
FormFieldData BuildField(const std::string& autocomplete_attribute) {
  FormFieldData field;
  field.name = string16();
  field.value = string16();
  field.autocomplete_attribute = autocomplete_attribute;
  field.form_control_type = "text";
  return field;
}

// Build Autocheckout specific form data to be consumed by
// AutofillDialogController to show the Autocheckout specific UI.
FormData BuildAutocheckoutFormData() {
  FormData formdata;
  formdata.fields.push_back(BuildField("email"));
  formdata.fields.push_back(BuildField("cc-name"));
  formdata.fields.push_back(BuildField("cc-number"));
  formdata.fields.push_back(BuildField("cc-exp-month"));
  formdata.fields.push_back(BuildField("cc-exp-year"));
  formdata.fields.push_back(BuildField("cc-csc"));
  formdata.fields.push_back(BuildField("billing street-address"));
  formdata.fields.push_back(BuildField("billing locality"));
  formdata.fields.push_back(BuildField("billing region"));
  formdata.fields.push_back(BuildField("billing country"));
  formdata.fields.push_back(BuildField("billing postal-code"));
  formdata.fields.push_back(BuildField("billing tel"));
  formdata.fields.push_back(BuildField("shipping name"));
  formdata.fields.push_back(BuildField("shipping street-address"));
  formdata.fields.push_back(BuildField("shipping locality"));
  formdata.fields.push_back(BuildField("shipping region"));
  formdata.fields.push_back(BuildField("shipping country"));
  formdata.fields.push_back(BuildField("shipping postal-code"));
  formdata.fields.push_back(BuildField("shipping tel"));
  return formdata;
}

}  // namespace

namespace autofill {

AutocheckoutManager::AutocheckoutManager(AutofillManager* autofill_manager)
    : autofill_manager_(autofill_manager),
      autocheckout_offered_(false),
      is_autocheckout_bubble_showing_(false),
      in_autocheckout_flow_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
}

AutocheckoutManager::~AutocheckoutManager() {
}

void AutocheckoutManager::FillForms() {
  // |page_meta_data_| should have been set by OnLoadedPageMetaData.
  DCHECK(page_meta_data_);

  // Fill the forms on the page with data given by user.
  std::vector<FormData> filled_forms;
  const std::vector<FormStructure*>& form_structures =
    autofill_manager_->GetFormStructures();
  for (std::vector<FormStructure*>::const_iterator iter =
           form_structures.begin(); iter != form_structures.end(); ++iter) {
    const FormStructure& form_structure = **iter;
    FormData form = form_structure.ToFormData();
    DCHECK_EQ(form_structure.field_count(), form.fields.size());

    for (size_t i = 0; i < form_structure.field_count(); ++i) {
      const AutofillField* field = form_structure.field(i);
      SetValue(*field, &form.fields[i]);
    }

    filled_forms.push_back(form);
  }

  // Send filled forms along with proceed descriptor to renderer.
  RenderViewHost* host =
      autofill_manager_->GetWebContents()->GetRenderViewHost();
  if (!host)
    return;

  host->Send(new AutofillMsg_FillFormsAndClick(
      host->GetRoutingID(),
      filled_forms,
      *page_meta_data_->proceed_element_descriptor));
}

void AutocheckoutManager::OnLoadedPageMetaData(
    scoped_ptr<AutocheckoutPageMetaData> page_meta_data) {
  scoped_ptr<AutocheckoutPageMetaData> old_meta_data =
      page_meta_data_.Pass();
  page_meta_data_ = page_meta_data.Pass();

  // On the first page of an Autocheckout flow, when this function is called the
  // user won't have opted into the flow yet.
  if (!in_autocheckout_flow_)
    return;

  // Missing Autofill server results.
  if (!page_meta_data_) {
    in_autocheckout_flow_ = false;
  } else if (page_meta_data_->IsStartOfAutofillableFlow()) {
    // Not possible unless Autocheckout failed to proceed.
    in_autocheckout_flow_ = false;
  } else if (!page_meta_data_->IsInAutofillableFlow()) {
    // Missing Autocheckout meta data in the Autofill server results.
    in_autocheckout_flow_ = false;
  } else if (page_meta_data_->current_page_number <=
                 old_meta_data->current_page_number) {
    // Not possible unless Autocheckout failed to proceed.
    in_autocheckout_flow_ = false;
  }

  // Encountered an error during the Autocheckout flow.
  if (!in_autocheckout_flow_) {
    // TODO(ahutter): SendAutocheckoutStatus of the error.
    autofill_manager_->delegate()->OnAutocheckoutError();
    return;
  }

  // Add 1.0 since page numbers are 0-indexed.
  autofill_manager_->delegate()->UpdateProgressBar(
      (1.0 + page_meta_data_->current_page_number) /
          page_meta_data_->total_pages);
  FillForms();
  // If the current page is the last page in the flow, close the dialog.
  if (page_meta_data_->IsEndOfAutofillableFlow()) {
    // TODO(ahutter): SendAutocheckoutStatus of SUCCESS.
    autofill_manager_->delegate()->HideRequestAutocompleteDialog();
    in_autocheckout_flow_ = false;
  }
}

void AutocheckoutManager::OnFormsSeen() {
  autocheckout_offered_ = false;
}

void AutocheckoutManager::MaybeShowAutocheckoutBubble(
    const GURL& frame_url,
    const content::SSLStatus& ssl_status,
    const gfx::NativeView& native_view,
    const gfx::RectF& bounding_box) {
  if (autocheckout_offered_ ||
      is_autocheckout_bubble_showing_ ||
      !IsStartOfAutofillableFlow())
    return;

  base::Callback<void(bool)> callback = base::Bind(
      &AutocheckoutManager::MaybeShowAutocheckoutDialog,
      weak_ptr_factory_.GetWeakPtr(),
      frame_url,
      ssl_status);
  autofill_manager_->delegate()->ShowAutocheckoutBubble(
      bounding_box,
      native_view,
      callback);
  is_autocheckout_bubble_showing_ = true;
  autocheckout_offered_ = true;
}

void AutocheckoutManager::MaybeShowAutocheckoutDialog(
    const GURL& frame_url,
    const SSLStatus& ssl_status,
    bool show_dialog) {
  is_autocheckout_bubble_showing_ = false;
  if (!show_dialog)
    return;

  base::Callback<void(const FormStructure*)> callback =
      base::Bind(&AutocheckoutManager::ReturnAutocheckoutData,
                 weak_ptr_factory_.GetWeakPtr());
  autofill_manager_->ShowRequestAutocompleteDialog(
      BuildAutocheckoutFormData(), frame_url, ssl_status,
      DIALOG_TYPE_AUTOCHECKOUT, callback);
}

bool AutocheckoutManager::IsStartOfAutofillableFlow() const {
  return page_meta_data_ && page_meta_data_->IsStartOfAutofillableFlow();
}

bool AutocheckoutManager::IsInAutofillableFlow() const {
  return page_meta_data_ && page_meta_data_->IsInAutofillableFlow();
}

void AutocheckoutManager::ReturnAutocheckoutData(const FormStructure* result) {
  if (!result)
    return;

  in_autocheckout_flow_ = true;

  profile_.reset(new AutofillProfile());
  credit_card_.reset(new CreditCard());

  for (size_t i = 0; i < result->field_count(); ++i) {
    AutofillFieldType type = result->field(i)->type();
    if (type == CREDIT_CARD_VERIFICATION_CODE) {
      // TODO(ramankk): CVV is not handled by CreditCard, not sure how to
      // handle it yet.
      cvv_ = result->field(i)->value;
      continue;
    }
    if (AutofillType(type).group() == AutofillType::CREDIT_CARD) {
      credit_card_->SetRawInfo(result->field(i)->type(),
                               result->field(i)->value);
    } else {
      profile_->SetRawInfo(result->field(i)->type(), result->field(i)->value);
    }
  }

  // Add 1.0 since page numbers are 0-indexed.
  autofill_manager_->delegate()->UpdateProgressBar(
      (1.0 + page_meta_data_->current_page_number) /
          page_meta_data_->total_pages);
  FillForms();

  // If the current page is the last page in the flow, close the dialog.
  if (page_meta_data_->IsEndOfAutofillableFlow()) {
    // TODO(ahutter): SendAutocheckoutStatus of SUCCESS.
    autofill_manager_->delegate()->HideRequestAutocompleteDialog();
    in_autocheckout_flow_ = false;
  }
}

void AutocheckoutManager::SetValue(const AutofillField& field,
                                   FormFieldData* field_to_fill) {
  AutofillFieldType type = field.type();

  if (type == FIELD_WITH_DEFAULT_VALUE) {
    DCHECK(field.is_checkable);
    // For a form with radio buttons, like:
    // <form>
    //   <input type="radio" name="sex" value="male">Male<br>
    //   <input type="radio" name="sex" value="female">Female
    // </form>
    // If the default value specified at the server is "female", then
    // Autofill server responds back with following field mappings
    //   (fieldtype: FIELD_WITH_DEFAULT_VALUE, value: "female")
    //   (fieldtype: FIELD_WITH_DEFAULT_VALUE, value: "female")
    // Note that, the field mapping is repeated twice to respond to both the
    // input elements with the same name/signature in the form.
    string16 default_value = UTF8ToUTF16(field.default_value());
    // Mark the field checked if server says the default value of the field
    // to be this field's value.
    field_to_fill->is_checked = (field.value == default_value);
    return;
  }

  // Handle verification code directly.
  if (type == CREDIT_CARD_VERIFICATION_CODE) {
    field_to_fill->value = cvv_;
    return;
  }

  // TODO(ramankk): Handle variants in a better fashion, need to distinguish
  // between shipping and billing address.
  if (AutofillType(type).group() == AutofillType::CREDIT_CARD)
    credit_card_->FillFormField(field, 0, field_to_fill);
  else
    profile_->FillFormField(field, 0, field_to_fill);
}

}  // namespace autofill
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbResetMarginFilter.h"
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

namespace otb
{
namespace Wrapper
{

/**
 * Application that fills margins to 0.
 *
 * This application is similar to ExtractROI with the difference the margin is
 * kept, and filled with 0.
 *
 * This application is used to implement the _cut_ processing in S1Tiling
 * chain.
 *
 * \author Luc Hermitte (CS Group)
 * \copyright CNES
 */
class ResetMargin : public Application
{
public:
  using Self    = ResetMargin;
  using Pointer = itk::SmartPointer<Self>;

  itkNewMacro(Self);
  itkTypeMacro(ResetMargin, otb::Wrapper::Application);

private:
  void DoInit() override
  {
    SetName("ResetMargin");

    SetDescription("This is the ResetMargin application");
    SetDocLongDescription(
        "This application is similar to ExtractROI in the sense it extracts a Region of Interrest.\n"
        "However, the region outside of the ROI isn't trimmed, but set to 0.\n"
        "\n"
        "The filter set lines of index < threshold.y, and of index >= threshold.y to 0\n"
        "The filter set columns of index < threshold.x, and of index >= threshold.x to 0");

    SetDocLimitations("This application only works on scalar (and complex) images.");
    SetDocAuthors("Luc Hermitte (CS Group)");
    SetDocSeeAlso("ManageNoData, ExtractROI");
    AddDocTag(Tags::Manip);

    AddParameter(ParameterType_InputImage,  "in",   "Input image");
    SetParameterDescription("in", "Scalar Input image");

    AddParameter(ParameterType_OutputImage, "out", "Output Image");
    SetParameterDescription("out", "Scalar Output image");

    AddParameter(ParameterType_Group, "threshold", "threshold group");
    AddParameter(ParameterType_Group, "threshold.y", "threshold group");
    MandatoryOff("threshold");
    MandatoryOff("threshold.y");
    AddParameter(ParameterType_Int, "threshold.x", "Column index threshold");
    SetParameterDescription("threshold.x", "Column index threshold");
    SetDefaultParameterInt("threshold.x", 0);

    AddParameter(ParameterType_Int, "threshold.y.start", "Top line index threshold");
    SetParameterDescription("threshold.y.start", "Top line index threshold");
    SetDefaultParameterInt("threshold.y.start", 0);

    AddParameter(ParameterType_Int, "threshold.y.end", "Bottom line index threshold");
    SetParameterDescription("threshold.y.end", "Bottom line index threshold");
    SetDefaultParameterInt("threshold.y.end", 0);

    SetMinimumParameterIntValue("threshold.x",       0);
    SetMinimumParameterIntValue("threshold.y.start", 0);
    SetMinimumParameterIntValue("threshold.y.end",   0);

    AddParameter(ParameterType_Group, "roi", "ROI group");
    SetParameterDescription("roi", "Contains index and size of ROI");

    AddParameter(ParameterType_Int, "roi.startx", "X start index");
    SetParameterDescription("roi.startx", "X position of ROI start (in pixels)");
    SetDefaultParameterInt("roi.startx", 0);

    AddParameter(ParameterType_Int, "roi.starty", "Y start index");
    SetParameterDescription("roi.starty", "Y position of ROI start (in pixels)");
    SetDefaultParameterInt("roi.starty", 0);

    AddParameter(ParameterType_Int, "roi.sizex", "X size");
    SetParameterDescription("roi.sizex", "X size of ROI (in pixels)");
    SetDefaultParameterInt("roi.sizex", 0);

    AddParameter(ParameterType_Int, "roi.sizey", "Y size");
    SetParameterDescription("roi.sizey", "Y size of ROI (in pixels)");
    SetDefaultParameterInt("roi.sizey", 0);

    MandatoryOff("roi.startx");
    MandatoryOff("roi.starty");
    MandatoryOff("roi.sizex");
    MandatoryOff("roi.sizey");

    AddParameter(ParameterType_Choice, "mode", "Region mode");
    AddChoice("mode.roi", "Pixel region with start and size");

    AddChoice("mode.threshold", "Threshold for X, Y top and botton (DEPRECATED)");

    AddParameter(ParameterType_Float, "val", "Padding value");
    SetParameterDescription("val", "Value to insert in margins");
    SetDefaultParameterFloat("val", 0.0);
    MandatoryOff("val");

    AddRAMParameter();

    SetDocExampleParameterValue("in", "ResetMarginInput100x100.tiff");
    SetDocExampleParameterValue("threshold.x",       "10");
    SetDocExampleParameterValue("threshold.y.start", "12");
    SetDocExampleParameterValue("threshold.y.end",    "25");
    SetDocExampleParameterValue("out", "ResetMargin.tiff");
    SetOfficialDocLink();
  }

  void DoUpdateParameters() override
  {}

  void DoExecute() override
  {
    FloatImageType* input = GetParameterFloatImage("in");
    FloatImageType::RegionType region;
    switch (GetParameterInt("mode"))
      {
      case 0: // roi
        {
        region.SetIndex({GetParameterInt("roi.startx"),GetParameterInt("roi.starty")});
        region.SetSize({(unsigned long)GetParameterInt("roi.sizex"),(unsigned long)GetParameterInt("roi.sizey")});
        break;
        }
      case 1: // threshold
        {
        region = input->GetLargestPossibleRegion();
        FloatImageType::IndexType idx = region.GetIndex();
        FloatImageType::SizeType sz = region.GetSize();
        int thrX = GetParameterInt("threshold.x");
        int thrYtop = GetParameterInt("threshold.y.start");
        int thrYbot = GetParameterInt("threshold.y.end");
        region.SetIndex(0, idx[0] + thrX);
        region.SetSize(0,  std::max(0UL, sz[0] - 2 * thrX));
        region.SetIndex(1, idx[1] + thrYtop);
        region.SetSize(1, std::max(0UL, sz[1] - thrYtop - thrYbot));
        break;
        }
      default:
        {
        otbAppLogFATAL("Unknown region mode: "<<GetParameterString("mode"));
        break;
        }
      }

    auto filter = ResetMarginFilter<FloatImageType>::New();
    filter->SetROI(region);
    filter->SetInput(input);
    filter->SetPaddingValue(GetParameterFloat("val"));

    SetParameterOutputImage("out", filter->GetOutput());
    RegisterPipeline();
  }

};

} // otb::Wrapper namespace
} // otb namespace

OTB_APPLICATION_EXPORT(otb::Wrapper::ResetMargin)
<commit_msg>ENH: ResetMargin app support vector images<commit_after>/*
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbResetMarginFilter.h"
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

namespace otb
{
namespace Wrapper
{

/**
 * Application that fills margins to 0.
 *
 * This application is similar to ExtractROI with the difference the margin is
 * kept, and filled with 0.
 *
 * This application is used to implement the _cut_ processing in S1Tiling
 * chain.
 *
 * \author Luc Hermitte (CS Group)
 * \copyright CNES
 */
class ResetMargin : public Application
{
public:
  using Self    = ResetMargin;
  using Pointer = itk::SmartPointer<Self>;

  itkNewMacro(Self);
  itkTypeMacro(ResetMargin, otb::Wrapper::Application);

private:
  void DoInit() override
  {
    SetName("ResetMargin");

    SetDescription("This is the ResetMargin application");
    SetDocLongDescription(
        "This application is similar to ExtractROI in the sense it extracts a Region of Interrest.\n"
        "However, the region outside of the ROI isn't trimmed, but set to 0.\n"
        "\n"
        "The filter set lines of index < threshold.y, and of index >= threshold.y to 0\n"
        "The filter set columns of index < threshold.x, and of index >= threshold.x to 0");

    SetDocLimitations("This application only works on scalar (and complex) images.");
    SetDocAuthors("Luc Hermitte (CS Group)");
    SetDocSeeAlso("ManageNoData, ExtractROI");
    AddDocTag(Tags::Manip);

    AddParameter(ParameterType_InputImage,  "in",   "Input image");
    SetParameterDescription("in", "Scalar Input image");

    AddParameter(ParameterType_OutputImage, "out", "Output Image");
    SetParameterDescription("out", "Scalar Output image");

    AddParameter(ParameterType_Group, "threshold", "threshold group");
    AddParameter(ParameterType_Group, "threshold.y", "threshold group");
    MandatoryOff("threshold");
    MandatoryOff("threshold.y");
    AddParameter(ParameterType_Int, "threshold.x", "Column index threshold");
    SetParameterDescription("threshold.x", "Column index threshold");
    SetDefaultParameterInt("threshold.x", 0);

    AddParameter(ParameterType_Int, "threshold.y.start", "Top line index threshold");
    SetParameterDescription("threshold.y.start", "Top line index threshold");
    SetDefaultParameterInt("threshold.y.start", 0);

    AddParameter(ParameterType_Int, "threshold.y.end", "Bottom line index threshold");
    SetParameterDescription("threshold.y.end", "Bottom line index threshold");
    SetDefaultParameterInt("threshold.y.end", 0);

    SetMinimumParameterIntValue("threshold.x",       0);
    SetMinimumParameterIntValue("threshold.y.start", 0);
    SetMinimumParameterIntValue("threshold.y.end",   0);

    AddParameter(ParameterType_Group, "roi", "ROI group");
    SetParameterDescription("roi", "Contains index and size of ROI");

    AddParameter(ParameterType_Int, "roi.startx", "X start index");
    SetParameterDescription("roi.startx", "X position of ROI start (in pixels)");
    SetDefaultParameterInt("roi.startx", 0);

    AddParameter(ParameterType_Int, "roi.starty", "Y start index");
    SetParameterDescription("roi.starty", "Y position of ROI start (in pixels)");
    SetDefaultParameterInt("roi.starty", 0);

    AddParameter(ParameterType_Int, "roi.sizex", "X size");
    SetParameterDescription("roi.sizex", "X size of ROI (in pixels)");
    SetDefaultParameterInt("roi.sizex", 0);

    AddParameter(ParameterType_Int, "roi.sizey", "Y size");
    SetParameterDescription("roi.sizey", "Y size of ROI (in pixels)");
    SetDefaultParameterInt("roi.sizey", 0);

    MandatoryOff("roi.startx");
    MandatoryOff("roi.starty");
    MandatoryOff("roi.sizex");
    MandatoryOff("roi.sizey");

    AddParameter(ParameterType_Choice, "mode", "Region mode");
    AddChoice("mode.roi", "Pixel region with start and size");

    AddChoice("mode.threshold", "Threshold for X, Y top and botton (DEPRECATED)");

    AddParameter(ParameterType_Float, "val", "Padding value");
    SetParameterDescription("val", "Value to insert in margins");
    SetDefaultParameterFloat("val", 0.0);
    MandatoryOff("val");

    AddRAMParameter();

    SetDocExampleParameterValue("in", "ResetMarginInput100x100.tiff");
    SetDocExampleParameterValue("threshold.x",       "10");
    SetDocExampleParameterValue("threshold.y.start", "12");
    SetDocExampleParameterValue("threshold.y.end",    "25");
    SetDocExampleParameterValue("out", "ResetMargin.tiff");
    SetOfficialDocLink();
  }

  void DoUpdateParameters() override
  {}

  void DoExecute() override
  {
    FloatVectorImageType* input = GetParameterFloatVectorImage("in");
    FloatVectorImageType::RegionType region;
    switch (GetParameterInt("mode"))
      {
      case 0: // roi
        {
        region.SetIndex({GetParameterInt("roi.startx"),GetParameterInt("roi.starty")});
        region.SetSize({(unsigned long)GetParameterInt("roi.sizex"),(unsigned long)GetParameterInt("roi.sizey")});
        break;
        }
      case 1: // threshold
        {
        region = input->GetLargestPossibleRegion();
        FloatVectorImageType::IndexType idx = region.GetIndex();
        FloatVectorImageType::SizeType sz = region.GetSize();
        int thrX = GetParameterInt("threshold.x");
        int thrYtop = GetParameterInt("threshold.y.start");
        int thrYbot = GetParameterInt("threshold.y.end");
        region.SetIndex(0, idx[0] + thrX);
        region.SetSize(0,  std::max(0UL, sz[0] - 2 * thrX));
        region.SetIndex(1, idx[1] + thrYtop);
        region.SetSize(1, std::max(0UL, sz[1] - thrYtop - thrYbot));
        break;
        }
      default:
        {
        otbAppLogFATAL("Unknown region mode: "<<GetParameterString("mode"));
        break;
        }
      }

    auto filter = ResetMarginFilter<FloatVectorImageType>::New();
    filter->SetROI(region);
    filter->SetInput(input);
    filter->SetPaddingValue(GetParameterFloat("val"));

    SetParameterOutputImage("out", filter->GetOutput());
    RegisterPipeline();
  }

};

} // otb::Wrapper namespace
} // otb namespace

OTB_APPLICATION_EXPORT(otb::Wrapper::ResetMargin)
<|endoftext|>