<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 "ash/wm/workspace/multi_window_resize_controller.h"

#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/root_window_event_filter.h"
#include "ash/wm/workspace/workspace_event_filter.h"
#include "ash/wm/workspace/workspace_window_resizer.h"
#include "ui/aura/event_filter.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/base/hit_test.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/canvas_skia.h"
#include "ui/gfx/screen.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

using aura::Window;

namespace ash {
namespace internal {

namespace {

const int kShowDelayMS = 100;

// Padding from the bottom/right edge the resize widget is shown at.
const int kResizeWidgetPadding = 40;

bool ContainsX(Window* window, int x) {
  return window->bounds().x() <= x && window->bounds().right() >= x;
}

bool ContainsY(Window* window, int y) {
  return window->bounds().y() <= y && window->bounds().bottom() >= y;
}

}  // namespace

// View contained in the widget. Passes along mouse events to the
// MultiWindowResizeController so that it can start/stop the resize loop.
class MultiWindowResizeController::ResizeView : public views::View {
 public:
  explicit ResizeView(MultiWindowResizeController* controller,
                      Direction direction)
      : controller_(controller),
        direction_(direction) {
  }

  // views::View overrides:
  virtual gfx::Size GetPreferredSize() OVERRIDE {
    return gfx::Size(30, 30);
  }
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
    // TODO: replace with real assets.
    SkPaint paint;
    paint.setColor(SkColorSetARGB(128, 0, 0, 0));
    paint.setStyle(SkPaint::kFill_Style);
    paint.setAntiAlias(true);
    canvas->AsCanvasSkia()->GetSkCanvas()->drawCircle(
        SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(15), paint);
  }
  virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE {
    gfx::Point location(event.location());
    views::View::ConvertPointToScreen(this, &location);
    controller_->StartResize(location);
    return true;
  }
  virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE {
    gfx::Point location(event.location());
    views::View::ConvertPointToScreen(this, &location);
    controller_->Resize(location);
    return true;
  }
  virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE {
    controller_->CompleteResize();
  }
  virtual void OnMouseCaptureLost() OVERRIDE {
    controller_->CancelResize();
  }
  virtual gfx::NativeCursor GetCursor(
      const views::MouseEvent& event) OVERRIDE {
    int component = (direction_ == LEFT_RIGHT) ? HTRIGHT : HTBOTTOM;
    return RootWindowEventFilter::CursorForWindowComponent(component);
  }

 private:
  MultiWindowResizeController* controller_;
  const Direction direction_;

  DISALLOW_COPY_AND_ASSIGN(ResizeView);
};

// MouseWatcherHost implementation for MultiWindowResizeController. Forwards
// Contains() to MultiWindowResizeController.
class MultiWindowResizeController::ResizeMouseWatcherHost :
   public views::MouseWatcherHost {
 public:
  ResizeMouseWatcherHost(MultiWindowResizeController* host) : host_(host) {}

  // MouseWatcherHost overrides:
  virtual bool Contains(const gfx::Point& screen_point,
                        MouseEventType type) OVERRIDE {
    return !host_->IsOverWindows(screen_point);
  }

 private:
  MultiWindowResizeController* host_;

  DISALLOW_COPY_AND_ASSIGN(ResizeMouseWatcherHost);
};

MultiWindowResizeController::ResizeWindows::ResizeWindows()
    : window1(NULL),
      window2(NULL),
      direction(TOP_BOTTOM){
}

MultiWindowResizeController::ResizeWindows::~ResizeWindows() {
}

bool MultiWindowResizeController::ResizeWindows::Equals(
    const ResizeWindows& other) const {
  return window1 == other.window1 &&
      window2 == other.window2 &&
      direction == other.direction;
}

MultiWindowResizeController::MultiWindowResizeController()
    : resize_widget_(NULL),
      grid_size_(0) {
}

MultiWindowResizeController::~MultiWindowResizeController() {
  Hide();
}

void MultiWindowResizeController::Show(Window* window,
                                       int component,
                                       const gfx::Point& point) {
  ResizeWindows windows(DetermineWindows(window, component, point));
  if (IsShowing()) {
    if (windows_.Equals(windows))
      return;
    Hide();
    // Fall through to see if there is another hot spot we should show at.
  }

  windows_ = windows;
  if (!windows_.is_valid())
    return;
  // TODO: need to listen for windows to be closed/destroyed, maybe window
  // moved too.
  show_timer_.Start(FROM_HERE,
                    base::TimeDelta::FromMilliseconds(kShowDelayMS),
                    this, &MultiWindowResizeController::ShowNow);
}

void MultiWindowResizeController::Hide() {
  if (window_resizer_.get())
    return;  // Ignore hides while actively resizing.

  show_timer_.Stop();
  if (!resize_widget_)
    return;
  resize_widget_->Close();
  resize_widget_ = NULL;
  windows_ = ResizeWindows();
}

void MultiWindowResizeController::MouseMovedOutOfHost() {
  Hide();
}

MultiWindowResizeController::ResizeWindows
MultiWindowResizeController::DetermineWindows(
    Window* window,
    int window_component,
    const gfx::Point& point) const {
  ResizeWindows result;
  gfx::Point point_in_parent(point);
  Window::ConvertPointToWindow(window, window->parent(), &point_in_parent);
  switch (window_component) {
    case HTRIGHT:
      result.direction = LEFT_RIGHT;
      result.window1 = window;
      result.window2 = FindWindowByEdge(
          window, HTLEFT, window->bounds().right(), point_in_parent.y());
      break;
    case HTLEFT:
      result.direction = LEFT_RIGHT;
      result.window1 = FindWindowByEdge(
          window, HTRIGHT, window->bounds().x(), point_in_parent.y());
      result.window2 = window;
      break;
    case HTTOP:
      result.direction = TOP_BOTTOM;
      result.window1 = FindWindowByEdge(
          window, HTBOTTOM, point_in_parent.x(), window->bounds().y());
      result.window2 = window;
      break;
    case HTBOTTOM:
      result.direction = TOP_BOTTOM;
      result.window1 = window;
      result.window2 = FindWindowByEdge(
          window, HTTOP, point_in_parent.x(), window->bounds().bottom());
      break;
    default:
      break;
  }
  return result;
}

Window* MultiWindowResizeController::FindWindowByEdge(
    Window* window_to_ignore,
    int edge_want,
    int x,
    int y) const {
  Window* parent = window_to_ignore->parent();
  const Window::Windows& windows(parent->children());
  for (Window::Windows::const_reverse_iterator i = windows.rbegin();
       i != windows.rend(); ++i) {
    Window* window = *i;
    if (window == window_to_ignore || !window->IsVisible())
      continue;
    switch (edge_want) {
      case HTLEFT:
        if (ContainsY(window, y) && window->bounds().x() == x)
          return window;
        break;
      case HTRIGHT:
        if (ContainsY(window, y) && window->bounds().right() == x)
          return window;
        break;
      case HTTOP:
        if (ContainsX(window, x) && window->bounds().y() == y)
          return window;
        break;
      case HTBOTTOM:
        if (ContainsX(window, x) && window->bounds().bottom() == y)
          return window;
        break;
      default:
        NOTREACHED();
    }
    // Window doesn't contain the edge, but if window contains |point|
    // it's obscuring any other window that could be at the location.
    if (window->bounds().Contains(x, y))
      return NULL;
  }
  return NULL;
}

void MultiWindowResizeController::ShowNow() {
  DCHECK(!resize_widget_);
  DCHECK(windows_.is_valid());
  show_timer_.Stop();
  resize_widget_ = new views::Widget;
  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  params.transparent = true;
  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params.parent = Shell::GetInstance()->GetContainer(
      ash::internal::kShellWindowId_AlwaysOnTopContainer);
  params.can_activate = false;
  ResizeView* view = new ResizeView(this, windows_.direction);
  params.delegate = new views::WidgetDelegateView;
  resize_widget_->set_focus_on_creation(false);
  resize_widget_->Init(params);
  resize_widget_->GetNativeWindow()->SetName("MultiWindowResizeController");
  resize_widget_->SetContentsView(view);
  show_bounds_ = CalculateResizeWidgetBounds();
  resize_widget_->SetBounds(show_bounds_);
  resize_widget_->Show();
}

bool MultiWindowResizeController::IsShowing() const {
  return resize_widget_ || show_timer_.IsRunning();
}

void MultiWindowResizeController::StartResize(
    const gfx::Point& screen_location) {
  DCHECK(!window_resizer_.get());
  DCHECK(windows_.is_valid());
  gfx::Point parent_location(screen_location);
  aura::Window::ConvertPointToWindow(
      windows_.window1->GetRootWindow(), windows_.window1->parent(),
      &parent_location);
  std::vector<aura::Window*> windows;
  windows.push_back(windows_.window2);
  // TODO: search for other windows.
  int component = windows_.direction == LEFT_RIGHT ? HTRIGHT : HTBOTTOM;
  window_resizer_.reset(WorkspaceWindowResizer::Create(
      windows_.window1, parent_location, component, grid_size_, windows));
}

void MultiWindowResizeController::Resize(const gfx::Point& screen_location) {
  gfx::Point parent_location(screen_location);
  aura::Window::ConvertPointToWindow(windows_.window1->GetRootWindow(),
                                     windows_.window1->parent(),
                                     &parent_location);
  window_resizer_->Drag(parent_location);
  gfx::Rect bounds = CalculateResizeWidgetBounds();
  if (windows_.direction == LEFT_RIGHT)
    bounds.set_y(show_bounds_.y());
  else
    bounds.set_x(show_bounds_.x());
  resize_widget_->SetBounds(bounds);
}

void MultiWindowResizeController::CompleteResize() {
  window_resizer_->CompleteDrag();
  window_resizer_.reset();

  // Mouse may still be over resizer, if not hide.
  gfx::Point screen_loc = gfx::Screen::GetCursorScreenPoint();
  if (!resize_widget_->GetWindowScreenBounds().Contains(screen_loc))
    Hide();
}

void MultiWindowResizeController::CancelResize() {
  window_resizer_->RevertDrag();
  window_resizer_.reset();
  Hide();
}

gfx::Rect MultiWindowResizeController::CalculateResizeWidgetBounds() const {
  gfx::Size pref = resize_widget_->GetContentsView()->GetPreferredSize();
  int x = 0, y = 0;
  if (windows_.direction == LEFT_RIGHT) {
    x = windows_.window1->bounds().right() - pref.width() / 2;
    y = std::min(windows_.window1->bounds().bottom(),
                 windows_.window2->bounds().bottom()) - kResizeWidgetPadding;
    y -= pref.height() / 2;
  } else {
    x = std::min(windows_.window1->bounds().right(),
                 windows_.window2->bounds().right()) - kResizeWidgetPadding;
    x -= pref.width() / 2;
    y = windows_.window1->bounds().bottom() - pref.height() / 2;
  }
  return gfx::Rect(x, y, pref.width(), pref.height());
}

bool MultiWindowResizeController::IsOverWindows(
    const gfx::Point& screen_location) const {
  if (window_resizer_.get())
    return true;  // Ignore hides while actively resizing.

  if (resize_widget_->GetWindowScreenBounds().Contains(screen_location))
    return true;

  return IsOverWindow(windows_.window1, screen_location) ||
      IsOverWindow(windows_.window2, screen_location);
}

bool MultiWindowResizeController::IsOverWindow(
    aura::Window* window,
    const gfx::Point& screen_location) const {
  if (!window->GetScreenBounds().Contains(screen_location))
    return false;

  gfx::Point window_loc(screen_location);
  aura::Window::ConvertPointToWindow(
      window->GetRootWindow(), window->parent(), &window_loc);
  int component = window->delegate()->GetNonClientComponent(window_loc);
  // TODO: this needs to make sure no other window is obscuring window.
  return windows_.Equals(DetermineWindows(window, component, window_loc));
}

}  // namespace internal
}  // namespace ash
<commit_msg>Wires up using images in multi window resize controller.<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 "ash/wm/workspace/multi_window_resize_controller.h"

#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/root_window_event_filter.h"
#include "ash/wm/workspace/workspace_event_filter.h"
#include "ash/wm/workspace/workspace_window_resizer.h"
#include "grit/ui_resources.h"
#include "ui/aura/event_filter.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/base/hit_test.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/screen.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

using aura::Window;

namespace ash {
namespace internal {

namespace {

const int kShowDelayMS = 100;

// Padding from the bottom/right edge the resize widget is shown at.
const int kResizeWidgetPadding = 40;

bool ContainsX(Window* window, int x) {
  return window->bounds().x() <= x && window->bounds().right() >= x;
}

bool ContainsY(Window* window, int y) {
  return window->bounds().y() <= y && window->bounds().bottom() >= y;
}

}  // namespace

// View contained in the widget. Passes along mouse events to the
// MultiWindowResizeController so that it can start/stop the resize loop.
class MultiWindowResizeController::ResizeView : public views::View {
 public:
  explicit ResizeView(MultiWindowResizeController* controller,
                      Direction direction)
      : controller_(controller),
        direction_(direction),
        image_(NULL) {
    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    int image_id =
        direction == TOP_BOTTOM ? IDR_AURA_MULTI_WINDOW_RESIZE_H :
                                  IDR_AURA_MULTI_WINDOW_RESIZE_V;
    image_ = rb.GetImageNamed(image_id).ToSkBitmap();
  }

  // views::View overrides:
  virtual gfx::Size GetPreferredSize() OVERRIDE {
    return gfx::Size(image_->width(), image_->height());
  }
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
    canvas->DrawBitmapInt(*image_, 0, 0);
  }
  virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE {
    gfx::Point location(event.location());
    views::View::ConvertPointToScreen(this, &location);
    controller_->StartResize(location);
    return true;
  }
  virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE {
    gfx::Point location(event.location());
    views::View::ConvertPointToScreen(this, &location);
    controller_->Resize(location);
    return true;
  }
  virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE {
    controller_->CompleteResize();
  }
  virtual void OnMouseCaptureLost() OVERRIDE {
    controller_->CancelResize();
  }
  virtual gfx::NativeCursor GetCursor(
      const views::MouseEvent& event) OVERRIDE {
    int component = (direction_ == LEFT_RIGHT) ? HTRIGHT : HTBOTTOM;
    return RootWindowEventFilter::CursorForWindowComponent(component);
  }

 private:
  MultiWindowResizeController* controller_;
  const Direction direction_;
  const SkBitmap* image_;

  DISALLOW_COPY_AND_ASSIGN(ResizeView);
};

// MouseWatcherHost implementation for MultiWindowResizeController. Forwards
// Contains() to MultiWindowResizeController.
class MultiWindowResizeController::ResizeMouseWatcherHost :
   public views::MouseWatcherHost {
 public:
  ResizeMouseWatcherHost(MultiWindowResizeController* host) : host_(host) {}

  // MouseWatcherHost overrides:
  virtual bool Contains(const gfx::Point& screen_point,
                        MouseEventType type) OVERRIDE {
    return !host_->IsOverWindows(screen_point);
  }

 private:
  MultiWindowResizeController* host_;

  DISALLOW_COPY_AND_ASSIGN(ResizeMouseWatcherHost);
};

MultiWindowResizeController::ResizeWindows::ResizeWindows()
    : window1(NULL),
      window2(NULL),
      direction(TOP_BOTTOM){
}

MultiWindowResizeController::ResizeWindows::~ResizeWindows() {
}

bool MultiWindowResizeController::ResizeWindows::Equals(
    const ResizeWindows& other) const {
  return window1 == other.window1 &&
      window2 == other.window2 &&
      direction == other.direction;
}

MultiWindowResizeController::MultiWindowResizeController()
    : resize_widget_(NULL),
      grid_size_(0) {
}

MultiWindowResizeController::~MultiWindowResizeController() {
  Hide();
}

void MultiWindowResizeController::Show(Window* window,
                                       int component,
                                       const gfx::Point& point) {
  ResizeWindows windows(DetermineWindows(window, component, point));
  if (IsShowing()) {
    if (windows_.Equals(windows))
      return;
    Hide();
    // Fall through to see if there is another hot spot we should show at.
  }

  windows_ = windows;
  if (!windows_.is_valid())
    return;
  // TODO: need to listen for windows to be closed/destroyed, maybe window
  // moved too.
  show_timer_.Start(FROM_HERE,
                    base::TimeDelta::FromMilliseconds(kShowDelayMS),
                    this, &MultiWindowResizeController::ShowNow);
}

void MultiWindowResizeController::Hide() {
  if (window_resizer_.get())
    return;  // Ignore hides while actively resizing.

  show_timer_.Stop();
  if (!resize_widget_)
    return;
  resize_widget_->Close();
  resize_widget_ = NULL;
  windows_ = ResizeWindows();
}

void MultiWindowResizeController::MouseMovedOutOfHost() {
  Hide();
}

MultiWindowResizeController::ResizeWindows
MultiWindowResizeController::DetermineWindows(
    Window* window,
    int window_component,
    const gfx::Point& point) const {
  ResizeWindows result;
  gfx::Point point_in_parent(point);
  Window::ConvertPointToWindow(window, window->parent(), &point_in_parent);
  switch (window_component) {
    case HTRIGHT:
      result.direction = LEFT_RIGHT;
      result.window1 = window;
      result.window2 = FindWindowByEdge(
          window, HTLEFT, window->bounds().right(), point_in_parent.y());
      break;
    case HTLEFT:
      result.direction = LEFT_RIGHT;
      result.window1 = FindWindowByEdge(
          window, HTRIGHT, window->bounds().x(), point_in_parent.y());
      result.window2 = window;
      break;
    case HTTOP:
      result.direction = TOP_BOTTOM;
      result.window1 = FindWindowByEdge(
          window, HTBOTTOM, point_in_parent.x(), window->bounds().y());
      result.window2 = window;
      break;
    case HTBOTTOM:
      result.direction = TOP_BOTTOM;
      result.window1 = window;
      result.window2 = FindWindowByEdge(
          window, HTTOP, point_in_parent.x(), window->bounds().bottom());
      break;
    default:
      break;
  }
  return result;
}

Window* MultiWindowResizeController::FindWindowByEdge(
    Window* window_to_ignore,
    int edge_want,
    int x,
    int y) const {
  Window* parent = window_to_ignore->parent();
  const Window::Windows& windows(parent->children());
  for (Window::Windows::const_reverse_iterator i = windows.rbegin();
       i != windows.rend(); ++i) {
    Window* window = *i;
    if (window == window_to_ignore || !window->IsVisible())
      continue;
    switch (edge_want) {
      case HTLEFT:
        if (ContainsY(window, y) && window->bounds().x() == x)
          return window;
        break;
      case HTRIGHT:
        if (ContainsY(window, y) && window->bounds().right() == x)
          return window;
        break;
      case HTTOP:
        if (ContainsX(window, x) && window->bounds().y() == y)
          return window;
        break;
      case HTBOTTOM:
        if (ContainsX(window, x) && window->bounds().bottom() == y)
          return window;
        break;
      default:
        NOTREACHED();
    }
    // Window doesn't contain the edge, but if window contains |point|
    // it's obscuring any other window that could be at the location.
    if (window->bounds().Contains(x, y))
      return NULL;
  }
  return NULL;
}

void MultiWindowResizeController::ShowNow() {
  DCHECK(!resize_widget_);
  DCHECK(windows_.is_valid());
  show_timer_.Stop();
  resize_widget_ = new views::Widget;
  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  params.transparent = true;
  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params.parent = Shell::GetInstance()->GetContainer(
      ash::internal::kShellWindowId_AlwaysOnTopContainer);
  params.can_activate = false;
  ResizeView* view = new ResizeView(this, windows_.direction);
  params.delegate = new views::WidgetDelegateView;
  resize_widget_->set_focus_on_creation(false);
  resize_widget_->Init(params);
  resize_widget_->GetNativeWindow()->SetName("MultiWindowResizeController");
  resize_widget_->SetContentsView(view);
  show_bounds_ = CalculateResizeWidgetBounds();
  resize_widget_->SetBounds(show_bounds_);
  resize_widget_->Show();
}

bool MultiWindowResizeController::IsShowing() const {
  return resize_widget_ || show_timer_.IsRunning();
}

void MultiWindowResizeController::StartResize(
    const gfx::Point& screen_location) {
  DCHECK(!window_resizer_.get());
  DCHECK(windows_.is_valid());
  gfx::Point parent_location(screen_location);
  aura::Window::ConvertPointToWindow(
      windows_.window1->GetRootWindow(), windows_.window1->parent(),
      &parent_location);
  std::vector<aura::Window*> windows;
  windows.push_back(windows_.window2);
  // TODO: search for other windows.
  int component = windows_.direction == LEFT_RIGHT ? HTRIGHT : HTBOTTOM;
  window_resizer_.reset(WorkspaceWindowResizer::Create(
      windows_.window1, parent_location, component, grid_size_, windows));
}

void MultiWindowResizeController::Resize(const gfx::Point& screen_location) {
  gfx::Point parent_location(screen_location);
  aura::Window::ConvertPointToWindow(windows_.window1->GetRootWindow(),
                                     windows_.window1->parent(),
                                     &parent_location);
  window_resizer_->Drag(parent_location);
  gfx::Rect bounds = CalculateResizeWidgetBounds();
  if (windows_.direction == LEFT_RIGHT)
    bounds.set_y(show_bounds_.y());
  else
    bounds.set_x(show_bounds_.x());
  resize_widget_->SetBounds(bounds);
}

void MultiWindowResizeController::CompleteResize() {
  window_resizer_->CompleteDrag();
  window_resizer_.reset();

  // Mouse may still be over resizer, if not hide.
  gfx::Point screen_loc = gfx::Screen::GetCursorScreenPoint();
  if (!resize_widget_->GetWindowScreenBounds().Contains(screen_loc))
    Hide();
}

void MultiWindowResizeController::CancelResize() {
  window_resizer_->RevertDrag();
  window_resizer_.reset();
  Hide();
}

gfx::Rect MultiWindowResizeController::CalculateResizeWidgetBounds() const {
  gfx::Size pref = resize_widget_->GetContentsView()->GetPreferredSize();
  int x = 0, y = 0;
  if (windows_.direction == LEFT_RIGHT) {
    x = windows_.window1->bounds().right() - pref.width() / 2;
    y = std::min(windows_.window1->bounds().bottom(),
                 windows_.window2->bounds().bottom()) - kResizeWidgetPadding;
    y -= pref.height() / 2;
  } else {
    x = std::min(windows_.window1->bounds().right(),
                 windows_.window2->bounds().right()) - kResizeWidgetPadding;
    x -= pref.width() / 2;
    y = windows_.window1->bounds().bottom() - pref.height() / 2;
  }
  return gfx::Rect(x, y, pref.width(), pref.height());
}

bool MultiWindowResizeController::IsOverWindows(
    const gfx::Point& screen_location) const {
  if (window_resizer_.get())
    return true;  // Ignore hides while actively resizing.

  if (resize_widget_->GetWindowScreenBounds().Contains(screen_location))
    return true;

  return IsOverWindow(windows_.window1, screen_location) ||
      IsOverWindow(windows_.window2, screen_location);
}

bool MultiWindowResizeController::IsOverWindow(
    aura::Window* window,
    const gfx::Point& screen_location) const {
  if (!window->GetScreenBounds().Contains(screen_location))
    return false;

  gfx::Point window_loc(screen_location);
  aura::Window::ConvertPointToWindow(
      window->GetRootWindow(), window->parent(), &window_loc);
  int component = window->delegate()->GetNonClientComponent(window_loc);
  // TODO: this needs to make sure no other window is obscuring window.
  return windows_.Equals(DetermineWindows(window, component, window_loc));
}

}  // namespace internal
}  // namespace ash
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Kamil Michalak <kmichalak8@gmail.com>
 *
 * 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 <gtest/gtest.h>
#include "test/inc/restclienttest.hpp"
#include "inc/restclient.hpp"

void RestClientTest::SetUp()
{
	get_url = "http://echo.jsontest.com/value/get";
	put_url = "http://httpbin.org/put";
	post_url = "http://httpbin.org/post";
	content_type = "application/json";
	client = new jippi::RestClient;
}

//---------------------------------------------
// GET
//---------------------------------------------

TEST_F(RestClientTest, ResponseShouldContain200CodeWhenGetSucceed)
{
	// when
	jippi::rest_response response = client->doHttpGet(get_url);
	// then
	EXPECT_EQ(200, response.code);
}

TEST_F(RestClientTest, ResponseShouldContainValidResponseBodyWhenGetSucceed)
{
	// when
	jippi::rest_response response = client->doHttpGet(get_url);
	// then
	EXPECT_EQ("{\"value\": \"get\"}\n", response.body);
}

TEST_F(RestClientTest, ResponseShouldContainValidResponseCodeWhenGetFails)
{
	// when
	jippi::rest_response response = client->doHttpGet("http://nonexisting.org");
	// then
	EXPECT_EQ(-1, response.code);
}

//---------------------------------------------
// PUT
//---------------------------------------------

TEST_F(RestClientTest, ResponseShouldContain200CodeWhenPutSucceed)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPut(put_url, content_type, data);
	// then
	EXPECT_EQ(200, response.code);
}

TEST_F(RestClientTest, ResponseShouldContainValidResponseBodyWhenPutSucceed)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPut(put_url, content_type, data);
	size_t data_content_position = response.body.find(data);
	// then
 	ASSERT_TRUE(data_content_position != std::string::npos);
}

TEST_F(RestClientTest, ResponseShouldContainValidCodeWhenPUTFails)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPut("http://nonexisting.org", content_type, data);
	// then
	EXPECT_EQ(-1, response.code);
}

//---------------------------------------------
// POST
//---------------------------------------------

TEST_F(RestClientTest, ResponseShouldContainValidResponseCodeWhenPostSucceed)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPost(post_url, content_type, data);
	// then
	EXPECT_EQ(200, response.code);
}

TEST_F(RestClientTest, ResponseShouldContainValidResponseBodyWhenPostSucceed)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPost(post_url, content_type, data);
	size_t data_content_position = response.body.find(data);
	// then
	ASSERT_TRUE(data_content_position != std::string::npos);
}<commit_msg>Fix typo in test method name<commit_after>/*
 * Copyright 2013 Kamil Michalak <kmichalak8@gmail.com>
 *
 * 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 <gtest/gtest.h>
#include "test/inc/restclienttest.hpp"
#include "inc/restclient.hpp"

void RestClientTest::SetUp()
{
	get_url = "http://echo.jsontest.com/value/get";
	put_url = "http://httpbin.org/put";
	post_url = "http://httpbin.org/post";
	content_type = "application/json";
	client = new jippi::RestClient;
}

//---------------------------------------------
// GET
//---------------------------------------------

TEST_F(RestClientTest, ResponseShouldContain200CodeWhenGetSucceed)
{
	// when
	jippi::rest_response response = client->doHttpGet(get_url);
	// then
	EXPECT_EQ(200, response.code);
}

TEST_F(RestClientTest, ResponseShouldContainValidResponseBodyWhenGetSucceed)
{
	// when
	jippi::rest_response response = client->doHttpGet(get_url);
	// then
	EXPECT_EQ("{\"value\": \"get\"}\n", response.body);
}

TEST_F(RestClientTest, ResponseShouldContainValidResponseCodeWhenGetFails)
{
	// when
	jippi::rest_response response = client->doHttpGet("http://nonexisting.org");
	// then
	EXPECT_EQ(-1, response.code);
}

//---------------------------------------------
// PUT
//---------------------------------------------

TEST_F(RestClientTest, ResponseShouldContain200CodeWhenPutSucceed)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPut(put_url, content_type, data);
	// then
	EXPECT_EQ(200, response.code);
}

TEST_F(RestClientTest, ResponseShouldContainValidResponseBodyWhenPutSucceed)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPut(put_url, content_type, data);
	size_t data_content_position = response.body.find(data);
	// then
 	ASSERT_TRUE(data_content_position != std::string::npos);
}

TEST_F(RestClientTest, ResponseShouldContainValidCodeWhenPutFails)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPut("http://nonexisting.org", content_type, data);
	// then
	EXPECT_EQ(-1, response.code);
}

//---------------------------------------------
// POST
//---------------------------------------------

TEST_F(RestClientTest, ResponseShouldContainValidResponseCodeWhenPostSucceed)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPost(post_url, content_type, data);
	// then
	EXPECT_EQ(200, response.code);
}

TEST_F(RestClientTest, ResponseShouldContainValidResponseBodyWhenPostSucceed)
{
	// given
	std::string data = "testdata";
	// when
	jippi::rest_response response = client->doHttpPost(post_url, content_type, data);
	size_t data_content_position = response.body.find(data);
	// then
	ASSERT_TRUE(data_content_position != std::string::npos);
}<|endoftext|>


<commit_before>#include<stdlib.h>

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

#include <cmath>
#include <algorithm>

using namespace std;

class Stat {
private:
	double sum, sum2;
	int n;
public:
	double min, max;
	double sigma, mu;
	Stat() {reset();}
	void add(double x) {
		++n;
		sum += x;
		sum2 += pow(x,2);

		if ( x < min ) min = x;
		if ( x > max ) max = x;
	}
	void calc() {
		if ( n < 2 ) return;
		mu = sum / n;
		sigma = sqrt( (sum2 - n * pow(mu,2) ) / (n-1) );
	}
	void reset() {
		sum = sum2 = sigma = mu = 0;
		n=0;
		min = INFINITY;
		max = -INFINITY;
	}
};

class Histo {
private:
	vector<double> hist;
	double min, max, width;
	double min_border, max_border, min_output;
	int n, n_out;

public:
	Histo() {output_reset();}
	Histo(int in, double imin, double imax) {
		set(in,imin,imax);
		output_reset();
	}
	void set(int in, double imin, double imax) {
		n = in;
		min = imin;
		max = imax;

		width = (max-min)/n;
		min_border = min + width;
		max_border = max - width;
		min_output = min + width * .5;

		hist.clear();
		hist.resize(n);
	}
	void add(double val) {
		if ( val < min_border ) hist[0]++;
		else if (val >= max_border ) hist[n-1]++;
		else hist[ (int) ( (val-min) / width ) ]++;
	}
	bool output(ostream& os=cout) {
		if ( n_out >= n ) return false;
		os.precision(8);
		os << scientific;
		os << min_output + n_out * width << " ";
		os << hist[n_out] << " ";
		n_out++;
		return true;
	}
	void output_reset() {n_out=0;}
	void norm() {
		double scale{};
		for (auto& s : hist) scale += s;
		scale *= width;

		for (auto& s : hist) s /= scale;
	}
};

void assign(istringstream &iss, const string &str) {
	iss.clear();
	iss.str(str);
}

ostream& operator<<(ostream& os, const Stat& stat) {
	os << stat.mu << " " << stat.sigma << " ";
	os << stat.min << " " << stat.max;
return os;
}

bool histoLine(vector<Histo> &v_h, ostream &os=cout) {
	bool ret=false;
	for (auto& h : v_h ) ret = ret | h.output(os);
	return ret;
}

ostream& operator << (ostream& os , vector<Histo> &v_h) {
	while ( histoLine(v_h,os) ) os << endl;
	for (auto& h : v_h) h.output_reset();
return os;
}

int main(int argc, char* argv[]) {
	ifstream input( argv[1] );
	ofstream output( "hist_out.dat", ios::trunc);
	istringstream iss;
	string line{};

	int n_header=0;
	string s_header;
	streampos pos_header;

	int columns{}, histoN {500};
	double val;
	vector <Stat> v_stat;
	vector <Histo> v_histo;

	if (argc > 2) histoN = stod(argv[2]);

	for (int i=0; i<n_header; ++i) {
		getline(input,line);
		s_header += line;
		s_header += '\n';
	}
	pos_header=input.tellg();

	getline(input,line);
	assign(iss,line);
	for (columns=0; ! iss.eof() ; ++columns) iss >> val;
	v_stat.resize(columns);
	v_histo.resize(columns);

	input.clear();
	input.seekg(pos_header);
	for ( ; getline(input,line); ) {
		assign(iss,line);
		for ( int i=0 ; ! iss.eof() ; ++i) {
			iss >> val;
			v_stat[i].add(val);
		}
	}

	for (auto& stat : v_stat)
		stat.calc();

	double borderH {};
	for (int i=0; i < columns; i++) {
		borderH=max( abs(v_stat[i].min) , abs(v_stat[i].max) );
		v_histo[i].set(histoN,-borderH,borderH);
	}

	input.clear();
	input.seekg(pos_header);
	for ( ; getline(input,line); ) {
		assign(iss,line);
		for ( int i=0 ; ! iss.eof() ; ++i) {
			iss >> val;
			v_histo[i].add(val);
		}
	}
	input.close();

	for (auto& h : v_histo) h.norm();

	output << v_histo;

return 0;
}
<commit_msg>Automatisches erkennen der Headergröße<commit_after>#include<stdlib.h>

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

#include <cmath>
#include <algorithm>

using namespace std;

class Stat {
private:
	double sum, sum2;
	int n;
public:
	double min, max;
	double sigma, mu;
	Stat() {reset();}
	void add(double x) {
		++n;
		sum += x;
		sum2 += pow(x,2);

		if ( x < min ) min = x;
		if ( x > max ) max = x;
	}
	void calc() {
		if ( n < 2 ) return;
		mu = sum / n;
		sigma = sqrt( (sum2 - n * pow(mu,2) ) / (n-1) );
	}
	void reset() {
		sum = sum2 = sigma = mu = 0;
		n=0;
		min = INFINITY;
		max = -INFINITY;
	}
};

class Histo {
private:
	vector<double> hist;
	double min, max, width;
	double min_border, max_border, min_output;
	int n, n_out;

public:
	Histo() {output_reset();}
	Histo(int in, double imin, double imax) {
		set(in,imin,imax);
		output_reset();
	}
	void set(int in, double imin, double imax) {
		n = in;
		min = imin;
		max = imax;

		width = (max-min)/n;
		min_border = min + width;
		max_border = max - width;
		min_output = min + width * .5;

		hist.clear();
		hist.resize(n);
	}
	void add(double val) {
		if ( val < min_border ) hist[0]++;
		else if (val >= max_border ) hist[n-1]++;
		else hist[ (int) ( (val-min) / width ) ]++;
	}
	bool output(ostream& os=cout) {
		if ( n_out >= n ) return false;
		os.precision(8);
		os << scientific;
		os << min_output + n_out * width << " ";
		os << hist[n_out] << " ";
		n_out++;
		return true;
	}
	void output_reset() {n_out=0;}
	void norm() {
		double scale{};
		for (auto& s : hist) scale += s;
		scale *= width;

		for (auto& s : hist) s /= scale;
	}
};

void assign(istringstream &iss, const string &str) {
	iss.clear();
	iss.str(str);
}

ostream& operator<<(ostream& os, const Stat& stat) {
	os << stat.mu << " " << stat.sigma << " ";
	os << stat.min << " " << stat.max;
return os;
}

bool histoLine(vector<Histo> &v_h, ostream &os=cout) {
	bool ret=false;
	for (auto& h : v_h ) ret = ret | h.output(os);
	return ret;
}

ostream& operator << (ostream& os , vector<Histo> &v_h) {
	while ( histoLine(v_h,os) ) os << endl;
	for (auto& h : v_h) h.output_reset();
return os;
}

int main(int argc, char* argv[]) {
	string filename{argv[1]};
	ifstream input(filename);
	ofstream output;
	istringstream iss;
	string line{};

	string s_header;
	streampos pos_header;

	int columns{}, histoN {500};
	double val;
	vector <Stat> v_stat;
	vector <Histo> v_histo;

	output.open(filename.insert(filename.find(".dat"),"_histo"), ios::out | ios::trunc);

	if (argc > 2) histoN = stod(argv[2]);

	pos_header=input.tellg();
	for (getline(input,line); !line.compare(0, 1, "#"); getline(input,line)) {
		s_header += line;
		s_header += '\n';
	pos_header=input.tellg();
	}

	assign(iss,line);
	for (columns=0; ! iss.eof() ; ++columns) iss >> val;
	v_stat.resize(columns);
	v_histo.resize(columns);

	input.clear();
	input.seekg(pos_header);
	for ( ; getline(input,line); ) {
		assign(iss,line);
		for ( int i=0 ; ! iss.eof() ; ++i) {
			iss >> val;
			v_stat[i].add(val);
		}
	}

	for (auto& stat : v_stat)
		stat.calc();

	double borderH {};
	for (int i=0; i < columns; i++) {
		borderH=max( abs(v_stat[i].min) , abs(v_stat[i].max) );
		v_histo[i].set(histoN,-borderH,borderH);
	}

	input.clear();
	input.seekg(pos_header);
	for ( ; getline(input,line); ) {
		assign(iss,line);
		for ( int i=0 ; ! iss.eof() ; ++i) {
			iss >> val;
			v_histo[i].add(val);
		}
	}
	input.close();

	for (auto& h : v_histo) h.norm();

	output << s_header;
	output << v_histo;

	cout << "<< Die Datei '" << filename << "' wurde erstellt." << endl;
	output.close();
return 0;
}
<|endoftext|>


<commit_before><commit_msg>coverity#1158122 : Explicit null dereferenced<commit_after><|endoftext|>


<commit_before>
/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkDICOMImageIO2.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#ifdef WIN32
#pragma warning(disable:4786)
#endif

#include "itkDICOMImageIO2.h"
#include "itkExceptionObject.h"
#include "itkByteSwapper.h"
#include <iostream>
#include <list>

#include <math.h>
#include <stdio.h>
#include <string.h>

#include "DICOMCallback.h"

namespace itk
{

/** Constructor */
DICOMImageIO2::DICOMImageIO2()
{
  this->SetNumberOfDimensions(2);
  m_PixelType  = UCHAR;
  m_ByteOrder = BigEndian;
  this->Parser = new DICOMParser();
  this->AppHelper = new DICOMAppHelper();
}

/** Destructor */
DICOMImageIO2::~DICOMImageIO2()
{
  delete this->Parser;
  delete this->AppHelper;
}

bool DICOMImageIO2::CanReadFile( const char* filename )
{
  bool open = Parser->OpenFile( filename);
  if (!open)
    {
    std::cerr << "Couldn't open file: " << filename << std::endl;
    return false;
    }
  bool magic = Parser->IsDICOMFile();
  return magic;
}

void DICOMImageIO2::ReadDataCallback( doublebyte,
                                      doublebyte,
                                      DICOMParser::VRTypes,
                                      unsigned char* val,
                                      quadbyte len)
{
  unsigned int imageBytes = this->GetImageSizeInBytes();
  if (len < 0)
    {
    len = 0;
    }
  if (len < static_cast<int>( imageBytes ) )
    {
    imageBytes = len;
    }
  memcpy(this->ImageDataBuffer, val, imageBytes);
}

void DICOMImageIO2::Read(void* buffer)
{
  Parser->ClearAllDICOMTagCallbacks();
  AppHelper->RegisterCallbacks(Parser);
  AppHelper->RegisterPixelDataCallback(Parser);

  bool open = Parser->OpenFile(m_FileName.c_str());
  if (!open)
    {
    std::cerr << "Couldn't open file: " << m_FileName << std::endl;
    return;
    }

  // Should ReadHeader() be Read() since more than just a header is read?
  Parser->ReadHeader();

  void* newData;
  DICOMParser::VRTypes newType;
  unsigned long imageDataLength = 0;

  AppHelper->GetImageData(newData, newType, imageDataLength);
  memcpy(buffer, newData, imageDataLength);

  // Clean up
  AppHelper->Clear();
}

/**
 *  Read Information about the dicom file
 */
void DICOMImageIO2::ReadImageInformation()
{
  Parser->ClearAllDICOMTagCallbacks();
  AppHelper->RegisterCallbacks(Parser);

  bool open = Parser->OpenFile(m_FileName.c_str());
  if (!open)
    {
    std::cerr << "Couldn't open file: " << m_FileName << std::endl;
    return;
    }

  Parser->ReadHeader();

  float* spacing = AppHelper->GetPixelSpacing();
  float origin[3] = {0.0, 0.0, 0.0};

  int* dims = AppHelper->GetDimensions();

  for (int i = 0; i < 2; i++)
    {
    this->SetOrigin(i, origin[i]);
    this->SetSpacing(i, spacing[i]);
    this->SetDimensions(i, dims[i]);
    }

  int numBits = AppHelper->GetBitsAllocated();
  bool sign = AppHelper->RescaledImageDataIsSigned();

  bool isFloat = AppHelper->RescaledImageDataIsFloat();
  int num_comp = AppHelper->GetNumberOfComponents();

  if (isFloat)  // Float
    {
    this->SetPixelType(ImageIOBase::FLOAT);
    this->SetComponentType(ImageIOBase::FLOAT);
    }
  else if (num_comp == 3) //RGB
    {
    if (numBits == 8)
      {
      this->SetComponentType(ImageIOBase::UCHAR);
      this->SetPixelType(ImageIOBase::UCHAR);
      }
    else
      {
      this->SetComponentType(ImageIOBase::USHORT);
      this->SetPixelType(ImageIOBase::USHORT);
      }
    }
  else // Everything else
    {
    if (numBits == 8)
      {
      if (sign)
        {
        this->SetPixelType(ImageIOBase::CHAR);
        this->SetComponentType(ImageIOBase::CHAR);
        }
      else
        {
        this->SetPixelType(ImageIOBase::UCHAR);
        this->SetComponentType(ImageIOBase::UCHAR);
        }
      }
    else if (numBits == 16)
      {
      if (sign)
        {
        this->SetPixelType(ImageIOBase::SHORT);
        this->SetComponentType(ImageIOBase::SHORT);
        }
      else
        {
        this->SetPixelType(ImageIOBase::USHORT);
        this->SetComponentType(ImageIOBase::USHORT);
        }
      }
    else
      {
      this->SetPixelType(ImageIOBase::USHORT);
      this->SetComponentType(ImageIOBase::USHORT);
      }
    }

  this->SetNumberOfComponents(num_comp);

  // Cleanup
  AppHelper->Clear();
}

/** Print Self Method */
void DICOMImageIO2::PrintSelf(std::ostream& os, Indent indent) const
{
  unsigned int i;
  Superclass::PrintSelf(os, indent);
  os << indent << "Spacing: ( ";
  for (i=0; i < m_NumberOfDimensions; i++)
    {
    os << m_Spacing[i] << " ";
    }
  os << " )\n";
  os << indent << "Origin: ( ";
  for (i=0; i < m_Origin.size(); i++)
    {
    os << m_Origin[i] << " ";
    }
  os << " )" << std::endl;
}

} // end namespace itk

<commit_msg>FIX: Set the image origin (x,y) based on ImagePositionPatient<commit_after>
/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkDICOMImageIO2.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#ifdef WIN32
#pragma warning(disable:4786)
#endif

#include "itkDICOMImageIO2.h"
#include "itkExceptionObject.h"
#include "itkByteSwapper.h"
#include <iostream>
#include <list>

#include <math.h>
#include <stdio.h>
#include <string.h>

#include "DICOMCallback.h"

namespace itk
{

/** Constructor */
DICOMImageIO2::DICOMImageIO2()
{
  this->SetNumberOfDimensions(2);
  m_PixelType  = UCHAR;
  m_ByteOrder = BigEndian;
  this->Parser = new DICOMParser();
  this->AppHelper = new DICOMAppHelper();
}

/** Destructor */
DICOMImageIO2::~DICOMImageIO2()
{
  delete this->Parser;
  delete this->AppHelper;
}

bool DICOMImageIO2::CanReadFile( const char* filename )
{
  bool open = Parser->OpenFile( filename);
  if (!open)
    {
    std::cerr << "Couldn't open file: " << filename << std::endl;
    return false;
    }
  bool magic = Parser->IsDICOMFile();
  return magic;
}

void DICOMImageIO2::ReadDataCallback( doublebyte,
                                      doublebyte,
                                      DICOMParser::VRTypes,
                                      unsigned char* val,
                                      quadbyte len)
{
  unsigned int imageBytes = this->GetImageSizeInBytes();
  if (len < 0)
    {
    len = 0;
    }
  if (len < static_cast<int>( imageBytes ) )
    {
    imageBytes = len;
    }
  memcpy(this->ImageDataBuffer, val, imageBytes);
}

void DICOMImageIO2::Read(void* buffer)
{
  Parser->ClearAllDICOMTagCallbacks();
  AppHelper->RegisterCallbacks(Parser);
  AppHelper->RegisterPixelDataCallback(Parser);

  bool open = Parser->OpenFile(m_FileName.c_str());
  if (!open)
    {
    std::cerr << "Couldn't open file: " << m_FileName << std::endl;
    return;
    }

  // Should ReadHeader() be Read() since more than just a header is read?
  Parser->ReadHeader();

  void* newData;
  DICOMParser::VRTypes newType;
  unsigned long imageDataLength = 0;

  AppHelper->GetImageData(newData, newType, imageDataLength);
  memcpy(buffer, newData, imageDataLength);

  // Clean up
  AppHelper->Clear();
}

/**
 *  Read Information about the dicom file
 */
void DICOMImageIO2::ReadImageInformation()
{
  Parser->ClearAllDICOMTagCallbacks();
  AppHelper->RegisterCallbacks(Parser);

  bool open = Parser->OpenFile(m_FileName.c_str());
  if (!open)
    {
    std::cerr << "Couldn't open file: " << m_FileName << std::endl;
    return;
    }

  Parser->ReadHeader();

  float* spacing = AppHelper->GetPixelSpacing();
  float* origin  = AppHelper->GetImagePositionPatient();

  int* dims = AppHelper->GetDimensions();

  for (int i = 0; i < 2; i++)
    {
    this->SetOrigin(i, origin[i]);
    this->SetSpacing(i, spacing[i]);
    this->SetDimensions(i, dims[i]);
    }

  int numBits = AppHelper->GetBitsAllocated();
  bool sign = AppHelper->RescaledImageDataIsSigned();

  bool isFloat = AppHelper->RescaledImageDataIsFloat();
  int num_comp = AppHelper->GetNumberOfComponents();

  if (isFloat)  // Float
    {
    this->SetPixelType(ImageIOBase::FLOAT);
    this->SetComponentType(ImageIOBase::FLOAT);
    }
  else if (num_comp == 3) //RGB
    {
    if (numBits == 8)
      {
      this->SetComponentType(ImageIOBase::UCHAR);
      this->SetPixelType(ImageIOBase::UCHAR);
      }
    else
      {
      this->SetComponentType(ImageIOBase::USHORT);
      this->SetPixelType(ImageIOBase::USHORT);
      }
    }
  else // Everything else
    {
    if (numBits == 8)
      {
      if (sign)
        {
        this->SetPixelType(ImageIOBase::CHAR);
        this->SetComponentType(ImageIOBase::CHAR);
        }
      else
        {
        this->SetPixelType(ImageIOBase::UCHAR);
        this->SetComponentType(ImageIOBase::UCHAR);
        }
      }
    else if (numBits == 16)
      {
      if (sign)
        {
        this->SetPixelType(ImageIOBase::SHORT);
        this->SetComponentType(ImageIOBase::SHORT);
        }
      else
        {
        this->SetPixelType(ImageIOBase::USHORT);
        this->SetComponentType(ImageIOBase::USHORT);
        }
      }
    else
      {
      this->SetPixelType(ImageIOBase::USHORT);
      this->SetComponentType(ImageIOBase::USHORT);
      }
    }

  this->SetNumberOfComponents(num_comp);

  // Cleanup
  AppHelper->Clear();
}

/** Print Self Method */
void DICOMImageIO2::PrintSelf(std::ostream& os, Indent indent) const
{
  unsigned int i;
  Superclass::PrintSelf(os, indent);
  os << indent << "Spacing: ( ";
  for (i=0; i < m_NumberOfDimensions; i++)
    {
    os << m_Spacing[i] << " ";
    }
  os << " )\n";
  os << indent << "Origin: ( ";
  for (i=0; i < m_Origin.size(); i++)
    {
    os << m_Origin[i] << " ";
    }
  os << " )" << std::endl;
}

} // end namespace itk

<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <move_basic/queued_action_server.h>
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>

class GoalQueueSuite : public ::testing::Test {
protected:
	virtual void SetUp() {
		resetFlags();
		ros::NodeHandle actionNh("");
		qserv.reset(new actionlib::QueuedActionServer<move_base_msgs::MoveBaseAction> (actionNh, "queue_server", boost::bind(&GoalQueueSuite::executeCallback, this, _1)));
		cli.reset(new actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> ("queue_server", true)); // true -> don't need ros::spin()

		qserv->start();
	}

	virtual void TearDown() {
		// Kill the executeCallback if it is running
		// New Scope so that lock_guard destructs and unlocks
		{
			std::lock_guard<std::mutex> lk1(execute_lock);
			finish_executing = true;
			execute_cv.notify_one(); // Releases one waiting thread
		}
		qserv->shutdown();
	}

	// TODO: Making sure it gets called at the right time (flags!) - in its own thread
	void executeCallback(const move_base_msgs::MoveBaseGoalConstPtr &msg) {
		resetFlags();
		got_goal = true;
		received_goal = msg;
		while (true) {
			if (qserv->isPreemptRequested()) {
				goal_preempted = true;
			} else {
				goal_preempted = false;
			}
			if (qserv->isNewGoalAvailable()) {
				next_goal_available = true;
			} else {
				next_goal_available = false;
			}

			// Wait until signalled to end
			std::unique_lock<std::mutex> lk(execute_lock);
			execute_cv.wait(lk,
				//lambda function to wait on our bool variables
				[this](){return finish_executing || resume_executing;} // blocks only if lambda returns false
			);
			// We were requested to stop, so we stop
			if (finish_executing) {
				break;
			}
		}

		// Signal that we are done here
		std::lock_guard<std::mutex> lk(execute_done_lock);
		execute_done = true;
		execute_done_cv.notify_all(); // Releases all waiting thread
	}

	// Helper function to signal the executeCallback to end
	void finishExecuting() {
		// New Scope so that lock_guard destructs and unlocks
		{
			std::lock_guard<std::mutex> lk1(execute_lock);
			finish_executing = true;
			execute_cv.notify_one(); //unlocks lambda waiting function in ExecuteCallback
		}

		// Wait for execute callback to actually finish
		std::unique_lock<std::mutex> lk2(execute_done_lock);
		execute_done_cv.wait(lk2, [this]() {return execute_done;}); // at the end of ExecuteCallback
	}

	// Helper function to signal the executeCallback to resume
	// This is useful for seeing if the callback code sees a preempt
	void resumeExecuting() {
		std::lock_guard<std::mutex> lk(execute_lock);
		resume_executing = true;
		execute_cv.notify_one();
	}

	void resetFlags() {
		got_goal = false;
		goal_preempted = false;
		next_goal_available = false;
		received_goal = nullptr;

		// Signal flags
		finish_executing = false;
		resume_executing = false;
		execute_done = false;
	}

	// Flags for assertions
	bool got_goal;
	bool goal_preempted;
	bool next_goal_available;
	move_base_msgs::MoveBaseGoalConstPtr received_goal;

	// Signaling variables
	bool finish_executing;
	bool resume_executing;
	bool execute_done;
	std::mutex execute_lock;
	std::mutex execute_done_lock;
	std::condition_variable execute_cv;
	std::condition_variable execute_done_cv;

	std::unique_ptr<actionlib::QueuedActionServer<move_base_msgs::MoveBaseAction>> qserv;
	std::unique_ptr<actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>> cli;
};

// IMPORTANT: Time delays and ros::spinOnce() sequence should stay the same, otherwise, strange things happen
TEST_F(GoalQueueSuite, establishDuplex) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	finishExecuting();
	ASSERT_TRUE(got_goal);
	ASSERT_FALSE(goal_preempted);
	ASSERT_FALSE(next_goal_available);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);
}

TEST_F(GoalQueueSuite, addGoalWhileExecuting) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Cancelling the first goal - PITFALL
	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	finishExecuting();

	// "Second" goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x);

	// Third goal
	goal.target_pose.pose.position.x = 13.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	resumeExecuting();
	// Make sure that the "second" goal is still executing, but a new goal is seen
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x);
	ASSERT_FALSE(goal_preempted);
	ASSERT_TRUE(next_goal_available); // Fails when changing other code
	// Finish the "second" goal, then the 3nd goal should start executing
	finishExecuting();
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_FALSE(goal_preempted);
	ASSERT_FALSE(next_goal_available);
	ASSERT_EQ(13.0, received_goal->target_pose.pose.position.x);
	finishExecuting(); // Finish the 3nd goal
/*
	- if another goal is received add it to the queue (DONE)
	- if the queue full, set the current goal as preempted - explicitly called! - so cancelling the current goal and start executing the next one
	- start executing the next goal in queue (the one after the preempted)
*/
}

TEST_F(GoalQueueSuite, goalPreempting) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// One goal -> Cancel request -> Stop
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(1.0).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(got_goal);
	ASSERT_TRUE(qserv->isActive());
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	finishExecuting(); // Finish the goal
	ros::spinOnce();
	ros::Duration(1.0).sleep();
	ASSERT_FALSE(qserv->isActive());

	// Two goals -> Cancel current goal -> Start executing the second
	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Cancelling the first goal - PITFALL
	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	finishExecuting(); // Finish 1st goal

	// "Second" goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x); // call FINISH!
	finishExecuting(); // Finish 2nd goal

//	- if a cancel request is received for the current goal, set it as preempted (DONE)
//	- if there another goal, start executing it
//	- if no goal, stop (DONE)
}

// TODO: How can I cancel the second goal??? CANCEL REQUEST CANCELS THE LAST GOAL SENT
TEST_F(GoalQueueSuite, goalCancelling) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(1.0).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(got_goal);
	ASSERT_TRUE(qserv->isActive());
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	/*
	- if a cancel request on the "next_goal" received, remove it from the queue and set it as cancelled
	- TODO: How to check next goal is executing? It becomes current, so isActive()
	*/
}

// Two more TEST_F missing and a pitfall

int main(int argc, char **argv) {
    ros::init(argc, argv, "goal_queueing_test");
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<commit_msg>Fourth goal progress.<commit_after>#include <gtest/gtest.h>
#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <move_basic/queued_action_server.h>
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>

class GoalQueueSuite : public ::testing::Test {
protected:
	virtual void SetUp() {
		resetFlags();
		ros::NodeHandle actionNh("");
		qserv.reset(new actionlib::QueuedActionServer<move_base_msgs::MoveBaseAction> (actionNh, "queue_server", boost::bind(&GoalQueueSuite::executeCallback, this, _1)));
		cli.reset(new actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> ("queue_server", true)); // true -> don't need ros::spin()

		qserv->start();
	}

	virtual void TearDown() {
		// Kill the executeCallback if it is running
		// New Scope so that lock_guard destructs and unlocks
		{
			std::lock_guard<std::mutex> lk1(execute_lock);
			finish_executing = true;
			execute_cv.notify_one(); // Releases one waiting thread
		}
		qserv->shutdown();
	}

	// TODO: Making sure it gets called at the right time (flags!) - in its own thread
	void executeCallback(const move_base_msgs::MoveBaseGoalConstPtr &msg) {
		resetFlags();
		got_goal = true;
		received_goal = msg;
		while (true) {
			if (qserv->isPreemptRequested()) {
				goal_preempted = true;
			} else {
				goal_preempted = false;
			}
			if (qserv->isNewGoalAvailable()) {
				next_goal_available = true;
			} else {
				next_goal_available = false;
			}

			// Wait until signalled to end
			std::unique_lock<std::mutex> lk(execute_lock);
			execute_cv.wait(lk,
				//lambda function to wait on our bool variables
				[this](){return finish_executing || resume_executing;} // blocks only if lambda returns false
			);
			// We were requested to stop, so we stop
			if (finish_executing) {
				break;
			}
		}

		// Signal that we are done here
		std::lock_guard<std::mutex> lk(execute_done_lock);
		execute_done = true;
		execute_done_cv.notify_all(); // Releases all waiting thread
	}

	// Helper function to signal the executeCallback to end
	void finishExecuting() {
		// New Scope so that lock_guard destructs and unlocks
		{
			std::lock_guard<std::mutex> lk1(execute_lock);
			finish_executing = true;
			execute_cv.notify_one(); //unlocks lambda waiting function in ExecuteCallback
		}

		// Wait for execute callback to actually finish
		std::unique_lock<std::mutex> lk2(execute_done_lock);
		execute_done_cv.wait(lk2, [this]() {return execute_done;}); // at the end of ExecuteCallback
	}

	// Helper function to signal the executeCallback to resume
	// This is useful for seeing if the callback code sees a preempt
	void resumeExecuting() {
		std::lock_guard<std::mutex> lk(execute_lock);
		resume_executing = true;
		execute_cv.notify_one();
	}

	void resetFlags() {
		got_goal = false;
		goal_preempted = false;
		next_goal_available = false;
		received_goal = nullptr;

		// Signal flags
		finish_executing = false;
		resume_executing = false;
		execute_done = false;
	}

	// Flags for assertions
	bool got_goal;
	bool goal_preempted;
	bool next_goal_available;
	move_base_msgs::MoveBaseGoalConstPtr received_goal;

	// Signaling variables
	bool finish_executing;
	bool resume_executing;
	bool execute_done;
	std::mutex execute_lock;
	std::mutex execute_done_lock;
	std::condition_variable execute_cv;
	std::condition_variable execute_done_cv;

	std::unique_ptr<actionlib::QueuedActionServer<move_base_msgs::MoveBaseAction>> qserv;
	std::unique_ptr<actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>> cli;
};

// IMPORTANT: Time delays and ros::spinOnce() sequence should stay the same, otherwise, strange things happen
TEST_F(GoalQueueSuite, establishDuplex) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	finishExecuting();
	ASSERT_TRUE(got_goal);
	ASSERT_FALSE(goal_preempted);
	ASSERT_FALSE(next_goal_available);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);
}

TEST_F(GoalQueueSuite, addGoalWhileExecuting) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Cancelling the first goal - PITFALL
	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	finishExecuting();

	// "Second" goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x);

	// Third goal
	goal.target_pose.pose.position.x = 13.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	resumeExecuting();
	// Make sure that the "second" goal is still executing, but a new goal is seen
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x);
	ASSERT_FALSE(goal_preempted);
	ASSERT_TRUE(next_goal_available); // Fails when changing other code
	// Finish the "second" goal, then the 3nd goal should start executing
	finishExecuting();
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_FALSE(goal_preempted);
	ASSERT_FALSE(next_goal_available);
	ASSERT_EQ(13.0, received_goal->target_pose.pose.position.x);
	finishExecuting(); // Finish the 3nd goal
/*
	- if another goal is received add it to the queue (DONE)
	- if the queue full, set the current goal as preempted - explicitly called! - so cancelling the current goal and start executing the next one
	- start executing the next goal in queue (the one after the preempted)
*/
}

TEST_F(GoalQueueSuite, goalPreempting) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// One goal -> Cancel request -> Stop
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(1.0).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(got_goal);
	ASSERT_TRUE(qserv->isActive());
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	finishExecuting(); // Finish the goal
	ros::spinOnce();
	ros::Duration(1.0).sleep();
	ASSERT_FALSE(qserv->isActive());

	// Two goals -> Cancel current goal -> Start executing the second
	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Cancelling the first goal - PITFALL
	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	finishExecuting(); // Finish 1st goal

	// "Second" goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x); // call FINISH!
	finishExecuting(); // Finish 2nd goal

//	- if a cancel request is received for the current goal, set it as preempted (DONE)
//	- if there another goal, start executing it
//	- if no goal, stop (DONE)
}

TEST_F(GoalQueueSuite, goalCancelling) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// Two goals -> Cancel current goal -> Start executing the second
	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Second goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear

	// Cancelling the first goal - PITFALL
	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);

	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x); // call FINISH!
	finishExecuting(); // Finish 2nd goal

	// Cancelling the first goal - PITFALL
	//resumeExecuting();
	//cli->cancelGoal();
	//ros::Duration(1.0).sleep();
	//ros::spinOnce();
	//ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	//finishExecuting(); // Finish 1st goal
	/*
	- if a cancel request on the "next_goal" received, remove it from the queue and set it as cancelled
	- TODO: How to check next goal is executing? It becomes current, so isActive()
	*/
}

// Two more TEST_F missing and a pitfall

int main(int argc, char **argv) {
    ros::init(argc, argv, "goal_queueing_test");
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>#include <cstddef>
#include <utility>
#include <new>

namespace archie {
namespace utils {
  namespace containers {

    template <typename Tp, std::size_t N>
    struct stack_allocator {
      using value_type = Tp;
      using pointer = Tp*;
      using reference = Tp&;
      using const_reference = Tp const&;
      using const_pointer = Tp const*;
      using size_type = std::size_t;
      using difference_type = std::ptrdiff_t;

      pointer allocate(size_type n) {
        auto s = taken_;
        taken_ += n;
        return &store.array[s];
      }
      void deallocate(pointer, size_type) {}
      size_type max_size() const { return N - taken_; }

    private:
      union storage {
        char raw[N * sizeof(value_type)];
        value_type array[N];
      };
      storage store;
      size_type taken_ = 0u;
    };

    template <typename Tp, std::size_t N,
              typename Alloc = stack_allocator<Tp, N>>
    struct fixed_buffer {
      using allocator_type = Alloc;
      using value_type = typename allocator_type::value_type;
      using pointer = typename allocator_type::pointer;
      using reference = typename allocator_type::reference;
      using const_reference = typename allocator_type::const_reference;
      using const_pointer = typename allocator_type::const_pointer;
      using size_type = typename allocator_type::size_type;
      using iterator = pointer;
      using const_iterator = const_pointer;

      explicit fixed_buffer(size_type n) : impl_() {
        while (n--) emplace_back();
      }

      template <typename Up>
      fixed_buffer(size_type n, Up const& ref)
          : impl_() {
        while (n--) emplace_back(ref);
      }

      fixed_buffer() : fixed_buffer(0u) {}
      ~fixed_buffer() {
        while (size()) {
          back().~value_type();
          --impl_.size_;
        }
      }

      const_reference operator[](size_type n) const {
        return *(impl_.begin_ + n);
      }
      const_reference front() const { return operator[](0); }
      const_iterator begin() const { return &front(); }
      const_iterator end() const { return begin() + size(); }

      reference operator[](size_type n) { return *(impl_.begin_ + n); }
      reference front() { return operator[](0); }
      reference back() { return operator[](size() - 1u); }
      iterator begin() { return &front(); }
      iterator end() { return begin() + size(); }

      size_type size() const { return impl_.size_; }

      template <typename... Us>
      void emplace_back(Us&&... us) {
        new (end()) value_type(std::forward<Us>(us)...);
        ++impl_.size_;
      }

    private:
      struct impl : allocator_type {
        using allocator_type::allocate;
        using allocator_type::deallocate;
        impl() : begin_(allocate(N)) {}
        ~impl() { deallocate(begin_, N); }
        pointer const begin_ = nullptr;
        size_type size_ = 0u;
      };
      impl impl_;
    };

    template <typename Tp, std::size_t N>
    struct ring_iterator {
    private:
      using buff_t = fixed_buffer<Tp, N>;
    };
  }
}
}

#include <archie/utils/test.h>
#include <memory>
namespace cont = archie::utils::containers;

void canCreateStackAlloc() {
  auto alloc = cont::stack_allocator<int, 4>{};
  EXPECT_EQ(4u, alloc.max_size());
}

void canAllocateWithStackAlloc() {
  auto alloc = cont::stack_allocator<int, 4>{};
  EXPECT_EQ(4u, alloc.max_size());
  auto ptr1 = alloc.allocate(2);
  EXPECT_EQ(2u, alloc.max_size());
  EXPECT_TRUE((void*)ptr1 >= (void*)&alloc);
  EXPECT_TRUE((void*)ptr1 < (void*)(&alloc + 1));

  auto ptr2 = alloc.allocate(1);
  EXPECT_EQ(1u, alloc.max_size());
  EXPECT_TRUE((void*)ptr2 >= (void*)&alloc);
  EXPECT_TRUE((void*)ptr2 < (void*)(&alloc + 1));

  EXPECT_NE(ptr1, ptr2);
}

void canDefaultConstructFixedBuffer() {
  auto buff = cont::fixed_buffer<int, 5>{};
  EXPECT_EQ(0u, buff.size());
}

void canConstructFixedBufferWithNum() {
  using buff_t = cont::fixed_buffer<int, 5>;
  EXPECT_EQ(0u, buff_t{0}.size());
  EXPECT_EQ(1u, buff_t{1}.size());

  auto buff2 = buff_t{2, 1};
  EXPECT_EQ(2u, buff2.size());
  EXPECT_EQ(1u, buff2[0]);
  EXPECT_EQ(1u, buff2[1]);
}

int main() {
  canCreateStackAlloc();
  canDefaultConstructFixedBuffer();
  canConstructFixedBufferWithNum();
  return 0;
}
<commit_msg>ring_buffer<commit_after>#include <cstddef>
#include <utility>
#include <new>

namespace archie {
namespace utils {
  namespace containers {

    template <typename Tp, std::size_t N>
    struct stack_allocator {
      using value_type = Tp;
      using pointer = Tp*;
      using reference = Tp&;
      using const_reference = Tp const&;
      using const_pointer = Tp const*;
      using size_type = std::size_t;
      using difference_type = std::ptrdiff_t;

      pointer allocate(size_type n) {
        auto s = taken_;
        taken_ += n;
        return &store.array[s];
      }
      void deallocate(pointer, size_type) {}
      size_type max_size() const { return N - taken_; }

    private:
      union storage {
        char raw[N * sizeof(value_type)];
        value_type array[N];
      };
      storage store;
      size_type taken_ = 0u;
    };

    template <typename Tp, std::size_t N,
              typename Alloc = stack_allocator<Tp, N>>
    struct fixed_buffer {
      using allocator_type = Alloc;
      using value_type = typename allocator_type::value_type;
      using pointer = typename allocator_type::pointer;
      using reference = typename allocator_type::reference;
      using const_reference = typename allocator_type::const_reference;
      using const_pointer = typename allocator_type::const_pointer;
      using size_type = typename allocator_type::size_type;
      using iterator = pointer;
      using const_iterator = const_pointer;

      explicit fixed_buffer(size_type n) : impl_() {
        while (n--) emplace_back();
      }

      template <typename Up>
      fixed_buffer(size_type n, Up const& ref)
          : impl_() {
        while (n--) emplace_back(ref);
      }

      fixed_buffer() : fixed_buffer(0u) {}
      ~fixed_buffer() {
        while (size()) {
          back().~value_type();
          --impl_.size_;
        }
      }

      const_reference operator[](size_type n) const {
        return *(impl_.begin_ + n);
      }
      const_reference front() const { return operator[](0); }
      const_reference back() const { return operator[](size() - 1u); }
      const_iterator begin() const { return &front(); }
      const_iterator end() const { return begin() + size(); }

      reference operator[](size_type n) { return *(impl_.begin_ + n); }
      reference front() { return operator[](0); }
      reference back() { return operator[](size() - 1u); }
      iterator begin() { return &front(); }
      iterator end() { return begin() + size(); }

      size_type size() const { return impl_.size_; }

      template <typename... Us>
      void emplace_back(Us&&... us) {
        new (end()) value_type(std::forward<Us>(us)...);
        ++impl_.size_;
      }

    private:
      struct impl : allocator_type {
        using allocator_type::allocate;
        using allocator_type::deallocate;
        impl() : begin_(allocate(N)) {}
        ~impl() { deallocate(begin_, N); }
        pointer const begin_ = nullptr;
        size_type size_ = 0u;
      };
      impl impl_;
    };

    template <typename Tp, std::size_t N>
    struct ring_buffer {
      using buff_t = fixed_buffer<Tp, N>;
      using value_type = typename buff_t::value_type;
      using pointer = typename buff_t::pointer;
      using reference = typename buff_t::reference;
      using const_reference = typename buff_t::const_reference;
      using const_pointer = typename buff_t::const_pointer;
      using size_type = typename buff_t::size_type;

      size_type size() const { return 0; }

    private:
      buff_t buff_;
    };
  }
}
}

#include <archie/utils/test.h>
#include <memory>
namespace cont = archie::utils::containers;

void canCreateStackAlloc() {
  auto alloc = cont::stack_allocator<int, 4>{};
  EXPECT_EQ(4u, alloc.max_size());
}

void canAllocateWithStackAlloc() {
  auto alloc = cont::stack_allocator<int, 4>{};
  EXPECT_EQ(4u, alloc.max_size());
  auto ptr1 = alloc.allocate(2);
  EXPECT_EQ(2u, alloc.max_size());
  EXPECT_TRUE((void*)ptr1 >= (void*)&alloc);
  EXPECT_TRUE((void*)ptr1 < (void*)(&alloc + 1));

  auto ptr2 = alloc.allocate(1);
  EXPECT_EQ(1u, alloc.max_size());
  EXPECT_TRUE((void*)ptr2 >= (void*)&alloc);
  EXPECT_TRUE((void*)ptr2 < (void*)(&alloc + 1));

  EXPECT_NE(ptr1, ptr2);
}

void canDefaultConstructFixedBuffer() {
  auto buff = cont::fixed_buffer<int, 5>{};
  EXPECT_EQ(0u, buff.size());
}

void canConstructFixedBufferWithNum() {
  using buff_t = cont::fixed_buffer<int, 5>;
  EXPECT_EQ(0u, buff_t{0}.size());
  EXPECT_EQ(1u, buff_t{1}.size());

  auto buff2 = buff_t{2, 1};
  EXPECT_EQ(2u, buff2.size());
  EXPECT_EQ(1u, buff2[0]);
  EXPECT_EQ(1u, buff2[1]);
}

void canCreateRingBuffer() {
  auto buff = cont::ring_buffer<int, 5>{};
  EXPECT_EQ(0u, buff.size());
}

int main() {
  canCreateStackAlloc();
  canAllocateWithStackAlloc();
  canDefaultConstructFixedBuffer();
  canConstructFixedBufferWithNum();
  canCreateRingBuffer();
  return 0;
}
<|endoftext|>


<commit_before>/*
FileName:
GUID.aof
File struct:
   CapHeader| LevelHeader | Meas0 | Meas1|....| LevelHeader|Meas0 |Meas1...
Alg:
        1. Measurements save to COLA file struct
        2. When the append-only-file is fulle,
        it is converted to a sorted table (*.page) and a new log file is created
for future updates.
*/

#include "capacitor.h"
#include "../utils/cz.h"
#include "../utils/fs.h"
#include "../utils/utils.h"
#include <algorithm>
#include <cassert>
#include <limits>
#include <list>

using namespace dariadb;
using namespace dariadb::storage;

struct level_header {
  size_t lvl;
  size_t count;
  size_t pos;
};

struct level {
  level_header *hdr;
  Meas *begin;

  Meas at(size_t _pos) const { return begin[_pos]; }

  void push_back(const Meas &m) {
    begin[hdr->pos] = m;
    ++hdr->pos;
  }

  void clear() { hdr->pos = 0; }

  bool empty() const { return hdr->pos == 0; }
};

class CapReader : public storage::Reader {
public:
  bool isEnd() const override{
      return _values.empty();
  }
  dariadb::IdArray getIds() const override{
dariadb::IdSet res;
for(auto v:_values){
    res.insert(v.id);
}
return dariadb::IdArray(res.begin(),res.end());
  }
  void readNext(dariadb::storage::ReaderClb *clb) override{
clb->call(_values.front());
_values.pop_front();
  }
  Reader_ptr clone() const override{
      return nullptr;
  }
  void reset() override{
  }
  dariadb::Meas::MeasList _values;
   ~CapReader() {}
};

class Capacitor::Private {
public:
  struct meas_time_compare {
    bool operator()(const dariadb::Meas &lhs, const dariadb::Meas &rhs) const {
      return lhs.time < rhs.time;
    }
  };

  struct Header {
    bool is_dropped;
    size_t B;
    size_t size;    // sizeof file in bytes
    size_t _size_B; // how many block (sizeof(B)) addeded.
    size_t levels_count;
    size_t _writed;
  };

  Private(const BaseStorage_ptr stor, const Capacitor::Params &params)
      : _minTime(std::numeric_limits<dariadb::Time>::max()),
        _maxTime(std::numeric_limits<dariadb::Time>::min()), _stor(stor),
        _params(params), mmap(nullptr), _size(0) {
    open_or_create();
    _memvalues.resize(_header->B);
    _memvalues_pos = 0;
  }

  ~Private() {
    if (mmap != nullptr) {
      mmap->close();
    }
  }

  void open_or_create() {
    if (!dariadb::utils::fs::path_exists(_params.path)) {
      create();
    } else {
      auto logs = dariadb::utils::fs::ls(_params.path, CAP_FILE_EXT);
      if (logs.empty()) {
        create();
      } else {
        open(logs.front());
      }
    }
  }

  size_t one_block_size(size_t B) const { return sizeof(Meas) * B; }

  size_t block_in_level(size_t lev_num) const { return (size_t(1) << lev_num); }

  size_t meases_in_level(size_t B, size_t lvl) const {
    return one_block_size(B) * block_in_level(lvl);
  }

  uint64_t cap_size() const {
    uint64_t result = 0;

    for (size_t lvl = 0; lvl < _params.max_levels; ++lvl) {
      result += sizeof(level_header) + meases_in_level(_params.B, lvl); // 2^lvl
    }
    return result + sizeof(Header);
  }

  std::string file_name() {
    return dariadb::utils::fs::random_file_name(CAP_FILE_EXT);
  }

  void create() {
    dariadb::utils::fs::mkdir(_params.path);
    utils::fs::mkdir(_params.path);
    auto sz = cap_size();
    mmap = utils::fs::MappedFile::touch(
        utils::fs::append_path(_params.path, file_name()), sz);

    _header = reinterpret_cast<Header *>(mmap->data());
    _raw_data = reinterpret_cast<uint8_t *>(_header + sizeof(Header));
    _header->size = sz;
    _header->B = _params.B;
    _header->is_dropped = false;
    _header->levels_count = _params.max_levels;

    auto pos = _raw_data;
    for (size_t lvl = 0; lvl < _header->levels_count; ++lvl) {
      auto it = reinterpret_cast<level_header *>(pos);
      it->lvl = lvl;
      it->count = block_in_level(lvl) * _params.B;
      it->pos = 0;
      pos += sizeof(level_header) + meases_in_level(_header->B, lvl);
    }
    load();
  }

  void load() {
    _levels.resize(_header->levels_count);
    auto pos = _raw_data;
    for (size_t lvl = 0; lvl < _header->levels_count; ++lvl) {
      auto h = reinterpret_cast<level_header *>(pos);
      auto m = reinterpret_cast<Meas *>(pos + sizeof(level_header));
      level new_l;
      new_l.begin = m;
      new_l.hdr = h;
      _levels[lvl] = new_l;
      pos += sizeof(level_header) + meases_in_level(_header->B, lvl);
    }
  }

  void open(const std::string &fname) {
    mmap = utils::fs::MappedFile::open(fname);

    _header = reinterpret_cast<Header *>(mmap->data());
    _raw_data = reinterpret_cast<uint8_t *>(_header + sizeof(Header));

    load();
  }

  append_result append(const Meas &value) {
    if (_memvalues_pos < _header->B) {
      _memvalues[_memvalues_pos] = value;
      _memvalues_pos++;
      ++_header->_writed;
      return append_result(1, 0);
    } else {
      return append_to_levels(value);
    }
  }

  append_result append_to_levels(const Meas &value) {
    meas_time_compare less_by_time;
    std::sort(_memvalues.begin(), _memvalues.end(), less_by_time);
    _memvalues_pos = 0;
    size_t new_items_count = _header->_size_B + 1;
    size_t outlvl = dariadb::utils::ctz(~_size & new_items_count);
    //std::cout<<"outlvl: "<<outlvl<<std::endl;
    if (outlvl >= _header->levels_count) {

      return append_result(0, 1);
    }

    std::list<level *> to_merge;
    level tmp;
    level_header tmp_hdr;
    tmp.hdr = &tmp_hdr;
    tmp.hdr->lvl = 0;
    tmp.hdr->count = _memvalues.size();
    tmp.hdr->pos = _memvalues.size();
    tmp.begin = _memvalues.data();
    to_merge.push_back(&tmp);

    for (size_t i = 1; i <= outlvl; ++i) {
      to_merge.push_back(&_levels[i - 1]);
    }

    auto merge_target = _levels[outlvl];

    { // merge
      auto vals_size = to_merge.size();
      std::list<size_t> poses;
      for (size_t i = 0; i < vals_size; ++i) {
        poses.push_back(0);
      }
      while (!to_merge.empty()) {
        vals_size = to_merge.size();
        // get cur max;
        auto with_max_index = poses.begin();
        auto max_val = to_merge.front()->at(*with_max_index);
        auto it = to_merge.begin();
        auto with_max_index_it = it;
        for (auto pos_it = poses.begin(); pos_it != poses.end(); ++pos_it) {
          if (!less_by_time(max_val, (*it)->at(*pos_it))) {
            with_max_index = pos_it;
            max_val = (*it)->at(*pos_it);
            with_max_index_it = it;
          }
          ++it;
        }

        auto val = (*with_max_index_it)->at(*with_max_index);
        merge_target.push_back(val);
        // remove ended in-list
        (*with_max_index)++;
        auto cur_src = (*with_max_index_it);
        if ((*with_max_index) >= cur_src->hdr->count) {
          poses.erase(with_max_index);
          to_merge.erase(with_max_index_it);
        }
      }
    }
    for (size_t i = 1; i <= outlvl; ++i) {
      _levels[i - 1].clear();
    }
    ++_header->_size_B;
    ++_header->_writed;
    return append_result(1, 0);
  }
  Reader_ptr readInterval(Time from, Time to) { return nullptr; }
  virtual Reader_ptr readInTimePoint(Time time_point) { return nullptr; }

  virtual Reader_ptr readInterval(const IdArray &ids, Flag flag, Time from,
                                  Time to) {
      CapReader *raw=new CapReader;
      for(size_t j=0;j<_memvalues.size();++j){
          raw->_values.push_back(_memvalues[j]);
      }

      for(size_t i=0;i<this->_levels.size();++i){
          if(_levels[i].empty()){
              continue;
          }
          for(size_t j=0;j<_levels[i].hdr->pos;++j){
              raw->_values.push_back(_levels[i].at(j));
          }
      }
    return Reader_ptr(raw);
  }

  virtual Reader_ptr readInTimePoint(const IdArray &ids, Flag flag,
                                     Time time_point) {
    return nullptr;
  }

  dariadb::Time minTime() const { return _minTime; }
  dariadb::Time maxTime() const { return _maxTime; }

  bool flush() { return false; }

  size_t in_queue_size() const { return 0; }

  size_t levels_count() const { return _levels.size(); }

  size_t size() const { return _header->_writed; }

protected:
  dariadb::Time _minTime;
  dariadb::Time _maxTime;
  BaseStorage_ptr _stor;
  Capacitor::Params _params;

  dariadb::utils::fs::MappedFile::MapperFile_ptr mmap;
  Header *_header;
  uint8_t *_raw_data;
  std::vector<level> _levels;
  size_t _size;
  std::vector<Meas> _memvalues;
  size_t _memvalues_pos;
};

Capacitor::~Capacitor() {}

Capacitor::Capacitor(const BaseStorage_ptr stor, const Params &params)
    : _Impl(new Capacitor::Private(stor, params)) {}

dariadb::Time Capacitor::minTime() { return _Impl->minTime(); }

dariadb::Time Capacitor::maxTime() { return _Impl->maxTime(); }

bool Capacitor::flush() { // write all to storage;
  return _Impl->flush();
}

append_result dariadb::storage::Capacitor::append(const Meas &value) {
  return _Impl->append(value);
}

Reader_ptr dariadb::storage::Capacitor::readInterval(Time from, Time to) {
  return _Impl->readInterval(from, to);
}

Reader_ptr dariadb::storage::Capacitor::readInTimePoint(Time time_point) {
  return _Impl->readInTimePoint(time_point);
}

Reader_ptr dariadb::storage::Capacitor::readInterval(const IdArray &ids,
                                                     Flag flag, Time from,
                                                     Time to) {
  return _Impl->readInterval(ids, flag, from, to);
}

Reader_ptr dariadb::storage::Capacitor::readInTimePoint(const IdArray &ids,
                                                        Flag flag,
                                                        Time time_point) {
  return _Impl->readInTimePoint(ids, flag, time_point);
}

size_t dariadb::storage::Capacitor::in_queue_size() const {
  return _Impl->in_queue_size();
}

size_t dariadb::storage::Capacitor::levels_count() const {
  return _Impl->levels_count();
}

size_t dariadb::storage::Capacitor::size() const { return _Impl->size(); }
<commit_msg>readinterval work.<commit_after>/*
FileName:
GUID.aof
File struct:
   CapHeader| LevelHeader | Meas0 | Meas1|....| LevelHeader|Meas0 |Meas1...
Alg:
        1. Measurements save to COLA file struct
        2. When the append-only-file is fulle,
        it is converted to a sorted table (*.page) and a new log file is created
for future updates.
*/

#include "capacitor.h"
#include "../utils/cz.h"
#include "../utils/fs.h"
#include "../utils/utils.h"
#include <algorithm>
#include <cassert>
#include <limits>
#include <list>

using namespace dariadb;
using namespace dariadb::storage;

struct level_header {
  size_t lvl;
  size_t count;
  size_t pos;
};

struct level {
  level_header *hdr;
  Meas *begin;

  Meas at(size_t _pos) const { return begin[_pos]; }

  void push_back(const Meas &m) {
    begin[hdr->pos] = m;
    ++hdr->pos;
  }

  void clear() { hdr->pos = 0; }

  bool empty() const { return hdr->pos == 0; }
};

class CapReader : public storage::Reader {
public:
  bool isEnd() const override { return _values.empty(); }
  dariadb::IdArray getIds() const override {
    dariadb::IdSet res;
    for (auto v : _values) {
      res.insert(v.id);
    }
    return dariadb::IdArray(res.begin(), res.end());
  }
  void readNext(dariadb::storage::ReaderClb *clb) override {
    clb->call(_values.front());
    _values.pop_front();
  }
  Reader_ptr clone() const override { return nullptr; }
  void reset() override {}
  dariadb::Meas::MeasList _values;
  ~CapReader() {}
};

class Capacitor::Private {
public:
  struct meas_time_compare {
    bool operator()(const dariadb::Meas &lhs, const dariadb::Meas &rhs) const {
      return lhs.time < rhs.time;
    }
  };

  struct Header {
    bool is_dropped;
    size_t B;
    size_t size;    // sizeof file in bytes
    size_t _size_B; // how many block (sizeof(B)) addeded.
    size_t levels_count;
    size_t _writed;
  };

  Private(const BaseStorage_ptr stor, const Capacitor::Params &params)
      : _minTime(std::numeric_limits<dariadb::Time>::max()),
        _maxTime(std::numeric_limits<dariadb::Time>::min()), _stor(stor),
        _params(params), mmap(nullptr), _size(0) {
    open_or_create();
    _memvalues.resize(_header->B);
    _memvalues_pos = 0;
  }

  ~Private() {
    if (mmap != nullptr) {
      mmap->close();
    }
  }

  void open_or_create() {
    if (!dariadb::utils::fs::path_exists(_params.path)) {
      create();
    } else {
      auto logs = dariadb::utils::fs::ls(_params.path, CAP_FILE_EXT);
      if (logs.empty()) {
        create();
      } else {
        open(logs.front());
      }
    }
  }

  size_t one_block_size(size_t B) const { return sizeof(Meas) * B; }

  size_t block_in_level(size_t lev_num) const { return (size_t(1) << lev_num); }

  size_t meases_in_level(size_t B, size_t lvl) const {
    return one_block_size(B) * block_in_level(lvl);
  }

  uint64_t cap_size() const {
    uint64_t result = 0;

    for (size_t lvl = 0; lvl < _params.max_levels; ++lvl) {
      result += sizeof(level_header) + meases_in_level(_params.B, lvl); // 2^lvl
    }
    return result + sizeof(Header);
  }

  std::string file_name() {
    return dariadb::utils::fs::random_file_name(CAP_FILE_EXT);
  }

  void create() {
    dariadb::utils::fs::mkdir(_params.path);
    utils::fs::mkdir(_params.path);
    auto sz = cap_size();
    mmap = utils::fs::MappedFile::touch(
        utils::fs::append_path(_params.path, file_name()), sz);

    _header = reinterpret_cast<Header *>(mmap->data());
    _raw_data = reinterpret_cast<uint8_t *>(_header + sizeof(Header));
    _header->size = sz;
    _header->B = _params.B;
    _header->is_dropped = false;
    _header->levels_count = _params.max_levels;

    auto pos = _raw_data;
    for (size_t lvl = 0; lvl < _header->levels_count; ++lvl) {
      auto it = reinterpret_cast<level_header *>(pos);
      it->lvl = lvl;
      it->count = block_in_level(lvl) * _params.B;
      it->pos = 0;
      pos += sizeof(level_header) + meases_in_level(_header->B, lvl);
    }
    load();
  }

  void load() {
    _levels.resize(_header->levels_count);
    auto pos = _raw_data;
    for (size_t lvl = 0; lvl < _header->levels_count; ++lvl) {
      auto h = reinterpret_cast<level_header *>(pos);
      auto m = reinterpret_cast<Meas *>(pos + sizeof(level_header));
      level new_l;
      new_l.begin = m;
      new_l.hdr = h;
      _levels[lvl] = new_l;
      pos += sizeof(level_header) + meases_in_level(_header->B, lvl);
    }
  }

  void open(const std::string &fname) {
    mmap = utils::fs::MappedFile::open(fname);

    _header = reinterpret_cast<Header *>(mmap->data());
    _raw_data = reinterpret_cast<uint8_t *>(_header + sizeof(Header));

    load();
  }

  append_result append(const Meas &value) {
    if (_memvalues_pos < _header->B) {
      return append_to_mem(value);
    } else {
      return append_to_levels(value);
    }
  }

  append_result append_to_mem(const Meas &value) {
    _memvalues[_memvalues_pos] = value;
    _memvalues_pos++;
    ++_header->_writed;
    return append_result(1, 0);
  }

  append_result append_to_levels(const Meas &value) {
    meas_time_compare less_by_time;
    std::sort(_memvalues.begin(), _memvalues.end(), less_by_time);
    _memvalues_pos = 0;
    size_t new_items_count = _header->_size_B + 1;
    size_t outlvl = dariadb::utils::ctz(~_size & new_items_count);
    // std::cout<<"outlvl: "<<outlvl<<std::endl;
    if (outlvl >= _header->levels_count) {

      return append_result(0, 1);
    }

    std::list<level *> to_merge;
    level tmp;
    level_header tmp_hdr;
    tmp.hdr = &tmp_hdr;
    tmp.hdr->lvl = 0;
    tmp.hdr->count = _memvalues.size();
    tmp.hdr->pos = _memvalues.size();
    tmp.begin = _memvalues.data();
    to_merge.push_back(&tmp);

    for (size_t i = 1; i <= outlvl; ++i) {
      to_merge.push_back(&_levels[i - 1]);
    }

    auto merge_target = _levels[outlvl];

    { // merge
      auto vals_size = to_merge.size();
      std::list<size_t> poses;
      for (size_t i = 0; i < vals_size; ++i) {
        poses.push_back(0);
      }
      while (!to_merge.empty()) {
        vals_size = to_merge.size();
        // get cur max;
        auto with_max_index = poses.begin();
        auto max_val = to_merge.front()->at(*with_max_index);
        auto it = to_merge.begin();
        auto with_max_index_it = it;
        for (auto pos_it = poses.begin(); pos_it != poses.end(); ++pos_it) {
          if (!less_by_time(max_val, (*it)->at(*pos_it))) {
            with_max_index = pos_it;
            max_val = (*it)->at(*pos_it);
            with_max_index_it = it;
          }
          ++it;
        }

        auto val = (*with_max_index_it)->at(*with_max_index);
        merge_target.push_back(val);
        // remove ended in-list
        (*with_max_index)++;
        auto cur_src = (*with_max_index_it);
        if ((*with_max_index) >= cur_src->hdr->count) {
          poses.erase(with_max_index);
          to_merge.erase(with_max_index_it);
        }
      }
    }
    for (size_t i = 1; i <= outlvl; ++i) {
      _levels[i - 1].clear();
    }
    ++_header->_size_B;
    return append_to_mem(value);
  }
  Reader_ptr readInterval(Time from, Time to) { return nullptr; }
  virtual Reader_ptr readInTimePoint(Time time_point) { return nullptr; }

  virtual Reader_ptr readInterval(const IdArray &ids, Flag flag, Time from,
                                  Time to) {
    CapReader *raw = new CapReader;
    for (size_t j = 0; j < _memvalues_pos; ++j) {
        auto m=_memvalues[j];
        //TODO refact. move to utils libs or meas::static.
        if((in_filter(flag,m.flag)) && ((ids.size()==0) || (std::find(ids.begin(),ids.end(),m.id)!=ids.end()))){
            raw->_values.push_back(_memvalues[j]);
        }
    }

    for (size_t i = 0; i < this->_levels.size(); ++i) {
      if (_levels[i].empty()) {
        continue;
      }
      for (size_t j = 0; j < _levels[i].hdr->pos; ++j) {
          auto m=_levels[i].at(j);
          if((in_filter(flag,m.flag)) && ((ids.size()==0) || (std::find(ids.begin(),ids.end(),m.id)!=ids.end()))){
              raw->_values.push_back(m);
          }
      }
    }
    return Reader_ptr(raw);
  }

  virtual Reader_ptr readInTimePoint(const IdArray &ids, Flag flag,
                                     Time time_point) {
    return nullptr;
  }

  dariadb::Time minTime() const { return _minTime; }
  dariadb::Time maxTime() const { return _maxTime; }

  bool flush() { return false; }

  size_t in_queue_size() const { return 0; }

  size_t levels_count() const { return _levels.size(); }

  size_t size() const { return _header->_writed; }

protected:
  dariadb::Time _minTime;
  dariadb::Time _maxTime;
  BaseStorage_ptr _stor;
  Capacitor::Params _params;

  dariadb::utils::fs::MappedFile::MapperFile_ptr mmap;
  Header *_header;
  uint8_t *_raw_data;
  std::vector<level> _levels;
  size_t _size;
  std::vector<Meas> _memvalues;
  size_t _memvalues_pos;
};

Capacitor::~Capacitor() {}

Capacitor::Capacitor(const BaseStorage_ptr stor, const Params &params)
    : _Impl(new Capacitor::Private(stor, params)) {}

dariadb::Time Capacitor::minTime() { return _Impl->minTime(); }

dariadb::Time Capacitor::maxTime() { return _Impl->maxTime(); }

bool Capacitor::flush() { // write all to storage;
  return _Impl->flush();
}

append_result dariadb::storage::Capacitor::append(const Meas &value) {
  return _Impl->append(value);
}

Reader_ptr dariadb::storage::Capacitor::readInterval(Time from, Time to) {
  return _Impl->readInterval(from, to);
}

Reader_ptr dariadb::storage::Capacitor::readInTimePoint(Time time_point) {
  return _Impl->readInTimePoint(time_point);
}

Reader_ptr dariadb::storage::Capacitor::readInterval(const IdArray &ids,
                                                     Flag flag, Time from,
                                                     Time to) {
  return _Impl->readInterval(ids, flag, from, to);
}

Reader_ptr dariadb::storage::Capacitor::readInTimePoint(const IdArray &ids,
                                                        Flag flag,
                                                        Time time_point) {
  return _Impl->readInTimePoint(ids, flag, time_point);
}

size_t dariadb::storage::Capacitor::in_queue_size() const {
  return _Impl->in_queue_size();
}

size_t dariadb::storage::Capacitor::levels_count() const {
  return _Impl->levels_count();
}

size_t dariadb::storage::Capacitor::size() const { return _Impl->size(); }
<|endoftext|>