<commit_before>/*
 * Copyright (C) 2010, 2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <algorithm>

#include <kernel/userver.hh>
#include <object/symbols.hh>
#include <runner/job.hh>
#include <urbi/object/event.hh>
#include <urbi/object/event-handler.hh>
#include <urbi/object/lobby.hh>
#include <urbi/sdk.hh>

#include <eval/call.hh>
#include <eval/exec.hh>
#include <eval/send-message.hh>

#include <urbi/runner/raise.hh>

GD_CATEGORY(Urbi.Event);

namespace urbi
{
  namespace object
  {

    /*---------.
    | Waiter.  |
    `---------*/

    inline
    Event::Waiter::Waiter(rTag ct, runner::Job* r, rObject& p)
      : controlTag(ct), runner(r), pattern(p)
    {}

    /*--------.
    | Event.  |
    `--------*/

    Event::Event()
    {
      proto_add(proto);
      slot_set_value(SYMBOL(active), to_urbi(false));
    }

    Event::Event(rEvent model)
      : waiters_()
      , callbacks_(model->callbacks_)
    {
      proto_add(model);
      slot_set_value(SYMBOL(active), to_urbi(false));
    }

    URBI_CXX_OBJECT_INIT(Event)
    {
      BIND(hasSubscribers);
      BIND(subscribe);
      BIND(subscribers, callbacks_);
      BIND(onEvent, onEvent, on_event_type);
      BIND(waituntil);
      BIND_VARIADIC(emit);
      BIND_VARIADIC(syncEmit);
      BIND_VARIADIC(syncTrigger);
      BIND_VARIADIC(trigger);
    }

    Event::~Event()
    {
      destructed_();
    }

    /*------------.
    | Callbacks.  |
    `------------*/

    Event::signal_type&
    Event::destructed_get()
    {
      return destructed_;
    }

    Event::signal_type&
    Event::subscribed_get()
    {
      return subscribed_;
    }

    Event::signal_type&
    Event::unsubscribed_get()
    {
      return unsubscribed_;
    }

    void
    Event::subscribe(rSubscription s)
    {
      aver(s);
      GD_FINFO_TRACE("%s: New subscription %s", this, s);
      callbacks_ << s;
      if (slot_has(SYMBOL(onSubscribe)))
        slot_get_value(SYMBOL(onSubscribe))->call(SYMBOL(syncEmit));
    }

    rSubscription
    Event::onEvent(const callback_type& cb)
    {
      aver(!cb.empty());
      rSubscription res = new Subscription(new callback_type(cb));
      res->asynchronous_ = false;
      callbacks_ << res;
      if (slot_has(SYMBOL(onSubscribe)))
        slot_get_value(SYMBOL(onSubscribe))->call(SYMBOL(syncEmit));
      return res;
    }

    /*-----------------.
    | Urbi functions.  |
    `-----------------*/

    void
    Event::onEvent(rExecutable guard, rExecutable enter, rExecutable leave,
                   bool sync)
    {
      rSubscription actions(new Subscription(this, guard, enter, leave, sync));
      GD_FPUSH_TRACE("%s: New registration %s.", this, actions);
      runner::Job& r = ::kernel::runner();
      actions->call_stack = r.state.call_stack_get();
      actions->call_stack <<
        std::make_pair(SYMBOL(MINUS_MINUS_MINUS_MINUS_SP_event_SP_handler_SP_backtrace_COLON),
                       boost::optional<ast::loc>());

      actions->profile = r.profile_get();
      actions->tag_stack = r.state.tag_stack_get();
      actions->lobby = r.state.lobby_get();
      foreach (object::rTag& tag, actions->tag_stack)
      {
        GD_FINFO_DEBUG("%s: Hook tag %s.", this, tag->name());
        sched::rTag t = tag->value_get();
        using boost::bind;
        actions->connections
          << t->stop_hook_get().connect(
            bind(&Subscription::unregister, actions))
          << t->freeze_hook_get().connect(
            bind(&Subscription::freeze, actions))
          << t->unfreeze_hook_get().connect(
            bind(&Subscription::unfreeze, actions));
      }

      callbacks_ << actions;
      foreach (const actives_type::value_type& active, active_)
      {
        objects_type args;
        args << this << this << active->payload();
        rObject pattern = nil_class;
        if (actions->guard)
        {
          pattern = (*actions->guard)(args);
          if (pattern == void_class)
            continue;
        }
        args << pattern;
        if (actions->leave_)
          active->register_stop_job(EventHandler::stop_job_type(actions, args, true));
        active->trigger_job(actions, true, args);
      }
      if (slot_has(SYMBOL(onSubscribe)))
        slot_get_value(SYMBOL(onSubscribe))->call(SYMBOL(syncEmit));
      subscribed_();
    }

    void
    Event::waituntil(rObject pattern)
    {
      if (slot_has(SYMBOL(onSubscribe)))
        slot_get_value(SYMBOL(onSubscribe))->call(SYMBOL(syncEmit));
      // Check whether there's already a matching instance.
      foreach (const actives_type::value_type& active, active_)
        if (pattern == nil_class
            || pattern->call(SYMBOL(match), active->payload())->as_bool())
          return;

      runner::Job& r = ::kernel::runner();
      rTag t(new Tag);
      waiters_ << Waiter(t, &r, pattern);
      libport::Finally f;
      r.state.apply_tag(t, &f);
      f << boost::bind(&Event::waituntil_remove, this, t);
      t->freeze(); // Will yield.
    }

    void
    Event::waituntil_remove(rTag what)
    {
      for(unsigned i=0; i < waiters_.size(); ++i)
        if (waiters_[i].controlTag == what)
        {
          waiters_[i] = waiters_[waiters_.size()-1];
          waiters_.pop_back();
          return;
        }
    }

    rEvent
    Event::source()
    {
      rObject proto = protos_get_first();
      return from_urbi<rEvent>(proto);
    }

    /*-------.
    | Emit.  |
    `-------*/

    runner::rJob
    Event::spawn_actions_job(rLobby lobby, const call_stack_type& stack,
                             rExecutable e,
                             rProfile profile, const objects_type& args)
    {
      runner::Job& r = ::kernel::runner();
      if (!lobby)
        lobby = r.state.lobby_get();
      runner::Job* res =
        e->make_job(lobby, r.scheduler_get(), args, SYMBOL(event));
      // Append the back-trace of the event handler (the "at") below
      // that of the emission back trace.
      res->state
        .call_stack_get()
        .insert(res->state.call_stack_get().begin(),
                stack.begin(), stack.end());
      if (profile)
        res->profile_start(profile, SYMBOL(event), e.get());
      return res;
    }

    void
    Event::emit_backend(const objects_type& pl, bool detach, EventHandler* h)
    {
      GD_FPUSH_TRACE("%s: Emit, %s subscribers.", this, callbacks_.size());
      rList payload = new List(pl);
      if (!h)
        slot_update(SYMBOL(active), to_urbi(false));
      waituntil_release(payload);
      bool empty = callbacks_.empty();
      objects_type apl;
      callbacks_type cbcopy;
      // Copy active subscriptions, cleanup list
      for (unsigned i= 0; i<callbacks_.size(); ++i)
      {
        rSubscription& s = callbacks_[i];
        if (s->disconnected_get())
        {
          callbacks_[i] = callbacks_[callbacks_.size()-1];
          callbacks_.pop_back();
          --i;
        }
        else
          cbcopy << s;
      }
      // List was empty and we didn't notice.
      if (!empty && callbacks_.empty())
      {
        GD_INFO_TRACE("No more subscribers, calling unsubscribed");
        unsubscribed_();
      }
      GD_FINFO_TRACE("%s subscribers after cleanup", callbacks_.size());
      for (unsigned i = 0; i < cbcopy.size(); ++i)
      {
        rSubscription s = cbcopy[i];
        aver(s);
        libport::utime_t now = kernel::server().getTime();
        GD_FPUSH_TRACE
          ("Considering %s, mi %s(%s), lc %s(%s), now %s, enabl %s, async %s",
           s,
           s->minInterval_, libport::seconds_to_utime(s->minInterval_),
           s->lastCall_, libport::seconds_to_utime(s->lastCall_),
           now,
           s->enabled_,
           s->asynchronous_);
        if (s->disconnected_get())
        {
          GD_INFO_TRACE("Subscriber is disconnected");
          // For removal we just swap with the last for better performances.
          cbcopy[i] = cbcopy[cbcopy.size()-1];
          cbcopy.pop_back();
            --i;
        }
        else if (s->enabled_
                 && now - libport::seconds_to_utime(s->lastCall_) >=
                 libport::seconds_to_utime(s->minInterval_)
                 && (!s->maxParallelEvents_
                     || s->maxParallelEvents_ > s->processing_))
        {
          // FIXME CRAP if we honor the event emit sync/at sync rule, no way
          // to catch changed asynchronously
          bool async =
            (s->event_ && (detach && s->asynchronous_get()))
            || (!s->event_ && (detach || s->asynchronous_get()));
          GD_FINFO_TRACE("Subscriber is live for notification"
                         " (cb: %s e: %s), async: %s",
                         s->cb_, s->event_, async);
          if (s->frozen)
          {
            GD_FINFO_TRACE("%s: Skip frozen registration %s.", this, s);
            continue;
          }
          objects_type args;
          args << this << this << payload;
          rObject pattern = nil_class;
          if (s->guard)
          {
            pattern = (*s->guard)(args);
            if (pattern == void_class)
            {
              GD_FINFO_TRACE("%s: Skip patters mismatch %s.", this, s);
              continue;
            }
          }
          args << pattern;
          if (h && s->leave_)
            h->register_stop_job(EventHandler::stop_job_type(s, args, detach));
          if (async)
          {
            // If we create a job, it can die before executing a single line
            // of code.
            // To avoid any race condition, we just create the job without
            // touching any stat or holding any lock.
            eval::Action a =
              eval::exec(boost::bind(&Subscription::run_sync,
                                     s, this, pl, h, detach, false, args),
                         this);
            rLobby l =
              s->lobby ? s->lobby.get()
              : kernel::runner().state.lobby_get();
            runner::rJob j =
              new runner::Job(l, kernel::runner().scheduler_get());
            j->set_action(a);
            j->state.tag_stack_set(s->tag_stack);
            GD_FINFO_DUMP("Subscriber will run in job %s", j);
            j->start_job();
          }
          else
            s->run_sync(this, pl, h, detach, true, args);
        }
      }
    }

    void
    Event::syncEmit(const objects_type& pl)
    {
      emit_backend(pl, false);
    }

    void
    Event::emit(const objects_type& args)
    {
      emit_backend(args, true);
    }

    void
    Event::emit()
    {
      emit(objects_type());
    }

    /*----------.
    | Trigger.  |
    `----------*/

    rEventHandler
    Event::trigger_backend(const objects_type& pl, bool detach)
    {
      rList payload = new List(pl);
      rEventHandler handler = new EventHandler(this, payload);
      waituntil_release(payload);
      handler->trigger(detach);
      return handler;
    }

    rEventHandler
    Event::syncTrigger(const objects_type& pl)
    {
      return trigger_backend(pl, false);
    }

    rEventHandler
    Event::trigger(const objects_type& pl)
    {
      return trigger_backend(pl, true);
    }

    void
    Event::waituntil_release(rObject payload)
    {
      // This iteration needs to remove some elements as it goes.
      for (unsigned i = 0; i < waiters_.size(); )
      {
	Waiter& waiter = waiters_[i];
	if (waiter.pattern == nil_class
            || waiter.pattern->call(SYMBOL(match), payload)->as_bool())
	{
          // Check if any tag is frozen beside the first one.
          bool frozen = false;
          foreach (const rTag& t, waiter.runner->state.tag_stack_get_all())
            if (t != waiter.controlTag && t->frozen())
            {
              frozen = true;
              break;
            }
          if (!frozen) // Do not trigger a frozen at.
          {
            waiter.controlTag->unfreeze();
            // Yes this is also valid for the last element.
            waiters_[i] = waiters_[waiters_.size()-1];
            waiters_.pop_back();
          }
	}
	else
	  ++i;
      }
    }

    bool
    Event::hasSubscribers() const
    {
      return !waiters_.empty() || !callbacks_.empty();
    }

  }
}
<commit_msg>event: fix frozen waituntil.<commit_after>/*
 * Copyright (C) 2010, 2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <algorithm>

#include <kernel/userver.hh>
#include <object/symbols.hh>
#include <runner/job.hh>
#include <urbi/object/event.hh>
#include <urbi/object/event-handler.hh>
#include <urbi/object/lobby.hh>
#include <urbi/sdk.hh>

#include <eval/call.hh>
#include <eval/exec.hh>
#include <eval/send-message.hh>

#include <urbi/runner/raise.hh>

GD_CATEGORY(Urbi.Event);

namespace urbi
{
  namespace object
  {

    /*---------.
    | Waiter.  |
    `---------*/

    inline
    Event::Waiter::Waiter(rTag ct, runner::Job* r, rObject& p)
      : controlTag(ct), runner(r), pattern(p)
    {}

    /*--------.
    | Event.  |
    `--------*/

    Event::Event()
    {
      proto_add(proto);
      slot_set_value(SYMBOL(active), to_urbi(false));
    }

    Event::Event(rEvent model)
      : waiters_()
      , callbacks_(model->callbacks_)
    {
      proto_add(model);
      slot_set_value(SYMBOL(active), to_urbi(false));
    }

    URBI_CXX_OBJECT_INIT(Event)
    {
      BIND(hasSubscribers);
      BIND(subscribe);
      BIND(subscribers, callbacks_);
      BIND(onEvent, onEvent, on_event_type);
      BIND(waituntil);
      BIND_VARIADIC(emit);
      BIND_VARIADIC(syncEmit);
      BIND_VARIADIC(syncTrigger);
      BIND_VARIADIC(trigger);
    }

    Event::~Event()
    {
      destructed_();
    }

    /*------------.
    | Callbacks.  |
    `------------*/

    Event::signal_type&
    Event::destructed_get()
    {
      return destructed_;
    }

    Event::signal_type&
    Event::subscribed_get()
    {
      return subscribed_;
    }

    Event::signal_type&
    Event::unsubscribed_get()
    {
      return unsubscribed_;
    }

    void
    Event::subscribe(rSubscription s)
    {
      aver(s);
      GD_FINFO_TRACE("%s: New subscription %s", this, s);
      callbacks_ << s;
      if (slot_has(SYMBOL(onSubscribe)))
        slot_get_value(SYMBOL(onSubscribe))->call(SYMBOL(syncEmit));
    }

    rSubscription
    Event::onEvent(const callback_type& cb)
    {
      aver(!cb.empty());
      rSubscription res = new Subscription(new callback_type(cb));
      res->asynchronous_ = false;
      callbacks_ << res;
      if (slot_has(SYMBOL(onSubscribe)))
        slot_get_value(SYMBOL(onSubscribe))->call(SYMBOL(syncEmit));
      return res;
    }

    /*-----------------.
    | Urbi functions.  |
    `-----------------*/

    void
    Event::onEvent(rExecutable guard, rExecutable enter, rExecutable leave,
                   bool sync)
    {
      rSubscription actions(new Subscription(this, guard, enter, leave, sync));
      GD_FPUSH_TRACE("%s: New registration %s.", this, actions);
      runner::Job& r = ::kernel::runner();
      actions->call_stack = r.state.call_stack_get();
      actions->call_stack <<
        std::make_pair(SYMBOL(MINUS_MINUS_MINUS_MINUS_SP_event_SP_handler_SP_backtrace_COLON),
                       boost::optional<ast::loc>());

      actions->profile = r.profile_get();
      actions->tag_stack = r.state.tag_stack_get();
      actions->lobby = r.state.lobby_get();
      foreach (object::rTag& tag, actions->tag_stack)
      {
        GD_FINFO_DEBUG("%s: Hook tag %s.", this, tag->name());
        sched::rTag t = tag->value_get();
        using boost::bind;
        actions->connections
          << t->stop_hook_get().connect(
            bind(&Subscription::unregister, actions))
          << t->freeze_hook_get().connect(
            bind(&Subscription::freeze, actions))
          << t->unfreeze_hook_get().connect(
            bind(&Subscription::unfreeze, actions));
      }

      callbacks_ << actions;
      foreach (const actives_type::value_type& active, active_)
      {
        objects_type args;
        args << this << this << active->payload();
        rObject pattern = nil_class;
        if (actions->guard)
        {
          pattern = (*actions->guard)(args);
          if (pattern == void_class)
            continue;
        }
        args << pattern;
        if (actions->leave_)
          active->register_stop_job(EventHandler::stop_job_type(actions, args, true));
        active->trigger_job(actions, true, args);
      }
      if (slot_has(SYMBOL(onSubscribe)))
        slot_get_value(SYMBOL(onSubscribe))->call(SYMBOL(syncEmit));
      subscribed_();
    }

    void
    Event::waituntil(rObject pattern)
    {
      if (slot_has(SYMBOL(onSubscribe)))
        slot_get_value(SYMBOL(onSubscribe))->call(SYMBOL(syncEmit));
      // Check whether there's already a matching instance.
      foreach (const actives_type::value_type& active, active_)
        if (pattern == nil_class
            || pattern->call(SYMBOL(match), active->payload())->as_bool())
          return;

      runner::Job& r = ::kernel::runner();
      rTag t(new Tag);
      waiters_ << Waiter(t, &r, pattern);
      libport::Finally f;
      r.state.apply_tag(t, &f);
      f << boost::bind(&Event::waituntil_remove, this, t);
      t->freeze(); // Will yield.
    }

    void
    Event::waituntil_remove(rTag what)
    {
      for(unsigned i=0; i < waiters_.size(); ++i)
        if (waiters_[i].controlTag == what)
        {
          waiters_[i] = waiters_[waiters_.size()-1];
          waiters_.pop_back();
          return;
        }
    }

    rEvent
    Event::source()
    {
      rObject proto = protos_get_first();
      return from_urbi<rEvent>(proto);
    }

    /*-------.
    | Emit.  |
    `-------*/

    runner::rJob
    Event::spawn_actions_job(rLobby lobby, const call_stack_type& stack,
                             rExecutable e,
                             rProfile profile, const objects_type& args)
    {
      runner::Job& r = ::kernel::runner();
      if (!lobby)
        lobby = r.state.lobby_get();
      runner::Job* res =
        e->make_job(lobby, r.scheduler_get(), args, SYMBOL(event));
      // Append the back-trace of the event handler (the "at") below
      // that of the emission back trace.
      res->state
        .call_stack_get()
        .insert(res->state.call_stack_get().begin(),
                stack.begin(), stack.end());
      if (profile)
        res->profile_start(profile, SYMBOL(event), e.get());
      return res;
    }

    void
    Event::emit_backend(const objects_type& pl, bool detach, EventHandler* h)
    {
      GD_FPUSH_TRACE("%s: Emit, %s subscribers.", this, callbacks_.size());
      rList payload = new List(pl);
      if (!h)
        slot_update(SYMBOL(active), to_urbi(false));
      waituntil_release(payload);
      bool empty = callbacks_.empty();
      objects_type apl;
      callbacks_type cbcopy;
      // Copy active subscriptions, cleanup list
      for (unsigned i= 0; i<callbacks_.size(); ++i)
      {
        rSubscription& s = callbacks_[i];
        if (s->disconnected_get())
        {
          callbacks_[i] = callbacks_[callbacks_.size()-1];
          callbacks_.pop_back();
          --i;
        }
        else
          cbcopy << s;
      }
      // List was empty and we didn't notice.
      if (!empty && callbacks_.empty())
      {
        GD_INFO_TRACE("No more subscribers, calling unsubscribed");
        unsubscribed_();
      }
      GD_FINFO_TRACE("%s subscribers after cleanup", callbacks_.size());
      for (unsigned i = 0; i < cbcopy.size(); ++i)
      {
        rSubscription s = cbcopy[i];
        aver(s);
        libport::utime_t now = kernel::server().getTime();
        GD_FPUSH_TRACE
          ("Considering %s, mi %s(%s), lc %s(%s), now %s, enabl %s, async %s",
           s,
           s->minInterval_, libport::seconds_to_utime(s->minInterval_),
           s->lastCall_, libport::seconds_to_utime(s->lastCall_),
           now,
           s->enabled_,
           s->asynchronous_);
        if (s->disconnected_get())
        {
          GD_INFO_TRACE("Subscriber is disconnected");
          // For removal we just swap with the last for better performances.
          cbcopy[i] = cbcopy[cbcopy.size()-1];
          cbcopy.pop_back();
            --i;
        }
        else if (s->enabled_
                 && now - libport::seconds_to_utime(s->lastCall_) >=
                 libport::seconds_to_utime(s->minInterval_)
                 && (!s->maxParallelEvents_
                     || s->maxParallelEvents_ > s->processing_))
        {
          // FIXME CRAP if we honor the event emit sync/at sync rule, no way
          // to catch changed asynchronously
          bool async =
            (s->event_ && (detach && s->asynchronous_get()))
            || (!s->event_ && (detach || s->asynchronous_get()));
          GD_FINFO_TRACE("Subscriber is live for notification"
                         " (cb: %s e: %s), async: %s",
                         s->cb_, s->event_, async);
          if (s->frozen)
          {
            GD_FINFO_TRACE("%s: Skip frozen registration %s.", this, s);
            continue;
          }
          objects_type args;
          args << this << this << payload;
          rObject pattern = nil_class;
          if (s->guard)
          {
            pattern = (*s->guard)(args);
            if (pattern == void_class)
            {
              GD_FINFO_TRACE("%s: Skip patters mismatch %s.", this, s);
              continue;
            }
          }
          args << pattern;
          if (h && s->leave_)
            h->register_stop_job(EventHandler::stop_job_type(s, args, detach));
          if (async)
          {
            // If we create a job, it can die before executing a single line
            // of code.
            // To avoid any race condition, we just create the job without
            // touching any stat or holding any lock.
            eval::Action a =
              eval::exec(boost::bind(&Subscription::run_sync,
                                     s, this, pl, h, detach, false, args),
                         this);
            rLobby l =
              s->lobby ? s->lobby.get()
              : kernel::runner().state.lobby_get();
            runner::rJob j =
              new runner::Job(l, kernel::runner().scheduler_get());
            j->set_action(a);
            j->state.tag_stack_set(s->tag_stack);
            GD_FINFO_DUMP("Subscriber will run in job %s", j);
            j->start_job();
          }
          else
            s->run_sync(this, pl, h, detach, true, args);
        }
      }
    }

    void
    Event::syncEmit(const objects_type& pl)
    {
      emit_backend(pl, false);
    }

    void
    Event::emit(const objects_type& args)
    {
      emit_backend(args, true);
    }

    void
    Event::emit()
    {
      emit(objects_type());
    }

    /*----------.
    | Trigger.  |
    `----------*/

    rEventHandler
    Event::trigger_backend(const objects_type& pl, bool detach)
    {
      rList payload = new List(pl);
      rEventHandler handler = new EventHandler(this, payload);
      waituntil_release(payload);
      handler->trigger(detach);
      return handler;
    }

    rEventHandler
    Event::syncTrigger(const objects_type& pl)
    {
      return trigger_backend(pl, false);
    }

    rEventHandler
    Event::trigger(const objects_type& pl)
    {
      return trigger_backend(pl, true);
    }

    void
    Event::waituntil_release(rObject payload)
    {
      // This iteration needs to remove some elements as it goes.
      for (unsigned i = 0; i < waiters_.size(); )
      {
	Waiter& waiter = waiters_[i];
	if (waiter.pattern == nil_class
            || waiter.pattern->call(SYMBOL(match), payload)->as_bool())
	{
          // Check if any tag is frozen beside the first one.
          bool frozen = false;
          foreach (const rTag& t, waiter.runner->state.tag_stack_get_all())
            if (t != waiter.controlTag && t->frozen())
            {
              frozen = true;
              ++i;
              break;
            }
          if (!frozen) // Do not trigger a frozen at.
          {
            waiter.controlTag->unfreeze();
            // Yes this is also valid for the last element.
            waiters_[i] = waiters_[waiters_.size()-1];
            waiters_.pop_back();
          }
	}
	else
	  ++i;
      }
    }

    bool
    Event::hasSubscribers() const
    {
      return !waiters_.empty() || !callbacks_.empty();
    }

  }
}
<|endoftext|>


<commit_before>#include "onig-scanner.h"
#include "onig-reg-exp.h"
#include "onig-result.h"
#include "unicode-utils.h"

using ::v8::Function;
using ::v8::FunctionTemplate;
using ::v8::HandleScope;
using ::v8::Local;
using ::v8::Null;

void OnigScanner::Init(Handle<Object> target) {
  Local<FunctionTemplate> tpl = FunctionTemplate::New(OnigScanner::New);
  tpl->SetClassName(String::NewSymbol("OnigScanner"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  tpl->PrototypeTemplate()->Set(String::NewSymbol("findNextMatch"), FunctionTemplate::New(OnigScanner::FindNextMatch)->GetFunction());

  target->Set(String::NewSymbol("OnigScanner"), tpl->GetFunction());
}

NODE_MODULE(onig_scanner, OnigScanner::Init)

NAN_METHOD(OnigScanner::New) {
  NanScope();
  OnigScanner* scanner = new OnigScanner(Local<Array>::Cast(args[0]));
  scanner->Wrap(args.This());
  NanReturnUndefined();
}

NAN_METHOD(OnigScanner::FindNextMatch) {
  NanScope();
  OnigScanner* scanner = node::ObjectWrap::Unwrap<OnigScanner>(args.This());
  NanReturnValue(scanner->FindNextMatch(Local<String>::Cast(args[0]), Local<Number>::Cast(args[1]), args.This()));
}

OnigScanner::OnigScanner(Handle<Array> sources) {
  int length = sources->Length();
  regExps.resize(length);
  cachedResults.resize(length);

  for (int i = 0; i < length; i++) {
    String::Utf8Value utf8Value(sources->Get(i));
    regExps[i] = shared_ptr<OnigRegExp>(new OnigRegExp(string(*utf8Value)));
  }
}

OnigScanner::~OnigScanner() {}

Handle<Value> OnigScanner::FindNextMatch(Handle<String> v8String, Handle<Number> v8StartLocation, Handle<Value> v8Scanner) {
  String::Utf8Value utf8Value(v8String);
  string string(*utf8Value);
  int charOffset = v8StartLocation->Value();

#ifdef _WIN32
  String::Value utf16Value(v8String);
  int byteOffset = UnicodeUtils::bytes_in_characters(
      reinterpret_cast<const wchar_t*>(*utf16Value), charOffset);
#else
  int byteOffset = UnicodeUtils::bytes_in_characters(*utf8Value, charOffset);
#endif
  int bestIndex = -1;
  int bestLocation = 0;
  OnigResult* bestResult = NULL;

  bool useCachedResults = (string == lastMatchedString && byteOffset >= lastStartLocation);
  lastStartLocation = byteOffset;

  if (!useCachedResults) {
    ClearCachedResults();
    lastMatchedString = string;
  }

  vector< shared_ptr<OnigRegExp> >::iterator iter = regExps.begin();
  int index = 0;
  while (iter < regExps.end()) {
    OnigRegExp *regExp = (*iter).get();

    bool useCachedResult = false;
    shared_ptr<OnigResult> result;

    if (useCachedResults && index <= maxCachedIndex) {
      result = cachedResults[index];
      useCachedResult = (!result || result->LocationAt(0) >= charOffset);
    }

    if (!useCachedResult) {
      result = regExp->Search(string, byteOffset);
      cachedResults[index] = result;
      maxCachedIndex = index;
    }

    if (result && result->Count() > 0) {
      int location = result->LocationAt(0);
      if (bestIndex == -1 || location < bestLocation) {
        bestLocation = location;
        bestResult = result.get();
        bestIndex = index;
      }

      if (location == charOffset) {
        break;
      }
    }

    ++iter;
    index++;
  }

  if (bestIndex >= 0) {
    Local<Object> result = Object::New();
    result->Set(String::NewSymbol("index"), Number::New(bestIndex));
    result->Set(String::NewSymbol("captureIndices"), CaptureIndicesForMatch(bestResult));
    result->Set(String::NewSymbol("scanner"), v8Scanner);
    return result;
  } else {
    return Null();
  }
}

void OnigScanner::ClearCachedResults() {
  maxCachedIndex = -1;
  cachedResults.clear();
}

Handle<Value> OnigScanner::CaptureIndicesForMatch(OnigResult* result) {
  int resultCount = result->Count();
  Local<Array> captures = Array::New(resultCount);
  for (int index = 0; index < resultCount; index++) {
    int captureLength = result->LengthAt(index);
    int captureStart = result->LocationAt(index);

    Local<Object> capture = Object::New();
    capture->Set(String::NewSymbol("index"), Number::New(index));
    capture->Set(String::NewSymbol("start"), Number::New(captureStart));
    capture->Set(String::NewSymbol("end"), Number::New(captureStart + captureLength));
    capture->Set(String::NewSymbol("length"), Number::New(captureLength));
    captures->Set(index, capture);
  }

  return captures;
}
<commit_msg>Actually use cache.<commit_after>#include "onig-scanner.h"
#include "onig-reg-exp.h"
#include "onig-result.h"
#include "unicode-utils.h"

using ::v8::Function;
using ::v8::FunctionTemplate;
using ::v8::HandleScope;
using ::v8::Local;
using ::v8::Null;

void OnigScanner::Init(Handle<Object> target) {
  Local<FunctionTemplate> tpl = FunctionTemplate::New(OnigScanner::New);
  tpl->SetClassName(String::NewSymbol("OnigScanner"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  tpl->PrototypeTemplate()->Set(String::NewSymbol("findNextMatch"), FunctionTemplate::New(OnigScanner::FindNextMatch)->GetFunction());

  target->Set(String::NewSymbol("OnigScanner"), tpl->GetFunction());
}

NODE_MODULE(onig_scanner, OnigScanner::Init)

NAN_METHOD(OnigScanner::New) {
  NanScope();
  OnigScanner* scanner = new OnigScanner(Local<Array>::Cast(args[0]));
  scanner->Wrap(args.This());
  NanReturnUndefined();
}

NAN_METHOD(OnigScanner::FindNextMatch) {
  NanScope();
  OnigScanner* scanner = node::ObjectWrap::Unwrap<OnigScanner>(args.This());
  NanReturnValue(scanner->FindNextMatch(Local<String>::Cast(args[0]), Local<Number>::Cast(args[1]), args.This()));
}

OnigScanner::OnigScanner(Handle<Array> sources) {
  int length = sources->Length();
  regExps.resize(length);
  cachedResults.resize(length);

  for (int i = 0; i < length; i++) {
    String::Utf8Value utf8Value(sources->Get(i));
    regExps[i] = shared_ptr<OnigRegExp>(new OnigRegExp(string(*utf8Value)));
  }
}

OnigScanner::~OnigScanner() {}

Handle<Value> OnigScanner::FindNextMatch(Handle<String> v8String, Handle<Number> v8StartLocation, Handle<Value> v8Scanner) {
  String::Utf8Value utf8Value(v8String);
  string string(*utf8Value);
  int charOffset = v8StartLocation->Value();

#ifdef _WIN32
  String::Value utf16Value(v8String);
  int byteOffset = UnicodeUtils::bytes_in_characters(
      reinterpret_cast<const wchar_t*>(*utf16Value), charOffset);
#else
  int byteOffset = UnicodeUtils::bytes_in_characters(*utf8Value, charOffset);
#endif
  int bestIndex = -1;
  int bestLocation = 0;
  OnigResult* bestResult = NULL;

  bool useCachedResults = (string == lastMatchedString && byteOffset >= lastStartLocation);
  lastStartLocation = byteOffset;

  if (!useCachedResults) {
    ClearCachedResults();
    lastMatchedString = string;
  }

  vector< shared_ptr<OnigRegExp> >::iterator iter = regExps.begin();
  int index = 0;
  while (iter < regExps.end()) {
    OnigRegExp *regExp = (*iter).get();

    bool useCachedResult = false;
    shared_ptr<OnigResult> result;

    if (index <= maxCachedIndex) {
      result = cachedResults[index];
      useCachedResult = (!result || result->LocationAt(0) >= charOffset);
    }

    if (!useCachedResult) {
      result = regExp->Search(string, byteOffset);
      cachedResults[index] = result;
      maxCachedIndex = index;
    }

    if (result && result->Count() > 0) {
      int location = result->LocationAt(0);
      if (bestIndex == -1 || location < bestLocation) {
        bestLocation = location;
        bestResult = result.get();
        bestIndex = index;
      }

      if (location == charOffset) {
        break;
      }
    }

    ++iter;
    index++;
  }

  if (bestIndex >= 0) {
    Local<Object> result = Object::New();
    result->Set(String::NewSymbol("index"), Number::New(bestIndex));
    result->Set(String::NewSymbol("captureIndices"), CaptureIndicesForMatch(bestResult));
    result->Set(String::NewSymbol("scanner"), v8Scanner);
    return result;
  } else {
    return Null();
  }
}

void OnigScanner::ClearCachedResults() {
  maxCachedIndex = -1;
  cachedResults.clear();
}

Handle<Value> OnigScanner::CaptureIndicesForMatch(OnigResult* result) {
  int resultCount = result->Count();
  Local<Array> captures = Array::New(resultCount);
  for (int index = 0; index < resultCount; index++) {
    int captureLength = result->LengthAt(index);
    int captureStart = result->LocationAt(index);

    Local<Object> capture = Object::New();
    capture->Set(String::NewSymbol("index"), Number::New(index));
    capture->Set(String::NewSymbol("start"), Number::New(captureStart));
    capture->Set(String::NewSymbol("end"), Number::New(captureStart + captureLength));
    capture->Set(String::NewSymbol("length"), Number::New(captureLength));
    captures->Set(index, capture);
  }

  return captures;
}
<|endoftext|>


<commit_before>/**
 * Appcelerator Kroll - licensed under the Apache Public License 2
 * see LICENSE in the root folder for details on the license.
 * Copyright (c) 2009 Appcelerator, Inc. All Rights Reserved.
 */

#include "php_module.h"
#include <sstream>

namespace kroll
{
	PHPEvaluator::PHPEvaluator()
		: StaticBoundObject("PHPEvaluator")
	{
		/**
		 * @tiapi(method=True,name=PHP.canEvaluate,since=0.7)
		 * @tiarg[String, mimeType] Code mime type
		 * @tiresult[bool] whether or not the mimetype is understood by PHP
		 */
		SetMethod("canEvaluate", &PHPEvaluator::CanEvaluate);

		/**
		 * @tiapi(method=True,name=PHP.evaluate,since=0.7) Evaluates a string as PHP code
		 * @tiarg[String, mimeType] Code mime type (normally "text/php")
		 * @tiarg[String, name] name of the script source
		 * @tiarg[String, code] PHP script code
		 * @tiarg[Object, scope] global variable scope
		 * @tiresult[Any] result of the evaluation
		 */
		SetMethod("evaluate", &PHPEvaluator::Evaluate);

		/**
		 * @tiapi(method=True,name=PHP.canPreprocess,since=0.7)
		 * @tiarg[String, url] URL to preprocess
		 * @tiresult[bool] whether or not the mimetype is understood by PHP
		 */
		SetMethod("canPreprocess", &PHPEvaluator::CanPreprocess);

		/**
		 * @tiapi(method=True,name=PHP.preprocess,since=0.7)
		 * Runs a string and URL through preprocessing
		 * @tiarg[String, url] URL used to load this resource
		 * @tiarg[Object, scope] Global variables to bind for PHP
		 * @tiresult[String] result of the evaluation
		 */
		SetMethod("preprocess", &PHPEvaluator::Preprocess);
	}

	void PHPEvaluator::CanEvaluate(const ValueList& args, SharedValue result)
	{
		args.VerifyException("canEvaluate", "s");

		result->SetBool(false);
		std::string mimeType = args.GetString(0);
		if (mimeType == "text/php")
		{
			result->SetBool(true);
		}
	}

	static std::string GetContextId(SharedKObject global)
	{
		std::string contextId(global->GetString("__php_module_id__"));
		if (contextId.empty())
		{
			static int nextId = 0;
			contextId.append("__kroll__namespace__");
			contextId.append(KList::IntToChars(++nextId));
			global->SetString("__php_module_id__", contextId);
		}

		return contextId;
	}

	void PHPEvaluator::Evaluate(const ValueList& args, SharedValue result)
	{
		static Poco::Mutex evaluatorMutex;
		Poco::Mutex::ScopedLock evaluatorLock(evaluatorMutex);

		args.VerifyException("evaluate", "s s s o");

		TSRMLS_FETCH();
		std::string mimeType = args.GetString(0);
		std::string name = args.GetString(1);
		std::string code = args.GetString(2);
		SharedKObject windowGlobal = args.GetObject(3);
		SharedValue kv = Value::Undefined;

		// Contexts must be the same for runs with the same global object.
		std::string contextId(GetContextId(windowGlobal));

		// Each function we create must have unique name though.
		static int nextFunctionId = 0;
		nextFunctionId++;

		std::ostringstream codeString;
		codeString << "namespace " << contextId << " {\n";
		codeString << code << "\n";
		codeString << "  $__fns = get_defined_functions();\n";
		codeString << "  if (array_key_exists(\"user\", $__fns)) {\n";
		codeString << "   foreach($__fns[\"user\"] as $fname) {\n";
		codeString << "    if (!$window->$fname) {";
		codeString << "      krollAddFunction($window, $fname);\n";
		codeString << "    }\n";
		codeString << "   }\n";
		codeString << "  }\n";
		codeString << "}\n";
		printf("%s\n", codeString.str().c_str());

		// This seems to be needed to make PHP actually give us errors
		// at parse/compile time -- see: main/main.c line 969
		PG(during_request_startup) = 0;

		SharedKObject previousGlobal(PHPUtils::GetCurrentGlobalObject());
		PHPUtils::SwapGlobalObject(windowGlobal, &EG(symbol_table) TSRMLS_CC);

		zend_first_try
		{
			zend_eval_string((char *) codeString.str().c_str(), NULL,
				(char *) name.c_str() TSRMLS_CC);
		}
		zend_catch
		{
			Logger::Get("PHP")->Error("Evaluation of script failed");
		}
		zend_end_try();

		PHPUtils::SwapGlobalObject(previousGlobal, &EG(symbol_table) TSRMLS_CC);

		result->SetValue(kv);
	}

	void PHPEvaluator::CanPreprocess(const ValueList& args, SharedValue result)
	{
		args.VerifyException("canPreprocess", "s");

		std::string url = args.GetString(0);
		Poco::URI uri(url);

		result->SetBool(false);
		if (Script::HasExtension(uri.getPath().c_str(), "php"))
		{
			result->SetBool(true);
		}
	}

	void PHPEvaluator::FillGet(Poco::URI& uri TSRMLS_DC)
	{
		std::string queryString = uri.getQuery();
		Poco::StringTokenizer tokens(uri.getQuery(), "&=");
		Poco::StringTokenizer::Iterator iter = tokens.begin();

		for (; iter != tokens.end(); iter++)
		{
			std::string key = *iter;
			std::string value = *(++iter);

			zval *val;
			ALLOC_INIT_ZVAL(val);
			ZVAL_STRING(val, (char *) value.c_str(), 1);
			zend_hash_add(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]),
				(char *) key.c_str(), key.size()+1, &val, sizeof(zval*), NULL);
		}
	}

	void PHPEvaluator::Preprocess(const ValueList& args, SharedValue result)
	{
		args.VerifyException("preprocess", "s o");

		std::string url = args.GetString(0);
		Logger::Get("PHP")->Debug("preprocessing php => %s", url.c_str());

		Poco::URI uri(url);
		std::string path = URLUtils::URLToPath(url);

		SharedKObject scope = args.GetObject(1);
		TSRMLS_FETCH();

		PHPModule::SetBuffering(true);
		PHPModule::Instance()->PushURI(uri);

		// These variables are normally initialized by php_module_startup
		// but we do not call that function, so we manually initialize.
		PG(header_is_being_sent) = 0;
		SG(request_info).headers_only = 0;
		SG(request_info).argv0 = NULL;
		SG(request_info).argc= 0;
		SG(request_info).argv= (char **) NULL;
		php_request_startup(TSRMLS_C);
		FillGet(uri TSRMLS_CC);

		// This seems to be needed to make PHP actually give  us errors
		// at parse/compile time -- see: main/main.c line 969
		PG(during_request_startup) = 0;

		zend_file_handle script;
		script.type = ZEND_HANDLE_FP;
		script.filename = (char*)path.c_str();
		script.opened_path = NULL;
		script.free_filename = 0;
		script.handle.fp = fopen(script.filename, "rb");

		zend_first_try
		{
			php_execute_script(&script TSRMLS_CC);
		}
		zend_catch
		{

		}
		zend_end_try();

		std::string output(PHPModule::GetBuffer().str());
		SharedKObject o = new StaticBoundObject();
		o->SetObject("data", new Blob(output.c_str(), output.size(), true));
		o->SetString("mimeType", PHPModule::GetMimeType().c_str());
		result->SetObject(o);

		PHPModule::Instance()->PopURI();
		PHPModule::SetBuffering(false);
	}
}
<commit_msg>remove unnecessary variable initialization<commit_after>/**
 * Appcelerator Kroll - licensed under the Apache Public License 2
 * see LICENSE in the root folder for details on the license.
 * Copyright (c) 2009 Appcelerator, Inc. All Rights Reserved.
 */

#include "php_module.h"
#include <sstream>

namespace kroll
{
	PHPEvaluator::PHPEvaluator()
		: StaticBoundObject("PHPEvaluator")
	{
		/**
		 * @tiapi(method=True,name=PHP.canEvaluate,since=0.7)
		 * @tiarg[String, mimeType] Code mime type
		 * @tiresult[bool] whether or not the mimetype is understood by PHP
		 */
		SetMethod("canEvaluate", &PHPEvaluator::CanEvaluate);

		/**
		 * @tiapi(method=True,name=PHP.evaluate,since=0.7) Evaluates a string as PHP code
		 * @tiarg[String, mimeType] Code mime type (normally "text/php")
		 * @tiarg[String, name] name of the script source
		 * @tiarg[String, code] PHP script code
		 * @tiarg[Object, scope] global variable scope
		 * @tiresult[Any] result of the evaluation
		 */
		SetMethod("evaluate", &PHPEvaluator::Evaluate);

		/**
		 * @tiapi(method=True,name=PHP.canPreprocess,since=0.7)
		 * @tiarg[String, url] URL to preprocess
		 * @tiresult[bool] whether or not the mimetype is understood by PHP
		 */
		SetMethod("canPreprocess", &PHPEvaluator::CanPreprocess);

		/**
		 * @tiapi(method=True,name=PHP.preprocess,since=0.7)
		 * Runs a string and URL through preprocessing
		 * @tiarg[String, url] URL used to load this resource
		 * @tiarg[Object, scope] Global variables to bind for PHP
		 * @tiresult[String] result of the evaluation
		 */
		SetMethod("preprocess", &PHPEvaluator::Preprocess);
	}

	void PHPEvaluator::CanEvaluate(const ValueList& args, SharedValue result)
	{
		args.VerifyException("canEvaluate", "s");

		result->SetBool(false);
		std::string mimeType = args.GetString(0);
		if (mimeType == "text/php")
		{
			result->SetBool(true);
		}
	}

	static std::string GetContextId(SharedKObject global)
	{
		std::string contextId(global->GetString("__php_module_id__"));
		if (contextId.empty())
		{
			static int nextId = 0;
			contextId.append("__kroll__namespace__");
			contextId.append(KList::IntToChars(++nextId));
			global->SetString("__php_module_id__", contextId);
		}

		return contextId;
	}

	void PHPEvaluator::Evaluate(const ValueList& args, SharedValue result)
	{
		static Poco::Mutex evaluatorMutex;
		Poco::Mutex::ScopedLock evaluatorLock(evaluatorMutex);

		args.VerifyException("evaluate", "s s s o");

		TSRMLS_FETCH();
		std::string mimeType = args.GetString(0);
		std::string name = args.GetString(1);
		std::string code = args.GetString(2);
		SharedKObject windowGlobal = args.GetObject(3);
		SharedValue kv = Value::Undefined;

		// Contexts must be the same for runs with the same global object.
		std::string contextId(GetContextId(windowGlobal));

		// Each function we create must have unique name though.
		static int nextFunctionId = 0;
		nextFunctionId++;

		std::ostringstream codeString;
		codeString << "namespace " << contextId << " {\n";
		codeString << code << "\n";
		codeString << "  $__fns = get_defined_functions();\n";
		codeString << "  if (array_key_exists(\"user\", $__fns)) {\n";
		codeString << "   foreach($__fns[\"user\"] as $fname) {\n";
		codeString << "    if (!$window->$fname) {";
		codeString << "      krollAddFunction($window, $fname);\n";
		codeString << "    }\n";
		codeString << "   }\n";
		codeString << "  }\n";
		codeString << "}\n";
		printf("%s\n", codeString.str().c_str());

		// This seems to be needed to make PHP actually give us errors
		// at parse/compile time -- see: main/main.c line 969
		PG(during_request_startup) = 0;

		SharedKObject previousGlobal(PHPUtils::GetCurrentGlobalObject());
		PHPUtils::SwapGlobalObject(windowGlobal, &EG(symbol_table) TSRMLS_CC);

		zend_first_try
		{
			zend_eval_string((char *) codeString.str().c_str(), NULL,
				(char *) name.c_str() TSRMLS_CC);
		}
		zend_catch
		{
			Logger::Get("PHP")->Error("Evaluation of script failed");
		}
		zend_end_try();

		PHPUtils::SwapGlobalObject(previousGlobal, &EG(symbol_table) TSRMLS_CC);

		result->SetValue(kv);
	}

	void PHPEvaluator::CanPreprocess(const ValueList& args, SharedValue result)
	{
		args.VerifyException("canPreprocess", "s");

		std::string url = args.GetString(0);
		Poco::URI uri(url);

		result->SetBool(false);
		if (Script::HasExtension(uri.getPath().c_str(), "php"))
		{
			result->SetBool(true);
		}
	}

	void PHPEvaluator::FillGet(Poco::URI& uri TSRMLS_DC)
	{
		Poco::StringTokenizer tokens(uri.getQuery(), "&=");
		Poco::StringTokenizer::Iterator iter = tokens.begin();

		for (; iter != tokens.end(); iter++)
		{
			std::string key = *iter;
			std::string value = *(++iter);

			zval *val;
			ALLOC_INIT_ZVAL(val);
			ZVAL_STRING(val, (char *) value.c_str(), 1);
			zend_hash_add(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]),
				(char *) key.c_str(), key.size()+1, &val, sizeof(zval*), NULL);
		}
	}

	void PHPEvaluator::Preprocess(const ValueList& args, SharedValue result)
	{
		args.VerifyException("preprocess", "s o");

		std::string url = args.GetString(0);
		Logger::Get("PHP")->Debug("preprocessing php => %s", url.c_str());

		Poco::URI uri(url);
		std::string path = URLUtils::URLToPath(url);

		SharedKObject scope = args.GetObject(1);
		TSRMLS_FETCH();

		PHPModule::SetBuffering(true);
		PHPModule::Instance()->PushURI(uri);

		// These variables are normally initialized by php_module_startup
		// but we do not call that function, so we manually initialize.
		PG(header_is_being_sent) = 0;
		SG(request_info).headers_only = 0;
		SG(request_info).argv0 = NULL;
		SG(request_info).argc= 0;
		SG(request_info).argv= (char **) NULL;
		php_request_startup(TSRMLS_C);
		FillGet(uri TSRMLS_CC);

		// This seems to be needed to make PHP actually give  us errors
		// at parse/compile time -- see: main/main.c line 969
		PG(during_request_startup) = 0;

		zend_file_handle script;
		script.type = ZEND_HANDLE_FP;
		script.filename = (char*)path.c_str();
		script.opened_path = NULL;
		script.free_filename = 0;
		script.handle.fp = fopen(script.filename, "rb");

		zend_first_try
		{
			php_execute_script(&script TSRMLS_CC);
		}
		zend_catch
		{

		}
		zend_end_try();

		std::string output(PHPModule::GetBuffer().str());
		SharedKObject o = new StaticBoundObject();
		o->SetObject("data", new Blob(output.c_str(), output.size(), true));
		o->SetString("mimeType", PHPModule::GetMimeType().c_str());
		result->SetObject(o);

		PHPModule::Instance()->PopURI();
		PHPModule::SetBuffering(false);
	}
}
<|endoftext|>


<commit_before>
#include <leveldb/db.h>
#include <leveldb/comparator.h>

#if defined(OS_MACOSX)
  #include <machine/endian.h>
#elif defined(OS_SOLARIS)
  #include <sys/isa_defs.h>
  #ifdef _LITTLE_ENDIAN
    #define LITTLE_ENDIAN
  #else
    #define BIG_ENDIAN
  #endif
#else
  #include <endian.h>
#endif

#ifdef LITTLE_ENDIAN
#define IS_LITTLE_ENDIAN true
#else
#define IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)
#endif

extern "C" {

#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/alloc.h>

const char cMetadata_prefix = 0;
const char cDatum_prefix = 1;

static int64_t
decode_var_int(const char *p)
{
 int64_t n = 0;
 int shift = 0;

 do {
     n += (*p & 0x7F) << shift;
     shift += 7;
 } while (*p++ & 0x80);

 return n;
}

class OStoreComparator1 : public leveldb::Comparator {
    public:
    OStoreComparator1() {}

    virtual const char* Name() const {
        return "org.eigenclass/OStoreComparator1";
    }

    virtual int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
        size_t sa = a.size(), sb = b.size();

        if(!sa && sb) return -1;
        if(!sb && sa) return 1;
        if(!sa && !sb) return 0;

        if(a[0] != b[0]) return (a[0] - b[0]);

        switch(a[0]) {
            case '1': {
                // first compare keyspaces
                int kscmp = a[1] - b[1];
                if(kscmp) return kscmp;

                // then compare table names
                int tlen_a = a[sa - 3],
                    tlen_b = b[sb - 3];

                leveldb::Slice ta(a.data() + 2, tlen_a),
                               tb(b.data() + 2, tlen_b);
                int tblcmp = ta.compare(tb);
                if(tblcmp) return tblcmp;

                // then decode column and key lengths, then compare the
                // latter, then the former

                int last_a = a[sa - 2], last_b = b[sb - 2];
                int clen_len_a = last_a & 0x7,
                    clen_len_b = last_b & 0x7;
                int klen_len_a = (last_a >> 3) & 0x7,
                    klen_len_b = (last_b >> 3) & 0x7;

                int64_t klen_a, klen_b;

                klen_a = decode_var_int(a.data() + sa - 3 - clen_len_a - klen_len_a);
                klen_b = decode_var_int(b.data() + sb - 3 - clen_len_b - klen_len_b);

                leveldb::Slice key_a(a.data() + 2 + tlen_a, klen_a),
                               key_b(b.data() + 2 + tlen_b, klen_b);

                int keycmp = key_a.compare(key_b);
                if(keycmp) return keycmp;

                int64_t clen_a, clen_b;
                clen_a = decode_var_int(a.data() + sa - 3 - clen_len_a);
                clen_b = decode_var_int(b.data() + sb - 3 - clen_len_b);

                leveldb::Slice col_a(a.data() + 2 + tlen_a + klen_a, clen_a),
                               col_b(b.data() + 2 + tlen_b + klen_b, clen_b);

                int colcmp = col_a.compare(col_b);
                if (colcmp) return colcmp;

                // FIXME: unaligned read
                // compare timestamps
                int64_t *ts_a = (int64_t *)(a.data() + 2 + tlen_a + klen_a + clen_a),
                        *ts_b = (int64_t *)(a.data() + 2 + tlen_b + klen_b + clen_b);

                return (*ts_a > *ts_b ? 1 : *ts_a < *ts_b ? -1 : 0);

                break;
            }
            default:
                // bytewise comparison for all others
                return a.compare(b);
        }

    }

    virtual void FindShortestSeparator(std::string *start,
                                       const leveldb::Slice& limit) const {
        return;
    }

    virtual void FindShortSuccessor(std::string *key) const {
        return;
    }
};

static const OStoreComparator1 comparator1;

CAMLprim const leveldb::Comparator*
ostore_custom_comparator() {
    return &comparator1;
}

CAMLprim value
ostore_apply_custom_comparator(value s1, value s2)
{
 CAMLparam2(s1, s2);
 leveldb::Slice d1(String_val(s1), string_length(s1)),
                d2(String_val(s2), string_length(s2));

 CAMLreturn(Val_int(comparator1.Compare(d1, d2)));
}

CAMLprim value
ostore_bytea_blit_int64_complement_le(value dst, value off, value n)
{
 int64_t v = Int64_val(n);

#ifdef IS_LITTLE_ENDIAN
 // FIXME: non-aligned writes
 int64_t *p = (int64_t *)(String_val(dst) + Int_val(off));
 *p = v ^ -1;
#else
 char *d = String_val(dst);
 char *s = (char *)&v;

 d[0] = s[7]; d[1] = s[6]; d[2] = s[5]; d[3] = s[4];
 d[4] = s[3]; d[5] = s[2]; d[6] = s[1]; d[7] = s[0];
#endif

 return(Val_unit);
}

CAMLprim value
ostore_decode_int64_complement_le(value s, value off)
{
#ifdef IS_LITTLE_ENDIAN
  int64_t p = *((int64_t *)(String_val(s) + Int_val(off))) ^ -1;
  return(caml_copy_int64(p));
#else
  char *s = String_val(s) + Int_val(off);
  union {
      int64_t ll;
      unsigned char c[8];
  } x;

  x.c[0] = s[7]; x.c[1] = s[6]; x.c[2] = s[5]; x.c[3] = x[4];
  x.c[4] = s[3]; x.c[5] = s[2]; x.c[6] = s[1]; x.c[7] = x[0];

  return(caml_copy_int64(x.ll));
#endif
}

}
<commit_msg>Must use compare timestamps as uint64.<commit_after>
#include <leveldb/db.h>
#include <leveldb/comparator.h>

#if defined(OS_MACOSX)
  #include <machine/endian.h>
#elif defined(OS_SOLARIS)
  #include <sys/isa_defs.h>
  #ifdef _LITTLE_ENDIAN
    #define LITTLE_ENDIAN
  #else
    #define BIG_ENDIAN
  #endif
#else
  #include <endian.h>
#endif

#ifdef LITTLE_ENDIAN
#define IS_LITTLE_ENDIAN true
#else
#define IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)
#endif

extern "C" {

#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/alloc.h>

const char cMetadata_prefix = 0;
const char cDatum_prefix = 1;

static int64_t
decode_var_int(const char *p)
{
 int64_t n = 0;
 int shift = 0;

 do {
     n += (*p & 0x7F) << shift;
     shift += 7;
 } while (*p++ & 0x80);

 return n;
}

class OStoreComparator1 : public leveldb::Comparator {
    public:
    OStoreComparator1() {}

    virtual const char* Name() const {
        return "org.eigenclass/OStoreComparator1";
    }

    virtual int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
        size_t sa = a.size(), sb = b.size();

        if(!sa && sb) return -1;
        if(!sb && sa) return 1;
        if(!sa && !sb) return 0;

        if(a[0] != b[0]) return (a[0] - b[0]);

        switch(a[0]) {
            case '1': {
                // first compare keyspaces
                int kscmp = a[1] - b[1];
                if(kscmp) return kscmp;

                // then compare table names
                int tlen_a = a[sa - 3],
                    tlen_b = b[sb - 3];

                leveldb::Slice ta(a.data() + 2, tlen_a),
                               tb(b.data() + 2, tlen_b);
                int tblcmp = ta.compare(tb);
                if(tblcmp) return tblcmp;

                // then decode column and key lengths, then compare the
                // latter, then the former

                int last_a = a[sa - 2], last_b = b[sb - 2];
                int clen_len_a = last_a & 0x7,
                    clen_len_b = last_b & 0x7;
                int klen_len_a = (last_a >> 3) & 0x7,
                    klen_len_b = (last_b >> 3) & 0x7;

                int64_t klen_a, klen_b;

                klen_a = decode_var_int(a.data() + sa - 3 - clen_len_a - klen_len_a);
                klen_b = decode_var_int(b.data() + sb - 3 - clen_len_b - klen_len_b);

                leveldb::Slice key_a(a.data() + 2 + tlen_a, klen_a),
                               key_b(b.data() + 2 + tlen_b, klen_b);

                int keycmp = key_a.compare(key_b);
                if(keycmp) return keycmp;

                int64_t clen_a, clen_b;
                clen_a = decode_var_int(a.data() + sa - 3 - clen_len_a);
                clen_b = decode_var_int(b.data() + sb - 3 - clen_len_b);

                leveldb::Slice col_a(a.data() + 2 + tlen_a + klen_a, clen_a),
                               col_b(b.data() + 2 + tlen_b + klen_b, clen_b);

                int colcmp = col_a.compare(col_b);
                if (colcmp) return colcmp;

                // FIXME: unaligned read
                // compare timestamps
                uint64_t *ts_a = (uint64_t *)(a.data() + 2 + tlen_a + klen_a + clen_a),
                         *ts_b = (uint64_t *)(a.data() + 2 + tlen_b + klen_b + clen_b);

                return (*ts_a > *ts_b ? 1 : *ts_a < *ts_b ? -1 : 0);

                break;
            }
            default:
                // bytewise comparison for all others
                return a.compare(b);
        }

    }

    virtual void FindShortestSeparator(std::string *start,
                                       const leveldb::Slice& limit) const {
        return;
    }

    virtual void FindShortSuccessor(std::string *key) const {
        return;
    }
};

static const OStoreComparator1 comparator1;

CAMLprim const leveldb::Comparator*
ostore_custom_comparator() {
    return &comparator1;
}

CAMLprim value
ostore_apply_custom_comparator(value s1, value s2)
{
 CAMLparam2(s1, s2);
 leveldb::Slice d1(String_val(s1), string_length(s1)),
                d2(String_val(s2), string_length(s2));

 CAMLreturn(Val_int(comparator1.Compare(d1, d2)));
}

CAMLprim value
ostore_bytea_blit_int64_complement_le(value dst, value off, value n)
{
 int64_t v = Int64_val(n);

#ifdef IS_LITTLE_ENDIAN
 // FIXME: non-aligned writes
 int64_t *p = (int64_t *)(String_val(dst) + Int_val(off));
 *p = v ^ -1;
#else
 char *d = String_val(dst);
 char *s = (char *)&v;

 d[0] = s[7]; d[1] = s[6]; d[2] = s[5]; d[3] = s[4];
 d[4] = s[3]; d[5] = s[2]; d[6] = s[1]; d[7] = s[0];
#endif

 return(Val_unit);
}

CAMLprim value
ostore_decode_int64_complement_le(value s, value off)
{
#ifdef IS_LITTLE_ENDIAN
  int64_t p = *((int64_t *)(String_val(s) + Int_val(off))) ^ -1;
  return(caml_copy_int64(p));
#else
  char *s = String_val(s) + Int_val(off);
  union {
      int64_t ll;
      unsigned char c[8];
  } x;

  x.c[0] = s[7]; x.c[1] = s[6]; x.c[2] = s[5]; x.c[3] = x[4];
  x.c[4] = s[3]; x.c[5] = s[2]; x.c[6] = s[1]; x.c[7] = x[0];

  return(caml_copy_int64(x.ll));
#endif
}

}
<|endoftext|>


<commit_before>/*
 * MRustC - Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * parse/types.cpp
 * - Parsing for type usages
 */
#include "common.hpp"
#include "parseerror.hpp"
#include <ast/types.hpp>
#include <ast/ast.hpp>

// === PROTOTYPES ===
//TypeRef Parse_Type(TokenStream& lex, bool allow_trait_list);
TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list);
TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls = {});
TypeRef Parse_Type_Path(TokenStream& lex, ::std::vector<::std::string> hrls, bool allow_trait_list);

// === CODE ===
TypeRef Parse_Type(TokenStream& lex, bool allow_trait_list)
{
    ProtoSpan ps = lex.start_span();
    TypeRef rv = Parse_Type_Int(lex, allow_trait_list);
    //rv.set_span(lex.end_span(ps));
    return rv;
}

TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list)
{
    //TRACE_FUNCTION;
    auto ps = lex.start_span();

    Token tok;

    switch( GET_TOK(tok, lex) )
    {
    case TOK_INTERPOLATED_TYPE:
        return mv$(tok.frag_type());
    case TOK_MACRO:
        return TypeRef(TypeRef::TagMacro(), Parse_MacroInvocation(ps, mv$(tok.str()), lex));
    // '!' - Only ever used as part of function prototypes, but is kinda a type... not allowed here though
    case TOK_EXCLAM:
        return TypeRef( Span(tok.get_pos()), TypeData::make_Bang({}) );
    // '_' = Wildcard (type inferrence variable)
    case TOK_UNDERSCORE:
        return TypeRef(Span(tok.get_pos()));

    // 'unsafe' - An unsafe function type
    case TOK_RWORD_UNSAFE:
    // 'extern' - A function type with an ABI
    case TOK_RWORD_EXTERN:
    // 'fn' - Rust function
    case TOK_RWORD_FN:
        PUTBACK(tok, lex);
        return Parse_Type_Fn(lex);

    // '<' - An associated type cast
    case TOK_LT:
    case TOK_DOUBLE_LT: {
        PUTBACK(tok, lex);
        auto path = Parse_Path(lex, PATH_GENERIC_TYPE);
        return TypeRef(TypeRef::TagPath(), lex.end_span(ps), mv$(path));
        }
    //
    case TOK_RWORD_FOR: {
        GET_CHECK_TOK(tok, lex, TOK_LT);
        ::std::vector<::std::string>    hrls;
        do {
            GET_CHECK_TOK(tok, lex, TOK_LIFETIME);
            hrls.push_back( mv$(tok.str()) );
        } while( GET_TOK(tok, lex) == TOK_COMMA );
        CHECK_TOK(tok, TOK_GT);
        switch(LOOK_AHEAD(lex))
        {
        case TOK_RWORD_UNSAFE:
        case TOK_RWORD_EXTERN:
        case TOK_RWORD_FN:
            // TODO: Handle HRLS in fn types
            return Parse_Type_Fn(lex, hrls);
        default:
            return Parse_Type_Path(lex, hrls, true);
        }
        }
    // <ident> - Either a primitive, or a path
    case TOK_IDENT:
        // or a primitive
        //if( auto ct = coretype_fromstring(tok.str()) )
        //{
        //    return TypeRef(TypeRef::TagPrimitive(), Span(tok.get_pos()), ct);
        //}
        PUTBACK(tok, lex);
        return Parse_Type_Path(lex, {}, allow_trait_list);
        // - Fall through to path handling
    // '::' - Absolute path
    case TOK_DOUBLE_COLON:
        PUTBACK(tok, lex);
        return Parse_Type_Path(lex, {}, allow_trait_list);
    // 'super' - Parent relative path
    case TOK_RWORD_SUPER:
        PUTBACK(tok, lex);
        return Parse_Type_Path(lex, {}, allow_trait_list);

    // HACK! Convert && into & &
    case TOK_DOUBLE_AMP:
        lex.putback(Token(TOK_AMP));
    // '&' - Reference type
    case TOK_AMP: {
        ::std::string   lifetime;
        // Reference
        tok = lex.getToken();
        if( tok.type() == TOK_LIFETIME ) {
            lifetime = tok.str();
            tok = lex.getToken();
        }
        if( tok.type() == TOK_RWORD_MUT ) {
            // Mutable reference
            return TypeRef(TypeRef::TagReference(), lex.end_span(ps), true, Parse_Type(lex, false));
        }
        else {
            PUTBACK(tok, lex);
            // Immutable reference
            return TypeRef(TypeRef::TagReference(), lex.end_span(ps), false, Parse_Type(lex, false));
        }
        throw ParseError::BugCheck("Reached end of Parse_Type:AMP");
        }
    // '*' - Raw pointer
    case TOK_STAR:
        // Pointer
        switch( GET_TOK(tok, lex) )
        {
        case TOK_RWORD_MUT:
            // Mutable pointer
            return TypeRef(TypeRef::TagPointer(), lex.end_span(ps), true, Parse_Type(lex, false));
        case TOK_RWORD_CONST:
            // Immutable pointer
            return TypeRef(TypeRef::TagPointer(), lex.end_span(ps), false, Parse_Type(lex, false));
        default:
            throw ParseError::Unexpected(lex, tok, {TOK_RWORD_CONST, TOK_RWORD_MUT});
        }
        throw ParseError::BugCheck("Reached end of Parse_Type:STAR");
    // '[' - Array type
    case TOK_SQUARE_OPEN: {
        // Array
        TypeRef inner = Parse_Type(lex);
        if( GET_TOK(tok, lex)  == TOK_SEMICOLON ) {
            // Sized array
            AST::Expr array_size = Parse_Expr(lex);
            GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE);
            return TypeRef(TypeRef::TagSizedArray(), lex.end_span(ps), inner, array_size.take_node());
        }
        else if( tok.type() == TOK_SQUARE_CLOSE )
        {
            return TypeRef(TypeRef::TagUnsizedArray(), lex.end_span(ps), inner);
        }
        else {
            throw ParseError::Unexpected(lex, tok/*, "; or ]"*/);
        }
        }

    // '(' - Tuple (or lifetime bounded trait)
    case TOK_PAREN_OPEN: {
        DEBUG("Tuple");
        if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE )
            return TypeRef(TypeRef::TagTuple(), lex.end_span(ps), {});
        PUTBACK(tok, lex);

        TypeRef inner = Parse_Type(lex, true);
        if( LOOK_AHEAD(lex) == TOK_PAREN_CLOSE )
        {
            // Type in parens, NOT a tuple
            GET_CHECK_TOK(tok, lex, TOK_PAREN_CLOSE);
            return inner;
        }
        else
        {
            ::std::vector<TypeRef>  types;
            types.push_back( mv$(inner) );
            while( GET_TOK(tok, lex) == TOK_COMMA )
            {
                if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE )
                    break;
                else
                    PUTBACK(tok, lex);
                types.push_back(Parse_Type(lex));
            }
            CHECK_TOK(tok, TOK_PAREN_CLOSE);
            return TypeRef(TypeRef::TagTuple(), lex.end_span(ps), mv$(types)); }
        }
    default:
        throw ParseError::Unexpected(lex, tok);
    }
    throw ParseError::BugCheck("Reached end of Parse_Type");
}

TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls)
{
    auto ps = lex.start_span();
    // TODO: HRLs
    TRACE_FUNCTION;
    Token   tok;

    ::std::string   abi = "";
    bool    is_unsafe = false;

    GET_TOK(tok, lex);

    if( tok.type() == TOK_RWORD_UNSAFE )
    {
        is_unsafe = true;
        GET_TOK(tok, lex);
    }
    if( tok.type() == TOK_RWORD_EXTERN )
    {
        if( GET_TOK(tok, lex) == TOK_STRING ) {
            abi = tok.str();
            if( abi == "" )
                ERROR(lex.getPosition(), E0000, "Empty ABI");
            GET_TOK(tok, lex);
        }
        else {
            abi = "C";
        }
    }
    CHECK_TOK(tok, TOK_RWORD_FN);

    ::std::vector<TypeRef>  args;
    bool is_variadic = false;
    GET_CHECK_TOK(tok, lex, TOK_PAREN_OPEN);
    while( LOOK_AHEAD(lex) != TOK_PAREN_CLOSE )
    {
        if( LOOK_AHEAD(lex) == TOK_TRIPLE_DOT ) {
            GET_TOK(tok, lex);
            is_variadic = true;
            break;
        }
        // Handle `ident: `
        if( lex.lookahead(0) == TOK_IDENT && lex.lookahead(1) == TOK_COLON ) {
            GET_TOK(tok, lex);
            GET_TOK(tok, lex);
        }
        args.push_back( Parse_Type(lex) );
        if( GET_TOK(tok, lex) != TOK_COMMA ) {
            PUTBACK(tok, lex);
            break;
        }
    }
    GET_CHECK_TOK(tok, lex, TOK_PAREN_CLOSE);

    TypeRef ret_type = TypeRef(TypeRef::TagUnit(), Span(tok.get_pos()));
    if( GET_TOK(tok, lex) == TOK_THINARROW )
    {
        ret_type = Parse_Type(lex, false);
    }
    else {
        PUTBACK(tok, lex);
    }

    return TypeRef(TypeRef::TagFunction(), lex.end_span(ps), is_unsafe, mv$(abi), mv$(args), is_variadic, mv$(ret_type));
}

TypeRef Parse_Type_Path(TokenStream& lex, ::std::vector<::std::string> hrls, bool allow_trait_list)
{
    Token   tok;

    auto ps = lex.start_span();

    if( ! allow_trait_list )
    {
        return TypeRef(TypeRef::TagPath(), lex.end_span(ps), Parse_Path(lex, PATH_GENERIC_TYPE));
    }
    else
    {
        ::std::vector<AST::Path>    traits;
        ::std::vector< ::std::string>   lifetimes;
        do {
            if( LOOK_AHEAD(lex) == TOK_LIFETIME ) {
                GET_TOK(tok, lex);
                lifetimes.push_back( tok.str() );
            }
            else
                traits.push_back( Parse_Path(lex, PATH_GENERIC_TYPE) );
        } while( GET_TOK(tok, lex) == TOK_PLUS );
        PUTBACK(tok, lex);
        if( hrls.size() > 0 || traits.size() > 1 || lifetimes.size() > 0 ) {
            if( lifetimes.size() )
                DEBUG("TODO: Lifetime bounds on trait objects");
            return TypeRef(lex.end_span(ps), mv$(hrls), ::std::move(traits));
        }
        else {
            return TypeRef(TypeRef::TagPath(), lex.end_span(ps), traits.at(0));
        }
    }
}

<commit_msg>Parse Types - Allow 'self' to start a path<commit_after>/*
 * MRustC - Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * parse/types.cpp
 * - Parsing for type usages
 */
#include "common.hpp"
#include "parseerror.hpp"
#include <ast/types.hpp>
#include <ast/ast.hpp>

// === PROTOTYPES ===
//TypeRef Parse_Type(TokenStream& lex, bool allow_trait_list);
TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list);
TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls = {});
TypeRef Parse_Type_Path(TokenStream& lex, ::std::vector<::std::string> hrls, bool allow_trait_list);

// === CODE ===
TypeRef Parse_Type(TokenStream& lex, bool allow_trait_list)
{
    ProtoSpan ps = lex.start_span();
    TypeRef rv = Parse_Type_Int(lex, allow_trait_list);
    //rv.set_span(lex.end_span(ps));
    return rv;
}

TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list)
{
    //TRACE_FUNCTION;
    auto ps = lex.start_span();

    Token tok;

    switch( GET_TOK(tok, lex) )
    {
    case TOK_INTERPOLATED_TYPE:
        return mv$(tok.frag_type());
    case TOK_MACRO:
        return TypeRef(TypeRef::TagMacro(), Parse_MacroInvocation(ps, mv$(tok.str()), lex));
    // '!' - Only ever used as part of function prototypes, but is kinda a type... not allowed here though
    case TOK_EXCLAM:
        return TypeRef( Span(tok.get_pos()), TypeData::make_Bang({}) );
    // '_' = Wildcard (type inferrence variable)
    case TOK_UNDERSCORE:
        return TypeRef(Span(tok.get_pos()));

    // 'unsafe' - An unsafe function type
    case TOK_RWORD_UNSAFE:
    // 'extern' - A function type with an ABI
    case TOK_RWORD_EXTERN:
    // 'fn' - Rust function
    case TOK_RWORD_FN:
        PUTBACK(tok, lex);
        return Parse_Type_Fn(lex);

    // '<' - An associated type cast
    case TOK_LT:
    case TOK_DOUBLE_LT: {
        PUTBACK(tok, lex);
        auto path = Parse_Path(lex, PATH_GENERIC_TYPE);
        return TypeRef(TypeRef::TagPath(), lex.end_span(ps), mv$(path));
        }
    //
    case TOK_RWORD_FOR: {
        GET_CHECK_TOK(tok, lex, TOK_LT);
        ::std::vector<::std::string>    hrls;
        do {
            GET_CHECK_TOK(tok, lex, TOK_LIFETIME);
            hrls.push_back( mv$(tok.str()) );
        } while( GET_TOK(tok, lex) == TOK_COMMA );
        CHECK_TOK(tok, TOK_GT);
        switch(LOOK_AHEAD(lex))
        {
        case TOK_RWORD_UNSAFE:
        case TOK_RWORD_EXTERN:
        case TOK_RWORD_FN:
            // TODO: Handle HRLS in fn types
            return Parse_Type_Fn(lex, hrls);
        default:
            return Parse_Type_Path(lex, hrls, true);
        }
        }
    // <ident> - Either a primitive, or a path
    case TOK_IDENT:
        // or a primitive
        //if( auto ct = coretype_fromstring(tok.str()) )
        //{
        //    return TypeRef(TypeRef::TagPrimitive(), Span(tok.get_pos()), ct);
        //}
        PUTBACK(tok, lex);
        return Parse_Type_Path(lex, {}, allow_trait_list);
        // - Fall through to path handling
    // '::' - Absolute path
    case TOK_DOUBLE_COLON:
        PUTBACK(tok, lex);
        return Parse_Type_Path(lex, {}, allow_trait_list);
    // 'self' - This relative path
    // 'super' - Parent relative path
    case TOK_RWORD_SELF:
    case TOK_RWORD_SUPER:
        PUTBACK(tok, lex);
        return Parse_Type_Path(lex, {}, allow_trait_list);

    // HACK! Convert && into & &
    case TOK_DOUBLE_AMP:
        lex.putback(Token(TOK_AMP));
    // '&' - Reference type
    case TOK_AMP: {
        ::std::string   lifetime;
        // Reference
        tok = lex.getToken();
        if( tok.type() == TOK_LIFETIME ) {
            lifetime = tok.str();
            tok = lex.getToken();
        }
        if( tok.type() == TOK_RWORD_MUT ) {
            // Mutable reference
            return TypeRef(TypeRef::TagReference(), lex.end_span(ps), true, Parse_Type(lex, false));
        }
        else {
            PUTBACK(tok, lex);
            // Immutable reference
            return TypeRef(TypeRef::TagReference(), lex.end_span(ps), false, Parse_Type(lex, false));
        }
        throw ParseError::BugCheck("Reached end of Parse_Type:AMP");
        }
    // '*' - Raw pointer
    case TOK_STAR:
        // Pointer
        switch( GET_TOK(tok, lex) )
        {
        case TOK_RWORD_MUT:
            // Mutable pointer
            return TypeRef(TypeRef::TagPointer(), lex.end_span(ps), true, Parse_Type(lex, false));
        case TOK_RWORD_CONST:
            // Immutable pointer
            return TypeRef(TypeRef::TagPointer(), lex.end_span(ps), false, Parse_Type(lex, false));
        default:
            throw ParseError::Unexpected(lex, tok, {TOK_RWORD_CONST, TOK_RWORD_MUT});
        }
        throw ParseError::BugCheck("Reached end of Parse_Type:STAR");
    // '[' - Array type
    case TOK_SQUARE_OPEN: {
        // Array
        TypeRef inner = Parse_Type(lex);
        if( GET_TOK(tok, lex)  == TOK_SEMICOLON ) {
            // Sized array
            AST::Expr array_size = Parse_Expr(lex);
            GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE);
            return TypeRef(TypeRef::TagSizedArray(), lex.end_span(ps), inner, array_size.take_node());
        }
        else if( tok.type() == TOK_SQUARE_CLOSE )
        {
            return TypeRef(TypeRef::TagUnsizedArray(), lex.end_span(ps), inner);
        }
        else {
            throw ParseError::Unexpected(lex, tok/*, "; or ]"*/);
        }
        }

    // '(' - Tuple (or lifetime bounded trait)
    case TOK_PAREN_OPEN: {
        DEBUG("Tuple");
        if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE )
            return TypeRef(TypeRef::TagTuple(), lex.end_span(ps), {});
        PUTBACK(tok, lex);

        TypeRef inner = Parse_Type(lex, true);
        if( LOOK_AHEAD(lex) == TOK_PAREN_CLOSE )
        {
            // Type in parens, NOT a tuple
            GET_CHECK_TOK(tok, lex, TOK_PAREN_CLOSE);
            return inner;
        }
        else
        {
            ::std::vector<TypeRef>  types;
            types.push_back( mv$(inner) );
            while( GET_TOK(tok, lex) == TOK_COMMA )
            {
                if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE )
                    break;
                else
                    PUTBACK(tok, lex);
                types.push_back(Parse_Type(lex));
            }
            CHECK_TOK(tok, TOK_PAREN_CLOSE);
            return TypeRef(TypeRef::TagTuple(), lex.end_span(ps), mv$(types)); }
        }
    default:
        throw ParseError::Unexpected(lex, tok);
    }
    throw ParseError::BugCheck("Reached end of Parse_Type");
}

TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls)
{
    auto ps = lex.start_span();
    // TODO: HRLs
    TRACE_FUNCTION;
    Token   tok;

    ::std::string   abi = "";
    bool    is_unsafe = false;

    GET_TOK(tok, lex);

    if( tok.type() == TOK_RWORD_UNSAFE )
    {
        is_unsafe = true;
        GET_TOK(tok, lex);
    }
    if( tok.type() == TOK_RWORD_EXTERN )
    {
        if( GET_TOK(tok, lex) == TOK_STRING ) {
            abi = tok.str();
            if( abi == "" )
                ERROR(lex.getPosition(), E0000, "Empty ABI");
            GET_TOK(tok, lex);
        }
        else {
            abi = "C";
        }
    }
    CHECK_TOK(tok, TOK_RWORD_FN);

    ::std::vector<TypeRef>  args;
    bool is_variadic = false;
    GET_CHECK_TOK(tok, lex, TOK_PAREN_OPEN);
    while( LOOK_AHEAD(lex) != TOK_PAREN_CLOSE )
    {
        if( LOOK_AHEAD(lex) == TOK_TRIPLE_DOT ) {
            GET_TOK(tok, lex);
            is_variadic = true;
            break;
        }
        // Handle `ident: `
        if( lex.lookahead(0) == TOK_IDENT && lex.lookahead(1) == TOK_COLON ) {
            GET_TOK(tok, lex);
            GET_TOK(tok, lex);
        }
        args.push_back( Parse_Type(lex) );
        if( GET_TOK(tok, lex) != TOK_COMMA ) {
            PUTBACK(tok, lex);
            break;
        }
    }
    GET_CHECK_TOK(tok, lex, TOK_PAREN_CLOSE);

    TypeRef ret_type = TypeRef(TypeRef::TagUnit(), Span(tok.get_pos()));
    if( GET_TOK(tok, lex) == TOK_THINARROW )
    {
        ret_type = Parse_Type(lex, false);
    }
    else {
        PUTBACK(tok, lex);
    }

    return TypeRef(TypeRef::TagFunction(), lex.end_span(ps), is_unsafe, mv$(abi), mv$(args), is_variadic, mv$(ret_type));
}

TypeRef Parse_Type_Path(TokenStream& lex, ::std::vector<::std::string> hrls, bool allow_trait_list)
{
    Token   tok;

    auto ps = lex.start_span();

    if( ! allow_trait_list )
    {
        return TypeRef(TypeRef::TagPath(), lex.end_span(ps), Parse_Path(lex, PATH_GENERIC_TYPE));
    }
    else
    {
        ::std::vector<AST::Path>    traits;
        ::std::vector< ::std::string>   lifetimes;
        do {
            if( LOOK_AHEAD(lex) == TOK_LIFETIME ) {
                GET_TOK(tok, lex);
                lifetimes.push_back( tok.str() );
            }
            else
                traits.push_back( Parse_Path(lex, PATH_GENERIC_TYPE) );
        } while( GET_TOK(tok, lex) == TOK_PLUS );
        PUTBACK(tok, lex);
        if( hrls.size() > 0 || traits.size() > 1 || lifetimes.size() > 0 ) {
            if( lifetimes.size() )
                DEBUG("TODO: Lifetime bounds on trait objects");
            return TypeRef(lex.end_span(ps), mv$(hrls), ::std::move(traits));
        }
        else {
            return TypeRef(TypeRef::TagPath(), lex.end_span(ps), traits.at(0));
        }
    }
}

<|endoftext|>


<commit_before>#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>

#include "parsers/parse_error.hh"
#include "parsers/tina.hh"

namespace pnmc { namespace parsers {

namespace {

/*------------------------------------------------------------------------------------------------*/

unsigned int
value(std::string::const_iterator cit, std::string::const_iterator cend)
{
  std::string s(cit, cend);
  try
  {
    std::size_t pos;
    auto value = std::stoi(s, &pos);
    std::advance(cit, pos);
    if (std::distance(cit, cend) == 1)
    {
      switch (*cit)
      {
        case 'K': value *= 1000; break;
        case 'M': value *= 1000000; break;
        default: throw parse_error("Invalid suffix, got '" + std::string(cit, cend) + "'");
      }
    }
    else if (std::distance(cit, cend) > 1)
    {
      throw parse_error("Invalid suffix, got " + std::string(cit, cend));
    }
    return value;
  }
  catch (const std::invalid_argument&)
  {
    throw parse_error("Expected a value, got " + s);
  }
}

/*------------------------------------------------------------------------------------------------*/

std::pair<std::string, unsigned int>
place_valuation(const std::string& s)
{
  const auto star_cit = std::find(s.cbegin(), s.cend(), '*');
  if (star_cit == s.cend())
  {
    return std::make_pair(s, 1);
  }
  const auto valuation = value(star_cit + 1, s.cend());
  return std::make_pair(std::string(s.cbegin(), star_cit), valuation);
}

/*------------------------------------------------------------------------------------------------*/

unsigned int
marking(const std::string& s)
{
  if (*s.cbegin() == '(' and *std::prev(s.cend()) == ')')
  {
    return value(std::next(s.cbegin()), std::prev(s.cend()));
  }
  else
  {
    throw parse_error("Invalid marking format: " + s);
  }
}

} // namespace anonymous

/*------------------------------------------------------------------------------------------------*/

std::shared_ptr<pn::net>
tina(std::istream& in)
{
  // Known limitations:
  // - there's must be a transition or place per line
  // - labels are ignored
  // - time intervals are not completely validated (e.g. [0, 2a] is wrongly validated

  std::shared_ptr<pn::net> net_ptr = std::make_shared<pn::net>();
  auto& net = *net_ptr;

  std::string line, s0, s1, s2, ignore;
  line.reserve(1024);

  while (std::getline(in, line))
  {
    std::istringstream ss(line);

    // Empty line or comment.
    if (((ss >> std::ws).peek() == std::char_traits<char>::to_int_type('#')) or not (ss >> s0))
    {
      continue;
    }

    // Net
    if (s0 == "net")
    {
      ss >> s0;
      net_ptr->name = s0;
    }

    // Transitions
    else if (s0 == "tr")
    {
      std::string place_id;
      unsigned int valuation;

      if (ss >> s0)
      {
        net.add_transition(s0);
      }
      else
      {
        throw parse_error("Invalid transition: " + line);
      }

      // Ignore label, if any.
      if ((ss >> std::ws).peek() == std::char_traits<char>::to_int_type(':'))
      {
        ss >> ignore >> ignore; // ':' <label>
      }

      // Time interval.
      const auto peek = (ss >> std::ws).peek();
      if (   peek == std::char_traits<char>::to_int_type('[')
          or peek == std::char_traits<char>::to_int_type(']'))
      {
        char c = ss.get(); // '[' or ']';
        while (ss.good())
        {
          c = ss.get();
          if (c == '[' or c == ']')
          {
            break;
          }
          s1.push_back(c);
        }

        if (not ss.good())
        {
          throw parse_error("Unexpected end of file. Missing '[' or ']'?");
        }

        std::istringstream ss1(s1);
        std::getline(ss1, s2, ','); // split on ','
        unsigned int first, last;
        try
        {
          first = std::stoi(s2);
        }
        catch (const std::invalid_argument&)
        {
          throw parse_error("Expected a value, got: '" + s2 + "'");
        }
        ss1 >> s2;
        try
        {
          last = std::stoi(s2);
        }
        catch (const std::invalid_argument& e)
        {
          if (s2 == "w")
          {
            last = std::numeric_limits<unsigned int>::max();
          }
          else
          {
            throw parse_error("Expected a value, got: '" + s2 + "'");
          }
        }

        net_ptr->add_time_interval(s0, first, last);
      }

      bool found_arrow = false;
      while (ss >> s1)
      {
        if (s1 == "->")
        {
          found_arrow = true;
          break;
        }
        std::tie(place_id, valuation) = place_valuation(s1);
        net.add_pre_place(s0, place_id, valuation);
      }

      if (not found_arrow)
      {
        throw parse_error("Invalid transition (missing '->'): " + line);
      }

      while (ss >> s1)
      {
        std::tie(place_id, valuation) = place_valuation(s1);
        net.add_post_place(s0, place_id, valuation);
      }
    }

    // Places
    else if (s0 == "pl")
    {
      if (ss >> s1)
      {
        // Ignore label, if any.
        if ((ss >> std::ws).peek() == std::char_traits<char>::to_int_type(':'))
        {
          ss >> ignore >> ignore; // ':' <label>
        }

        if (ss >> s2)
        {
          net.add_place(s1, marking(s2));
        }
        else
        {
          net.add_place(s1, 0);
        }
      }
      else
      {
        throw parse_error("Invalid place: " + line);
      }
    }

    // Error.
    else
    {
      throw parse_error("Invalid line: " + line);
    }
  }

  return net_ptr;
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace pnmc::parsers
<commit_msg>Tina parser: reset string used to parse time intervals.<commit_after>#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>

#include "parsers/parse_error.hh"
#include "parsers/tina.hh"

namespace pnmc { namespace parsers {

namespace {

/*------------------------------------------------------------------------------------------------*/

unsigned int
value(std::string::const_iterator cit, std::string::const_iterator cend)
{
  std::string s(cit, cend);
  try
  {
    std::size_t pos;
    auto value = std::stoi(s, &pos);
    std::advance(cit, pos);
    if (std::distance(cit, cend) == 1)
    {
      switch (*cit)
      {
        case 'K': value *= 1000; break;
        case 'M': value *= 1000000; break;
        default: throw parse_error("Invalid suffix, got '" + std::string(cit, cend) + "'");
      }
    }
    else if (std::distance(cit, cend) > 1)
    {
      throw parse_error("Invalid suffix, got " + std::string(cit, cend));
    }
    return value;
  }
  catch (const std::invalid_argument&)
  {
    throw parse_error("Expected a value, got " + s);
  }
}

/*------------------------------------------------------------------------------------------------*/

std::pair<std::string, unsigned int>
place_valuation(const std::string& s)
{
  const auto star_cit = std::find(s.cbegin(), s.cend(), '*');
  if (star_cit == s.cend())
  {
    return std::make_pair(s, 1);
  }
  const auto valuation = value(star_cit + 1, s.cend());
  return std::make_pair(std::string(s.cbegin(), star_cit), valuation);
}

/*------------------------------------------------------------------------------------------------*/

unsigned int
marking(const std::string& s)
{
  if (*s.cbegin() == '(' and *std::prev(s.cend()) == ')')
  {
    return value(std::next(s.cbegin()), std::prev(s.cend()));
  }
  else
  {
    throw parse_error("Invalid marking format: " + s);
  }
}

} // namespace anonymous

/*------------------------------------------------------------------------------------------------*/

std::shared_ptr<pn::net>
tina(std::istream& in)
{
  // Known limitations:
  // - there's must be a transition or place per line
  // - labels are ignored
  // - time intervals are not completely validated (e.g. [0, 2a] is wrongly validated

  std::shared_ptr<pn::net> net_ptr = std::make_shared<pn::net>();
  auto& net = *net_ptr;

  std::string line, s0, s1, s2, ignore;
  line.reserve(1024);

  while (std::getline(in, line))
  {
    std::istringstream ss(line);

    // Empty line or comment.
    if (((ss >> std::ws).peek() == std::char_traits<char>::to_int_type('#')) or not (ss >> s0))
    {
      continue;
    }

    // Net
    if (s0 == "net")
    {
      ss >> s0;
      net_ptr->name = s0;
    }

    // Transitions
    else if (s0 == "tr")
    {
      std::string place_id;
      unsigned int valuation;

      if (ss >> s0)
      {
        net.add_transition(s0);
      }
      else
      {
        throw parse_error("Invalid transition: " + line);
      }

      // Ignore label, if any.
      if ((ss >> std::ws).peek() == std::char_traits<char>::to_int_type(':'))
      {
        ss >> ignore >> ignore; // ':' <label>
      }

      // Time interval.
      const auto peek = (ss >> std::ws).peek();
      if (   peek == std::char_traits<char>::to_int_type('[')
          or peek == std::char_traits<char>::to_int_type(']'))
      {
        s1.clear();
        char c = ss.get(); // '[' or ']';
        while (ss.good())
        {
          c = ss.get();
          if (c == '[' or c == ']')
          {
            break;
          }
          s1.push_back(c);
        }

        if (not ss.good())
        {
          throw parse_error("Unexpected end of file. Missing '[' or ']'?");
        }

        std::istringstream ss1(s1);
        std::getline(ss1, s2, ','); // split on ','
        unsigned int first, last;
        try
        {
          first = std::stoi(s2);
        }
        catch (const std::invalid_argument&)
        {
          throw parse_error("Expected a value, got: '" + s2 + "'");
        }
        ss1 >> s2;
        try
        {
          last = std::stoi(s2);
        }
        catch (const std::invalid_argument& e)
        {
          if (s2 == "w")
          {
            last = std::numeric_limits<unsigned int>::max();
          }
          else
          {
            throw parse_error("Expected a value, got: '" + s2 + "'");
          }
        }

        net_ptr->add_time_interval(s0, first, last);
      }

      bool found_arrow = false;
      while (ss >> s1)
      {
        if (s1 == "->")
        {
          found_arrow = true;
          break;
        }
        std::tie(place_id, valuation) = place_valuation(s1);
        net.add_pre_place(s0, place_id, valuation);
      }

      if (not found_arrow)
      {
        throw parse_error("Invalid transition (missing '->'): " + line);
      }

      while (ss >> s1)
      {
        std::tie(place_id, valuation) = place_valuation(s1);
        net.add_post_place(s0, place_id, valuation);
      }
    }

    // Places
    else if (s0 == "pl")
    {
      if (ss >> s1)
      {
        // Ignore label, if any.
        if ((ss >> std::ws).peek() == std::char_traits<char>::to_int_type(':'))
        {
          ss >> ignore >> ignore; // ':' <label>
        }

        if (ss >> s2)
        {
          net.add_place(s1, marking(s2));
        }
        else
        {
          net.add_place(s1, 0);
        }
      }
      else
      {
        throw parse_error("Invalid place: " + line);
      }
    }

    // Error.
    else
    {
      throw parse_error("Invalid line: " + line);
    }
  }

  return net_ptr;
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace pnmc::parsers
<|endoftext|>