<commit_before>#include "Block.h"
#include "BlockCache.h"
#include "DirConst.h"
#include "Directory.h"
#include "DirectoryBuilder.h"
#include "MemoryDataSource.h"
#include "FilesystemException.h"
#include "Rad50.h"
#include "gtest/gtest.h"

#include <array>
#include <cerrno>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <unistd.h>
#include <vector>

using namespace RT11FS;
using namespace RT11FS::Dir;

using std::array;
using std::copy;
using std::out_of_range;
using std::make_unique;
using std::vector;

namespace {

class DirectoryTest : public ::testing::Test
{
protected:
  static const int sectors = 256;

  DirectoryTest()
    : dataSource(make_unique<MemoryDataSource>(sectors * Block::SECTOR_SIZE))
    , blockCache(make_unique<BlockCache>(dataSource.get()))
    , data(dataSource->getData())
    , builder(*dataSource.get())
  {
  }

  std::unique_ptr<MemoryDataSource> dataSource;
  std::unique_ptr<BlockCache> blockCache;
  std::vector<uint8_t> &data;
  DirectoryBuilder builder;
};

TEST_F(DirectoryTest, BasicEnumeration)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto dirp = dir.startScan();
  EXPECT_TRUE(dirp.beforeStart());
  EXPECT_FALSE(dirp.afterEnd());

  auto firstDataSector = FIRST_SEGMENT_SECTOR + segments * SECTORS_PER_SEGMENT;

  ++dirp;
  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_FALSE(dirp.afterEnd());

  EXPECT_EQ(dirp.getDataSector(), firstDataSector);
  EXPECT_EQ(dirp.getWord(TOTAL_LENGTH_WORD), 2);
  EXPECT_EQ(dirp.getSegment(), 1);
  EXPECT_EQ(dirp.getIndex(), 0);
  EXPECT_EQ(dirp.offset(0), FIRST_ENTRY_OFFSET);
  EXPECT_FALSE(dirp.hasStatus(E_EOS));
  EXPECT_TRUE(dirp.hasStatus(E_PERM));

  ++dirp;
  EXPECT_EQ(dirp.getDataSector(), firstDataSector + 2);
  EXPECT_EQ(dirp.getWord(TOTAL_LENGTH_WORD), sectors - firstDataSector - 2);
  EXPECT_EQ(dirp.getSegment(), 1);
  EXPECT_EQ(dirp.getIndex(), 1);
  EXPECT_EQ(dirp.offset(0), FIRST_ENTRY_OFFSET + ENTRY_LENGTH);
  EXPECT_TRUE(dirp.hasStatus(E_EOS));
  EXPECT_FALSE(dirp.hasStatus(E_PERM));

  ++dirp;
  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_TRUE(dirp.afterEnd());
}

TEST_F(DirectoryTest, GetByName)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_PERM, 2, { 075131, 062000, 075273 }},      // SWAP.SYS
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto ent = DirEnt {};
  auto err = dir.getEnt("SWAP.SYS", ent);
  EXPECT_EQ(err, 0);
  EXPECT_EQ(ent.status, E_PERM);
  EXPECT_EQ(ent.length, 2 * Block::SECTOR_SIZE);
  EXPECT_EQ(ent.sector0, 6 + 8 * 2 + 2);

  err = dir.getEnt("NONONO.NOM", ent);
  EXPECT_EQ(err, -ENOENT);
}

TEST_F(DirectoryTest, GetByNameInSecondSegment)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_EOS},
    },
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_PERM, 2, { 075131, 062000, 075273 }},      // SWAP.SYS
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto ent = DirEnt {};
  auto err = dir.getEnt("SWAP.SYS", ent);
  EXPECT_EQ(err, 0);
  EXPECT_EQ(ent.status, E_PERM);
  EXPECT_EQ(ent.length, 2 * Block::SECTOR_SIZE);
  EXPECT_EQ(ent.sector0, 6 + 8 * 2 + 2);
}

TEST_F(DirectoryTest, GetByRad50)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_PERM, 2, { 075131, 062000, 075273 }},      // SWAP.SYS
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto search = Rad50Name { 075131, 062000, 075273 };

  auto dirp = dir.getDirPointer(search);

  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_FALSE(dirp.afterEnd());
  EXPECT_EQ(dirp.getSegment(), 1);
  EXPECT_EQ(dirp.getIndex(), 1);

  search = Rad50Name { 075131, 062000, 075274 };

  dirp = dir.getDirPointer(search);

  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_TRUE(dirp.afterEnd());
}

TEST_F(DirectoryTest, GetByRad50InSecondSegment)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_EOS},
    },
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_PERM, 2, { 075131, 062000, 075273 }},      // SWAP.SYS
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto search = Rad50Name { 075131, 062000, 075273 };

  auto dirp = dir.getDirPointer(search);

  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_FALSE(dirp.afterEnd());
  EXPECT_EQ(dirp.getSegment(), 2);
  EXPECT_EQ(dirp.getIndex(), 1);
}

}
<commit_msg>Test get entry by directory pointer<commit_after>#include "Block.h"
#include "BlockCache.h"
#include "DirConst.h"
#include "Directory.h"
#include "DirectoryBuilder.h"
#include "MemoryDataSource.h"
#include "FilesystemException.h"
#include "Rad50.h"
#include "gtest/gtest.h"

#include <array>
#include <cerrno>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <unistd.h>
#include <vector>

using namespace RT11FS;
using namespace RT11FS::Dir;

using std::array;
using std::copy;
using std::out_of_range;
using std::make_unique;
using std::vector;

namespace {

class DirectoryTest : public ::testing::Test
{
protected:
  static const int sectors = 256;

  DirectoryTest()
    : dataSource(make_unique<MemoryDataSource>(sectors * Block::SECTOR_SIZE))
    , blockCache(make_unique<BlockCache>(dataSource.get()))
    , data(dataSource->getData())
    , builder(*dataSource.get())
  {
  }

  std::unique_ptr<MemoryDataSource> dataSource;
  std::unique_ptr<BlockCache> blockCache;
  std::vector<uint8_t> &data;
  DirectoryBuilder builder;
};

TEST_F(DirectoryTest, BasicEnumeration)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto dirp = dir.startScan();
  EXPECT_TRUE(dirp.beforeStart());
  EXPECT_FALSE(dirp.afterEnd());

  auto firstDataSector = FIRST_SEGMENT_SECTOR + segments * SECTORS_PER_SEGMENT;

  ++dirp;
  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_FALSE(dirp.afterEnd());

  EXPECT_EQ(dirp.getDataSector(), firstDataSector);
  EXPECT_EQ(dirp.getWord(TOTAL_LENGTH_WORD), 2);
  EXPECT_EQ(dirp.getSegment(), 1);
  EXPECT_EQ(dirp.getIndex(), 0);
  EXPECT_EQ(dirp.offset(0), FIRST_ENTRY_OFFSET);
  EXPECT_FALSE(dirp.hasStatus(E_EOS));
  EXPECT_TRUE(dirp.hasStatus(E_PERM));

  ++dirp;
  EXPECT_EQ(dirp.getDataSector(), firstDataSector + 2);
  EXPECT_EQ(dirp.getWord(TOTAL_LENGTH_WORD), sectors - firstDataSector - 2);
  EXPECT_EQ(dirp.getSegment(), 1);
  EXPECT_EQ(dirp.getIndex(), 1);
  EXPECT_EQ(dirp.offset(0), FIRST_ENTRY_OFFSET + ENTRY_LENGTH);
  EXPECT_TRUE(dirp.hasStatus(E_EOS));
  EXPECT_FALSE(dirp.hasStatus(E_PERM));

  ++dirp;
  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_TRUE(dirp.afterEnd());
}

TEST_F(DirectoryTest, GetByName)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_PERM, 2, { 075131, 062000, 075273 }},      // SWAP.SYS
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto ent = DirEnt {};
  auto err = dir.getEnt("SWAP.SYS", ent);
  EXPECT_EQ(err, 0);
  EXPECT_EQ(ent.status, E_PERM);
  EXPECT_EQ(ent.length, 2 * Block::SECTOR_SIZE);
  EXPECT_EQ(ent.sector0, 6 + 8 * 2 + 2);

  err = dir.getEnt("NONONO.NOM", ent);
  EXPECT_EQ(err, -ENOENT);
}

TEST_F(DirectoryTest, GetByNameInSecondSegment)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_EOS},
    },
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_PERM, 2, { 075131, 062000, 075273 }},      // SWAP.SYS
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto ent = DirEnt {};
  auto err = dir.getEnt("SWAP.SYS", ent);
  EXPECT_EQ(err, 0);
  EXPECT_EQ(ent.status, E_PERM);
  EXPECT_EQ(ent.length, 2 * Block::SECTOR_SIZE);
  EXPECT_EQ(ent.sector0, 6 + 8 * 2 + 2);
}

TEST_F(DirectoryTest, GetByRad50)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_PERM, 3, { 075131, 062000, 075273 }},      // SWAP.SYS
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto search = Rad50Name { 075131, 062000, 075273 };

  auto dirp = dir.getDirPointer(search);

  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_FALSE(dirp.afterEnd());
  EXPECT_EQ(dirp.getSegment(), 1);
  EXPECT_EQ(dirp.getIndex(), 1);

  auto ent = DirEnt {};
  auto gotent = dir.getEnt(dirp, ent);
  EXPECT_TRUE(gotent);
  EXPECT_EQ(ent.status, E_PERM);
  EXPECT_EQ(ent.length, 3 * Block::SECTOR_SIZE);
  EXPECT_EQ(ent.sector0, 6 + 8 * 2 + 2);

  search = Rad50Name { 075131, 062000, 075274 };

  dirp = dir.getDirPointer(search);

  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_TRUE(dirp.afterEnd());
}

TEST_F(DirectoryTest, GetByRad50InSecondSegment)
{
  auto segments = 8;

  using Ent = DirectoryBuilder::DirEntry;
  vector<vector<Ent>> dirdata = {
    {
      Ent {E_EOS},
    },
    {
      Ent {E_PERM, 2, { 1, 2, 3 }},
      Ent {E_PERM, 2, { 075131, 062000, 075273 }},      // SWAP.SYS
      Ent {E_EOS, DirectoryBuilder::REST_OF_DATA}
    },
  };

  builder.formatWithEntries(segments, dirdata);

  auto dir = Directory {blockCache.get()};

  auto search = Rad50Name { 075131, 062000, 075273 };

  auto dirp = dir.getDirPointer(search);

  EXPECT_FALSE(dirp.beforeStart());
  EXPECT_FALSE(dirp.afterEnd());
  EXPECT_EQ(dirp.getSegment(), 2);
  EXPECT_EQ(dirp.getIndex(), 1);
}

}
<|endoftext|>


<commit_before><commit_msg>Note: purify and valgrind suppressions already exist.  If the purify expression isn't being used, something else is wrong.<commit_after><|endoftext|>


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

#include "ui/views/controls/combobox/native_combobox_views.h"

#include <algorithm>

#include "grit/ui_resources.h"
#include "ui/base/events/event.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/path.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/color_constants.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/focusable_border.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"

namespace views {

namespace {

// Define the size of the insets.
const int kTopInsetSize = 4;
const int kLeftInsetSize = 4;
const int kBottomInsetSize = 4;
const int kRightInsetSize = 4;

// Menu border widths
const int kMenuBorderWidthLeft = 1;
const int kMenuBorderWidthTop = 1;
const int kMenuBorderWidthRight = 1;
const int kMenuBorderWidthBottom = 2;

// Limit how small a combobox can be.
const int kMinComboboxWidth = 148;

// Size of the combobox arrow margins
const int kDisclosureArrowLeftPadding = 7;
const int kDisclosureArrowRightPadding = 7;

// Define the id of the first item in the menu (since it needs to be > 0)
const int kFirstMenuItemId = 1000;

const SkColor kInvalidTextColor = SK_ColorWHITE;

// The background to use for invalid comboboxes.
class InvalidBackground : public Background {
 public:
  InvalidBackground() {}
  virtual ~InvalidBackground() {}

  // Overridden from Background:
  virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE {
    gfx::Rect bounds(view->GetLocalBounds());
    // Inset by 2 to leave 1 empty pixel between background and border.
    bounds.Inset(2, 2, 2, 2);
    canvas->FillRect(bounds, kWarningColor);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(InvalidBackground);
};

}  // namespace

const char NativeComboboxViews::kViewClassName[] =
    "views/NativeComboboxViews";

NativeComboboxViews::NativeComboboxViews(Combobox* combobox)
    : combobox_(combobox),
      text_border_(new FocusableBorder()),
      disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
          IDR_MENU_DROPARROW).ToImageSkia()),
      dropdown_open_(false),
      selected_index_(-1),
      content_width_(0),
      content_height_(0) {
  set_border(text_border_);
}

NativeComboboxViews::~NativeComboboxViews() {
}

////////////////////////////////////////////////////////////////////////////////
// NativeComboboxViews, View overrides:

bool NativeComboboxViews::OnMousePressed(const ui::MouseEvent& mouse_event) {
  combobox_->RequestFocus();
  if (mouse_event.IsLeftMouseButton()) {
    UpdateFromModel();
    ShowDropDownMenu();
  }

  return true;
}

bool NativeComboboxViews::OnMouseDragged(const ui::MouseEvent& mouse_event) {
  return true;
}

bool NativeComboboxViews::OnKeyPressed(const ui::KeyEvent& key_event) {
  // TODO(oshima): handle IME.
  DCHECK_EQ(key_event.type(), ui::ET_KEY_PRESSED);

  // Check if we are in the default state (-1) and set to first item.
  if (selected_index_ == -1)
    selected_index_ = 0;

  int new_index = selected_index_;
  switch (key_event.key_code()) {
    // Move to the next item if any.
    case ui::VKEY_DOWN:
      if (new_index < (combobox_->model()->GetItemCount() - 1))
        new_index++;
      break;

    // Move to the end of the list.
    case ui::VKEY_END:
    case ui::VKEY_NEXT:
      new_index = combobox_->model()->GetItemCount() - 1;
      break;

    // Move to the beginning of the list.
   case ui::VKEY_HOME:
   case ui::VKEY_PRIOR:
      new_index = 0;
      break;

    // Move to the previous item if any.
    case ui::VKEY_UP:
      if (new_index > 0)
        new_index--;
      break;

    default:
      return false;
  }

  if (new_index != selected_index_) {
    selected_index_ = new_index;
    combobox_->SelectionChanged();
    SchedulePaint();
  }

  return true;
}

bool NativeComboboxViews::OnKeyReleased(const ui::KeyEvent& key_event) {
  return true;
}

void NativeComboboxViews::OnPaint(gfx::Canvas* canvas) {
  text_border_->set_has_focus(combobox_->HasFocus());
  OnPaintBackground(canvas);
  PaintText(canvas);
  OnPaintBorder(canvas);
}

void NativeComboboxViews::OnFocus() {
  NOTREACHED();
}

void NativeComboboxViews::OnBlur() {
  NOTREACHED();
}

/////////////////////////////////////////////////////////////////
// NativeComboboxViews, ui::EventHandler overrides:

void NativeComboboxViews::OnGestureEvent(ui::GestureEvent* gesture) {
  if (gesture->type() == ui::ET_GESTURE_TAP) {
    UpdateFromModel();
    ShowDropDownMenu();
    gesture->StopPropagation();
    return;
  }
  View::OnGestureEvent(gesture);
}

/////////////////////////////////////////////////////////////////
// NativeComboboxViews, NativeComboboxWrapper overrides:

void NativeComboboxViews::UpdateFromModel() {
  int max_width = 0;
  const gfx::Font& font = Combobox::GetFont();

  MenuItemView* menu = new MenuItemView(this);
  // MenuRunner owns |menu|.
  dropdown_list_menu_runner_.reset(new MenuRunner(menu));

  int num_items = combobox_->model()->GetItemCount();
  for (int i = 0; i < num_items; ++i) {
    if (combobox_->model()->IsItemSeparatorAt(i)) {
      menu->AppendSeparator();
      continue;
    }

    string16 text = combobox_->model()->GetItemAt(i);

    // Inserting the Unicode formatting characters if necessary so that the
    // text is displayed correctly in right-to-left UIs.
    base::i18n::AdjustStringForLocaleDirection(&text);

    menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL);
    max_width = std::max(max_width, font.GetStringWidth(text));
  }

  content_width_ = max_width;
  content_height_ = font.GetHeight();
}

void NativeComboboxViews::UpdateSelectedIndex() {
  selected_index_ = combobox_->selected_index();
  SchedulePaint();
}

void NativeComboboxViews::UpdateEnabled() {
  SetEnabled(combobox_->enabled());
}

int NativeComboboxViews::GetSelectedIndex() const {
  return selected_index_;
}

bool NativeComboboxViews::IsDropdownOpen() const {
  return dropdown_open_;
}

gfx::Size NativeComboboxViews::GetPreferredSize() {
  if (content_width_ == 0)
    UpdateFromModel();

  // The preferred size will drive the local bounds which in turn is used to set
  // the minimum width for the dropdown list.
  gfx::Insets insets = GetInsets();
  int total_width = content_width_ + insets.width() +
      kDisclosureArrowLeftPadding + disclosure_arrow_->width() +
      kDisclosureArrowRightPadding;

  return gfx::Size(std::min(kMinComboboxWidth, total_width),
                   content_height_ + insets.height());
}

View* NativeComboboxViews::GetView() {
  return this;
}

void NativeComboboxViews::SetFocus() {
  text_border_->set_has_focus(true);
}

void NativeComboboxViews::ValidityStateChanged() {
  if (combobox_->invalid()) {
    text_border_->SetColor(kWarningColor);
    set_background(new InvalidBackground());
  } else {
    text_border_->UseDefaultColor();
    set_background(NULL);
  }
  SchedulePaint();
}

bool NativeComboboxViews::HandleKeyPressed(const ui::KeyEvent& e) {
  return OnKeyPressed(e);
}

bool NativeComboboxViews::HandleKeyReleased(const ui::KeyEvent& e) {
  return false;  // crbug.com/127520
}

void NativeComboboxViews::HandleFocus() {
  SchedulePaint();
}

void NativeComboboxViews::HandleBlur() {
}

gfx::NativeView NativeComboboxViews::GetTestingHandle() const {
  NOTREACHED();
  return NULL;
}

/////////////////////////////////////////////////////////////////
// NativeComboboxViews, views::MenuDelegate overrides:
// (note that the id received is offset by kFirstMenuItemId)

bool NativeComboboxViews::IsItemChecked(int id) const {
  return false;
}

bool NativeComboboxViews::IsCommandEnabled(int id) const {
  return true;
}

void NativeComboboxViews::ExecuteCommand(int id) {
  // Revert menu offset to map back to combobox model.
  id -= kFirstMenuItemId;
  DCHECK_LT(id, combobox_->model()->GetItemCount());
  selected_index_ = id;
  combobox_->SelectionChanged();
  SchedulePaint();
}

bool NativeComboboxViews::GetAccelerator(int id, ui::Accelerator* accel) {
  return false;
}

/////////////////////////////////////////////////////////////////
// NativeComboboxViews private methods:

void NativeComboboxViews::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
  rect->set_x(GetMirroredXForRect(*rect));
}

void NativeComboboxViews::PaintText(gfx::Canvas* canvas) {
  gfx::Insets insets = GetInsets();

  canvas->Save();
  canvas->ClipRect(GetContentsBounds());

  int x = insets.left();
  int y = insets.top();
  int text_height = height() - insets.height();
  SkColor text_color = combobox_->invalid() ? kInvalidTextColor :
      GetNativeTheme()->GetSystemColor(
          ui::NativeTheme::kColorId_LabelEnabledColor);

  int index = GetSelectedIndex();
  if (index < 0 || index > combobox_->model()->GetItemCount())
    index = 0;
  string16 text = combobox_->model()->GetItemAt(index);

  int disclosure_arrow_offset = width() - disclosure_arrow_->width()
      - kDisclosureArrowLeftPadding - kDisclosureArrowRightPadding;

  const gfx::Font& font = Combobox::GetFont();
  int text_width = font.GetStringWidth(text);
  if ((text_width + insets.width()) > disclosure_arrow_offset)
    text_width = disclosure_arrow_offset - insets.width();

  gfx::Rect text_bounds(x, y, text_width, text_height);
  AdjustBoundsForRTLUI(&text_bounds);
  canvas->DrawStringInt(text, font, text_color, text_bounds);

  gfx::Rect arrow_bounds(disclosure_arrow_offset + kDisclosureArrowLeftPadding,
                         height() / 2 - disclosure_arrow_->height() / 2,
                         disclosure_arrow_->width(),
                         disclosure_arrow_->height());
  AdjustBoundsForRTLUI(&arrow_bounds);

  SkPaint paint;
  // This makes the arrow subtractive.
  if (combobox_->invalid())
    paint.setXfermodeMode(SkXfermode::kDstOut_Mode);
  canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(),
                       paint);

  canvas->Restore();
}

void NativeComboboxViews::ShowDropDownMenu() {

  if (!dropdown_list_menu_runner_.get())
    UpdateFromModel();

  // Extend the menu to the width of the combobox.
  MenuItemView* menu = dropdown_list_menu_runner_->GetMenu();
  SubmenuView* submenu = menu->CreateSubmenu();
  submenu->set_minimum_preferred_width(size().width() -
                                (kMenuBorderWidthLeft + kMenuBorderWidthRight));

  gfx::Rect lb = GetLocalBounds();
  gfx::Point menu_position(lb.origin());

  // Inset the menu's requested position so the border of the menu lines up
  // with the border of the combobox.
  menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
  menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
  lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));

  View::ConvertPointToScreen(this, &menu_position);
  if (menu_position.x() < 0)
      menu_position.set_x(0);

  gfx::Rect bounds(menu_position, lb.size());

  dropdown_open_ = true;
  if (dropdown_list_menu_runner_->RunMenuAt(
          GetWidget(), NULL, bounds, MenuItemView::TOPLEFT,
          MenuRunner::HAS_MNEMONICS) == MenuRunner::MENU_DELETED)
    return;
  dropdown_open_ = false;

  // Need to explicitly clear mouse handler so that events get sent
  // properly after the menu finishes running. If we don't do this, then
  // the first click to other parts of the UI is eaten.
  SetMouseHandler(NULL);
}

////////////////////////////////////////////////////////////////////////////////
// NativeComboboxWrapper, public:

#if defined(USE_AURA)
// static
NativeComboboxWrapper* NativeComboboxWrapper::CreateWrapper(
    Combobox* combobox) {
  return new NativeComboboxViews(combobox);
}
#endif

}  // namespace views
<commit_msg>fix inverted size bound in views::Combobox<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/combobox/native_combobox_views.h"

#include <algorithm>

#include "grit/ui_resources.h"
#include "ui/base/events/event.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/path.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/color_constants.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/focusable_border.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"

namespace views {

namespace {

// Define the size of the insets.
const int kTopInsetSize = 4;
const int kLeftInsetSize = 4;
const int kBottomInsetSize = 4;
const int kRightInsetSize = 4;

// Menu border widths
const int kMenuBorderWidthLeft = 1;
const int kMenuBorderWidthTop = 1;
const int kMenuBorderWidthRight = 1;
const int kMenuBorderWidthBottom = 2;

// Limit how small a combobox can be.
const int kMinComboboxWidth = 25;

// Size of the combobox arrow margins
const int kDisclosureArrowLeftPadding = 7;
const int kDisclosureArrowRightPadding = 7;

// Define the id of the first item in the menu (since it needs to be > 0)
const int kFirstMenuItemId = 1000;

const SkColor kInvalidTextColor = SK_ColorWHITE;

// The background to use for invalid comboboxes.
class InvalidBackground : public Background {
 public:
  InvalidBackground() {}
  virtual ~InvalidBackground() {}

  // Overridden from Background:
  virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE {
    gfx::Rect bounds(view->GetLocalBounds());
    // Inset by 2 to leave 1 empty pixel between background and border.
    bounds.Inset(2, 2, 2, 2);
    canvas->FillRect(bounds, kWarningColor);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(InvalidBackground);
};

}  // namespace

const char NativeComboboxViews::kViewClassName[] =
    "views/NativeComboboxViews";

NativeComboboxViews::NativeComboboxViews(Combobox* combobox)
    : combobox_(combobox),
      text_border_(new FocusableBorder()),
      disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
          IDR_MENU_DROPARROW).ToImageSkia()),
      dropdown_open_(false),
      selected_index_(-1),
      content_width_(0),
      content_height_(0) {
  set_border(text_border_);
}

NativeComboboxViews::~NativeComboboxViews() {
}

////////////////////////////////////////////////////////////////////////////////
// NativeComboboxViews, View overrides:

bool NativeComboboxViews::OnMousePressed(const ui::MouseEvent& mouse_event) {
  combobox_->RequestFocus();
  if (mouse_event.IsLeftMouseButton()) {
    UpdateFromModel();
    ShowDropDownMenu();
  }

  return true;
}

bool NativeComboboxViews::OnMouseDragged(const ui::MouseEvent& mouse_event) {
  return true;
}

bool NativeComboboxViews::OnKeyPressed(const ui::KeyEvent& key_event) {
  // TODO(oshima): handle IME.
  DCHECK_EQ(key_event.type(), ui::ET_KEY_PRESSED);

  // Check if we are in the default state (-1) and set to first item.
  if (selected_index_ == -1)
    selected_index_ = 0;

  int new_index = selected_index_;
  switch (key_event.key_code()) {
    // Move to the next item if any.
    case ui::VKEY_DOWN:
      if (new_index < (combobox_->model()->GetItemCount() - 1))
        new_index++;
      break;

    // Move to the end of the list.
    case ui::VKEY_END:
    case ui::VKEY_NEXT:
      new_index = combobox_->model()->GetItemCount() - 1;
      break;

    // Move to the beginning of the list.
   case ui::VKEY_HOME:
   case ui::VKEY_PRIOR:
      new_index = 0;
      break;

    // Move to the previous item if any.
    case ui::VKEY_UP:
      if (new_index > 0)
        new_index--;
      break;

    default:
      return false;
  }

  if (new_index != selected_index_) {
    selected_index_ = new_index;
    combobox_->SelectionChanged();
    SchedulePaint();
  }

  return true;
}

bool NativeComboboxViews::OnKeyReleased(const ui::KeyEvent& key_event) {
  return true;
}

void NativeComboboxViews::OnPaint(gfx::Canvas* canvas) {
  text_border_->set_has_focus(combobox_->HasFocus());
  OnPaintBackground(canvas);
  PaintText(canvas);
  OnPaintBorder(canvas);
}

void NativeComboboxViews::OnFocus() {
  NOTREACHED();
}

void NativeComboboxViews::OnBlur() {
  NOTREACHED();
}

/////////////////////////////////////////////////////////////////
// NativeComboboxViews, ui::EventHandler overrides:

void NativeComboboxViews::OnGestureEvent(ui::GestureEvent* gesture) {
  if (gesture->type() == ui::ET_GESTURE_TAP) {
    UpdateFromModel();
    ShowDropDownMenu();
    gesture->StopPropagation();
    return;
  }
  View::OnGestureEvent(gesture);
}

/////////////////////////////////////////////////////////////////
// NativeComboboxViews, NativeComboboxWrapper overrides:

void NativeComboboxViews::UpdateFromModel() {
  int max_width = 0;
  const gfx::Font& font = Combobox::GetFont();

  MenuItemView* menu = new MenuItemView(this);
  // MenuRunner owns |menu|.
  dropdown_list_menu_runner_.reset(new MenuRunner(menu));

  int num_items = combobox_->model()->GetItemCount();
  for (int i = 0; i < num_items; ++i) {
    if (combobox_->model()->IsItemSeparatorAt(i)) {
      menu->AppendSeparator();
      continue;
    }

    string16 text = combobox_->model()->GetItemAt(i);

    // Inserting the Unicode formatting characters if necessary so that the
    // text is displayed correctly in right-to-left UIs.
    base::i18n::AdjustStringForLocaleDirection(&text);

    menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL);
    max_width = std::max(max_width, font.GetStringWidth(text));
  }

  content_width_ = max_width;
  content_height_ = font.GetHeight();
}

void NativeComboboxViews::UpdateSelectedIndex() {
  selected_index_ = combobox_->selected_index();
  SchedulePaint();
}

void NativeComboboxViews::UpdateEnabled() {
  SetEnabled(combobox_->enabled());
}

int NativeComboboxViews::GetSelectedIndex() const {
  return selected_index_;
}

bool NativeComboboxViews::IsDropdownOpen() const {
  return dropdown_open_;
}

gfx::Size NativeComboboxViews::GetPreferredSize() {
  if (content_width_ == 0)
    UpdateFromModel();

  // The preferred size will drive the local bounds which in turn is used to set
  // the minimum width for the dropdown list.
  gfx::Insets insets = GetInsets();
  int total_width = std::max(kMinComboboxWidth, content_width_) +
      insets.width() + kDisclosureArrowLeftPadding +
      disclosure_arrow_->width() + kDisclosureArrowRightPadding;

  return gfx::Size(total_width, content_height_ + insets.height());
}

View* NativeComboboxViews::GetView() {
  return this;
}

void NativeComboboxViews::SetFocus() {
  text_border_->set_has_focus(true);
}

void NativeComboboxViews::ValidityStateChanged() {
  if (combobox_->invalid()) {
    text_border_->SetColor(kWarningColor);
    set_background(new InvalidBackground());
  } else {
    text_border_->UseDefaultColor();
    set_background(NULL);
  }
  SchedulePaint();
}

bool NativeComboboxViews::HandleKeyPressed(const ui::KeyEvent& e) {
  return OnKeyPressed(e);
}

bool NativeComboboxViews::HandleKeyReleased(const ui::KeyEvent& e) {
  return false;  // crbug.com/127520
}

void NativeComboboxViews::HandleFocus() {
  SchedulePaint();
}

void NativeComboboxViews::HandleBlur() {
}

gfx::NativeView NativeComboboxViews::GetTestingHandle() const {
  NOTREACHED();
  return NULL;
}

/////////////////////////////////////////////////////////////////
// NativeComboboxViews, views::MenuDelegate overrides:
// (note that the id received is offset by kFirstMenuItemId)

bool NativeComboboxViews::IsItemChecked(int id) const {
  return false;
}

bool NativeComboboxViews::IsCommandEnabled(int id) const {
  return true;
}

void NativeComboboxViews::ExecuteCommand(int id) {
  // Revert menu offset to map back to combobox model.
  id -= kFirstMenuItemId;
  DCHECK_LT(id, combobox_->model()->GetItemCount());
  selected_index_ = id;
  combobox_->SelectionChanged();
  SchedulePaint();
}

bool NativeComboboxViews::GetAccelerator(int id, ui::Accelerator* accel) {
  return false;
}

/////////////////////////////////////////////////////////////////
// NativeComboboxViews private methods:

void NativeComboboxViews::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
  rect->set_x(GetMirroredXForRect(*rect));
}

void NativeComboboxViews::PaintText(gfx::Canvas* canvas) {
  gfx::Insets insets = GetInsets();

  canvas->Save();
  canvas->ClipRect(GetContentsBounds());

  int x = insets.left();
  int y = insets.top();
  int text_height = height() - insets.height();
  SkColor text_color = combobox_->invalid() ? kInvalidTextColor :
      GetNativeTheme()->GetSystemColor(
          ui::NativeTheme::kColorId_LabelEnabledColor);

  int index = GetSelectedIndex();
  if (index < 0 || index > combobox_->model()->GetItemCount())
    index = 0;
  string16 text = combobox_->model()->GetItemAt(index);

  int disclosure_arrow_offset = width() - disclosure_arrow_->width()
      - kDisclosureArrowLeftPadding - kDisclosureArrowRightPadding;

  const gfx::Font& font = Combobox::GetFont();
  int text_width = font.GetStringWidth(text);
  if ((text_width + insets.width()) > disclosure_arrow_offset)
    text_width = disclosure_arrow_offset - insets.width();

  gfx::Rect text_bounds(x, y, text_width, text_height);
  AdjustBoundsForRTLUI(&text_bounds);
  canvas->DrawStringInt(text, font, text_color, text_bounds);

  gfx::Rect arrow_bounds(disclosure_arrow_offset + kDisclosureArrowLeftPadding,
                         height() / 2 - disclosure_arrow_->height() / 2,
                         disclosure_arrow_->width(),
                         disclosure_arrow_->height());
  AdjustBoundsForRTLUI(&arrow_bounds);

  SkPaint paint;
  // This makes the arrow subtractive.
  if (combobox_->invalid())
    paint.setXfermodeMode(SkXfermode::kDstOut_Mode);
  canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(),
                       paint);

  canvas->Restore();
}

void NativeComboboxViews::ShowDropDownMenu() {

  if (!dropdown_list_menu_runner_.get())
    UpdateFromModel();

  // Extend the menu to the width of the combobox.
  MenuItemView* menu = dropdown_list_menu_runner_->GetMenu();
  SubmenuView* submenu = menu->CreateSubmenu();
  submenu->set_minimum_preferred_width(size().width() -
                                (kMenuBorderWidthLeft + kMenuBorderWidthRight));

  gfx::Rect lb = GetLocalBounds();
  gfx::Point menu_position(lb.origin());

  // Inset the menu's requested position so the border of the menu lines up
  // with the border of the combobox.
  menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
  menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
  lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));

  View::ConvertPointToScreen(this, &menu_position);
  if (menu_position.x() < 0)
      menu_position.set_x(0);

  gfx::Rect bounds(menu_position, lb.size());

  dropdown_open_ = true;
  if (dropdown_list_menu_runner_->RunMenuAt(
          GetWidget(), NULL, bounds, MenuItemView::TOPLEFT,
          MenuRunner::HAS_MNEMONICS) == MenuRunner::MENU_DELETED)
    return;
  dropdown_open_ = false;

  // Need to explicitly clear mouse handler so that events get sent
  // properly after the menu finishes running. If we don't do this, then
  // the first click to other parts of the UI is eaten.
  SetMouseHandler(NULL);
}

////////////////////////////////////////////////////////////////////////////////
// NativeComboboxWrapper, public:

#if defined(USE_AURA)
// static
NativeComboboxWrapper* NativeComboboxWrapper::CreateWrapper(
    Combobox* combobox) {
  return new NativeComboboxViews(combobox);
}
#endif

}  // namespace views
<|endoftext|>


<commit_before><commit_msg>Update the window shape after changing themes.  We were losing the rounded corners on theme change in the options dialog.<commit_after><|endoftext|>


<commit_before><commit_msg>Only call gtk_window_present on an alert dialog if the activation state of the window is changing to active.  The signal gets sent even when windows are going inactive (to make the frame light blue), but we only want to do anything if the window is trying to become active.<commit_after><|endoftext|>


<commit_before><commit_msg>gtk: Notify the TabContents when the browser window has been moved or resized.  This is used to close the AutoFill popup in the renderer.<commit_after><|endoftext|>


<commit_before><commit_msg>Speculative fix for throbber showing on all tabs.<commit_after><|endoftext|>


<commit_before>/**
 * meshoptimizer - version 1.0
 *
 * Copyright (C) 2016-2017, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
 * Report bugs and download new versions at https://github.com/zeux/meshoptimizer
 *
 * This library is distributed under the MIT License. See notice at the end of this file.
 */
#pragma once

#include <cstddef>

// Version macro; major * 100 + minor * 10 + patch
#define MESHOPTIMIZER_VERSION 100

// If no API is defined, assume default
#ifndef MESHOPTIMIZER_API
#	define MESHOPTIMIZER_API
#endif

// Default vertex cache size
#define MESHOPTIMIZER_DEFAULT_VCACHE_SIZE 16

// Interface
namespace meshopt
{

// Generates a vertex remap table from the vertex buffer and an optional index buffer and returns number of unique vertices
//
// destination must contain enough space for the resulting remap table (vertex_count elements)
// indices can be NULL if the input is unindexed
MESHOPTIMIZER_API size_t generateVertexRemap(unsigned int* destination, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size);

// Generates vertex buffer from the source vertex buffer and remap table generated by generateVertexRemap
//
// destination must contain enough space for the resulting vertex buffer (unique_vertex_count elements, returned by generateVertexRemap)
MESHOPTIMIZER_API void remapVertexBuffer(void* destination, const void* vertices, size_t vertex_count, size_t vertex_size, const unsigned int* remap);

// Generate index buffer from the source index buffer and remap table generated by generateVertexRemap
//
// destination must contain enough space for the resulting index buffer (index_count elements)
// indices can be NULL if the input is unindexed
MESHOPTIMIZER_API void remapIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const unsigned int* remap);

// Vertex transform cache optimizer
// Reorders indices to reduce the number of GPU vertex shader invocations
//
// destination must contain enough space for the resulting index buffer (index_count elements)
// cache_size should be less than the actual GPU cache size to avoid cache thrashing
MESHOPTIMIZER_API void optimizeVertexCache(unsigned short* destination, const unsigned short* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE);
MESHOPTIMIZER_API void optimizeVertexCache(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE);

// Overdraw optimizer
// Reorders indices to reduce the number of GPU vertex shader invocations and the pixel overdraw
//
// destination must contain enough space for the resulting index buffer (index_count elements)
// indices must contain index data that is the result of optimizeVertexCache (*not* the original mesh indices!)
// vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
// cache_size should be less than the actual GPU cache size to avoid cache thrashing
// threshold indicates how much the overdraw optimizer can degrade vertex cache efficiency (1.05 = up to 5%) to reduce overdraw more efficiently
MESHOPTIMIZER_API void optimizeOverdraw(unsigned short* destination, const unsigned short* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE, float threshold = 1);
MESHOPTIMIZER_API void optimizeOverdraw(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE, float threshold = 1);

// Vertex fetch cache optimizer
// Reorders vertices and changes indices to reduce the amount of GPU memory fetches during vertex processing
//
// desination must contain enough space for the resulting vertex buffer (vertex_count elements)
// indices is used both as an input and as an output index buffer
MESHOPTIMIZER_API size_t optimizeVertexFetch(void* destination, const void* vertices, unsigned short* indices, size_t index_count, size_t vertex_count, size_t vertex_size);
MESHOPTIMIZER_API size_t optimizeVertexFetch(void* destination, const void* vertices, unsigned int* indices, size_t index_count, size_t vertex_count, size_t vertex_size);

struct VertexCacheStatistics
{
	unsigned int vertices_transformed;
	float acmr; // transformed vertices / triangle count; best case 0.5, worst case 3.0, optimum depends on topology
	float atvr; // transformed vertices / vertex count; best case 1.0, worse case 6.0, optimum is 1.0 (each vertex is transformed once)
};

// Vertex transform cache analyzer
// Returns cache hit statistics using a simplified FIFO model
// Results will not match actual GPU performance
MESHOPTIMIZER_API VertexCacheStatistics analyzeVertexCache(const unsigned short* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE);
MESHOPTIMIZER_API VertexCacheStatistics analyzeVertexCache(const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE);

struct OverdrawStatistics
{
	unsigned int pixels_covered;
	unsigned int pixels_shaded;
	float overdraw; // shaded pixels / covered pixels; best case 1.0
};

// Overdraw analyzer
// Returns overdraw statistics using a software rasterizer
// Results will not match actual GPU performance
//
// vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
MESHOPTIMIZER_API OverdrawStatistics analyzeOverdraw(const unsigned short* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count);
MESHOPTIMIZER_API OverdrawStatistics analyzeOverdraw(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count);

struct VertexFetchStatistics
{
	unsigned int bytes_fetched;
	float overfetch; // fetched bytes / vertex buffer size; best case 1.0 (each byte is fetched once)
};

// Vertex fetch cache analyzer
// Returns cache hit statistics using a simplified direct mapped model
// Results will not match actual GPU performance
MESHOPTIMIZER_API VertexFetchStatistics analyzeVertexFetch(const unsigned short* indices, size_t index_count, size_t vertex_count, size_t vertex_size);
MESHOPTIMIZER_API VertexFetchStatistics analyzeVertexFetch(const unsigned int* indices, size_t index_count, size_t vertex_count, size_t vertex_size);

// Quantization into commonly supported data formats

// Quantize a float in [0..1] range into an N-bit fixed point unorm value
// Assumes reconstruction function (q / (2^N-1)), which is the case for fixed-function normalized fixed point conversion
// Maximum reconstruction error: 1/2^(N+1)
template <int N> inline int quantizeUnorm(float v);

// Quantize a float in [-1..1] range into an N-bit fixed point snorm value
// Assumes reconstruction function (q / (2^(N-1)-1)), which is the case for fixed-function normalized fixed point conversion (except OpenGL)
// Maximum reconstruction error: 1/2^N
// Warning: OpenGL fixed function reconstruction function can't represent 0 exactly; when using OpenGL, use this function and have the shader reconstruct by dividing by 2^(N-1)-1.
template <int N> inline int quantizeSnorm(float v);

// Quantize a float into half-precision floating point value
// Generates +-inf for overflow, preserves NaN, flushes denormals to zero, rounds to nearest
// Representable magnitude range: [6e-5; 65504]
// Maximum relative reconstruction error: 5e-4
inline unsigned short quantizeHalf(float v);

} // namespace meshopt

// Inline implementation
namespace meshopt
{

template <int N> inline int quantizeUnorm(float v)
{
	const float scale = float((1 << N) - 1);

	v = (v >= 0) ? v : 0;
	v = (v <= 1) ? v : 1;

	return int(v * scale + 0.5f);
}

template <int N> inline int quantizeSnorm(float v)
{
	const float scale = float((1 << (N - 1)) - 1);

	float round = (v >= 0 ? 0.5f : -0.5f);

	v = (v >= -1) ? v : -1;
	v = (v <= +1) ? v : +1;

	return int(v * scale + round);
}

inline unsigned short quantizeHalf(float v)
{
	union { float f; unsigned int ui; } u = {v};
	unsigned int ui = u.ui;

	int s = (ui >> 16) & 0x8000;
	int em = ui & 0x7fffffff;

	// bias exponent and round to nearest; 112 is relative exponent bias (127-15)
	int h = (em - (112 << 23) + (1 << 12)) >> 13;

	// underflow: flush to zero; 113 encodes exponent -14
	h = (em < (113 << 23)) ? 0 : h;

	// overflow: infinity; 143 encodes exponent 16
	h = (em >= (143 << 23)) ? 0x7c00 : h;

	// NaN; note that we convert all types of NaN to qNaN
	h = (em > (255 << 23)) ? 0x7e00 : h;

	return (unsigned short)(s | h);
}

} // namespace meshopt

/**
 * Copyright (c) 2016-2017 Arseny Kapoulkine
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
<commit_msg>Add C++ template interface<commit_after>/**
 * meshoptimizer - version 1.0
 *
 * Copyright (C) 2016-2017, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
 * Report bugs and download new versions at https://github.com/zeux/meshoptimizer
 *
 * This library is distributed under the MIT License. See notice at the end of this file.
 */
#pragma once

#include <cstddef>

// Version macro; major * 100 + minor * 10 + patch
#define MESHOPTIMIZER_VERSION 100

// If no API is defined, assume default
#ifndef MESHOPTIMIZER_API
#	define MESHOPTIMIZER_API
#endif

// Default vertex cache size
#define MESHOPTIMIZER_DEFAULT_VCACHE_SIZE 16

// Interface
namespace meshopt
{

// Generates a vertex remap table from the vertex buffer and an optional index buffer and returns number of unique vertices
//
// destination must contain enough space for the resulting remap table (vertex_count elements)
// indices can be NULL if the input is unindexed
MESHOPTIMIZER_API size_t generateVertexRemap(unsigned int* destination, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size);

// Generates vertex buffer from the source vertex buffer and remap table generated by generateVertexRemap
//
// destination must contain enough space for the resulting vertex buffer (unique_vertex_count elements, returned by generateVertexRemap)
MESHOPTIMIZER_API void remapVertexBuffer(void* destination, const void* vertices, size_t vertex_count, size_t vertex_size, const unsigned int* remap);

// Generate index buffer from the source index buffer and remap table generated by generateVertexRemap
//
// destination must contain enough space for the resulting index buffer (index_count elements)
// indices can be NULL if the input is unindexed
MESHOPTIMIZER_API void remapIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const unsigned int* remap);

// Vertex transform cache optimizer
// Reorders indices to reduce the number of GPU vertex shader invocations
//
// destination must contain enough space for the resulting index buffer (index_count elements)
// cache_size should be less than the actual GPU cache size to avoid cache thrashing
MESHOPTIMIZER_API void optimizeVertexCache(unsigned short* destination, const unsigned short* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE);
MESHOPTIMIZER_API void optimizeVertexCache(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE);

// Overdraw optimizer
// Reorders indices to reduce the number of GPU vertex shader invocations and the pixel overdraw
//
// destination must contain enough space for the resulting index buffer (index_count elements)
// indices must contain index data that is the result of optimizeVertexCache (*not* the original mesh indices!)
// vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
// cache_size should be less than the actual GPU cache size to avoid cache thrashing
// threshold indicates how much the overdraw optimizer can degrade vertex cache efficiency (1.05 = up to 5%) to reduce overdraw more efficiently
MESHOPTIMIZER_API void optimizeOverdraw(unsigned short* destination, const unsigned short* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE, float threshold = 1);
MESHOPTIMIZER_API void optimizeOverdraw(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE, float threshold = 1);

// Vertex fetch cache optimizer
// Reorders vertices and changes indices to reduce the amount of GPU memory fetches during vertex processing
//
// desination must contain enough space for the resulting vertex buffer (vertex_count elements)
// indices is used both as an input and as an output index buffer
MESHOPTIMIZER_API size_t optimizeVertexFetch(void* destination, const void* vertices, unsigned short* indices, size_t index_count, size_t vertex_count, size_t vertex_size);
MESHOPTIMIZER_API size_t optimizeVertexFetch(void* destination, const void* vertices, unsigned int* indices, size_t index_count, size_t vertex_count, size_t vertex_size);

struct VertexCacheStatistics
{
	unsigned int vertices_transformed;
	float acmr; // transformed vertices / triangle count; best case 0.5, worst case 3.0, optimum depends on topology
	float atvr; // transformed vertices / vertex count; best case 1.0, worse case 6.0, optimum is 1.0 (each vertex is transformed once)
};

// Vertex transform cache analyzer
// Returns cache hit statistics using a simplified FIFO model
// Results will not match actual GPU performance
MESHOPTIMIZER_API VertexCacheStatistics analyzeVertexCache(const unsigned short* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE);
MESHOPTIMIZER_API VertexCacheStatistics analyzeVertexCache(const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE);

struct OverdrawStatistics
{
	unsigned int pixels_covered;
	unsigned int pixels_shaded;
	float overdraw; // shaded pixels / covered pixels; best case 1.0
};

// Overdraw analyzer
// Returns overdraw statistics using a software rasterizer
// Results will not match actual GPU performance
//
// vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
MESHOPTIMIZER_API OverdrawStatistics analyzeOverdraw(const unsigned short* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count);
MESHOPTIMIZER_API OverdrawStatistics analyzeOverdraw(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count);

struct VertexFetchStatistics
{
	unsigned int bytes_fetched;
	float overfetch; // fetched bytes / vertex buffer size; best case 1.0 (each byte is fetched once)
};

// Vertex fetch cache analyzer
// Returns cache hit statistics using a simplified direct mapped model
// Results will not match actual GPU performance
MESHOPTIMIZER_API VertexFetchStatistics analyzeVertexFetch(const unsigned short* indices, size_t index_count, size_t vertex_count, size_t vertex_size);
MESHOPTIMIZER_API VertexFetchStatistics analyzeVertexFetch(const unsigned int* indices, size_t index_count, size_t vertex_count, size_t vertex_size);

// Quantization into commonly supported data formats

// Quantize a float in [0..1] range into an N-bit fixed point unorm value
// Assumes reconstruction function (q / (2^N-1)), which is the case for fixed-function normalized fixed point conversion
// Maximum reconstruction error: 1/2^(N+1)
template <int N> inline int quantizeUnorm(float v);

// Quantize a float in [-1..1] range into an N-bit fixed point snorm value
// Assumes reconstruction function (q / (2^(N-1)-1)), which is the case for fixed-function normalized fixed point conversion (except OpenGL)
// Maximum reconstruction error: 1/2^N
// Warning: OpenGL fixed function reconstruction function can't represent 0 exactly; when using OpenGL, use this function and have the shader reconstruct by dividing by 2^(N-1)-1.
template <int N> inline int quantizeSnorm(float v);

// Quantize a float into half-precision floating point value
// Generates +-inf for overflow, preserves NaN, flushes denormals to zero, rounds to nearest
// Representable magnitude range: [6e-5; 65504]
// Maximum relative reconstruction error: 5e-4
inline unsigned short quantizeHalf(float v);

} // namespace meshopt

// Static assertion
#define MESHOPTIMIZER_STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }

// C++ template interface
namespace meshopt
{

template <typename T>
inline size_t generateVertexRemap(unsigned int* destination, const T* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size)
{
	MESHOPTIMIZER_STATIC_ASSERT(sizeof(T) == sizeof(unsigned int));

	return generateVertexRemap(destination, reinterpret_cast<const unsigned int*>(indices), index_count, vertices, vertex_count, vertex_size);
}

template <typename T>
inline void remapIndexBuffer(T* destination, const T* indices, size_t index_count, const unsigned int* remap)
{
	MESHOPTIMIZER_STATIC_ASSERT(sizeof(T) == sizeof(unsigned int));

	remapIndexBuffer(reinterpret_cast<unsigned int*>(destination), reinterpret_cast<const unsigned int*>(indices), index_count, remap);
}

template <typename T>
inline void optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count, unsigned int cache_size)
{
	MESHOPTIMIZER_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4);

	if (sizeof(T) == 2)
		optimizeVertexCache(reinterpret_cast<unsigned short*>(destination), reinterpret_cast<const unsigned short*>(indices), index_count, vertex_count, cache_size);
	else
		optimizeVertexCache(reinterpret_cast<unsigned int*>(destination), reinterpret_cast<const unsigned int*>(indices), index_count, vertex_count, cache_size);
}

template <typename T>
inline void optimizeOverdraw(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE, float threshold = 1)
{
	MESHOPTIMIZER_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4);

	if (sizeof(T) == 2)
		optimizeOverdraw(reinterpret_cast<unsigned short*>(destination), reinterpret_cast<const unsigned short*>(indices), index_count, vertex_positions, vertex_positions_stride, vertex_count, cache_size, threshold);
	else
		optimizeOverdraw(reinterpret_cast<unsigned int*>(destination), reinterpret_cast<const unsigned int*>(indices), index_count, vertex_positions, vertex_positions_stride, vertex_count, cache_size, threshold);
}

template <typename T>
inline size_t optimizeVertexFetch(void* destination, const void* vertices, T* indices, size_t index_count, size_t vertex_count, size_t vertex_size)
{
	MESHOPTIMIZER_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4);

	if (sizeof(T) == 2)
		return optimizeVertexFetch(destination, vertices, reinterpret_cast<unsigned short*>(indices), index_count, vertex_count, vertex_size);
	else
		return optimizeVertexFetch(destination, vertices, reinterpret_cast<unsigned int*>(indices), index_count, vertex_count, vertex_size);
}

template <typename T>
inline VertexCacheStatistics analyzeVertexCache(const T* indices, size_t index_count, size_t vertex_count, unsigned int cache_size = MESHOPTIMIZER_DEFAULT_VCACHE_SIZE)
{
	MESHOPTIMIZER_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4);

	if (sizeof(T) == 2)
		return analyzeVertexCache(reinterpret_cast<const unsigned short*>(indices), index_count, vertex_count, cache_size);
	else
		return analyzeVertexCache(reinterpret_cast<const unsigned int*>(indices), index_count, vertex_count, cache_size);
}

template <typename T>
inline OverdrawStatistics analyzeOverdraw(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_positions_stride, size_t vertex_count)
{
	MESHOPTIMIZER_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4);

	if (sizeof(T) == 2)
		return analyzeOverdraw(reinterpret_cast<const unsigned short*>(indices), index_count, vertex_positions, vertex_positions_stride, vertex_count);
	else
		return analyzeOverdraw(reinterpret_cast<const unsigned int*>(indices), index_count, vertex_positions, vertex_positions_stride, vertex_count);
}

template <typename T>
inline VertexFetchStatistics analyzeVertexFetch(const T* indices, size_t index_count, size_t vertex_count, size_t vertex_size)
{
	MESHOPTIMIZER_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4);

	if (sizeof(T) == 2)
		return analyzeVertexFetch(reinterpret_cast<const unsigned short*>(indices), index_count, vertex_count, vertex_size);
	else
		return analyzeVertexFetch(reinterpret_cast<const unsigned int*>(indices), index_count, vertex_count, vertex_size);
}

} // namespace meshopt

// Inline implementation
namespace meshopt
{

template <int N> inline int quantizeUnorm(float v)
{
	const float scale = float((1 << N) - 1);

	v = (v >= 0) ? v : 0;
	v = (v <= 1) ? v : 1;

	return int(v * scale + 0.5f);
}

template <int N> inline int quantizeSnorm(float v)
{
	const float scale = float((1 << (N - 1)) - 1);

	float round = (v >= 0 ? 0.5f : -0.5f);

	v = (v >= -1) ? v : -1;
	v = (v <= +1) ? v : +1;

	return int(v * scale + round);
}

inline unsigned short quantizeHalf(float v)
{
	union { float f; unsigned int ui; } u = {v};
	unsigned int ui = u.ui;

	int s = (ui >> 16) & 0x8000;
	int em = ui & 0x7fffffff;

	// bias exponent and round to nearest; 112 is relative exponent bias (127-15)
	int h = (em - (112 << 23) + (1 << 12)) >> 13;

	// underflow: flush to zero; 113 encodes exponent -14
	h = (em < (113 << 23)) ? 0 : h;

	// overflow: infinity; 143 encodes exponent 16
	h = (em >= (143 << 23)) ? 0x7c00 : h;

	// NaN; note that we convert all types of NaN to qNaN
	h = (em > (255 << 23)) ? 0x7e00 : h;

	return (unsigned short)(s | h);
}

} // namespace meshopt

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


<commit_before>#include <map>
#include <iostream>
#include <stdexcept>
#include <sstream>
#include "config/VRDataIndex.h"
#include "config/VRDataQueue.h"

#define HELPMESSAGE  std::cout << "ls - get the list of data names" << std::endl; \
      std::cout << "px - show namespaces / containers " << std::endl; \
      std::cout << "p <name> - print a value from the list" << std::endl; \
      std::cout << "file <filename> - read an XML file" << std::endl; \
      std::cout << "add <newname> <type> <value> - add a primitive value" << std::endl; \
      std::cout << "add <name> container  - create a container" << std::endl; \
      std::cout << "cd [<namespace>] - set or show the default namespace" << std::endl; \
      std::cout << "q quit" << std::endl; \
      std::cout << "? print this message." << std::endl;

// This is just a test program meant to be a vague illustration of the
// use of the VRDataIndex type system.  The program is essentially
// a really dumb interpreter with a few commands implemented, such as
// look at a program, read a data file, add a data value and change
// the namespace.  Use the operation of the program to learn about
// namespaces and use the code below as a way to learn how to use the
// data index. It's just hacked together, so don't read too much into
// it.
int main() {
  VRDataIndex *index = new VRDataIndex;

  // Set up some sample data names and values.
  int a = 4;
  int b = 6;
  double f = 3.1415926;
  double g = 2.71828;
  std::string s1 = "wowie!";
  std::string s2 = "shazam!";

  index->addData("/henry", a);
  index->addData("/ralph", b);

  index->addData("/george", f);
  index->addData("/mary", g);

  index->addData("/billy", s1);
  index->addData("/johnny", s2);

  index->addSerializedValue("<bob type=\"container\"><flora type=\"int\">3274</flora><morton type=\"double\">34.5</morton><cora type=\"container\"><flora type=\"int\">1234</flora><nora type=\"double\">23.45</nora></cora></bob>");

  index->addSerializedValue("<chester type=\"doublearray\">32.7@44.56@22.3@78.2@99.134@</chester>");

  std::vector<std::string> elems;
  std::string elem;
  std::string line;
  std::string nameSpace("/");

  HELPMESSAGE

  while ((std::cout << "> ") && std::getline(std::cin, line)) {

    elems.clear();
    std::stringstream ss(line);
    while (std::getline(ss, elem, ' ')) {
      elems.push_back(elem);
    }

    ////// command: ? (help)
    if (elems[0].compare("?") == 0) {
      HELPMESSAGE

    ////// command: file (open file)
    } else if (elems[0].compare("file") == 0) {

      index->processXMLFile(elems[1], nameSpace);

    ////// command: cd (set namespace)
    } else if (elems[0].compare("cd") == 0) {

      if (elems.size() > 1) {

        try {

          if (elems[1][0] != '/') {

            nameSpace = index->validateNameSpace(nameSpace + elems[1]);

          } else {

            nameSpace = index->validateNameSpace(elems[1]);
          }
        } catch (const std::exception& e) {

          std::cout << "oops: " << e.what() << std::endl;
        }

      } else {
        std::cout << "using namespace: " << nameSpace << std::endl;
      }

    ////// command: px
    } else if (elems[0].compare("px") == 0) {

      index->printStructure();

    ////// command: z (undocumented in help; use for testing)
    } else if (elems[0].compare("z") == 0) {

      std::cout << elems.size() << std::endl;

      int N = elems.size();

      while (N >= 1)
        {

          std::vector<std::string> nms(&elems[1], &elems[N]);
          std::string out;
          for (std::vector<std::string>::iterator it = nms.begin();
               it != nms.end(); ++it)
            {
              out += "/" + *it;
            }
          std::cout << ">" << out << "<" << std::endl;
          N -= 1;
        }

    ////// command: p (print value)
    } else if (elems[0].compare("p") == 0) {

      try {

        // This illustrates one way to get the data out of the index.
        // You can also do something like this:
        //
        //  int ip = index->getValue("henry", "/")
        VRDatumPtr p = index->getDatum(elems[1], nameSpace);

        switch (p->getType()) {
        case VRCORETYPE_INT:
          std::cout << "an integer containing: " << ((int)p->getValue()) << std::endl;

          std::cout << "same as: " << (int)index->getValue(elems[1], nameSpace) << std::endl;
          break;

        case VRCORETYPE_DOUBLE:
          std::cout << "a double containing: " << ((double)p->getValue()) << std::endl;
          break;

        case VRCORETYPE_STRING:
          std::cout << "a string containing: " << ((std::string)p->getValue()) << std::endl;
          break;

        case VRCORETYPE_INTARRAY:
          {
            VRIntArray pdata = p->getValue();
            for (VRIntArray::iterator it = pdata.begin(); it != pdata.end(); ++it) {
              std::cout << "element: " << *it << std::endl;
            }
            break;
          }

        case VRCORETYPE_DOUBLEARRAY:
          {
            VRDoubleArray pdata = p->getValue();
            for (VRDoubleArray::iterator it = pdata.begin(); it != pdata.end(); ++it) {
              std::cout << "element: " << *it << std::endl;
            }
            break;
          }

        case VRCORETYPE_STRINGARRAY:
          {
            VRStringArray pdata = p->getValue();
            for (VRStringArray::iterator it = pdata.begin(); it != pdata.end(); ++it) {
              std::cout << "element: " << *it << std::endl;
            }
            break;
          }

        case VRCORETYPE_CONTAINER:

          {
            std::cout << "a container containing: " << std::endl;

            VRContainer nameList = p->getValue();
            for (VRContainer::iterator nl = nameList.begin();
                 nl != nameList.end(); nl++) {
              std::cout << "                        " << *nl << std::endl;
            }
            break;
          }

        case VRCORETYPE_NONE:
          {
            break;
          }
        }

        std::cout << "serialization: " << index->serialize(elems[1], nameSpace) << std::endl;
      } catch (const std::exception& e) {

        std::cout << "oops: " << e.what() << std::endl;

      }

    ////// command: l (list all values)
    } else if (elems[0].compare("ls") == 0) {

      VRContainer nameList;
      if (elems.size() > 1) {
        nameList = index->getValue(elems[1]);
      } else {
        if (nameSpace.compare("/") == 0) {
          nameList = index->getNames();
        } else {
          nameList = index->getValue(nameSpace.substr(0,nameSpace.size() - 1));
        }
      }
      for (VRContainer::iterator it = nameList.begin();
           it != nameList.end(); it++) {
        std::cout << *it << std::endl;
      }
    ////// command: add (add something)
    } else if (elems[0].compare("add") == 0) {

      if ((elems.size() == 4) || (elems.size() == 3)) {

        // Make a list of data values to add to the parent container.
        // In this case, the list will have only one value, but it
        // could have more.
        VRContainer c;
        c.push_back(nameSpace + elems[1]);

        std::cout << "nameSpace: " << nameSpace << " c: " << c.front() << std::endl;

        if (elems[2].compare("container") == 0) {
          index->addNameSpace(nameSpace + elems[1]);
        } else {

          if (elems[2].compare("int") == 0) {

            int iVal;
            sscanf(elems[3].c_str(), "%d", &iVal);
            // Add the value to the index.
            index->addData(nameSpace + elems[1], iVal);
            // Add it to the parent container.
            if (nameSpace.size() > 1) index->addData(nameSpace.substr(0,nameSpace.size() - 1), c);

          } else if (elems[2].compare("double") == 0) {

            double dVal;
            sscanf(elems[3].c_str(), "%lf", &dVal);
            index->addData(nameSpace + elems[1], dVal);
            if (nameSpace.size() > 1) index->addData(nameSpace.substr(0,nameSpace.size() - 1), c);

          } else if (elems[2].compare("string") == 0) {

            index->addData(nameSpace + elems[1], elems[3]);
            if (nameSpace.size() > 1) index->addData(nameSpace.substr(0,nameSpace.size() - 1), c);

          } else {

            std::cout << "can't handle that data type." << std::endl;

          }
        }
      } else {
        std::cout << "try 'add harry int 27' (that is, do 'add <name> <type> <value>' \n or 'add <name> container')" << std::endl;
      }

    ////// command: q (exit)
    } else if (elems[0].compare("q") == 0) {
      return EXIT_SUCCESS;

    } else {

      std::cout << "Was '" << elems[0] << "' meant to be a command? Try '?'." << std::endl;
    }
  }
}

<commit_msg>added arg to printStructure<commit_after>#include <map>
#include <iostream>
#include <stdexcept>
#include <sstream>
#include "config/VRDataIndex.h"
#include "config/VRDataQueue.h"

#define HELPMESSAGE  std::cout << "ls - get the list of data names" << std::endl; \
      std::cout << "px - show namespaces / containers " << std::endl; \
      std::cout << "p <name> - print a value from the list" << std::endl; \
      std::cout << "file <filename> - read an XML file" << std::endl; \
      std::cout << "add <newname> <type> <value> - add a primitive value" << std::endl; \
      std::cout << "add <name> container  - create a container" << std::endl; \
      std::cout << "cd [<namespace>] - set or show the default namespace" << std::endl; \
      std::cout << "q quit" << std::endl; \
      std::cout << "? print this message." << std::endl;

// This is just a test program meant to be a vague illustration of the
// use of the VRDataIndex type system.  The program is essentially
// a really dumb interpreter with a few commands implemented, such as
// look at a program, read a data file, add a data value and change
// the namespace.  Use the operation of the program to learn about
// namespaces and use the code below as a way to learn how to use the
// data index. It's just hacked together, so don't read too much into
// it.
int main() {
  VRDataIndex *index = new VRDataIndex;

  // Set up some sample data names and values.
  int a = 4;
  int b = 6;
  double f = 3.1415926;
  double g = 2.71828;
  std::string s1 = "wowie!";
  std::string s2 = "shazam!";

  index->addData("/henry", a);
  index->addData("/ralph", b);

  index->addData("/george", f);
  index->addData("/mary", g);

  index->addData("/billy", s1);
  index->addData("/johnny", s2);

  index->addSerializedValue("<bob type=\"container\"><flora type=\"int\">3274</flora><morton type=\"double\">34.5</morton><cora type=\"container\"><flora type=\"int\">1234</flora><nora type=\"double\">23.45</nora></cora></bob>");

  index->addSerializedValue("<chester type=\"doublearray\">32.7@44.56@22.3@78.2@99.134@</chester>");

  std::vector<std::string> elems;
  std::string elem;
  std::string line;
  std::string nameSpace("/");

  HELPMESSAGE

  while ((std::cout << "> ") && std::getline(std::cin, line)) {

    elems.clear();
    std::stringstream ss(line);
    while (std::getline(ss, elem, ' ')) {
      elems.push_back(elem);
    }

    ////// command: ? (help)
    if (elems[0].compare("?") == 0) {
      HELPMESSAGE

    ////// command: file (open file)
    } else if (elems[0].compare("file") == 0) {

      index->processXMLFile(elems[1], nameSpace);

    ////// command: cd (set namespace)
    } else if (elems[0].compare("cd") == 0) {

      if (elems.size() > 1) {

        try {

          if (elems[1][0] != '/') {

            nameSpace = index->validateNameSpace(nameSpace + elems[1]);

          } else {

            nameSpace = index->validateNameSpace(elems[1]);
          }
        } catch (const std::exception& e) {

          std::cout << "oops: " << e.what() << std::endl;
        }

      } else {
        std::cout << "using namespace: " << nameSpace << std::endl;
      }

    ////// command: px
    } else if (elems[0].compare("px") == 0) {

      if (elems.size() > 1) {
        index->printStructure(elems[1]);
      } else {
        index->printStructure();
      }

    ////// command: z (undocumented in help; use for testing)
    } else if (elems[0].compare("z") == 0) {

      std::cout << elems.size() << std::endl;

      int N = elems.size();

      while (N >= 1)
        {

          std::vector<std::string> nms(&elems[1], &elems[N]);
          std::string out;
          for (std::vector<std::string>::iterator it = nms.begin();
               it != nms.end(); ++it)
            {
              out += "/" + *it;
            }
          std::cout << ">" << out << "<" << std::endl;
          N -= 1;
        }

    ////// command: p (print value)
    } else if (elems[0].compare("p") == 0) {

      try {

        // This illustrates one way to get the data out of the index.
        // You can also do something like this:
        //
        //  int ip = index->getValue("henry", "/")
        VRDatumPtr p = index->getDatum(elems[1], nameSpace);

        switch (p->getType()) {
        case VRCORETYPE_INT:
          std::cout << "an integer containing: " << ((int)p->getValue()) << std::endl;

          std::cout << "same as: " << (int)index->getValue(elems[1], nameSpace) << std::endl;
          break;

        case VRCORETYPE_DOUBLE:
          std::cout << "a double containing: " << ((double)p->getValue()) << std::endl;
          break;

        case VRCORETYPE_STRING:
          std::cout << "a string containing: " << ((std::string)p->getValue()) << std::endl;
          break;

        case VRCORETYPE_INTARRAY:
          {
            VRIntArray pdata = p->getValue();
            for (VRIntArray::iterator it = pdata.begin(); it != pdata.end(); ++it) {
              std::cout << "element: " << *it << std::endl;
            }
            break;
          }

        case VRCORETYPE_DOUBLEARRAY:
          {
            VRDoubleArray pdata = p->getValue();
            for (VRDoubleArray::iterator it = pdata.begin(); it != pdata.end(); ++it) {
              std::cout << "element: " << *it << std::endl;
            }
            break;
          }

        case VRCORETYPE_STRINGARRAY:
          {
            VRStringArray pdata = p->getValue();
            for (VRStringArray::iterator it = pdata.begin(); it != pdata.end(); ++it) {
              std::cout << "element: " << *it << std::endl;
            }
            break;
          }

        case VRCORETYPE_CONTAINER:

          {
            std::cout << "a container containing: " << std::endl;

            VRContainer nameList = p->getValue();
            for (VRContainer::iterator nl = nameList.begin();
                 nl != nameList.end(); nl++) {
              std::cout << "                        " << *nl << std::endl;
            }
            break;
          }

        case VRCORETYPE_NONE:
          {
            break;
          }
        }

        std::cout << "serialization: " << index->serialize(elems[1], nameSpace) << std::endl;
      } catch (const std::exception& e) {

        std::cout << "oops: " << e.what() << std::endl;

      }

    ////// command: l (list all values)
    } else if (elems[0].compare("ls") == 0) {

      VRContainer nameList;
      if (elems.size() > 1) {
        nameList = index->getValue(elems[1]);
      } else {
        if (nameSpace.compare("/") == 0) {
          nameList = index->getNames();
        } else {
          nameList = index->getValue(nameSpace.substr(0,nameSpace.size() - 1));
        }
      }
      for (VRContainer::iterator it = nameList.begin();
           it != nameList.end(); it++) {
        std::cout << *it << std::endl;
      }
    ////// command: add (add something)
    } else if (elems[0].compare("add") == 0) {

      if ((elems.size() == 4) || (elems.size() == 3)) {

        // Make a list of data values to add to the parent container.
        // In this case, the list will have only one value, but it
        // could have more.
        VRContainer c;
        c.push_back(nameSpace + elems[1]);

        std::cout << "nameSpace: " << nameSpace << " c: " << c.front() << std::endl;

        if (elems[2].compare("container") == 0) {
          index->addNameSpace(nameSpace + elems[1]);
        } else {

          if (elems[2].compare("int") == 0) {

            int iVal;
            sscanf(elems[3].c_str(), "%d", &iVal);
            // Add the value to the index.
            index->addData(nameSpace + elems[1], iVal);
            // Add it to the parent container.
            if (nameSpace.size() > 1) index->addData(nameSpace.substr(0,nameSpace.size() - 1), c);

          } else if (elems[2].compare("double") == 0) {

            double dVal;
            sscanf(elems[3].c_str(), "%lf", &dVal);
            index->addData(nameSpace + elems[1], dVal);
            if (nameSpace.size() > 1) index->addData(nameSpace.substr(0,nameSpace.size() - 1), c);

          } else if (elems[2].compare("string") == 0) {

            index->addData(nameSpace + elems[1], elems[3]);
            if (nameSpace.size() > 1) index->addData(nameSpace.substr(0,nameSpace.size() - 1), c);

          } else {

            std::cout << "can't handle that data type." << std::endl;

          }
        }
      } else {
        std::cout << "try 'add harry int 27' (that is, do 'add <name> <type> <value>' \n or 'add <name> container')" << std::endl;
      }

    ////// command: q (exit)
    } else if (elems[0].compare("q") == 0) {
      return EXIT_SUCCESS;

    } else {

      std::cout << "Was '" << elems[0] << "' meant to be a command? Try '?'." << std::endl;
    }
  }
}

<|endoftext|>


<commit_before>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010  Belledonne Communications SARL.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "agent.hh"
#include "etchosts.hh"

static char const *compute_branch(nta_agent_t *sa,msg_t *msg,sip_t const *sip,char const * host,char const * port);

class ForwardModule : public Module, ModuleToolbox {
	public:
		ForwardModule(Agent *ag);
		virtual void onDeclare(ConfigStruct * module_config);
		virtual void onLoad(Agent *agent, const ConfigStruct *root);
		virtual void onRequest(SipEvent *ev);
		virtual void onResponse(SipEvent *ev);
		~ForwardModule();
	private:
		url_t* overrideDest(SipEvent *ev, url_t* dest);
		void checkRecordRoutes(SipEvent *ev, url_t *dest);
		bool isLooping(SipEvent *ev, const char * branch);
		unsigned int countVia(SipEvent *ev);
		su_home_t mHome;
		sip_route_t *mOutRoute;
		bool mRewriteReqUri;
		static ModuleInfo<ForwardModule> sInfo;
};

ModuleInfo<ForwardModule> ForwardModule::sInfo("Forward",
   "This module executes the basic routing task of SIP requests and pass them to the transport layer. "
	"It must always be enabled.");

ForwardModule::ForwardModule(Agent *ag) : Module(ag){
	su_home_init(&mHome);
	mOutRoute=NULL;
}

ForwardModule::~ForwardModule(){
	su_home_deinit(&mHome);
}

void ForwardModule::onDeclare(ConfigStruct * module_config){
	ConfigItemDescriptor items[]={
			{	String	,	"route"	, 	"A sip uri where to send all requests",	""	},
			{	Boolean	,	"rewrite-req-uri"	,	"Rewrite request-uri's host and port according to above route", "false"	},
			config_item_end
	};
	module_config->addChildrenValues(items);
}

void ForwardModule::onLoad(Agent *agent, const ConfigStruct *module_config){
	std::string route=module_config->get<ConfigString>("route")->read();
	mRewriteReqUri=module_config->get<ConfigBoolean>("rewrite-req-uri")->read();
	if (route.size()>0){
		mOutRoute=sip_route_make(&mHome,route.c_str());
		if (mOutRoute==NULL || mOutRoute->r_url->url_host==NULL){
			LOGF("Bad route parameter '%s' in configuration of Forward module",route.c_str());
		}
	}
}

url_t* ForwardModule::overrideDest(SipEvent *ev, url_t *dest){
	if (mOutRoute){
		dest=mOutRoute->r_url;
		if (mRewriteReqUri){
			ev->mSip->sip_request->rq_url->url_host=mOutRoute->r_url->url_host;
			ev->mSip->sip_request->rq_url->url_port=mOutRoute->r_url->url_port;
		}
	}
	return dest;
}

/* the goal of this method is to check whether we added ourself to the record route, and handle a possible
 transport change by adding a new record-route with transport updated.
 Typically, if we transfer an INVITE from TCP to UDP, we should find two consecutive record-route, first one with UDP, and second one with TCP
 so that further request from both sides are sent to the appropriate transport of flexisip, and also we don't ask to a UDP only equipment to route to TCP.
*/
void ForwardModule::checkRecordRoutes(SipEvent *ev, url_t *dest){
	sip_record_route_t *rr=ev->mSip->sip_record_route;
	char last_transport[16]={0};
	char next_transport[16]={0};

	if (rr){
		if (getAgent()->isUs(rr->r_url,false)){
			if (!url_param(rr->r_url->url_params,"transport",last_transport,sizeof(last_transport))){
				strncpy(last_transport,"UDP",sizeof(last_transport));
			}
			if (!url_param(dest->url_params,"transport",next_transport,sizeof(next_transport))){
				strncpy(next_transport,"UDP",sizeof(next_transport));
			}
			if (strcasecmp(next_transport,last_transport)!=0){
				addRecordRoute(ev->getHome(),getAgent(),ev->mMsg,ev->mSip,next_transport);
			}
		}
	}
}

void ForwardModule::onRequest(SipEvent *ev){
	size_t msg_size;
	char *buf;
	url_t* dest=NULL;
	sip_t *sip=ev->mSip;
	msg_t *msg=ev->mMsg;

	// Check max forwards
	if(sip->sip_max_forwards != NULL && sip->sip_max_forwards->mf_count <= countVia(ev))
	{
		nta_msg_treply(getSofiaAgent(), msg, 483, "Too Many Hops", SIPTAG_SERVER_STR(getAgent()->getServerString()), TAG_END());
		return;
	}

	switch(sip->sip_request->rq_method){
		case sip_method_invite:
			LOGD("This is an invite");
			break;
		case sip_method_register:
			LOGD("This is a register");

		case sip_method_ack:
		default:
			break;
	}
	dest=sip->sip_request->rq_url;
	// removes top route headers if they maches us
	while (sip->sip_route!=NULL && getAgent()->isUs(sip->sip_route->r_url) ){
		sip_route_remove(msg,sip);
	}
	if (sip->sip_route!=NULL){
		/*forward to this route*/
		dest=sip->sip_route->r_url;
	}

	/* workaround bad sip uris with two @ that results in host part being "something@somewhere" */
	if (strchr(dest->url_host,'@')!=0){
		nta_msg_treply (getSofiaAgent(),msg,400,"Bad request",SIPTAG_SERVER_STR(getAgent()->getServerString()),TAG_END());
		return;
	}

	dest=overrideDest(ev,dest);

	std::string ip;
	if (EtcHostsResolver::get()->resolve(dest->url_host,&ip)){
		LOGD("Found %s in /etc/hosts",dest->url_host);
		/* duplication dest because we don't want to modify the message with our name resolution result*/
		dest=url_hdup(ev->getHome(),dest);
		dest->url_host=ip.c_str();
	}

	// Compute branch
	char const * branch = compute_branch(getSofiaAgent(), msg, sip, dest->url_host, dest->url_port);

	// Check looping
	if (!isLooping(ev, branch)) {
		checkRecordRoutes(ev, dest);
		buf = msg_as_string(ev->getHome(), msg, NULL, 0, &msg_size);
		LOGD("About to forward request to %s:\n%s", url_as_string(ev->getHome(), dest), buf);
		nta_msg_tsend(getSofiaAgent(), msg, (url_string_t*) dest, NTATAG_BRANCH_KEY(branch), TAG_END());
	} else {
		nta_msg_treply(getSofiaAgent(), msg, 482, "Loop Detected", SIPTAG_SERVER_STR(getAgent()->getServerString()), TAG_END());
	}
}

unsigned int ForwardModule::countVia(SipEvent *ev) {
	uint32_t via_count = 0;
	for (sip_via_t *via = ev->mSip->sip_via; via != NULL; via = via->v_next)
		++via_count;
	return via_count;
}

bool ForwardModule::isLooping(SipEvent *ev, const char * branch){
	for (sip_via_t *via = ev->mSip->sip_via; via != NULL; via = via->v_next)
	{
		if(strcmp(via->v_branch, branch + 7) == 0)
		{
			return true;
		}
	}
	return false;
}

void ForwardModule::onResponse(SipEvent *ev) {
	char *buf;
	size_t msg_size;

	buf = msg_as_string(ev->getHome(), ev->mMsg, NULL, 0, &msg_size);
	LOGD("About to forward response:\n%s", buf);

	nta_msg_tsend(getSofiaAgent(), ev->mMsg, (url_string_t*) NULL, TAG_END());
}

#include <sofia-sip/su_md5.h>
#include <sofia-sip/tport.h>
static char const *compute_branch(nta_agent_t *sa,
	msg_t *msg,
	sip_t const *sip,
	char const * host,
	char const * port) {
	su_md5_t md5[1];
	uint8_t digest[SU_MD5_DIGEST_SIZE];
	char branch[(SU_MD5_DIGEST_SIZE * 8 + 4) / 5 + 1];
	sip_route_t const *r;

	su_md5_init(md5);

	su_md5_str0update(md5, host);
	su_md5_str0update(md5, port);

	url_update(md5, sip->sip_request->rq_url);
	if (sip->sip_call_id) {
		su_md5_str0update(md5, sip->sip_call_id->i_id);
	}
	if (sip->sip_from) {
		url_update(md5, sip->sip_from->a_url);
		su_md5_stri0update(md5, sip->sip_from->a_tag);
	}
	if (sip->sip_to) {
		url_update(md5, sip->sip_to->a_url);
		/* XXX - some broken implementations include To tag in CANCEL */
		/* su_md5_str0update(md5, sip->sip_to->a_tag); */
	}
	if (sip->sip_cseq) {
		uint32_t cseq = htonl(sip->sip_cseq->cs_seq);
		su_md5_update(md5, &cseq, sizeof (cseq));
	}

	for (r = sip->sip_route; r; r = r->r_next)
		url_update(md5, r->r_url);

	su_md5_digest(md5, digest);

	msg_random_token(branch, sizeof (branch) - 1, digest, sizeof (digest));

	return su_sprintf(msg_home(msg), "branch=z9hG4bK.%s", branch);
}

<commit_msg>isLooping: check v_branch presence before compare it<commit_after>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010  Belledonne Communications SARL.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "agent.hh"
#include "etchosts.hh"

static char const *compute_branch(nta_agent_t *sa,msg_t *msg,sip_t const *sip,char const * host,char const * port);

class ForwardModule : public Module, ModuleToolbox {
	public:
		ForwardModule(Agent *ag);
		virtual void onDeclare(ConfigStruct * module_config);
		virtual void onLoad(Agent *agent, const ConfigStruct *root);
		virtual void onRequest(std::shared_ptr<SipEvent> &ev);
		virtual void onResponse(std::shared_ptr<SipEvent> &ev);
		~ForwardModule();
	private:
		url_t* overrideDest(std::shared_ptr<SipEvent> &ev, url_t* dest);
		void checkRecordRoutes(std::shared_ptr<SipEvent> &ev, url_t *dest);
                bool isLooping(std::shared_ptr<SipEvent> &ev, const char * branch);
                unsigned int countVia(std::shared_ptr<SipEvent> &ev);

		su_home_t mHome;
		sip_route_t *mOutRoute;
		bool mRewriteReqUri;
		static ModuleInfo<ForwardModule> sInfo;
};

ModuleInfo<ForwardModule> ForwardModule::sInfo("Forward",
   "This module executes the basic routing task of SIP requests and pass them to the transport layer. "
	"It must always be enabled.");

ForwardModule::ForwardModule(Agent *ag) : Module(ag){
	su_home_init(&mHome);
	mOutRoute=NULL;
}

ForwardModule::~ForwardModule(){
	su_home_deinit(&mHome);
}

void ForwardModule::onDeclare(ConfigStruct * module_config){
	ConfigItemDescriptor items[]={
			{	String	,	"route"	, 	"A sip uri where to send all requests",	""	},
			{	Boolean	,	"rewrite-req-uri"	,	"Rewrite request-uri's host and port according to above route", "false"	},
			config_item_end
	};
	module_config->addChildrenValues(items);
}

void ForwardModule::onLoad(Agent *agent, const ConfigStruct *module_config){
	std::string route=module_config->get<ConfigString>("route")->read();
	mRewriteReqUri=module_config->get<ConfigBoolean>("rewrite-req-uri")->read();
	if (route.size()>0){
		mOutRoute=sip_route_make(&mHome,route.c_str());
		if (mOutRoute==NULL || mOutRoute->r_url->url_host==NULL){
			LOGF("Bad route parameter '%s' in configuration of Forward module",route.c_str());
		}
	}
}

url_t* ForwardModule::overrideDest(std::shared_ptr<SipEvent> &ev, url_t *dest){
	if (mOutRoute){
		dest=mOutRoute->r_url;
		if (mRewriteReqUri){
			ev->mSip->sip_request->rq_url->url_host=mOutRoute->r_url->url_host;
			ev->mSip->sip_request->rq_url->url_port=mOutRoute->r_url->url_port;
		}
	}
	return dest;
}

/* the goal of this method is to check whether we added ourself to the record route, and handle a possible
 transport change by adding a new record-route with transport updated.
 Typically, if we transfer an INVITE from TCP to UDP, we should find two consecutive record-route, first one with UDP, and second one with TCP
 so that further request from both sides are sent to the appropriate transport of flexisip, and also we don't ask to a UDP only equipment to route to TCP.
*/
void ForwardModule::checkRecordRoutes(std::shared_ptr<SipEvent> &ev, url_t *dest){
	sip_record_route_t *rr=ev->mSip->sip_record_route;
	char last_transport[16]={0};
	char next_transport[16]={0};

	if (rr){
		if (getAgent()->isUs(rr->r_url,false)){
			if (!url_param(rr->r_url->url_params,"transport",last_transport,sizeof(last_transport))){
				strncpy(last_transport,"UDP",sizeof(last_transport));
			}
			if (!url_param(dest->url_params,"transport",next_transport,sizeof(next_transport))){
				strncpy(next_transport,"UDP",sizeof(next_transport));
			}
			if (strcasecmp(next_transport,last_transport)!=0){
				addRecordRoute(ev->getHome(),getAgent(),ev->mMsg,ev->mSip,next_transport);
			}
		}
	}
}

void ForwardModule::onRequest(std::shared_ptr<SipEvent> &ev){
	size_t msg_size;
	char *buf;
	url_t* dest=NULL;
	sip_t *sip=ev->mSip;
	msg_t *msg=ev->mMsg;

	// Check max forwards
	if(sip->sip_max_forwards != NULL && sip->sip_max_forwards->mf_count <= countVia(ev))
	{
		nta_msg_treply(getSofiaAgent(), msg, 483, "Too Many Hops", SIPTAG_SERVER_STR(getAgent()->getServerString()), TAG_END());
		return;
	}

	switch(sip->sip_request->rq_method){
		case sip_method_invite:
			LOGD("This is an invite");
			break;
		case sip_method_register:
			LOGD("This is a register");

		case sip_method_ack:
		default:
			break;
	}
	dest=sip->sip_request->rq_url;
	// removes top route headers if they maches us
	while (sip->sip_route!=NULL && getAgent()->isUs(sip->sip_route->r_url) ){
		sip_route_remove(msg,sip);
	}
	if (sip->sip_route!=NULL){
		/*forward to this route*/
		dest=sip->sip_route->r_url;
	}

	/* workaround bad sip uris with two @ that results in host part being "something@somewhere" */
	if (strchr(dest->url_host,'@')!=0){
		nta_msg_treply (getSofiaAgent(),msg,400,"Bad request",SIPTAG_SERVER_STR(getAgent()->getServerString()),TAG_END());
		return;
	}

	dest=overrideDest(ev,dest);

	std::string ip;
	if (EtcHostsResolver::get()->resolve(dest->url_host,&ip)){
		LOGD("Found %s in /etc/hosts",dest->url_host);
		/* duplication dest because we don't want to modify the message with our name resolution result*/
		dest=url_hdup(ev->getHome(),dest);
		dest->url_host=ip.c_str();
	}

	// Compute branch
	char const * branch = compute_branch(getSofiaAgent(), msg, sip, dest->url_host, dest->url_port);

	// Check looping
	if (!isLooping(ev, branch)) {
		checkRecordRoutes(ev, dest);
		buf = msg_as_string(ev->getHome(), msg, NULL, 0, &msg_size);
		LOGD("About to forward request to %s:\n%s", url_as_string(ev->getHome(), dest), buf);
		nta_msg_tsend(getSofiaAgent(), msg, (url_string_t*) dest, NTATAG_BRANCH_KEY(branch), TAG_END());
	} else {
		nta_msg_treply(getSofiaAgent(), msg, 482, "Loop Detected", SIPTAG_SERVER_STR(getAgent()->getServerString()), TAG_END());
	}
}

unsigned int ForwardModule::countVia(std::shared_ptr<SipEvent> &ev) {
        uint32_t via_count = 0;
        for (sip_via_t *via = ev->mSip->sip_via; via != NULL; via = via->v_next)
                ++via_count;
        return via_count;
}

bool ForwardModule::isLooping(std::shared_ptr<SipEvent> &ev, const char * branch){
        for (sip_via_t *via = ev->mSip->sip_via; via != NULL; via = via->v_next)
        {
                if(via->v_branch != NULL && strcmp(via->v_branch, branch + 7) == 0)
                {
                    return true;
                }
        }

        return false;
}

void ForwardModule::onResponse(std::shared_ptr<SipEvent> &ev){
	char *buf;
	size_t msg_size;

	buf = msg_as_string(ev->getHome(), ev->mMsg, NULL, 0, &msg_size);
	LOGD("About to forward response:\n%s", buf);

	nta_msg_tsend(getSofiaAgent(), ev->mMsg, (url_string_t*) NULL, TAG_END());
}

#include <sofia-sip/su_md5.h>
static char const *compute_branch(nta_agent_t *sa,
	msg_t *msg,
	sip_t const *sip,
	char const * host,
	char const * port) {
	su_md5_t md5[1];
	uint8_t digest[SU_MD5_DIGEST_SIZE];
	char branch[(SU_MD5_DIGEST_SIZE * 8 + 4) / 5 + 1];
	sip_route_t const *r;

	su_md5_init(md5);

	su_md5_str0update(md5, host);
	su_md5_str0update(md5, port);

	url_update(md5, sip->sip_request->rq_url);
	if (sip->sip_call_id) {
		su_md5_str0update(md5, sip->sip_call_id->i_id);
	}
	if (sip->sip_from) {
		url_update(md5, sip->sip_from->a_url);
		su_md5_stri0update(md5, sip->sip_from->a_tag);
	}
	if (sip->sip_to) {
		url_update(md5, sip->sip_to->a_url);
		/* XXX - some broken implementations include To tag in CANCEL */
		/* su_md5_str0update(md5, sip->sip_to->a_tag); */
	}
	if (sip->sip_cseq) {
		uint32_t cseq = htonl(sip->sip_cseq->cs_seq);
		su_md5_update(md5, &cseq, sizeof (cseq));
	}

	for (r = sip->sip_route; r; r = r->r_next)
		url_update(md5, r->r_url);

	su_md5_digest(md5, digest);

	msg_random_token(branch, sizeof (branch) - 1, digest, sizeof (digest));

	return su_sprintf(msg_home(msg), "branch=z9hG4bK.%s", branch);
}

<|endoftext|>