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

#include "ui/events/gestures/gesture_recognizer_impl.h"

#include <limits>

#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/time/time.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_switches.h"
#include "ui/events/event_utils.h"
#include "ui/events/gesture_detection/gesture_configuration.h"
#include "ui/events/gestures/gesture_types.h"

namespace ui {

namespace {

template <typename T>
void TransferConsumer(GestureConsumer* current_consumer,
                      GestureConsumer* new_consumer,
                      std::map<GestureConsumer*, T>* map) {
  if (map->count(current_consumer)) {
    (*map)[new_consumer] = (*map)[current_consumer];
    map->erase(current_consumer);
  }
}

bool RemoveConsumerFromMap(GestureConsumer* consumer,
                           GestureRecognizerImpl::TouchIdToConsumerMap* map) {
  bool consumer_removed = false;
  for (GestureRecognizerImpl::TouchIdToConsumerMap::iterator i = map->begin();
       i != map->end();) {
    if (i->second == consumer) {
      map->erase(i++);
      consumer_removed = true;
    } else {
      ++i;
    }
  }
  return consumer_removed;
}

void TransferTouchIdToConsumerMap(
    GestureConsumer* old_consumer,
    GestureConsumer* new_consumer,
    GestureRecognizerImpl::TouchIdToConsumerMap* map) {
  for (GestureRecognizerImpl::TouchIdToConsumerMap::iterator i = map->begin();
       i != map->end(); ++i) {
    if (i->second == old_consumer)
      i->second = new_consumer;
  }
}

GestureProviderAura* CreateGestureProvider(GestureProviderAuraClient* client) {
  return new GestureProviderAura(client);
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// GestureRecognizerImpl, public:

GestureRecognizerImpl::GestureRecognizerImpl() {
}

GestureRecognizerImpl::~GestureRecognizerImpl() {
  STLDeleteValues(&consumer_gesture_provider_);
}

// Checks if this finger is already down, if so, returns the current target.
// Otherwise, returns NULL.
GestureConsumer* GestureRecognizerImpl::GetTouchLockedTarget(
    const TouchEvent& event) {
  return touch_id_target_[event.touch_id()];
}

GestureConsumer* GestureRecognizerImpl::GetTargetForGestureEvent(
    const GestureEvent& event) {
  GestureConsumer* target = NULL;
  int touch_id = event.details().oldest_touch_id();
  target = touch_id_target_for_gestures_[touch_id];
  return target;
}

GestureConsumer* GestureRecognizerImpl::GetTargetForLocation(
    const gfx::PointF& location, int source_device_id) {
  const float max_distance =
      GestureConfiguration::GetInstance()
          ->max_separation_for_gesture_touches_in_pixels();

  gfx::PointF closest_point;
  int closest_touch_id = 0;
  double closest_distance_squared = std::numeric_limits<double>::infinity();

  std::map<GestureConsumer*, GestureProviderAura*>::iterator i;
  for (i = consumer_gesture_provider_.begin();
       i != consumer_gesture_provider_.end();
       ++i) {
    const MotionEventAura& pointer_state = i->second->pointer_state();
    for (size_t j = 0; j < pointer_state.GetPointerCount(); ++j) {
      if (source_device_id != pointer_state.GetSourceDeviceId(j))
        continue;
      gfx::PointF point(pointer_state.GetX(j), pointer_state.GetY(j));
      // Relative distance is all we need here, so LengthSquared() is
      // appropriate, and cheaper than Length().
      double distance_squared = (point - location).LengthSquared();
      if (distance_squared < closest_distance_squared) {
        closest_point = point;
        closest_touch_id = pointer_state.GetPointerId(j);
        closest_distance_squared = distance_squared;
      }
    }
  }

  if (closest_distance_squared < max_distance * max_distance)
    return touch_id_target_[closest_touch_id];
  return NULL;
}

void GestureRecognizerImpl::TransferEventsTo(GestureConsumer* current_consumer,
                                             GestureConsumer* new_consumer) {
  // Send cancel to all those save |new_consumer| and |current_consumer|.
  // Don't send a cancel to |current_consumer|, unless |new_consumer| is NULL.
  // Dispatching a touch-cancel event can end up altering |touch_id_target_|
  // (e.g. when the target of the event is destroyed, causing it to be removed
  // from |touch_id_target_| in |CleanupStateForConsumer()|). So create a list
  // of the touch-ids that need to be cancelled, and dispatch the cancel events
  // for them at the end.

  std::vector<GestureConsumer*> consumers;
  std::map<GestureConsumer*, GestureProviderAura*>::iterator i;
  for (i = consumer_gesture_provider_.begin();
       i != consumer_gesture_provider_.end();
       ++i) {
    if (i->first && i->first != new_consumer &&
        (i->first != current_consumer || new_consumer == NULL)) {
      consumers.push_back(i->first);
    }
  }
  for (std::vector<GestureConsumer*>::iterator iter = consumers.begin();
       iter != consumers.end();
       ++iter) {
    CancelActiveTouches(*iter);
  }
  // Transfer events from |current_consumer| to |new_consumer|.
  if (current_consumer && new_consumer) {
    TransferTouchIdToConsumerMap(current_consumer, new_consumer,
                                 &touch_id_target_);
    TransferTouchIdToConsumerMap(current_consumer, new_consumer,
                                 &touch_id_target_for_gestures_);
    TransferConsumer(
        current_consumer, new_consumer, &consumer_gesture_provider_);
  }
}

bool GestureRecognizerImpl::GetLastTouchPointForTarget(
    GestureConsumer* consumer,
    gfx::PointF* point) {
    if (consumer_gesture_provider_.count(consumer) == 0)
      return false;
    const MotionEvent& pointer_state =
        consumer_gesture_provider_[consumer]->pointer_state();
    *point = gfx::PointF(pointer_state.GetX(), pointer_state.GetY());
    return true;
}

bool GestureRecognizerImpl::CancelActiveTouches(GestureConsumer* consumer) {
  bool cancelled_touch = false;
  if (consumer_gesture_provider_.count(consumer) == 0)
    return false;
  const MotionEventAura& pointer_state =
      consumer_gesture_provider_[consumer]->pointer_state();
  if (pointer_state.GetPointerCount() == 0)
    return false;
  // Pointer_state is modified every time after DispatchCancelTouchEvent.
  scoped_ptr<MotionEvent> pointer_state_clone = pointer_state.Clone();
  for (size_t i = 0; i < pointer_state_clone->GetPointerCount(); ++i) {
    gfx::PointF point(pointer_state_clone->GetX(i),
                      pointer_state_clone->GetY(i));
    TouchEvent touch_event(ui::ET_TOUCH_CANCELLED,
                           point,
                           ui::EF_IS_SYNTHESIZED,
                           pointer_state_clone->GetPointerId(i),
                           ui::EventTimeForNow(),
                           0.0f,
                           0.0f,
                           0.0f,
                           0.0f);
    GestureEventHelper* helper = FindDispatchHelperForConsumer(consumer);
    if (helper)
      helper->DispatchCancelTouchEvent(&touch_event);
    cancelled_touch = true;
  }
  return cancelled_touch;
}

////////////////////////////////////////////////////////////////////////////////
// GestureRecognizerImpl, private:

GestureProviderAura* GestureRecognizerImpl::GetGestureProviderForConsumer(
    GestureConsumer* consumer) {
  GestureProviderAura* gesture_provider = consumer_gesture_provider_[consumer];
  if (!gesture_provider) {
    gesture_provider = CreateGestureProvider(this);
    consumer_gesture_provider_[consumer] = gesture_provider;
  }
  return gesture_provider;
}

void GestureRecognizerImpl::SetupTargets(const TouchEvent& event,
                                         GestureConsumer* target) {
  if (event.type() == ui::ET_TOUCH_RELEASED ||
      event.type() == ui::ET_TOUCH_CANCELLED) {
    touch_id_target_.erase(event.touch_id());
  } else if (event.type() == ui::ET_TOUCH_PRESSED) {
    touch_id_target_[event.touch_id()] = target;
    if (target)
      touch_id_target_for_gestures_[event.touch_id()] = target;
  }
}

void GestureRecognizerImpl::DispatchGestureEvent(GestureEvent* event) {
  GestureConsumer* consumer = GetTargetForGestureEvent(*event);
  if (consumer) {
    GestureEventHelper* helper = FindDispatchHelperForConsumer(consumer);
    if (helper)
      helper->DispatchGestureEvent(event);
  }
}

bool GestureRecognizerImpl::ProcessTouchEventPreDispatch(
    const TouchEvent& event,
    GestureConsumer* consumer) {
  SetupTargets(event, consumer);

  if (event.result() & ER_CONSUMED)
    return false;

  GestureProviderAura* gesture_provider =
      GetGestureProviderForConsumer(consumer);
  return gesture_provider->OnTouchEvent(event);
}

GestureRecognizer::Gestures*
GestureRecognizerImpl::AckAsyncTouchEvent(
    const TouchEvent& event,
    ui::EventResult result,
    GestureConsumer* consumer) {
  GestureProviderAura* gesture_provider =
      GetGestureProviderForConsumer(consumer);
  gesture_provider->OnAsyncTouchEventAck(result != ER_UNHANDLED);
  return gesture_provider->GetAndResetPendingGestures();
}

GestureRecognizer::Gestures* GestureRecognizerImpl::AckSyncTouchEvent(
    const uint64 unique_event_id,
    ui::EventResult result,
    GestureConsumer* consumer) {
  GestureProviderAura* gesture_provider =
      GetGestureProviderForConsumer(consumer);
  gesture_provider->OnSyncTouchEventAck(unique_event_id,
                                        result != ER_UNHANDLED);
  return gesture_provider->GetAndResetPendingGestures();
}

bool GestureRecognizerImpl::CleanupStateForConsumer(
    GestureConsumer* consumer) {
  bool state_cleaned_up = false;

  if (consumer_gesture_provider_.count(consumer)) {
    state_cleaned_up = true;
    delete consumer_gesture_provider_[consumer];
    consumer_gesture_provider_.erase(consumer);
  }

  state_cleaned_up |= RemoveConsumerFromMap(consumer, &touch_id_target_);
  state_cleaned_up |=
      RemoveConsumerFromMap(consumer, &touch_id_target_for_gestures_);
  return state_cleaned_up;
}

void GestureRecognizerImpl::AddGestureEventHelper(GestureEventHelper* helper) {
  helpers_.push_back(helper);
}

void GestureRecognizerImpl::RemoveGestureEventHelper(
    GestureEventHelper* helper) {
  std::vector<GestureEventHelper*>::iterator it = std::find(helpers_.begin(),
      helpers_.end(), helper);
  if (it != helpers_.end())
    helpers_.erase(it);
}

void GestureRecognizerImpl::OnGestureEvent(GestureEvent* event) {
  DispatchGestureEvent(event);
}

GestureEventHelper* GestureRecognizerImpl::FindDispatchHelperForConsumer(
    GestureConsumer* consumer) {
  std::vector<GestureEventHelper*>::iterator it;
  for (it = helpers_.begin(); it != helpers_.end(); ++it) {
    if ((*it)->CanDispatchToConsumer(consumer))
      return (*it);
  }
  return NULL;
}

// GestureRecognizer, static
GestureRecognizer* GestureRecognizer::Create() {
  return new GestureRecognizerImpl();
}

static GestureRecognizerImpl* g_gesture_recognizer_instance = NULL;

// GestureRecognizer, static
GestureRecognizer* GestureRecognizer::Get() {
  if (!g_gesture_recognizer_instance)
    g_gesture_recognizer_instance = new GestureRecognizerImpl();
  return g_gesture_recognizer_instance;
}

// GestureRecognizer, static
void GestureRecognizer::Reset() {
  delete g_gesture_recognizer_instance;
  g_gesture_recognizer_instance = NULL;
}

void SetGestureRecognizerForTesting(GestureRecognizer* gesture_recognizer) {
  // Transfer helpers to the new GR.
  std::vector<GestureEventHelper*>& helpers =
      g_gesture_recognizer_instance->helpers();
  std::vector<GestureEventHelper*>::iterator it;
  for (it = helpers.begin(); it != helpers.end(); ++it)
    gesture_recognizer->AddGestureEventHelper(*it);

  helpers.clear();
  g_gesture_recognizer_instance =
      static_cast<GestureRecognizerImpl*>(gesture_recognizer);
}

}  // namespace ui
<commit_msg>The GestureRecognizer should never map an ID to a NULL consumer<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/gestures/gesture_recognizer_impl.h"

#include <limits>

#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/time/time.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_switches.h"
#include "ui/events/event_utils.h"
#include "ui/events/gesture_detection/gesture_configuration.h"
#include "ui/events/gestures/gesture_types.h"

namespace ui {

namespace {

template <typename T>
void TransferConsumer(GestureConsumer* current_consumer,
                      GestureConsumer* new_consumer,
                      std::map<GestureConsumer*, T>* map) {
  if (map->count(current_consumer)) {
    (*map)[new_consumer] = (*map)[current_consumer];
    map->erase(current_consumer);
  }
}

bool RemoveConsumerFromMap(GestureConsumer* consumer,
                           GestureRecognizerImpl::TouchIdToConsumerMap* map) {
  bool consumer_removed = false;
  for (GestureRecognizerImpl::TouchIdToConsumerMap::iterator i = map->begin();
       i != map->end();) {
    if (i->second == consumer) {
      map->erase(i++);
      consumer_removed = true;
    } else {
      ++i;
    }
  }
  return consumer_removed;
}

void TransferTouchIdToConsumerMap(
    GestureConsumer* old_consumer,
    GestureConsumer* new_consumer,
    GestureRecognizerImpl::TouchIdToConsumerMap* map) {
  for (GestureRecognizerImpl::TouchIdToConsumerMap::iterator i = map->begin();
       i != map->end(); ++i) {
    if (i->second == old_consumer)
      i->second = new_consumer;
  }
}

GestureProviderAura* CreateGestureProvider(GestureProviderAuraClient* client) {
  return new GestureProviderAura(client);
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// GestureRecognizerImpl, public:

GestureRecognizerImpl::GestureRecognizerImpl() {
}

GestureRecognizerImpl::~GestureRecognizerImpl() {
  STLDeleteValues(&consumer_gesture_provider_);
}

// Checks if this finger is already down, if so, returns the current target.
// Otherwise, returns NULL.
GestureConsumer* GestureRecognizerImpl::GetTouchLockedTarget(
    const TouchEvent& event) {
  return touch_id_target_[event.touch_id()];
}

GestureConsumer* GestureRecognizerImpl::GetTargetForGestureEvent(
    const GestureEvent& event) {
  int touch_id = event.details().oldest_touch_id();
  if (!touch_id_target_for_gestures_.count(touch_id)) {
    NOTREACHED() << "Touch ID does not map to a valid GestureConsumer.";
    return nullptr;
  }

  return touch_id_target_for_gestures_.at(touch_id);
}

GestureConsumer* GestureRecognizerImpl::GetTargetForLocation(
    const gfx::PointF& location, int source_device_id) {
  const float max_distance =
      GestureConfiguration::GetInstance()
          ->max_separation_for_gesture_touches_in_pixels();

  gfx::PointF closest_point;
  int closest_touch_id = 0;
  double closest_distance_squared = std::numeric_limits<double>::infinity();

  std::map<GestureConsumer*, GestureProviderAura*>::iterator i;
  for (i = consumer_gesture_provider_.begin();
       i != consumer_gesture_provider_.end();
       ++i) {
    const MotionEventAura& pointer_state = i->second->pointer_state();
    for (size_t j = 0; j < pointer_state.GetPointerCount(); ++j) {
      if (source_device_id != pointer_state.GetSourceDeviceId(j))
        continue;
      gfx::PointF point(pointer_state.GetX(j), pointer_state.GetY(j));
      // Relative distance is all we need here, so LengthSquared() is
      // appropriate, and cheaper than Length().
      double distance_squared = (point - location).LengthSquared();
      if (distance_squared < closest_distance_squared) {
        closest_point = point;
        closest_touch_id = pointer_state.GetPointerId(j);
        closest_distance_squared = distance_squared;
      }
    }
  }

  if (closest_distance_squared < max_distance * max_distance)
    return touch_id_target_[closest_touch_id];
  return NULL;
}

void GestureRecognizerImpl::TransferEventsTo(GestureConsumer* current_consumer,
                                             GestureConsumer* new_consumer) {
  // Send cancel to all those save |new_consumer| and |current_consumer|.
  // Don't send a cancel to |current_consumer|, unless |new_consumer| is NULL.
  // Dispatching a touch-cancel event can end up altering |touch_id_target_|
  // (e.g. when the target of the event is destroyed, causing it to be removed
  // from |touch_id_target_| in |CleanupStateForConsumer()|). So create a list
  // of the touch-ids that need to be cancelled, and dispatch the cancel events
  // for them at the end.

  std::vector<GestureConsumer*> consumers;
  std::map<GestureConsumer*, GestureProviderAura*>::iterator i;
  for (i = consumer_gesture_provider_.begin();
       i != consumer_gesture_provider_.end();
       ++i) {
    if (i->first && i->first != new_consumer &&
        (i->first != current_consumer || new_consumer == NULL)) {
      consumers.push_back(i->first);
    }
  }
  for (std::vector<GestureConsumer*>::iterator iter = consumers.begin();
       iter != consumers.end();
       ++iter) {
    CancelActiveTouches(*iter);
  }
  // Transfer events from |current_consumer| to |new_consumer|.
  if (current_consumer && new_consumer) {
    TransferTouchIdToConsumerMap(current_consumer, new_consumer,
                                 &touch_id_target_);
    TransferTouchIdToConsumerMap(current_consumer, new_consumer,
                                 &touch_id_target_for_gestures_);
    TransferConsumer(
        current_consumer, new_consumer, &consumer_gesture_provider_);
  }
}

bool GestureRecognizerImpl::GetLastTouchPointForTarget(
    GestureConsumer* consumer,
    gfx::PointF* point) {
    if (consumer_gesture_provider_.count(consumer) == 0)
      return false;
    const MotionEvent& pointer_state =
        consumer_gesture_provider_[consumer]->pointer_state();
    *point = gfx::PointF(pointer_state.GetX(), pointer_state.GetY());
    return true;
}

bool GestureRecognizerImpl::CancelActiveTouches(GestureConsumer* consumer) {
  bool cancelled_touch = false;
  if (consumer_gesture_provider_.count(consumer) == 0)
    return false;
  const MotionEventAura& pointer_state =
      consumer_gesture_provider_[consumer]->pointer_state();
  if (pointer_state.GetPointerCount() == 0)
    return false;
  // Pointer_state is modified every time after DispatchCancelTouchEvent.
  scoped_ptr<MotionEvent> pointer_state_clone = pointer_state.Clone();
  for (size_t i = 0; i < pointer_state_clone->GetPointerCount(); ++i) {
    gfx::PointF point(pointer_state_clone->GetX(i),
                      pointer_state_clone->GetY(i));
    TouchEvent touch_event(ui::ET_TOUCH_CANCELLED,
                           point,
                           ui::EF_IS_SYNTHESIZED,
                           pointer_state_clone->GetPointerId(i),
                           ui::EventTimeForNow(),
                           0.0f,
                           0.0f,
                           0.0f,
                           0.0f);
    GestureEventHelper* helper = FindDispatchHelperForConsumer(consumer);
    if (helper)
      helper->DispatchCancelTouchEvent(&touch_event);
    cancelled_touch = true;
  }
  return cancelled_touch;
}

////////////////////////////////////////////////////////////////////////////////
// GestureRecognizerImpl, private:

GestureProviderAura* GestureRecognizerImpl::GetGestureProviderForConsumer(
    GestureConsumer* consumer) {
  GestureProviderAura* gesture_provider = consumer_gesture_provider_[consumer];
  if (!gesture_provider) {
    gesture_provider = CreateGestureProvider(this);
    consumer_gesture_provider_[consumer] = gesture_provider;
  }
  return gesture_provider;
}

void GestureRecognizerImpl::SetupTargets(const TouchEvent& event,
                                         GestureConsumer* target) {
  if (event.type() == ui::ET_TOUCH_RELEASED ||
      event.type() == ui::ET_TOUCH_CANCELLED) {
    touch_id_target_.erase(event.touch_id());
  } else if (event.type() == ui::ET_TOUCH_PRESSED) {
    touch_id_target_[event.touch_id()] = target;
    if (target)
      touch_id_target_for_gestures_[event.touch_id()] = target;
  }
}

void GestureRecognizerImpl::DispatchGestureEvent(GestureEvent* event) {
  GestureConsumer* consumer = GetTargetForGestureEvent(*event);
  if (consumer) {
    GestureEventHelper* helper = FindDispatchHelperForConsumer(consumer);
    if (helper)
      helper->DispatchGestureEvent(event);
  }
}

bool GestureRecognizerImpl::ProcessTouchEventPreDispatch(
    const TouchEvent& event,
    GestureConsumer* consumer) {
  SetupTargets(event, consumer);

  if (event.result() & ER_CONSUMED)
    return false;

  GestureProviderAura* gesture_provider =
      GetGestureProviderForConsumer(consumer);
  return gesture_provider->OnTouchEvent(event);
}

GestureRecognizer::Gestures*
GestureRecognizerImpl::AckAsyncTouchEvent(
    const TouchEvent& event,
    ui::EventResult result,
    GestureConsumer* consumer) {
  GestureProviderAura* gesture_provider =
      GetGestureProviderForConsumer(consumer);
  gesture_provider->OnAsyncTouchEventAck(result != ER_UNHANDLED);
  return gesture_provider->GetAndResetPendingGestures();
}

GestureRecognizer::Gestures* GestureRecognizerImpl::AckSyncTouchEvent(
    const uint64 unique_event_id,
    ui::EventResult result,
    GestureConsumer* consumer) {
  GestureProviderAura* gesture_provider =
      GetGestureProviderForConsumer(consumer);
  gesture_provider->OnSyncTouchEventAck(unique_event_id,
                                        result != ER_UNHANDLED);
  return gesture_provider->GetAndResetPendingGestures();
}

bool GestureRecognizerImpl::CleanupStateForConsumer(
    GestureConsumer* consumer) {
  bool state_cleaned_up = false;

  if (consumer_gesture_provider_.count(consumer)) {
    state_cleaned_up = true;
    delete consumer_gesture_provider_[consumer];
    consumer_gesture_provider_.erase(consumer);
  }

  state_cleaned_up |= RemoveConsumerFromMap(consumer, &touch_id_target_);
  state_cleaned_up |=
      RemoveConsumerFromMap(consumer, &touch_id_target_for_gestures_);
  return state_cleaned_up;
}

void GestureRecognizerImpl::AddGestureEventHelper(GestureEventHelper* helper) {
  helpers_.push_back(helper);
}

void GestureRecognizerImpl::RemoveGestureEventHelper(
    GestureEventHelper* helper) {
  std::vector<GestureEventHelper*>::iterator it = std::find(helpers_.begin(),
      helpers_.end(), helper);
  if (it != helpers_.end())
    helpers_.erase(it);
}

void GestureRecognizerImpl::OnGestureEvent(GestureEvent* event) {
  DispatchGestureEvent(event);
}

GestureEventHelper* GestureRecognizerImpl::FindDispatchHelperForConsumer(
    GestureConsumer* consumer) {
  std::vector<GestureEventHelper*>::iterator it;
  for (it = helpers_.begin(); it != helpers_.end(); ++it) {
    if ((*it)->CanDispatchToConsumer(consumer))
      return (*it);
  }
  return NULL;
}

// GestureRecognizer, static
GestureRecognizer* GestureRecognizer::Create() {
  return new GestureRecognizerImpl();
}

static GestureRecognizerImpl* g_gesture_recognizer_instance = NULL;

// GestureRecognizer, static
GestureRecognizer* GestureRecognizer::Get() {
  if (!g_gesture_recognizer_instance)
    g_gesture_recognizer_instance = new GestureRecognizerImpl();
  return g_gesture_recognizer_instance;
}

// GestureRecognizer, static
void GestureRecognizer::Reset() {
  delete g_gesture_recognizer_instance;
  g_gesture_recognizer_instance = NULL;
}

void SetGestureRecognizerForTesting(GestureRecognizer* gesture_recognizer) {
  // Transfer helpers to the new GR.
  std::vector<GestureEventHelper*>& helpers =
      g_gesture_recognizer_instance->helpers();
  std::vector<GestureEventHelper*>::iterator it;
  for (it = helpers.begin(); it != helpers.end(); ++it)
    gesture_recognizer->AddGestureEventHelper(*it);

  helpers.clear();
  g_gesture_recognizer_instance =
      static_cast<GestureRecognizerImpl*>(gesture_recognizer);
}

}  // namespace ui
<|endoftext|>


<commit_before>/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2016 The PHP Group                                |
  +----------------------------------------------------------------------+
  | http://www.opensource.org/licenses/mit-license.php  MIT License      |
  +----------------------------------------------------------------------+
  | Author: Stefan Siegl <stesie@brokenpipe.de>                          |
  +----------------------------------------------------------------------+
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php_v8js_macros.h"
#include "v8js_array_access.h"
#include "v8js_object_export.h"

extern "C" {
#include "php.h"
#include "ext/date/php_date.h"
#include "ext/standard/php_string.h"
#include "zend_interfaces.h"
#include "zend_closures.h"
}

static zval v8js_array_access_dispatch(zend_object *object, const char *method_name, int param_count,
									   uint32_t index, zval zvalue TSRMLS_DC) /* {{{ */
{
	zend_fcall_info fci;
	zval php_value;

	fci.size = sizeof(fci);
#if (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0)
	fci.function_table = &object->ce->function_table;
	fci.symbol_table = NULL;
#endif
	ZVAL_STRING(&fci.function_name, method_name);
	fci.retval = &php_value;

	zval params[2];
	ZVAL_LONG(&params[0], index);
	params[1] = zvalue;

	fci.param_count = param_count;
	fci.params = params;

	fci.object = object;
	fci.no_separation = 0;

	zend_call_function(&fci, NULL TSRMLS_CC);
	zval_dtor(&fci.function_name);
	return php_value;
}
/* }}} */

void v8js_array_access_getter(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	zval php_value = v8js_array_access_dispatch(object, "offsetGet", 1, index, zvalue TSRMLS_CC);
	v8::Local<v8::Value> ret_value = zval_to_v8js(&php_value, isolate TSRMLS_CC);
	zval_ptr_dtor(&php_value);

	info.GetReturnValue().Set(ret_value);
}
/* }}} */

void v8js_array_access_setter(uint32_t index, v8::Local<v8::Value> value,
								  const v8::PropertyCallbackInfo<v8::Value>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	if (v8js_to_zval(value, &zvalue, 0, isolate TSRMLS_CC) != SUCCESS) {
		info.GetReturnValue().Set(v8::Handle<v8::Value>());
		return;
	}

	zval php_value = v8js_array_access_dispatch(object, "offsetSet", 2, index, zvalue TSRMLS_CC);
	zval_ptr_dtor(&php_value);

	/* simply pass back the value to tell we intercepted the call
	 * as the offsetSet function returns void. */
	info.GetReturnValue().Set(value);

	/* if PHP wanted to hold on to this value, zend_call_function would
	 * have bumped the refcount. */
	zval_ptr_dtor(&zvalue);
}
/* }}} */

static int v8js_array_access_get_count_result(zend_object *object TSRMLS_DC) /* {{{ */
{
	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	zval php_value = v8js_array_access_dispatch(object, "count", 0, 0, zvalue TSRMLS_CC);

	if(Z_TYPE(php_value) != IS_LONG) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Non-numeric return value from count() method");
		zval_ptr_dtor(&php_value);
		return 0;
	}

	int result = Z_LVAL(php_value);
	return result;
}
/* }}} */

static bool v8js_array_access_isset_p(zend_object *object, int index TSRMLS_DC) /* {{{ */
{
	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	zval php_value = v8js_array_access_dispatch(object, "offsetExists", 1, index, zvalue TSRMLS_CC);

	if(Z_TYPE(php_value) != IS_TRUE && Z_TYPE(php_value) != IS_FALSE) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Non-boolean return value from offsetExists() method");
		zval_ptr_dtor(&php_value);
		return false;
	}

	return Z_TYPE(php_value) == IS_TRUE;
}
/* }}} */

static void v8js_array_access_length(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	int length = v8js_array_access_get_count_result(object TSRMLS_CC);
	info.GetReturnValue().Set(V8JS_INT(length));
}
/* }}} */

void v8js_array_access_deleter(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	zval php_value = v8js_array_access_dispatch(object, "offsetUnset", 1, index, zvalue TSRMLS_CC);
	zval_ptr_dtor(&php_value);

	info.GetReturnValue().Set(V8JS_BOOL(true));
}
/* }}} */

void v8js_array_access_query(uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	/* If index is set, then return an integer encoding a v8::PropertyAttribute;
	 * otherwise we're expected to return an empty handle. */
	if(v8js_array_access_isset_p(object, index TSRMLS_CC)) {
		info.GetReturnValue().Set(V8JS_UINT(v8::PropertyAttribute::None));
	}
}
/* }}} */

void v8js_array_access_enumerator(const v8::PropertyCallbackInfo<v8::Array>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	int length = v8js_array_access_get_count_result(object TSRMLS_CC);
	v8::Local<v8::Array> result = v8::Array::New(isolate, length);

	int i = 0;

	for(int j = 0; j < length; j ++) {
		if(v8js_array_access_isset_p(object, j TSRMLS_CC)) {
			result->Set(i ++, V8JS_INT(j));
		}
	}

	result->Set(V8JS_STR("length"), V8JS_INT(i));
	info.GetReturnValue().Set(result);
}
/* }}} */

void v8js_array_access_named_getter(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info) /* {{{ */
{
	v8::String::Utf8Value cstr(property);
	const char *name = ToCString(cstr);

	if(strcmp(name, "length") == 0) {
		v8js_array_access_length(property, info);
		return;
	}

	v8::Local<v8::Value> ret_value = v8js_named_property_callback(property, info, V8JS_PROP_GETTER);

	if(ret_value.IsEmpty()) {
		v8::Isolate *isolate = info.GetIsolate();
		v8::Local<v8::Array> arr = v8::Array::New(isolate);
		v8::Local<v8::Value> prototype = arr->GetPrototype();

		if(!prototype->IsObject()) {
			/* ehh?  Array.prototype not an object? strange, stop. */
			info.GetReturnValue().Set(ret_value);
		}

		ret_value = prototype->ToObject()->Get(property);
	}

	info.GetReturnValue().Set(ret_value);
}
/* }}} */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: t
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
<commit_msg>check ArrayAccess offset/count against int max limits<commit_after>/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2016 The PHP Group                                |
  +----------------------------------------------------------------------+
  | http://www.opensource.org/licenses/mit-license.php  MIT License      |
  +----------------------------------------------------------------------+
  | Author: Stefan Siegl <stesie@brokenpipe.de>                          |
  +----------------------------------------------------------------------+
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php_v8js_macros.h"
#include "v8js_array_access.h"
#include "v8js_exceptions.h"
#include "v8js_object_export.h"

extern "C" {
#include "php.h"
#include "ext/date/php_date.h"
#include "ext/standard/php_string.h"
#include "zend_interfaces.h"
#include "zend_closures.h"
#include "zend_exceptions.h"
}

static zval v8js_array_access_dispatch(zend_object *object, const char *method_name, int param_count,
									   uint32_t index, zval zvalue TSRMLS_DC) /* {{{ */
{
	zend_fcall_info fci;
	zval php_value;

	fci.size = sizeof(fci);
#if (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0)
	fci.function_table = &object->ce->function_table;
	fci.symbol_table = NULL;
#endif
	ZVAL_STRING(&fci.function_name, method_name);
	fci.retval = &php_value;

	zval params[2];
	ZVAL_LONG(&params[0], index);
	params[1] = zvalue;

	fci.param_count = param_count;
	fci.params = params;

	fci.object = object;
	fci.no_separation = 0;

	zend_call_function(&fci, NULL TSRMLS_CC);
	zval_dtor(&fci.function_name);
	return php_value;
}
/* }}} */

void v8js_array_access_getter(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	zval php_value = v8js_array_access_dispatch(object, "offsetGet", 1, index, zvalue TSRMLS_CC);
	v8::Local<v8::Value> ret_value = zval_to_v8js(&php_value, isolate TSRMLS_CC);
	zval_ptr_dtor(&php_value);

	info.GetReturnValue().Set(ret_value);
}
/* }}} */

void v8js_array_access_setter(uint32_t index, v8::Local<v8::Value> value,
								  const v8::PropertyCallbackInfo<v8::Value>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	if (v8js_to_zval(value, &zvalue, 0, isolate TSRMLS_CC) != SUCCESS) {
		info.GetReturnValue().Set(v8::Handle<v8::Value>());
		return;
	}

	zval php_value = v8js_array_access_dispatch(object, "offsetSet", 2, index, zvalue TSRMLS_CC);
	zval_ptr_dtor(&php_value);

	/* simply pass back the value to tell we intercepted the call
	 * as the offsetSet function returns void. */
	info.GetReturnValue().Set(value);

	/* if PHP wanted to hold on to this value, zend_call_function would
	 * have bumped the refcount. */
	zval_ptr_dtor(&zvalue);
}
/* }}} */

static int v8js_array_access_get_count_result(zend_object *object TSRMLS_DC) /* {{{ */
{
	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	zval php_value = v8js_array_access_dispatch(object, "count", 0, 0, zvalue TSRMLS_CC);

	if(Z_TYPE(php_value) != IS_LONG) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Non-numeric return value from count() method");
		zval_ptr_dtor(&php_value);
		return 0;
	}

	zend_long result = Z_LVAL(php_value);

	if (result > std::numeric_limits<int>::max()) {
		zend_throw_exception(php_ce_v8js_exception,
			"Array size/offset exceeds maximum supported length", 0);
		return 0;
	}

	return static_cast<int>(result);
}
/* }}} */

static bool v8js_array_access_isset_p(zend_object *object, int index TSRMLS_DC) /* {{{ */
{
	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	zval php_value = v8js_array_access_dispatch(object, "offsetExists", 1, index, zvalue TSRMLS_CC);

	if(Z_TYPE(php_value) != IS_TRUE && Z_TYPE(php_value) != IS_FALSE) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Non-boolean return value from offsetExists() method");
		zval_ptr_dtor(&php_value);
		return false;
	}

	return Z_TYPE(php_value) == IS_TRUE;
}
/* }}} */

static void v8js_array_access_length(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	int length = v8js_array_access_get_count_result(object TSRMLS_CC);
	info.GetReturnValue().Set(V8JS_INT(length));
}
/* }}} */

void v8js_array_access_deleter(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	zval zvalue;
	ZVAL_UNDEF(&zvalue);

	zval php_value = v8js_array_access_dispatch(object, "offsetUnset", 1, index, zvalue TSRMLS_CC);
	zval_ptr_dtor(&php_value);

	info.GetReturnValue().Set(V8JS_BOOL(true));
}
/* }}} */

void v8js_array_access_query(uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	/* If index is set, then return an integer encoding a v8::PropertyAttribute;
	 * otherwise we're expected to return an empty handle. */
	if(v8js_array_access_isset_p(object, index TSRMLS_CC)) {
		info.GetReturnValue().Set(V8JS_UINT(v8::PropertyAttribute::None));
	}
}
/* }}} */

void v8js_array_access_enumerator(const v8::PropertyCallbackInfo<v8::Array>& info) /* {{{ */
{
	v8::Isolate *isolate = info.GetIsolate();
	v8::Local<v8::Object> self = info.Holder();

	V8JS_TSRMLS_FETCH();

	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));

	int length = v8js_array_access_get_count_result(object TSRMLS_CC);
	v8::Local<v8::Array> result = v8::Array::New(isolate, length);

	int i = 0;

	for(int j = 0; j < length; j ++) {
		if(v8js_array_access_isset_p(object, j TSRMLS_CC)) {
			result->Set(i ++, V8JS_INT(j));
		}
	}

	result->Set(V8JS_STR("length"), V8JS_INT(i));
	info.GetReturnValue().Set(result);
}
/* }}} */

void v8js_array_access_named_getter(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info) /* {{{ */
{
	v8::String::Utf8Value cstr(property);
	const char *name = ToCString(cstr);

	if(strcmp(name, "length") == 0) {
		v8js_array_access_length(property, info);
		return;
	}

	v8::Local<v8::Value> ret_value = v8js_named_property_callback(property, info, V8JS_PROP_GETTER);

	if(ret_value.IsEmpty()) {
		v8::Isolate *isolate = info.GetIsolate();
		v8::Local<v8::Array> arr = v8::Array::New(isolate);
		v8::Local<v8::Value> prototype = arr->GetPrototype();

		if(!prototype->IsObject()) {
			/* ehh?  Array.prototype not an object? strange, stop. */
			info.GetReturnValue().Set(ret_value);
		}

		ret_value = prototype->ToObject()->Get(property);
	}

	info.GetReturnValue().Set(ret_value);
}
/* }}} */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: t
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
<|endoftext|>


<commit_before>#include <Network/cUDPServerManager.h>
#include <Network/cUDPManager.h>
#include <Network/cDeliverManager.h>
#include <Network/cEventManager.h>
#include <Network/cRequestManager.h>
#include <Network/cResponseManager.h>
#include <cinder/app/App.h>
#include <limits>
#include <Node/action.hpp>
#include <Utility/MessageBox.h>
#include <Network/IpHost.h>
namespace Network
{
cUDPServerManager::cUDPServerManager( )
    : mRoot( Node::node::create( ) )
{
    mRoot->set_schedule_update( );
    mIsAccept = false;
    mIdCount = 0;
}
void cUDPServerManager::close( )
{
    mSocket.close( );
    mIsAccept = false;
}
void cUDPServerManager::open( )
{
    mSocket.open( 25565 );
    mIsAccept = true;
}
void cUDPServerManager::closeAccepter( )
{
    mIsAccept = false;
}
void cUDPServerManager::openAccepter( )
{
    mIsAccept = true;
}
void cUDPServerManager::update( float delta )
{
    updateRecv( );
    updateSend( );
    mRoot->entry_update( delta );
}
ubyte1 cUDPServerManager::getPlayerId( cNetworkHandle const & handle )
{
    auto itr = mHandle.find( handle );
    if ( itr != mHandle.end( ) )
    {
        return itr->second.id;
    }
    else
    {
        throw std::runtime_error( "Networkhandle nothing" );
    }
}
void cUDPServerManager::updateSend( )
{
    // M̂΃obt@[ođB
    for ( auto& handle : mHandle )
    {
        // ]ĂpPbg𑗂܂B
        if ( !handle.second.buffer.empty( ) )
        {
            mSocket.write( handle.first, handle.second.buffer.size( ), handle.second.buffer.data( ) );
            handle.second.buffer.clear( );
            handle.second.buffer.shrink_to_fit( );
        }
    }
}
void cUDPServerManager::updateRecv( )
{
    // M̂΃obt@[oăpPbg̕ʂsB
    while ( !mSocket.emptyChunk( ) )
    {
        auto chunk = mSocket.popChunk( );
        if ( cUDPManager::getInstance( )->isConnectPacket( chunk ) ||
            ( mHandle.find( chunk.networkHandle ) != mHandle.end( ) ) )
        {
            cUDPManager::getInstance( )->onReceive( chunk );
        }
        else
        {
            // RlNVmȂ܂ܑMĂꍇB
        }
    }

    connection( );
    ping( );
}
void cUDPServerManager::sendDataBufferAdd( cNetworkHandle const & networkHandle, cPacketBuffer const & packetBuffer )
{
    auto itr = mHandle.find( networkHandle );
    if ( itr == mHandle.end( ) ) return;

    auto& buf = itr->second.buffer;

    // pPbg傫Ȃ肻ȂɑĂ܂܂B
    if ( 1024 < buf.size( ) )
    {
        mSocket.write( networkHandle, buf.size( ), buf.data( ) );
        buf.clear( );
        buf.shrink_to_fit( );
    }

    ubyte2 const& byte = packetBuffer.transferredBytes;
    cBuffer const& buffer = packetBuffer.buffer;

    buf.resize( buf.size( ) + byte );
    memcpy( buf.data( ) + buf.size( ) - byte, &buffer, byte );
}
void cUDPServerManager::connection( )
{
    while ( auto p = cRequestManager::getInstance( )->getReqConnect( ) )
    {
        if ( !mIsAccept ) continue;

        cinder::app::console( ) << p->mNetworkHandle.ipAddress << ":" << p->mNetworkHandle.port << "";
        cinder::app::console( ) << "cUDPServerManager: " << "connecting..." << (int)mIdCount << std::endl;

        auto itr = mHandle.insert( std::make_pair( p->mNetworkHandle, std::move( cClientInfo( mIdCount ) ) ) );
        if ( itr.second )
        {
            cinder::app::console( ) << p->mNetworkHandle.ipAddress << ":" << p->mNetworkHandle.port << "";
            cinder::app::console( ) << "cUDPServerManager: " << "connect success!! " << (int)mIdCount << std::endl;

            mIdCount += 1;

            send( p->mNetworkHandle, new Packet::Response::cResConnect( ) );

            using namespace Node::Action;
            auto networkHandle = p->mNetworkHandle;
            auto act = repeat_forever::create( sequence::create( delay::create( 1.5F ), call_func::create( [ networkHandle, this ]
            {
                send( networkHandle, new Packet::Event::cEvePing( ) );
                cinder::app::console( ) << "cUDPServerManager: " << "ping to " << (int)getPlayerId( networkHandle ) << std::endl;
            } ) ) );
            act->set_tag( itr.first->second.id );
            mRoot->run_action( act );
        }
        else
        {
            cinder::app::console( ) << p->mNetworkHandle.ipAddress << ":" << p->mNetworkHandle.port << "";
            cinder::app::console( ) << "cUDPServerManager: " << "connect faild " << (int)mIdCount << std::endl;
        }
    }
}
void cUDPServerManager::ping( )
{
    while ( auto p = cDeliverManager::getInstance( )->getDliPing( ) )
    {
        auto itr = mHandle.find( p->mNetworkHandle );
        if ( itr != mHandle.end( ) )
        {
            itr->second.closeSecond = cinder::app::getElapsedSeconds( ) + HOLD_SECOND;
        }
    }

    for ( auto itr = mHandle.begin( ); itr != mHandle.end( ); )
    {
        // [J̏ꍇ̓JEg_E܂B
        if ( itr->first.ipAddress != Network::getLocalIpAddressHost( ) )
        {
            if ( itr->second.closeSecond < cinder::app::getElapsedSeconds( ) )
            {
                cinder::app::console( ) << itr->first.ipAddress << ":" << itr->first.port << "";
                cinder::app::console( ) << "cUDPServerManager: " << "disconnect" << itr->second.id << std::endl;
                mRoot->remove_action_by_tag( itr->second.id );
                mHandle.erase( itr++ );
                continue;
            }
        }
        itr++;
    }
}
cUDPServerManager::cClientInfo::cClientInfo( ubyte1 idCount )
    : closeSecond( cinder::app::getElapsedSeconds( ) + HOLD_SECOND )
{
    if ( idCount == 255 )
    {
        MES_ERR( "NCAg̐ɒB܂B",
                 [ ] { exit( 0 ); } );
    }
    else
    {
        id = idCount;
    }
}
}<commit_msg>サーバーが閉じた時にハンドルもクリアするようにしました。<commit_after>#include <Network/cUDPServerManager.h>
#include <Network/cUDPManager.h>
#include <Network/cDeliverManager.h>
#include <Network/cEventManager.h>
#include <Network/cRequestManager.h>
#include <Network/cResponseManager.h>
#include <cinder/app/App.h>
#include <limits>
#include <Node/action.hpp>
#include <Utility/MessageBox.h>
#include <Network/IpHost.h>
namespace Network
{
cUDPServerManager::cUDPServerManager( )
{
    mRoot = Node::node::create( );
    mRoot->set_schedule_update( );
    mIsAccept = false;
    mIdCount = 0;
}
void cUDPServerManager::close( )
{
    mRoot.reset( );
    mSocket.close( );
    closeAccepter( );
}
void cUDPServerManager::open( )
{
    mRoot = Node::node::create( );
    mRoot->set_schedule_update( );
    mSocket.open( 25565 );
    openAccepter( );
}
void cUDPServerManager::closeAccepter( )
{
    mIsAccept = false;
    mIdCount = 0;
    mHandle.clear( );
}
void cUDPServerManager::openAccepter( )
{
    mIsAccept = true;
}
void cUDPServerManager::update( float delta )
{
    updateRecv( );
    updateSend( );
    mRoot->entry_update( delta );
}
ubyte1 cUDPServerManager::getPlayerId( cNetworkHandle const & handle )
{
    auto itr = mHandle.find( handle );
    if ( itr != mHandle.end( ) )
    {
        return itr->second.id;
    }
    else
    {
        throw std::runtime_error( "Networkhandle nothing" );
    }
}
void cUDPServerManager::updateSend( )
{
    // M̂΃obt@[ođB
    for ( auto& handle : mHandle )
    {
        // ]ĂpPbg𑗂܂B
        if ( !handle.second.buffer.empty( ) )
        {
            mSocket.write( handle.first, handle.second.buffer.size( ), handle.second.buffer.data( ) );
            handle.second.buffer.clear( );
            handle.second.buffer.shrink_to_fit( );
        }
    }
}
void cUDPServerManager::updateRecv( )
{
    // M̂΃obt@[oăpPbg̕ʂsB
    while ( !mSocket.emptyChunk( ) )
    {
        auto chunk = mSocket.popChunk( );
        if ( cUDPManager::getInstance( )->isConnectPacket( chunk ) ||
            ( mHandle.find( chunk.networkHandle ) != mHandle.end( ) ) )
        {
            cUDPManager::getInstance( )->onReceive( chunk );
        }
        else
        {
            // RlNVmȂ܂ܑMĂꍇB
        }
    }

    connection( );
    ping( );
}
void cUDPServerManager::sendDataBufferAdd( cNetworkHandle const & networkHandle, cPacketBuffer const & packetBuffer )
{
    auto itr = mHandle.find( networkHandle );
    if ( itr == mHandle.end( ) ) return;

    auto& buf = itr->second.buffer;

    // pPbg傫Ȃ肻ȂɑĂ܂܂B
    if ( 1024 < buf.size( ) )
    {
        mSocket.write( networkHandle, buf.size( ), buf.data( ) );
        buf.clear( );
        buf.shrink_to_fit( );
    }

    ubyte2 const& byte = packetBuffer.transferredBytes;
    cBuffer const& buffer = packetBuffer.buffer;

    buf.resize( buf.size( ) + byte );
    memcpy( buf.data( ) + buf.size( ) - byte, &buffer, byte );
}
void cUDPServerManager::connection( )
{
    while ( auto p = cRequestManager::getInstance( )->getReqConnect( ) )
    {
        if ( !mIsAccept ) continue;

        cinder::app::console( ) << p->mNetworkHandle.ipAddress << ":" << p->mNetworkHandle.port << "";
        cinder::app::console( ) << "cUDPServerManager: " << "connecting..." << (int)mIdCount << std::endl;

        auto itr = mHandle.insert( std::make_pair( p->mNetworkHandle, std::move( cClientInfo( mIdCount ) ) ) );
        if ( itr.second )
        {
            cinder::app::console( ) << p->mNetworkHandle.ipAddress << ":" << p->mNetworkHandle.port << "";
            cinder::app::console( ) << "cUDPServerManager: " << "connect success!! " << (int)mIdCount << std::endl;

            mIdCount += 1;

            send( p->mNetworkHandle, new Packet::Response::cResConnect( ) );

            using namespace Node::Action;
            auto networkHandle = p->mNetworkHandle;
            auto act = repeat_forever::create( sequence::create( delay::create( 1.5F ), call_func::create( [ networkHandle, this ]
            {
                send( networkHandle, new Packet::Event::cEvePing( ) );
                cinder::app::console( ) << "cUDPServerManager: " << "ping to " << (int)getPlayerId( networkHandle ) << std::endl;
            } ) ) );
            act->set_tag( itr.first->second.id );
            mRoot->run_action( act );
        }
        else
        {
            cinder::app::console( ) << p->mNetworkHandle.ipAddress << ":" << p->mNetworkHandle.port << "";
            cinder::app::console( ) << "cUDPServerManager: " << "connect faild " << (int)mIdCount << std::endl;
        }
    }
}
void cUDPServerManager::ping( )
{
    while ( auto p = cDeliverManager::getInstance( )->getDliPing( ) )
    {
        auto itr = mHandle.find( p->mNetworkHandle );
        if ( itr != mHandle.end( ) )
        {
            itr->second.closeSecond = cinder::app::getElapsedSeconds( ) + HOLD_SECOND;
        }
    }

    for ( auto itr = mHandle.begin( ); itr != mHandle.end( ); )
    {
        // [J̏ꍇ̓JEg_E܂B
        if ( itr->first.ipAddress != Network::getLocalIpAddressHost( ) )
        {
            if ( itr->second.closeSecond < cinder::app::getElapsedSeconds( ) )
            {
                cinder::app::console( ) << itr->first.ipAddress << ":" << itr->first.port << "";
                cinder::app::console( ) << "cUDPServerManager: " << "disconnect" << itr->second.id << std::endl;
                mRoot->remove_action_by_tag( itr->second.id );
                mHandle.erase( itr++ );
                continue;
            }
        }
        itr++;
    }
}
cUDPServerManager::cClientInfo::cClientInfo( ubyte1 idCount )
    : closeSecond( cinder::app::getElapsedSeconds( ) + HOLD_SECOND )
{
    if ( idCount == 255 )
    {
        MES_ERR( "NCAg̐ɒB܂B",
                 [ ] { exit( 0 ); } );
    }
    else
    {
        id = idCount;
    }
}
}<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "details/VertexBuffer.h"

#include "details/Engine.h"

#include "FilamentAPI-impl.h"

#include <math/mat3.h>
#include <math/norm.h>
#include <math/quat.h>
#include <math/vec3.h>
#include <math/vec4.h>

#include <utils/Panic.h>

namespace filament {

using namespace details;
using namespace filament::math;

struct VertexBuffer::BuilderDetails {
    VertexBuffer::Builder::AttributeData mAttributes[MAX_ATTRIBUTE_BUFFERS_COUNT];
    AttributeBitset mDeclaredAttributes;
    uint32_t mVertexCount = 0;
    uint8_t mBufferCount = 0;
};

static bool hasIntegerTarget(VertexAttribute attribute) {
    return attribute == BONE_INDICES;
}

using BuilderType = VertexBuffer;
BuilderType::Builder::Builder() noexcept = default;
BuilderType::Builder::~Builder() noexcept = default;
BuilderType::Builder::Builder(BuilderType::Builder const& rhs) noexcept = default;
BuilderType::Builder::Builder(BuilderType::Builder&& rhs) noexcept = default;
BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder const& rhs) noexcept = default;
BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder&& rhs) noexcept = default;

VertexBuffer::Builder& VertexBuffer::Builder::vertexCount(uint32_t vertexCount) noexcept {
    mImpl->mVertexCount = vertexCount;
    return *this;
}

VertexBuffer::Builder& VertexBuffer::Builder::bufferCount(uint8_t bufferCount) noexcept {
    mImpl->mBufferCount = bufferCount;
    return *this;
}

VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute attribute,
        uint8_t bufferIndex,
        AttributeType attributeType,
        uint32_t byteOffset,
        uint8_t byteStride) noexcept {

    size_t attributeSize = Driver::getElementTypeSize(attributeType);
    if (byteStride == 0) {
        byteStride = (uint8_t)attributeSize;
    }

    if (size_t(attribute) < MAX_ATTRIBUTE_BUFFERS_COUNT &&
        size_t(bufferIndex) < MAX_ATTRIBUTE_BUFFERS_COUNT) {

#ifndef NDEBUG
        if (byteOffset & 0x3) {
            utils::slog.d << "[performance] VertexBuffer::Builder::attribute() "
                             "byteOffset not multiple of 4" << utils::io::endl;
        }
        if (byteStride & 0x3) {
            utils::slog.d << "[performance] VertexBuffer::Builder::attribute() "
                             "byteStride not multiple of 4" << utils::io::endl;
        }
#endif

        AttributeData& entry = mImpl->mAttributes[attribute];
        entry.buffer = bufferIndex;
        entry.offset = byteOffset;
        entry.stride = byteStride;
        entry.type = attributeType;
        if (hasIntegerTarget(attribute)) {
            entry.flags |= Driver::Attribute::FLAG_INTEGER_TARGET;
        }
        mImpl->mDeclaredAttributes.set(attribute);
    }
    return *this;
}

VertexBuffer::Builder& VertexBuffer::Builder::normalized(VertexAttribute attribute,
        bool normalized) noexcept {
    if (size_t(attribute) < MAX_ATTRIBUTE_BUFFERS_COUNT) {
        AttributeData& entry = mImpl->mAttributes[attribute];
        if (normalized) {
            entry.flags |= Driver::Attribute::FLAG_NORMALIZED;
        } else {
            entry.flags &= ~Driver::Attribute::FLAG_NORMALIZED;
        }
    }
    return *this;
}

VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
    if (!ASSERT_PRECONDITION_NON_FATAL(mImpl->mVertexCount > 0, "vertexCount cannot be 0")) {
        return nullptr;
    }

    if (!ASSERT_PRECONDITION_NON_FATAL(mImpl->mBufferCount > 0, "bufferCount cannot be 0")) {
        return nullptr;
    }

    return upcast(engine).createVertexBuffer(*this);
}

// ------------------------------------------------------------------------------------------------

namespace details {

FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& builder)
        : mVertexCount(builder->mVertexCount), mBufferCount(builder->mBufferCount) {
    std::copy(std::begin(builder->mAttributes), std::end(builder->mAttributes), mAttributes.begin());

    mDeclaredAttributes = builder->mDeclaredAttributes;
    uint8_t attributeCount = (uint8_t) mDeclaredAttributes.count();

    Driver::AttributeArray attributeArray;

    static_assert(attributeArray.size() == MAX_ATTRIBUTE_BUFFERS_COUNT,
            "Driver::Attribute and Builder::Attribute arrays must match");

    static_assert(sizeof(Driver::Attribute) == sizeof(Builder::AttributeData),
            "Driver::Attribute and Builder::Attribute must match");

    auto const& declaredAttributes = mDeclaredAttributes;
    auto const& attributes = mAttributes;
    #pragma nounroll
    for (size_t i = 0, n = attributeArray.size(); i < n; ++i) {
        if (declaredAttributes[i]) {
            attributeArray[i].offset = attributes[i].offset;
            attributeArray[i].stride = attributes[i].stride;
            attributeArray[i].buffer = attributes[i].buffer;
            attributeArray[i].type   = attributes[i].type;
            attributeArray[i].flags  = attributes[i].flags;
        }
    }

    FEngine::DriverApi& driver = engine.getDriverApi();
    mHandle = driver.createVertexBuffer(
            mBufferCount, attributeCount, mVertexCount, attributeArray, driver::BufferUsage::STATIC);
}

void FVertexBuffer::terminate(FEngine& engine) {
    FEngine::DriverApi& driver = engine.getDriverApi();
    driver.destroyVertexBuffer(mHandle);
}

size_t FVertexBuffer::getVertexCount() const noexcept {
    return mVertexCount;
}

void FVertexBuffer::setBufferAt(FEngine& engine, uint8_t bufferIndex,
        driver::BufferDescriptor&& buffer, uint32_t byteOffset, uint32_t byteSize) {

    if (byteSize == 0) {
        byteSize = uint32_t(buffer.size);
    }

    if (bufferIndex < mBufferCount) {
        engine.getDriverApi().updateVertexBuffer(mHandle, bufferIndex,
                std::move(buffer), byteOffset, byteSize);
    } else {
        ASSERT_PRECONDITION_NON_FATAL(bufferIndex < mBufferCount,
                "bufferIndex must be < bufferCount");
    }
}

} // namespace details

// ------------------------------------------------------------------------------------------------
// Trampoline calling into private implementation
// ------------------------------------------------------------------------------------------------

using namespace filament::details;

size_t VertexBuffer::getVertexCount() const noexcept {
    return upcast(this)->getVertexCount();
}

void VertexBuffer::setBufferAt(Engine& engine, uint8_t bufferIndex,
        driver::BufferDescriptor&& buffer, uint32_t byteOffset, uint32_t byteSize) {
    upcast(this)->setBufferAt(upcast(engine), bufferIndex,
            std::move(buffer), byteOffset, byteSize);
}

void VertexBuffer::populateTangentQuaternions(const QuatTangentContext& ctx) {
    if (!ASSERT_PRECONDITION_NON_FATAL(ctx.normals, "Normals must be provided")) {
        return;
    }

    // Define a small lambda that converts fp32 into the desired output format.
    size_t outStride = ctx.outStride;
    void (*writeQuat)(quatf, uint8_t*);
    switch (ctx.quatType) {
        case HALF4:
            writeQuat = [] (quatf inquat, uint8_t* outquat) {
                *((quath*) outquat) = quath(inquat);
            };
            outStride = outStride ? outStride : sizeof(half4);
            break;
        case SHORT4:
            writeQuat = [] (quatf inquat, uint8_t* outquat) {
                *((short4*) outquat) = packSnorm16(inquat.xyzw);
            };
            outStride = outStride ? outStride : sizeof(short4);
            break;
        case FLOAT4:
            writeQuat = [] (quatf inquat, uint8_t* outquat) {
                *((quatf*) outquat) = inquat;
            };
            outStride = outStride ? outStride : sizeof(float4);
            break;
    }

    const float3* normal = ctx.normals;
    const size_t nstride = ctx.normalsStride ? ctx.normalsStride : sizeof(float3);
    uint8_t* outquat = (uint8_t*) ctx.outBuffer;

    // If tangents are not provided, simply cross N with arbitrary vector (1, 0, 0)
    if (!ctx.tangents) {
        for (size_t qindex = 0, qcount = ctx.quatCount; qindex < qcount; ++qindex) {
            float3 n = *normal;
            float3 b = normalize(cross(n, float3{1, 0, 0}));
            float3 t = cross(n, b);
            writeQuat(mat3f::packTangentFrame({t, b, n}), outquat);
            normal = (const float3*) (((const uint8_t*) normal) + nstride);
            outquat += outStride;
        }
        return;
    }

    const float3* tanvec = &ctx.tangents->xyz;
    const float* tandir = &ctx.tangents->w;
    const size_t tstride = ctx.tangentsStride ? ctx.tangentsStride : sizeof(float4);

    for (size_t qindex = 0, qcount = ctx.quatCount; qindex < qcount; ++qindex) {
        float3 n = *normal;
        float3 t = *tanvec;
        float3 b = *tandir > 0 ? cross(t, n) : cross(n, t);
        writeQuat(mat3f::packTangentFrame({t, b, n}), outquat);
        normal = (const float3*) (((const uint8_t*) normal) + nstride);
        tanvec = (const float3*) (((const uint8_t*) tanvec) + tstride);
        tandir = (const float*) (((const uint8_t*) tandir) + tstride);
        outquat += ctx.outStride;
    }
}

} // namespace filament
<commit_msg>Fix potential zero stride in populateTangentQuaternions.<commit_after>/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "details/VertexBuffer.h"

#include "details/Engine.h"

#include "FilamentAPI-impl.h"

#include <math/mat3.h>
#include <math/norm.h>
#include <math/quat.h>
#include <math/vec3.h>
#include <math/vec4.h>

#include <utils/Panic.h>

namespace filament {

using namespace details;
using namespace filament::math;

struct VertexBuffer::BuilderDetails {
    VertexBuffer::Builder::AttributeData mAttributes[MAX_ATTRIBUTE_BUFFERS_COUNT];
    AttributeBitset mDeclaredAttributes;
    uint32_t mVertexCount = 0;
    uint8_t mBufferCount = 0;
};

static bool hasIntegerTarget(VertexAttribute attribute) {
    return attribute == BONE_INDICES;
}

using BuilderType = VertexBuffer;
BuilderType::Builder::Builder() noexcept = default;
BuilderType::Builder::~Builder() noexcept = default;
BuilderType::Builder::Builder(BuilderType::Builder const& rhs) noexcept = default;
BuilderType::Builder::Builder(BuilderType::Builder&& rhs) noexcept = default;
BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder const& rhs) noexcept = default;
BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder&& rhs) noexcept = default;

VertexBuffer::Builder& VertexBuffer::Builder::vertexCount(uint32_t vertexCount) noexcept {
    mImpl->mVertexCount = vertexCount;
    return *this;
}

VertexBuffer::Builder& VertexBuffer::Builder::bufferCount(uint8_t bufferCount) noexcept {
    mImpl->mBufferCount = bufferCount;
    return *this;
}

VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute attribute,
        uint8_t bufferIndex,
        AttributeType attributeType,
        uint32_t byteOffset,
        uint8_t byteStride) noexcept {

    size_t attributeSize = Driver::getElementTypeSize(attributeType);
    if (byteStride == 0) {
        byteStride = (uint8_t)attributeSize;
    }

    if (size_t(attribute) < MAX_ATTRIBUTE_BUFFERS_COUNT &&
        size_t(bufferIndex) < MAX_ATTRIBUTE_BUFFERS_COUNT) {

#ifndef NDEBUG
        if (byteOffset & 0x3) {
            utils::slog.d << "[performance] VertexBuffer::Builder::attribute() "
                             "byteOffset not multiple of 4" << utils::io::endl;
        }
        if (byteStride & 0x3) {
            utils::slog.d << "[performance] VertexBuffer::Builder::attribute() "
                             "byteStride not multiple of 4" << utils::io::endl;
        }
#endif

        AttributeData& entry = mImpl->mAttributes[attribute];
        entry.buffer = bufferIndex;
        entry.offset = byteOffset;
        entry.stride = byteStride;
        entry.type = attributeType;
        if (hasIntegerTarget(attribute)) {
            entry.flags |= Driver::Attribute::FLAG_INTEGER_TARGET;
        }
        mImpl->mDeclaredAttributes.set(attribute);
    }
    return *this;
}

VertexBuffer::Builder& VertexBuffer::Builder::normalized(VertexAttribute attribute,
        bool normalized) noexcept {
    if (size_t(attribute) < MAX_ATTRIBUTE_BUFFERS_COUNT) {
        AttributeData& entry = mImpl->mAttributes[attribute];
        if (normalized) {
            entry.flags |= Driver::Attribute::FLAG_NORMALIZED;
        } else {
            entry.flags &= ~Driver::Attribute::FLAG_NORMALIZED;
        }
    }
    return *this;
}

VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
    if (!ASSERT_PRECONDITION_NON_FATAL(mImpl->mVertexCount > 0, "vertexCount cannot be 0")) {
        return nullptr;
    }

    if (!ASSERT_PRECONDITION_NON_FATAL(mImpl->mBufferCount > 0, "bufferCount cannot be 0")) {
        return nullptr;
    }

    return upcast(engine).createVertexBuffer(*this);
}

// ------------------------------------------------------------------------------------------------

namespace details {

FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& builder)
        : mVertexCount(builder->mVertexCount), mBufferCount(builder->mBufferCount) {
    std::copy(std::begin(builder->mAttributes), std::end(builder->mAttributes), mAttributes.begin());

    mDeclaredAttributes = builder->mDeclaredAttributes;
    uint8_t attributeCount = (uint8_t) mDeclaredAttributes.count();

    Driver::AttributeArray attributeArray;

    static_assert(attributeArray.size() == MAX_ATTRIBUTE_BUFFERS_COUNT,
            "Driver::Attribute and Builder::Attribute arrays must match");

    static_assert(sizeof(Driver::Attribute) == sizeof(Builder::AttributeData),
            "Driver::Attribute and Builder::Attribute must match");

    auto const& declaredAttributes = mDeclaredAttributes;
    auto const& attributes = mAttributes;
    #pragma nounroll
    for (size_t i = 0, n = attributeArray.size(); i < n; ++i) {
        if (declaredAttributes[i]) {
            attributeArray[i].offset = attributes[i].offset;
            attributeArray[i].stride = attributes[i].stride;
            attributeArray[i].buffer = attributes[i].buffer;
            attributeArray[i].type   = attributes[i].type;
            attributeArray[i].flags  = attributes[i].flags;
        }
    }

    FEngine::DriverApi& driver = engine.getDriverApi();
    mHandle = driver.createVertexBuffer(
            mBufferCount, attributeCount, mVertexCount, attributeArray, driver::BufferUsage::STATIC);
}

void FVertexBuffer::terminate(FEngine& engine) {
    FEngine::DriverApi& driver = engine.getDriverApi();
    driver.destroyVertexBuffer(mHandle);
}

size_t FVertexBuffer::getVertexCount() const noexcept {
    return mVertexCount;
}

void FVertexBuffer::setBufferAt(FEngine& engine, uint8_t bufferIndex,
        driver::BufferDescriptor&& buffer, uint32_t byteOffset, uint32_t byteSize) {

    if (byteSize == 0) {
        byteSize = uint32_t(buffer.size);
    }

    if (bufferIndex < mBufferCount) {
        engine.getDriverApi().updateVertexBuffer(mHandle, bufferIndex,
                std::move(buffer), byteOffset, byteSize);
    } else {
        ASSERT_PRECONDITION_NON_FATAL(bufferIndex < mBufferCount,
                "bufferIndex must be < bufferCount");
    }
}

} // namespace details

// ------------------------------------------------------------------------------------------------
// Trampoline calling into private implementation
// ------------------------------------------------------------------------------------------------

using namespace filament::details;

size_t VertexBuffer::getVertexCount() const noexcept {
    return upcast(this)->getVertexCount();
}

void VertexBuffer::setBufferAt(Engine& engine, uint8_t bufferIndex,
        driver::BufferDescriptor&& buffer, uint32_t byteOffset, uint32_t byteSize) {
    upcast(this)->setBufferAt(upcast(engine), bufferIndex,
            std::move(buffer), byteOffset, byteSize);
}

void VertexBuffer::populateTangentQuaternions(const QuatTangentContext& ctx) {
    if (!ASSERT_PRECONDITION_NON_FATAL(ctx.normals, "Normals must be provided")) {
        return;
    }

    // Define a small lambda that converts fp32 into the desired output format.
    size_t outStride = ctx.outStride;
    void (*writeQuat)(quatf, uint8_t*);
    switch (ctx.quatType) {
        case HALF4:
            writeQuat = [] (quatf inquat, uint8_t* outquat) {
                *((quath*) outquat) = quath(inquat);
            };
            outStride = outStride ? outStride : sizeof(half4);
            break;
        case SHORT4:
            writeQuat = [] (quatf inquat, uint8_t* outquat) {
                *((short4*) outquat) = packSnorm16(inquat.xyzw);
            };
            outStride = outStride ? outStride : sizeof(short4);
            break;
        case FLOAT4:
            writeQuat = [] (quatf inquat, uint8_t* outquat) {
                *((quatf*) outquat) = inquat;
            };
            outStride = outStride ? outStride : sizeof(float4);
            break;
    }

    const float3* normal = ctx.normals;
    const size_t nstride = ctx.normalsStride ? ctx.normalsStride : sizeof(float3);
    uint8_t* outquat = (uint8_t*) ctx.outBuffer;

    // If tangents are not provided, simply cross N with arbitrary vector (1, 0, 0)
    if (!ctx.tangents) {
        for (size_t qindex = 0, qcount = ctx.quatCount; qindex < qcount; ++qindex) {
            float3 n = *normal;
            float3 b = normalize(cross(n, float3{1, 0, 0}));
            float3 t = cross(n, b);
            writeQuat(mat3f::packTangentFrame({t, b, n}), outquat);
            normal = (const float3*) (((const uint8_t*) normal) + nstride);
            outquat += outStride;
        }
        return;
    }

    const float3* tanvec = &ctx.tangents->xyz;
    const float* tandir = &ctx.tangents->w;
    const size_t tstride = ctx.tangentsStride ? ctx.tangentsStride : sizeof(float4);

    for (size_t qindex = 0, qcount = ctx.quatCount; qindex < qcount; ++qindex) {
        float3 n = *normal;
        float3 t = *tanvec;
        float3 b = *tandir > 0 ? cross(t, n) : cross(n, t);
        writeQuat(mat3f::packTangentFrame({t, b, n}), outquat);
        normal = (const float3*) (((const uint8_t*) normal) + nstride);
        tanvec = (const float3*) (((const uint8_t*) tanvec) + tstride);
        tandir = (const float*) (((const uint8_t*) tandir) + tstride);
        outquat += outStride;
    }
}

} // namespace filament
<|endoftext|>


<commit_before>/*
 * Copyright 2006-2008 The FLWOR Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <typeinfo>
#include "compiler/normalizer/normalizer.h"
#include "context/static_context.h"
#include "compiler/expression/expr.h"
#include "functions/signature.h"
#include "types/root_typemanager.h"
#include "types/typeops.h"
#include "system/globalenv.h"
#include <iostream>

namespace zorba {

#define LOOKUP_FN( pfx, local, arity ) (sctx->lookup_fn (pfx, local, arity))
#define LOOKUP_OP1( local ) (m_sctx->lookup_builtin_fn (":" local, 1))

static inline expr::expr_t wrap_in_bev(static_context *sctx, expr::expr_t e)
{
  expr::expr_t fh(new fo_expr(e->get_loc(), LOOKUP_FN("fn", "boolean", 1)));
  fo_expr *fp = static_cast<fo_expr *>(fh.getp());
  fp->add(e);
  return fh;
}

static inline expr::expr_t wrap_in_atomization(static_context *sctx, expr::expr_t e)
{
  expr::expr_t fh(new fo_expr(e->get_loc(), LOOKUP_FN("fn", "data", 1)));
  fo_expr *fp = static_cast<fo_expr *>(fh.getp());
  fp->add(e);
  return fh;
}

static inline expr::expr_t wrap_in_typematch(expr::expr_t e, xqtref_t type)
{
  // treat_expr should be avoided for updating expressions too,
  // but in that case "type" will be item()* anyway
  return TypeOps::is_subtype(*GENV_TYPESYSTEM.ITEM_TYPE_STAR, *type)
    ? e
    : new treat_expr (e->get_loc (), e, type, XPTY0004);
}

static inline expr::expr_t wrap_in_type_conversion(expr::expr_t e, xqtref_t type)
{
  expr::expr_t ph = new promote_expr(e->get_loc(), e, type);
  // TODO : Need to add convert_simple_operand
  return ph;
}

static inline void checkNonUpdating(const expr* lExpr)
{
  if (lExpr != 0 && lExpr->is_updating())
    ZORBA_ERROR_LOC(XUST0001, lExpr->get_loc());
}

class normalizer : public expr_visitor
{
  static_context *m_sctx;

public:
  normalizer(CompilerCB* aCompilerCB) : m_sctx(aCompilerCB->m_sctx) { }
  ~normalizer() { }

#define DEF_VISIT_METHODS( e )                  \
  bool begin_visit (e& node) { return true; }   \
  void end_visit (e& node) {}

DEF_VISIT_METHODS (expr)

#ifdef ZORBA_DEBUGGER
DEF_VISIT_METHODS (debugger_expr)
#endif

DEF_VISIT_METHODS (wrapper_expr)

DEF_VISIT_METHODS (sequential_expr)

DEF_VISIT_METHODS (var_expr)

DEF_VISIT_METHODS (order_modifier)

DEF_VISIT_METHODS (eval_expr)

DEF_VISIT_METHODS (typeswitch_expr)

void wrap_flwor_wincond(static_context* sctx, flwor_wincond* c)
{
  if (c == NULL)
    return;

  c->set_cond(wrap_in_bev(sctx, c->get_cond()));
}

bool begin_visit(flwor_expr& node)
{
  return true;
}

void end_visit(flwor_expr& node)
{
  for (unsigned i = 0; i < node.num_clauses(); i++)
  {
    flwor_clause& c = *(node[i]);

    if (c.get_kind() == flwor_clause::where_clause)
    {
      where_clause* wc = static_cast<where_clause *>(&c);
      wc->set_where(wrap_in_bev(m_sctx, wc->get_where()));
    }
    else if (c.get_kind() == flwor_clause::order_clause)
    {
      orderby_clause* obc = static_cast<orderby_clause *>(&c);

      unsigned numColumns = obc->num_columns();

      for (unsigned i = 0; i < numColumns; ++i)
      {
        obc->set_column_expr(i, wrap_in_atomization(m_sctx, obc->get_column_expr(i)));
      }
    }
    else if (c.get_kind() == flwor_clause::for_clause)
    {
      for_clause* fc = static_cast<for_clause *> (&c);

      xqtref_t vartype = fc->get_var()->get_type();
      if (vartype != NULL)
      {
        if (TypeOps::is_empty(*vartype))
          ZORBA_ERROR_LOC_PARAM(XPTY0004, fc->get_loc(), "empty-sequence()", "");

        xqtref_t promote_type = m_sctx->get_typemanager()->
                                create_type(*vartype, TypeConstants::QUANT_STAR);

        expr_t e = fc->get_expr();
        fc->set_expr(wrap_in_typematch(e, promote_type));
      }
    }
    else if (c.get_kind() == flwor_clause::let_clause)
    {
      let_clause* lc = static_cast<let_clause *> (&c);

      xqtref_t vartype = lc->get_var()->get_type();
      if (vartype != NULL)
      {
        expr_t e = lc->get_expr();
        lc->set_expr(wrap_in_typematch(e, vartype));
      }
    }
    else if (c.get_kind() == flwor_clause::window_clause)
    {
      window_clause* wc = static_cast<window_clause *> (&c);

      xqtref_t vartype = wc->get_var()->get_type();
      if (vartype != NULL)
      {
        expr_t e = wc->get_expr();
        wc->set_expr(wrap_in_typematch(e, vartype));
      }

      wrap_flwor_wincond(m_sctx, wc->get_win_start());
      wrap_flwor_wincond(m_sctx, wc->get_win_stop());
    }
  }
}

void end_visit (case_clause&)
{
}

bool begin_visit (case_clause& node)
{
  checkNonUpdating(&*node.get_var_h());
  return true;
}

void end_visit (promote_expr&) {}
bool begin_visit (promote_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

void end_visit (trycatch_expr&) {}
bool begin_visit (trycatch_expr& node)
{
  checkNonUpdating(&*node.get_try_expr());
  return true;
}

void end_visit (if_expr&) {}
bool begin_visit (if_expr& node)
{
  node.set_cond_expr(wrap_in_bev(m_sctx, node.get_cond_expr()));
  return true;
}

void end_visit (fo_expr&) {}
bool begin_visit (fo_expr& node)
{
  const signature& sign = node.get_signature();

  int n = node.size();
  for(int i = 0; i < n; ++i) {
    expr::expr_t arg = node[i];
    if (!node.is_updating())
      checkNonUpdating(&*arg);
    const xqtref_t& arg_type = sign[i];
    xqtref_t arg_prime_type = TypeOps::prime_type(*arg_type);
    if (TypeOps::is_subtype(*arg_prime_type, *GENV_TYPESYSTEM.ANY_ATOMIC_TYPE_ONE)) {
      arg = wrap_in_atomization(m_sctx, arg);
      arg = wrap_in_type_conversion(arg, arg_type);
    }
    else arg = wrap_in_typematch(arg, arg_type);

    node[i] = arg;
  }

  return true;
}

void end_visit (instanceof_expr&) {}
bool begin_visit (instanceof_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

DEF_VISIT_METHODS (treat_expr)

void end_visit (castable_expr&) {}
bool begin_visit (castable_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

void end_visit (cast_expr&) {}
bool begin_visit (cast_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

void end_visit (name_cast_expr&) {}
bool begin_visit (name_cast_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

void end_visit (validate_expr&) {}
bool begin_visit (validate_expr& node)
{
  checkNonUpdating(&*node.get_expr());
  return true;
}

void end_visit (extension_expr&) {}
bool begin_visit (extension_expr& node)
{
  checkNonUpdating(&*node.get_expr());
  return true;
}

void end_visit (relpath_expr&)
{
}

bool begin_visit (relpath_expr& node)
{
  if (node.size() > 0) {
    expr_t ie = node[0];
    expr_t pe = new treat_expr(ie->get_loc(), ie, GENV_TYPESYSTEM.ANY_NODE_TYPE_STAR, XPTY0019);
    node[0] = pe;
  }
  return true;
}

DEF_VISIT_METHODS (axis_step_expr)
DEF_VISIT_METHODS (match_expr)
DEF_VISIT_METHODS (const_expr)
DEF_VISIT_METHODS (order_expr)

void end_visit (elem_expr&) {}
bool begin_visit (elem_expr& node)
{
  //node.setQNameExpr(wrap_in_atomization(m_sctx, node.getQNameExpr()));
  checkNonUpdating(&*node.getQNameExpr());
  checkNonUpdating(&*node.getAttrs());
  checkNonUpdating(&*node.getContent());
  return true;
}

void end_visit (doc_expr&) {}
bool begin_visit (doc_expr& node)
{
  checkNonUpdating(node.getContent());
  return true;
}

void end_visit (attr_expr&) {}
bool begin_visit (attr_expr& node)
{
  assert (node.getQNameExpr() != NULL);

  checkNonUpdating(&*node.getQNameExpr());
  checkNonUpdating(&*node.getValueExpr());

  // node.setQNameExpr(wrap_in_atomization(m_sctx, node.getQNameExpr()));

  if (node.getValueExpr() != NULL) {
    rchandle<expr> lExpr = node.getValueExpr();
    fo_expr* lFoExpr = 0;
    if ((lFoExpr = dynamic_cast<fo_expr*>(&*lExpr)))
    {
      function* lTestFunc = LOOKUP_OP1("enclosed-expr");
      if (lFoExpr->get_func() == lTestFunc)
      {
        (*lFoExpr)[0] = wrap_in_atomization(m_sctx, (*lFoExpr)[0].getp());
        return true;
      }
    }
    node.setValueExpr(wrap_in_atomization(m_sctx, node.getValueExpr()));
  }

  return true;
}

#undef LOOKUP_OP1

void end_visit (text_expr&) {}
bool begin_visit (text_expr& node)
{
  checkNonUpdating(&*node.get_text());
  return true;
}

void end_visit (pi_expr&) {}
bool begin_visit (pi_expr& node)
{
  checkNonUpdating(&*node.get_target_expr());
  return true;
}

DEF_VISIT_METHODS (insert_expr)
DEF_VISIT_METHODS (delete_expr)
DEF_VISIT_METHODS (rename_expr)
DEF_VISIT_METHODS (replace_expr)
DEF_VISIT_METHODS (transform_expr)
DEF_VISIT_METHODS (exit_expr)
DEF_VISIT_METHODS (while_expr)
DEF_VISIT_METHODS (flowctl_expr)
DEF_VISIT_METHODS (function_def_expr)

DEF_VISIT_METHODS (ft_select_expr)
DEF_VISIT_METHODS (ft_contains_expr)

};

void normalize_expr_tree(
    const char* norm_descr,
    CompilerCB* aCompilerCB,
    expr_t& root,
    const XQType* rType)
{
  normalizer n (aCompilerCB);

  root->accept(n);

  if (rType != NULL)
  {
    if (TypeOps::is_simple(*rType)) {
      root = wrap_in_atomization(aCompilerCB->m_sctx, root);
      root = wrap_in_type_conversion(root, rType);
    } else {
      root = wrap_in_typematch (root, rType);
    }
  }
  if (aCompilerCB->m_config.normalize_cb)
    aCompilerCB->m_config.normalize_cb (&*root, norm_descr);
}

}

/* vim:set ts=2 sw=2: */
<commit_msg>do atomization before casting<commit_after>/*
 * Copyright 2006-2008 The FLWOR Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <typeinfo>
#include "compiler/normalizer/normalizer.h"
#include "context/static_context.h"
#include "compiler/expression/expr.h"
#include "functions/signature.h"
#include "types/root_typemanager.h"
#include "types/typeops.h"
#include "system/globalenv.h"
#include <iostream>

namespace zorba {

#define LOOKUP_FN( pfx, local, arity ) (sctx->lookup_fn (pfx, local, arity))
#define LOOKUP_OP1( local ) (m_sctx->lookup_builtin_fn (":" local, 1))

static inline expr::expr_t wrap_in_bev(static_context *sctx, expr::expr_t e)
{
  expr::expr_t fh(new fo_expr(e->get_loc(), LOOKUP_FN("fn", "boolean", 1)));
  fo_expr *fp = static_cast<fo_expr *>(fh.getp());
  fp->add(e);
  return fh;
}

static inline expr::expr_t wrap_in_atomization(static_context *sctx, expr::expr_t e)
{
  expr::expr_t fh(new fo_expr(e->get_loc(), LOOKUP_FN("fn", "data", 1)));
  fo_expr *fp = static_cast<fo_expr *>(fh.getp());
  fp->add(e);
  return fh;
}

static inline expr::expr_t wrap_in_typematch(expr::expr_t e, xqtref_t type)
{
  // treat_expr should be avoided for updating expressions too,
  // but in that case "type" will be item()* anyway
  return TypeOps::is_subtype(*GENV_TYPESYSTEM.ITEM_TYPE_STAR, *type)
    ? e
    : new treat_expr (e->get_loc (), e, type, XPTY0004);
}

static inline expr::expr_t wrap_in_type_conversion(expr::expr_t e, xqtref_t type)
{
  expr::expr_t ph = new promote_expr(e->get_loc(), e, type);
  // TODO : Need to add convert_simple_operand
  return ph;
}

static inline void checkNonUpdating(const expr* lExpr)
{
  if (lExpr != 0 && lExpr->is_updating())
    ZORBA_ERROR_LOC(XUST0001, lExpr->get_loc());
}

class normalizer : public expr_visitor
{
  static_context *m_sctx;

public:
  normalizer(CompilerCB* aCompilerCB) : m_sctx(aCompilerCB->m_sctx) { }
  ~normalizer() { }

#define DEF_VISIT_METHODS( e )                  \
  bool begin_visit (e& node) { return true; }   \
  void end_visit (e& node) {}

DEF_VISIT_METHODS (expr)

#ifdef ZORBA_DEBUGGER
DEF_VISIT_METHODS (debugger_expr)
#endif

DEF_VISIT_METHODS (wrapper_expr)

DEF_VISIT_METHODS (sequential_expr)

DEF_VISIT_METHODS (var_expr)

DEF_VISIT_METHODS (order_modifier)

DEF_VISIT_METHODS (eval_expr)

DEF_VISIT_METHODS (typeswitch_expr)

void wrap_flwor_wincond(static_context* sctx, flwor_wincond* c)
{
  if (c == NULL)
    return;

  c->set_cond(wrap_in_bev(sctx, c->get_cond()));
}

bool begin_visit(flwor_expr& node)
{
  return true;
}

void end_visit(flwor_expr& node)
{
  for (unsigned i = 0; i < node.num_clauses(); i++)
  {
    flwor_clause& c = *(node[i]);

    if (c.get_kind() == flwor_clause::where_clause)
    {
      where_clause* wc = static_cast<where_clause *>(&c);
      wc->set_where(wrap_in_bev(m_sctx, wc->get_where()));
    }
    else if (c.get_kind() == flwor_clause::order_clause)
    {
      orderby_clause* obc = static_cast<orderby_clause *>(&c);

      unsigned numColumns = obc->num_columns();

      for (unsigned i = 0; i < numColumns; ++i)
      {
        obc->set_column_expr(i, wrap_in_atomization(m_sctx, obc->get_column_expr(i)));
      }
    }
    else if (c.get_kind() == flwor_clause::for_clause)
    {
      for_clause* fc = static_cast<for_clause *> (&c);

      xqtref_t vartype = fc->get_var()->get_type();
      if (vartype != NULL)
      {
        if (TypeOps::is_empty(*vartype))
          ZORBA_ERROR_LOC_PARAM(XPTY0004, fc->get_loc(), "empty-sequence()", "");

        xqtref_t promote_type = m_sctx->get_typemanager()->
                                create_type(*vartype, TypeConstants::QUANT_STAR);

        expr_t e = fc->get_expr();
        fc->set_expr(wrap_in_typematch(e, promote_type));
      }
    }
    else if (c.get_kind() == flwor_clause::let_clause)
    {
      let_clause* lc = static_cast<let_clause *> (&c);

      xqtref_t vartype = lc->get_var()->get_type();
      if (vartype != NULL)
      {
        expr_t e = lc->get_expr();
        lc->set_expr(wrap_in_typematch(e, vartype));
      }
    }
    else if (c.get_kind() == flwor_clause::window_clause)
    {
      window_clause* wc = static_cast<window_clause *> (&c);

      xqtref_t vartype = wc->get_var()->get_type();
      if (vartype != NULL)
      {
        expr_t e = wc->get_expr();
        wc->set_expr(wrap_in_typematch(e, vartype));
      }

      wrap_flwor_wincond(m_sctx, wc->get_win_start());
      wrap_flwor_wincond(m_sctx, wc->get_win_stop());
    }
  }
}

void end_visit (case_clause&)
{
}

bool begin_visit (case_clause& node)
{
  checkNonUpdating(&*node.get_var_h());
  return true;
}

void end_visit (promote_expr&) {}
bool begin_visit (promote_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

void end_visit (trycatch_expr&) {}
bool begin_visit (trycatch_expr& node)
{
  checkNonUpdating(&*node.get_try_expr());
  return true;
}

void end_visit (if_expr&) {}
bool begin_visit (if_expr& node)
{
  node.set_cond_expr(wrap_in_bev(m_sctx, node.get_cond_expr()));
  return true;
}

void end_visit (fo_expr&) {}
bool begin_visit (fo_expr& node)
{
  const signature& sign = node.get_signature();

  int n = node.size();
  for(int i = 0; i < n; ++i) {
    expr::expr_t arg = node[i];
    if (!node.is_updating())
      checkNonUpdating(&*arg);
    const xqtref_t& arg_type = sign[i];
    xqtref_t arg_prime_type = TypeOps::prime_type(*arg_type);
    if (TypeOps::is_subtype(*arg_prime_type, *GENV_TYPESYSTEM.ANY_ATOMIC_TYPE_ONE)) {
      arg = wrap_in_atomization(m_sctx, arg);
      arg = wrap_in_type_conversion(arg, arg_type);
    }
    else arg = wrap_in_typematch(arg, arg_type);

    node[i] = arg;
  }

  return true;
}

void end_visit (instanceof_expr&) {}
bool begin_visit (instanceof_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

DEF_VISIT_METHODS (treat_expr)

void end_visit (castable_expr&)
{
}

bool begin_visit (castable_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

void end_visit (cast_expr&)
{
}

bool begin_visit (cast_expr& node)
{
  checkNonUpdating(&*node.get_input());

  expr_t arg = wrap_in_atomization(m_sctx, node.get_input());

  node.set_input(arg);

  return true;
}

void end_visit (name_cast_expr&)
{
}

bool begin_visit (name_cast_expr& node)
{
  checkNonUpdating(&*node.get_input());
  return true;
}

void end_visit (validate_expr&)
{
}

bool begin_visit (validate_expr& node)
{
  checkNonUpdating(&*node.get_expr());
  return true;
}

void end_visit (extension_expr&)
{
}

bool begin_visit (extension_expr& node)
{
  checkNonUpdating(&*node.get_expr());
  return true;
}

void end_visit (relpath_expr&)
{
}

bool begin_visit (relpath_expr& node)
{
  if (node.size() > 0) {
    expr_t ie = node[0];
    expr_t pe = new treat_expr(ie->get_loc(), ie, GENV_TYPESYSTEM.ANY_NODE_TYPE_STAR, XPTY0019);
    node[0] = pe;
  }
  return true;
}

DEF_VISIT_METHODS (axis_step_expr)
DEF_VISIT_METHODS (match_expr)
DEF_VISIT_METHODS (const_expr)
DEF_VISIT_METHODS (order_expr)

void end_visit (elem_expr&) {}
bool begin_visit (elem_expr& node)
{
  //node.setQNameExpr(wrap_in_atomization(m_sctx, node.getQNameExpr()));
  checkNonUpdating(&*node.getQNameExpr());
  checkNonUpdating(&*node.getAttrs());
  checkNonUpdating(&*node.getContent());
  return true;
}

void end_visit (doc_expr&) {}
bool begin_visit (doc_expr& node)
{
  checkNonUpdating(node.getContent());
  return true;
}

void end_visit (attr_expr&) {}
bool begin_visit (attr_expr& node)
{
  assert (node.getQNameExpr() != NULL);

  checkNonUpdating(&*node.getQNameExpr());
  checkNonUpdating(&*node.getValueExpr());

  // node.setQNameExpr(wrap_in_atomization(m_sctx, node.getQNameExpr()));

  if (node.getValueExpr() != NULL) {
    rchandle<expr> lExpr = node.getValueExpr();
    fo_expr* lFoExpr = 0;
    if ((lFoExpr = dynamic_cast<fo_expr*>(&*lExpr)))
    {
      function* lTestFunc = LOOKUP_OP1("enclosed-expr");
      if (lFoExpr->get_func() == lTestFunc)
      {
        (*lFoExpr)[0] = wrap_in_atomization(m_sctx, (*lFoExpr)[0].getp());
        return true;
      }
    }
    node.setValueExpr(wrap_in_atomization(m_sctx, node.getValueExpr()));
  }

  return true;
}

#undef LOOKUP_OP1

void end_visit (text_expr&) {}
bool begin_visit (text_expr& node)
{
  checkNonUpdating(&*node.get_text());
  return true;
}

void end_visit (pi_expr&) {}
bool begin_visit (pi_expr& node)
{
  checkNonUpdating(&*node.get_target_expr());
  return true;
}

DEF_VISIT_METHODS (insert_expr)
DEF_VISIT_METHODS (delete_expr)
DEF_VISIT_METHODS (rename_expr)
DEF_VISIT_METHODS (replace_expr)
DEF_VISIT_METHODS (transform_expr)
DEF_VISIT_METHODS (exit_expr)
DEF_VISIT_METHODS (while_expr)
DEF_VISIT_METHODS (flowctl_expr)
DEF_VISIT_METHODS (function_def_expr)

DEF_VISIT_METHODS (ft_select_expr)
DEF_VISIT_METHODS (ft_contains_expr)

};

void normalize_expr_tree(
    const char* norm_descr,
    CompilerCB* aCompilerCB,
    expr_t& root,
    const XQType* rType)
{
  normalizer n (aCompilerCB);

  root->accept(n);

  if (rType != NULL)
  {
    if (TypeOps::is_builtin_simple(*rType))
    {
      root = wrap_in_atomization(aCompilerCB->m_sctx, root);
      root = wrap_in_type_conversion(root, rType);
    }
    else
    {
      root = wrap_in_typematch (root, rType);
    }
  }

  if (aCompilerCB->m_config.normalize_cb)
    aCompilerCB->m_config.normalize_cb (&*root, norm_descr);
}

}

/* vim:set ts=2 sw=2: */
<|endoftext|>