<commit_before>/**
 ** \file object/object-class.cc
 ** \brief Creation of the URBI object object.
 */

//#define ENABLE_DEBUG_TRACES
#include <libport/compiler.hh>
#include <libport/throw-exception.hh>
#include <libport/tokenizer.hh>

#include "kernel/uconnection.hh"

#include "runner/runner.hh"

#include "object/atom.hh"
#include "object/object-class.hh"
#include "object/object.hh"

namespace object
{
  rObject object_class;

  /*--------------------.
  | Object primitives.  |
  `--------------------*/

  static rObject
  object_class_clone (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (1);
    return args[0]->clone();
  }

  static rObject
  object_class_init (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (1);
    return args[0];
  }

  /// Send dumped self on the connection.
  /// args[1], if present, can be the tag to use.
  static rObject
  object_class_dump (runner::Runner& r, objects_type args)
  {
    // Second argument is the tag name.
    std::string tag;
    if (args.size() == 2)
    {
      FETCH_ARG(1, String);
      tag = arg1->value_get().name_get();
    }
    std::ostringstream os;
    args[0]->dump(os, r);
    //for now our best choice is to dump line by line in "system" messages.
    const std::string stream = os.str();
    boost::tokenizer< boost::char_separator<char> > tok =
      libport::make_tokenizer(stream, "\n");
    std::string system_header("*** ");
    BOOST_FOREACH(std::string line, tok)
      r.lobby_get()->value_get().connection.send (
	(system_header+line+"\n").c_str(), tag.c_str());
    return void_class;
  }

  static rObject
  object_echo(runner::Runner& r, objects_type args, bool is_echo)
  {
    // Second argument is the tag name.
    std::string tag;
    if (args.size() == 3)
    {
      FETCH_ARG(2, String);
      tag = arg2->value_get().name_get();
    }

    rObject res = urbi_call(r, args[1], SYMBOL(asString));
    std::string s = res->value<String>().name_get();

    //Hack: special case for Strings to have k1 behavior
    //special case for Strings
    if (is_echo && args[1]->kind_is(Object::kind_string)
      && s[0] == '"' && s.length() && s[s.length()-1] == '"')
      s = libport::unescape(s.substr(1, s.length()-2));

    std::string data = std::string(is_echo?"*** ":"") + s + "\n";
    r.lobby_get()->value_get().connection.send(data.c_str(), data.size(),
					       tag.c_str());

    return void_class;
  }

  /// Send pretty-printed args[1] to the connection.
  static rObject
  object_class_echo (runner::Runner& r, objects_type args)
  {
    return object_echo(r, args, true);
  }

  /// Send pretty-printed self on the connection.
  /// args[1], if present, can be the tag to use.
  static rObject
  object_class_print (runner::Runner& r, objects_type args)
  {
    CHECK_ARG_COUNT_RANGE (1, 2);
    objects_type nargs;
    nargs.push_back(args[0]);
    nargs.insert(nargs.end(),  args.begin(), args.end());
    return object_echo(r, nargs, false);
  }

  /// Return the address of an object as a number, mostly
  /// for debugging purpose.
  static rObject
  object_class_uid (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (1);
    std::string str = (boost::format ("0x%x")
		       % reinterpret_cast<long long> (args[0].get ())).str ();
    return String::fresh(libport::Symbol (str));
  }

  /// Structural equality
  static rObject
  object_class_sameAs(runner::Runner& r, objects_type args)
  {
    // Unless overridden, structural equality is physical equality.
    CHECK_ARG_COUNT (2);
    objects_type mem_args;
    mem_args.push_back(args[1]);
    return urbi_call(r, args[0], SYMBOL(memSameAs), mem_args);
  }

  /// Physical equality
  static rObject
  object_class_memSameAs(runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    rObject res = global_class->slot_get((args[0] == args[1])
					  ? SYMBOL(true)
					  : SYMBOL(false));
    return res;
  }

  static rObject
  object_class_apply (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    FETCH_ARG (1, List);
    if (arg1->value_get ().size () != 1)
      throw PrimitiveError ("apply", "first argument must be [self]");
    return arg1->value_get ().front ();
  }

  static rObject
  object_class_callMessage (runner::Runner& r, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    libport::Symbol msg = args[1]->slot_get(SYMBOL(message))->value<String>();
    rObject code = args[0]->slot_get(msg);
    // FIXME: Sanity checks on the call message are probably required
    object::objects_type self;
    self.push_back(args[0]);
    return r.apply(code, msg, self, args[1]);
  }

  /*---------.
  | Protos.  |
  `---------*/

  /// Adding or removing protos. \a Verb is "add" or "remove".
#define CHANGE_PARENTS(Verb)						\
  static rObject							\
  object_class_ ## Verb ## Proto (runner::Runner&, objects_type args)	\
  {									\
    CHECK_ARG_COUNT(2);							\
    args[0]->proto_ ## Verb (args[1]);					\
    return args[0];							\
  }

  /// Add a proto.
  CHANGE_PARENTS(add);
  /// Remove a proto.
  CHANGE_PARENTS(remove);
#undef CHANGE_PARENTS

  /// Get protos' list.
  static rObject
  object_class_protos (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(1);
    return args[0]->urbi_protos_get ();
  }

  /*--------.
  | Slots.  |
  `--------*/

  /// List of slot names.
  static rObject
  object_class_slotNames (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(1);
    rObject obj = args[0];

    object::list_traits::type l;
    BOOST_FOREACH (const Object::slot_type& p, obj->slots_get())
      l.push_back (object::String::fresh(p.first));

    return object::List::fresh(l);
  }

  /// Get a slot content.
  static rObject
  object_class_getSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(2);
    rObject obj = args[0];
    FETCH_ARG(1, String);
    return obj->slot_get(arg1->value_get());
  }

  // self.getLazyLocalSlot(SLOT-NAME, DEFAULT-VALUE, CREATE?).
  static rObject
  object_class_getLazyLocalSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (4);

    FETCH_ARG (1, String);
    Object::key_type slot_name = arg1->value_get ();

    // If the slot already exists, return its content.
    if (rObject slot = args[0]->own_slot_get (slot_name, 0))
      return slot;

    // The slot doesn't exist. Should we create it?
    if (IS_TRUE (args[3]))
      args[0]->slot_set (slot_name, args[2]);

    // Return the default value for this slot.
    return args[2];
  }

  /// Remove a slot.
  static rObject
  object_class_removeSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(2);
    rObject obj = args[0];
    FETCH_ARG(1, String);
    obj->slot_remove(arg1->value_get());
    return obj;
  }

   /// Locate a slot.
  static rObject
  object_class_locateSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(2);
    FETCH_ARG(1, String);

    rObject o = args[0]->slot_locate(arg1->value_get());
    return o ? o : nil_class;
  }

  static rObject
  object_class_setSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (3);
    FETCH_ARG (1, String);
    args[0]->slot_set (arg1->value_get (), args[2]);
    return args[2];
  }

  static rObject
  object_class_updateSlot (runner::Runner& r, objects_type args)
  {
    CHECK_ARG_COUNT (3);
    FETCH_ARG (1, String);
    args[0]->slot_update(r, arg1->value_get (), args[2]);
    return args[2];
  }

  static rObject
  object_class_changeSlot (runner::Runner& r, objects_type args)
  {
    CHECK_ARG_COUNT (3);
    FETCH_ARG (1, String);
    args[0]->slot_update(r, arg1->value_get (), args[2], false);
    return args[2];
  }

  static rObject
  object_class_makeScope (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    args[0]->locals_set (IS_TRUE (args[1]));
    return args[0];
  }

  static rObject
  object_class_isScope (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (1);
    return Float::fresh(!!args[0]->locals_get ());
  }

  static rObject
  object_class_isA(runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    return Float::fresh(is_a(args[0], args[1])? 1.0:0.0);
  }

  void
  object_class_initialize ()
  {
    /// \a Call gives the name of the C++ function, and \a Name that in Urbi.
#define DECLARE(Name)				\
    DECLARE_PRIMITIVE(object, Name)

    DECLARE(addProto);
    DECLARE(apply);
    DECLARE(callMessage);
    DECLARE(changeSlot);
    DECLARE(clone);
    DECLARE(dump);
    DECLARE(echo); // This guy should be in System.
    DECLARE(getLazyLocalSlot);
    DECLARE(getSlot);
    DECLARE(init);
    DECLARE(isA);
    DECLARE(isScope);
    DECLARE(locateSlot);
    DECLARE(makeScope);
    DECLARE(memSameAs);
    DECLARE(print);
    DECLARE(protos);
    DECLARE(removeProto);
    DECLARE(removeSlot);
    DECLARE(sameAs);
    DECLARE(setSlot);
    DECLARE(slotNames);
    DECLARE(uid);
    DECLARE(updateSlot);
#undef DECLARE
  }

}; // namespace object
<commit_msg>Use a static boost formater.<commit_after>/**
 ** \file object/object-class.cc
 ** \brief Creation of the URBI object object.
 */

//#define ENABLE_DEBUG_TRACES
#include <libport/compiler.hh>
#include <libport/throw-exception.hh>
#include <libport/tokenizer.hh>

#include "kernel/uconnection.hh"

#include "runner/runner.hh"

#include "object/atom.hh"
#include "object/object-class.hh"
#include "object/object.hh"

namespace object
{
  rObject object_class;

  /*--------------------.
  | Object primitives.  |
  `--------------------*/

  static rObject
  object_class_clone (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (1);
    return args[0]->clone();
  }

  static rObject
  object_class_init (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (1);
    return args[0];
  }

  /// Send dumped self on the connection.
  /// args[1], if present, can be the tag to use.
  static rObject
  object_class_dump (runner::Runner& r, objects_type args)
  {
    // Second argument is the tag name.
    std::string tag;
    if (args.size() == 2)
    {
      FETCH_ARG(1, String);
      tag = arg1->value_get().name_get();
    }
    std::ostringstream os;
    args[0]->dump(os, r);
    //for now our best choice is to dump line by line in "system" messages.
    const std::string stream = os.str();
    boost::tokenizer< boost::char_separator<char> > tok =
      libport::make_tokenizer(stream, "\n");
    std::string system_header("*** ");
    BOOST_FOREACH(std::string line, tok)
      r.lobby_get()->value_get().connection.send (
	(system_header+line+"\n").c_str(), tag.c_str());
    return void_class;
  }

  static rObject
  object_echo(runner::Runner& r, objects_type args, bool is_echo)
  {
    // Second argument is the tag name.
    std::string tag;
    if (args.size() == 3)
    {
      FETCH_ARG(2, String);
      tag = arg2->value_get().name_get();
    }

    rObject res = urbi_call(r, args[1], SYMBOL(asString));
    std::string s = res->value<String>().name_get();

    // Hack: special case for Strings to have k1 behavior special case
    // for Strings.
    if (is_echo
        && args[1]->kind_is(Object::kind_string)
        && s[0] == '"'
        && s.length() && s[s.length()-1] == '"')
      s = libport::unescape(s.substr(1, s.length()-2));

    std::string data = std::string(is_echo?"*** ":"") + s + "\n";
    r.lobby_get()->value_get().connection.send(data.c_str(), data.size(),
					       tag.c_str());

    return void_class;
  }

  /// Send pretty-printed args[1] to the connection.
  static rObject
  object_class_echo (runner::Runner& r, objects_type args)
  {
    return object_echo(r, args, true);
  }

  /// Send pretty-printed self on the connection.
  /// args[1], if present, can be the tag to use.
  static rObject
  object_class_print (runner::Runner& r, objects_type args)
  {
    CHECK_ARG_COUNT_RANGE (1, 2);
    objects_type nargs;
    nargs.push_back(args[0]);
    nargs.insert(nargs.end(),  args.begin(), args.end());
    return object_echo(r, nargs, false);
  }

  /// Return the address of an object as a number, mostly
  /// for debugging purpose.
  static rObject
  object_class_uid (runner::Runner&, objects_type args)
  {
    static boost::format uid("0x%x");
    CHECK_ARG_COUNT(1);
    return
      String::fresh(libport::Symbol
                    (str(uid % reinterpret_cast<long long>(args[0].get()))));
  }

  /// Structural equality
  static rObject
  object_class_sameAs(runner::Runner& r, objects_type args)
  {
    // Unless overridden, structural equality is physical equality.
    CHECK_ARG_COUNT (2);
    objects_type mem_args;
    mem_args.push_back(args[1]);
    return urbi_call(r, args[0], SYMBOL(memSameAs), mem_args);
  }

  /// Physical equality
  static rObject
  object_class_memSameAs(runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    rObject res = global_class->slot_get((args[0] == args[1])
					  ? SYMBOL(true)
					  : SYMBOL(false));
    return res;
  }

  static rObject
  object_class_apply (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    FETCH_ARG (1, List);
    if (arg1->value_get ().size () != 1)
      throw PrimitiveError ("apply", "first argument must be [self]");
    return arg1->value_get ().front ();
  }

  static rObject
  object_class_callMessage (runner::Runner& r, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    libport::Symbol msg = args[1]->slot_get(SYMBOL(message))->value<String>();
    rObject code = args[0]->slot_get(msg);
    // FIXME: Sanity checks on the call message are probably required
    object::objects_type self;
    self.push_back(args[0]);
    return r.apply(code, msg, self, args[1]);
  }

  /*---------.
  | Protos.  |
  `---------*/

  /// Adding or removing protos. \a Verb is "add" or "remove".
#define CHANGE_PARENTS(Verb)						\
  static rObject							\
  object_class_ ## Verb ## Proto (runner::Runner&, objects_type args)	\
  {									\
    CHECK_ARG_COUNT(2);							\
    args[0]->proto_ ## Verb (args[1]);					\
    return args[0];							\
  }

  /// Add a proto.
  CHANGE_PARENTS(add);
  /// Remove a proto.
  CHANGE_PARENTS(remove);
#undef CHANGE_PARENTS

  /// Get protos' list.
  static rObject
  object_class_protos (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(1);
    return args[0]->urbi_protos_get ();
  }

  /*--------.
  | Slots.  |
  `--------*/

  /// List of slot names.
  static rObject
  object_class_slotNames (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(1);
    rObject obj = args[0];

    object::list_traits::type l;
    BOOST_FOREACH (const Object::slot_type& p, obj->slots_get())
      l.push_back (object::String::fresh(p.first));

    return object::List::fresh(l);
  }

  /// Get a slot content.
  static rObject
  object_class_getSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(2);
    rObject obj = args[0];
    FETCH_ARG(1, String);
    return obj->slot_get(arg1->value_get());
  }

  // self.getLazyLocalSlot(SLOT-NAME, DEFAULT-VALUE, CREATE?).
  static rObject
  object_class_getLazyLocalSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (4);

    FETCH_ARG (1, String);
    Object::key_type slot_name = arg1->value_get ();

    // If the slot already exists, return its content.
    if (rObject slot = args[0]->own_slot_get (slot_name, 0))
      return slot;

    // The slot doesn't exist. Should we create it?
    if (IS_TRUE (args[3]))
      args[0]->slot_set (slot_name, args[2]);

    // Return the default value for this slot.
    return args[2];
  }

  /// Remove a slot.
  static rObject
  object_class_removeSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(2);
    rObject obj = args[0];
    FETCH_ARG(1, String);
    obj->slot_remove(arg1->value_get());
    return obj;
  }

   /// Locate a slot.
  static rObject
  object_class_locateSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT(2);
    FETCH_ARG(1, String);

    rObject o = args[0]->slot_locate(arg1->value_get());
    return o ? o : nil_class;
  }

  static rObject
  object_class_setSlot (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (3);
    FETCH_ARG (1, String);
    args[0]->slot_set (arg1->value_get (), args[2]);
    return args[2];
  }

  static rObject
  object_class_updateSlot (runner::Runner& r, objects_type args)
  {
    CHECK_ARG_COUNT (3);
    FETCH_ARG (1, String);
    args[0]->slot_update(r, arg1->value_get (), args[2]);
    return args[2];
  }

  static rObject
  object_class_changeSlot (runner::Runner& r, objects_type args)
  {
    CHECK_ARG_COUNT (3);
    FETCH_ARG (1, String);
    args[0]->slot_update(r, arg1->value_get (), args[2], false);
    return args[2];
  }

  static rObject
  object_class_makeScope (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    args[0]->locals_set (IS_TRUE (args[1]));
    return args[0];
  }

  static rObject
  object_class_isScope (runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (1);
    return Float::fresh(!!args[0]->locals_get ());
  }

  static rObject
  object_class_isA(runner::Runner&, objects_type args)
  {
    CHECK_ARG_COUNT (2);
    return Float::fresh(is_a(args[0], args[1])? 1.0:0.0);
  }

  void
  object_class_initialize ()
  {
    /// \a Call gives the name of the C++ function, and \a Name that in Urbi.
#define DECLARE(Name)				\
    DECLARE_PRIMITIVE(object, Name)

    DECLARE(addProto);
    DECLARE(apply);
    DECLARE(callMessage);
    DECLARE(changeSlot);
    DECLARE(clone);
    DECLARE(dump);
    DECLARE(echo); // This guy should be in System.
    DECLARE(getLazyLocalSlot);
    DECLARE(getSlot);
    DECLARE(init);
    DECLARE(isA);
    DECLARE(isScope);
    DECLARE(locateSlot);
    DECLARE(makeScope);
    DECLARE(memSameAs);
    DECLARE(print);
    DECLARE(protos);
    DECLARE(removeProto);
    DECLARE(removeSlot);
    DECLARE(sameAs);
    DECLARE(setSlot);
    DECLARE(slotNames);
    DECLARE(uid);
    DECLARE(updateSlot);
#undef DECLARE
  }

}; // namespace object
<|endoftext|>


<commit_before>/*
* Copyright (c) 2003-2011 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

#ifndef __WX__
#include <afxwin.h>
#endif

#include "../KeySend.h"
#include "../env.h"
#include "../debug.h"

class CKeySendImpl {
public:
  void SendChar(TCHAR c);
  void OldSendChar(TCHAR c);
  void NewSendChar(TCHAR c);
  int m_delay;
  HKL m_hlocale;
  bool m_isOldOS;
};

CKeySend::CKeySend(bool bForceOldMethod)
 : m_delayMS(10)
{
  m_impl = new CKeySendImpl;
  m_impl->m_delay = m_delayMS;
  // We want to use keybd_event (OldSendChar) for Win2K & older,
  // SendInput (NewSendChar) for newer versions.
  if (bForceOldMethod)
    m_impl->m_isOldOS = true;
  else {
    DWORD majorVersion, minorVersion;
    pws_os::getosversion(majorVersion, minorVersion);
    m_impl->m_isOldOS = ((majorVersion <= 4) ||
                         (majorVersion == 5 && minorVersion == 0));
  }
  // get the locale of the current thread.
  // we are assuming that all window and threading in the
  // current users desktop have the same locale.
  m_impl->m_hlocale = GetKeyboardLayout(0);
}

CKeySend::~CKeySend()
{
  delete m_impl;
}

void CKeySend::SendString(const StringX &data)
{
  for (StringX::const_iterator iter = data.begin();
       iter != data.end(); iter++)
    m_impl->SendChar(*iter);
}

void CKeySendImpl::SendChar(TCHAR c)
{
  if (m_isOldOS)
    OldSendChar(c);
  else
    NewSendChar(c);
}

void CKeySendImpl::NewSendChar(TCHAR c)
{
  UINT status;
  INPUT input[2];
  input[0].ki.time = input[1].ki.time = 0; //probably needed
  input[0].ki.dwExtraInfo = input[1].ki.dwExtraInfo = 0; //probably not

  input[0].type = input[1].type = INPUT_KEYBOARD;

  bool tabWait = false; // if sending tab\newline, wait a bit after send
  //                       for the dust to settle. Thanks to Larry...
  switch (c) {
    case L'\t':
    case L'\r':
      input[0].ki.wVk = c == L'\t' ? VK_TAB : VK_RETURN;
      input[0].ki.wScan = 0;
      input[0].ki.dwFlags = 0;
      tabWait = true;
      break;
    default:
      input[0].ki.wVk = 0;
      input[0].ki.wScan = c;
      input[0].ki.dwFlags = KEYEVENTF_UNICODE;
      break;
  }
  // add the key-up event
  input[1].ki = input[0].ki;
  input[1].ki.dwFlags |= KEYEVENTF_KEYUP;

  status = ::SendInput(2, input, sizeof(INPUT));
  if (status != 2)
    pws_os::Trace(L"CKeySend::SendChar: SendInput failed status=%d\n", status);
  // wait at least 200 mS if we just sent a tab, regardless of m_delay
  int delay = ( m_delay < 200 && tabWait) ? 200 : m_delay;
  ::Sleep(delay);
}

void CKeySendImpl::OldSendChar(TCHAR c)
{
  BOOL shiftDown = false; //assume shift key is up at start.
  BOOL ctrlDown = false;
  BOOL altDown = false;
  SHORT keyScanCode = VkKeyScanEx(c, m_hlocale);
  // high order byte of keyscancode indicates if SHIFT, CTRL etc keys should be down
  if (keyScanCode & 0x100) {
    shiftDown=true;
    //send a shift down
    keybd_event(VK_SHIFT, (BYTE) MapVirtualKeyEx(VK_SHIFT, 0, m_hlocale), 0, 3); //Fixes bug #1208955
  }

  if (keyScanCode & 0x200) {
    ctrlDown=true;
    //send a ctrl down
    keybd_event(VK_CONTROL, (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_hlocale), KEYEVENTF_EXTENDEDKEY, 0);
  }

  if (keyScanCode & 0x400) {
    altDown=true;
    //send a alt down
    keybd_event(VK_MENU, (BYTE) MapVirtualKeyEx(VK_MENU, 0, m_hlocale), KEYEVENTF_EXTENDEDKEY, 0);
  }

  // the lower order byte has the key scan code we need.
  keyScanCode =(SHORT)(keyScanCode & 0xFF);

  keybd_event((BYTE)keyScanCode, (BYTE) MapVirtualKeyEx(keyScanCode, 0, m_hlocale), 0, 0);
  keybd_event((BYTE)keyScanCode, (BYTE) MapVirtualKeyEx(keyScanCode, 0, m_hlocale), KEYEVENTF_KEYUP, 0);

  if (shiftDown) {
    //send a shift up
    keybd_event(VK_SHIFT, (BYTE) MapVirtualKeyEx(VK_SHIFT, 0, m_hlocale), KEYEVENTF_KEYUP, 3); //Fixes bug #1208955
    shiftDown=false;
  }

  if (ctrlDown) {
    //send a ctrl up
    keybd_event(VK_CONTROL, (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_hlocale),
                KEYEVENTF_KEYUP |KEYEVENTF_EXTENDEDKEY, 0);
    ctrlDown=false;
  }

  if (altDown) {
    //send a alt up
    keybd_event(VK_MENU, (BYTE) MapVirtualKeyEx(VK_MENU, 0, m_hlocale), KEYEVENTF_KEYUP |KEYEVENTF_EXTENDEDKEY, 0);
    altDown=false;
  }
  ::Sleep(m_delay);
}

static void newSendVK(WORD vk)
{
  UINT status;
  INPUT input[2];
  input[0].ki.time = input[1].ki.time = 0; //probably needed
  input[0].ki.dwExtraInfo = input[1].ki.dwExtraInfo = 0; //probably not
  input[0].type = input[1].type = INPUT_KEYBOARD;
  input[0].ki.wVk = input[1].ki.wVk = vk;
  input[0].ki.dwFlags = 0;
  input[1].ki.dwFlags = KEYEVENTF_KEYUP;
  status = ::SendInput(2, input, sizeof(INPUT));
  if (status != 2)
    pws_os::Trace(L"newSendVK: SendInput failed status=%d\n", status);
}

void CKeySend::ResetKeyboardState() const
{
  // We need to make sure that the Control Key is still not down.
  // It will be down while the user presses ctrl-T the shortcut for autotype.

  BYTE keys[256];

  GetKeyboardState((LPBYTE)&keys);

  while((keys[VK_CONTROL] & 0x80) != 0) {
    // VK_CONTROL is down so send a key down and an key up...
    if (m_impl->m_isOldOS) {
      keybd_event(VK_CONTROL, (BYTE)MapVirtualKeyEx(VK_CONTROL, 0, m_impl->m_hlocale),
                  KEYEVENTF_EXTENDEDKEY, 0);
      keybd_event(VK_CONTROL, (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_impl->m_hlocale),
                  KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
    } else {
      newSendVK(VK_CONTROL); // Send Ctrl keydown/keyup via SendInput
    }

    //now we let the messages be processed by the applications to set the keyboard state
    MSG msg;
    while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
      // so there is a message process it.
#ifndef __WX__
      if (!AfxGetThread()->PumpMessage())
        break;
#else
      // Not sure this is correct!
      if (msg.message == WM_QUIT) {
        // Put it back on the queue and leave now
        ::PostQuitMessage(0);
        return;
      }

      ::TranslateMessage(&msg);
      ::DispatchMessage(&msg);
#endif
    }

    ::Sleep(10);
    SecureZeroMemory(keys, sizeof(keys));
    GetKeyboardState((LPBYTE)&keys);
  } // while
}

void CKeySend::SetDelay(unsigned d)
{
  m_delayMS = m_impl->m_delay = d;
}

// SetAndDelay allows users to put \d500\d10 in autotype and
// then it will cause a delay of half a second then subsequent
// key stokes will be delayed by 10 ms
// thedavecollins 2004-08-05

void CKeySend::SetAndDelay(unsigned d) {
  SetDelay(d);
  ::Sleep(m_delayMS);
}

void CKeySend::SetCapsLock(const bool bState)
{
  BYTE keyState[256];

  GetKeyboardState((LPBYTE)&keyState);
  if ((bState && !(keyState[VK_CAPITAL] & 0x01)) ||
      (!bState && (keyState[VK_CAPITAL] & 0x01))) {
    if (m_impl->m_isOldOS) {
      // Simulate a key press
      keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
      // Simulate a key release
      keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
    } else {
      newSendVK(VK_CAPITAL); // Send CapLock keydown/keyup via SendInput
    }
  }

  MSG msg;
  while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
    // so there is a message process it.
#ifndef __WX__
    if (!AfxGetThread()->PumpMessage())
      break;
#else
    // Not sure this is correct!
    if (msg.message == WM_QUIT) {
      // Put it back on the queue and leave now
      ::PostQuitMessage(0);
      return;
    }

    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);
#endif
  }
}

bool CKeySend::isCapsLocked() const
{
  return ::GetKeyState(VK_CAPITAL) != 0;
}

void CKeySend::BlockInput(bool bi) const
{
  ::BlockInput(bi ? TRUE : FALSE);
}

<commit_msg>Implement Larry's suggestions re: default delay and pre-tab wait<commit_after>/*
* Copyright (c) 2003-2011 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

#ifndef __WX__
#include <afxwin.h>
#endif

#include "../KeySend.h"
#include "../env.h"
#include "../debug.h"

class CKeySendImpl {
public:
  void SendChar(TCHAR c);
  void OldSendChar(TCHAR c);
  void NewSendChar(TCHAR c);
  int m_delay;
  HKL m_hlocale;
  bool m_isOldOS;
};

// New default delay of 50ms (increased from 10ms) introduced 2011-05-08

CKeySend::CKeySend(bool bForceOldMethod)
 : m_delayMS(50)
{
  m_impl = new CKeySendImpl;
  m_impl->m_delay = m_delayMS;
  // We want to use keybd_event (OldSendChar) for Win2K & older,
  // SendInput (NewSendChar) for newer versions.
  if (bForceOldMethod)
    m_impl->m_isOldOS = true;
  else {
    DWORD majorVersion, minorVersion;
    pws_os::getosversion(majorVersion, minorVersion);
    m_impl->m_isOldOS = ((majorVersion <= 4) ||
                         (majorVersion == 5 && minorVersion == 0));
  }
  // get the locale of the current thread.
  // we are assuming that all window and threading in the
  // current users desktop have the same locale.
  m_impl->m_hlocale = GetKeyboardLayout(0);
}

CKeySend::~CKeySend()
{
  delete m_impl;
}

void CKeySend::SendString(const StringX &data)
{
  for (StringX::const_iterator iter = data.begin();
       iter != data.end(); iter++)
    m_impl->SendChar(*iter);
}

void CKeySendImpl::SendChar(TCHAR c)
{
  if (m_isOldOS)
    OldSendChar(c);
  else
    NewSendChar(c);
}

void CKeySendImpl::NewSendChar(TCHAR c)
{
  UINT status;
  INPUT input[2];
  input[0].ki.time = input[1].ki.time = 0; //probably needed
  input[0].ki.dwExtraInfo = input[1].ki.dwExtraInfo = 0; //probably not

  input[0].type = input[1].type = INPUT_KEYBOARD;

  bool tabWait = false; // if sending tab\newline, wait a bit after send
  //                       for the dust to settle. Thanks to Larry...

  // Again thanks to Larry - always delay by 100ms before a tab
  if (c == L'\t')
    ::Sleep(100);

  switch (c) {
    case L'\t':
    case L'\r':
      input[0].ki.wVk = c == L'\t' ? VK_TAB : VK_RETURN;
      input[0].ki.wScan = 0;
      input[0].ki.dwFlags = 0;
      tabWait = true;
      break;
    default:
      input[0].ki.wVk = 0;
      input[0].ki.wScan = c;
      input[0].ki.dwFlags = KEYEVENTF_UNICODE;
      break;
  }
  // add the key-up event
  input[1].ki = input[0].ki;
  input[1].ki.dwFlags |= KEYEVENTF_KEYUP;

  status = ::SendInput(2, input, sizeof(INPUT));
  if (status != 2)
    pws_os::Trace(L"CKeySend::SendChar: SendInput failed status=%d\n", status);
  // wait at least 200 mS if we just sent a tab, regardless of m_delay
  int delay = (m_delay < 200 && tabWait) ? 200 : m_delay;
  ::Sleep(delay);
}

void CKeySendImpl::OldSendChar(TCHAR c)
{
  BOOL shiftDown = false; //assume shift key is up at start.
  BOOL ctrlDown = false;
  BOOL altDown = false;
  SHORT keyScanCode = VkKeyScanEx(c, m_hlocale);
  // high order byte of keyscancode indicates if SHIFT, CTRL etc keys should be down
  if (keyScanCode & 0x100) {
    shiftDown=true;
    //send a shift down
    keybd_event(VK_SHIFT, (BYTE) MapVirtualKeyEx(VK_SHIFT, 0, m_hlocale), 0, 3); //Fixes bug #1208955
  }

  if (keyScanCode & 0x200) {
    ctrlDown=true;
    //send a ctrl down
    keybd_event(VK_CONTROL, (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_hlocale), KEYEVENTF_EXTENDEDKEY, 0);
  }

  if (keyScanCode & 0x400) {
    altDown=true;
    //send a alt down
    keybd_event(VK_MENU, (BYTE) MapVirtualKeyEx(VK_MENU, 0, m_hlocale), KEYEVENTF_EXTENDEDKEY, 0);
  }

  // the lower order byte has the key scan code we need.
  keyScanCode =(SHORT)(keyScanCode & 0xFF);

  keybd_event((BYTE)keyScanCode, (BYTE) MapVirtualKeyEx(keyScanCode, 0, m_hlocale), 0, 0);
  keybd_event((BYTE)keyScanCode, (BYTE) MapVirtualKeyEx(keyScanCode, 0, m_hlocale), KEYEVENTF_KEYUP, 0);

  if (shiftDown) {
    //send a shift up
    keybd_event(VK_SHIFT, (BYTE) MapVirtualKeyEx(VK_SHIFT, 0, m_hlocale), KEYEVENTF_KEYUP, 3); //Fixes bug #1208955
    shiftDown=false;
  }

  if (ctrlDown) {
    //send a ctrl up
    keybd_event(VK_CONTROL, (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_hlocale),
                KEYEVENTF_KEYUP |KEYEVENTF_EXTENDEDKEY, 0);
    ctrlDown=false;
  }

  if (altDown) {
    //send a alt up
    keybd_event(VK_MENU, (BYTE) MapVirtualKeyEx(VK_MENU, 0, m_hlocale), KEYEVENTF_KEYUP |KEYEVENTF_EXTENDEDKEY, 0);
    altDown=false;
  }
  ::Sleep(m_delay);
}

static void newSendVK(WORD vk)
{
  UINT status;
  INPUT input[2];
  input[0].ki.time = input[1].ki.time = 0; //probably needed
  input[0].ki.dwExtraInfo = input[1].ki.dwExtraInfo = 0; //probably not
  input[0].type = input[1].type = INPUT_KEYBOARD;
  input[0].ki.wVk = input[1].ki.wVk = vk;
  input[0].ki.dwFlags = 0;
  input[1].ki.dwFlags = KEYEVENTF_KEYUP;
  status = ::SendInput(2, input, sizeof(INPUT));
  if (status != 2)
    pws_os::Trace(L"newSendVK: SendInput failed status=%d\n", status);
}

void CKeySend::ResetKeyboardState() const
{
  // We need to make sure that the Control Key is still not down.
  // It will be down while the user presses ctrl-T the shortcut for autotype.

  BYTE keys[256];

  GetKeyboardState((LPBYTE)&keys);

  while((keys[VK_CONTROL] & 0x80) != 0) {
    // VK_CONTROL is down so send a key down and an key up...
    if (m_impl->m_isOldOS) {
      keybd_event(VK_CONTROL, (BYTE)MapVirtualKeyEx(VK_CONTROL, 0, m_impl->m_hlocale),
                  KEYEVENTF_EXTENDEDKEY, 0);
      keybd_event(VK_CONTROL, (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_impl->m_hlocale),
                  KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
    } else {
      newSendVK(VK_CONTROL); // Send Ctrl keydown/keyup via SendInput
    }

    //now we let the messages be processed by the applications to set the keyboard state
    MSG msg;
    while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
      // so there is a message process it.
#ifndef __WX__
      if (!AfxGetThread()->PumpMessage())
        break;
#else
      // Not sure this is correct!
      if (msg.message == WM_QUIT) {
        // Put it back on the queue and leave now
        ::PostQuitMessage(0);
        return;
      }

      ::TranslateMessage(&msg);
      ::DispatchMessage(&msg);
#endif
    }

    ::Sleep(10);
    SecureZeroMemory(keys, sizeof(keys));
    GetKeyboardState((LPBYTE)&keys);
  } // while
}

void CKeySend::SetDelay(unsigned d)
{
  m_delayMS = m_impl->m_delay = d;
}

// SetAndDelay allows users to put \d500\d10 in autotype and
// then it will cause a delay of half a second then subsequent
// key stokes will be delayed by 10 ms
// thedavecollins 2004-08-05

void CKeySend::SetAndDelay(unsigned d) {
  SetDelay(d);
  ::Sleep(m_delayMS);
}

void CKeySend::SetCapsLock(const bool bState)
{
  BYTE keyState[256];

  GetKeyboardState((LPBYTE)&keyState);
  if ((bState && !(keyState[VK_CAPITAL] & 0x01)) ||
      (!bState && (keyState[VK_CAPITAL] & 0x01))) {
    if (m_impl->m_isOldOS) {
      // Simulate a key press
      keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
      // Simulate a key release
      keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
    } else {
      newSendVK(VK_CAPITAL); // Send CapLock keydown/keyup via SendInput
    }
  }

  MSG msg;
  while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
    // so there is a message process it.
#ifndef __WX__
    if (!AfxGetThread()->PumpMessage())
      break;
#else
    // Not sure this is correct!
    if (msg.message == WM_QUIT) {
      // Put it back on the queue and leave now
      ::PostQuitMessage(0);
      return;
    }

    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);
#endif
  }
}

bool CKeySend::isCapsLocked() const
{
  return ::GetKeyState(VK_CAPITAL) != 0;
}

void CKeySend::BlockInput(bool bi) const
{
  ::BlockInput(bi ? TRUE : FALSE);
}

<|endoftext|>


<commit_before>/*
 * =====================================================================================
 *
 *       Filename:  requesthandler.cpp
 *
 *    Description:  Handle requests
 *
 *        Version:  0.1
 *        Created:  28/01/13 21:47:34
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Daniel Bugl <Daniel.Bugl@touchlay.com>
 *   Organization:  TouchLay
 *
 * =====================================================================================
 */

// Headers
#include "requesthandler.h"

/* RequestHandler: Constructor */
RequestHandler::RequestHandler(int connection, char *cip) {
 clientip = cip;

 // Initialise request
 InitRequest(&request);

 // Handle request and check request type
 if (handle(connection) == true) {
  #ifdef DEBUG
  printf("[DEBUG] [request] handle() returned true, continuing!\n");
  #endif
  if (request.type == HTTP) {
   #ifdef DEBUG
   printf("[DEBUG] [request_http] This is an HTTP request.\n");
   #endif
   if (request.status == 200) {
    #ifdef DEBUG
    printf("[DEBUG] [request_http] Status 200, returning result.\n");
    #endif
    if (parseJSON()) {
     API api = API(jRoot);
     #ifdef DEBUG
     printf("[DEBUG] [request_api] Got API result(%lu): %s\n", strlen(api.result.c_str()), api.result.c_str());
     #endif
     outputHTTP(connection, &request, api.result.c_str());
    } else outputHTTP(connection, &request, "{\"type\": \"error\", \"msg\": \"Invalid JSON.\"}");
    #ifdef DEBUG
    printf("[DEBUG] [request_http] Answered to request with HTTP.\n");
    #endif
   } else {
    char buffer[32];
    sprintf(buffer, "{\"type\": \"error\", \"msg\": \"HTTP Error %d\"}", request.status);
    outputHTTP(connection, &request, (const char*)&buffer);
   }
  } else if (request.type == TN) {
   #ifdef DEBUG
   printf("[DEBUG] [request_tn] This is a TN request.\n");
   #endif
   if (parseJSON()) {
    API api = API(jRoot);
    #ifdef DEBUG
    printf("[DEBUG] [request_api] Got API result(%lu): %s\n", strlen(api.result.c_str()), api.result.c_str());
    #endif
    s_writeline(connection, api.result.c_str(), strlen(api.result.c_str()));
   } else s_writeline(connection, "{\"type\": \"error\", \"msg\": \"Invalid JSON.\"}", 41);
   #ifdef DEBUG
   printf("[DEBUG] [request_tn] Answered to request with TN.\n");
   #endif
  } else printf("[WARN ] [request] Unknown request type, killing request.\n");
 } else printf("[WARN ] [request] Couldn't handle request, killing it.\n");
}

/* parseJSON: JSON parser */
bool RequestHandler::parseJSON() {
 if (request.type == HTTP) memmove(request.resource, request.resource+1, strlen(request.resource)); // Remove / prefix from the GET request
 std::string data = decodeURI(request.resource);
 #ifdef DEBUG
 std::cout << "[DEBUG] [request_json] Got data: " << data << "\n";
 #endif
 if (!jReader.parse(data, jRoot, false)) { printf("[WARN ] [request_json] Invalid JSON.\n"); return false; }
 else return true;
}

/* ~RequestHandler: Destructor */
RequestHandler::~RequestHandler() {
 // Free request data stored in ram
 //FreeRequest(&request);

 #ifdef DEBUG
 printf("[DEBUG] [request] Destructed RequestHandler.\n");
 #endif
}

/* InitRequest: Initialises the request data */
void RequestHandler::InitRequest(Request *request) {
 request->status = 200;
 request->method = UNSUPPORTED;
 request->resource = NULL;
 #ifdef DEBUG
 printf("[DEBUG] [request] Initialised request.\n");
 #endif
}

/* FreeRequest: Clear the request data */
void RequestHandler::FreeRequest(Request *request) {
 if (request->resource) delete request->resource;
 #ifdef DEBUG
 printf("[DEBUG] [request] Free'd request.\n");
 #endif
}

/* handle: Handle a request */
bool RequestHandler::handle(int connection) {
 char buffer[MAX_REQ_LINE] = {0};
 int rval;
 fd_set fds;
 struct timeval tv;

 // Timeout
 tv.tv_sec = REQUEST_TIMEOUT;
 tv.tv_usec = 0;

 do {
  // Reset FD
  FD_ZERO(&fds);
  FD_SET(connection, &fds);

  rval = select(connection+1, &fds, NULL, NULL, &tv); // Select from request

  if (rval < 0) printf("[WARN ] [request_handle] Couldn't select from request.\n");
  else if (rval == 0) return false; // Timeout, kill request
  else {
   s_readline(connection, buffer, MAX_REQ_LINE - 1);

   if (buffer[0] == '{') {
    request.type = TN;
    request.resource = buffer;
    return true;
   } else {
    request.type = HTTP;
    strim(buffer);
   }

   // HTTP stuff
   if (buffer[0] == '\0') break; // End of HTTP headers
   if (parseHTTPHeader(buffer, &request)) break; // Parse headers
  }
 } while (request.level != SIMPLE);
 return true; // Successfully processed
}

bool RequestHandler::outputHTTP(int connection, Request *request, const char *content) {
 char buffer[32];

 sprintf(buffer, "HTTP/1.1 %d OK\r\n", request->status);
 s_writeline(connection, buffer, strlen(buffer));
 s_writeline(connection, "Server: TNREQ/0.2\r\n", 19);
 s_writeline(connection, "Content-Type: application/json\r\n", 32);
 sprintf(buffer, "Content-Length: %lu\r\n", strlen(content));
 s_writeline(connection, buffer, strlen(buffer));
 s_writeline(connection, "\r\n", 2);
 s_writeline(connection, content, strlen(content));

 usleep(1000000); // FIXME: This is a quickfix, it should be changed later

 return true;
}

bool RequestHandler::parseHTTPHeader(char *buffer, Request *request) {
 static bool first_line = true;
 char *temp = NULL;
 char *endptr = NULL;
 int len;
 if (first_line == true) {
  if (!strncmp(buffer, "GET ", 4)) {
   request->method = GET;
   buffer += 4;
  } else if (!strncmp(buffer, "HEAD ", 5)) {
   request->method = HEAD;
   buffer += 5;
  } else {
   request->method = UNSUPPORTED;
   request->status = 501;
   return false;
  }
  while (*buffer && isspace(*buffer)) buffer++; // Skip the start
  endptr = strchr(buffer, ' ');
  if (endptr == NULL) len = strlen(buffer);
  else len = endptr - buffer;

  if (len == 0) {
   request->status = 400;
   return false;
  }

  request->resource = (char *)calloc(len + 1, sizeof(char));
  strncpy(request->resource, buffer, len);

  if (strstr(buffer, "HTTP/")) request->level = FULL;
  else request->level = SIMPLE;

  first_line = 0;
  return true;
 }

 // More headers aside from the request line
 endptr = strchr(buffer, 58); // 58 is a : character
 if (endptr == NULL) {
  request->status = 400;
  return false;
 }

 temp = (char *)calloc((endptr - buffer) + 1, sizeof(char));
 strncpy(temp, buffer, (endptr - buffer));
 stoupper(temp);

 buffer = endptr + 1;
 while (*buffer && isspace(*buffer)) ++buffer;
 if (*buffer == '\0') return true;

 return true;
}
<commit_msg>Invalid JSON message is now DEBUG instead of WARN.<commit_after>/*
 * =====================================================================================
 *
 *       Filename:  requesthandler.cpp
 *
 *    Description:  Handle requests
 *
 *        Version:  0.1
 *        Created:  28/01/13 21:47:34
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Daniel Bugl <Daniel.Bugl@touchlay.com>
 *   Organization:  TouchLay
 *
 * =====================================================================================
 */

// Headers
#include "requesthandler.h"

/* RequestHandler: Constructor */
RequestHandler::RequestHandler(int connection, char *cip) {
 clientip = cip;

 // Initialise request
 InitRequest(&request);

 // Handle request and check request type
 if (handle(connection) == true) {
  #ifdef DEBUG
  printf("[DEBUG] [request] handle() returned true, continuing!\n");
  #endif
  if (request.type == HTTP) {
   #ifdef DEBUG
   printf("[DEBUG] [request_http] This is an HTTP request.\n");
   #endif
   if (request.status == 200) {
    #ifdef DEBUG
    printf("[DEBUG] [request_http] Status 200, returning result.\n");
    #endif
    if (parseJSON()) {
     API api = API(jRoot);
     #ifdef DEBUG
     printf("[DEBUG] [request_api] Got API result(%lu): %s\n", strlen(api.result.c_str()), api.result.c_str());
     #endif
     outputHTTP(connection, &request, api.result.c_str());
    } else outputHTTP(connection, &request, "{\"type\": \"error\", \"msg\": \"Invalid JSON.\"}");
    #ifdef DEBUG
    printf("[DEBUG] [request_http] Answered to request with HTTP.\n");
    #endif
   } else {
    char buffer[32];
    sprintf(buffer, "{\"type\": \"error\", \"msg\": \"HTTP Error %d\"}", request.status);
    outputHTTP(connection, &request, (const char*)&buffer);
   }
  } else if (request.type == TN) {
   #ifdef DEBUG
   printf("[DEBUG] [request_tn] This is a TN request.\n");
   #endif
   if (parseJSON()) {
    API api = API(jRoot);
    #ifdef DEBUG
    printf("[DEBUG] [request_api] Got API result(%lu): %s\n", strlen(api.result.c_str()), api.result.c_str());
    #endif
    s_writeline(connection, api.result.c_str(), strlen(api.result.c_str()));
   } else s_writeline(connection, "{\"type\": \"error\", \"msg\": \"Invalid JSON.\"}", 41);
   #ifdef DEBUG
   printf("[DEBUG] [request_tn] Answered to request with TN.\n");
   #endif
  } else printf("[WARN ] [request] Unknown request type, killing request.\n");
 } else printf("[WARN ] [request] Couldn't handle request, killing it.\n");
}

/* parseJSON: JSON parser */
bool RequestHandler::parseJSON() {
 if (request.type == HTTP) memmove(request.resource, request.resource+1, strlen(request.resource)); // Remove / prefix from the GET request
 std::string data = decodeURI(request.resource);
 #ifdef DEBUG
 std::cout << "[DEBUG] [request_json] Got data: " << data << "\n";
 #endif
 if (!jReader.parse(data, jRoot, false)) {
  #ifdef DEBUG
  printf("[DEBUG] [request_json] Invalid JSON (%s).\n", data);
  #endif
  return false;
 }
 else return true;
}

/* ~RequestHandler: Destructor */
RequestHandler::~RequestHandler() {
 // Free request data stored in ram
 //FreeRequest(&request);

 #ifdef DEBUG
 printf("[DEBUG] [request] Destructed RequestHandler.\n");
 #endif
}

/* InitRequest: Initialises the request data */
void RequestHandler::InitRequest(Request *request) {
 request->status = 200;
 request->method = UNSUPPORTED;
 request->resource = NULL;
 #ifdef DEBUG
 printf("[DEBUG] [request] Initialised request.\n");
 #endif
}

/* FreeRequest: Clear the request data */
void RequestHandler::FreeRequest(Request *request) {
 if (request->resource) delete request->resource;
 #ifdef DEBUG
 printf("[DEBUG] [request] Free'd request.\n");
 #endif
}

/* handle: Handle a request */
bool RequestHandler::handle(int connection) {
 char buffer[MAX_REQ_LINE] = {0};
 int rval;
 fd_set fds;
 struct timeval tv;

 // Timeout
 tv.tv_sec = REQUEST_TIMEOUT;
 tv.tv_usec = 0;

 do {
  // Reset FD
  FD_ZERO(&fds);
  FD_SET(connection, &fds);

  rval = select(connection+1, &fds, NULL, NULL, &tv); // Select from request

  if (rval < 0) printf("[WARN ] [request_handle] Couldn't select from request.\n");
  else if (rval == 0) return false; // Timeout, kill request
  else {
   s_readline(connection, buffer, MAX_REQ_LINE - 1);

   if (buffer[0] == '{') {
    request.type = TN;
    request.resource = buffer;
    return true;
   } else {
    request.type = HTTP;
    strim(buffer);
   }

   // HTTP stuff
   if (buffer[0] == '\0') break; // End of HTTP headers
   if (parseHTTPHeader(buffer, &request)) break; // Parse headers
  }
 } while (request.level != SIMPLE);
 return true; // Successfully processed
}

bool RequestHandler::outputHTTP(int connection, Request *request, const char *content) {
 char buffer[32];

 sprintf(buffer, "HTTP/1.1 %d OK\r\n", request->status);
 s_writeline(connection, buffer, strlen(buffer));
 s_writeline(connection, "Server: TNREQ/0.2\r\n", 19);
 s_writeline(connection, "Content-Type: application/json\r\n", 32);
 sprintf(buffer, "Content-Length: %lu\r\n", strlen(content));
 s_writeline(connection, buffer, strlen(buffer));
 s_writeline(connection, "\r\n", 2);
 s_writeline(connection, content, strlen(content));

 usleep(1000000); // FIXME: This is a quickfix, it should be changed later

 return true;
}

bool RequestHandler::parseHTTPHeader(char *buffer, Request *request) {
 static bool first_line = true;
 char *temp = NULL;
 char *endptr = NULL;
 int len;
 if (first_line == true) {
  if (!strncmp(buffer, "GET ", 4)) {
   request->method = GET;
   buffer += 4;
  } else if (!strncmp(buffer, "HEAD ", 5)) {
   request->method = HEAD;
   buffer += 5;
  } else {
   request->method = UNSUPPORTED;
   request->status = 501;
   return false;
  }
  while (*buffer && isspace(*buffer)) buffer++; // Skip the start
  endptr = strchr(buffer, ' ');
  if (endptr == NULL) len = strlen(buffer);
  else len = endptr - buffer;

  if (len == 0) {
   request->status = 400;
   return false;
  }

  request->resource = (char *)calloc(len + 1, sizeof(char));
  strncpy(request->resource, buffer, len);

  if (strstr(buffer, "HTTP/")) request->level = FULL;
  else request->level = SIMPLE;

  first_line = 0;
  return true;
 }

 // More headers aside from the request line
 endptr = strchr(buffer, 58); // 58 is a : character
 if (endptr == NULL) {
  request->status = 400;
  return false;
 }

 temp = (char *)calloc((endptr - buffer) + 1, sizeof(char));
 strncpy(temp, buffer, (endptr - buffer));
 stoupper(temp);

 buffer = endptr + 1;
 while (*buffer && isspace(*buffer)) ++buffer;
 if (*buffer == '\0') return true;

 return true;
}
<|endoftext|>


<commit_before>#include <node.h>
#include <node_events.h>
#include <raptor.h>
#include <cstring>
#include <cstdio>

using namespace v8;
using namespace node;

static Persistent<String> subject_symbol   = NODE_PSYMBOL("subject");
static Persistent<String> predicate_symbol = NODE_PSYMBOL("predicate");
static Persistent<String> object_symbol    = NODE_PSYMBOL("object");
static Persistent<String> type_symbol      = NODE_PSYMBOL("type");
static Persistent<String> value_symbol     = NODE_PSYMBOL("value");
static Persistent<String> uri_symbol       = NODE_PSYMBOL("uri");
static Persistent<String> literal_symbol   = NODE_PSYMBOL("literal");
static Persistent<String> tliteral_symbol  = NODE_PSYMBOL("typed-literal");
static Persistent<String> bnode_symbol     = NODE_PSYMBOL("bnode");
static Persistent<String> dtype_symbol     = NODE_PSYMBOL("datatype");
static Persistent<String> lang_symbol      = NODE_PSYMBOL("lang");
static Persistent<String> statement_symbol = NODE_PSYMBOL("statement");
static Persistent<String> namespace_symbol = NODE_PSYMBOL("namespace");
static Persistent<String> end_symbol       = NODE_PSYMBOL("end");

static raptor_world* world = raptor_new_world();

/**
 * Raptor statement wrapper class
 */
class Statement {
public:
    static Handle<Object> NewInstance() {
        HandleScope scope;

        if (template_.IsEmpty()) {
            Handle<ObjectTemplate> t = ObjectTemplate::New();
            t->SetInternalFieldCount(1);
            t->SetAccessor(subject_symbol, SubjectAccessor);
            t->SetAccessor(predicate_symbol, PredicateAccessor);
            t->SetAccessor(object_symbol, ObjectAccessor);

            NODE_SET_METHOD(t, "toString", ToString);

            template_ = Persistent<ObjectTemplate>::New(t);
        }

        Handle<Object> i = template_->NewInstance();

        return scope.Close(i);
    }

    static Handle<Value> ToString(const Arguments& args) {
        HandleScope scope;

        // get raptor_statement out of info
        Handle<External> field = Handle<External>::Cast(args.Holder()->GetInternalField(0));
        raptor_statement* statement = static_cast<raptor_statement*>(field->Value());

        raptor_iostream* iostream;
        void* statement_string = NULL;
        size_t statement_string_len;
        iostream = raptor_new_iostream_to_string(statement->world,
                                                 &statement_string,
                                                 &statement_string_len,
                                                 NULL);
        if (!iostream) {
            ThrowException(String::New("Error serializing statement."));
            return Undefined();
        }

        int ret_val;
        ret_val = raptor_statement_ntriples_write(statement, iostream, 0);
        raptor_free_iostream(iostream);

        if (ret_val > 0) {
            if (statement_string) {
                free(statement_string);
                statement_string = NULL;
            }
        }

        Handle<String> result = String::New(reinterpret_cast<char*>(statement_string),
                                            statement_string_len - 1 /* remove trailing newline */);

        if (statement_string) {
            free(statement_string);
            statement_string = NULL;
        }

        return scope.Close(result);
    }

    static Handle<Value> SubjectAccessor(Local<String> property, const AccessorInfo& info) {
        HandleScope scope;

        Handle<Object> result = Object::New();

        // get raptor_statement out of info
        Handle<External> field = Handle<External>::Cast(info.Holder()->GetInternalField(0));
        raptor_statement* statement = static_cast<raptor_statement*>(field->Value());

        // get value out of statement
        raptor_term_value* subject_term = &statement->subject->value;

        Handle<String> type;
        Handle<String> value;

        raptor_uri* uri_value;
        raptor_term_blank_value* blank_value;

        if (statement->subject->type == RAPTOR_TERM_TYPE_URI) {
            uri_value = subject_term->uri;
            value = String::New(reinterpret_cast<char*>(raptor_uri_as_string(uri_value)));
            type = uri_symbol;
        } else if (statement->subject->type == RAPTOR_TERM_TYPE_BLANK) {
            blank_value = &subject_term->blank;
            value = String::New(reinterpret_cast<char*>(blank_value->string),
                                                        blank_value->string_len);
            type = bnode_symbol;
        }

        result->Set(type_symbol, type);
        result->Set(value_symbol, value);

        return scope.Close(result);
    }

    static Handle<Value> PredicateAccessor(Local<String> property, const AccessorInfo& info) {
        HandleScope scope;

        Handle<Object> result = Object::New();

        // get raptor_statement out of info
        Handle<External> field = Handle<External>::Cast(info.Holder()->GetInternalField(0));
        raptor_statement* statement = static_cast<raptor_statement*>(field->Value());

        // get value out of statement
        raptor_term_value* predicate_term = &statement->predicate->value;
        raptor_uri* uri_value = predicate_term->uri;
        Handle<String> value = String::New(reinterpret_cast<char*>(raptor_uri_as_string(uri_value)));

        result->Set(type_symbol, uri_symbol);
        result->Set(value_symbol, value);

        return scope.Close(result);
    }

    static Handle<Value> ObjectAccessor(Local<String> property, const AccessorInfo& info) {
        HandleScope scope;

        Handle<Object> result = Object::New();

        // get raptor_statement out of info
        Handle<External> field = Handle<External>::Cast(info.Holder()->GetInternalField(0));
        raptor_statement* statement = static_cast<raptor_statement*>(field->Value());

        // get value out of statement
        raptor_term_value* object_term = &statement->object->value;

        Handle<String> type;
        Handle<String> value;

        raptor_uri* uri_value;
        raptor_term_literal_value* literal_value;
        raptor_term_blank_value* blank_value;

        switch (statement->object->type) {
        case RAPTOR_TERM_TYPE_URI:
            uri_value = object_term->uri;
            value = String::New(reinterpret_cast<char*>(raptor_uri_as_string(uri_value)));
            type = uri_symbol;
            break;
        case RAPTOR_TERM_TYPE_LITERAL:
            literal_value = &object_term->literal;
            value = String::New(reinterpret_cast<char*>(literal_value->string),
                                literal_value->string_len);
            type = literal_symbol;

            if (literal_value->datatype) {
                raptor_uri* datatype_uri = literal_value->datatype;
                unsigned char* datatype_uri_string = raptor_uri_to_string(datatype_uri);
                result->Set(dtype_symbol, String::New(reinterpret_cast<char*>(datatype_uri_string)));

                type = tliteral_symbol;
            } else if (literal_value->language) {
                result->Set(lang_symbol, String::New(reinterpret_cast<char*>(literal_value->language),
                                                     literal_value->language_len));
            }
            break;
        case RAPTOR_TERM_TYPE_BLANK:
            blank_value = &object_term->blank;
            value = String::New(reinterpret_cast<char*>(blank_value->string),
                                                        blank_value->string_len);
            type = bnode_symbol;
            break;
        }

        result->Set(type_symbol, type);
        result->Set(value_symbol, value);

        return scope.Close(result);
    }
private:
    static Handle<ObjectTemplate> template_;
};

Handle<ObjectTemplate> Statement::template_;

/**
 * Raptor parser wrapper class
 */
class Parser : public EventEmitter {
public:
    static Handle<Value> Initialize(const Arguments& args) {
        HandleScope scope;

        Handle<FunctionTemplate> t = FunctionTemplate::New(New);

        t->Inherit(EventEmitter::constructor_template);
        t->InstanceTemplate()->SetInternalFieldCount(1);

        NODE_SET_PROTOTYPE_METHOD(t, "parse", Parse);
        NODE_SET_PROTOTYPE_METHOD(t, "abort", Abort);
        NODE_SET_PROTOTYPE_METHOD(t, "setOption", SetOption);

        t->InstanceTemplate()->SetAccessor(String::NewSymbol("name"), GetName);

        Handle<Function> function = t->GetFunction();
        Handle<Value> arguments[args.Length()];
        ExtractArguments(args, arguments);
        Handle<Object> parser = function->NewInstance(args.Length(), arguments);

        if (args.Length() == 2 && args[1]->IsFunction()) {
            const int argc = 1;
            Handle<Value> argv[argc] = {parser};
            Handle<Function> callback = Handle<Function>::Cast(args[1]);
            callback->Call(Context::GetCurrent()->Global(), argc, argv);
        }

        return scope.Close(parser);
    }

    static Handle<Value> New(const Arguments& args) {
        HandleScope scope;

        Handle<String> parser_name;
        if (args.Length() == 0) {
            parser_name = String::New("rdfxml");
        } else {
            parser_name = args[0]->ToString();
        }

        String::Utf8Value name(parser_name);
        Parser* parser = new Parser(*name);
        parser->Wrap(args.This());

        return scope.Close(args.This());
    }

    static Handle<Value> Parse(const Arguments& args) {
        Parser* parser = ObjectWrap::Unwrap<Parser>(args.This());
        HandleScope scope;

        if (!args.Length() == 1 || !args[0]->IsString()) {
            return Undefined();
        }

        Handle<String> file = args[0]->ToString();

        String::Utf8Value filename(file);
        parser->Parse(*filename);

        return Undefined();
    }

    static Handle<Value> Abort(const Arguments& args) {
        Parser* parser = ObjectWrap::Unwrap<Parser>(args.This());
        parser->Abort();

        return Undefined();
    }

    static Handle<Value> SetOption(const Arguments& args) {
        if (args.Length() != 2 || !args[0]->IsString() || !(args[1]->IsBoolean() || args[1]->IsString())) {
            // TODO: exception
            return Undefined();
        }

        Parser* parser = ObjectWrap::Unwrap<Parser>(args.This());

        String::Utf8Value optionName(args[0]->ToString());
        if (args[1]->IsString()) {
            String::Utf8Value optionValue(args[1]->ToString());
            parser->SetOption(*optionName, *optionValue);
        } else /* if (args[1]->IsBoolean()) */ {
            parser->SetOption(*optionName, NULL, args[1]->ToBoolean()->Value());
        }
    }

    static Handle<Value> GetName (Local<String> property, const AccessorInfo& info) {
        Parser* parser = ObjectWrap::Unwrap<Parser>(info.This());
        HandleScope scope;

        Handle<String> name = String::New(parser->GetName());

        return scope.Close(name);
    }

    static void CallbackWrapper(void* user_data, raptor_statement* statement) {
        Parser *p = static_cast<Parser*>(user_data);
        p->StatementHandler(statement);
    }

    static void CallbackWrapper(void* user_data, raptor_namespace *nspace) {
        Parser *p = static_cast<Parser*>(user_data);
        p->NamespaceHandler(nspace);
    }

    Parser(const char* name) {
        // keep parser name; actual parser created lazily
        parser_name_ = new char[strlen(name)];
        strcpy(parser_name_, const_cast<char*>(name));
    }

    ~Parser() {
        raptor_free_parser(parser_);
        delete parser_name_;
    }
protected:
    raptor_parser* parser_;
    char* parser_name_;

    void Abort() {
        if (parser_) {
            raptor_parser_parse_abort(parser_);
        }
    }

    void Parse(const char* filename) {
        /* TODO: parse asynchonously */
        raptor_parser* parser = raptor_new_parser(world, parser_name_);
        parser_ = parser;
        raptor_parser_set_statement_handler(parser, this, Parser::CallbackWrapper);
        raptor_parser_set_namespace_handler(parser, this, Parser::CallbackWrapper);

        uint8_t* file_uri_string = raptor_uri_filename_to_uri_string(filename);
        raptor_uri* file_uri = raptor_new_uri(world, file_uri_string);
        raptor_parser_parse_file(parser, file_uri, NULL);
        raptor_free_uri(file_uri);

        Emit(end_symbol, 0, NULL);
    }

    void DoParse(eio_req* request) {

    }

    static Handle<Value>* ExtractArguments(const Arguments& args, Handle<Value>* arguments) {
        for (int i = 0; i < args.Length(); ++i) {
            arguments[i] = args[i];
        }

        return arguments;
    }

    const char* GetName() {
        return parser_name_;
    }

    void SetOption(const char* option_name, const char* string_option_value) {
        // set option in parser
    }

    void SetOption(const char* option_name, const char* string_option_value, bool bool_option_value) {
        // set option in parser
    }

    void StatementHandler(raptor_statement* statement) {
        HandleScope scope;

        // get new statement object
        Handle<Object> statement_instance = Statement::NewInstance();

        // external objects needs to be copied
        // FIXME: probably leakes
        Handle<External> statement_instance_ptr = External::New(raptor_statement_copy(statement));
        statement_instance->SetInternalField(0, statement_instance_ptr);

        Handle<Value> args[1];
        args[0] = statement_instance;

        Emit(statement_symbol, 1, args);
    }

    void NamespaceHandler(raptor_namespace *nspace) {
        raptor_uri* namespace_uri = raptor_namespace_get_uri(nspace);
        unsigned char* namespace_uri_string = raptor_uri_to_string(namespace_uri);

        size_t length;
        const unsigned char* namespace_prefix = raptor_namespace_get_counted_prefix(nspace, &length);

        HandleScope scope;

        Handle<Value> args[2];
        args[0] = String::New(reinterpret_cast<const char*>(namespace_prefix), length);
        args[1] = String::New(reinterpret_cast<const char*>(namespace_uri_string));

        Emit(namespace_symbol, 2, args);

        raptor_free_memory(namespace_uri_string);
    }
};

/* extern "C" to prevent name mangling */
extern "C" void init(Handle<Object> target) {
    NODE_SET_METHOD(target, "newParser", Parser::Initialize);
    // NODE_SET_METHOD(target, "newParserForContent", Raptor::newParserForContent);
}<commit_msg>Fixed mem leaks<commit_after>#include <node.h>
#include <node_events.h>
#include <raptor.h>
#include <cstring>
#include <cstdio>

using namespace v8;
using namespace node;

static Persistent<String> subject_symbol   = NODE_PSYMBOL("subject");
static Persistent<String> predicate_symbol = NODE_PSYMBOL("predicate");
static Persistent<String> object_symbol    = NODE_PSYMBOL("object");
static Persistent<String> type_symbol      = NODE_PSYMBOL("type");
static Persistent<String> value_symbol     = NODE_PSYMBOL("value");
static Persistent<String> uri_symbol       = NODE_PSYMBOL("uri");
static Persistent<String> literal_symbol   = NODE_PSYMBOL("literal");
static Persistent<String> tliteral_symbol  = NODE_PSYMBOL("typed-literal");
static Persistent<String> bnode_symbol     = NODE_PSYMBOL("bnode");
static Persistent<String> dtype_symbol     = NODE_PSYMBOL("datatype");
static Persistent<String> lang_symbol      = NODE_PSYMBOL("lang");
static Persistent<String> statement_symbol = NODE_PSYMBOL("statement");
static Persistent<String> namespace_symbol = NODE_PSYMBOL("namespace");
static Persistent<String> end_symbol       = NODE_PSYMBOL("end");

static raptor_world* world = raptor_new_world();

/**
 * Raptor statement wrapper class
 */
class Statement {
public:
    static Handle<Object> NewInstance() {
        HandleScope scope;

        if (template_.IsEmpty()) {
            Handle<ObjectTemplate> t = ObjectTemplate::New();
            t->SetInternalFieldCount(1);
            t->SetAccessor(subject_symbol, SubjectAccessor);
            t->SetAccessor(predicate_symbol, PredicateAccessor);
            t->SetAccessor(object_symbol, ObjectAccessor);

            NODE_SET_METHOD(t, "toString", ToString);

            template_ = Persistent<ObjectTemplate>::New(t);
        }

        Handle<Object> i = template_->NewInstance();

        return scope.Close(i);
    }

    static Handle<Value> ToString(const Arguments& args) {
        HandleScope scope;

        // get raptor_statement out of info
        Handle<External> field = Handle<External>::Cast(args.Holder()->GetInternalField(0));
        raptor_statement* statement = static_cast<raptor_statement*>(field->Value());

        raptor_iostream* iostream;
        void* statement_string = NULL;
        size_t statement_string_len;
        iostream = raptor_new_iostream_to_string(statement->world,
                                                 &statement_string,
                                                 &statement_string_len,
                                                 malloc);
        if (!iostream) {
            ThrowException(String::New("Error serializing statement."));
            return Undefined();
        }

        int ret_val;
        ret_val = raptor_statement_ntriples_write(statement, iostream, 0);
        raptor_free_iostream(iostream);

        if (ret_val > 0) {
            if (statement_string) {
                free(statement_string);
                statement_string = NULL;
            }
        }

        Handle<String> result;
        if (statement_string) {
            result = String::New(reinterpret_cast<char*>(statement_string),
                                 statement_string_len - 1 /* remove trailing newline */);
            free(statement_string);
            statement_string = NULL;
        }

        return scope.Close(result);
    }

    static Handle<Value> SubjectAccessor(Local<String> property, const AccessorInfo& info) {
        HandleScope scope;

        Handle<Object> result = Object::New();

        // get raptor_statement out of info
        Handle<External> field = Handle<External>::Cast(info.Holder()->GetInternalField(0));
        raptor_statement* statement = static_cast<raptor_statement*>(field->Value());

        // get value out of statement
        raptor_term_value* subject_term = &statement->subject->value;

        Handle<String> type;
        Handle<String> value;

        raptor_uri* uri_value;
        raptor_term_blank_value* blank_value;

        if (statement->subject->type == RAPTOR_TERM_TYPE_URI) {
            uri_value = subject_term->uri;
            value = String::New(reinterpret_cast<char*>(raptor_uri_as_string(uri_value)));
            type = uri_symbol;
        } else if (statement->subject->type == RAPTOR_TERM_TYPE_BLANK) {
            blank_value = &subject_term->blank;
            value = String::New(reinterpret_cast<char*>(blank_value->string),
                                                        blank_value->string_len);
            type = bnode_symbol;
        }

        result->Set(type_symbol, type);
        result->Set(value_symbol, value);

        return scope.Close(result);
    }

    static Handle<Value> PredicateAccessor(Local<String> property, const AccessorInfo& info) {
        HandleScope scope;

        Handle<Object> result = Object::New();

        // get raptor_statement out of info
        Handle<External> field = Handle<External>::Cast(info.Holder()->GetInternalField(0));
        raptor_statement* statement = static_cast<raptor_statement*>(field->Value());

        // get value out of statement
        raptor_term_value* predicate_term = &statement->predicate->value;
        raptor_uri* uri_value = predicate_term->uri;
        Handle<String> value = String::New(reinterpret_cast<char*>(raptor_uri_as_string(uri_value)));

        result->Set(type_symbol, uri_symbol);
        result->Set(value_symbol, value);

        return scope.Close(result);
    }

    static Handle<Value> ObjectAccessor(Local<String> property, const AccessorInfo& info) {
        HandleScope scope;

        Handle<Object> result = Object::New();

        // get raptor_statement out of info
        Handle<External> field = Handle<External>::Cast(info.Holder()->GetInternalField(0));
        raptor_statement* statement = static_cast<raptor_statement*>(field->Value());

        // get value out of statement
        raptor_term_value* object_term = &statement->object->value;

        Handle<String> type;
        Handle<String> value;

        raptor_uri* uri_value;
        raptor_term_literal_value* literal_value;
        raptor_term_blank_value* blank_value;

        switch (statement->object->type) {
        case RAPTOR_TERM_TYPE_URI:
            uri_value = object_term->uri;
            value = String::New(reinterpret_cast<char*>(raptor_uri_as_string(uri_value)));
            type = uri_symbol;
            break;
        case RAPTOR_TERM_TYPE_LITERAL:
            literal_value = &object_term->literal;
            value = String::New(reinterpret_cast<char*>(literal_value->string),
                                literal_value->string_len);
            type = literal_symbol;

            if (literal_value->datatype) {
                raptor_uri* datatype_uri = literal_value->datatype;
                unsigned char* datatype_uri_string = raptor_uri_to_string(datatype_uri);
                result->Set(dtype_symbol, String::New(reinterpret_cast<char*>(datatype_uri_string)));
                if (datatype_uri_string) {
                    raptor_free_memory(datatype_uri_string);
                    datatype_uri_string = NULL;
                }

                type = tliteral_symbol;
            } else if (literal_value->language) {
                result->Set(lang_symbol, String::New(reinterpret_cast<char*>(literal_value->language),
                                                     literal_value->language_len));
            }
            break;
        case RAPTOR_TERM_TYPE_BLANK:
            blank_value = &object_term->blank;
            value = String::New(reinterpret_cast<char*>(blank_value->string),
                                                        blank_value->string_len);
            type = bnode_symbol;
            break;
        }

        result->Set(type_symbol, type);
        result->Set(value_symbol, value);

        return scope.Close(result);
    }
private:
    static Handle<ObjectTemplate> template_;
};

Handle<ObjectTemplate> Statement::template_;

/**
 * Raptor parser wrapper class
 */
class Parser : public EventEmitter {
public:
    static Handle<Value> Initialize(const Arguments& args) {
        HandleScope scope;

        Handle<FunctionTemplate> t = FunctionTemplate::New(New);

        t->Inherit(EventEmitter::constructor_template);
        t->InstanceTemplate()->SetInternalFieldCount(1);

        NODE_SET_PROTOTYPE_METHOD(t, "parse", Parse);
        NODE_SET_PROTOTYPE_METHOD(t, "abort", Abort);
        NODE_SET_PROTOTYPE_METHOD(t, "setOption", SetOption);

        t->InstanceTemplate()->SetAccessor(String::NewSymbol("name"), GetName);

        Handle<Function> function = t->GetFunction();
        Handle<Value> arguments[args.Length()];
        ExtractArguments(args, arguments);
        Handle<Object> parser = function->NewInstance(args.Length(), arguments);

        if (args.Length() == 2 && args[1]->IsFunction()) {
            const int argc = 1;
            Handle<Value> argv[argc] = {parser};
            Handle<Function> callback = Handle<Function>::Cast(args[1]);
            callback->Call(Context::GetCurrent()->Global(), argc, argv);
        }

        return scope.Close(parser);
    }

    static Handle<Value> New(const Arguments& args) {
        HandleScope scope;

        Handle<String> parser_name;
        if (args.Length() == 0) {
            parser_name = String::New("rdfxml");
        } else {
            parser_name = args[0]->ToString();
        }

        String::Utf8Value name(parser_name);
        Parser* parser = new Parser(*name);
        parser->Wrap(args.This());

        return scope.Close(args.This());
    }

    static Handle<Value> Parse(const Arguments& args) {
        Parser* parser = ObjectWrap::Unwrap<Parser>(args.This());
        HandleScope scope;

        if (!args.Length() == 1 || !args[0]->IsString()) {
            return Undefined();
        }

        Handle<String> file = args[0]->ToString();

        String::Utf8Value filename(file);
        parser->Parse(*filename);

        return Undefined();
    }

    static Handle<Value> Abort(const Arguments& args) {
        Parser* parser = ObjectWrap::Unwrap<Parser>(args.This());
        parser->Abort();

        return Undefined();
    }

    static Handle<Value> SetOption(const Arguments& args) {
        if (args.Length() != 2 || !args[0]->IsString() || !(args[1]->IsBoolean() || args[1]->IsString())) {
            // TODO: exception
            return Undefined();
        }

        Parser* parser = ObjectWrap::Unwrap<Parser>(args.This());

        String::Utf8Value optionName(args[0]->ToString());
        if (args[1]->IsString()) {
            String::Utf8Value optionValue(args[1]->ToString());
            parser->SetOption(*optionName, *optionValue);
        } else /* if (args[1]->IsBoolean()) */ {
            parser->SetOption(*optionName, NULL, args[1]->ToBoolean()->Value());
        }
    }

    static Handle<Value> GetName (Local<String> property, const AccessorInfo& info) {
        Parser* parser = ObjectWrap::Unwrap<Parser>(info.This());
        HandleScope scope;

        Handle<String> name = String::New(parser->GetName());

        return scope.Close(name);
    }

    static void CallbackWrapper(void* user_data, raptor_statement* statement) {
        Parser *p = static_cast<Parser*>(user_data);
        p->StatementHandler(statement);
    }

    static void CallbackWrapper(void* user_data, raptor_namespace *nspace) {
        Parser *p = static_cast<Parser*>(user_data);
        p->NamespaceHandler(nspace);
    }

    Parser(const char* name) {
        // keep parser name; actual parser created lazily
        parser_name_ = new char[strlen(name)];
        strcpy(parser_name_, const_cast<char*>(name));
    }

    ~Parser() {
        raptor_free_parser(parser_);
        delete parser_name_;
    }
protected:
    raptor_parser* parser_;
    char* parser_name_;

    void Abort() {
        if (parser_) {
            raptor_parser_parse_abort(parser_);
        }
    }

    void Parse(const char* filename) {
        /* TODO: parse asynchonously */
        raptor_parser* parser = raptor_new_parser(world, parser_name_);
        parser_ = parser;
        raptor_parser_set_statement_handler(parser, this, Parser::CallbackWrapper);
        raptor_parser_set_namespace_handler(parser, this, Parser::CallbackWrapper);

        uint8_t* file_uri_string = raptor_uri_filename_to_uri_string(filename);
        raptor_uri* file_uri = raptor_new_uri(world, file_uri_string);

        if (file_uri) {
            raptor_parser_parse_file(parser, file_uri, NULL);
            raptor_free_uri(file_uri);
            file_uri = NULL;
        }

        if (file_uri_string) {
            raptor_free_memory(file_uri_string);
        }

        Emit(end_symbol, 0, NULL);
    }

    void DoParse(eio_req* request) {

    }

    static Handle<Value>* ExtractArguments(const Arguments& args, Handle<Value>* arguments) {
        for (int i = 0; i < args.Length(); ++i) {
            arguments[i] = args[i];
        }

        return arguments;
    }

    const char* GetName() {
        return parser_name_;
    }

    void SetOption(const char* option_name, const char* string_option_value) {
        // set option in parser
    }

    void SetOption(const char* option_name, const char* string_option_value, bool bool_option_value) {
        // set option in parser
    }

    void StatementHandler(raptor_statement* statement) {
        HandleScope scope;

        // get new statement object
        Handle<Object> statement_instance = Statement::NewInstance();

        // external objects needs to be copied
        // FIXME: probably leakes
        raptor_statement* statement_copy = raptor_statement_copy(statement);
        Handle<External> statement_instance_ptr = External::New(statement_copy);
        statement_instance->SetInternalField(0, statement_instance_ptr);

        Handle<Value> args[1];
        args[0] = statement_instance;

        Emit(statement_symbol, 1, args);
    }

    void NamespaceHandler(raptor_namespace *nspace) {
        raptor_uri* namespace_uri = raptor_namespace_get_uri(nspace);
        unsigned char* namespace_uri_string = raptor_uri_to_string(namespace_uri);

        size_t length;
        const unsigned char* namespace_prefix = raptor_namespace_get_counted_prefix(nspace, &length);

        HandleScope scope;

        Handle<Value> args[2];
        args[0] = String::New(reinterpret_cast<const char*>(namespace_prefix), length);
        args[1] = String::New(reinterpret_cast<const char*>(namespace_uri_string));

        if (namespace_uri_string) {
            raptor_free_memory(namespace_uri_string);
        }

        Emit(namespace_symbol, 2, args);
    }
};

/* extern "C" to prevent name mangling */
extern "C" void init(Handle<Object> target) {
    NODE_SET_METHOD(target, "newParser", Parser::Initialize);
    // NODE_SET_METHOD(target, "newParserForContent", Raptor::newParserForContent);
}<|endoftext|>


<commit_before>/*
 * The "slice" memory allocator.  It is an allocator for large numbers
 * of small fixed-size objects.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "slice.hxx"
#include "mmap.h"

#include <inline/list.h>

#include <new>

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

static constexpr unsigned ALLOCATED = -1;
static constexpr unsigned END_OF_LIST = -2;

#ifndef NDEBUG
static constexpr unsigned MARK = -3;
#endif

struct slice_slot {
    unsigned next;

    constexpr bool IsAllocated() const {
        return next == ALLOCATED;
    }
};

struct slice_area {
    struct list_head siblings;

    unsigned allocated_count;

    unsigned free_head;

    struct slice_slot slices[1];

private:
    slice_area(struct slice_pool &pool);

    ~slice_area() {
        assert(allocated_count == 0);
    }

public:
    static struct slice_area *New(struct slice_pool &pool);
    void Delete(struct slice_pool &pool);

    bool IsEmpty() const {
        return allocated_count == 0;
    }

    bool IsFull(const struct slice_pool &pool) const;

    gcc_pure
    void *GetPage(const struct slice_pool &pool, unsigned page);

    gcc_pure
    void *GetSlice(const struct slice_pool &pool, unsigned slice);

    /**
     * Calculates the allocation slot index from an allocated pointer.
     * This is used to locate the #slice_slot for a pointer passed to a
     * public function.
     */
    gcc_pure
    unsigned IndexOf(const struct slice_pool &pool, const void *_p);

    /**
     * Find the first free slot index, starting at the specified position.
     */
    gcc_pure
    unsigned FindFree(const struct slice_pool &pool, unsigned start) const;

    /**
     * Find the first allocated slot index, starting at the specified
     * position.
     */
    gcc_pure
    unsigned FindAllocated(const struct slice_pool &pool,
                           unsigned start) const;

    /**
     * Punch a hole in the memory map in the specified slot index range.
     * This means notifying the kernel that we will no longer need the
     * contents, which allows the kernel to drop the allocated pages and
     * reuse it for other processes.
     */
    void PunchSliceRange(struct slice_pool &pool,
                         unsigned start, gcc_unused unsigned end);

    void Compress(struct slice_pool &pool);

    void *Alloc(struct slice_pool &pool);
    void Free(struct slice_pool &pool, void *p);
};

constexpr
static inline size_t
align_size(size_t size)
{
    return ((size - 1) | 0x1f) + 1;
}

gcc_const
static inline size_t
align_page_size(size_t size)
{
    return ((size - 1) | (mmap_page_size() - 1)) + 1;
}

static constexpr unsigned
divide_round_up(unsigned a, unsigned b)
{
    return (a + b - 1) / b;
}

struct slice_pool {
    size_t slice_size;

    /**
     * Number of slices that fit on one MMU page (4 kB).
     */
    unsigned slices_per_page;

    unsigned pages_per_slice;

    unsigned pages_per_area;

    unsigned slices_per_area;

    /**
     * Number of pages for the area header.
     */
    unsigned header_pages;

    size_t area_size;

    struct list_head areas;

    slice_pool(size_t _slice_size, unsigned _slices_per_area);
    ~slice_pool();

    void Compress();

    gcc_pure
    struct slice_area *FindNonFullArea();

    struct slice_area *GetArea();
};

/*
 * slice_area methods
 *
 */

slice_area::slice_area(struct slice_pool &pool)
    :allocated_count(0), free_head(0)
{
    /* build the "free" list */
    for (unsigned i = 0; i < pool.slices_per_area - 1; ++i)
        slices[i].next = i + 1;

    slices[pool.slices_per_area - 1].next = END_OF_LIST;
}

struct slice_area *
slice_area::New(struct slice_pool &pool)
{
    void *p = mmap_alloc_anonymous(pool.area_size);
    if (p == (void *)-1) {
        fputs("Out of adress space\n", stderr);
        abort();
    }

    return ::new(p) slice_area(pool);
}

inline bool
slice_area::IsFull(gcc_unused const struct slice_pool &pool) const
{
    assert(free_head < pool.slices_per_area ||
           free_head == END_OF_LIST);

    return free_head == END_OF_LIST;
}

void
slice_area::Delete(struct slice_pool &pool)
{
    assert(allocated_count == 0);

#ifndef NDEBUG
    for (unsigned i = 0; i < pool.slices_per_area; ++i)
        assert(slices[i].next < pool.slices_per_area ||
               slices[i].next == END_OF_LIST);

    unsigned i = free_head;
    while (i != END_OF_LIST) {
        assert(i < pool.slices_per_area);

        unsigned next = slices[i].next;
        slices[i].next = MARK;
        i = next;
    }
#endif

    this->~slice_area();
    mmap_free(this, pool.area_size);
}

inline void *
slice_area::GetPage(const struct slice_pool &pool, unsigned page)
{
    assert(page <= pool.pages_per_area);

    return (uint8_t *)this + (pool.header_pages + page) * mmap_page_size();
}

inline void *
slice_area::GetSlice(const struct slice_pool &pool, unsigned slice)
{
    assert(slice < pool.slices_per_area);
    assert(slices[slice].IsAllocated());

    unsigned page = (slice / pool.slices_per_page) * pool.pages_per_slice;
    slice %= pool.slices_per_page;

    return (uint8_t *)GetPage(pool, page) + slice * pool.slice_size;
}

inline unsigned
slice_area::IndexOf(const struct slice_pool &pool, const void *_p)
{
    const uint8_t *p = (const uint8_t *)_p;
    assert(p >= (uint8_t *)GetPage(pool, 0));
    assert(p < (uint8_t *)GetPage(pool, pool.pages_per_area));

    size_t offset = p - (const uint8_t *)this;
    const unsigned page = offset / mmap_page_size() - pool.header_pages;
    offset %= mmap_page_size();
    assert(offset % pool.slice_size == 0);

    return page * pool.slices_per_page / pool.pages_per_slice
        + offset / pool.slice_size;
}

unsigned
slice_area::FindFree(const struct slice_pool &pool, unsigned start) const
{
    assert(start <= pool.slices_per_page);

    const unsigned end = pool.slices_per_page;

    unsigned i;
    for (i = start; i != end; ++i)
        if (!slices[i].IsAllocated())
            break;

    return i;
}

/**
 * Find the first allocated slot index, starting at the specified
 * position.
 */
gcc_pure
unsigned
slice_area::FindAllocated(const struct slice_pool &pool, unsigned start) const
{
    assert(start <= pool.slices_per_page);

    const unsigned end = pool.slices_per_page;

    unsigned i;
    for (i = start; i != end; ++i)
        if (slices[i].IsAllocated())
            break;

    return i;
}

void
slice_area::PunchSliceRange(struct slice_pool &pool,
                            unsigned start, gcc_unused unsigned end)
{
    assert(start <= end);

    unsigned start_page = divide_round_up(start, pool.slices_per_page)
        * pool.pages_per_slice;
    unsigned end_page = (start / pool.slices_per_page)
        * pool.pages_per_slice;
    assert(start_page <= end_page);
    if (start_page == end_page)
        return;

    uint8_t *start_pointer = (uint8_t *)GetPage(pool, start_page);
    uint8_t *end_pointer = (uint8_t *)GetPage(pool, end_page);

    mmap_discard_pages(start_pointer, end_pointer - start_pointer);
}

void
slice_area::Compress(struct slice_pool &pool)
{
    unsigned position = 0;

    while (true) {
        unsigned first_free = FindFree(pool, position);
        if (first_free == pool.slices_per_page)
            break;

        unsigned first_allocated = FindAllocated(pool, first_free + 1);
        PunchSliceRange(pool, first_free, first_allocated);

        position = first_allocated;
    }
}

/*
 * slice_pool methods
 *
 */

inline
slice_pool::slice_pool(size_t _slice_size, unsigned _slices_per_area)
{
    assert(_slice_size > 0);
    assert(_slices_per_area > 0);

    if (_slice_size <= mmap_page_size() / 2) {
        slice_size = align_size(_slice_size);

        slices_per_page = mmap_page_size() / slice_size;
        pages_per_slice = 1;

        pages_per_area = divide_round_up(_slices_per_area,
                                         slices_per_page);
    } else {
        slice_size = align_page_size(_slice_size);

        slices_per_page = 1;
        pages_per_slice = slice_size / mmap_page_size();

        pages_per_area = _slices_per_area * pages_per_slice;
    }

    slices_per_area = (pages_per_area / pages_per_slice) * slices_per_page;

    const struct slice_area *area = nullptr;
    const size_t header_size = sizeof(*area)
        + sizeof(area->slices[0]) * (slices_per_area - 1);
    header_pages = divide_round_up(header_size, mmap_page_size());

    area_size = mmap_page_size() * (header_pages + pages_per_area);

    list_init(&areas);
}

inline
slice_pool::~slice_pool()
{
    while (!list_empty(&areas)) {
        struct slice_area *area = (struct slice_area *)areas.next;

        /* must be empty at this point, or it's a memory leak */
        assert(area->allocated_count == 0);

        list_remove(&area->siblings);
        area->Delete(*this);
    }
}

struct slice_pool *
slice_pool_new(size_t slice_size, unsigned slices_per_area)
{
    return new slice_pool(slice_size, slices_per_area);
}

void
slice_pool_free(struct slice_pool *pool)
{
    delete pool;
}

size_t
slice_pool_get_slice_size(const struct slice_pool *pool)
{
    assert(pool != nullptr);

    return pool->slice_size;
}

inline void
slice_pool::Compress()
{
    for (struct slice_area *area = (struct slice_area *)areas.next,
             *next = (struct slice_area *)area->siblings.next;
         &area->siblings != &areas;
         area = next, next = (struct slice_area *)area->siblings.next) {
        if (area->IsEmpty()) {
            list_remove(&area->siblings);
            area->Delete(*this);
        } else
            area->Compress(*this);
    }
}

void
slice_pool_compress(struct slice_pool *pool)
{
    pool->Compress();
}

gcc_pure
inline struct slice_area *
slice_pool::FindNonFullArea()
{
    for (struct slice_area *area = (struct slice_area *)areas.next;
         &area->siblings != &areas;
         area = (struct slice_area *)area->siblings.next)
        if (!area->IsFull(*this))
            return area;

    return nullptr;
}

inline struct slice_area *
slice_pool::GetArea()
{
    struct slice_area *area = FindNonFullArea();
    if (area == nullptr) {
        area = slice_area::New(*this);
        list_add(&area->siblings, &areas);
    }

    return area;
}

struct slice_area *
slice_pool_get_area(struct slice_pool *pool)
{
    return pool->GetArea();
}

inline void *
slice_area::Alloc(struct slice_pool &pool)
{
    assert(!IsFull(pool));

    const unsigned i = free_head;
    struct slice_slot *const slot = &slices[i];

    ++allocated_count;
    free_head = slot->next;
    slot->next = ALLOCATED;

    return GetSlice(pool, i);
}

void *
slice_alloc(struct slice_pool *pool, struct slice_area *area)
{
    assert(pool != nullptr);
    assert(area != nullptr);

    return area->Alloc(*pool);
}

void
slice_area::Free(struct slice_pool &pool, void *p)
{
    unsigned i = IndexOf(pool, p);
    assert(slices[i].IsAllocated());

    slices[i].next = free_head;
    free_head = i;

    assert(allocated_count > 0);
    --allocated_count;
}

void
slice_free(struct slice_pool *pool, struct slice_area *area, void *p)
{
    assert(pool != nullptr);
    assert(area != nullptr);

    area->Free(*pool, p);
}
<commit_msg>slice: use class boost::intrusive::list<commit_after>/*
 * The "slice" memory allocator.  It is an allocator for large numbers
 * of small fixed-size objects.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "slice.hxx"
#include "mmap.h"

#include <boost/intrusive/list.hpp>

#include <new>

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

static constexpr unsigned ALLOCATED = -1;
static constexpr unsigned END_OF_LIST = -2;

#ifndef NDEBUG
static constexpr unsigned MARK = -3;
#endif

struct slice_slot {
    unsigned next;

    constexpr bool IsAllocated() const {
        return next == ALLOCATED;
    }
};

struct slice_area {
    static constexpr auto link_mode = boost::intrusive::normal_link;
    typedef boost::intrusive::link_mode<link_mode> LinkMode;
    typedef boost::intrusive::list_member_hook<LinkMode> SiblingsHook;
    SiblingsHook siblings;

    unsigned allocated_count;

    unsigned free_head;

    struct slice_slot slices[1];

private:
    slice_area(struct slice_pool &pool);

    ~slice_area() {
        assert(allocated_count == 0);
    }

public:
    static struct slice_area *New(struct slice_pool &pool);
    void Delete(struct slice_pool &pool);

    bool IsEmpty() const {
        return allocated_count == 0;
    }

    bool IsFull(const struct slice_pool &pool) const;

    gcc_pure
    void *GetPage(const struct slice_pool &pool, unsigned page);

    gcc_pure
    void *GetSlice(const struct slice_pool &pool, unsigned slice);

    /**
     * Calculates the allocation slot index from an allocated pointer.
     * This is used to locate the #slice_slot for a pointer passed to a
     * public function.
     */
    gcc_pure
    unsigned IndexOf(const struct slice_pool &pool, const void *_p);

    /**
     * Find the first free slot index, starting at the specified position.
     */
    gcc_pure
    unsigned FindFree(const struct slice_pool &pool, unsigned start) const;

    /**
     * Find the first allocated slot index, starting at the specified
     * position.
     */
    gcc_pure
    unsigned FindAllocated(const struct slice_pool &pool,
                           unsigned start) const;

    /**
     * Punch a hole in the memory map in the specified slot index range.
     * This means notifying the kernel that we will no longer need the
     * contents, which allows the kernel to drop the allocated pages and
     * reuse it for other processes.
     */
    void PunchSliceRange(struct slice_pool &pool,
                         unsigned start, gcc_unused unsigned end);

    void Compress(struct slice_pool &pool);

    void *Alloc(struct slice_pool &pool);
    void Free(struct slice_pool &pool, void *p);

    struct Disposer {
        struct slice_pool &pool;

        void operator()(struct slice_area *area) {
            area->Delete(pool);
        }
    };
};

constexpr
static inline size_t
align_size(size_t size)
{
    return ((size - 1) | 0x1f) + 1;
}

gcc_const
static inline size_t
align_page_size(size_t size)
{
    return ((size - 1) | (mmap_page_size() - 1)) + 1;
}

static constexpr unsigned
divide_round_up(unsigned a, unsigned b)
{
    return (a + b - 1) / b;
}

struct slice_pool {
    size_t slice_size;

    /**
     * Number of slices that fit on one MMU page (4 kB).
     */
    unsigned slices_per_page;

    unsigned pages_per_slice;

    unsigned pages_per_area;

    unsigned slices_per_area;

    /**
     * Number of pages for the area header.
     */
    unsigned header_pages;

    size_t area_size;

    boost::intrusive::list<struct slice_area,
                           boost::intrusive::member_hook<struct slice_area,
                                                         slice_area::SiblingsHook,
                                                         &slice_area::siblings>,
                           boost::intrusive::constant_time_size<false>> areas;

    slice_pool(size_t _slice_size, unsigned _slices_per_area);
    ~slice_pool();

    void Compress();

    gcc_pure
    struct slice_area *FindNonFullArea();

    struct slice_area *GetArea();
};

/*
 * slice_area methods
 *
 */

slice_area::slice_area(struct slice_pool &pool)
    :allocated_count(0), free_head(0)
{
    /* build the "free" list */
    for (unsigned i = 0; i < pool.slices_per_area - 1; ++i)
        slices[i].next = i + 1;

    slices[pool.slices_per_area - 1].next = END_OF_LIST;
}

struct slice_area *
slice_area::New(struct slice_pool &pool)
{
    void *p = mmap_alloc_anonymous(pool.area_size);
    if (p == (void *)-1) {
        fputs("Out of adress space\n", stderr);
        abort();
    }

    return ::new(p) slice_area(pool);
}

inline bool
slice_area::IsFull(gcc_unused const struct slice_pool &pool) const
{
    assert(free_head < pool.slices_per_area ||
           free_head == END_OF_LIST);

    return free_head == END_OF_LIST;
}

void
slice_area::Delete(struct slice_pool &pool)
{
    assert(allocated_count == 0);

#ifndef NDEBUG
    for (unsigned i = 0; i < pool.slices_per_area; ++i)
        assert(slices[i].next < pool.slices_per_area ||
               slices[i].next == END_OF_LIST);

    unsigned i = free_head;
    while (i != END_OF_LIST) {
        assert(i < pool.slices_per_area);

        unsigned next = slices[i].next;
        slices[i].next = MARK;
        i = next;
    }
#endif

    this->~slice_area();
    mmap_free(this, pool.area_size);
}

inline void *
slice_area::GetPage(const struct slice_pool &pool, unsigned page)
{
    assert(page <= pool.pages_per_area);

    return (uint8_t *)this + (pool.header_pages + page) * mmap_page_size();
}

inline void *
slice_area::GetSlice(const struct slice_pool &pool, unsigned slice)
{
    assert(slice < pool.slices_per_area);
    assert(slices[slice].IsAllocated());

    unsigned page = (slice / pool.slices_per_page) * pool.pages_per_slice;
    slice %= pool.slices_per_page;

    return (uint8_t *)GetPage(pool, page) + slice * pool.slice_size;
}

inline unsigned
slice_area::IndexOf(const struct slice_pool &pool, const void *_p)
{
    const uint8_t *p = (const uint8_t *)_p;
    assert(p >= (uint8_t *)GetPage(pool, 0));
    assert(p < (uint8_t *)GetPage(pool, pool.pages_per_area));

    size_t offset = p - (const uint8_t *)this;
    const unsigned page = offset / mmap_page_size() - pool.header_pages;
    offset %= mmap_page_size();
    assert(offset % pool.slice_size == 0);

    return page * pool.slices_per_page / pool.pages_per_slice
        + offset / pool.slice_size;
}

unsigned
slice_area::FindFree(const struct slice_pool &pool, unsigned start) const
{
    assert(start <= pool.slices_per_page);

    const unsigned end = pool.slices_per_page;

    unsigned i;
    for (i = start; i != end; ++i)
        if (!slices[i].IsAllocated())
            break;

    return i;
}

/**
 * Find the first allocated slot index, starting at the specified
 * position.
 */
gcc_pure
unsigned
slice_area::FindAllocated(const struct slice_pool &pool, unsigned start) const
{
    assert(start <= pool.slices_per_page);

    const unsigned end = pool.slices_per_page;

    unsigned i;
    for (i = start; i != end; ++i)
        if (slices[i].IsAllocated())
            break;

    return i;
}

void
slice_area::PunchSliceRange(struct slice_pool &pool,
                            unsigned start, gcc_unused unsigned end)
{
    assert(start <= end);

    unsigned start_page = divide_round_up(start, pool.slices_per_page)
        * pool.pages_per_slice;
    unsigned end_page = (start / pool.slices_per_page)
        * pool.pages_per_slice;
    assert(start_page <= end_page);
    if (start_page == end_page)
        return;

    uint8_t *start_pointer = (uint8_t *)GetPage(pool, start_page);
    uint8_t *end_pointer = (uint8_t *)GetPage(pool, end_page);

    mmap_discard_pages(start_pointer, end_pointer - start_pointer);
}

void
slice_area::Compress(struct slice_pool &pool)
{
    unsigned position = 0;

    while (true) {
        unsigned first_free = FindFree(pool, position);
        if (first_free == pool.slices_per_page)
            break;

        unsigned first_allocated = FindAllocated(pool, first_free + 1);
        PunchSliceRange(pool, first_free, first_allocated);

        position = first_allocated;
    }
}

/*
 * slice_pool methods
 *
 */

inline
slice_pool::slice_pool(size_t _slice_size, unsigned _slices_per_area)
{
    assert(_slice_size > 0);
    assert(_slices_per_area > 0);

    if (_slice_size <= mmap_page_size() / 2) {
        slice_size = align_size(_slice_size);

        slices_per_page = mmap_page_size() / slice_size;
        pages_per_slice = 1;

        pages_per_area = divide_round_up(_slices_per_area,
                                         slices_per_page);
    } else {
        slice_size = align_page_size(_slice_size);

        slices_per_page = 1;
        pages_per_slice = slice_size / mmap_page_size();

        pages_per_area = _slices_per_area * pages_per_slice;
    }

    slices_per_area = (pages_per_area / pages_per_slice) * slices_per_page;

    const struct slice_area *area = nullptr;
    const size_t header_size = sizeof(*area)
        + sizeof(area->slices[0]) * (slices_per_area - 1);
    header_pages = divide_round_up(header_size, mmap_page_size());

    area_size = mmap_page_size() * (header_pages + pages_per_area);
}

inline
slice_pool::~slice_pool()
{
    areas.clear_and_dispose(slice_area::Disposer{*this});
}

struct slice_pool *
slice_pool_new(size_t slice_size, unsigned slices_per_area)
{
    return new slice_pool(slice_size, slices_per_area);
}

void
slice_pool_free(struct slice_pool *pool)
{
    delete pool;
}

size_t
slice_pool_get_slice_size(const struct slice_pool *pool)
{
    assert(pool != nullptr);

    return pool->slice_size;
}

inline void
slice_pool::Compress()
{
    for (auto i = areas.begin(), end = areas.end(); i != end;) {
        if (i->IsEmpty()) {
            i = areas.erase_and_dispose(i, slice_area::Disposer{*this});
        } else {
            i->Compress(*this);
            ++i;
        }
    }
}

void
slice_pool_compress(struct slice_pool *pool)
{
    pool->Compress();
}

gcc_pure
inline struct slice_area *
slice_pool::FindNonFullArea()
{
    for (struct slice_area &area : areas)
        if (!area.IsFull(*this))
            return &area;

    return nullptr;
}

inline struct slice_area *
slice_pool::GetArea()
{
    struct slice_area *area = FindNonFullArea();
    if (area == nullptr) {
        area = slice_area::New(*this);
        areas.push_front(*area);
    }

    return area;
}

struct slice_area *
slice_pool_get_area(struct slice_pool *pool)
{
    return pool->GetArea();
}

inline void *
slice_area::Alloc(struct slice_pool &pool)
{
    assert(!IsFull(pool));

    const unsigned i = free_head;
    struct slice_slot *const slot = &slices[i];

    ++allocated_count;
    free_head = slot->next;
    slot->next = ALLOCATED;

    return GetSlice(pool, i);
}

void *
slice_alloc(struct slice_pool *pool, struct slice_area *area)
{
    assert(pool != nullptr);
    assert(area != nullptr);

    return area->Alloc(*pool);
}

void
slice_area::Free(struct slice_pool &pool, void *p)
{
    unsigned i = IndexOf(pool, p);
    assert(slices[i].IsAllocated());

    slices[i].next = free_head;
    free_head = i;

    assert(allocated_count > 0);
    --allocated_count;
}

void
slice_free(struct slice_pool *pool, struct slice_area *area, void *p)
{
    assert(pool != nullptr);
    assert(area != nullptr);

    area->Free(*pool, p);
}
<|endoftext|>