<commit_before>#ifndef Rice__Enum__hpp_
#define Rice__Enum__hpp_

#include "to_from_ruby_defn.hpp"
#include "Address_Registration_Guard.hpp"
#include "Array.hpp"
#include "Hash.hpp"
#include "String.hpp"
#include "Module.hpp"
#include "Data_Type.hpp"

namespace Rice
{

//! Default traits for the Enum class template.
template<typename Enum_T>
struct Default_Enum_Traits
{
  //! Converts the enum value to a long.
  static long as_long(Enum_T value);
};

/*!
 *  \example enum/sample_enum.cpp
 */

//! A wrapper for enumerated types.
/*! Provides a simple type-safe wrapper for enumerated types.  At the
 *  ruby level, the class will have convenience methods for iterating
 *  over all the defined enum values, converting the values to strings,
 *  and more.
 *
 *  \param Enum_T the enumerated type
 *  \param Enum_Traits specifies the traits of the enumerated type.
 *
 *  Example:
 *  \code
 *    enum Color { Red, Green, Blue };
 *    Enum<Color> rb_cColor = define_enum<Color>()
 *      .define_value("Red", Red)
 *      .define_value("Green", Green)
 *      .define_value("Blue", Blue)
 *      .initialize("Color");
 *  \endcode
 */
template<typename Enum_T, typename Enum_Traits = Default_Enum_Traits<Enum_T> >
class Enum
  : public Module_impl<Data_Type<Enum_T>, Enum<Enum_T, Enum_Traits> >
{
public:
  //! Default constructor.
  Enum();

  //! Construct and initialize.
  Enum(
      char const * name,
      Module module = rb_cObject);

  //! Copy constructor.
  Enum(Enum const & other);

  //! Assignment operator.
  Enum & operator=(Enum const & other);

  //! Destructor.
  virtual ~Enum();

  //! Define a new enum value.
  /*! \param name the name of the enum value.
   *  \param value the value to associate with name.
   *  \return *this
   */
  Enum<Enum_T, Enum_Traits> & define_value(
      char const * name,
      Enum_T value);

  void swap(Enum & other);

private:
  //! Initialize the enum type.
  /*! Must be called only once.
   *  \param name the name of the class to define
   *  \param module the module in which to place the enum class.
   *  \return *this
   */
  Enum<Enum_T, Enum_Traits> & initialize(
      char const * name,
      Module module = rb_cObject);

private:
  static Object each(Object self);
  static Object to_s(Object self);
  static Object to_i(Object self);
  static Object inspect(Object self);
  static Object compare(Object lhs, Object rhs);
  static Object eql(Object lhs, Object rhs);
  static Object hash(Object self);
  static Object from_int(Class klass, Object i);

private:
  Array enums_;
  Address_Registration_Guard enums_guard_;

  Hash names_;
  Address_Registration_Guard names_guard_;
};

template<typename T>
Enum<T> define_enum(
    char const * name,
    Module module = rb_cObject);

} // namespace Rice

#include "Enum.ipp"

#endif // Rice__Enum__hpp_

<commit_msg>Comment update<commit_after>#ifndef Rice__Enum__hpp_
#define Rice__Enum__hpp_

#include "to_from_ruby_defn.hpp"
#include "Address_Registration_Guard.hpp"
#include "Array.hpp"
#include "Hash.hpp"
#include "String.hpp"
#include "Module.hpp"
#include "Data_Type.hpp"

namespace Rice
{

//! Default traits for the Enum class template.
template<typename Enum_T>
struct Default_Enum_Traits
{
  //! Converts the enum value to a long.
  static long as_long(Enum_T value);
};

/*!
 *  \example enum/sample_enum.cpp
 */

//! A wrapper for enumerated types.
/*! Provides a simple type-safe wrapper for enumerated types.  At the
 *  ruby level, the class will have convenience methods for iterating
 *  over all the defined enum values, converting the values to strings,
 *  and more.
 *
 *  \param Enum_T the enumerated type
 *  \param Enum_Traits specifies the traits of the enumerated type.
 *
 *  Example:
 *  \code
 *    enum Color { Red, Green, Blue };
 *    Enum<Color> rb_cColor = define_enum<Color>("Color")
 *      .define_value("Red", Red)
 *      .define_value("Green", Green)
 *      .define_value("Blue", Blue);
 *  \endcode
 */
template<typename Enum_T, typename Enum_Traits = Default_Enum_Traits<Enum_T> >
class Enum
  : public Module_impl<Data_Type<Enum_T>, Enum<Enum_T, Enum_Traits> >
{
public:
  //! Default constructor.
  Enum();

  //! Construct and initialize.
  Enum(
      char const * name,
      Module module = rb_cObject);

  //! Copy constructor.
  Enum(Enum const & other);

  //! Assignment operator.
  Enum & operator=(Enum const & other);

  //! Destructor.
  virtual ~Enum();

  //! Define a new enum value.
  /*! \param name the name of the enum value.
   *  \param value the value to associate with name.
   *  \return *this
   */
  Enum<Enum_T, Enum_Traits> & define_value(
      char const * name,
      Enum_T value);

  void swap(Enum & other);

private:
  //! Initialize the enum type.
  /*! Must be called only once.
   *  \param name the name of the class to define
   *  \param module the module in which to place the enum class.
   *  \return *this
   */
  Enum<Enum_T, Enum_Traits> & initialize(
      char const * name,
      Module module = rb_cObject);

private:
  static Object each(Object self);
  static Object to_s(Object self);
  static Object to_i(Object self);
  static Object inspect(Object self);
  static Object compare(Object lhs, Object rhs);
  static Object eql(Object lhs, Object rhs);
  static Object hash(Object self);
  static Object from_int(Class klass, Object i);

private:
  Array enums_;
  Address_Registration_Guard enums_guard_;

  Hash names_;
  Address_Registration_Guard names_guard_;
};

template<typename T>
Enum<T> define_enum(
    char const * name,
    Module module = rb_cObject);

} // namespace Rice

#include "Enum.ipp"

#endif // Rice__Enum__hpp_

<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/**
 * Randomized malloc, for detecting memory-based non-determinisms in redex.
 * To use, build redex-all-malloc-dbg, and then run as follows (you'll probably
 * want all the args from a "real" redex run, but this is the idea):
 *
 *   MALLOC_SEED=seed1 ./redex-all-malloc-dbg --out out-seed1.apk in.apk
 *   MALLOC_SEED=seed2 ./redex-all-malloc-dbg --out out-seed2.apk in.apk
 *
 * If the two output APKs differ, it could be because of an indeterminism
 * caused by branching on pointer values (e.g. containers sorted by pointer
 * keys).
 *
 * Note that this is NOT an attempt to make a deterministic allocator. System
 * malloc is non deterministic (practically speaking), but it will often behave
 * very similarly, which can hide non determinisms caused by pointers. This
 * allocator is intended to make such non determinisms happen *every* time,
 * instead of only once in a while.
 */

#ifdef __APPLE__
#include <dlfcn.h>
#endif

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <string>
#include <vector>

#include <thread>

#include "Debug.h"

namespace {

// The tinyest PRNG ever.
// http://www.woodmann.com/forum/showthread.php?3100-super-tiny-PRNG
class TinyPRNG {
 public:
  explicit TinyPRNG(const std::string& randSeed) { seed(randSeed); }

  uint32_t next_rand() {
    uint32_t result = 0;
    for (int i = 0; i < 32; i++) {
      // Advance the rand state.
      m_next_rand += (m_next_rand * m_next_rand) | 5;
      // pull out the high bit.
      result |= (m_next_rand >> 31) << i;
    }
    return result;
  }

  void seed(const std::string& randSeed) {
    constexpr auto state_size = sizeof(m_next_rand);
    char state[state_size] = {};
    std::string::size_type idx = 0;
    for (auto& e : randSeed) {
      state[idx % state_size] ^= e;
      idx++;
    }
    memcpy(&m_next_rand, state, state_size);
  }

 private:
  uint32_t m_next_rand = 0;
};

} // namespace

#ifdef __APPLE__
typedef void* (*MallocFn)(size_t);
static auto libc_malloc =
    reinterpret_cast<MallocFn>(dlsym(RTLD_NEXT, "malloc"));
#endif

#ifdef __linux__
extern "C" {
extern void* __libc_malloc(size_t size);
}

static auto libc_malloc = __libc_malloc;
#endif

size_t next_power_of_two(size_t x) {
  // Turn off all but msb.
  while ((x & (x - 1)) != 0) {
    x &= x - 1;
  }
  return x << 1;
}

namespace {
class MallocDebug {
 public:
  explicit MallocDebug() : m_rand("wharblegarbl") {
    const char* seed_env = getenv("MALLOC_SEED");
    if (seed_env != nullptr) {
      printf("re-seeding with %s\n", seed_env);
      m_rand.seed(seed_env);
    }
  }

  void* malloc(size_t size) noexcept {
    if (m_in_malloc) {
      return libc_malloc(size);
    }
    m_in_malloc = true;
    auto ret = malloc_impl(size);
    m_in_malloc = false;
    return ret;
  }

 private:
  struct Block {
    void* ptr;
    size_t size;
    Block(void* ptr, size_t size) : ptr(ptr), size(size) {}
    ~Block() { free(ptr); }

    Block(const Block&) = delete;
    Block(Block&& other) noexcept : ptr(other.release()), size(other.size) {}

    Block& operator=(const Block&) = delete;
    Block& operator=(Block&& rhs) {
      ptr = rhs.release();
      size = rhs.size;
      return *this;
    }

    void* release() {
      void* tmp = ptr;
      ptr = nullptr;
      return tmp;
    }
  };

  bool m_in_malloc = false;
  std::map<size_t, std::vector<Block>> m_blocks;
  TinyPRNG m_rand;

  void* malloc_impl(size_t size) noexcept {
    constexpr int block_count = 8;

    auto next_size = next_power_of_two(size);

    auto it = m_blocks.find(next_size);
    if (it == m_blocks.end()) {
      it = m_blocks.emplace(next_size, std::vector<Block>()).first;
    }

    auto& vec = it->second;

    while (vec.size() < block_count) {
      auto ptr = libc_malloc(next_size);
      vec.emplace_back(ptr, next_size);
    }

    auto idx = m_rand.next_rand() % block_count;
    auto block_size = vec[idx].size;
    auto block_ptr = vec[idx].release();
    vec.erase(vec.begin() + idx);

    always_assert(block_size >= size);
    return block_ptr;
  }
};

thread_local MallocDebug malloc_debug;

} // namespace

extern "C" {

void* malloc(size_t sz) { return malloc_debug.malloc(sz); }
}
<commit_msg>Do not print seed in MallocDebug<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/**
 * Randomized malloc, for detecting memory-based non-determinisms in redex.
 * To use, build redex-all-malloc-dbg, and then run as follows (you'll probably
 * want all the args from a "real" redex run, but this is the idea):
 *
 *   MALLOC_SEED=seed1 ./redex-all-malloc-dbg --out out-seed1.apk in.apk
 *   MALLOC_SEED=seed2 ./redex-all-malloc-dbg --out out-seed2.apk in.apk
 *
 * If the two output APKs differ, it could be because of an indeterminism
 * caused by branching on pointer values (e.g. containers sorted by pointer
 * keys).
 *
 * Note that this is NOT an attempt to make a deterministic allocator. System
 * malloc is non deterministic (practically speaking), but it will often behave
 * very similarly, which can hide non determinisms caused by pointers. This
 * allocator is intended to make such non determinisms happen *every* time,
 * instead of only once in a while.
 */

#ifdef __APPLE__
#include <dlfcn.h>
#endif

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <string>
#include <vector>

#include <thread>

#include "Debug.h"

namespace {

// The tinyest PRNG ever.
// http://www.woodmann.com/forum/showthread.php?3100-super-tiny-PRNG
class TinyPRNG {
 public:
  explicit TinyPRNG(const std::string& randSeed) { seed(randSeed); }

  uint32_t next_rand() {
    uint32_t result = 0;
    for (int i = 0; i < 32; i++) {
      // Advance the rand state.
      m_next_rand += (m_next_rand * m_next_rand) | 5;
      // pull out the high bit.
      result |= (m_next_rand >> 31) << i;
    }
    return result;
  }

  void seed(const std::string& randSeed) {
    constexpr auto state_size = sizeof(m_next_rand);
    char state[state_size] = {};
    std::string::size_type idx = 0;
    for (auto& e : randSeed) {
      state[idx % state_size] ^= e;
      idx++;
    }
    memcpy(&m_next_rand, state, state_size);
  }

 private:
  uint32_t m_next_rand = 0;
};

} // namespace

#ifdef __APPLE__
typedef void* (*MallocFn)(size_t);
static auto libc_malloc =
    reinterpret_cast<MallocFn>(dlsym(RTLD_NEXT, "malloc"));
#endif

#ifdef __linux__
extern "C" {
extern void* __libc_malloc(size_t size);
}

static auto libc_malloc = __libc_malloc;
#endif

size_t next_power_of_two(size_t x) {
  // Turn off all but msb.
  while ((x & (x - 1)) != 0) {
    x &= x - 1;
  }
  return x << 1;
}

namespace {

constexpr bool PRINT_SEED = false;

class MallocDebug {
 public:
  explicit MallocDebug() : m_rand("wharblegarbl") {
    const char* seed_env = getenv("MALLOC_SEED");
    if (seed_env != nullptr) {
      if (PRINT_SEED) {
        printf("re-seeding with %s\n", seed_env);
      }
      m_rand.seed(seed_env);
    }
  }

  void* malloc(size_t size) noexcept {
    if (m_in_malloc) {
      return libc_malloc(size);
    }
    m_in_malloc = true;
    auto ret = malloc_impl(size);
    m_in_malloc = false;
    return ret;
  }

 private:
  struct Block {
    void* ptr;
    size_t size;
    Block(void* ptr, size_t size) : ptr(ptr), size(size) {}
    ~Block() { free(ptr); }

    Block(const Block&) = delete;
    Block(Block&& other) noexcept : ptr(other.release()), size(other.size) {}

    Block& operator=(const Block&) = delete;
    Block& operator=(Block&& rhs) {
      ptr = rhs.release();
      size = rhs.size;
      return *this;
    }

    void* release() {
      void* tmp = ptr;
      ptr = nullptr;
      return tmp;
    }
  };

  bool m_in_malloc = false;
  std::map<size_t, std::vector<Block>> m_blocks;
  TinyPRNG m_rand;

  void* malloc_impl(size_t size) noexcept {
    constexpr int block_count = 8;

    auto next_size = next_power_of_two(size);

    auto it = m_blocks.find(next_size);
    if (it == m_blocks.end()) {
      it = m_blocks.emplace(next_size, std::vector<Block>()).first;
    }

    auto& vec = it->second;

    while (vec.size() < block_count) {
      auto ptr = libc_malloc(next_size);
      vec.emplace_back(ptr, next_size);
    }

    auto idx = m_rand.next_rand() % block_count;
    auto block_size = vec[idx].size;
    auto block_ptr = vec[idx].release();
    vec.erase(vec.begin() + idx);

    always_assert(block_size >= size);
    return block_ptr;
  }
};

thread_local MallocDebug malloc_debug;

} // namespace

extern "C" {

void* malloc(size_t sz) { return malloc_debug.malloc(sz); }
}
<|endoftext|>


<commit_before>#include "JsPlaylistItems.h"

#include "NodeTools.h"
#include "JsVlcPlayer.h"

v8::Persistent<v8::Function> JsVlcPlaylistItems::_jsConstructor;

JsVlcPlaylistItems::JsVlcPlaylistItems( v8::Local<v8::Object>& thisObject, JsVlcPlayer* jsPlayer ) :
    _jsPlayer( jsPlayer )
{
    Wrap( thisObject );
}

v8::UniquePersistent<v8::Object> JsVlcPlaylistItems::create( JsVlcPlayer& player )
{
    using namespace v8;

    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope( isolate );

    Local<Function> constructor =
        Local<Function>::New( isolate, _jsConstructor );

    Local<Value> argv[] = { player.handle() };

    return { isolate, constructor->NewInstance( sizeof( argv ) / sizeof( argv[0] ), argv ) };
}

void JsVlcPlaylistItems::initJsApi()
{
    using namespace v8;

    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope( isolate );

    Local<FunctionTemplate> ct = FunctionTemplate::New( isolate, jsCreate );
    ct->SetClassName( String::NewFromUtf8( isolate, "JsVlcPlaylistItems" ) );

    Local<ObjectTemplate> jsTemplate = ct->InstanceTemplate();
    jsTemplate->SetInternalFieldCount( 1 );

    SET_RO_PROPERTY( jsTemplate, "count", &JsVlcPlaylistItems::count );

    SET_METHOD( ct, "clear", &JsVlcPlaylistItems::clear );
    SET_METHOD( ct, "remove", &JsVlcPlaylistItems::remove );

    _jsConstructor.Reset( isolate, ct->GetFunction() );
}

void JsVlcPlaylistItems::jsCreate( const v8::FunctionCallbackInfo<v8::Value>& args )
{
    using namespace v8;

    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope( isolate );

    Local<Object> thisObject = args.Holder();
    if( args.IsConstructCall() && thisObject->InternalFieldCount() > 0 ) {
        JsVlcPlayer* jsPlayer =
            ObjectWrap::Unwrap<JsVlcPlayer>( Handle<Object>::Cast( args[0] ) );
        if( jsPlayer ) {
            JsVlcPlaylistItems* jsPlaylist = new JsVlcPlaylistItems( thisObject, jsPlayer );
            args.GetReturnValue().Set( thisObject );
        }
    } else {
        Local<Function> constructor =
            Local<Function>::New( isolate, _jsConstructor );
        Local<Value> argv[] = { args[0] };
        args.GetReturnValue().Set(
            constructor->NewInstance( sizeof( argv ) / sizeof( argv[0] ), argv ) );
    }
}

unsigned JsVlcPlaylistItems::count()
{
    return _jsPlayer->player().item_count();
}

void JsVlcPlaylistItems::clear()
{
    return _jsPlayer->player().clear_items();
}

bool JsVlcPlaylistItems::remove( unsigned int idx )
{
   return _jsPlayer->player().delete_item( idx );
}
<commit_msg>cosmetics<commit_after>#include "JsPlaylistItems.h"

#include "NodeTools.h"
#include "JsVlcPlayer.h"

v8::Persistent<v8::Function> JsVlcPlaylistItems::_jsConstructor;

JsVlcPlaylistItems::JsVlcPlaylistItems( v8::Local<v8::Object>& thisObject, JsVlcPlayer* jsPlayer ) :
    _jsPlayer( jsPlayer )
{
    Wrap( thisObject );
}

v8::UniquePersistent<v8::Object> JsVlcPlaylistItems::create( JsVlcPlayer& player )
{
    using namespace v8;

    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope( isolate );

    Local<Function> constructor =
        Local<Function>::New( isolate, _jsConstructor );

    Local<Value> argv[] = { player.handle() };

    return { isolate, constructor->NewInstance( sizeof( argv ) / sizeof( argv[0] ), argv ) };
}

void JsVlcPlaylistItems::initJsApi()
{
    using namespace v8;

    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope( isolate );

    Local<FunctionTemplate> constructorTemplate = FunctionTemplate::New( isolate, jsCreate );
    constructorTemplate->SetClassName(
        String::NewFromUtf8( isolate, "VlcPlaylistItems", v8::String::kInternalizedString ) );

    Local<ObjectTemplate> protoTemplate = constructorTemplate->PrototypeTemplate();
    Local<ObjectTemplate> instanceTemplate = constructorTemplate->InstanceTemplate();
    instanceTemplate->SetInternalFieldCount( 1 );

    SET_RO_PROPERTY( instanceTemplate, "count", &JsVlcPlaylistItems::count );

    SET_METHOD( constructorTemplate, "clear", &JsVlcPlaylistItems::clear );
    SET_METHOD( constructorTemplate, "remove", &JsVlcPlaylistItems::remove );

    Local<Function> constructor = constructorTemplate->GetFunction();
    _jsConstructor.Reset( isolate, constructor );
}

void JsVlcPlaylistItems::jsCreate( const v8::FunctionCallbackInfo<v8::Value>& args )
{
    using namespace v8;

    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope( isolate );

    Local<Object> thisObject = args.Holder();
    if( args.IsConstructCall() && thisObject->InternalFieldCount() > 0 ) {
        JsVlcPlayer* jsPlayer =
            ObjectWrap::Unwrap<JsVlcPlayer>( Handle<Object>::Cast( args[0] ) );
        if( jsPlayer ) {
            JsVlcPlaylistItems* jsPlaylist = new JsVlcPlaylistItems( thisObject, jsPlayer );
            args.GetReturnValue().Set( thisObject );
        }
    } else {
        Local<Function> constructor =
            Local<Function>::New( isolate, _jsConstructor );
        Local<Value> argv[] = { args[0] };
        args.GetReturnValue().Set(
            constructor->NewInstance( sizeof( argv ) / sizeof( argv[0] ), argv ) );
    }
}

unsigned JsVlcPlaylistItems::count()
{
    return _jsPlayer->player().item_count();
}

void JsVlcPlaylistItems::clear()
{
    return _jsPlayer->player().clear_items();
}

bool JsVlcPlaylistItems::remove( unsigned int idx )
{
   return _jsPlayer->player().delete_item( idx );
}
<|endoftext|>


<commit_before>/****************** <VPR heading BEGIN do not edit this line> *****************
 *
 * VR Juggler Portable Runtime
 *
 * Original Authors:
 *   Allen Bierbaum, Patrick Hartling, Kevin Meinert, Carolina Cruz-Neira
 *
 ****************** <VPR heading END do not edit this line> ******************/

/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2006 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <vpr/vprConfig.h>
#include <vpr/IO/Selector.h>
#include <vpr/Util/Assert.h>
#include <vpr/Util/Debug.h>
#include <vpr/IO/Socket/SocketConnector.h>

namespace vpr
{

void SocketConnector::connect(vpr::SocketStream& newStream,
                                           const vpr::InetAddr& remoteAddr,
                                           vpr::Interval timeout,
                                           const vpr::InetAddr& localAddr)
{
    //vpr::InetAddr remote_addr;

    // Open the socket
    checkOpen(newStream);

    /*  This actually happens in connect start
    if ( localAddr != vpr::InetAddr::AnyAddr )
    {
       vpr::ReturnStatus status;
       newStream.setLocalAddr(localAddr);
       status = newStream.bind();
       vprASSERT(status.success() && "Failed to bind local address");
    }
    */

    // Start the connection
    connectStart(newStream, timeout, localAddr);

    newStream.setRemoteAddr(remoteAddr);

    // Attempt the connection
    newStream.connect(timeout);

    /*
    // If the connect call did not return success, it may be the result of
    // using non-blocking sockets.
    if ( ! ret_val.success() )
    {
       // If connect() gave us a status saying that the connection is in
       // progress, try to complete the connection after the timeout period.
       // If there is no timeout period, simply return immediately.
       if ( ret_val == vpr::ReturnStatus::InProgress ||
            ret_val == vpr::ReturnStatus::WouldBlock )
       {
          if ( timeout != vpr::Interval::NoWait ) {
             ret_val = complete(newStream, &remote_addr, timeout);
          }
       }
    }
    // Finish up successful connection.
    else if(vpr::Interval::NoWait != timeout) {
       ret_val = complete(newStream, &remote_addr, timeout);
    }
    */

    /*
    ** Since complete doesn't do anything really we don't need this
    if(ret_val.success())
    {
       ret_val = complete(newStream, timeout);
    }
    */
}

// Complete a non-blocking connection
// Try to complete a non-blocking connection.
void SocketConnector::complete(vpr::SocketStream& newStream,
                               const vpr::Interval timeout)
{
   if( newStream.isConnected() )
   {
      // XXX: Should this actually be a failure
      return;
   }

   // If non-blocking, then we can only wait as long as the timeout
   if ( ! newStream.isBlocking() )
   {
      vpr::IOSys::Handle handle;
      vpr::Selector selector;
      vpr::Uint16 num_events;

      // Use the selector to be informed when the SocketStream object is ready
      // to be used.  That is, when the object is connected.
      handle = newStream.getHandle();
      selector.addHandle(handle);
      selector.setIn(handle, vpr::Selector::Read | vpr::Selector::Write);
      selector.select(num_events, timeout);

      // If the selector told us that our handle is ready, we are successfully
      // connected.
      if ( selector.getOut(handle) & (vpr::Selector::Read | vpr::Selector::Write) )
      {
         /*
         if ( remoteAddr != NULL ) {
            (*remoteAddr) = newStream.getRemoteAddr();
         }
         */
      }
      // else Use the status from the selector
   }
   else     // Not a non-blocking socket
   {
      vprASSERT(false && "Should not call complete on a non-blocking socket");
      /*
      if ( remoteAddr != NULL ) {
         (*remoteAddr) = newStream.getRemoteAddr();
      }
      */
   }
}

void SocketConnector::checkOpen(SocketStream& newStream)
{
   if (!newStream.isOpen())
   {
      try
      {
         newStream.open();
      }
      catch (IOException& ex)
      {
         vprDEBUG(vprDBG_ALL,vprDBG_CRITICAL_LVL)
            << "vpr::Connector:CheckOpen: Failed to open socket\n"
            << vprDEBUG_FLUSH;
         throw;
      }
   }
}

// Do preconnection rituals
// - If not bound, then bind to local addr
// - If timeout == 0, then set nonblocking
void SocketConnector::connectStart(SocketStream& newStream,
                                   vpr::Interval timeout,
                                   const vpr::InetAddr& localAddr)
{
   vprASSERT(newStream.isOpen());

   if(!newStream.isBound())      // If we are not bound yet
   {
      // If timeout is 0, then we are non-blocking
      if(vpr::Interval::NoWait == timeout)
      {
         newStream.setBlocking(false);
      }

      // Set addr and bind
      newStream.setLocalAddr(localAddr);
      newStream.bind();
   }
}

}
<commit_msg>Removed commented out code that contains the last remaining references to vpr::ReturnStatus under vpr/IO.<commit_after>/****************** <VPR heading BEGIN do not edit this line> *****************
 *
 * VR Juggler Portable Runtime
 *
 * Original Authors:
 *   Allen Bierbaum, Patrick Hartling, Kevin Meinert, Carolina Cruz-Neira
 *
 ****************** <VPR heading END do not edit this line> ******************/

/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2006 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <vpr/vprConfig.h>
#include <vpr/IO/Selector.h>
#include <vpr/Util/Assert.h>
#include <vpr/Util/Debug.h>
#include <vpr/IO/Socket/SocketConnector.h>

namespace vpr
{

void SocketConnector::connect(vpr::SocketStream& newStream,
                                           const vpr::InetAddr& remoteAddr,
                                           vpr::Interval timeout,
                                           const vpr::InetAddr& localAddr)
{
    //vpr::InetAddr remote_addr;

    // Open the socket
    checkOpen(newStream);

    // Start the connection
    connectStart(newStream, timeout, localAddr);

    newStream.setRemoteAddr(remoteAddr);

    // Attempt the connection
    newStream.connect(timeout);
}

// Complete a non-blocking connection
// Try to complete a non-blocking connection.
void SocketConnector::complete(vpr::SocketStream& newStream,
                               const vpr::Interval timeout)
{
   if( newStream.isConnected() )
   {
      // XXX: Should this actually be a failure
      return;
   }

   // If non-blocking, then we can only wait as long as the timeout
   if ( ! newStream.isBlocking() )
   {
      vpr::IOSys::Handle handle;
      vpr::Selector selector;
      vpr::Uint16 num_events;

      // Use the selector to be informed when the SocketStream object is ready
      // to be used.  That is, when the object is connected.
      handle = newStream.getHandle();
      selector.addHandle(handle);
      selector.setIn(handle, vpr::Selector::Read | vpr::Selector::Write);
      selector.select(num_events, timeout);

      // If the selector told us that our handle is ready, we are successfully
      // connected.
      if ( selector.getOut(handle) & (vpr::Selector::Read | vpr::Selector::Write) )
      {
         /*
         if ( remoteAddr != NULL ) {
            (*remoteAddr) = newStream.getRemoteAddr();
         }
         */
      }
      // else Use the status from the selector
   }
   else     // Not a non-blocking socket
   {
      vprASSERT(false && "Should not call complete on a non-blocking socket");
      /*
      if ( remoteAddr != NULL ) {
         (*remoteAddr) = newStream.getRemoteAddr();
      }
      */
   }
}

void SocketConnector::checkOpen(SocketStream& newStream)
{
   if (!newStream.isOpen())
   {
      try
      {
         newStream.open();
      }
      catch (IOException& ex)
      {
         vprDEBUG(vprDBG_ALL,vprDBG_CRITICAL_LVL)
            << "vpr::Connector:CheckOpen: Failed to open socket\n"
            << vprDEBUG_FLUSH;
         throw;
      }
   }
}

// Do preconnection rituals
// - If not bound, then bind to local addr
// - If timeout == 0, then set nonblocking
void SocketConnector::connectStart(SocketStream& newStream,
                                   vpr::Interval timeout,
                                   const vpr::InetAddr& localAddr)
{
   vprASSERT(newStream.isOpen());

   if(!newStream.isBound())      // If we are not bound yet
   {
      // If timeout is 0, then we are non-blocking
      if(vpr::Interval::NoWait == timeout)
      {
         newStream.setBlocking(false);
      }

      // Set addr and bind
      newStream.setLocalAddr(localAddr);
      newStream.bind();
   }
}

}
<|endoftext|>


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

#include <libc/stdio.h>
#include <libcpp/algorithm.h>
#include "pty.h"

namespace OS
{
	IODefineMeta(PTY, IO::Object)
	IODefineMeta(PTYDevice, IO::Object)

	PTYDevice *PTYDevice::Init(PTY *pty, bool master, uint32_t id)
	{
		if(!IO::Object::Init())
			return nullptr;

		char name[64];
		sprintf(name, "%cty%03d", master ? 'p' : 't', id);

		_pty = pty;
		_master = master;
		_other = nullptr;
		_putProc = nullptr;
		_node = VFS::GetDevFS()->CreateNode(name, this, IOMemberFunctionCast(CFS::Node::ReadProc, this, &PTYDevice::Read), IOMemberFunctionCast(CFS::Node::WriteProc , this, &PTYDevice::Write));
		_node->SetSizeProc(IOMemberFunctionCast(CFS::Node::SizeProc, this, &PTYDevice::GetSize));
		_node->SetIoctlProc(IOMemberFunctionCast(CFS::Node::IoctlProc, this, &PTYDevice::Ioctl));
		_node->SetOpenProc(IOMemberFunctionCast(CFS::Node::OpenCloseProc, this, &PTYDevice::Open));

		return this;
	}

	void PTYDevice::Connect(PTYDevice *other)
	{
		_other = other;
	}

	size_t PTYDevice::GetSize()
	{
		spinlock_lock(&_pty->_lock);
		size_t size = _data.size();
		spinlock_unlock(&_pty->_lock);

		return size;
	}

	void PTYDevice::SetPutcProc(PutcProc proc)
	{
		_putProc = proc;
	}

	void PTYDevice::Open()
	{
		_data.clear();
	}

	void PTYDevice::Putc(char character)
	{
		if(__expect_false(_putProc))
		{
			_putProc(character);
			return;
		}

		_data.push(character);
	}

	KernReturn<void> PTYDevice::Ioctl(VFS::Context *context, uint32_t request, void *arg)
	{
		return _pty->Ioctl(_master, context, request, arg);
	}

	void PTYDevice::ParseCanonicalInput(char data)
	{
		if(data == _pty->_termios.c_cc[VKILL])
		{
			while(_pty->_canonicalBufferSize)
			{
				_pty->_canonicalBufferSize --;
				_pty->_canonicalBuffer[_pty->_canonicalBufferSize] = '\0';

				if(_pty->_termios.c_lflag & ECHO)
				{
					_other->Putc('\010');
					_other->Putc(' ');
					_other->Putc('\010');
				}
			}

			return;
		}

		if(data == _pty->_termios.c_cc[VERASE])
		{
			if(_pty->_canonicalBufferSize)
			{
				_pty->_canonicalBufferSize --;
				_pty->_canonicalBuffer[_pty->_canonicalBufferSize] = '\0';

				if(_pty->_termios.c_lflag & ECHO)
				{
					_other->Putc('\010');
					_other->Putc(' ');
					_other->Putc('\010');
				}
			}

			return;
		}

		if(data == _pty->_termios.c_cc[VEOF])
		{
			if(_pty->_canonicalBufferSize)
				_pty->FlushCanonicalBuffer();

			return;
		}

		_pty->_canonicalBuffer[_pty->_canonicalBufferSize] = data;

		if(_pty->_termios.c_lflag & ECHO)
			Putc(data);

		if(data == '\n')
		{
			_pty->_canonicalBufferSize ++;
			_pty->FlushCanonicalBuffer();

			return;
		}

		if(_pty->_canonicalBufferSize == sizeof(_pty->_canonicalBuffer))
		{
			_pty->FlushCanonicalBuffer();
			return;
		}

		_pty->_canonicalBufferSize ++;
	}

	void PTYDevice::ParseInput(char data)
	{
		if(data == '\n' && _pty->_termios.c_oflag & ONLCR)
			_other->Putc('\r');

		_other->Putc(data);
	}

	size_t PTYDevice::Write(VFS::Context *context, off_t offset, const void *data, size_t size)
	{
		spinlock_lock(&_pty->_lock);

		char buffer[kPTYMaxBufferSize];
		size_t write = std::min(size, sizeof(buffer));

		offset = size - write;
		context->CopyDataOut(reinterpret_cast<const uint8_t *>(data) + offset, buffer, write);

		if(_master)
		{
			for(size_t i = 0; i < write; i ++)
			{
				char data = buffer[i];

				if(_pty->_termios.c_lflag & ICANON)
				{
					ParseCanonicalInput(data);
					continue;
				}
				else if(_pty->_termios.c_lflag & ECHO)
					Putc(data);

				_other->Putc(data);
			}
		}
		else
		{
			for(size_t i = 0; i < write; i ++)
			{
				char data = buffer[i];
				ParseInput(data);
			}
		}

		spinlock_unlock(&_pty->_lock);

		return size;
	}

	size_t PTYDevice::Read(VFS::Context *context, __unused off_t offset, void *data, size_t size)
	{
		spinlock_lock(&_pty->_lock);

		size_t read = std::min(size, _data.size());
		if(read == 0)
		{
			spinlock_unlock(&_pty->_lock);
			return read;
		}

		char buffer[kPTYMaxBufferSize];

		for(size_t i = 0; i < read; i ++)
			buffer[i] = _data.pop();

		context->CopyDataIn(buffer, data, read);

		// TODO: Support VMIN for non ICANON TTYs

		spinlock_unlock(&_pty->_lock);

		return read;
	}

	static std::atomic<uint32_t> _ptyIDs = 0;

	PTY *PTY::Init()
	{
		if(!IO::Object::Init())
			return nullptr;

		_id = (++ _ptyIDs);

		_master = PTYDevice::Alloc()->Init(this, true, _id);
		_slave = PTYDevice::Alloc()->Init(this, false, _id);

		_master->Connect(_slave);
		_slave->Connect(_master);

		_winsize.ws_col = 80;
		_winsize.ws_row = 25;

		_termios.c_iflag = ICRNL | BRKINT;
		_termios.c_oflag = ONLCR | OPOST;
		_termios.c_lflag = ECHO | ECHOE | ECHOK | ICANON | ISIG | IEXTEN;
		_termios.c_cflag = CREAD;
		_termios.c_cc[VEOF] = 4; /* ^D */
		_termios.c_cc[VEOL] = 0; /* Not set */
		_termios.c_cc[VERASE] = '\b';
		_termios.c_cc[VINTR] = 3; /* ^C */
		_termios.c_cc[VKILL] = 21; /* ^U */
		_termios.c_cc[VMIN] = 1;
		_termios.c_cc[VQUIT] = 28; /* ^\ */
		_termios.c_cc[VSTART] = 17; /* ^Q */
		_termios.c_cc[VSTOP] = 19; /* ^S */
		_termios.c_cc[VSUSP] = 26; /* ^Z */
		_termios.c_cc[VTIME] = 0;

		return this;
	}

	PTYDevice *PTY::GetMasterDevice() const
	{
		return _master;
	}

	PTYDevice *PTY::GetSlaveDevice() const
	{
		return _slave;
	}

	KernReturn<void> PTY::Ioctl(bool fromMaster, VFS::Context *context, uint32_t request, void *arg)
	{
		switch(request)
		{
			case TIOCSWINSZ:
				if(!arg || !fromMaster)
					return Error(KERN_INVALID_ARGUMENT);

				context->CopyDataOut(arg, &_winsize, sizeof(winsize));
				return ErrorNone;

			case TIOCGWINSZ:
				if(!arg)
					return Error(KERN_INVALID_ARGUMENT);

				context->CopyDataIn(&_winsize, arg, sizeof(winsize));
				return ErrorNone;

			case TCGETS:
				if(!arg)
					return Error(KERN_INVALID_ARGUMENT);

				context->CopyDataIn(&_termios, arg, sizeof(termios));
				return ErrorNone;

			case TCSETS:
				if(!arg)
					return Error(KERN_INVALID_ARGUMENT);

				context->CopyDataOut(arg, &_termios, sizeof(termios));
				return ErrorNone;

			case TCSETSW:
			case TCSETSF:
			{
				if(!arg)
					return Error(KERN_INVALID_ARGUMENT);

				bool wasCanon = (_termios.c_lflag & ICANON);

				context->CopyDataOut(arg, &_termios, sizeof(termios));

				if(wasCanon && !(_termios.c_cflag & ICANON))
					FlushCanonicalBuffer();

				return ErrorNone;
			}
		}

		return Error(KERN_INVALID_ARGUMENT);
	}

	void PTY::FlushCanonicalBuffer()
	{
		for(size_t i = 0; i < _canonicalBufferSize; i ++)
			_slave->Putc(_canonicalBuffer[i]);

		_canonicalBufferSize = 0;
	}
}
<commit_msg>Fixed PTYDevice::Write bug with empty content<commit_after>//
//  pty.cpp
//  Firedrake
//
//  Created by Sidney Just
//  Copyright (c) 2015 by Sidney Just
//  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
//  documentation files (the "Software"), to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
//  and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
//  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
//  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#include <libc/stdio.h>
#include <libcpp/algorithm.h>
#include "pty.h"

namespace OS
{
	IODefineMeta(PTY, IO::Object)
	IODefineMeta(PTYDevice, IO::Object)

	PTYDevice *PTYDevice::Init(PTY *pty, bool master, uint32_t id)
	{
		if(!IO::Object::Init())
			return nullptr;

		char name[64];
		sprintf(name, "%cty%03d", master ? 'p' : 't', id);

		_pty = pty;
		_master = master;
		_other = nullptr;
		_putProc = nullptr;
		_node = VFS::GetDevFS()->CreateNode(name, this, IOMemberFunctionCast(CFS::Node::ReadProc, this, &PTYDevice::Read), IOMemberFunctionCast(CFS::Node::WriteProc , this, &PTYDevice::Write));
		_node->SetSizeProc(IOMemberFunctionCast(CFS::Node::SizeProc, this, &PTYDevice::GetSize));
		_node->SetIoctlProc(IOMemberFunctionCast(CFS::Node::IoctlProc, this, &PTYDevice::Ioctl));
		_node->SetOpenProc(IOMemberFunctionCast(CFS::Node::OpenCloseProc, this, &PTYDevice::Open));

		return this;
	}

	void PTYDevice::Connect(PTYDevice *other)
	{
		_other = other;
	}

	size_t PTYDevice::GetSize()
	{
		spinlock_lock(&_pty->_lock);
		size_t size = _data.size();
		spinlock_unlock(&_pty->_lock);

		return size;
	}

	void PTYDevice::SetPutcProc(PutcProc proc)
	{
		_putProc = proc;
	}

	void PTYDevice::Open()
	{
		_data.clear();
	}

	void PTYDevice::Putc(char character)
	{
		if(__expect_false(_putProc))
		{
			_putProc(character);
			return;
		}

		_data.push(character);
	}

	KernReturn<void> PTYDevice::Ioctl(VFS::Context *context, uint32_t request, void *arg)
	{
		return _pty->Ioctl(_master, context, request, arg);
	}

	void PTYDevice::ParseCanonicalInput(char data)
	{
		if(data == _pty->_termios.c_cc[VKILL])
		{
			while(_pty->_canonicalBufferSize)
			{
				_pty->_canonicalBufferSize --;
				_pty->_canonicalBuffer[_pty->_canonicalBufferSize] = '\0';

				if(_pty->_termios.c_lflag & ECHO)
				{
					_other->Putc('\010');
					_other->Putc(' ');
					_other->Putc('\010');
				}
			}

			return;
		}

		if(data == _pty->_termios.c_cc[VERASE])
		{
			if(_pty->_canonicalBufferSize)
			{
				_pty->_canonicalBufferSize --;
				_pty->_canonicalBuffer[_pty->_canonicalBufferSize] = '\0';

				if(_pty->_termios.c_lflag & ECHO)
				{
					_other->Putc('\010');
					_other->Putc(' ');
					_other->Putc('\010');
				}
			}

			return;
		}

		if(data == _pty->_termios.c_cc[VEOF])
		{
			if(_pty->_canonicalBufferSize)
				_pty->FlushCanonicalBuffer();

			return;
		}

		_pty->_canonicalBuffer[_pty->_canonicalBufferSize] = data;

		if(_pty->_termios.c_lflag & ECHO)
			Putc(data);

		if(data == '\n')
		{
			_pty->_canonicalBufferSize ++;
			_pty->FlushCanonicalBuffer();

			return;
		}

		if(_pty->_canonicalBufferSize == sizeof(_pty->_canonicalBuffer))
		{
			_pty->FlushCanonicalBuffer();
			return;
		}

		_pty->_canonicalBufferSize ++;
	}

	void PTYDevice::ParseInput(char data)
	{
		if(data == '\n' && _pty->_termios.c_oflag & ONLCR)
			_other->Putc('\r');

		_other->Putc(data);
	}

	size_t PTYDevice::Write(VFS::Context *context, off_t offset, const void *data, size_t size)
	{
		if(size == 0)
			return 0;

		spinlock_lock(&_pty->_lock);

		char buffer[kPTYMaxBufferSize];
		size_t write = std::min(size, sizeof(buffer));

		offset = size - write;
		context->CopyDataOut(reinterpret_cast<const uint8_t *>(data) + offset, buffer, write);

		if(_master)
		{
			for(size_t i = 0; i < write; i ++)
			{
				char data = buffer[i];

				if(_pty->_termios.c_lflag & ICANON)
				{
					ParseCanonicalInput(data);
					continue;
				}
				else if(_pty->_termios.c_lflag & ECHO)
					Putc(data);

				_other->Putc(data);
			}
		}
		else
		{
			for(size_t i = 0; i < write; i ++)
			{
				char data = buffer[i];
				ParseInput(data);
			}
		}

		spinlock_unlock(&_pty->_lock);

		return size;
	}

	size_t PTYDevice::Read(VFS::Context *context, __unused off_t offset, void *data, size_t size)
	{
		spinlock_lock(&_pty->_lock);

		size_t read = std::min(size, _data.size());
		if(read == 0)
		{
			spinlock_unlock(&_pty->_lock);
			return read;
		}

		char buffer[kPTYMaxBufferSize];

		for(size_t i = 0; i < read; i ++)
			buffer[i] = _data.pop();

		context->CopyDataIn(buffer, data, read);

		// TODO: Support VMIN for non ICANON TTYs

		spinlock_unlock(&_pty->_lock);

		return read;
	}

	static std::atomic<uint32_t> _ptyIDs = 0;

	PTY *PTY::Init()
	{
		if(!IO::Object::Init())
			return nullptr;

		_id = (++ _ptyIDs);

		_master = PTYDevice::Alloc()->Init(this, true, _id);
		_slave = PTYDevice::Alloc()->Init(this, false, _id);

		_master->Connect(_slave);
		_slave->Connect(_master);

		_winsize.ws_col = 80;
		_winsize.ws_row = 25;

		_termios.c_iflag = ICRNL | BRKINT;
		_termios.c_oflag = ONLCR | OPOST;
		_termios.c_lflag = ECHO | ECHOE | ECHOK | ICANON | ISIG | IEXTEN;
		_termios.c_cflag = CREAD;
		_termios.c_cc[VEOF] = 4; /* ^D */
		_termios.c_cc[VEOL] = 0; /* Not set */
		_termios.c_cc[VERASE] = '\b';
		_termios.c_cc[VINTR] = 3; /* ^C */
		_termios.c_cc[VKILL] = 21; /* ^U */
		_termios.c_cc[VMIN] = 1;
		_termios.c_cc[VQUIT] = 28; /* ^\ */
		_termios.c_cc[VSTART] = 17; /* ^Q */
		_termios.c_cc[VSTOP] = 19; /* ^S */
		_termios.c_cc[VSUSP] = 26; /* ^Z */
		_termios.c_cc[VTIME] = 0;

		return this;
	}

	PTYDevice *PTY::GetMasterDevice() const
	{
		return _master;
	}

	PTYDevice *PTY::GetSlaveDevice() const
	{
		return _slave;
	}

	KernReturn<void> PTY::Ioctl(bool fromMaster, VFS::Context *context, uint32_t request, void *arg)
	{
		switch(request)
		{
			case TIOCSWINSZ:
				if(!arg || !fromMaster)
					return Error(KERN_INVALID_ARGUMENT);

				context->CopyDataOut(arg, &_winsize, sizeof(winsize));
				return ErrorNone;

			case TIOCGWINSZ:
				if(!arg)
					return Error(KERN_INVALID_ARGUMENT);

				context->CopyDataIn(&_winsize, arg, sizeof(winsize));
				return ErrorNone;

			case TCGETS:
				if(!arg)
					return Error(KERN_INVALID_ARGUMENT);

				context->CopyDataIn(&_termios, arg, sizeof(termios));
				return ErrorNone;

			case TCSETS:
				if(!arg)
					return Error(KERN_INVALID_ARGUMENT);

				context->CopyDataOut(arg, &_termios, sizeof(termios));
				return ErrorNone;

			case TCSETSW:
			case TCSETSF:
			{
				if(!arg)
					return Error(KERN_INVALID_ARGUMENT);

				bool wasCanon = (_termios.c_lflag & ICANON);

				context->CopyDataOut(arg, &_termios, sizeof(termios));

				if(wasCanon && !(_termios.c_cflag & ICANON))
					FlushCanonicalBuffer();

				return ErrorNone;
			}
		}

		return Error(KERN_INVALID_ARGUMENT);
	}

	void PTY::FlushCanonicalBuffer()
	{
		for(size_t i = 0; i < _canonicalBufferSize; i ++)
			_slave->Putc(_canonicalBuffer[i]);

		_canonicalBufferSize = 0;
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 by Glenn Hickey (hickey@soe.ucsc.edu)
 *
 * Released under the MIT license, see LICENSE.txt
 */

#include <cstdlib>
#include <iostream>
#include "hal.h"

using namespace std;
using namespace hal;

static void getDimensions(AlignmentConstPtr outAlignment, const Genome* genome,
                          vector<Sequence::Info>& dimensions);

static void copyGenome(const Genome* inGenome, Genome* outGenome);

static void extractTree(const AlignmentConstPtr inAlignment,
                        AlignmentPtr outAlignment,
                        const string& rootName);

static void extract(const AlignmentConstPtr inAlignment,
                    AlignmentPtr outAlignment, const string& rootName);

static CLParserPtr initParser()
{
  CLParserPtr optionsParser = hdf5CLParserInstance(true);
  optionsParser->addArgument("inHalPath", "input hal file");
  optionsParser->addArgument("outHalPath", "output hal file");
  optionsParser->addOption("root", "root of subtree to extract", "\"\"");
  return optionsParser;
}

int main(int argc, char** argv)
{
  CLParserPtr optionsParser = initParser();

  string inHalPath;
  string outHalPath;
  string rootName;
  try
  {
    optionsParser->parseOptions(argc, argv);
    inHalPath = optionsParser->getArgument<string>("inHalPath");
    outHalPath = optionsParser->getArgument<string>("outHalPath");
    rootName = optionsParser->getOption<string>("root");
  }
  catch(exception& e)
  {
    cerr << e.what() << endl;
    optionsParser->printUsage(cerr);
    exit(1);
  }

  try
  {
    AlignmentConstPtr inAlignment = openHalAlignmentReadOnly(inHalPath,
                                                             optionsParser);
    if (inAlignment->getNumGenomes() == 0)
    {
      throw hal_exception("input hal alignmenet is empty");
    }

    AlignmentPtr outAlignment = hdf5AlignmentInstance();
    outAlignment->setOptionsFromParser(optionsParser);
    outAlignment->createNew(outHalPath);

    if (outAlignment->getNumGenomes() != 0)
    {
      throw hal_exception("output hal alignmenet cannot be initialized");
    }

    if (rootName == "\"\"" || inAlignment->getNumGenomes() == 0)
    {
      rootName = inAlignment->getRootName();
    }

    extractTree(inAlignment, outAlignment, rootName);
    extract(inAlignment, outAlignment, rootName);
  }
  catch(hal_exception& e)
  {
    cerr << "hal exception caught: " << e.what() << endl;
    return 1;
  }
  catch(exception& e)
  {
    cerr << "Exception caught: " << e.what() << endl;
    return 1;
  }

  return 0;
}

void getDimensions(AlignmentConstPtr outAlignment, const Genome* genome,
                   vector<Sequence::Info>& dimensions)
{
  assert(dimensions.size() == 0);
  SequenceIteratorConstPtr seqIt = genome->getSequenceIterator();
  SequenceIteratorConstPtr seqEndIt = genome->getSequenceEndIterator();

  bool root = outAlignment->getParentName(genome->getName()).empty();
  bool leaf = outAlignment->getChildNames(genome->getName()).empty();

  for (; seqIt != seqEndIt; seqIt->toNext())
  {
    const Sequence* sequence = seqIt->getSequence();
    Sequence::Info info(sequence->getName(),
                        sequence->getSequenceLength(),
                        root ? 0 : sequence->getNumTopSegments(),
                        leaf ? 0 : sequence->getNumBottomSegments());
    dimensions.push_back(info);
  }
}

void copyGenome(const Genome* inGenome, Genome* outGenome)
{
  DNAIteratorConstPtr inDna = inGenome->getDNAIterator();
  DNAIteratorPtr outDna = outGenome->getDNAIterator();
  hal_size_t n = inGenome->getSequenceLength();
  assert(n == outGenome->getSequenceLength());
  for (; (hal_size_t)inDna->getArrayIndex() < n; inDna->toRight(),
          outDna->toRight())
  {
    outDna->setChar(inDna->getChar());
  }

  TopSegmentIteratorConstPtr inTop = inGenome->getTopSegmentIterator();
  TopSegmentIteratorPtr outTop = outGenome->getTopSegmentIterator();
  n = outGenome->getNumTopSegments();
  assert(n == 0 || n == inGenome->getNumTopSegments());
  for (; (hal_size_t)inTop->getArrayIndex() < n; inTop->toRight(),
          outTop->toRight())
  {
    outTop->setCoordinates(inTop->getStartPosition(), inTop->getLength());
    outTop->setParentIndex(inTop->getParentIndex());
    outTop->setParentReversed(inTop->getParentReversed());
    outTop->setBottomParseIndex(inTop->getBottomParseIndex());
    outTop->setNextParalogyIndex(inTop->getNextParalogyIndex());
  }

  BottomSegmentIteratorConstPtr inBot = inGenome->getBottomSegmentIterator();
  BottomSegmentIteratorPtr outBot = outGenome->getBottomSegmentIterator();
  n = outGenome->getNumBottomSegments();
  assert(n == 0 || n == inGenome->getNumBottomSegments());
  hal_size_t nc = inGenome->getNumChildren();
  assert(nc == outGenome->getNumChildren());
  for (; (hal_size_t)inBot->getArrayIndex() < n; inBot->toRight(),
          outBot->toRight())
  {
    outBot->setCoordinates(inBot->getStartPosition(), inBot->getLength());
    for (hal_size_t child = 0; child < nc; ++child)
    {
      outBot->setChildIndex(child, inBot->getChildIndex(child));
      outBot->setChildReversed(child, inBot->getChildReversed(child));
    }
    outBot->setTopParseIndex(inBot->getTopParseIndex());
  }

  const map<string, string>& meta = inGenome->getMetaData()->getMap();
  map<string, string>::const_iterator i = meta.begin();
  for (; i != meta.end(); ++i)
  {
    outGenome->getMetaData()->set(i->first, i->second);
  }
}

static void extractTree(const AlignmentConstPtr inAlignment,
                        AlignmentPtr outAlignment,
                        const string& rootName)
{
  const Genome* genome = inAlignment->openGenome(rootName);
  if (genome == NULL)
  {
    throw hal_exception(string("Genome not found: ") + rootName);
  }
  Genome* newGenome = NULL;
  if (outAlignment->getNumGenomes() == 0 || genome->getParent() == NULL)
  {
    newGenome = outAlignment->addRootGenome(rootName);
  }
  else
  {
    const Genome* parent = genome->getParent();
    assert(parent != NULL);
    newGenome = outAlignment->addLeafGenome(rootName, parent->getName(),
                                            inAlignment->getBranchLength(
                                              parent->getName(), rootName));
  }
  assert(newGenome != NULL);

  inAlignment->closeGenome(genome);
  outAlignment->closeGenome(newGenome);

  vector<string> childNames = inAlignment->getChildNames(rootName);
  for (size_t i = 0; i < childNames.size(); ++i)
  {
    extractTree(inAlignment, outAlignment, childNames[i]);
  }
}

void extract(const AlignmentConstPtr inAlignment,
             AlignmentPtr outAlignment, const string& rootName)
{
  const Genome* genome = inAlignment->openGenome(rootName);
  Genome* newGenome = outAlignment->openGenome(rootName);
  assert(newGenome != NULL);

  vector<Sequence::Info> dimensions;
  getDimensions(outAlignment, genome, dimensions);
  newGenome->setDimensions(dimensions);

  cout << "Extracting " << genome->getName() << endl;
  copyGenome(genome, newGenome);

  inAlignment->closeGenome(genome);
  outAlignment->closeGenome(newGenome);

  vector<string> childNames = inAlignment->getChildNames(rootName);
  for (size_t i = 0; i < childNames.size(); ++i)
  {
    extract(inAlignment, outAlignment, childNames[i]);
  }
}
<commit_msg>fix halExtract<commit_after>/*
 * Copyright (C) 2012 by Glenn Hickey (hickey@soe.ucsc.edu)
 *
 * Released under the MIT license, see LICENSE.txt
 */

#include <cstdlib>
#include <iostream>
#include "hal.h"

using namespace std;
using namespace hal;

static void getDimensions(AlignmentConstPtr outAlignment, const Genome* genome,
                          vector<Sequence::Info>& dimensions);

static void copyGenome(const Genome* inGenome, Genome* outGenome);

static void extractTree(const AlignmentConstPtr inAlignment,
                        AlignmentPtr outAlignment,
                        const string& rootName);

static void extract(const AlignmentConstPtr inAlignment,
                    AlignmentPtr outAlignment, const string& rootName);

static CLParserPtr initParser()
{
  CLParserPtr optionsParser = hdf5CLParserInstance(true);
  optionsParser->addArgument("inHalPath", "input hal file");
  optionsParser->addArgument("outHalPath", "output hal file");
  optionsParser->addOption("root", "root of subtree to extract", "\"\"");
  return optionsParser;
}

int main(int argc, char** argv)
{
  CLParserPtr optionsParser = initParser();

  string inHalPath;
  string outHalPath;
  string rootName;
  try
  {
    optionsParser->parseOptions(argc, argv);
    inHalPath = optionsParser->getArgument<string>("inHalPath");
    outHalPath = optionsParser->getArgument<string>("outHalPath");
    rootName = optionsParser->getOption<string>("root");
  }
  catch(exception& e)
  {
    cerr << e.what() << endl;
    optionsParser->printUsage(cerr);
    exit(1);
  }

  try
  {
    AlignmentConstPtr inAlignment = openHalAlignmentReadOnly(inHalPath,
                                                             optionsParser);
    if (inAlignment->getNumGenomes() == 0)
    {
      throw hal_exception("input hal alignmenet is empty");
    }

    AlignmentPtr outAlignment = hdf5AlignmentInstance();
    outAlignment->setOptionsFromParser(optionsParser);
    outAlignment->createNew(outHalPath);

    if (outAlignment->getNumGenomes() != 0)
    {
      throw hal_exception("output hal alignmenet cannot be initialized");
    }

    if (rootName == "\"\"" || inAlignment->getNumGenomes() == 0)
    {
      rootName = inAlignment->getRootName();
    }

    extractTree(inAlignment, outAlignment, rootName);
    extract(inAlignment, outAlignment, rootName);
  }
  catch(hal_exception& e)
  {
    cerr << "hal exception caught: " << e.what() << endl;
    return 1;
  }
  catch(exception& e)
  {
    cerr << "Exception caught: " << e.what() << endl;
    return 1;
  }

  return 0;
}

void getDimensions(AlignmentConstPtr outAlignment, const Genome* genome,
                   vector<Sequence::Info>& dimensions)
{
  assert(dimensions.size() == 0);
  SequenceIteratorConstPtr seqIt = genome->getSequenceIterator();
  SequenceIteratorConstPtr seqEndIt = genome->getSequenceEndIterator();

  bool root = outAlignment->getParentName(genome->getName()).empty();
  bool leaf = outAlignment->getChildNames(genome->getName()).empty();

  for (; seqIt != seqEndIt; seqIt->toNext())
  {
    const Sequence* sequence = seqIt->getSequence();
    Sequence::Info info(sequence->getName(),
                        sequence->getSequenceLength(),
                        root ? 0 : sequence->getNumTopSegments(),
                        leaf ? 0 : sequence->getNumBottomSegments());
    dimensions.push_back(info);
  }
}

void copyGenome(const Genome* inGenome, Genome* outGenome)
{
  DNAIteratorConstPtr inDna = inGenome->getDNAIterator();
  DNAIteratorPtr outDna = outGenome->getDNAIterator();
  hal_size_t n = inGenome->getSequenceLength();
  assert(n == outGenome->getSequenceLength());
  for (; (hal_size_t)inDna->getArrayIndex() < n; inDna->toRight(),
          outDna->toRight())
  {
    outDna->setChar(inDna->getChar());
  }

  TopSegmentIteratorConstPtr inTop = inGenome->getTopSegmentIterator();
  TopSegmentIteratorPtr outTop = outGenome->getTopSegmentIterator();
  n = outGenome->getNumTopSegments();
  assert(n == 0 || n == inGenome->getNumTopSegments());
  for (; (hal_size_t)inTop->getArrayIndex() < n; inTop->toRight(),
          outTop->toRight())
  {
    outTop->setCoordinates(inTop->getStartPosition(), inTop->getLength());
    outTop->setParentIndex(inTop->getParentIndex());
    outTop->setParentReversed(inTop->getParentReversed());
    outTop->setBottomParseIndex(inTop->getBottomParseIndex());
    outTop->setNextParalogyIndex(inTop->getNextParalogyIndex());
  }

  BottomSegmentIteratorConstPtr inBot = inGenome->getBottomSegmentIterator();
  BottomSegmentIteratorPtr outBot = outGenome->getBottomSegmentIterator();
  n = outGenome->getNumBottomSegments();
  assert(n == 0 || n == inGenome->getNumBottomSegments());
  hal_size_t nc = inGenome->getNumChildren();
  assert(nc == outGenome->getNumChildren());
  for (; (hal_size_t)inBot->getArrayIndex() < n; inBot->toRight(),
          outBot->toRight())
  {
    outBot->setCoordinates(inBot->getStartPosition(), inBot->getLength());
    for (hal_size_t child = 0; child < nc; ++child)
    {
      outBot->setChildIndex(child, inBot->getChildIndex(child));
      outBot->setChildReversed(child, inBot->getChildReversed(child));
    }
    if (outGenome->getAlignment()->getRootName() == outGenome->getName())
    {
      outBot->setTopParseIndex(NULL_INDEX);
    }
    else
    {
      outBot->setTopParseIndex(inBot->getTopParseIndex());
    }
  }

  const map<string, string>& meta = inGenome->getMetaData()->getMap();
  map<string, string>::const_iterator i = meta.begin();
  for (; i != meta.end(); ++i)
  {
    outGenome->getMetaData()->set(i->first, i->second);
  }
}

static void extractTree(const AlignmentConstPtr inAlignment,
                        AlignmentPtr outAlignment,
                        const string& rootName)
{
  const Genome* genome = inAlignment->openGenome(rootName);
  if (genome == NULL)
  {
    throw hal_exception(string("Genome not found: ") + rootName);
  }
  Genome* newGenome = NULL;
  if (outAlignment->getNumGenomes() == 0 || genome->getParent() == NULL)
  {
    newGenome = outAlignment->addRootGenome(rootName);
  }
  else
  {
    const Genome* parent = genome->getParent();
    assert(parent != NULL);
    newGenome = outAlignment->addLeafGenome(rootName, parent->getName(),
                                            inAlignment->getBranchLength(
                                              parent->getName(), rootName));
  }
  assert(newGenome != NULL);

  inAlignment->closeGenome(genome);
  outAlignment->closeGenome(newGenome);

  vector<string> childNames = inAlignment->getChildNames(rootName);
  for (size_t i = 0; i < childNames.size(); ++i)
  {
    extractTree(inAlignment, outAlignment, childNames[i]);
  }
}

void extract(const AlignmentConstPtr inAlignment,
             AlignmentPtr outAlignment, const string& rootName)
{
  const Genome* genome = inAlignment->openGenome(rootName);
  Genome* newGenome = outAlignment->openGenome(rootName);
  assert(newGenome != NULL);

  vector<Sequence::Info> dimensions;
  getDimensions(outAlignment, genome, dimensions);
  newGenome->setDimensions(dimensions);

  cout << "Extracting " << genome->getName() << endl;
  copyGenome(genome, newGenome);

  inAlignment->closeGenome(genome);
  outAlignment->closeGenome(newGenome);

  vector<string> childNames = inAlignment->getChildNames(rootName);
  for (size_t i = 0; i < childNames.size(); ++i)
  {
    extract(inAlignment, outAlignment, childNames[i]);
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

// This test only works with the GPU backend.

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkShader.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTileMode.h"
#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkGradientShader.h"
#include "include/gpu/GrConfig.h"
#include "include/gpu/GrContext.h"
#include "include/private/GrTypesPriv.h"
#include "include/private/SkColorData.h"
#include "src/core/SkMatrixProvider.h"
#include "src/gpu/GrColor.h"
#include "src/gpu/GrFragmentProcessor.h"
#include "src/gpu/GrPaint.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrRenderTargetContextPriv.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/generated/GrConstColorProcessor.h"
#include "src/gpu/ops/GrDrawOp.h"
#include "src/gpu/ops/GrFillRectOp.h"
#include "tools/ToolUtils.h"

#include <utility>

namespace skiagm {
/**
 * This GM directly exercises GrConstColorProcessor.
 */
class ConstColorProcessor : public GpuGM {
public:
    ConstColorProcessor() {
        this->setBGColor(0xFFDDDDDD);
    }

protected:
    SkString onShortName() override {
        return SkString("const_color_processor");
    }

    SkISize onISize() override {
        return SkISize::Make(kWidth, kHeight);
    }

    void onOnceBeforeDraw() override {
        SkColor colors[] = { 0xFFFF0000, 0x2000FF00, 0xFF0000FF};
        SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(kRectSize, kRectSize) };
        fShader = SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
                                               SkTileMode::kClamp);
    }

    void onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
                SkCanvas* canvas) override {
        constexpr GrColor kColors[] = {
            0xFFFFFFFF,
            0xFFFF00FF,
            0x80000000,
            0x00000000,
        };

        constexpr SkColor kPaintColors[] = {
            0xFFFFFFFF,
            0xFFFF0000,
            0x80FF0000,
            0x00000000,
        };

        const char* kModeStrs[] {
            "kIgnore",
            "kModulateRGBA",
            "kModulateA",
        };
        static_assert(SK_ARRAY_COUNT(kModeStrs) == GrConstColorProcessor::kInputModeCnt);

        SkScalar y = kPad;
        SkScalar x = kPad;
        SkScalar maxW = 0;
        for (size_t paintType = 0; paintType < SK_ARRAY_COUNT(kPaintColors) + 1; ++paintType) {
            for (size_t procColor = 0; procColor < SK_ARRAY_COUNT(kColors); ++procColor) {
                for (int m = 0; m < GrConstColorProcessor::kInputModeCnt; ++m) {
                    // translate by x,y for the canvas draws and the test target draws.
                    canvas->save();
                    canvas->translate(x, y);
                    const SkMatrix viewMatrix = SkMatrix::Translate(x, y);
                    SkSimpleMatrixProvider matrixProvider(viewMatrix);

                    // rect to draw
                    SkRect renderRect = SkRect::MakeXYWH(0, 0, kRectSize, kRectSize);

                    GrPaint grPaint;
                    SkPaint skPaint;
                    if (paintType >= SK_ARRAY_COUNT(kPaintColors)) {
                        skPaint.setShader(fShader);
                    } else {
                        skPaint.setColor(kPaintColors[paintType]);
                    }
                    SkAssertResult(SkPaintToGrPaint(context, renderTargetContext->colorInfo(),
                                                    skPaint, matrixProvider, &grPaint));

                    GrConstColorProcessor::InputMode mode = (GrConstColorProcessor::InputMode) m;
                    SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(kColors[procColor]);
                    auto fp = GrConstColorProcessor::Make(/*inputFP=*/nullptr, color, mode);

                    grPaint.addColorFragmentProcessor(std::move(fp));
                    renderTargetContext->priv().testingOnly_addDrawOp(
                            GrFillRectOp::MakeNonAARect(context, std::move(grPaint),
                                                        viewMatrix, renderRect));

                    // Draw labels for the input to the processor and the processor to the right of
                    // the test rect. The input label appears above the processor label.
                    SkFont labelFont;
                    labelFont.setTypeface(ToolUtils::create_portable_typeface());
                    labelFont.setEdging(SkFont::Edging::kAntiAlias);
                    labelFont.setSize(10.f);
                    SkPaint labelPaint;
                    labelPaint.setAntiAlias(true);
                    SkString inputLabel;
                    inputLabel.set("Input: ");
                    if (paintType >= SK_ARRAY_COUNT(kPaintColors)) {
                        inputLabel.append("gradient");
                    } else {
                        inputLabel.appendf("0x%08x", kPaintColors[paintType]);
                    }
                    SkString procLabel;
                    procLabel.printf("Proc: [0x%08x, %s]", kColors[procColor], kModeStrs[m]);

                    SkRect inputLabelBounds;
                    // get the bounds of the text in order to position it
                    labelFont.measureText(inputLabel.c_str(), inputLabel.size(),
                                          SkTextEncoding::kUTF8, &inputLabelBounds);
                    canvas->drawString(inputLabel, renderRect.fRight + kPad, -inputLabelBounds.fTop,
                                       labelFont, labelPaint);
                    // update the bounds to reflect the offset we used to draw it.
                    inputLabelBounds.offset(renderRect.fRight + kPad, -inputLabelBounds.fTop);

                    SkRect procLabelBounds;
                    labelFont.measureText(procLabel.c_str(), procLabel.size(),
                                          SkTextEncoding::kUTF8, &procLabelBounds);
                    canvas->drawString(procLabel, renderRect.fRight + kPad,
                                       inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop,
                                       labelFont, labelPaint);
                    procLabelBounds.offset(renderRect.fRight + kPad,
                                           inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop);

                    labelPaint.setStrokeWidth(0);
                    labelPaint.setStyle(SkPaint::kStroke_Style);
                    canvas->drawRect(renderRect, labelPaint);

                    canvas->restore();

                    // update x and y for the next test case.
                    SkScalar height = renderRect.height();
                    SkScalar width = std::max(inputLabelBounds.fRight, procLabelBounds.fRight);
                    maxW = std::max(maxW, width);
                    y += height + kPad;
                    if (y + height > kHeight) {
                        y = kPad;
                        x += maxW + kPad;
                        maxW = 0;
                    }
                }
            }
        }
    }

private:
    // Use this as a way of generating and input FP
    sk_sp<SkShader> fShader;

    static constexpr SkScalar       kPad = 10.f;
    static constexpr SkScalar       kRectSize = 20.f;
    static constexpr int            kWidth  = 820;
    static constexpr int            kHeight = 500;

    typedef GM INHERITED;
};

DEF_GM(return new ConstColorProcessor;)
}
<commit_msg>Redesign const-color unit test to leverage sk_gpu_test::test_ops.<commit_after>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

// This test only works with the GPU backend.

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkShader.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTileMode.h"
#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkGradientShader.h"
#include "include/gpu/GrConfig.h"
#include "include/gpu/GrContext.h"
#include "include/private/GrTypesPriv.h"
#include "include/private/SkColorData.h"
#include "src/core/SkMatrixProvider.h"
#include "src/gpu/GrColor.h"
#include "src/gpu/GrFragmentProcessor.h"
#include "src/gpu/GrPaint.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrRenderTargetContextPriv.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/generated/GrConstColorProcessor.h"
#include "src/gpu/ops/GrDrawOp.h"
#include "src/gpu/ops/GrFillRectOp.h"
#include "tools/ToolUtils.h"
#include "tools/gpu/TestOps.h"

#include <utility>

namespace skiagm {
/**
 * This GM directly exercises GrConstColorProcessor.
 */
class ConstColorProcessor : public GpuGM {
public:
    ConstColorProcessor() {
        this->setBGColor(0xFFDDDDDD);
    }

protected:
    SkString onShortName() override {
        return SkString("const_color_processor");
    }

    SkISize onISize() override {
        return SkISize::Make(kWidth, kHeight);
    }

    void onOnceBeforeDraw() override {
        SkColor colors[] = { 0xFFFF0000, 0x2000FF00, 0xFF0000FF};
        SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(kRectSize, kRectSize) };
        fShader = SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
                                               SkTileMode::kClamp);
    }

    void onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
                SkCanvas* canvas) override {
        constexpr GrColor kColors[] = {
            0xFFFFFFFF,
            0xFFFF00FF,
            0x80000000,
            0x00000000,
        };

        constexpr GrColor kPaintColors[] = {
            0xFFFFFFFF,
            0xFF0000FF,
            0x80000080,
            0x00000000,
        };

        const char* kModeStrs[] {
            "kIgnore",
            "kModulateRGBA",
            "kModulateA",
        };
        static_assert(SK_ARRAY_COUNT(kModeStrs) == GrConstColorProcessor::kInputModeCnt);

        SkScalar y = kPad;
        SkScalar x = kPad;
        SkScalar maxW = 0;
        for (size_t paintType = 0; paintType < SK_ARRAY_COUNT(kPaintColors) + 1; ++paintType) {
            for (size_t procColor = 0; procColor < SK_ARRAY_COUNT(kColors); ++procColor) {
                for (int m = 0; m < GrConstColorProcessor::kInputModeCnt; ++m) {
                    // translate by x,y for the canvas draws and the test target draws.
                    canvas->save();
                    canvas->translate(x, y);

                    // rect to draw
                    SkRect renderRect = SkRect::MakeXYWH(0, 0, kRectSize, kRectSize);

                    // Create a base-layer FP for the const color processor to draw on top of.
                    std::unique_ptr<GrFragmentProcessor> baseFP;
                    if (paintType >= SK_ARRAY_COUNT(kPaintColors)) {
                        GrColorInfo colorInfo;
                        GrFPArgs args(context, SkSimpleMatrixProvider(SkMatrix::I()),
                                      kHigh_SkFilterQuality, &colorInfo);
                        baseFP = as_SB(fShader)->asFragmentProcessor(args);
                    } else {
                        baseFP = GrConstColorProcessor::Make(
                                /*inputFP=*/nullptr,
                                SkPMColor4f::FromBytes_RGBA(kPaintColors[paintType]),
                                GrConstColorProcessor::InputMode::kIgnore);
                    }

                    // Layer a const-color FP on top of the base layer, using various modes/colors.
                    auto constColorFP = GrConstColorProcessor::Make(
                            std::move(baseFP), SkPMColor4f::FromBytes_RGBA(kColors[procColor]),
                            GrConstColorProcessor::InputMode(m));

                    // Render the FP tree.
                    if (auto op = sk_gpu_test::test_ops::MakeRect(context,
                                                                  std::move(constColorFP),
                                                                  renderRect.makeOffset(x, y),
                                                                  renderRect,
                                                                  SkMatrix::I())) {
                        renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
                    }

                    // Draw labels for the input to the processor and the processor to the right of
                    // the test rect. The input label appears above the processor label.
                    SkFont labelFont;
                    labelFont.setTypeface(ToolUtils::create_portable_typeface());
                    labelFont.setEdging(SkFont::Edging::kAntiAlias);
                    labelFont.setSize(10.f);
                    SkPaint labelPaint;
                    labelPaint.setAntiAlias(true);
                    SkString inputLabel;
                    inputLabel.set("Input: ");
                    if (paintType >= SK_ARRAY_COUNT(kPaintColors)) {
                        inputLabel.append("gradient");
                    } else {
                        inputLabel.appendf("0x%08x", kPaintColors[paintType]);
                    }
                    SkString procLabel;
                    procLabel.printf("Proc: [0x%08x, %s]", kColors[procColor], kModeStrs[m]);

                    SkRect inputLabelBounds;
                    // get the bounds of the text in order to position it
                    labelFont.measureText(inputLabel.c_str(), inputLabel.size(),
                                          SkTextEncoding::kUTF8, &inputLabelBounds);
                    canvas->drawString(inputLabel, renderRect.fRight + kPad, -inputLabelBounds.fTop,
                                       labelFont, labelPaint);
                    // update the bounds to reflect the offset we used to draw it.
                    inputLabelBounds.offset(renderRect.fRight + kPad, -inputLabelBounds.fTop);

                    SkRect procLabelBounds;
                    labelFont.measureText(procLabel.c_str(), procLabel.size(),
                                          SkTextEncoding::kUTF8, &procLabelBounds);
                    canvas->drawString(procLabel, renderRect.fRight + kPad,
                                       inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop,
                                       labelFont, labelPaint);
                    procLabelBounds.offset(renderRect.fRight + kPad,
                                           inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop);

                    labelPaint.setStrokeWidth(0);
                    labelPaint.setStyle(SkPaint::kStroke_Style);
                    canvas->drawRect(renderRect, labelPaint);

                    canvas->restore();

                    // update x and y for the next test case.
                    SkScalar height = renderRect.height();
                    SkScalar width = std::max(inputLabelBounds.fRight, procLabelBounds.fRight);
                    maxW = std::max(maxW, width);
                    y += height + kPad;
                    if (y + height > kHeight) {
                        y = kPad;
                        x += maxW + kPad;
                        maxW = 0;
                    }
                }
            }
        }
    }

private:
    // Use this as a way of generating an input FP
    sk_sp<SkShader> fShader;

    static constexpr SkScalar       kPad = 10.f;
    static constexpr SkScalar       kRectSize = 20.f;
    static constexpr int            kWidth  = 820;
    static constexpr int            kHeight = 500;

    typedef GM INHERITED;
};

DEF_GM(return new ConstColorProcessor;)
}
<|endoftext|>


<commit_before>#include <istream>
#include <utility>
#include <sstream>
#include <cstdlib>

#include "number.hh"

using namespace std;

// number class definitions.
template <>
Number::complex_type Number::coerce() const{
  switch(type_){
  case Type::complex:
    return z_;
  case Type::real:
    return complex_type{f_};
  case Type::integer:
    return complex_type{static_cast<real_type>(i_)};
  case Type::uninitialized:
  default:
    UNEXP_CONVERSION("complex");
  }
}

template <>
Number::real_type Number::coerce() const{
  switch(type_){
  case Type::real:
    return f_;
  case Type::integer:
    return static_cast<real_type>(i_);
  case Type::complex:
  case Type::uninitialized:
  default:
    UNEXP_CONVERSION("real");
  }
}

template <>
Number::integer_type Number::coerce() const{
  switch(type_){
  case Type::integer:
    return i_;
  case Type::complex:
  case Type::real:
  case Type::uninitialized:
  default:
    UNEXP_CONVERSION("integer");
  }
}

// number parsers
namespace {

enum class Exactness{
  exact, inexact, unspecified
    };

pair<int, Exactness> parse_number_prefix(std::istream& i){
  static const auto to_radix = [](char c){
    return (c == 'b') ? 2
    : (c == 'o') ? 8
    : (c == 'd') ? 10
    : (c == 'x') ? 16
    : -1;
  };

  static const auto to_exactness = [](char c){
    return (c == 'i') ? Exactness::inexact
    : (c == 'e') ? Exactness::exact
    : Exactness::unspecified;
  };

  int r = 10;
  Exactness e = Exactness::unspecified;
  bool r_appeared = false, e_appeared = false;

  for(int loop = 0; loop < 2; ++loop){
    if(i.peek() != '#')
      return {r, e};
    i.ignore(1);

    switch(auto c = i.get()){
    case 'i': case 'e':
      if(e_appeared) goto error;
      e = to_exactness(c);
      e_appeared = true;
      break;
    case 'b': case 'o': case 'd': case 'x':
      if(r_appeared) goto error;
      r = to_radix(c);
      r_appeared = true;
      break;
    default:
      goto error;
    }
  }

  return {r, e};

 error:
  return {-1, Exactness::unspecified};
}

inline
bool is_number_char(int radix, char c){
  switch(radix){
  case 16:
    return isxdigit(c);

  case 10:
    return isdigit(c);

  case 8:
    switch(c){
    case '0': case '1':
    case '2': case '3': case '4':
    case '5': case '6': case '7':
      return true;
    default:
      return false;
    }

  case 2:
    switch(c){
    case '0': case '1':
      return true;
    default:
      return false;
    }

  default:
    UNEXP_DEFAULT();
  }
}

int eat_sharp(istream& i, string& o){
  int sharps = 0;

  while(i.peek() == '#'){
    i.ignore(1);
    o.push_back('0');
    ++sharps;
  }

  return sharps;
}

typedef pair<Number, Exactness> ParserRet;

#define PARSE_ERROR_VALUE (ParserRet{{}, Exactness::unspecified})

ParserRet parse_unsigned(int radix, std::istream& i, string& s){
  while(is_number_char(radix, i.peek()))
    s.push_back(i.get());

  if(s.empty()){
    goto error;
  }else{
    Exactness e;

    if(eat_sharp(i, s) > 0){
      e = Exactness::inexact;
    }else{
      e = Exactness::exact;
    }

    errno = 0;
    long l = strtol(s.c_str(), nullptr, radix);
    if(errno) goto error;

    return {Number{l}, e};
  }

 error:
  return PARSE_ERROR_VALUE;
}

inline
bool check_decimal_suffix(char c){
  switch(c){
  case 'e': case 's': case 'f': case 'd': case 'l':
    return true;
  default:
    return false;
  }
}

ParserRet parse_decimal(std::istream& i, string& s){
  bool dot_start = false;
  int sharps_before_dot = 0;

  if(s.empty()){
    if(i.peek() == '.'){
      dot_start = true;
    }else{
      goto error;
    }
  }else{
    sharps_before_dot = eat_sharp(i, s);
  }

  if(i.peek() != '.'){
    goto end; // 1. no frac part
  }
  s.push_back(i.get());

  if(sharps_before_dot > 0){
    eat_sharp(i, s);
    goto end; // 4. only sharps after dot
  }

  if(dot_start && !is_number_char(10, i.peek()))
    goto error; // 2. dot start should have digits

  while(is_number_char(10, i.peek())){
    s.push_back(i.get());
  }

  eat_sharp(i, s);

 end:
  if(check_decimal_suffix(i.peek())){
    i.ignore(1);
    s.push_back('e');

    switch(i.peek()){
    case '+': case '-':
      s.push_back(i.get());
    }

    if(!is_number_char(10, i.peek())){
      goto error; // no number on exp. part
    }

    while(is_number_char(10, i.peek())){
      s.push_back(i.get());
    }
  }

  return {Number{strtod(s.c_str(), nullptr)},
      Exactness::inexact};

 error:
  return PARSE_ERROR_VALUE;
}

ParserRet parse_real_number(int radix, std::istream& i){
  int sign = 1;

  switch(i.peek()){
  case '+':
    sign = 1;
    i.ignore(1);
    break;
  case '-':
    sign = -1;
    i.ignore(1);
    break;
  default:
    sign = 1;
    break;
  }

  string s;

  auto u1 = parse_unsigned(radix, i, s);
  if(!get<0>(u1)){
    if(i.peek() == '.'){
      goto decimal_float_check;
    }else{
      goto error;
    }
  }

  if(check_decimal_suffix(i.peek()) || i.peek() == '.'){
  decimal_float_check:
    if(radix == 10){
      auto n = parse_decimal(i, s);

      if(get<0>(n).type() == Number::Type::real){
        return {Number{get<0>(n).coerce<double>() * sign},
            Exactness::inexact};
      }
    }
    goto error;
  }

  if(i.peek() != '/'){ // integer?
    // FIXME: inexact or super-big integer can be fall into float.
    return {Number(sign * get<0>(u1).coerce<long>()), get<1>(u1)};
  }else{
    // rational
    string s2;
    auto u2 = parse_unsigned(radix, i, s2);
    if(!get<0>(u2))
      goto error;
    return {Number(sign * get<0>(u1).coerce<double>() / get<0>(u2).coerce<double>()),
        Exactness::inexact};
  }

 error:
  return PARSE_ERROR_VALUE;
}

ParserRet parse_complex(int radix, std::istream& i){
  const auto first_char = i.peek();

  // has real part
  auto real = parse_real_number(radix, i);
  if(!get<0>(real))
    goto error;

  switch(auto c = i.peek()){
  case '@': {// polar literal
    i.ignore(1);
    auto deg = parse_real_number(radix, i);

    if(!get<0>(deg))
      goto error;

    return {Number{polar(get<0>(real).coerce<double>(), get<0>(deg).coerce<double>())},
        Exactness::inexact};
  }
  case '+': case '-': {
    i.ignore(1);
    const int sign = (c == '+') ? 1 : -1;

    if(i.peek() == 'i'){
      i.ignore(1);
      return {Number{get<0>(real).coerce<double>(), static_cast<double>(sign)},
          Exactness::inexact};
    }

    auto imag = parse_real_number(radix, i);
    if(!get<0>(imag) || i.get() != 'i')
      goto error;

    return {Number{get<0>(real).coerce<double>(), get<0>(imag).coerce<double>() * sign},
        Exactness::inexact};
  }
  case 'i':
    i.ignore(1);
    if(first_char == '+' || first_char == '-'){
      return {Number{0, get<0>(real).coerce<double>()},
          Exactness::inexact};
    }else{
      goto error;
    }
  default:
    return real;
  }

 error:
  return PARSE_ERROR_VALUE;
}

} // namespace

Number parse_number(std::istream& i){
  const auto prefix_info = parse_number_prefix(i);

  ParserRet r;

  switch(auto radix = get<0>(prefix_info)){
  case 2: case 8: case 10: case 16:
    r = parse_complex(radix, i);
    break;
  default:
    goto error;
  }

  if(!get<0>(r)) goto error;

  // TODO: check inexact integer, and warn.

  switch(auto e = get<1>(prefix_info)){
  case Exactness::exact:
    return (get<1>(r) == e) ? get<0>(r) : to_exact(get<0>(r));
  case Exactness::inexact:
    return (get<1>(r) == e) ? get<0>(r) : to_inexact(get<0>(r));
  case Exactness::unspecified:
    return get<0>(r);
  default:
    goto error;
  }

 error:
  return {};
}

bool eql(const Number& n, const Number& m){
  if(n.type() != m.type()) return false;

  switch(n.type()){
  case Number::Type::uninitialized:
    return false;
  case Number::Type::complex:
    return n.get<Number::complex_type>() == m.get<Number::complex_type>();
  case Number::Type::real:
    return n.get<Number::real_type>() == m.get<Number::real_type>();
  case Number::Type::integer:
    return n.get<Number::integer_type>() == m.get<Number::integer_type>();
  default:
    UNEXP_DEFAULT();
  }
}

void print(FILE* f, const Number& n){
  switch(n.type()){
  case Number::Type::uninitialized:
    fprintf(f, "(uninitialied number)");
    break;
  case Number::Type::complex: {
    auto&& z = n.get<Number::complex_type>();
    fprintf(f, "%g+%gi", z.real(), z.imag());
  }
    break;
  case Number::Type::real:
    fprintf(f, "%g", n.get<Number::real_type>());
    break;
  case Number::Type::integer:
    fprintf(f, "%ld", n.get<Number::integer_type>());
    break;
  default:
    UNEXP_DEFAULT();
  }
}

Number to_exact(const Number& n){
  switch(n.type()){
  case Number::Type::complex:
    return {}; // not supported
  case Number::Type::real:
    return {}; // not supported
  case Number::Type::integer:
    return n;
  case Number::Type::uninitialized:
  default:
    return {};
  }
}

Number to_inexact(const Number& n){
  switch(n.type()){
  case Number::Type::complex:
    return n;
  case Number::Type::real:
    return n;
  case Number::Type::integer:
    return Number{static_cast<Number::real_type>
        (n.get<Number::integer_type>())};
  case Number::Type::uninitialized:
  default:
    return {};
  }
}

const char* stringify(Number::Type t){
  switch(t){
  case Number::Type::uninitialized:
    return "uninitialized";
  case Number::Type::complex:
    return "complex";
  case Number::Type::real:
    return "real";
  case Number::Type::integer:
    return "integer";
  default:
    return "(unknown number type)";
  }
}

void describe(FILE* f, Number::Type t){
  fputs(stringify(t), f);
}

void describe(FILE* f, const Number& n){
  const auto t = n.type();

  fprintf(f, "Number: %s(", stringify(t));
  print(f, n);
  fputc(')', f);
}

<commit_msg>cleanup number<commit_after>#include <istream>
#include <utility>
#include <sstream>
#include <cstdlib>

#include "number.hh"

using namespace std;

// number class definitions.
template <>
Number::complex_type Number::coerce() const{
  switch(type_){
  case Type::complex:
    return z_;
  case Type::real:
    return complex_type{f_};
  case Type::integer:
    return complex_type{static_cast<real_type>(i_)};
  case Type::uninitialized:
  default:
    UNEXP_CONVERSION("complex");
  }
}

template <>
Number::real_type Number::coerce() const{
  switch(type_){
  case Type::real:
    return f_;
  case Type::integer:
    return static_cast<real_type>(i_);
  case Type::complex:
  case Type::uninitialized:
  default:
    UNEXP_CONVERSION("real");
  }
}

template <>
Number::integer_type Number::coerce() const{
  switch(type_){
  case Type::integer:
    return i_;
  case Type::complex:
  case Type::real:
  case Type::uninitialized:
  default:
    UNEXP_CONVERSION("integer");
  }
}

// number parsers
namespace {

enum class Exactness{
  exact, inexact, unspecified
    };

#define PREFIX_ERROR_VALUE {0, Exactness::unspecified}

pair<int, Exactness> parse_number_prefix(std::istream& i){
  static const auto to_radix = [](char c){
    return (c == 'b') ? 2
    : (c == 'o') ? 8
    : (c == 'd') ? 10
    : (c == 'x') ? 16
    : -1;
  };

  static const auto to_exactness = [](char c){
    return (c == 'i') ? Exactness::inexact
    : (c == 'e') ? Exactness::exact
    : Exactness::unspecified;
  };

  int r = 10;
  Exactness e = Exactness::unspecified;
  bool r_appeared = false, e_appeared = false;

  for(int loop = 0; loop < 2; ++loop){
    if(i.get() != '#'){
      i.unget();
      return {r, e};
    }

    switch(auto c = i.get()){
    case 'i': case 'e':
      if(e_appeared) return PREFIX_ERROR_VALUE;
      e = to_exactness(c);
      e_appeared = true;
      break;
    case 'b': case 'o': case 'd': case 'x':
      if(r_appeared) return PREFIX_ERROR_VALUE;
      r = to_radix(c);
      r_appeared = true;
      break;
    default:
      return PREFIX_ERROR_VALUE;
    }
  }

  return {r, e};
}

inline
bool is_number_char(int radix, char c){
  switch(radix){
  case 16:
    return isxdigit(c);

  case 10:
    return isdigit(c);

  case 8:
    switch(c){
    case '0': case '1':
    case '2': case '3': case '4':
    case '5': case '6': case '7':
      return true;
    default:
      return false;
    }

  case 2:
    switch(c){
    case '0': case '1':
      return true;
    default:
      return false;
    }

  default:
    UNEXP_DEFAULT();
  }
}

int eat_sharp(istream& i, string& o){
  int sharps = 0;

  while(i.get() == '#'){
    o.push_back('0');
    ++sharps;
  }
  i.unget();

  return sharps;
}

typedef pair<Number, Exactness> ParserRet;

#define PARSE_ERROR_VALUE (ParserRet{{}, Exactness::unspecified})

ParserRet parse_unsigned(int radix, std::istream& i, string& s){
  char c;

  while(is_number_char(radix, c = i.get()))
    s.push_back(c);
  i.unget();

  if(s.empty()){
    return PARSE_ERROR_VALUE;
  }

  Exactness e;

  if(eat_sharp(i, s) > 0){
    e = Exactness::inexact;
  }else{
    e = Exactness::exact;
  }

  errno = 0;
  long l = strtol(s.c_str(), nullptr, radix);
  if(errno) return PARSE_ERROR_VALUE;

  return {Number{l}, e};
}

inline
bool check_decimal_suffix(char c){
  switch(c){
  case 'e': case 's': case 'f': case 'd': case 'l':
    return true;
  default:
    return false;
  }
}

ParserRet parse_decimal(std::istream& i, string& s){
  bool dot_start = false;
  int sharps_before_dot = 0;

  if(s.empty()){
    if(i.get() == '.'){
      i.unget();
      dot_start = true;
    }else{
      return PARSE_ERROR_VALUE;
    }
  }else{
    sharps_before_dot = eat_sharp(i, s);
  }

  if(i.get() != '.'){
    goto end; // 1. no frac part
  }
  s.push_back('.');

  if(sharps_before_dot > 0){
    eat_sharp(i, s);
    goto end; // 4. only sharps after dot
  }

  {
    bool digits_after_dot = false;
    char c;

    while(is_number_char(10, c = i.get())){
      digits_after_dot = true;
      s.push_back(c);
    }
    i.unget();

    if(dot_start && !digits_after_dot)
      return PARSE_ERROR_VALUE; // 2. dot start should have digits

    eat_sharp(i, s);
  }

 end:
  if(check_decimal_suffix(i.get())){
    s.push_back('e');

    switch(char c = i.get()){
    case '+': case '-':
      s.push_back(c); break;
    default:
      i.unget(); break;
    }

    {
      bool exp_digits = false;
      char c;

      while(is_number_char(10, c = i.get())){
        exp_digits = true;
        s.push_back(c);
      }
      i.unget();

      if(!exp_digits)
        return PARSE_ERROR_VALUE; // no number on exp. part
    }
  }else{
    i.unget();
  }

  return {Number{strtod(s.c_str(), nullptr)},
      Exactness::inexact};
}

ParserRet parse_real_number(int radix, std::istream& i){
  int sign = 1;

  switch(i.get()){
  case '+':
    sign = 1;
    break;
  case '-':
    sign = -1;
    break;
  default:
    sign = 1;
    i.unget();
    break;
  }

  string s;

  auto u1 = parse_unsigned(radix, i, s);
  if(!get<0>(u1)){
    if(i.peek() == '.'){
      goto decimal_float_check;
    }else{
      return PARSE_ERROR_VALUE;
    }
  }

  if(check_decimal_suffix(i.peek()) || i.peek() == '.'){
  decimal_float_check:
    if(radix == 10){
      auto n = parse_decimal(i, s);

      if(get<0>(n).type() == Number::Type::real){
        return {Number{get<0>(n).coerce<double>() * sign},
            Exactness::inexact};
      }
    }
    return PARSE_ERROR_VALUE;
  }

  if(i.get() != '/'){ // integer?
    i.unget();
    // FIXME: inexact or super-big integer can be fall into float.
    return {Number(sign * get<0>(u1).coerce<long>()), get<1>(u1)};
  }else{
    // rational
    string s2;
    auto u2 = parse_unsigned(radix, i, s2);
    if(!get<0>(u2))
      return PARSE_ERROR_VALUE;

    return {Number(sign * get<0>(u1).coerce<double>() / get<0>(u2).coerce<double>()),
        Exactness::inexact};
  }
}

ParserRet parse_complex(int radix, std::istream& i){
  const auto first_char = i.peek();

  // has real part
  auto real = parse_real_number(radix, i);
  if(!get<0>(real))
    return PARSE_ERROR_VALUE;

  switch(auto c = i.get()){
  case '@': {// polar literal
    auto deg = parse_real_number(radix, i);

    if(!get<0>(deg))
      return PARSE_ERROR_VALUE;

    return {Number{polar(get<0>(real).coerce<double>(), get<0>(deg).coerce<double>())},
        Exactness::inexact};
  }
  case '+': case '-': {
    const int sign = (c == '+') ? 1 : -1;

    if(i.get() == 'i'){
      return {Number{get<0>(real).coerce<double>(), static_cast<double>(sign)},
          Exactness::inexact};
    }
    i.unget();

    auto imag = parse_real_number(radix, i);
    if(!get<0>(imag) || i.get() != 'i')
      return PARSE_ERROR_VALUE;

    return {Number{get<0>(real).coerce<double>(), get<0>(imag).coerce<double>() * sign},
        Exactness::inexact};
  }
  case 'i':
    if(first_char == '+' || first_char == '-'){
      return {Number{0, get<0>(real).coerce<double>()},
          Exactness::inexact};
    }else{
      return PARSE_ERROR_VALUE;
    }
  default:
    i.unget();
    return real;
  }
}

} // namespace

Number parse_number(std::istream& i){
  const auto prefix_info = parse_number_prefix(i);

  ParserRet r;

  switch(auto radix = get<0>(prefix_info)){
  case 2: case 8: case 10: case 16:
    r = parse_complex(radix, i);
    break;
  default:
    return {};
  }

  if(!get<0>(r)) return {};

  // TODO: check inexact integer, and warn.

  switch(auto e = get<1>(prefix_info)){
  case Exactness::exact:
    return (get<1>(r) == e) ? get<0>(r) : to_exact(get<0>(r));
  case Exactness::inexact:
    return (get<1>(r) == e) ? get<0>(r) : to_inexact(get<0>(r));
  case Exactness::unspecified:
    return get<0>(r);
  default:
    return {};
  }
}

bool eql(const Number& n, const Number& m){
  if(n.type() != m.type()) return false;

  switch(n.type()){
  case Number::Type::uninitialized:
    return false;
  case Number::Type::complex:
    return n.get<Number::complex_type>() == m.get<Number::complex_type>();
  case Number::Type::real:
    return n.get<Number::real_type>() == m.get<Number::real_type>();
  case Number::Type::integer:
    return n.get<Number::integer_type>() == m.get<Number::integer_type>();
  default:
    UNEXP_DEFAULT();
  }
}

void print(FILE* f, const Number& n){
  switch(n.type()){
  case Number::Type::uninitialized:
    fprintf(f, "(uninitialied number)");
    break;
  case Number::Type::complex: {
    auto&& z = n.get<Number::complex_type>();
    fprintf(f, "%g+%gi", z.real(), z.imag());
  }
    break;
  case Number::Type::real:
    fprintf(f, "%g", n.get<Number::real_type>());
    break;
  case Number::Type::integer:
    fprintf(f, "%ld", n.get<Number::integer_type>());
    break;
  default:
    UNEXP_DEFAULT();
  }
}

Number to_exact(const Number& n){
  switch(n.type()){
  case Number::Type::complex:
    return {}; // not supported
  case Number::Type::real:
    return {}; // not supported
  case Number::Type::integer:
    return n;
  case Number::Type::uninitialized:
  default:
    return {};
  }
}

Number to_inexact(const Number& n){
  switch(n.type()){
  case Number::Type::complex:
    return n;
  case Number::Type::real:
    return n;
  case Number::Type::integer:
    return Number{static_cast<Number::real_type>
        (n.get<Number::integer_type>())};
  case Number::Type::uninitialized:
  default:
    return {};
  }
}

const char* stringify(Number::Type t){
  switch(t){
  case Number::Type::uninitialized:
    return "uninitialized";
  case Number::Type::complex:
    return "complex";
  case Number::Type::real:
    return "real";
  case Number::Type::integer:
    return "integer";
  default:
    return "(unknown number type)";
  }
}

void describe(FILE* f, Number::Type t){
  fputs(stringify(t), f);
}

void describe(FILE* f, const Number& n){
  const auto t = n.type();

  fprintf(f, "Number: %s(", stringify(t));
  print(f, n);
  fputc(')', f);
}

<|endoftext|>