<commit_before>#ifndef OSRM_EDGE_BASED_GRAPH_READER_HPP
#define OSRM_EDGE_BASED_GRAPH_READER_HPP

#include "partition/edge_based_graph.hpp"

#include "extractor/edge_based_edge.hpp"
#include "extractor/files.hpp"
#include "storage/io.hpp"
#include "util/coordinate.hpp"
#include "util/dynamic_graph.hpp"
#include "util/typedefs.hpp"

#include <tbb/parallel_reduce.h>
#include <tbb/parallel_sort.h>

#include <cstdint>

#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>

namespace osrm
{
namespace partition
{

// Bidirectional (s,t) to (s,t) and (t,s)
std::vector<extractor::EdgeBasedEdge>
splitBidirectionalEdges(const std::vector<extractor::EdgeBasedEdge> &edges)
{
    std::vector<extractor::EdgeBasedEdge> directed;
    directed.reserve(edges.size() * 2);

    for (const auto &edge : edges)
    {
        if (edge.data.weight == INVALID_EDGE_WEIGHT)
            continue;

        directed.emplace_back(edge.source,
                              edge.target,
                              edge.data.turn_id,
                              std::max(edge.data.weight, 1),
                              edge.data.duration,
                              edge.data.forward,
                              edge.data.backward);

        directed.emplace_back(edge.target,
                              edge.source,
                              edge.data.turn_id,
                              std::max(edge.data.weight, 1),
                              edge.data.duration,
                              edge.data.backward,
                              edge.data.forward);
    }

    return directed;
}

template <typename OutputEdgeT>
std::vector<OutputEdgeT> prepareEdgesForUsageInGraph(std::vector<extractor::EdgeBasedEdge> edges)
{
    // sort into blocks of edges with same source + target
    // the we partition by the forward flag to sort all edges with a forward direction first.
    // the we sort by weight to ensure the first forward edge is the smallest forward edge
    std::sort(begin(edges), end(edges), [](const auto &lhs, const auto &rhs) {
        return std::tie(lhs.source, lhs.target, rhs.data.forward, lhs.data.weight) <
               std::tie(rhs.source, rhs.target, lhs.data.forward, rhs.data.weight);
    });

    std::vector<OutputEdgeT> output_edges;
    output_edges.reserve(edges.size());

    for (auto begin_interval = edges.begin(); begin_interval != edges.end();)
    {
        const NodeID source = begin_interval->source;
        const NodeID target = begin_interval->target;

        auto end_interval =
            std::find_if_not(begin_interval, edges.end(), [source, target](const auto &edge) {
                return std::tie(edge.source, edge.target) == std::tie(source, target);
            });
        BOOST_ASSERT(begin_interval != end_interval);

        // remove eigenloops
        if (source == target)
        {
            begin_interval = end_interval;
            continue;
        }

        BOOST_ASSERT_MSG(begin_interval->data.forward != begin_interval->data.backward,
                         "The forward and backward flag need to be mutally exclusive");

        // find smallest backward edge and check if we can merge
        auto first_backward = std::find_if(
            begin_interval, end_interval, [](const auto &edge) { return edge.data.backward; });

        // thanks to the sorting we know this is the smallest backward edge
        // and there is no forward edge
        if (begin_interval == first_backward)
        {
            output_edges.push_back(OutputEdgeT{source, target, first_backward->data});
        }
        // only a forward edge, thanks to the sorting this is the smallest
        else if (first_backward == end_interval)
        {
            output_edges.push_back(OutputEdgeT{source, target, begin_interval->data});
        }
        // we have both a forward and a backward edge, we need to evaluate
        // if we can merge them
        else
        {
            BOOST_ASSERT(begin_interval->data.forward);
            BOOST_ASSERT(first_backward->data.backward);
            BOOST_ASSERT(first_backward != end_interval);

            // same weight, so we can just merge them
            if (begin_interval->data.weight == first_backward->data.weight)
            {
                OutputEdgeT merged{source, target, begin_interval->data};
                merged.data.backward = true;
                output_edges.push_back(std::move(merged));
            }
            // we need to insert separate forward and reverse edges
            else
            {
                output_edges.push_back(OutputEdgeT{source, target, begin_interval->data});
                output_edges.push_back(OutputEdgeT{source, target, first_backward->data});
            }
        }

        begin_interval = end_interval;
    }

    return output_edges;
}

std::vector<extractor::EdgeBasedEdge> graphToEdges(const DynamicEdgeBasedGraph &edge_based_graph)
{
    auto range = tbb::blocked_range<NodeID>(0, edge_based_graph.GetNumberOfNodes());
    auto max_turn_id =
        tbb::parallel_reduce(range,
                             NodeID{0},
                             [&edge_based_graph](const auto range, NodeID initial) {
                                 NodeID max_turn_id = initial;
                                 for (auto node = range.begin(); node < range.end(); ++node)
                                 {
                                     for (auto edge : edge_based_graph.GetAdjacentEdgeRange(node))
                                     {
                                         const auto &data = edge_based_graph.GetEdgeData(edge);
                                         max_turn_id = std::max(max_turn_id, data.turn_id);
                                     }
                                 }
                                 return max_turn_id;
                             },
                             [](const NodeID lhs, const NodeID rhs) { return std::max(lhs, rhs); });

    std::vector<extractor::EdgeBasedEdge> edges(max_turn_id + 1);
    tbb::parallel_for(range, [&](const auto range) {
        for (auto node = range.begin(); node < range.end(); ++node)
        {
            for (auto edge : edge_based_graph.GetAdjacentEdgeRange(node))
            {
                const auto &data = edge_based_graph.GetEdgeData(edge);
                // we only need to save the forward edges, since the read method will
                // convert from forward to bi-directional edges again
                if (data.forward)
                {
                    auto target = edge_based_graph.GetTarget(edge);
                    BOOST_ASSERT(data.turn_id <= max_turn_id);
                    edges[data.turn_id] = extractor::EdgeBasedEdge{node, target, data};
                    // only save the forward edge
                    edges[data.turn_id].data.forward = true;
                    edges[data.turn_id].data.backward = false;
                }
            }
        }
    });

    return edges;
}

inline DynamicEdgeBasedGraph LoadEdgeBasedGraph(const boost::filesystem::path &path)
{
    EdgeID max_node_id;
    std::vector<extractor::EdgeBasedEdge> edges;
    extractor::files::readEdgeBasedGraph(path, max_node_id, edges);

    auto directed = splitBidirectionalEdges(edges);
    auto tidied = prepareEdgesForUsageInGraph<DynamicEdgeBasedGraphEdge>(std::move(directed));

    return DynamicEdgeBasedGraph(max_node_id + 1, std::move(tidied));
}

} // ns partition
} // ns osrm

#endif
<commit_msg>Prevent linkage errors when load/write extracted graph from external app<commit_after>#ifndef OSRM_EDGE_BASED_GRAPH_READER_HPP
#define OSRM_EDGE_BASED_GRAPH_READER_HPP

#include "partition/edge_based_graph.hpp"

#include "extractor/edge_based_edge.hpp"
#include "extractor/files.hpp"
#include "storage/io.hpp"
#include "util/coordinate.hpp"
#include "util/dynamic_graph.hpp"
#include "util/typedefs.hpp"

#include <tbb/parallel_reduce.h>
#include <tbb/parallel_sort.h>

#include <cstdint>

#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>

namespace osrm
{
namespace partition
{

// Bidirectional (s,t) to (s,t) and (t,s)
inline std::vector<extractor::EdgeBasedEdge>
splitBidirectionalEdges(const std::vector<extractor::EdgeBasedEdge> &edges)
{
    std::vector<extractor::EdgeBasedEdge> directed;
    directed.reserve(edges.size() * 2);

    for (const auto &edge : edges)
    {
        if (edge.data.weight == INVALID_EDGE_WEIGHT)
            continue;

        directed.emplace_back(edge.source,
                              edge.target,
                              edge.data.turn_id,
                              std::max(edge.data.weight, 1),
                              edge.data.duration,
                              edge.data.forward,
                              edge.data.backward);

        directed.emplace_back(edge.target,
                              edge.source,
                              edge.data.turn_id,
                              std::max(edge.data.weight, 1),
                              edge.data.duration,
                              edge.data.backward,
                              edge.data.forward);
    }

    return directed;
}

template <typename OutputEdgeT>
std::vector<OutputEdgeT> prepareEdgesForUsageInGraph(std::vector<extractor::EdgeBasedEdge> edges)
{
    // sort into blocks of edges with same source + target
    // the we partition by the forward flag to sort all edges with a forward direction first.
    // the we sort by weight to ensure the first forward edge is the smallest forward edge
    std::sort(begin(edges), end(edges), [](const auto &lhs, const auto &rhs) {
        return std::tie(lhs.source, lhs.target, rhs.data.forward, lhs.data.weight) <
               std::tie(rhs.source, rhs.target, lhs.data.forward, rhs.data.weight);
    });

    std::vector<OutputEdgeT> output_edges;
    output_edges.reserve(edges.size());

    for (auto begin_interval = edges.begin(); begin_interval != edges.end();)
    {
        const NodeID source = begin_interval->source;
        const NodeID target = begin_interval->target;

        auto end_interval =
            std::find_if_not(begin_interval, edges.end(), [source, target](const auto &edge) {
                return std::tie(edge.source, edge.target) == std::tie(source, target);
            });
        BOOST_ASSERT(begin_interval != end_interval);

        // remove eigenloops
        if (source == target)
        {
            begin_interval = end_interval;
            continue;
        }

        BOOST_ASSERT_MSG(begin_interval->data.forward != begin_interval->data.backward,
                         "The forward and backward flag need to be mutally exclusive");

        // find smallest backward edge and check if we can merge
        auto first_backward = std::find_if(
            begin_interval, end_interval, [](const auto &edge) { return edge.data.backward; });

        // thanks to the sorting we know this is the smallest backward edge
        // and there is no forward edge
        if (begin_interval == first_backward)
        {
            output_edges.push_back(OutputEdgeT{source, target, first_backward->data});
        }
        // only a forward edge, thanks to the sorting this is the smallest
        else if (first_backward == end_interval)
        {
            output_edges.push_back(OutputEdgeT{source, target, begin_interval->data});
        }
        // we have both a forward and a backward edge, we need to evaluate
        // if we can merge them
        else
        {
            BOOST_ASSERT(begin_interval->data.forward);
            BOOST_ASSERT(first_backward->data.backward);
            BOOST_ASSERT(first_backward != end_interval);

            // same weight, so we can just merge them
            if (begin_interval->data.weight == first_backward->data.weight)
            {
                OutputEdgeT merged{source, target, begin_interval->data};
                merged.data.backward = true;
                output_edges.push_back(std::move(merged));
            }
            // we need to insert separate forward and reverse edges
            else
            {
                output_edges.push_back(OutputEdgeT{source, target, begin_interval->data});
                output_edges.push_back(OutputEdgeT{source, target, first_backward->data});
            }
        }

        begin_interval = end_interval;
    }

    return output_edges;
}

inline std::vector<extractor::EdgeBasedEdge>
graphToEdges(const DynamicEdgeBasedGraph &edge_based_graph)
{
    auto range = tbb::blocked_range<NodeID>(0, edge_based_graph.GetNumberOfNodes());
    auto max_turn_id =
        tbb::parallel_reduce(range,
                             NodeID{0},
                             [&edge_based_graph](const auto range, NodeID initial) {
                                 NodeID max_turn_id = initial;
                                 for (auto node = range.begin(); node < range.end(); ++node)
                                 {
                                     for (auto edge : edge_based_graph.GetAdjacentEdgeRange(node))
                                     {
                                         const auto &data = edge_based_graph.GetEdgeData(edge);
                                         max_turn_id = std::max(max_turn_id, data.turn_id);
                                     }
                                 }
                                 return max_turn_id;
                             },
                             [](const NodeID lhs, const NodeID rhs) { return std::max(lhs, rhs); });

    std::vector<extractor::EdgeBasedEdge> edges(max_turn_id + 1);
    tbb::parallel_for(range, [&](const auto range) {
        for (auto node = range.begin(); node < range.end(); ++node)
        {
            for (auto edge : edge_based_graph.GetAdjacentEdgeRange(node))
            {
                const auto &data = edge_based_graph.GetEdgeData(edge);
                // we only need to save the forward edges, since the read method will
                // convert from forward to bi-directional edges again
                if (data.forward)
                {
                    auto target = edge_based_graph.GetTarget(edge);
                    BOOST_ASSERT(data.turn_id <= max_turn_id);
                    edges[data.turn_id] = extractor::EdgeBasedEdge{node, target, data};
                    // only save the forward edge
                    edges[data.turn_id].data.forward = true;
                    edges[data.turn_id].data.backward = false;
                }
            }
        }
    });

    return edges;
}

inline DynamicEdgeBasedGraph LoadEdgeBasedGraph(const boost::filesystem::path &path)
{
    EdgeID max_node_id;
    std::vector<extractor::EdgeBasedEdge> edges;
    extractor::files::readEdgeBasedGraph(path, max_node_id, edges);

    auto directed = splitBidirectionalEdges(edges);
    auto tidied = prepareEdgesForUsageInGraph<DynamicEdgeBasedGraphEdge>(std::move(directed));

    return DynamicEdgeBasedGraph(max_node_id + 1, std::move(tidied));
}

} // ns partition
} // ns osrm

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2016-2020 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_INTERNAL_CSTREAM_READER_HPP
#define TAO_PEGTL_INTERNAL_CSTREAM_READER_HPP

#include <cassert>
#include <cstddef>
#include <cstdio>

#include <system_error>

#include "../config.hpp"

namespace TAO_PEGTL_NAMESPACE::internal
{
   struct cstream_reader
   {
      explicit cstream_reader( std::FILE* s ) noexcept
         : m_cstream( s )
      {
         assert( m_cstream != nullptr );
      }

      [[nodiscard]] std::size_t operator()( char* buffer, const std::size_t length )
      {
         if( const auto r = std::fread( buffer, 1, length, m_cstream ) ) {
            return r;
         }
         if( std::feof( m_cstream ) != 0 ) {
            return 0;
         }

         // Please contact us if you know how to provoke the following exception.
         // The example on cppreference.com doesn't work, at least not on macOS.

         // LCOV_EXCL_START
         const auto ec = std::ferror( m_cstream );
         assert( ec != 0 );
         throw std::system_error( ec, std::system_category(), "fread() failed" );
         // LCOV_EXCL_STOP
      }

      std::FILE* m_cstream;
   };

}  // namespace TAO_PEGTL_NAMESPACE::internal

#endif
<commit_msg>Silence clang-tidy<commit_after>// Copyright (c) 2016-2020 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_INTERNAL_CSTREAM_READER_HPP
#define TAO_PEGTL_INTERNAL_CSTREAM_READER_HPP

#include <cassert>
#include <cstddef>
#include <cstdio>

#include <system_error>

#include "../config.hpp"

namespace TAO_PEGTL_NAMESPACE::internal
{
   struct cstream_reader
   {
      explicit cstream_reader( std::FILE* s ) noexcept
         : m_cstream( s )
      {
         assert( m_cstream != nullptr );
      }

      [[nodiscard]] std::size_t operator()( char* buffer, const std::size_t length ) const
      {
         if( const auto r = std::fread( buffer, 1, length, m_cstream ) ) {
            return r;
         }
         if( std::feof( m_cstream ) != 0 ) {
            return 0;
         }

         // Please contact us if you know how to provoke the following exception.
         // The example on cppreference.com doesn't work, at least not on macOS.

         // LCOV_EXCL_START
         const auto ec = std::ferror( m_cstream );
         assert( ec != 0 );
         throw std::system_error( ec, std::system_category(), "fread() failed" );
         // LCOV_EXCL_STOP
      }

      std::FILE* m_cstream;
   };

}  // namespace TAO_PEGTL_NAMESPACE::internal

#endif
<|endoftext|>


<commit_before>#include "RenderSector.h"

#include <GLFW\glfw3.h>
#include <iostream>
#include <assert.h>

struct RenderSectorFaceIndexing
{
  RenderSectorFaceIndexing()
  {
    unsigned int index = 0;
    // left, right
    for (unsigned int z = 0; z < SECTOR_SIZE; ++z)
    for (unsigned int y = 0; y < SECTOR_SIZE; ++y)
    {
      data[index++] = { 0, y, z };
      data[index++] = { SECTOR_SIZE - 1, y, z };
    }
    // front, back
    for (unsigned int y = 0; y < SECTOR_SIZE; ++y)
    for (unsigned int x = 1; x < SECTOR_SIZE - 1; ++x)
    {
      data[index++] = { x, y, SECTOR_SIZE - 1 };
      data[index++] = { x, y, 0 };
    }
    // top, bottom
    for (unsigned int z = 1; z < SECTOR_SIZE - 1; ++z)
    for (unsigned int x = 1; x < SECTOR_SIZE - 1; ++x)
    {
      data[index++] = { x, SECTOR_SIZE - 1, z };
      data[index++] = { x, 0, z };
    }
    assert(index == SIZE);
  };
  enum
  {
    SIZE = SECTOR_SIZE * SECTOR_SIZE * 2 +
    SECTOR_SIZE * (SECTOR_SIZE - 2) * 2 +
    (SECTOR_SIZE - 2) * (SECTOR_SIZE - 2) * 2,
  };
  glm::uvec3 data[SIZE];
} static faceIndexing;

RenderSector::RenderSector(const Sector &sector)
  : mSector(sector)
{
  mBufferStatic.EnableAttribute(ATTRIBUTE_VERTEX, sizeof(VertexVT::vertex), offsetof(VertexVT, vertex));
  mBufferStatic.EnableAttribute(ATTRIBUTE_TEXTURE, sizeof(VertexVT::texture), offsetof(VertexVT, texture));
}

RenderSector::~RenderSector()
{
}

void RenderSector::Generate()
{
  auto &vertex = mBufferStatic.Vertex();
  auto &index = mBufferStatic.Index();

  //auto currentTime = glfwGetTime();

  vertex.clear();
  index.clear();

  const auto &map = mSector.mMap;

  glm::uvec3 pos(1);

  for (; pos.z < SECTOR_SIZE - 1; ++pos.z)
  {
    for (; pos.y < SECTOR_SIZE - 1; ++pos.y)
    {
      for (; pos.x < SECTOR_SIZE - 1; ++pos.x)
      {
        const auto block = map[pos.z][pos.y][pos.x];
        Model::Side sides = Model::EMPTY;
        if (block)
        {
          if (!map[pos.z][pos.y][pos.x + 1]) sides |= Model::RIGHT;
          if (!map[pos.z][pos.y][pos.x - 1]) sides |= Model::LEFT;
          if (!map[pos.z][pos.y + 1][pos.x]) sides |= Model::TOP;
          if (!map[pos.z][pos.y - 1][pos.x]) sides |= Model::BOTTOM;
          if (!map[pos.z + 1][pos.y][pos.x]) sides |= Model::FRONT;
          if (!map[pos.z - 1][pos.y][pos.x]) sides |= Model::BACK;

          if (sides)
          {
            block->GetModel().FillBuffer(mBufferStatic, pos, sides);
          }
        }
      }
      pos.x = 1;
    }
    pos.y = 1;
  }

  for (unsigned int i = 0; i < RenderSectorFaceIndexing::SIZE; ++i)
  {
    const auto &pos = faceIndexing.data[i];
    const auto block = map[pos.z][pos.y][pos.x];
    if (block)
    {
      block->GetModel().FillBuffer(mBufferStatic, pos);
    }
  }

  //std::cout << "RenderGen: " << glfwGetTime() - currentTime << std::endl;

  //currentTime = glfwGetTime();
  mBufferStatic.Compile();
  //std::cout << "RenderCompile: " << glfwGetTime() - currentTime << std::endl;
}

BufferArray<VertexVT> & RenderSector::GetBuffer()
{
  return mBufferStatic;
}
<commit_msg>Генератор сетки сектора<commit_after>#include "RenderSector.h"

#include <GLFW\glfw3.h>
#include <iostream>
#include <assert.h>

class SectorFaceGenerator
{
public:
  enum
  {
    SIZE = SECTOR_SIZE * SECTOR_SIZE * 2 +
    SECTOR_SIZE * (SECTOR_SIZE - 2) * 2 +
    (SECTOR_SIZE - 2) * (SECTOR_SIZE - 2) * 2,
  };
public:

  static const glm::uvec3 *Data()
  {
    static SectorFaceGenerator generator;
    return generator.data;
  }

private:
  glm::uvec3 data[SIZE];
private:
  SectorFaceGenerator()
  {
    unsigned int index = 0;
    // left, right
    for (unsigned int z = 0; z < SECTOR_SIZE; ++z)
      for (unsigned int y = 0; y < SECTOR_SIZE; ++y)
      {
        data[index++] = { 0, y, z };
        data[index++] = { SECTOR_SIZE - 1, y, z };
      }
    // front, back
    for (unsigned int y = 0; y < SECTOR_SIZE; ++y)
      for (unsigned int x = 1; x < SECTOR_SIZE - 1; ++x)
      {
        data[index++] = { x, y, SECTOR_SIZE - 1 };
        data[index++] = { x, y, 0 };
      }
    // top, bottom
    for (unsigned int z = 1; z < SECTOR_SIZE - 1; ++z)
      for (unsigned int x = 1; x < SECTOR_SIZE - 1; ++x)
      {
        data[index++] = { x, SECTOR_SIZE - 1, z };
        data[index++] = { x, 0, z };
      }
    assert(index == SIZE);
  }
  ~SectorFaceGenerator() {};
  SectorFaceGenerator(const SectorFaceGenerator &);
  SectorFaceGenerator& operator=(const SectorFaceGenerator &);
};

RenderSector::RenderSector(const Sector &sector)
  : mSector(sector)
{
  mBufferStatic.EnableAttribute(ATTRIBUTE_VERTEX, sizeof(VertexVT::vertex), offsetof(VertexVT, vertex));
  mBufferStatic.EnableAttribute(ATTRIBUTE_TEXTURE, sizeof(VertexVT::texture), offsetof(VertexVT, texture));
}

RenderSector::~RenderSector()
{
}

void RenderSector::Generate()
{
  auto &vertex = mBufferStatic.Vertex();
  auto &index = mBufferStatic.Index();

  //auto currentTime = glfwGetTime();

  vertex.clear();
  index.clear();

  const auto &map = mSector.mMap;

  glm::uvec3 pos(1);

  for (; pos.z < SECTOR_SIZE - 1; ++pos.z)
  {
    for (; pos.y < SECTOR_SIZE - 1; ++pos.y)
    {
      for (; pos.x < SECTOR_SIZE - 1; ++pos.x)
      {
        const auto block = map[pos.z][pos.y][pos.x];
        Model::Side sides = Model::EMPTY;
        if (block)
        {
          if (!map[pos.z][pos.y][pos.x + 1]) sides |= Model::RIGHT;
          if (!map[pos.z][pos.y][pos.x - 1]) sides |= Model::LEFT;
          if (!map[pos.z][pos.y + 1][pos.x]) sides |= Model::TOP;
          if (!map[pos.z][pos.y - 1][pos.x]) sides |= Model::BOTTOM;
          if (!map[pos.z + 1][pos.y][pos.x]) sides |= Model::FRONT;
          if (!map[pos.z - 1][pos.y][pos.x]) sides |= Model::BACK;

          if (sides)
          {
            block->GetModel().FillBuffer(mBufferStatic, pos, sides);
          }
        }
      }
      pos.x = 1;
    }
    pos.y = 1;
  }

  //map[0][0][0]->GetModel().FillBuffer(mBufferStatic, {0, 0, 0});

  for (unsigned int i = 0; i < SectorFaceGenerator::SIZE; ++i)
  {
    const auto &pos = SectorFaceGenerator::Data()[i];
    const auto block = map[pos.z][pos.y][pos.x];
    if (block)
    {
      block->GetModel().FillBuffer(mBufferStatic, pos);
    }
  }

  //std::cout << "RenderGen: " << glfwGetTime() - currentTime << std::endl;

  //currentTime = glfwGetTime();
  mBufferStatic.Compile();
  //std::cout << "RenderCompile: " << glfwGetTime() - currentTime << std::endl;
}

BufferArray<VertexVT> & RenderSector::GetBuffer()
{
  return mBufferStatic;
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/trace/buffer.H $                                      */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2012,2017                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#ifndef __TRACE_BUFFER_H
#define __TRACE_BUFFER_H

/** @file buffer.H
 *  Declarations for the buffer class.
 */

#include <trace/interface.H>
#include <sys/sync.h>
#include "daemon/daemon.H"

#ifndef __HOSTBOOT_RUNTIME
namespace TRACEDAEMON { class Daemon; } // Forward declaration.
#endif

namespace TRACE
{
        // Forward declarations.
    class BufferPage;
    class Entry;
#ifndef __HOSTBOOT_RUNTIME
    class DaemonIf;
#endif
    /** @class Buffer
     *  @brief Class to manage the front-side (client) buffers
     *
     *  Provides interfaces for getting an entry to write trace data to and
     *  committing that entry as well as extracting the component trace.
     *
     *  Private interfaces for the daemon to ensure lockless ordering is
     *  maintained.
     */
    class Buffer
    {
        public:

            enum
            {
                /** Used as a parameter to the constructor to indicate that
                 *  the buffer should never block (and could instead run
                 *  the system out of memory). */
                UNLIMITED = UINT32_MAX
            };

#ifndef __HOSTBOOT_RUNTIME
            /** Constructor.
             *
             *  @param[in] i_daemon - Daemon interface for this buffer.
             *  @param[in] i_maxPages - Maximum number of pages to consume
             *                          before 'claimEntry' blocks.
             */
            Buffer(DaemonIf* i_daemon, size_t i_maxPages = 4);
#else
            /** Constructor.
             *
             *  @param[in] i_daemon   - Runtime "daemon" for this buffer.
             *  @param[in] i_maxPages - Maximum number of pages to consume
             *                          before 'claimEntry' blocks.
             */
            Buffer(TRACEDAEMON::Daemon * i_daemon, size_t i_maxPages = 4);
#endif
            /** @brief Claim an entry from the buffer to write data to.
             *
             *  @param[in] i_comp - Component which will own entry.
             *  @param[in] i_size - Size of data portion of entry.
             *
             *  @return An entry to write to.
             *
             *  If pages allocated is at max pages and there is no room to
             *  create an entry, this function may block.  For components
             *  which cannot block, it is expected that the buffer will be
             *  constructed large enough that we run out of memory before
             *  we block.
             */
            Entry* claimEntry(ComponentDesc* i_comp, size_t i_size);

            /** @brief Commit entry to buffer and component linked-list.
             *
             *  @param[in] i_entry - Entry to commit.
             */
            void commitEntry(Entry* i_entry);

            /** @brief Extract component trace.
             *
             *  See service::getBuffer.  Defined here due to lockless ordering
             *  needs.
             */
            size_t getTrace(ComponentDesc* i_comp, void* o_data, size_t i_size);

        private:
            volatile uint64_t iv_pagesAlloc;   //< Number of pages allocated.
            uint64_t iv_pagesMax;              //< Maximum pages allowed.

            BufferPage* volatile iv_firstPage; //< Most recent page.

#ifndef __HOSTBOOT_RUNTIME
            /** @union locklessCounter
             *
             *  This class is mostly lockless in that multiple trace entries
             *  can be created at the same time along with buffer extraction.
             *  This means that all client operations are lockless.
             *
             *  There are minor operations that the daemon must do to ensure
             *  that a client is not in the middle of another operation, such
             *  as when the daemon removes the pages from the front-end to
             *  merge them into the backend.  During this time, the daemon
             *  will obtain the equivalent of a write-lock, which will block
             *  all clients.
             *
             *  It is required that all times where the write-lock is held
             *  are short and cannot cause page-faults that might deadlock
             *  components such as VFS and PNOR.  This means that all code
             *  inside the write-lock is in the base image.
             *
             *  This read/write lock is modeled here as "producers" (clients)
             *  and a "consumer" (daemon).  The active counts of each are
             *  recorded with this structure and unioned to a single uint64_t
             *  so that they can be atomically updated as one entity.
             */
            union locklessCounter
            {
                struct
                {
                    uint32_t producerCount;
                    uint32_t consumerCount;
                };
                uint64_t totals;
            };

            locklessCounter iv_counters; //< The producer/consumer counts.

            DaemonIf* iv_daemon; //< Daemon interface.

            void _producerEnter();      //< Enter client section.
            void _producerExit();       //< Exit client section.
            void _consumerEnter();      //< Enter daemon section.
            void _consumerExit();       //< Exit daemon section.
#else
            TRACEDAEMON::Daemon * iv_daemon;  // Daemon
#endif
            /** @brief Claim front-side buffer pages to be merged into the
             *         common buffer.
             *
             *  @return Pointer to the first page (most recent) from this
             *          buffer.
             *
             *  Gives ownership of all the current pages to the caller and
             *  resets this buffer's allocated page counts.
             */
            BufferPage* claimPages();

            /** @brief Perform operations with the consumer lock held.
             *
             *  These operations cannot be done in the daemon code directly
             *  because the daemon code is swappable and we cannot block
             *  producers.
             *
             *  This function gives a set of primitives to the daemon to
             *  manipulate Entry pointers while secured under the consumer lock.
             *
             *  The primitives that can be done are:
             *     1) if (condAddr == condVal) { condActAddr = condActVal }
             *     2) addr = val
             *
             *  If any of the address parameters are NULL, their action will
             *  not be performed.  So, just calling consumerOp() has the
             *  side-effect of simply toggling the consumer lock on and off.
             *
             *  The return value indicates if primitive #1 failed.
             *
             *  @param[in] i_condAddr - The address to read for primitive 1.
             *  @param[in] i_condVal - The value to expect for primitive 1.
             *  @param[in] i_condActAddr - The address to store for primitive 1.
             *  @param[in] i_condActVal - The value to store for primitive 1.
             *
             *  @param[in] i_addr - The address to store for primitive 2.
             *  @param[in] i_val - The value to store for primitive 2.
             *
             *  @return bool - false if condAddr != condVal, true otherwise.
             */
            bool consumerOp(Entry** i_condAddr = NULL, Entry* i_condVal = NULL,
                                Entry** i_condActAddr = NULL,
                                Entry* i_condActVal = NULL,
                            Entry** i_addr = NULL, Entry* i_val = NULL);

            friend class BufferTest;

            friend class TRACEDAEMON::Daemon;

    };
}

#endif
<commit_msg>Increase trace buffer size to avoid hang between traces and PRD.<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/trace/buffer.H $                                      */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2012,2017                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#ifndef __TRACE_BUFFER_H
#define __TRACE_BUFFER_H

/** @file buffer.H
 *  Declarations for the buffer class.
 */

#include <trace/interface.H>
#include <sys/sync.h>
#include "daemon/daemon.H"

#ifndef __HOSTBOOT_RUNTIME
namespace TRACEDAEMON { class Daemon; } // Forward declaration.
#endif

namespace TRACE
{
        // Forward declarations.
    class BufferPage;
    class Entry;
#ifndef __HOSTBOOT_RUNTIME
    class DaemonIf;
#endif
    /** @class Buffer
     *  @brief Class to manage the front-side (client) buffers
     *
     *  Provides interfaces for getting an entry to write trace data to and
     *  committing that entry as well as extracting the component trace.
     *
     *  Private interfaces for the daemon to ensure lockless ordering is
     *  maintained.
     */
    class Buffer
    {
        public:

            enum
            {
                /** Used as a parameter to the constructor to indicate that
                 *  the buffer should never block (and could instead run
                 *  the system out of memory). */
                UNLIMITED = UINT32_MAX
            };

#ifndef __HOSTBOOT_RUNTIME
            /** Constructor.
             *
             *  @param[in] i_daemon - Daemon interface for this buffer.
             *  @param[in] i_maxPages - Maximum number of pages to consume
             *                          before 'claimEntry' blocks.
             */
            Buffer(DaemonIf* i_daemon, size_t i_maxPages = 4);
#else
            /** Constructor.
             *
             *  @param[in] i_daemon   - Runtime "daemon" for this buffer.
             *  @param[in] i_maxPages - Maximum number of pages to consume
             *                          before 'claimEntry' blocks.
             */
            Buffer(TRACEDAEMON::Daemon * i_daemon, size_t i_maxPages = 16);
#endif
            /** @brief Claim an entry from the buffer to write data to.
             *
             *  @param[in] i_comp - Component which will own entry.
             *  @param[in] i_size - Size of data portion of entry.
             *
             *  @return An entry to write to.
             *
             *  If pages allocated is at max pages and there is no room to
             *  create an entry, this function may block.  For components
             *  which cannot block, it is expected that the buffer will be
             *  constructed large enough that we run out of memory before
             *  we block.
             */
            Entry* claimEntry(ComponentDesc* i_comp, size_t i_size);

            /** @brief Commit entry to buffer and component linked-list.
             *
             *  @param[in] i_entry - Entry to commit.
             */
            void commitEntry(Entry* i_entry);

            /** @brief Extract component trace.
             *
             *  See service::getBuffer.  Defined here due to lockless ordering
             *  needs.
             */
            size_t getTrace(ComponentDesc* i_comp, void* o_data, size_t i_size);

        private:
            volatile uint64_t iv_pagesAlloc;   //< Number of pages allocated.
            uint64_t iv_pagesMax;              //< Maximum pages allowed.

            BufferPage* volatile iv_firstPage; //< Most recent page.

#ifndef __HOSTBOOT_RUNTIME
            /** @union locklessCounter
             *
             *  This class is mostly lockless in that multiple trace entries
             *  can be created at the same time along with buffer extraction.
             *  This means that all client operations are lockless.
             *
             *  There are minor operations that the daemon must do to ensure
             *  that a client is not in the middle of another operation, such
             *  as when the daemon removes the pages from the front-end to
             *  merge them into the backend.  During this time, the daemon
             *  will obtain the equivalent of a write-lock, which will block
             *  all clients.
             *
             *  It is required that all times where the write-lock is held
             *  are short and cannot cause page-faults that might deadlock
             *  components such as VFS and PNOR.  This means that all code
             *  inside the write-lock is in the base image.
             *
             *  This read/write lock is modeled here as "producers" (clients)
             *  and a "consumer" (daemon).  The active counts of each are
             *  recorded with this structure and unioned to a single uint64_t
             *  so that they can be atomically updated as one entity.
             */
            union locklessCounter
            {
                struct
                {
                    uint32_t producerCount;
                    uint32_t consumerCount;
                };
                uint64_t totals;
            };

            locklessCounter iv_counters; //< The producer/consumer counts.

            DaemonIf* iv_daemon; //< Daemon interface.

            void _producerEnter();      //< Enter client section.
            void _producerExit();       //< Exit client section.
            void _consumerEnter();      //< Enter daemon section.
            void _consumerExit();       //< Exit daemon section.
#else
            TRACEDAEMON::Daemon * iv_daemon;  // Daemon
#endif
            /** @brief Claim front-side buffer pages to be merged into the
             *         common buffer.
             *
             *  @return Pointer to the first page (most recent) from this
             *          buffer.
             *
             *  Gives ownership of all the current pages to the caller and
             *  resets this buffer's allocated page counts.
             */
            BufferPage* claimPages();

            /** @brief Perform operations with the consumer lock held.
             *
             *  These operations cannot be done in the daemon code directly
             *  because the daemon code is swappable and we cannot block
             *  producers.
             *
             *  This function gives a set of primitives to the daemon to
             *  manipulate Entry pointers while secured under the consumer lock.
             *
             *  The primitives that can be done are:
             *     1) if (condAddr == condVal) { condActAddr = condActVal }
             *     2) addr = val
             *
             *  If any of the address parameters are NULL, their action will
             *  not be performed.  So, just calling consumerOp() has the
             *  side-effect of simply toggling the consumer lock on and off.
             *
             *  The return value indicates if primitive #1 failed.
             *
             *  @param[in] i_condAddr - The address to read for primitive 1.
             *  @param[in] i_condVal - The value to expect for primitive 1.
             *  @param[in] i_condActAddr - The address to store for primitive 1.
             *  @param[in] i_condActVal - The value to store for primitive 1.
             *
             *  @param[in] i_addr - The address to store for primitive 2.
             *  @param[in] i_val - The value to store for primitive 2.
             *
             *  @return bool - false if condAddr != condVal, true otherwise.
             */
            bool consumerOp(Entry** i_condAddr = NULL, Entry* i_condVal = NULL,
                                Entry** i_condActAddr = NULL,
                                Entry* i_condActVal = NULL,
                            Entry** i_addr = NULL, Entry* i_val = NULL);

            friend class BufferTest;

            friend class TRACEDAEMON::Daemon;

    };
}

#endif
<|endoftext|>


<commit_before>/*
 * This file is part of the Phatbooks project and is distributed under the
 * terms of the license contained in the file LICENSE.txt distributed
 * with this package.
 *
 * Author: Matthew Harvey <matthew@matthewharvey.net>
 *
 * Copyright (c) 2012-2013, Matthew Harvey.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "commodity.hpp"
#include "balance_cache.hpp"
#include "phatbooks_database_connection.hpp"
#include "string_conv.hpp"
#include "string_conv.hpp"
#include <sqloxx/database_transaction.hpp>
#include <sqloxx/general_typedefs.hpp>
#include <sqloxx/identity_map.hpp>
#include <sqloxx/persistent_object.hpp>
#include <sqloxx/sqloxx_exceptions.hpp>
#include <sqloxx/sql_statement.hpp>
#include <jewel/assert.hpp>
#include <jewel/log.hpp>
#include <jewel/decimal.hpp>
#include <jewel/optional.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/optional.hpp>
#include <wx/string.h>
#include <algorithm>
#include <iostream>  // for debug logging
#include <stdexcept>
#include <string>

using boost::numeric_cast;
using boost::optional;
using jewel::clear;
using jewel::Decimal;
using jewel::value;
using sqloxx::DatabaseTransaction;
using sqloxx::Id;
using sqloxx::SQLStatement;
using std::endl;  // for debug logging
using std::exception;
using std::string;

namespace phatbooks
{

struct Commodity::CommodityData
{
	optional<wxString> abbreviation;
	optional<wxString> name;
	optional<wxString> description;
	optional<int> precision;
	optional<jewel::Decimal> multiplier_to_base;
};

void Commodity::setup_tables
(	PhatbooksDatabaseConnection& dbc
)
{
	// Create the table
	SQLStatement statement
	(	dbc,
		"create table commodities"
		"("
			"commodity_id integer primary key autoincrement, "
			"abbreviation text not null unique, "
			"name text not null unique, "
			"description text, "
			"precision integer default 2 not null, "
			"multiplier_to_base_intval integer not null, "
			"multiplier_to_base_places integer not null"
		")"
	);
	statement.step_final();
	return;
}

Id
Commodity::id_for_abbreviation
(	PhatbooksDatabaseConnection& dbc,
	wxString const& p_abbreviation
)
{
	SQLStatement statement
	(	dbc,
		"select commodity_id from commodities where abbreviation = :p"
	);
	statement.bind(":p", wx_to_std8(p_abbreviation));
	statement.step();
	Id const ret = statement.extract<Id>(0);
	statement.step_final();
	return ret;
}

Commodity::Commodity(Commodity const& rhs):
	PersistentObject(rhs),
	m_data(new CommodityData(*(rhs.m_data)))
{
}

Commodity::Commodity
(	IdentityMap& p_identity_map,
	IdentityMap::Signature const& p_signature
):
	PersistentObject(p_identity_map),
	m_data(new CommodityData)
{
	(void)p_signature;  // silence compiler re. unused parameter
}

Commodity::Commodity
(	IdentityMap& p_identity_map,
	Id p_id,
	IdentityMap::Signature const& p_signature
):
	PersistentObject(p_identity_map, p_id),
	m_data(new CommodityData)
{
	(void)p_signature;  // silence compiler. re. unused parameter
}

Commodity::~Commodity() = default;

bool
Commodity::exists_with_abbreviation
(	PhatbooksDatabaseConnection& p_database_connection,
	wxString const& p_abbreviation
)
{
	SQLStatement statement
	(	p_database_connection,
		"select abbreviation from commodities where abbreviation = :p"
	);
	statement.bind(":p", wx_to_std8(p_abbreviation));
	return statement.step();
}

bool
Commodity::exists_with_name
(	PhatbooksDatabaseConnection& p_database_connection,
	wxString const& p_name
)
{
	SQLStatement statement
	(	p_database_connection,
		"select name from commodities where name = :p"
	);
	statement.bind(":p", wx_to_std8(p_name));
	return statement.step();
}

void
Commodity::swap(Commodity& rhs)
{
	swap_base_internals(rhs);
	using std::swap;
	swap(m_data, rhs.m_data);
	return;
}

void Commodity::do_load()
{
	Commodity temp(*this);
	SQLStatement statement
	(	database_connection(),
		"select abbreviation, name, description, precision, "
		"multiplier_to_base_intval, multiplier_to_base_places from "
		"commodities where commodity_id = :p"
	);
	statement.bind(":p", id());
	statement.step();
	temp.m_data->abbreviation = std8_to_wx(statement.extract<string>(0));
	temp.m_data->name = std8_to_wx(statement.extract<string>(1));
	temp.m_data->description = std8_to_wx(statement.extract<string>(2));
	temp.m_data->precision = statement.extract<int>(3);
	temp.m_data->multiplier_to_base = Decimal
	(	statement.extract<Decimal::int_type>(4),
		numeric_cast<Decimal::places_type>(statement.extract<int>(5))
	);
	swap(temp);
	return;
}

void Commodity::process_saving_statement(SQLStatement& statement)
{
	statement.bind
	(	":abbreviation",
		wx_to_std8(value(m_data->abbreviation))
	);
	statement.bind(":name", wx_to_std8(value(m_data->name)));
	statement.bind
	(	":description",
		wx_to_std8(value(m_data->description))
	);
	statement.bind(":precision", value(m_data->precision));
	Decimal m = value(m_data->multiplier_to_base);
	statement.bind(":multiplier_to_base_intval", m.intval());
	statement.bind(":multiplier_to_base_places", m.places());
	statement.step_final();
	return;
}

void Commodity::do_save_existing()
{
	PhatbooksDatabaseConnection::BalanceCacheAttorney::mark_as_stale
	(	database_connection()
	);
	SQLStatement updater
	(	database_connection(),
		"update commodities set "
		"abbreviation = :abbreviation, "
		"name = :name, "
		"description = :description, "
		"precision = :precision, "
		"multiplier_to_base_intval = :multiplier_to_base_intval, "
		"multiplier_to_base_places = :multiplier_to_base_places "
		"where commodity_id = :commodity_id"
	);
	updater.bind(":commodity_id", id());
	process_saving_statement(updater);
	return;
}

void Commodity::do_save_new()
{
	PhatbooksDatabaseConnection::BalanceCacheAttorney::mark_as_stale
	(	database_connection()
	);
	SQLStatement inserter
	(	database_connection(),
		"insert into commodities(abbreviation, name, description, precision, "
		"multiplier_to_base_intval, multiplier_to_base_places) "
		"values(:abbreviation, :name, :description, :precision, "
		":multiplier_to_base_intval, :multiplier_to_base_places)"
	);
	process_saving_statement(inserter);
	return;
}

void Commodity::do_ghostify()
{
	clear(m_data->abbreviation);
	clear(m_data->name);
	clear(m_data->description);
	clear(m_data->precision);
	clear(m_data->multiplier_to_base);
	return;
}

wxString Commodity::abbreviation()
{
	load();
	return value(m_data->abbreviation);
}

wxString Commodity::name()
{
	load();
	return value(m_data->name);
}

wxString Commodity::description()
{
	load();
	return value(m_data->description);
}

int Commodity::precision()
{
	load();
	return value(m_data->precision);
}

jewel::Decimal Commodity::multiplier_to_base()
{
	load();
	return value(m_data->multiplier_to_base);
}

void Commodity::set_abbreviation(wxString const& p_abbreviation)
{
	load();
	m_data->abbreviation = p_abbreviation;
	return;
}

void Commodity::set_name(wxString const& p_name)
{
	load();
	m_data->name = p_name;
	return;
}

void Commodity::set_description(wxString const& p_description)
{
	load();
	m_data->description = p_description;
	return;
}

void Commodity::set_precision(int p_precision)
{
	load();
	m_data->precision = p_precision;
	return;
}

void Commodity::set_multiplier_to_base
(	jewel::Decimal const& p_multiplier_to_base
)
{
	load();
	m_data->multiplier_to_base = p_multiplier_to_base;
	return;
}

std::string Commodity::exclusive_table_name()
{
	return "commodities";
}

std::string Commodity::primary_key_name()
{
	return "commodity_id";
}

}  // namespace phatbooks
<commit_msg>In Commodity, eliminated call to sqloxx::SQLStatement::bind<unsigned char>(...), since unsigned char is no longer supported by this function.<commit_after>/*
 * This file is part of the Phatbooks project and is distributed under the
 * terms of the license contained in the file LICENSE.txt distributed
 * with this package.
 *
 * Author: Matthew Harvey <matthew@matthewharvey.net>
 *
 * Copyright (c) 2012-2013, Matthew Harvey.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "commodity.hpp"
#include "balance_cache.hpp"
#include "phatbooks_database_connection.hpp"
#include "string_conv.hpp"
#include "string_conv.hpp"
#include <sqloxx/database_transaction.hpp>
#include <sqloxx/general_typedefs.hpp>
#include <sqloxx/identity_map.hpp>
#include <sqloxx/persistent_object.hpp>
#include <sqloxx/sqloxx_exceptions.hpp>
#include <sqloxx/sql_statement.hpp>
#include <jewel/assert.hpp>
#include <jewel/log.hpp>
#include <jewel/decimal.hpp>
#include <jewel/optional.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/optional.hpp>
#include <wx/string.h>
#include <algorithm>
#include <iostream>  // for debug logging
#include <stdexcept>
#include <string>

using boost::numeric_cast;
using boost::optional;
using jewel::clear;
using jewel::Decimal;
using jewel::value;
using sqloxx::DatabaseTransaction;
using sqloxx::Id;
using sqloxx::SQLStatement;
using std::endl;  // for debug logging
using std::exception;
using std::string;

namespace phatbooks
{

struct Commodity::CommodityData
{
	optional<wxString> abbreviation;
	optional<wxString> name;
	optional<wxString> description;
	optional<int> precision;
	optional<jewel::Decimal> multiplier_to_base;
};

void Commodity::setup_tables
(	PhatbooksDatabaseConnection& dbc
)
{
	// Create the table
	SQLStatement statement
	(	dbc,
		"create table commodities"
		"("
			"commodity_id integer primary key autoincrement, "
			"abbreviation text not null unique, "
			"name text not null unique, "
			"description text, "
			"precision integer default 2 not null, "
			"multiplier_to_base_intval integer not null, "
			"multiplier_to_base_places integer not null"
		")"
	);
	statement.step_final();
	return;
}

Id
Commodity::id_for_abbreviation
(	PhatbooksDatabaseConnection& dbc,
	wxString const& p_abbreviation
)
{
	SQLStatement statement
	(	dbc,
		"select commodity_id from commodities where abbreviation = :p"
	);
	statement.bind(":p", wx_to_std8(p_abbreviation));
	statement.step();
	Id const ret = statement.extract<Id>(0);
	statement.step_final();
	return ret;
}

Commodity::Commodity(Commodity const& rhs):
	PersistentObject(rhs),
	m_data(new CommodityData(*(rhs.m_data)))
{
}

Commodity::Commodity
(	IdentityMap& p_identity_map,
	IdentityMap::Signature const& p_signature
):
	PersistentObject(p_identity_map),
	m_data(new CommodityData)
{
	(void)p_signature;  // silence compiler re. unused parameter
}

Commodity::Commodity
(	IdentityMap& p_identity_map,
	Id p_id,
	IdentityMap::Signature const& p_signature
):
	PersistentObject(p_identity_map, p_id),
	m_data(new CommodityData)
{
	(void)p_signature;  // silence compiler. re. unused parameter
}

Commodity::~Commodity() = default;

bool
Commodity::exists_with_abbreviation
(	PhatbooksDatabaseConnection& p_database_connection,
	wxString const& p_abbreviation
)
{
	SQLStatement statement
	(	p_database_connection,
		"select abbreviation from commodities where abbreviation = :p"
	);
	statement.bind(":p", wx_to_std8(p_abbreviation));
	return statement.step();
}

bool
Commodity::exists_with_name
(	PhatbooksDatabaseConnection& p_database_connection,
	wxString const& p_name
)
{
	SQLStatement statement
	(	p_database_connection,
		"select name from commodities where name = :p"
	);
	statement.bind(":p", wx_to_std8(p_name));
	return statement.step();
}

void
Commodity::swap(Commodity& rhs)
{
	swap_base_internals(rhs);
	using std::swap;
	swap(m_data, rhs.m_data);
	return;
}

void Commodity::do_load()
{
	Commodity temp(*this);
	SQLStatement statement
	(	database_connection(),
		"select abbreviation, name, description, precision, "
		"multiplier_to_base_intval, multiplier_to_base_places from "
		"commodities where commodity_id = :p"
	);
	statement.bind(":p", id());
	statement.step();
	temp.m_data->abbreviation = std8_to_wx(statement.extract<string>(0));
	temp.m_data->name = std8_to_wx(statement.extract<string>(1));
	temp.m_data->description = std8_to_wx(statement.extract<string>(2));
	temp.m_data->precision = statement.extract<int>(3);
	temp.m_data->multiplier_to_base = Decimal
	(	statement.extract<Decimal::int_type>(4),
		numeric_cast<Decimal::places_type>(statement.extract<int>(5))
	);
	swap(temp);
	return;
}

void Commodity::process_saving_statement(SQLStatement& statement)
{
	statement.bind
	(	":abbreviation",
		wx_to_std8(value(m_data->abbreviation))
	);
	statement.bind(":name", wx_to_std8(value(m_data->name)));
	statement.bind
	(	":description",
		wx_to_std8(value(m_data->description))
	);
	statement.bind(":precision", value(m_data->precision));
	Decimal m = value(m_data->multiplier_to_base);
	statement.bind(":multiplier_to_base_intval", m.intval());
	statement.bind
	(	":multiplier_to_base_places",
		numeric_cast<int>(m.places())
	);
	statement.step_final();
	return;
}

void Commodity::do_save_existing()
{
	PhatbooksDatabaseConnection::BalanceCacheAttorney::mark_as_stale
	(	database_connection()
	);
	SQLStatement updater
	(	database_connection(),
		"update commodities set "
		"abbreviation = :abbreviation, "
		"name = :name, "
		"description = :description, "
		"precision = :precision, "
		"multiplier_to_base_intval = :multiplier_to_base_intval, "
		"multiplier_to_base_places = :multiplier_to_base_places "
		"where commodity_id = :commodity_id"
	);
	updater.bind(":commodity_id", id());
	process_saving_statement(updater);
	return;
}

void Commodity::do_save_new()
{
	PhatbooksDatabaseConnection::BalanceCacheAttorney::mark_as_stale
	(	database_connection()
	);
	SQLStatement inserter
	(	database_connection(),
		"insert into commodities(abbreviation, name, description, precision, "
		"multiplier_to_base_intval, multiplier_to_base_places) "
		"values(:abbreviation, :name, :description, :precision, "
		":multiplier_to_base_intval, :multiplier_to_base_places)"
	);
	process_saving_statement(inserter);
	return;
}

void Commodity::do_ghostify()
{
	clear(m_data->abbreviation);
	clear(m_data->name);
	clear(m_data->description);
	clear(m_data->precision);
	clear(m_data->multiplier_to_base);
	return;
}

wxString Commodity::abbreviation()
{
	load();
	return value(m_data->abbreviation);
}

wxString Commodity::name()
{
	load();
	return value(m_data->name);
}

wxString Commodity::description()
{
	load();
	return value(m_data->description);
}

int Commodity::precision()
{
	load();
	return value(m_data->precision);
}

jewel::Decimal Commodity::multiplier_to_base()
{
	load();
	return value(m_data->multiplier_to_base);
}

void Commodity::set_abbreviation(wxString const& p_abbreviation)
{
	load();
	m_data->abbreviation = p_abbreviation;
	return;
}

void Commodity::set_name(wxString const& p_name)
{
	load();
	m_data->name = p_name;
	return;
}

void Commodity::set_description(wxString const& p_description)
{
	load();
	m_data->description = p_description;
	return;
}

void Commodity::set_precision(int p_precision)
{
	load();
	m_data->precision = p_precision;
	return;
}

void Commodity::set_multiplier_to_base
(	jewel::Decimal const& p_multiplier_to_base
)
{
	load();
	m_data->multiplier_to_base = p_multiplier_to_base;
	return;
}

std::string Commodity::exclusive_table_name()
{
	return "commodities";
}

std::string Commodity::primary_key_name()
{
	return "commodity_id";
}

}  // namespace phatbooks
<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
#include <windows.h>
#endif
#include <string>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include "util/exception.h"
#include "util/sstream.h"
#include "util/name.h"
#include "util/optional.h"
#include "util/realpath.h"
#include "util/lean_path.h"

#ifndef LEAN_DEFAULT_MODULE_FILE_NAME
#define LEAN_DEFAULT_MODULE_FILE_NAME "default"
#endif

namespace lean {
file_not_found_exception::file_not_found_exception(std::string const & fname):
    exception(sstream() << "file '" << fname << "' not found in the LEAN_PATH"),
    m_fname(fname) {}

static std::string * g_default_file_name             = nullptr;
static std::string *              g_lean_path        = nullptr;
static std::vector<std::string> * g_lean_path_vector = nullptr;

bool is_directory(char const * pathname) {
    struct stat info;
    if (stat(pathname, &info) != 0 )
        return false; // failed to access pathname
    return info.st_mode & S_IFDIR;
}

#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
// Windows version
static char g_path_sep     = ';';
static char g_path_alt_sep = ':';
static char g_sep          = '\\';
static char g_bad_sep      = '/';
static std::string get_exe_location() {
    HMODULE hModule = GetModuleHandleW(NULL);
    WCHAR path[MAX_PATH];
    GetModuleFileNameW(hModule, path, MAX_PATH);
    std::wstring pathstr(path);
    return std::string(pathstr.begin(), pathstr.end());
}
bool is_path_sep(char c) { return c == g_path_sep || c == g_path_alt_sep; }
#elif defined(__APPLE__)
// OSX version
#include <mach-o/dyld.h>
#include <limits.h>
#include <stdlib.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char buf1[PATH_MAX];
    char buf2[PATH_MAX];
    uint32_t bufsize = PATH_MAX;
    if (_NSGetExecutablePath(buf1, &bufsize) != 0)
        throw exception("failed to locate Lean executable location");
    if (!realpath(buf1, buf2))
        throw exception("failed to resolve symbolic links in " + std::string(buf1));
    return std::string(buf2);
}
bool is_path_sep(char c) { return c == g_path_sep; }
#else
// Linux version
#include <unistd.h>
#include <string.h>
#include <limits.h> // NOLINT
#include <stdio.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char path[PATH_MAX];
    char dest[PATH_MAX];
    memset(dest, 0, PATH_MAX);
    pid_t pid = getpid();
    snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
    if (readlink(path, dest, PATH_MAX) == -1) {
        throw exception("failed to locate Lean executable location");
    } else {
        return std::string(dest);
    }
}
bool is_path_sep(char c) { return c == g_path_sep; }
#endif

std::string normalize_path(std::string f) {
    for (auto & c : f) {
        if (c == g_bad_sep)
            c = g_sep;
    }
    return f;
}

std::string get_path(std::string f) {
    while (true) {
        if (f.empty())
            throw exception("failed to locate Lean executable location");
        if (f.back() == g_sep) {
            f.pop_back();
            return f;
        }
        f.pop_back();
    }
}

void init_lean_path(bool use_hott) {
#if defined(LEAN_EMSCRIPTEN)
    *g_lean_path = "/library";
    g_lean_path_vector->push_back(*g_lean_path);
#else
    char * r = nullptr;
    if (use_hott)
        r = getenv("HLEAN_PATH");
    else
        r = getenv("LEAN_PATH");
    if (r == nullptr) {
        std::string exe_path = get_path(get_exe_location());
        if (use_hott)
            *g_lean_path  = exe_path + g_sep + ".." + g_sep + "hott";
        else
            *g_lean_path  = exe_path + g_sep + ".." + g_sep + "library";
        *g_lean_path += g_path_sep;
        *g_lean_path += exe_path + g_sep + ".." + g_sep + "lib" + g_sep + "lean";
        *g_lean_path += g_path_sep;
        *g_lean_path += ".";
    } else {
        *g_lean_path = r;
    }
    g_lean_path_vector->clear();
    *g_lean_path = normalize_path(*g_lean_path);
    unsigned i  = 0;
    unsigned j  = 0;
    unsigned sz = g_lean_path->size();
    for (; j < sz; j++) {
        if (is_path_sep((*g_lean_path)[j])) {
            if (j > i)
                g_lean_path_vector->push_back(g_lean_path->substr(i, j - i));
            i = j + 1;
        }
    }
    if (j > i)
        g_lean_path_vector->push_back(g_lean_path->substr(i, j - i));
#endif
}

static char g_sep_str[2];

void initialize_lean_path(bool use_hott) {
    if (g_default_file_name != nullptr)
        finalize_lean_path();
    g_default_file_name = new std::string(LEAN_DEFAULT_MODULE_FILE_NAME);
    g_lean_path         = new std::string();
    g_lean_path_vector  = new std::vector<std::string>();
    g_sep_str[0]        = g_sep;
    g_sep_str[1]        = 0;
    init_lean_path(use_hott);
}

void finalize_lean_path() {
    delete g_lean_path_vector;
    delete g_lean_path;
    delete g_default_file_name;
}

bool has_file_ext(std::string const & fname, char const * ext) {
    unsigned ext_len = strlen(ext);
    return fname.size() > ext_len && fname.substr(fname.size() - ext_len, ext_len) == ext;
}

bool is_hlean_file(std::string const & fname) {
    return has_file_ext(fname, ".hlean");
}

bool is_lean_file(std::string const & fname) {
    return has_file_ext(fname, ".lean");
}

bool is_olean_file(std::string const & fname) {
    return has_file_ext(fname, ".olean");
}

bool is_lua_file(std::string const & fname) {
    return has_file_ext(fname, ".lua");
}

bool is_known_file_ext(std::string const & fname) {
    return is_lean_file(fname) || is_hlean_file(fname) || is_olean_file(fname) || is_lua_file(fname);
}

optional<std::string> check_file_core(std::string file, char const * ext) {
    if (ext)
        file += ext;
    std::ifstream ifile(file);
    if (ifile)
        return optional<std::string>(lrealpath(file.c_str()));
    else
        return optional<std::string>();
}

optional<std::string> check_file(std::string const & path, std::string const & fname, char const * ext = nullptr) {
    std::string file = path + g_sep + fname;
    if (is_directory(file.c_str())) {
        std::string default_file = file + g_sep + *g_default_file_name;
        if (auto r1 = check_file_core(default_file, ext)) {
            if (auto r2 = check_file_core(file, ext))
                throw exception(sstream() << "ambiguous import, it can be '" << *r1 << "' or '" << *r2 << "'");
            return r1;
        }
    }
    return check_file_core(file, ext);
}

std::string name_to_file(name const & fname) {
    return fname.to_string(g_sep_str);
}

std::string find_file(std::string fname, std::initializer_list<char const *> const & extensions) {
    bool is_known = is_known_file_ext(fname);
    fname = normalize_path(fname);
    for (auto path : *g_lean_path_vector) {
        if (is_known) {
            if (auto r = check_file(path, fname))
                return *r;
        } else {
            for (auto ext : extensions) {
                if (auto r = check_file(path, fname, ext))
                    return *r;
            }
        }
    }
    throw file_not_found_exception(fname);
}

std::string find_file(std::string const & base, optional<unsigned> const & rel, name const & fname,
                      std::initializer_list<char const *> const & extensions) {
    if (!rel) {
        return find_file(fname.to_string(g_sep_str), extensions);
    } else {
        auto path = base;
        for (unsigned i = 0; i < *rel; i++) {
            path += g_sep;
            path += "..";
        }
        for (auto ext : extensions) {
            if (auto r = check_file(path, fname.to_string(g_sep_str), ext))
                return *r;
        }
        throw file_not_found_exception(fname.to_string());
    }
}

std::string find_file(std::string const & base, optional<unsigned> const & k, name const & fname, char const * ext) {
    return find_file(base, k, fname, {ext});
}

std::string find_file(std::string fname) {
    return find_file(fname, {".olean", ".lean", ".lua"});
}

std::string find_file(name const & fname) {
    return find_file(fname.to_string(g_sep_str));
}

std::string find_file(name const & fname, std::initializer_list<char const *> const & exts) {
    return find_file(fname.to_string(g_sep_str), exts);
}

char const * get_lean_path() {
    return g_lean_path->c_str();
}

void display_path(std::ostream & out, std::string const & fname) {
    out << fname;
}

std::string dirname(char const * fname) {
    if (fname == nullptr)
        return ".";
    std::string nfname = normalize_path(std::string(fname));
    fname = nfname.c_str();
    unsigned i        = 0;
    unsigned last_sep = 0;
    bool found_sep    = false;
    char const * it   = fname;
    while (*it) {
        if (*it == g_sep) {
            found_sep = true;
            last_sep  = i;
        }
        ++i;
        ++it;
    }
    if (!found_sep) {
        return ".";
    } else {
        std::string r;
        for (unsigned i = 0; i < last_sep; i++)
            r.push_back(fname[i]);
        return r;
    }
}

std::string path_append(char const * p1, char const * p2) {
    std::string r(p1);
    r += g_sep;
    r += p2;
    return r;
}
}
<commit_msg>fix(util/lean_path): relative position of library files in binary distribution packages<commit_after>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
#include <windows.h>
#endif
#include <string>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include "util/exception.h"
#include "util/sstream.h"
#include "util/name.h"
#include "util/optional.h"
#include "util/realpath.h"
#include "util/lean_path.h"

#ifndef LEAN_DEFAULT_MODULE_FILE_NAME
#define LEAN_DEFAULT_MODULE_FILE_NAME "default"
#endif

namespace lean {
file_not_found_exception::file_not_found_exception(std::string const & fname):
    exception(sstream() << "file '" << fname << "' not found in the LEAN_PATH"),
    m_fname(fname) {}

static std::string * g_default_file_name             = nullptr;
static std::string *              g_lean_path        = nullptr;
static std::vector<std::string> * g_lean_path_vector = nullptr;

bool is_directory(char const * pathname) {
    struct stat info;
    if (stat(pathname, &info) != 0 )
        return false; // failed to access pathname
    return info.st_mode & S_IFDIR;
}

#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
// Windows version
static char g_path_sep     = ';';
static char g_path_alt_sep = ':';
static char g_sep          = '\\';
static char g_bad_sep      = '/';
static std::string get_exe_location() {
    HMODULE hModule = GetModuleHandleW(NULL);
    WCHAR path[MAX_PATH];
    GetModuleFileNameW(hModule, path, MAX_PATH);
    std::wstring pathstr(path);
    return std::string(pathstr.begin(), pathstr.end());
}
bool is_path_sep(char c) { return c == g_path_sep || c == g_path_alt_sep; }
#elif defined(__APPLE__)
// OSX version
#include <mach-o/dyld.h>
#include <limits.h>
#include <stdlib.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char buf1[PATH_MAX];
    char buf2[PATH_MAX];
    uint32_t bufsize = PATH_MAX;
    if (_NSGetExecutablePath(buf1, &bufsize) != 0)
        throw exception("failed to locate Lean executable location");
    if (!realpath(buf1, buf2))
        throw exception("failed to resolve symbolic links in " + std::string(buf1));
    return std::string(buf2);
}
bool is_path_sep(char c) { return c == g_path_sep; }
#else
// Linux version
#include <unistd.h>
#include <string.h>
#include <limits.h> // NOLINT
#include <stdio.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char path[PATH_MAX];
    char dest[PATH_MAX];
    memset(dest, 0, PATH_MAX);
    pid_t pid = getpid();
    snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
    if (readlink(path, dest, PATH_MAX) == -1) {
        throw exception("failed to locate Lean executable location");
    } else {
        return std::string(dest);
    }
}
bool is_path_sep(char c) { return c == g_path_sep; }
#endif

std::string normalize_path(std::string f) {
    for (auto & c : f) {
        if (c == g_bad_sep)
            c = g_sep;
    }
    return f;
}

std::string get_path(std::string f) {
    while (true) {
        if (f.empty())
            throw exception("failed to locate Lean executable location");
        if (f.back() == g_sep) {
            f.pop_back();
            return f;
        }
        f.pop_back();
    }
}

void init_lean_path(bool use_hott) {
#if defined(LEAN_EMSCRIPTEN)
    *g_lean_path = "/library";
    g_lean_path_vector->push_back(*g_lean_path);
#else
    char * r = nullptr;
    if (use_hott)
        r = getenv("HLEAN_PATH");
    else
        r = getenv("LEAN_PATH");
    if (r == nullptr) {
        std::string exe_path = get_path(get_exe_location());
        if (use_hott)
            *g_lean_path  = exe_path + g_sep + ".." + g_sep + "hott";
        else
            *g_lean_path  = exe_path + g_sep + ".." + g_sep + "library";
        *g_lean_path += g_path_sep;
        if (use_hott)
            *g_lean_path += exe_path + g_sep + ".." + g_sep + "lib" + g_sep + "lean" + g_sep + "hott";
        else
            *g_lean_path += exe_path + g_sep + ".." + g_sep + "lib" + g_sep + "lean" + g_sep + "library";
        *g_lean_path += g_path_sep;
        *g_lean_path += ".";
    } else {
        *g_lean_path = r;
    }
    g_lean_path_vector->clear();
    *g_lean_path = normalize_path(*g_lean_path);
    unsigned i  = 0;
    unsigned j  = 0;
    unsigned sz = g_lean_path->size();
    for (; j < sz; j++) {
        if (is_path_sep((*g_lean_path)[j])) {
            if (j > i)
                g_lean_path_vector->push_back(g_lean_path->substr(i, j - i));
            i = j + 1;
        }
    }
    if (j > i)
        g_lean_path_vector->push_back(g_lean_path->substr(i, j - i));
#endif
}

static char g_sep_str[2];

void initialize_lean_path(bool use_hott) {
    if (g_default_file_name != nullptr)
        finalize_lean_path();
    g_default_file_name = new std::string(LEAN_DEFAULT_MODULE_FILE_NAME);
    g_lean_path         = new std::string();
    g_lean_path_vector  = new std::vector<std::string>();
    g_sep_str[0]        = g_sep;
    g_sep_str[1]        = 0;
    init_lean_path(use_hott);
}

void finalize_lean_path() {
    delete g_lean_path_vector;
    delete g_lean_path;
    delete g_default_file_name;
}

bool has_file_ext(std::string const & fname, char const * ext) {
    unsigned ext_len = strlen(ext);
    return fname.size() > ext_len && fname.substr(fname.size() - ext_len, ext_len) == ext;
}

bool is_hlean_file(std::string const & fname) {
    return has_file_ext(fname, ".hlean");
}

bool is_lean_file(std::string const & fname) {
    return has_file_ext(fname, ".lean");
}

bool is_olean_file(std::string const & fname) {
    return has_file_ext(fname, ".olean");
}

bool is_lua_file(std::string const & fname) {
    return has_file_ext(fname, ".lua");
}

bool is_known_file_ext(std::string const & fname) {
    return is_lean_file(fname) || is_hlean_file(fname) || is_olean_file(fname) || is_lua_file(fname);
}

optional<std::string> check_file_core(std::string file, char const * ext) {
    if (ext)
        file += ext;
    std::ifstream ifile(file);
    if (ifile)
        return optional<std::string>(lrealpath(file.c_str()));
    else
        return optional<std::string>();
}

optional<std::string> check_file(std::string const & path, std::string const & fname, char const * ext = nullptr) {
    std::string file = path + g_sep + fname;
    if (is_directory(file.c_str())) {
        std::string default_file = file + g_sep + *g_default_file_name;
        if (auto r1 = check_file_core(default_file, ext)) {
            if (auto r2 = check_file_core(file, ext))
                throw exception(sstream() << "ambiguous import, it can be '" << *r1 << "' or '" << *r2 << "'");
            return r1;
        }
    }
    return check_file_core(file, ext);
}

std::string name_to_file(name const & fname) {
    return fname.to_string(g_sep_str);
}

std::string find_file(std::string fname, std::initializer_list<char const *> const & extensions) {
    bool is_known = is_known_file_ext(fname);
    fname = normalize_path(fname);
    for (auto path : *g_lean_path_vector) {
        if (is_known) {
            if (auto r = check_file(path, fname))
                return *r;
        } else {
            for (auto ext : extensions) {
                if (auto r = check_file(path, fname, ext))
                    return *r;
            }
        }
    }
    throw file_not_found_exception(fname);
}

std::string find_file(std::string const & base, optional<unsigned> const & rel, name const & fname,
                      std::initializer_list<char const *> const & extensions) {
    if (!rel) {
        return find_file(fname.to_string(g_sep_str), extensions);
    } else {
        auto path = base;
        for (unsigned i = 0; i < *rel; i++) {
            path += g_sep;
            path += "..";
        }
        for (auto ext : extensions) {
            if (auto r = check_file(path, fname.to_string(g_sep_str), ext))
                return *r;
        }
        throw file_not_found_exception(fname.to_string());
    }
}

std::string find_file(std::string const & base, optional<unsigned> const & k, name const & fname, char const * ext) {
    return find_file(base, k, fname, {ext});
}

std::string find_file(std::string fname) {
    return find_file(fname, {".olean", ".lean", ".lua"});
}

std::string find_file(name const & fname) {
    return find_file(fname.to_string(g_sep_str));
}

std::string find_file(name const & fname, std::initializer_list<char const *> const & exts) {
    return find_file(fname.to_string(g_sep_str), exts);
}

char const * get_lean_path() {
    return g_lean_path->c_str();
}

void display_path(std::ostream & out, std::string const & fname) {
    out << fname;
}

std::string dirname(char const * fname) {
    if (fname == nullptr)
        return ".";
    std::string nfname = normalize_path(std::string(fname));
    fname = nfname.c_str();
    unsigned i        = 0;
    unsigned last_sep = 0;
    bool found_sep    = false;
    char const * it   = fname;
    while (*it) {
        if (*it == g_sep) {
            found_sep = true;
            last_sep  = i;
        }
        ++i;
        ++it;
    }
    if (!found_sep) {
        return ".";
    } else {
        std::string r;
        for (unsigned i = 0; i < last_sep; i++)
            r.push_back(fname[i]);
        return r;
    }
}

std::string path_append(char const * p1, char const * p2) {
    std::string r(p1);
    r += g_sep;
    r += p2;
    return r;
}
}
<|endoftext|>