<commit_before>/**
 * @file   AccelerationStructureGeometry.cpp
 * @author Sebastian Maisch <sebastian.maisch@googlemail.com>
 * @date   2020.11.20
 *
 * @brief  Implementation of the acceleration structure geometry class.
 */

#include "gfx/vk//rt/AccelerationStructureGeometry.h"
#include "gfx/vk/buffers/DeviceBuffer.h"
#include "gfx/vk/buffers/HostBuffer.h"
#include "gfx/vk/memory/DeviceMemory.h"
#include "gfx/vk/CommandBuffers.h"
#include "gfx/vk/LogicalDevice.h"

namespace vkfw_core::gfx::rt {

    AccelerationStructureGeometry::AccelerationStructureGeometry(vkfw_core::gfx::LogicalDevice* device)
        : m_device{device}
    {
        auto features = m_device->GetPhysicalDevice()
                            .getFeatures2<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceRayTracingFeaturesKHR>();
        m_raytracingFeatures = features.get<vk::PhysicalDeviceRayTracingFeaturesKHR>();
    }

    AccelerationStructureGeometry::~AccelerationStructureGeometry() {}

    void AccelerationStructureGeometry::InitializeAccelerationStructure()
    {
        CreateBottomLevelAccelerationStructure();
        CreateTopLevelAccelerationStructure();
    }

    void AccelerationStructureGeometry::FillDescriptorLayoutBinding(vk::DescriptorSetLayoutBinding& asLayoutBinding,
                                                                    const vk::ShaderStageFlags& shaderFlags,
                                                                    std::uint32_t binding /*= 0*/) const
    {
        asLayoutBinding.setBinding(binding);
        asLayoutBinding.setDescriptorType(vk::DescriptorType::eAccelerationStructureKHR);
        asLayoutBinding.setDescriptorCount(1);
        asLayoutBinding.setStageFlags(shaderFlags);
    }

    void AccelerationStructureGeometry::CreateLayout(vk::DescriptorPool descPool,
                                                     const vk::ShaderStageFlags& shaderFlags,
                                                     std::uint32_t binding /*= 0*/)
    {
        m_descBinding = binding;
        vk::DescriptorSetLayoutBinding asLayoutBinding;
        FillDescriptorLayoutBinding(asLayoutBinding, shaderFlags, binding);

        vk::DescriptorSetLayoutCreateInfo asLayoutCreateInfo{vk::DescriptorSetLayoutCreateFlags(), 1, &asLayoutBinding};
        m_internalDescLayout = m_device->GetDevice().createDescriptorSetLayoutUnique(asLayoutCreateInfo);
        m_descLayout = *m_internalDescLayout;
        AllocateDescriptorSet(descPool);
    }

    void AccelerationStructureGeometry::UseLayout(vk::DescriptorPool descPool, vk::DescriptorSetLayout usedLayout,
                                                  std::uint32_t binding /*= 0*/)
    {
        m_descBinding = binding;
        m_descLayout = usedLayout;
        AllocateDescriptorSet(descPool);
    }

    void AccelerationStructureGeometry::UseDescriptorSet(vk::DescriptorSet descSet, vk::DescriptorSetLayout usedLayout,
                                                         std::uint32_t binding /*= 0*/)
    {
        m_descBinding = binding;
        m_descLayout = usedLayout;
        m_descSet = descSet;
    }

    void AccelerationStructureGeometry::FillDescriptorSetWrite(vk::WriteDescriptorSet& descWrite) const
    {
        descWrite.dstSet = m_descSet;
        descWrite.dstBinding = m_descBinding;
        descWrite.dstArrayElement = 0;
        descWrite.descriptorCount = 1;
        descWrite.descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
        descWrite.setPNext(&m_descriptorSetAccStructure);
    }

    void AccelerationStructureGeometry::AddTriangleGeometry(
        std::size_t primitiveCount, std::size_t vertexCount, std::size_t vertexSize,
        vk::DeviceOrHostAddressConstKHR vertexBufferDeviceAddress,
        vk::DeviceOrHostAddressConstKHR indexBufferDeviceAddress,
        vk::DeviceOrHostAddressConstKHR transformDeviceAddress /*= nullptr*/)
    {
        vk::Bool32 hasTransform = (transformDeviceAddress.hostAddress == nullptr) ? VK_FALSE : VK_TRUE;
        m_accelerationStructureCreateGeometryTypeInfo.emplace_back(
            vk::GeometryTypeKHR::eTriangles, static_cast<std::uint32_t>(primitiveCount), vk::IndexType::eUint32,
            static_cast<std::uint32_t>(vertexCount), vk::Format::eR32G32B32Sfloat, hasTransform);

        vk::AccelerationStructureGeometryTrianglesDataKHR asGeometryDataTriangles{
            vk::Format::eR32G32B32Sfloat, vertexBufferDeviceAddress, vertexSize, vk::IndexType::eUint32,
            indexBufferDeviceAddress, transformDeviceAddress};
        vk::AccelerationStructureGeometryDataKHR asGeometryData{asGeometryDataTriangles};

        m_accelerationStructureGeometries.emplace_back(vk::GeometryTypeKHR::eTriangles, asGeometryData,
                                                       vk::GeometryFlagBitsKHR::eOpaque);
        m_accelerationStructureBuildOffsets.emplace_back(static_cast<std::uint32_t>(primitiveCount), 0x0, 0, 0x0);
    }

    void AccelerationStructureGeometry::CreateBottomLevelAccelerationStructure()
    {
        vk::AccelerationStructureCreateInfoKHR blasCreateInfo{
            0, vk::AccelerationStructureTypeKHR::eBottomLevel,
            vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
            static_cast<std::uint32_t>(m_accelerationStructureCreateGeometryTypeInfo.size()),
            m_accelerationStructureCreateGeometryTypeInfo.data()};
        m_BLAS = CreateAS(blasCreateInfo);

        vk::AccelerationStructureGeometryKHR* asGeometriesPtr = m_accelerationStructureGeometries.data();
        auto scratchBuffer = CreateASScratchBuffer(m_BLAS);

        vk::AccelerationStructureBuildGeometryInfoKHR asBuildGeometryInfo{
            vk::AccelerationStructureTypeKHR::eBottomLevel,
            vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
            VK_FALSE,
            {},
            m_BLAS.as.get(),
            VK_FALSE,
            static_cast<std::uint32_t>(m_accelerationStructureGeometries.size()),
            &asGeometriesPtr,
            scratchBuffer->GetDeviceAddress()};

        // the parameter in buildAccelerationStructureKHR wants a pointer to n arrays of vk::AccelerationStructureBuildOffsetInfoKHR,
        // where n is the number of vk::AccelerationStructureBuildGeometryInfoKHR passed.
        auto accelerationStructureBuildOffsetsPtr = m_accelerationStructureBuildOffsets.data();

        if (m_raytracingFeatures.rayTracingHostAccelerationStructureCommands) {
            if (m_device->GetDevice().buildAccelerationStructureKHR(
                    asBuildGeometryInfo, vk::ArrayProxy<const vk::AccelerationStructureBuildOffsetInfoKHR* const>(
                                             1, &accelerationStructureBuildOffsetsPtr))
                != vk::Result::eSuccess) {
                spdlog::error("Could not build acceleration structure.");
                assert(false);
            }
        } else {
            auto vkAccStructureCmdBuffer = vkfw_core::gfx::CommandBuffers::beginSingleTimeSubmit(m_device, 0);
            vkAccStructureCmdBuffer->buildAccelerationStructureKHR(
                asBuildGeometryInfo, vk::ArrayProxy<const vk::AccelerationStructureBuildOffsetInfoKHR* const>(
                                         1, &accelerationStructureBuildOffsetsPtr));
            vkfw_core::gfx::CommandBuffers::endSingleTimeSubmitAndWait(m_device, vkAccStructureCmdBuffer.get(), 0,
                                                                       0);
        }
    }

    void AccelerationStructureGeometry::CreateTopLevelAccelerationStructure()
    {
        vk::AccelerationStructureCreateGeometryTypeInfoKHR tlasCreateGeometryInfo{
            vk::GeometryTypeKHR::eInstances, 1, vk::IndexType::eUint32, 0, vk::Format::eUndefined, VK_FALSE};
        vk::AccelerationStructureCreateInfoKHR tlasCreateInfo{
            0, vk::AccelerationStructureTypeKHR::eTopLevel, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
            1, &tlasCreateGeometryInfo};

        m_TLAS = CreateAS(tlasCreateInfo);

        vk::TransformMatrixKHR transform_matrix{std::array<std::array<float, 4>, 3>{
            std::array<float, 4>{1.0f, 0.0f, 0.0f, 0.0f}, std::array<float, 4>{0.0f, 1.0f, 0.0f, 0.0f},
            std::array<float, 4>{0.0f, 0.0f, 1.0f, 0.0f}}};

        vk::AccelerationStructureInstanceKHR tlasInstance{
            transform_matrix, 0, 0xFF, 0, vk::GeometryInstanceFlagBitsKHR::eTriangleFacingCullDisable, m_BLAS.handle};

        vkfw_core::gfx::HostBuffer instancesBuffer{m_device, vk::BufferUsageFlagBits::eShaderDeviceAddress};
        instancesBuffer.InitializeData(sizeof(vk::AccelerationStructureInstanceKHR),
                                       static_cast<const void*>(&tlasInstance));

        vk::AccelerationStructureGeometryInstancesDataKHR asGeometryDataInstances{
            VK_FALSE, instancesBuffer.GetDeviceAddressConst()};
        vk::AccelerationStructureGeometryDataKHR asGeometryData{asGeometryDataInstances};

        std::vector<vk::AccelerationStructureGeometryKHR> asGeometries;
        asGeometries.emplace_back(vk::GeometryTypeKHR::eInstances, asGeometryData, vk::GeometryFlagBitsKHR::eOpaque);
        vk::AccelerationStructureGeometryKHR* asGeometriesPtr = asGeometries.data();
        auto scratchBuffer = CreateASScratchBuffer(m_TLAS);

        vk::AccelerationStructureBuildGeometryInfoKHR asBuildGeometryInfo{
            vk::AccelerationStructureTypeKHR::eTopLevel,
            vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
            VK_FALSE,
            {},
            m_TLAS.as.get(),
            VK_FALSE,
            1,
            &asGeometriesPtr,
            scratchBuffer->GetDeviceAddress()};

        vk::AccelerationStructureBuildOffsetInfoKHR asBuildOffset{1, 0x0, 0, 0x0};
        std::vector<vk::AccelerationStructureBuildOffsetInfoKHR*> asBuildOffsets{&asBuildOffset};

        if (m_raytracingFeatures.rayTracingHostAccelerationStructureCommands) {
            if (m_device->GetDevice().buildAccelerationStructureKHR(
                    asBuildGeometryInfo,
                    vk::ArrayProxy<const vk::AccelerationStructureBuildOffsetInfoKHR* const>(1, asBuildOffsets.data()))
                != vk::Result::eSuccess) {
                spdlog::error("Could not build acceleration structure.");
                assert(false);
            }
        } else {
            auto vkAccStructureCmdBuffer = vkfw_core::gfx::CommandBuffers::beginSingleTimeSubmit(m_device, 0);
            vkAccStructureCmdBuffer->buildAccelerationStructureKHR(
                asBuildGeometryInfo,
                vk::ArrayProxy<const vk::AccelerationStructureBuildOffsetInfoKHR* const>(1, asBuildOffsets.data()));
            vkfw_core::gfx::CommandBuffers::endSingleTimeSubmitAndWait(m_device, vkAccStructureCmdBuffer.get(), 0,
                                                                       0);
        }

        m_descriptorSetAccStructure.setAccelerationStructureCount(1);
        m_descriptorSetAccStructure.setPAccelerationStructures(&*m_TLAS.as);
    }

    AccelerationStructureGeometry::AccelerationStructure AccelerationStructureGeometry::CreateAS(const vk::AccelerationStructureCreateInfoKHR& info)
    {
        AccelerationStructure result;

        result.as = m_device->GetDevice().createAccelerationStructureKHRUnique(info);

        vk::AccelerationStructureMemoryRequirementsInfoKHR accStructureMemReqInfo{
            vk::AccelerationStructureMemoryRequirementsTypeKHR::eObject, vk::AccelerationStructureBuildTypeKHR::eDevice,
            result.as.get()};
        auto accStructureMemReq =
            m_device->GetDevice().getAccelerationStructureMemoryRequirementsKHR(accStructureMemReqInfo);

        vk::MemoryAllocateInfo memoryAllocateInfo{accStructureMemReq.memoryRequirements.size,
            vkfw_core::gfx::DeviceMemory::FindMemoryType(m_device, accStructureMemReq.memoryRequirements.memoryTypeBits,
                                                         vk::MemoryPropertyFlagBits::eDeviceLocal)};
        result.memory = m_device->GetDevice().allocateMemoryUnique(memoryAllocateInfo);

        vk::BindAccelerationStructureMemoryInfoKHR accStructureMemoryInfo{result.as.get(), result.memory.get()};
        m_device->GetDevice().bindAccelerationStructureMemoryKHR(accStructureMemoryInfo);

        vk::AccelerationStructureDeviceAddressInfoKHR asDeviceAddressInfo{result.as.get()};
        result.handle = m_device->GetDevice().getAccelerationStructureAddressKHR(asDeviceAddressInfo);

        return result;
    }

    std::unique_ptr<vkfw_core::gfx::DeviceBuffer> AccelerationStructureGeometry::CreateASScratchBuffer(AccelerationStructure& as)
    {
        vk::AccelerationStructureMemoryRequirementsInfoKHR asMemoryReq{
            vk::AccelerationStructureMemoryRequirementsTypeKHR::eBuildScratch,
            vk::AccelerationStructureBuildTypeKHR::eDevice, as.as.get()};
        auto memReq = m_device->GetDevice().getAccelerationStructureMemoryRequirementsKHR(asMemoryReq);

        std::unique_ptr<vkfw_core::gfx::DeviceBuffer> scratchBuffer = std::make_unique<vkfw_core::gfx::DeviceBuffer>(
            m_device, vk::BufferUsageFlagBits::eRayTracingKHR | vk::BufferUsageFlagBits::eShaderDeviceAddress);
        scratchBuffer->InitializeBuffer(memReq.memoryRequirements.size);

        return scratchBuffer;
    }

    void AccelerationStructureGeometry::AllocateDescriptorSet(vk::DescriptorPool descPool)
    {
        vk::DescriptorSetAllocateInfo descSetAllocInfo{descPool, 1, &m_descLayout};
        m_descSet = m_device->GetDevice().allocateDescriptorSets(descSetAllocInfo)[0];
    }

}
<commit_msg>Reformated a matrix in source code.<commit_after>/**
 * @file   AccelerationStructureGeometry.cpp
 * @author Sebastian Maisch <sebastian.maisch@googlemail.com>
 * @date   2020.11.20
 *
 * @brief  Implementation of the acceleration structure geometry class.
 */

#include "gfx/vk//rt/AccelerationStructureGeometry.h"
#include "gfx/vk/buffers/DeviceBuffer.h"
#include "gfx/vk/buffers/HostBuffer.h"
#include "gfx/vk/memory/DeviceMemory.h"
#include "gfx/vk/CommandBuffers.h"
#include "gfx/vk/LogicalDevice.h"

namespace vkfw_core::gfx::rt {

    AccelerationStructureGeometry::AccelerationStructureGeometry(vkfw_core::gfx::LogicalDevice* device)
        : m_device{device}
    {
        auto features = m_device->GetPhysicalDevice()
                            .getFeatures2<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceRayTracingFeaturesKHR>();
        m_raytracingFeatures = features.get<vk::PhysicalDeviceRayTracingFeaturesKHR>();
    }

    AccelerationStructureGeometry::~AccelerationStructureGeometry() {}

    void AccelerationStructureGeometry::InitializeAccelerationStructure()
    {
        CreateBottomLevelAccelerationStructure();
        CreateTopLevelAccelerationStructure();
    }

    void AccelerationStructureGeometry::FillDescriptorLayoutBinding(vk::DescriptorSetLayoutBinding& asLayoutBinding,
                                                                    const vk::ShaderStageFlags& shaderFlags,
                                                                    std::uint32_t binding /*= 0*/) const
    {
        asLayoutBinding.setBinding(binding);
        asLayoutBinding.setDescriptorType(vk::DescriptorType::eAccelerationStructureKHR);
        asLayoutBinding.setDescriptorCount(1);
        asLayoutBinding.setStageFlags(shaderFlags);
    }

    void AccelerationStructureGeometry::CreateLayout(vk::DescriptorPool descPool,
                                                     const vk::ShaderStageFlags& shaderFlags,
                                                     std::uint32_t binding /*= 0*/)
    {
        m_descBinding = binding;
        vk::DescriptorSetLayoutBinding asLayoutBinding;
        FillDescriptorLayoutBinding(asLayoutBinding, shaderFlags, binding);

        vk::DescriptorSetLayoutCreateInfo asLayoutCreateInfo{vk::DescriptorSetLayoutCreateFlags(), 1, &asLayoutBinding};
        m_internalDescLayout = m_device->GetDevice().createDescriptorSetLayoutUnique(asLayoutCreateInfo);
        m_descLayout = *m_internalDescLayout;
        AllocateDescriptorSet(descPool);
    }

    void AccelerationStructureGeometry::UseLayout(vk::DescriptorPool descPool, vk::DescriptorSetLayout usedLayout,
                                                  std::uint32_t binding /*= 0*/)
    {
        m_descBinding = binding;
        m_descLayout = usedLayout;
        AllocateDescriptorSet(descPool);
    }

    void AccelerationStructureGeometry::UseDescriptorSet(vk::DescriptorSet descSet, vk::DescriptorSetLayout usedLayout,
                                                         std::uint32_t binding /*= 0*/)
    {
        m_descBinding = binding;
        m_descLayout = usedLayout;
        m_descSet = descSet;
    }

    void AccelerationStructureGeometry::FillDescriptorSetWrite(vk::WriteDescriptorSet& descWrite) const
    {
        descWrite.dstSet = m_descSet;
        descWrite.dstBinding = m_descBinding;
        descWrite.dstArrayElement = 0;
        descWrite.descriptorCount = 1;
        descWrite.descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
        descWrite.setPNext(&m_descriptorSetAccStructure);
    }

    void AccelerationStructureGeometry::AddTriangleGeometry(
        std::size_t primitiveCount, std::size_t vertexCount, std::size_t vertexSize,
        vk::DeviceOrHostAddressConstKHR vertexBufferDeviceAddress,
        vk::DeviceOrHostAddressConstKHR indexBufferDeviceAddress,
        vk::DeviceOrHostAddressConstKHR transformDeviceAddress /*= nullptr*/)
    {
        vk::Bool32 hasTransform = (transformDeviceAddress.hostAddress == nullptr) ? VK_FALSE : VK_TRUE;
        m_accelerationStructureCreateGeometryTypeInfo.emplace_back(
            vk::GeometryTypeKHR::eTriangles, static_cast<std::uint32_t>(primitiveCount), vk::IndexType::eUint32,
            static_cast<std::uint32_t>(vertexCount), vk::Format::eR32G32B32Sfloat, hasTransform);

        vk::AccelerationStructureGeometryTrianglesDataKHR asGeometryDataTriangles{
            vk::Format::eR32G32B32Sfloat, vertexBufferDeviceAddress, vertexSize, vk::IndexType::eUint32,
            indexBufferDeviceAddress, transformDeviceAddress};
        vk::AccelerationStructureGeometryDataKHR asGeometryData{asGeometryDataTriangles};

        m_accelerationStructureGeometries.emplace_back(vk::GeometryTypeKHR::eTriangles, asGeometryData,
                                                       vk::GeometryFlagBitsKHR::eOpaque);
        m_accelerationStructureBuildOffsets.emplace_back(static_cast<std::uint32_t>(primitiveCount), 0x0, 0, 0x0);
    }

    void AccelerationStructureGeometry::CreateBottomLevelAccelerationStructure()
    {
        vk::AccelerationStructureCreateInfoKHR blasCreateInfo{
            0, vk::AccelerationStructureTypeKHR::eBottomLevel,
            vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
            static_cast<std::uint32_t>(m_accelerationStructureCreateGeometryTypeInfo.size()),
            m_accelerationStructureCreateGeometryTypeInfo.data()};
        m_BLAS = CreateAS(blasCreateInfo);

        vk::AccelerationStructureGeometryKHR* asGeometriesPtr = m_accelerationStructureGeometries.data();
        auto scratchBuffer = CreateASScratchBuffer(m_BLAS);

        vk::AccelerationStructureBuildGeometryInfoKHR asBuildGeometryInfo{
            vk::AccelerationStructureTypeKHR::eBottomLevel,
            vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
            VK_FALSE,
            {},
            m_BLAS.as.get(),
            VK_FALSE,
            static_cast<std::uint32_t>(m_accelerationStructureGeometries.size()),
            &asGeometriesPtr,
            scratchBuffer->GetDeviceAddress()};

        // the parameter in buildAccelerationStructureKHR wants a pointer to n arrays of vk::AccelerationStructureBuildOffsetInfoKHR,
        // where n is the number of vk::AccelerationStructureBuildGeometryInfoKHR passed.
        auto accelerationStructureBuildOffsetsPtr = m_accelerationStructureBuildOffsets.data();

        if (m_raytracingFeatures.rayTracingHostAccelerationStructureCommands) {
            if (m_device->GetDevice().buildAccelerationStructureKHR(
                    asBuildGeometryInfo, vk::ArrayProxy<const vk::AccelerationStructureBuildOffsetInfoKHR* const>(
                                             1, &accelerationStructureBuildOffsetsPtr))
                != vk::Result::eSuccess) {
                spdlog::error("Could not build acceleration structure.");
                assert(false);
            }
        } else {
            auto vkAccStructureCmdBuffer = vkfw_core::gfx::CommandBuffers::beginSingleTimeSubmit(m_device, 0);
            vkAccStructureCmdBuffer->buildAccelerationStructureKHR(
                asBuildGeometryInfo, vk::ArrayProxy<const vk::AccelerationStructureBuildOffsetInfoKHR* const>(
                                         1, &accelerationStructureBuildOffsetsPtr));
            vkfw_core::gfx::CommandBuffers::endSingleTimeSubmitAndWait(m_device, vkAccStructureCmdBuffer.get(), 0,
                                                                       0);
        }
    }

    void AccelerationStructureGeometry::CreateTopLevelAccelerationStructure()
    {
        vk::AccelerationStructureCreateGeometryTypeInfoKHR tlasCreateGeometryInfo{
            vk::GeometryTypeKHR::eInstances, 1, vk::IndexType::eUint32, 0, vk::Format::eUndefined, VK_FALSE};
        vk::AccelerationStructureCreateInfoKHR tlasCreateInfo{
            0, vk::AccelerationStructureTypeKHR::eTopLevel, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
            1, &tlasCreateGeometryInfo};

        m_TLAS = CreateAS(tlasCreateInfo);

        vk::TransformMatrixKHR transform_matrix{std::array<std::array<float, 4>, 3>{
            std::array<float, 4>{1.0f, 0.0f, 0.0f, 0.0f},
            std::array<float, 4>{0.0f, 1.0f, 0.0f, 0.0f},
            std::array<float, 4>{0.0f, 0.0f, 1.0f, 0.0f}}};

        vk::AccelerationStructureInstanceKHR tlasInstance{
            transform_matrix, 0, 0xFF, 0, vk::GeometryInstanceFlagBitsKHR::eTriangleFacingCullDisable, m_BLAS.handle};

        vkfw_core::gfx::HostBuffer instancesBuffer{m_device, vk::BufferUsageFlagBits::eShaderDeviceAddress};
        instancesBuffer.InitializeData(sizeof(vk::AccelerationStructureInstanceKHR),
                                       static_cast<const void*>(&tlasInstance));

        vk::AccelerationStructureGeometryInstancesDataKHR asGeometryDataInstances{
            VK_FALSE, instancesBuffer.GetDeviceAddressConst()};
        vk::AccelerationStructureGeometryDataKHR asGeometryData{asGeometryDataInstances};

        std::vector<vk::AccelerationStructureGeometryKHR> asGeometries;
        asGeometries.emplace_back(vk::GeometryTypeKHR::eInstances, asGeometryData, vk::GeometryFlagBitsKHR::eOpaque);
        vk::AccelerationStructureGeometryKHR* asGeometriesPtr = asGeometries.data();
        auto scratchBuffer = CreateASScratchBuffer(m_TLAS);

        vk::AccelerationStructureBuildGeometryInfoKHR asBuildGeometryInfo{
            vk::AccelerationStructureTypeKHR::eTopLevel,
            vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
            VK_FALSE,
            {},
            m_TLAS.as.get(),
            VK_FALSE,
            1,
            &asGeometriesPtr,
            scratchBuffer->GetDeviceAddress()};

        vk::AccelerationStructureBuildOffsetInfoKHR asBuildOffset{1, 0x0, 0, 0x0};
        std::vector<vk::AccelerationStructureBuildOffsetInfoKHR*> asBuildOffsets{&asBuildOffset};

        if (m_raytracingFeatures.rayTracingHostAccelerationStructureCommands) {
            if (m_device->GetDevice().buildAccelerationStructureKHR(
                    asBuildGeometryInfo,
                    vk::ArrayProxy<const vk::AccelerationStructureBuildOffsetInfoKHR* const>(1, asBuildOffsets.data()))
                != vk::Result::eSuccess) {
                spdlog::error("Could not build acceleration structure.");
                assert(false);
            }
        } else {
            auto vkAccStructureCmdBuffer = vkfw_core::gfx::CommandBuffers::beginSingleTimeSubmit(m_device, 0);
            vkAccStructureCmdBuffer->buildAccelerationStructureKHR(
                asBuildGeometryInfo,
                vk::ArrayProxy<const vk::AccelerationStructureBuildOffsetInfoKHR* const>(1, asBuildOffsets.data()));
            vkfw_core::gfx::CommandBuffers::endSingleTimeSubmitAndWait(m_device, vkAccStructureCmdBuffer.get(), 0,
                                                                       0);
        }

        m_descriptorSetAccStructure.setAccelerationStructureCount(1);
        m_descriptorSetAccStructure.setPAccelerationStructures(&*m_TLAS.as);
    }

    AccelerationStructureGeometry::AccelerationStructure AccelerationStructureGeometry::CreateAS(const vk::AccelerationStructureCreateInfoKHR& info)
    {
        AccelerationStructure result;

        result.as = m_device->GetDevice().createAccelerationStructureKHRUnique(info);

        vk::AccelerationStructureMemoryRequirementsInfoKHR accStructureMemReqInfo{
            vk::AccelerationStructureMemoryRequirementsTypeKHR::eObject, vk::AccelerationStructureBuildTypeKHR::eDevice,
            result.as.get()};
        auto accStructureMemReq =
            m_device->GetDevice().getAccelerationStructureMemoryRequirementsKHR(accStructureMemReqInfo);

        vk::MemoryAllocateInfo memoryAllocateInfo{accStructureMemReq.memoryRequirements.size,
            vkfw_core::gfx::DeviceMemory::FindMemoryType(m_device, accStructureMemReq.memoryRequirements.memoryTypeBits,
                                                         vk::MemoryPropertyFlagBits::eDeviceLocal)};
        result.memory = m_device->GetDevice().allocateMemoryUnique(memoryAllocateInfo);

        vk::BindAccelerationStructureMemoryInfoKHR accStructureMemoryInfo{result.as.get(), result.memory.get()};
        m_device->GetDevice().bindAccelerationStructureMemoryKHR(accStructureMemoryInfo);

        vk::AccelerationStructureDeviceAddressInfoKHR asDeviceAddressInfo{result.as.get()};
        result.handle = m_device->GetDevice().getAccelerationStructureAddressKHR(asDeviceAddressInfo);

        return result;
    }

    std::unique_ptr<vkfw_core::gfx::DeviceBuffer> AccelerationStructureGeometry::CreateASScratchBuffer(AccelerationStructure& as)
    {
        vk::AccelerationStructureMemoryRequirementsInfoKHR asMemoryReq{
            vk::AccelerationStructureMemoryRequirementsTypeKHR::eBuildScratch,
            vk::AccelerationStructureBuildTypeKHR::eDevice, as.as.get()};
        auto memReq = m_device->GetDevice().getAccelerationStructureMemoryRequirementsKHR(asMemoryReq);

        std::unique_ptr<vkfw_core::gfx::DeviceBuffer> scratchBuffer = std::make_unique<vkfw_core::gfx::DeviceBuffer>(
            m_device, vk::BufferUsageFlagBits::eRayTracingKHR | vk::BufferUsageFlagBits::eShaderDeviceAddress);
        scratchBuffer->InitializeBuffer(memReq.memoryRequirements.size);

        return scratchBuffer;
    }

    void AccelerationStructureGeometry::AllocateDescriptorSet(vk::DescriptorPool descPool)
    {
        vk::DescriptorSetAllocateInfo descSetAllocInfo{descPool, 1, &m_descLayout};
        m_descSet = m_device->GetDevice().allocateDescriptorSets(descSetAllocInfo)[0];
    }

}
<|endoftext|>


<commit_before>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2011 Laszlo Papp <djszapi@archlinux.us>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "qgitoid.h"
#include "qgitexception.h"

#include <git2/oid.h>

namespace LibQGit2
{

QGitOId::QGitOId(const git_oid *oid)
    : d(GIT_OID_RAWSZ, '\0')
{
    if (oid != 0) {
        git_oid_cpy(data(), oid);
    }
}

QGitOId::QGitOId(const QGitOId& other)
    : d(other.d)
{
}

QGitOId::~QGitOId()
{
}

bool QGitOId::isValid() const
{
    return ( !d.isEmpty() &&
             (d.length() <= GIT_OID_RAWSZ) &&
             (d != QByteArray(GIT_OID_RAWSZ, '\0'))
             );
}

QGitOId QGitOId::fromString(const QByteArray& string)
{
    int len = qMin(string.length(), GIT_OID_HEXSZ);
    QGitOId oid;
    qGitThrow(git_oid_fromstrn(oid.data(), string.constData(), len));
    oid.d.resize(len / 2);
    return oid;
}

QGitOId QGitOId::fromRawData(const QByteArray& raw)
{
    QGitOId oid;
    oid.d = raw;
    return oid;
}

QByteArray QGitOId::format() const
{
    QByteArray ba(GIT_OID_HEXSZ, Qt::Uninitialized);
    git_oid_fmt(ba.data(), data());
    return ba;
}

QByteArray QGitOId::pathFormat() const
{
    QByteArray ba(GIT_OID_HEXSZ+1, Qt::Uninitialized);
    git_oid_pathfmt(ba.data(), data());
    return ba;
}

git_oid* QGitOId::data()
{
    return reinterpret_cast<git_oid*>(d.data());
}

const git_oid* QGitOId::data() const
{
    return reinterpret_cast<const git_oid*>(d.constData());
}

const git_oid* QGitOId::constData() const
{
    return reinterpret_cast<const git_oid*>(d.constData());
}

bool operator ==(const QGitOId &oid1, const QGitOId &oid2)
{
    return git_oid_cmp(oid1.data(), oid2.data()) == 0;
}

bool operator !=(const QGitOId &oid1, const QGitOId &oid2)
{
    return !(operator ==(oid1, oid2));
}

int QGitOId::length() const
{
    return d.length() * 2;
}

} // LibQGit2
<commit_msg>reverted change in qgitoid actor as it is a "byte array" <commit_after>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2011 Laszlo Papp <djszapi@archlinux.us>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "qgitoid.h"
#include "qgitexception.h"

#include <git2/oid.h>

namespace LibQGit2
{

QGitOId::QGitOId(const git_oid *oid)
    : d(GIT_OID_RAWSZ, 0)
{
    if (oid != 0) {
        git_oid_cpy(data(), oid);
    }
}

QGitOId::QGitOId(const QGitOId& other)
    : d(other.d)
{
}

QGitOId::~QGitOId()
{
}

bool QGitOId::isValid() const
{
    return ( !d.isEmpty() &&
             (d.length() <= GIT_OID_RAWSZ) &&
             (d != QByteArray(GIT_OID_RAWSZ, 0))
             );
}

QGitOId QGitOId::fromString(const QByteArray& string)
{
    int len = qMin(string.length(), GIT_OID_HEXSZ);
    QGitOId oid;
    qGitThrow(git_oid_fromstrn(oid.data(), string.constData(), len));
    oid.d.resize(len / 2);
    return oid;
}

QGitOId QGitOId::fromRawData(const QByteArray& raw)
{
    QGitOId oid;
    oid.d = raw;
    return oid;
}

QByteArray QGitOId::format() const
{
    QByteArray ba(GIT_OID_HEXSZ, Qt::Uninitialized);
    git_oid_fmt(ba.data(), data());
    return ba;
}

QByteArray QGitOId::pathFormat() const
{
    QByteArray ba(GIT_OID_HEXSZ+1, Qt::Uninitialized);
    git_oid_pathfmt(ba.data(), data());
    return ba;
}

git_oid* QGitOId::data()
{
    return reinterpret_cast<git_oid*>(d.data());
}

const git_oid* QGitOId::data() const
{
    return reinterpret_cast<const git_oid*>(d.constData());
}

const git_oid* QGitOId::constData() const
{
    return reinterpret_cast<const git_oid*>(d.constData());
}

bool operator ==(const QGitOId &oid1, const QGitOId &oid2)
{
    return git_oid_cmp(oid1.data(), oid2.data()) == 0;
}

bool operator !=(const QGitOId &oid1, const QGitOId &oid2)
{
    return !(operator ==(oid1, oid2));
}

int QGitOId::length() const
{
    return d.length() * 2;
}

} // LibQGit2
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009 Toni Gundogdu.
 *
 * This file is part of cclive.
 *
 * cclive is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * cclive 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <sstream>
#include <cstdlib>
#include <iomanip>

#include "hosthandler.h"
#include "macros.h"

// For well documented comments, see clive project and Redtube.pm file.

RedtubeHandler::RedtubeHandler()
    : HostHandler()
{
    props.setHost   ("redtube");
    props.setDomain ("redtube.com");
    props.setFormats("flv");
}

void
RedtubeHandler::parseId() {
    std::string id;
    partialMatch("(?i)videoid=(.*?)['\"]", &id);
    props.setId(id);
}

void
RedtubeHandler::parseTitle() {
    std::string title;
    partialMatch("(?i)videotitle['\"]>(.*?)</", &title);
    props.setTitle(title);
}

static long
digit(const long& num, const int& digit, const int& len) {
    std::stringstream b;
    b << zeropad(len,num);
    return atol(b.str().substr(digit,1).c_str());
}

static std::vector<int>
unpack_cstar(const char *str) { // perl: unpack('C*', '...')
    std::vector<int> v;
    for (register int i=0; str[i]!='\0'; ++i) {
        std::stringstream b;
        b << zeropad(2, static_cast<int>(str[i]));
        v.push_back( atoi(b.str().c_str()) );
    }
    return v;
}

static std::string
pack_cstar(const std::vector<int>& v) { // perl: pack('C', ...)
    std::stringstream b;
    for (std::vector<int>::const_iterator iter = v.begin();
        iter != v.end(); ++iter)
    {
        b << static_cast<char>(*iter);
    }
    return b.str();
}

void
RedtubeHandler::parseLink() {

    const long id = atol(props.getId().c_str());

    long var1 = 0;
    register int i=0;

    for (; i<7; ++i)
        var1 += (digit(id,i,7) * (i+1));

    std::stringstream b;
    b << var1;

    const size_t n = b.str().length();
    long var2 = 0;

    for (i=0; i<n; ++i)
        var2 += digit(var1, i, n);

    std::vector<int> map =
        unpack_cstar("R15342O7K9HBCDXFGAIJ8LMZ6PQ0STUVWEYN");

    std::vector<int> mapping;
    mapping.push_back( map[digit(id,3,7) + var2 + 3] );
    mapping.push_back( 48 + var2 % 10 );
    mapping.push_back( map[digit(id,0,7) + var2 + 2] );
    mapping.push_back( map[digit(id,2,7) + var2 + 1] );
    mapping.push_back( map[digit(id,5,7) + var2 + 6] );
    mapping.push_back( map[digit(id,1,7) + var2 + 5] );
    mapping.push_back( 48 + var2 / 10 );
    mapping.push_back( map[digit(id,4,7) + var2 + 7] );
    mapping.push_back( map[digit(id,6,7) + var2 + 4] );

    std::string tmp = pack_cstar(mapping);

    std::stringstream lnk;
    lnk << "http://dl.redtube.com/_videos_t4vn23s9jc5498tgj49icfj4678/"
        << zeropad(7,id/1000)
        << "/"
        << tmp
        << ".flv";

    props.setLink( lnk.str() );
}

<commit_msg>redtube.cpp: star -> asterisk.<commit_after>/*
 * Copyright (C) 2009 Toni Gundogdu.
 *
 * This file is part of cclive.
 *
 * cclive is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * cclive 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <sstream>
#include <cstdlib>
#include <iomanip>

#include "hosthandler.h"
#include "macros.h"

// For well documented comments, see clive project and Redtube.pm file.

RedtubeHandler::RedtubeHandler()
    : HostHandler()
{
    props.setHost   ("redtube");
    props.setDomain ("redtube.com");
    props.setFormats("flv");
}

void
RedtubeHandler::parseId() {
    std::string id;
    partialMatch("(?i)videoid=(.*?)['\"]", &id);
    props.setId(id);
}

void
RedtubeHandler::parseTitle() {
    std::string title;
    partialMatch("(?i)videotitle['\"]>(.*?)</", &title);
    props.setTitle(title);
}

static long
digit(const long& num, const int& digit, const int& len) {
    std::stringstream b;
    b << zeropad(len,num);
    return atol(b.str().substr(digit,1).c_str());
}

static std::vector<int>
unpack_casterisk(const char *str) { // perl: unpack('C*', '...')
    std::vector<int> v;
    for (register int i=0; str[i]!='\0'; ++i) {
        std::stringstream b;
        b << zeropad(2, static_cast<int>(str[i]));
        v.push_back( atoi(b.str().c_str()) );
    }
    return v;
}

static std::string
pack_casterisk(const std::vector<int>& v) { // perl: pack('C', ...)
    std::stringstream b;
    for (std::vector<int>::const_iterator iter = v.begin();
        iter != v.end(); ++iter)
    {
        b << static_cast<char>(*iter);
    }
    return b.str();
}

void
RedtubeHandler::parseLink() {

    const long id = atol(props.getId().c_str());

    long var1 = 0;
    register int i=0;

    for (; i<7; ++i)
        var1 += (digit(id,i,7) * (i+1));

    std::stringstream b;
    b << var1;

    const size_t n = b.str().length();
    long var2 = 0;

    for (i=0; i<n; ++i)
        var2 += digit(var1, i, n);

    std::vector<int> map =
        unpack_casterisk("R15342O7K9HBCDXFGAIJ8LMZ6PQ0STUVWEYN");

    std::vector<int> mapping;
    mapping.push_back( map[digit(id,3,7) + var2 + 3] );
    mapping.push_back( 48 + var2 % 10 );
    mapping.push_back( map[digit(id,0,7) + var2 + 2] );
    mapping.push_back( map[digit(id,2,7) + var2 + 1] );
    mapping.push_back( map[digit(id,5,7) + var2 + 6] );
    mapping.push_back( map[digit(id,1,7) + var2 + 5] );
    mapping.push_back( 48 + var2 / 10 );
    mapping.push_back( map[digit(id,4,7) + var2 + 7] );
    mapping.push_back( map[digit(id,6,7) + var2 + 4] );

    std::string tmp = pack_casterisk(mapping);

    std::stringstream lnk;
    lnk << "http://dl.redtube.com/_videos_t4vn23s9jc5498tgj49icfj4678/"
        << zeropad(7,id/1000)
        << "/"
        << tmp
        << ".flv";

    props.setLink( lnk.str() );
}

<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

// Disable "'this': used in base member initializer list"
#ifdef _MSC_VER
# pragma warning( push )
# pragma warning(disable: 4355)
#endif

#include <qimessaging/session.hpp>
#include "message.hpp"
#include "transportsocket.hpp"
#include <qitype/anyobject.hpp>
#include <qimessaging/serviceinfo.hpp>
#include <qitype/objectfactory.hpp>
#include "remoteobject_p.hpp"
#include "session_p.hpp"

qiLogCategory("qimessaging.session");

namespace qi {

  SessionPrivate::SessionPrivate(qi::Session *session)
    : _self(session)
    , _sdClient()
    , _serverObject(&_sdClient, session)
    , _serviceHandler(&_socketsCache, &_sdClient, &_serverObject)
    , _servicesHandler(&_sdClient, &_serverObject)
    , _sd(&_serverObject)
  {
    _self->connected.setCallType(qi::MetaCallType_Queued);
    _self->disconnected.setCallType(qi::MetaCallType_Queued);
    _self->serviceRegistered.setCallType(qi::MetaCallType_Queued);
    _self->serviceUnregistered.setCallType(qi::MetaCallType_Queued);

    _sdClientConnectedSignalLink    = _sdClient.connected.connect(boost::bind<void>(&SessionPrivate::onConnected, this));
    _sdClientDisconnectedSignalLink = _sdClient.disconnected.connect(boost::bind<void>(&SessionPrivate::onDisconnected, this, _1));
    _sdClientServiceAddedSignalLink = _sdClient.serviceAdded.connect(boost::bind<void>(&SessionPrivate::onServiceAdded, this, _1, _2));
    _sdClientServiceRemovedSignalLink = _sdClient.serviceRemoved.connect(boost::bind<void>(&SessionPrivate::onServiceRemoved, this, _1, _2));
  }

  SessionPrivate::~SessionPrivate() {
    sessionDestroy();
    close();
  }

  void SessionPrivate::onConnected() {
    _self->connected();
  }

  void SessionPrivate::onDisconnected(std::string error) {
    _self->disconnected(error);

    /*
     * Remove all proxies to services if the SD is fallen.
     */
    _serviceHandler.close();
  }

  void SessionPrivate::onServiceAdded(unsigned int idx, const std::string &name) {
    _self->serviceRegistered(idx, name);
  }

  void SessionPrivate::onServiceRemoved(unsigned int idx, const std::string &name) {
    _self->serviceUnregistered(idx, name);
  }

  void SessionPrivate::addSdSocketToCache(Future<void> f, const qi::Url& url)
  {
    if (f.hasError())
    {
      qiLogDebug() << "addSdSocketToCache: connect reported failure";
      return;
    }
    /* Allow reusing the SD socket for communicating with services.
     * To do this, we must add it to our socket cache, and for this we need
     * to know the sd machineId
     */
     std::string mid;
     try
     {
       mid = _sdClient.machineId();
     }
     catch (const std::exception& e)
     { // Provide a nice message for backward compatibility
       qiLogVerbose() << e.what();
       qiLogWarning() << "Failed to obtain machineId, connection to service directory will not be reused for other services.";
       return;
     }
     TransportSocketPtr s = _sdClient.socket();
     _socketsCache.insert(mid, url, s);
  }

  qi::FutureSync<void> SessionPrivate::connect(const qi::Url &serviceDirectoryURL)
  {
    if (isConnected()) {
      const char* s = "Session is already connected";
      qiLogInfo() << s;
      return qi::makeFutureError<void>(s);
    }
    //add the servicedirectory object into the service cache (avoid having
    // two remoteObject registered on the same transportSocket)
    _serviceHandler.addService("ServiceDirectory", _sdClient.object());

    _socketsCache.init();
    qi::Future<void> f = _sdClient.connect(serviceDirectoryURL);
    // go through hoops to get shared_ptr on this
    f.connect(&SessionPrivate::addSdSocketToCache, _self->_p, _1, serviceDirectoryURL);
    return f;
  }

  void SessionPrivate::sessionDestroy()
  {
    _sdClient.connected.disconnect(_sdClientConnectedSignalLink);
    _sdClient.disconnected.disconnect(_sdClientDisconnectedSignalLink);
    _sdClient.serviceAdded.disconnect(_sdClientServiceAddedSignalLink);
    _sdClient.serviceRemoved.disconnect(_sdClientServiceRemovedSignalLink);
    if (_self)
    {
      _self->disconnected.disconnectAll();
      _self->connected.disconnectAll();
      _self = 0;
    }
  }

  qi::FutureSync<void> SessionPrivate::close()
  {
    _serviceHandler.close();
    _serverObject.close();
    _socketsCache.close();
    return _sdClient.close();
  }

  bool SessionPrivate::isConnected() const {
    return _sdClient.isConnected();
  }

  // ###### Session
  Session::Session()
    : _p(new SessionPrivate(this))
  {
  }

  Session::~Session()
  {
    close();
    _p->sessionDestroy();
  }

  // ###### Client
  qi::FutureSync<void> Session::connect(const qi::Url &serviceDirectoryURL)
  {
    return _p->connect(serviceDirectoryURL);
  }

  qi::FutureSync<void> Session::close() {
    return _p->close();
  }

  bool Session::isConnected() const {
    return _p->isConnected();
  }

  qi::Url Session::url() const {
    if (_p->_sdClient.isLocal())
      return endpoints()[0];
    else
      return _p->_sdClient.url();
  }

  //3 cases:
  //  - local service => just return the vector
  //  - remote => ask the sd return the result
  //  - all => ask the sd, append local services, return the result
  qi::FutureSync< std::vector<ServiceInfo> > Session::services(ServiceLocality locality)
  {
    if (!isConnected()) {
      return qi::makeFutureError< std::vector<ServiceInfo> >("Session not connected.");
    }
    return _p->_servicesHandler.services(locality);
  }

  qi::FutureSync< qi::AnyObject > Session::service(const std::string &service,
                                               const std::string &protocol)
  {
    if (!isConnected()) {
      return qi::makeFutureError< qi::AnyObject >("Session not connected.");
    }
    return _p->_serviceHandler.service(service, protocol);
  }

  qi::FutureSync<void> Session::listen(const qi::Url &address)
  {
    qiLogInfo() << "Session listener created on " << address.str();
    return _p->_serverObject.listen(address);
  }

  qi::FutureSync<void> Session::listenStandalone(const qi::Url &address)
  {
    return _p->listenStandalone(address);
  }

  qi::FutureSync<void> SessionPrivate::listenStandalone(const qi::Url& address)
  {
    qi::Promise<void> p;
    //will listen and connect
    qi::Future<void> f = _sd.listenStandalone(address);
    f.connect(&SessionPrivate::listenStandaloneCont, _self->_p, p, _1);
    return p.future();
  }

  void SessionPrivate::listenStandaloneCont(qi::Promise<void> p, qi::Future<void> f)
  {
    if (f.hasError())
      p.setError(f.error());
    else
    {
      _sdClient.setServiceDirectory(static_cast<ServiceBoundObject*>(_sd._sdbo.get())->object());
      // _sdClient will trigger its connected, which will trigger our connected
      p.setValue(0);
    }
  }

  bool Session::setIdentity(const std::string& key, const std::string& crt)
  {
    return _p->_serverObject.setIdentity(key, crt);
  }

  qi::FutureSync<unsigned int> Session::registerService(const std::string &name, qi::AnyObject obj)
  {
    if (!obj)
      return makeFutureError<unsigned int>("registerService: Object is empty");
    if (endpoints().empty()) {
      qi::Url listeningAddress("tcp://0.0.0.0:0");
      qiLogVerbose() << listeningAddress.str() << "." << std::endl;
      listen(listeningAddress);
    }

    if (!isConnected()) {
      return qi::makeFutureError< unsigned int >("Session not connected.");
    }

    return _p->_serverObject.registerService(name, obj);
  }

  qi::FutureSync<void> Session::unregisterService(unsigned int idx)
  {
    if (!isConnected()) {
      return qi::makeFutureError<void>("Session not connected.");
    }

    return _p->_serverObject.unregisterService(idx);
  }

  std::vector<qi::Url> Session::endpoints() const
  {
    return _p->_serverObject.endpoints();
  }

  std::vector<std::string> Session::loadService(const std::string& name, int flags)
  {
    //TODO: what happens if the session is not connected?
    std::vector<std::string> names = ::qi::loadObject(name, flags);
    for (unsigned int i = 0; i < names.size(); ++i)
      registerService(names[i], createObject(names[i]));
    return names;
  }

}

#ifdef _MSC_VER
# pragma warning( pop )
#endif
<commit_msg>SessionPrivate: Fix connect() memory leak<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

// Disable "'this': used in base member initializer list"
#ifdef _MSC_VER
# pragma warning( push )
# pragma warning(disable: 4355)
#endif

#include <qimessaging/session.hpp>
#include "message.hpp"
#include "transportsocket.hpp"
#include <qitype/anyobject.hpp>
#include <qimessaging/serviceinfo.hpp>
#include <qitype/objectfactory.hpp>
#include "remoteobject_p.hpp"
#include "session_p.hpp"

qiLogCategory("qimessaging.session");

namespace qi {

  SessionPrivate::SessionPrivate(qi::Session *session)
    : _self(session)
    , _sdClient()
    , _serverObject(&_sdClient, session)
    , _serviceHandler(&_socketsCache, &_sdClient, &_serverObject)
    , _servicesHandler(&_sdClient, &_serverObject)
    , _sd(&_serverObject)
  {
    _self->connected.setCallType(qi::MetaCallType_Queued);
    _self->disconnected.setCallType(qi::MetaCallType_Queued);
    _self->serviceRegistered.setCallType(qi::MetaCallType_Queued);
    _self->serviceUnregistered.setCallType(qi::MetaCallType_Queued);

    _sdClientConnectedSignalLink    = _sdClient.connected.connect(boost::bind<void>(&SessionPrivate::onConnected, this));
    _sdClientDisconnectedSignalLink = _sdClient.disconnected.connect(boost::bind<void>(&SessionPrivate::onDisconnected, this, _1));
    _sdClientServiceAddedSignalLink = _sdClient.serviceAdded.connect(boost::bind<void>(&SessionPrivate::onServiceAdded, this, _1, _2));
    _sdClientServiceRemovedSignalLink = _sdClient.serviceRemoved.connect(boost::bind<void>(&SessionPrivate::onServiceRemoved, this, _1, _2));
  }

  SessionPrivate::~SessionPrivate() {
    sessionDestroy();
    close();
  }

  void SessionPrivate::onConnected() {
    _self->connected();
  }

  void SessionPrivate::onDisconnected(std::string error) {
    _self->disconnected(error);

    /*
     * Remove all proxies to services if the SD is fallen.
     */
    _serviceHandler.close();
  }

  void SessionPrivate::onServiceAdded(unsigned int idx, const std::string &name) {
    _self->serviceRegistered(idx, name);
  }

  void SessionPrivate::onServiceRemoved(unsigned int idx, const std::string &name) {
    _self->serviceUnregistered(idx, name);
  }

  void SessionPrivate::addSdSocketToCache(Future<void> f, const qi::Url& url)
  {
    if (f.hasError())
    {
      qiLogDebug() << "addSdSocketToCache: connect reported failure";
      return;
    }
    /* Allow reusing the SD socket for communicating with services.
     * To do this, we must add it to our socket cache, and for this we need
     * to know the sd machineId
     */
     std::string mid;
     try
     {
       mid = _sdClient.machineId();
     }
     catch (const std::exception& e)
     { // Provide a nice message for backward compatibility
       qiLogVerbose() << e.what();
       qiLogWarning() << "Failed to obtain machineId, connection to service directory will not be reused for other services.";
       return;
     }
     TransportSocketPtr s = _sdClient.socket();
     _socketsCache.insert(mid, url, s);
  }

  qi::FutureSync<void> SessionPrivate::connect(const qi::Url &serviceDirectoryURL)
  {
    if (isConnected()) {
      const char* s = "Session is already connected";
      qiLogInfo() << s;
      return qi::makeFutureError<void>(s);
    }
    //add the servicedirectory object into the service cache (avoid having
    // two remoteObject registered on the same transportSocket)
    _serviceHandler.addService("ServiceDirectory", _sdClient.object());

    _socketsCache.init();
    qi::Future<void> f = _sdClient.connect(serviceDirectoryURL);
    // go through hoops to get shared_ptr on this
    f.connect(&SessionPrivate::addSdSocketToCache, boost::weak_ptr<SessionPrivate>(_self->_p), _1, serviceDirectoryURL);
    return f;
  }

  void SessionPrivate::sessionDestroy()
  {
    _sdClient.connected.disconnect(_sdClientConnectedSignalLink);
    _sdClient.disconnected.disconnect(_sdClientDisconnectedSignalLink);
    _sdClient.serviceAdded.disconnect(_sdClientServiceAddedSignalLink);
    _sdClient.serviceRemoved.disconnect(_sdClientServiceRemovedSignalLink);
    if (_self)
    {
      _self->disconnected.disconnectAll();
      _self->connected.disconnectAll();
      _self = 0;
    }
  }

  qi::FutureSync<void> SessionPrivate::close()
  {
    _serviceHandler.close();
    _serverObject.close();
    _socketsCache.close();
    return _sdClient.close();
  }

  bool SessionPrivate::isConnected() const {
    return _sdClient.isConnected();
  }

  // ###### Session
  Session::Session()
    : _p(new SessionPrivate(this))
  {
  }

  Session::~Session()
  {
    close();
    _p->sessionDestroy();
  }

  // ###### Client
  qi::FutureSync<void> Session::connect(const qi::Url &serviceDirectoryURL)
  {
    return _p->connect(serviceDirectoryURL);
  }

  qi::FutureSync<void> Session::close() {
    return _p->close();
  }

  bool Session::isConnected() const {
    return _p->isConnected();
  }

  qi::Url Session::url() const {
    if (_p->_sdClient.isLocal())
      return endpoints()[0];
    else
      return _p->_sdClient.url();
  }

  //3 cases:
  //  - local service => just return the vector
  //  - remote => ask the sd return the result
  //  - all => ask the sd, append local services, return the result
  qi::FutureSync< std::vector<ServiceInfo> > Session::services(ServiceLocality locality)
  {
    if (!isConnected()) {
      return qi::makeFutureError< std::vector<ServiceInfo> >("Session not connected.");
    }
    return _p->_servicesHandler.services(locality);
  }

  qi::FutureSync< qi::AnyObject > Session::service(const std::string &service,
                                               const std::string &protocol)
  {
    if (!isConnected()) {
      return qi::makeFutureError< qi::AnyObject >("Session not connected.");
    }
    return _p->_serviceHandler.service(service, protocol);
  }

  qi::FutureSync<void> Session::listen(const qi::Url &address)
  {
    qiLogInfo() << "Session listener created on " << address.str();
    return _p->_serverObject.listen(address);
  }

  qi::FutureSync<void> Session::listenStandalone(const qi::Url &address)
  {
    return _p->listenStandalone(address);
  }

  qi::FutureSync<void> SessionPrivate::listenStandalone(const qi::Url& address)
  {
    qi::Promise<void> p;
    //will listen and connect
    qi::Future<void> f = _sd.listenStandalone(address);
    f.connect(&SessionPrivate::listenStandaloneCont, _self->_p, p, _1);
    return p.future();
  }

  void SessionPrivate::listenStandaloneCont(qi::Promise<void> p, qi::Future<void> f)
  {
    if (f.hasError())
      p.setError(f.error());
    else
    {
      _sdClient.setServiceDirectory(static_cast<ServiceBoundObject*>(_sd._sdbo.get())->object());
      // _sdClient will trigger its connected, which will trigger our connected
      p.setValue(0);
    }
  }

  bool Session::setIdentity(const std::string& key, const std::string& crt)
  {
    return _p->_serverObject.setIdentity(key, crt);
  }

  qi::FutureSync<unsigned int> Session::registerService(const std::string &name, qi::AnyObject obj)
  {
    if (!obj)
      return makeFutureError<unsigned int>("registerService: Object is empty");
    if (endpoints().empty()) {
      qi::Url listeningAddress("tcp://0.0.0.0:0");
      qiLogVerbose() << listeningAddress.str() << "." << std::endl;
      listen(listeningAddress);
    }

    if (!isConnected()) {
      return qi::makeFutureError< unsigned int >("Session not connected.");
    }

    return _p->_serverObject.registerService(name, obj);
  }

  qi::FutureSync<void> Session::unregisterService(unsigned int idx)
  {
    if (!isConnected()) {
      return qi::makeFutureError<void>("Session not connected.");
    }

    return _p->_serverObject.unregisterService(idx);
  }

  std::vector<qi::Url> Session::endpoints() const
  {
    return _p->_serverObject.endpoints();
  }

  std::vector<std::string> Session::loadService(const std::string& name, int flags)
  {
    //TODO: what happens if the session is not connected?
    std::vector<std::string> names = ::qi::loadObject(name, flags);
    for (unsigned int i = 0; i < names.size(); ++i)
      registerService(names[i], createObject(names[i]));
    return names;
  }

}

#ifdef _MSC_VER
# pragma warning( pop )
#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2003-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Nathan Binkert
 */

/**
 * @file
 * Defines host-dependent types:
 * Counter, Tick, and (indirectly) {int,uint}{8,16,32,64}_t.
 */

#ifndef __HOST_HH__
#define __HOST_HH__

#include <inttypes.h>
#include <limits>

/** uint64_t constant */
#define ULL(N)          ((uint64_t)N##ULL)
/** int64_t constant */
#define LL(N)           ((int64_t)N##LL)

/** Statistics counter type.  Not much excuse for not using a 64-bit
 * integer here, but if you're desperate and only run short
 * simulations you could make this 32 bits.
 */
typedef int64_t Counter;

/**
 * Clock cycle count type.
 * @note using an unsigned breaks the cache.
 */
typedef int64_t Tick;

const Tick MaxTick = std::numeric_limits<Tick>::max();

/**
 * Address type
 * This will probably be moved somewhere else in the near future.
 * This should be at least as big as the biggest address width in use
 * in the system, which will probably be 64 bits.
 */
typedef uint64_t Addr;

const Addr MaxAddr = (Addr)-1;

#endif // __HOST_H__
<commit_msg>eventq: I'm sick of the warning about MaxTick being unused.<commit_after>/*
 * Copyright (c) 2003-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Nathan Binkert
 */

/**
 * @file
 * Defines host-dependent types:
 * Counter, Tick, and (indirectly) {int,uint}{8,16,32,64}_t.
 */

#ifndef __HOST_HH__
#define __HOST_HH__

#include <inttypes.h>

/** uint64_t constant */
#define ULL(N)          ((uint64_t)N##ULL)
/** int64_t constant */
#define LL(N)           ((int64_t)N##LL)

/** Statistics counter type.  Not much excuse for not using a 64-bit
 * integer here, but if you're desperate and only run short
 * simulations you could make this 32 bits.
 */
typedef int64_t Counter;

/**
 * Clock cycle count type.
 * @note using an unsigned breaks the cache.
 */
typedef int64_t Tick;

const Tick MaxTick = LL(0x7fffffffffffffff);

/**
 * Address type
 * This will probably be moved somewhere else in the near future.
 * This should be at least as big as the biggest address width in use
 * in the system, which will probably be 64 bits.
 */
typedef uint64_t Addr;

const Addr MaxAddr = (Addr)-1;

#endif // __HOST_H__
<|endoftext|>


<commit_before>#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>

#include <algorithm>
#include <vector>

#include "storage.hpp"
#include "misc.hpp"

Block::Block(size_t blockSize) {
	this->id = 0xffffffff;
	this->refCount = 0;
	this->needUpdate = false;
	this->ptr = (char *)calloc(blockSize, 1);
}

Block::~Block() {
	::free(this->ptr);
}

void Block::update() {
	this->needUpdate = true;
}

void Block::free() {
	this->refCount--;
}

BlockStorageSuperblock::BlockStorageSuperblock(Block *block)
	:block(block),
	blockSize(*(uint32_t *)block->ptr),
	blocksCount(*(((uint32_t *)block->ptr) + 1))
{
}

BlockStorageSuperblock::~BlockStorageSuperblock() {
	this->block->free();
}

void BlockStorageSuperblock::update() {
	this->block->update();
}

/* -- */

BlockStorage::BlockStorage(const char *filename)
	:fd(-1),
	blocksCacheSize(SIZE_T_MAX),
	superblock(NULL)
{
	this->filename = new char[strlen(filename)+1];
	strcpy(this->filename, filename);
	blocksCacheCurrentSize = 0;
	gcTimer = 1;
	gcProbability = 2048;
}

BlockStorage::~BlockStorage() {
	if(this->superblock)
		delete superblock;

	this->superblock = NULL;

	fprintf(stderr, "Flush cached blocks ...");
	this->setCacheSize(0);
	this->_gc(true);
	fprintf(stderr, " done\n");

	delete[] this->filename;

	if(this->fd >= 0)
		close(this->fd);

	this->fd = -1;
}

void BlockStorage::create(size_t blockSize) {
	this->blockSize = blockSize;

	this->fd = open(this->filename, O_RDWR | O_CREAT | O_TRUNC, 0640);
	if(this->fd < 0) {
		perror(this->filename);
		exit(errno);
	}

	this->_extendFile(blockSize);

	this->superblock = new BlockStorageSuperblock(this->get(0));
	this->superblock->blockSize = blockSize;
	this->superblock->blocksCount = 1;
	this->superblock->update();
}

void BlockStorage::load() {
	this->fd = open(this->filename, O_RDWR);
	if(this->fd < 0) {
		perror(this->filename);
		exit(errno);
	}

	if(pread(this->fd, &this->blockSize, sizeof(this->blockSize), 0) != sizeof(this->blockSize)) {
		perror("load:pread");
		exit(errno);
	}

	this->superblock = new BlockStorageSuperblock(this->get(0));

	if(this->superblock->blocksCount < this->prefetchSize)
		this->prefetchSize = this->superblock->blocksCount;

	if(this->blocksCacheSize < this->prefetchSize)
		this->prefetchSize = this->blocksCacheSize;

	fprintf(stderr, "Prefetch %u leading blocks ...", (unsigned int)this->prefetchSize);

	for(size_t i=0; i<this->prefetchSize; i++) {
		this->get(i)->free();
	}
	fprintf(stderr, " done\n");
}

Block *BlockStorage::allocate() {
	Block *b;
	this->_extendFile((this->superblock->blocksCount + 1) * this->blockSize);
	b = new Block(this->blockSize);
	b->refCount = 1;
	b->id = this->superblock->blocksCount;

// 	fprintf(stderr, "alloc %u\n", b->id);
	this->blocksCache.push_back(b);
	this->blocksCacheMap[b->id] = --this->blocksCache.end();
	this->blocksCacheCurrentSize++;

	this->superblock->blocksCount++;
	this->superblock->update();

	if(this->gcTimer % this->gcProbability == 0)
		this->_gc();
	this->gcTimer++;

	return b;
}

Block *BlockStorage::get(uint32_t id) {
// 	fprintf(stderr, "get %u\n", id);
	if(this->superblock && id >= this->superblock->blocksCount) {
		fprintf(stderr, "Requested block %u is out of range [0; %u)\n", id, this->superblock->blocksCount);
		exit(11);
	}

	Block *b;

	if(this->blocksCacheMap.find(id) == this->blocksCacheMap.end()) {
		if(this->gcTimer % this->gcProbability == 0)
			this->_gc();
		this->gcTimer++;

		b = new Block(this->blockSize);
		b->id = id;

		off_t offset = id * this->blockSize;
		if(offset < this->_fileSize()) {
			if((size_t)pread(this->fd, b->ptr, this->blockSize, offset) != this->blockSize) {
				perror("get:pread");
				fprintf(stderr, "Block id: %u\n", (unsigned int)id);
				exit(errno ? errno : 255);
			}
		}

		this->blocksCache.push_back(b);
		this->blocksCacheMap[id] = --this->blocksCache.end();
		++this->blocksCacheCurrentSize;
	} else {
		if(this->blocksCacheSize != SIZE_T_MAX) {
			b = *this->blocksCacheMap[id];
			this->blocksCache.erase(this->blocksCacheMap[id]);
			this->blocksCache.push_back(b);
			this->blocksCacheMap[id] = --this->blocksCache.end();
		}
	}

	(*this->blocksCacheMap[id])->refCount++;

	return *this->blocksCacheMap[id];
}

void BlockStorage::_extendFile(off_t newSize) {
#define PREALLOC_ENABLED
#ifdef PREALLOC_ENABLED
	ssize_t blockSize = 4096;
	off_t oldSize = this->_fileSize();
	if(oldSize < newSize) {
		char *zero;
		ssize_t i;
		newSize -= newSize % 1024*1024;
		newSize += 1024*1024;
		zero = (char *)alloca(blockSize);
		bzero(zero, blockSize);

		if(lseek(this->fd, 0, SEEK_END) < 0)
			fatal("lseek");

		for(i=0; i<(newSize - oldSize)/blockSize; i++) {
			if(write(this->fd, zero, blockSize) != blockSize)
				fatal("write");
		}

		if((newSize - oldSize) % blockSize) {
			if(write(this->fd, zero, (newSize - oldSize)%blockSize) != (newSize - oldSize)%blockSize)
				fatal("write");
		}
	}
#else
	if(this->_fileSize() < newSize) {
		newSize -= newSize % 1024*1024;
		newSize += 1024*1024;

		if(ftruncate(this->fd, newSize) < 0)
			fatal("truncate");
	}
#endif
}

off_t BlockStorage::_fileSize() {
	struct stat s;

	if(fstat(this->fd, &s) < 0) {
		perror("fstat");
		exit(errno);
	}

	return s.st_size;
}

static bool blockcmp(const Block * b1, const Block *b2) {
	return b1->id < b2->id;
}

void BlockStorage::_gc(bool cleanAll) {
// 	fprintf(stderr, "GC launch ... %u %u\n", this->blocksCacheCurrentSize, this->blocksCacheSize);
	if(this->blocksCacheCurrentSize < this->blocksCacheSize)
		return;

// 	fprintf(stderr, "GC launch ...");
	std::list<Block *>::iterator i = this->blocksCache.begin();
	std::vector<Block *> flushBuffer;
	size_t deleted = 0;

	/* 1 stage: delete clean blocks */
	while(i != this->blocksCache.end() && this->blocksCacheCurrentSize > this->blocksCacheSize) {
		Block *b = *i;
		if(!b->needUpdate && (!b->refCount || cleanAll)) {
			i = this->blocksCache.erase(i);
			this->blocksCacheMap.erase(b->id);
			--this->blocksCacheCurrentSize;
			deleted++;

			delete b;
		} else {
			++i;
		}
	}

	/* 2 stage: delete dirty blocks */
	i = this->blocksCache.begin();

	while(i != this->blocksCache.end() && this->blocksCacheCurrentSize > this->blocksCacheSize) {
		Block *b = *i;
		if(b->needUpdate && (!b->refCount || cleanAll)) {
			i = this->blocksCache.erase(i);
			this->blocksCacheMap.erase(b->id);
			--this->blocksCacheCurrentSize;
			deleted++;

			flushBuffer.push_back(b);
		} else {
			++i;
		}
	}

	if(flushBuffer.size()) {
// 		fprintf(stderr, "\nDropped: %u (flushed: %u)\n", (unsigned int)deleted, (unsigned int)flushBuffer.size());
		std::sort(flushBuffer.begin(), flushBuffer.end(), blockcmp);

		std::vector<Block *>::iterator vi;

		for(vi=flushBuffer.begin(); vi!=flushBuffer.end(); ++vi) {
			Block *b = *vi;
			pwrite(this->fd, b->ptr, this->blockSize, b->id * this->blockSize);
			delete b;
		}
// 		fprintf(stderr, "done\n");
	}
}

void BlockStorage::setCacheSize(size_t size) {
	this->blocksCacheSize = size;
}

void BlockStorage::setPrefetchSize(size_t size) {
	this->prefetchSize= size;
}
<commit_msg>Debug output was removed<commit_after>#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>

#include <algorithm>
#include <vector>

#include "storage.hpp"
#include "misc.hpp"

Block::Block(size_t blockSize) {
	this->id = 0xffffffff;
	this->refCount = 0;
	this->needUpdate = false;
	this->ptr = (char *)calloc(blockSize, 1);
}

Block::~Block() {
	::free(this->ptr);
}

void Block::update() {
	this->needUpdate = true;
}

void Block::free() {
	this->refCount--;
}

BlockStorageSuperblock::BlockStorageSuperblock(Block *block)
	:block(block),
	blockSize(*(uint32_t *)block->ptr),
	blocksCount(*(((uint32_t *)block->ptr) + 1))
{
}

BlockStorageSuperblock::~BlockStorageSuperblock() {
	this->block->free();
}

void BlockStorageSuperblock::update() {
	this->block->update();
}

/* -- */

BlockStorage::BlockStorage(const char *filename)
	:fd(-1),
	blocksCacheSize(SIZE_T_MAX),
	superblock(NULL)
{
	this->filename = new char[strlen(filename)+1];
	strcpy(this->filename, filename);
	blocksCacheCurrentSize = 0;
	gcTimer = 1;
	gcProbability = 2048;
}

BlockStorage::~BlockStorage() {
	if(this->superblock)
		delete superblock;

	this->superblock = NULL;

	this->setCacheSize(0);
	this->_gc(true);

	delete[] this->filename;

	if(this->fd >= 0)
		close(this->fd);

	this->fd = -1;
}

void BlockStorage::create(size_t blockSize) {
	this->blockSize = blockSize;

	this->fd = open(this->filename, O_RDWR | O_CREAT | O_TRUNC, 0640);
	if(this->fd < 0) {
		perror(this->filename);
		exit(errno);
	}

	this->_extendFile(blockSize);

	this->superblock = new BlockStorageSuperblock(this->get(0));
	this->superblock->blockSize = blockSize;
	this->superblock->blocksCount = 1;
	this->superblock->update();
}

void BlockStorage::load() {
	this->fd = open(this->filename, O_RDWR);
	if(this->fd < 0) {
		perror(this->filename);
		exit(errno);
	}

	if(pread(this->fd, &this->blockSize, sizeof(this->blockSize), 0) != sizeof(this->blockSize)) {
		perror("load:pread");
		exit(errno);
	}

	this->superblock = new BlockStorageSuperblock(this->get(0));

	if(this->superblock->blocksCount < this->prefetchSize)
		this->prefetchSize = this->superblock->blocksCount;

	if(this->blocksCacheSize < this->prefetchSize)
		this->prefetchSize = this->blocksCacheSize;

	for(size_t i=0; i<this->prefetchSize; i++) {
		this->get(i)->free();
	}
}

Block *BlockStorage::allocate() {
	Block *b;
	this->_extendFile((this->superblock->blocksCount + 1) * this->blockSize);
	b = new Block(this->blockSize);
	b->refCount = 1;
	b->id = this->superblock->blocksCount;

	this->blocksCache.push_back(b);
	this->blocksCacheMap[b->id] = --this->blocksCache.end();
	this->blocksCacheCurrentSize++;

	this->superblock->blocksCount++;
	this->superblock->update();

	if(this->gcTimer % this->gcProbability == 0)
		this->_gc();
	this->gcTimer++;

	return b;
}

Block *BlockStorage::get(uint32_t id) {
// 	fprintf(stderr, "get %u\n", id);
	if(this->superblock && id >= this->superblock->blocksCount) {
		fprintf(stderr, "Requested block %u is out of range [0; %u)\n", id, this->superblock->blocksCount);
		exit(11);
	}

	Block *b;

	if(this->blocksCacheMap.find(id) == this->blocksCacheMap.end()) {
		if(this->gcTimer % this->gcProbability == 0)
			this->_gc();
		this->gcTimer++;

		b = new Block(this->blockSize);
		b->id = id;

		off_t offset = id * this->blockSize;
		if(offset < this->_fileSize()) {
			if((size_t)pread(this->fd, b->ptr, this->blockSize, offset) != this->blockSize) {
				perror("get:pread");
				fprintf(stderr, "Block id: %u\n", (unsigned int)id);
				exit(errno ? errno : 255);
			}
		}

		this->blocksCache.push_back(b);
		this->blocksCacheMap[id] = --this->blocksCache.end();
		++this->blocksCacheCurrentSize;
	} else {
		if(this->blocksCacheSize != SIZE_T_MAX) {
			b = *this->blocksCacheMap[id];
			this->blocksCache.erase(this->blocksCacheMap[id]);
			this->blocksCache.push_back(b);
			this->blocksCacheMap[id] = --this->blocksCache.end();
		}
	}

	(*this->blocksCacheMap[id])->refCount++;

	return *this->blocksCacheMap[id];
}

void BlockStorage::_extendFile(off_t newSize) {
#define PREALLOC_ENABLED
#ifdef PREALLOC_ENABLED
	ssize_t blockSize = 4096;
	off_t oldSize = this->_fileSize();
	if(oldSize < newSize) {
		char *zero;
		ssize_t i;
		newSize -= newSize % 1024*1024;
		newSize += 1024*1024;
		zero = (char *)alloca(blockSize);
		bzero(zero, blockSize);

		if(lseek(this->fd, 0, SEEK_END) < 0)
			fatal("lseek");

		for(i=0; i<(newSize - oldSize)/blockSize; i++) {
			if(write(this->fd, zero, blockSize) != blockSize)
				fatal("write");
		}

		if((newSize - oldSize) % blockSize) {
			if(write(this->fd, zero, (newSize - oldSize)%blockSize) != (newSize - oldSize)%blockSize)
				fatal("write");
		}
	}
#else
	if(this->_fileSize() < newSize) {
		newSize -= newSize % 1024*1024;
		newSize += 1024*1024;

		if(ftruncate(this->fd, newSize) < 0)
			fatal("truncate");
	}
#endif
}

off_t BlockStorage::_fileSize() {
	struct stat s;

	if(fstat(this->fd, &s) < 0) {
		perror("fstat");
		exit(errno);
	}

	return s.st_size;
}

static bool blockcmp(const Block * b1, const Block *b2) {
	return b1->id < b2->id;
}

void BlockStorage::_gc(bool cleanAll) {
// 	fprintf(stderr, "GC launch ... %u %u\n", this->blocksCacheCurrentSize, this->blocksCacheSize);
	if(this->blocksCacheCurrentSize < this->blocksCacheSize)
		return;

// 	fprintf(stderr, "GC launch ...");
	std::list<Block *>::iterator i = this->blocksCache.begin();
	std::vector<Block *> flushBuffer;
	size_t deleted = 0;

	/* 1 stage: delete clean blocks */
	while(i != this->blocksCache.end() && this->blocksCacheCurrentSize > this->blocksCacheSize) {
		Block *b = *i;
		if(!b->needUpdate && (!b->refCount || cleanAll)) {
			i = this->blocksCache.erase(i);
			this->blocksCacheMap.erase(b->id);
			--this->blocksCacheCurrentSize;
			deleted++;

			delete b;
		} else {
			++i;
		}
	}

	/* 2 stage: delete dirty blocks */
	i = this->blocksCache.begin();

	while(i != this->blocksCache.end() && this->blocksCacheCurrentSize > this->blocksCacheSize) {
		Block *b = *i;
		if(b->needUpdate && (!b->refCount || cleanAll)) {
			i = this->blocksCache.erase(i);
			this->blocksCacheMap.erase(b->id);
			--this->blocksCacheCurrentSize;
			deleted++;

			flushBuffer.push_back(b);
		} else {
			++i;
		}
	}

	if(flushBuffer.size()) {
// 		fprintf(stderr, "\nDropped: %u (flushed: %u)\n", (unsigned int)deleted, (unsigned int)flushBuffer.size());
		std::sort(flushBuffer.begin(), flushBuffer.end(), blockcmp);

		std::vector<Block *>::iterator vi;

		for(vi=flushBuffer.begin(); vi!=flushBuffer.end(); ++vi) {
			Block *b = *vi;
			pwrite(this->fd, b->ptr, this->blockSize, b->id * this->blockSize);
			delete b;
		}
// 		fprintf(stderr, "done\n");
	}
}

void BlockStorage::setCacheSize(size_t size) {
	this->blocksCacheSize = size;
}

void BlockStorage::setPrefetchSize(size_t size) {
	this->prefetchSize= size;
}
<|endoftext|>


<commit_before>/*-------------------------------------------------------------------------
 *
 *   FILE
 *	strconv.cxx
 *
 *   DESCRIPTION
 *      implementation of string conversions
 *
 * Copyright (c) 2008-2012, Jeroen T. Vermeulen <jtv@xs4all.nl>
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 *
 *-------------------------------------------------------------------------
 */
#include "pqxx/compiler-internal.hxx"

#include <cstring>

#ifdef PQXX_HAVE_LIMITS
#include <limits>
#endif

#ifdef PQXX_HAVE_LOCALE
#include <locale>
#endif

#include "pqxx/except"
#include "pqxx/strconv"

using namespace PGSTD;
using namespace pqxx::internal;

namespace
{
// It turns out that NaNs are pretty hard to do portably.  If the appropriate
// C++ traits functions are not available, C99 defines a NAN macro (also widely
// supported in other dialects, I believe) and some functions that can do the
// same work.  But if none of those are available, we need to resort to
// compile-time "0/0" expressions.  Most compilers won't crash while compiling
// those anymore, but there may be unneeded warnings--which are almost as bad.
template<typename T> void set_to_NaN(T &);

#if defined(PQXX_HAVE_QUIET_NAN)
template<typename T> inline void set_to_NaN(T &t)
	{ t = numeric_limits<T>::quiet_NaN(); }
#elif defined(PQXX_HAVE_C_NAN)
template<typename T> inline void set_to_NaN(T &t) { t = NAN; }
#elif defined(PQXX_HAVE_NAN)
template<> inline void set_to_NaN(float &t) { t = fnan(""); }
template<> inline void set_to_NaN(double &t) { t = nan(""); }
#ifdef PQXX_HAVE_LONG_DOUBLE
template<> inline void set_to_NaN(long double &t) { t = lnan(""); }
#endif
#else
const float nan_f(0.0/0.0);
template<> inline void set_to_NaN(float &t) { t = nan_f; }
const double nan_d(0.0/0.0);
template<> inline void set_to_NaN(double &t) { t = nan_d; }
#ifdef PQXX_HAVE_LONG_DOUBLE
const long double nan_ld(0.0/0.0);
template<> inline void set_to_NaN(long double &t) { t = nan_ld; }
#endif
#endif

// TODO: This may need tweaking for various compilers.
template<typename T> inline void set_to_Inf(T &t, int sign=1)
{
#ifdef PQXX_HAVE_LIMITS
  T value = numeric_limits<T>::infinity();
#else
  T value = INFINITY;
#endif
  if (sign < 0) value = -value;
  t = value;
}

void report_overflow()
{
  throw pqxx::failure(
	"Could not convert string to integer: value out of range.");
}

/** Helper to check for underflow before multiplying a number by 10.
 *
 * Needed just so the compiler doesn't get to complain about an "if (n < 0)"
 * clause that's pointless for unsigned numbers.
 */
template<typename T, bool is_signed> struct underflow_check;

/* Specialization for signed types: check.
 */
template<typename T> struct underflow_check<T, true>
{
  static void check_before_adding_digit(T n)
  {
    const T ten(10);
    if (n < 0 && (numeric_limits<T>::min() / ten) > n) report_overflow();
  }
};

/* Specialization for unsigned types: no check needed becaue negative
 * numbers don't exist.
 */
template<typename T> struct underflow_check<T, false>
{
  static void check_before_adding_digit(T) {}
};

/// Return 10*n, or throw exception if it overflows.
template<typename T> T safe_multiply_by_ten(T n)
{
  const T ten(10);
  if (n > 0 && (numeric_limits<T>::max() / n) < ten) report_overflow();
  underflow_check<T, numeric_limits<T>::is_signed>::check_before_adding_digit(
	n);
  return T(n * ten);
}

/// Add a digit d to n, or throw exception if it overflows.
template<typename T> T safe_add_digit(T n, T d)
{
  assert((n >= 0 && d >= 0) || (n <=0 && d <= 0));
  if ((n > 0) && (n > (numeric_limits<T>::max() - d))) report_overflow();
  if ((n < 0) && (n < (numeric_limits<T>::min() - d))) report_overflow();
  return n + d;
}

/// For use in string parsing: add new numeric digit to intermediate value
template<typename L, typename R>
  inline L absorb_digit(L value, R digit)
{
  return L(safe_multiply_by_ten(value) + L(digit));
}

template<typename T> void from_string_signed(const char Str[], T &Obj)
{
  int i = 0;
  T result = 0;

  if (!isdigit(Str[i]))
  {
    if (Str[i] != '-')
      throw pqxx::failure("Could not convert string to integer: '" +
	string(Str) + "'");

    for (++i; isdigit(Str[i]); ++i)
      result = absorb_digit(result, -digit_to_number(Str[i]));
  }
  else for (; isdigit(Str[i]); ++i)
    result = absorb_digit(result, digit_to_number(Str[i]));

  if (Str[i])
    throw pqxx::failure("Unexpected text after integer: '" + string(Str) + "'");

  Obj = result;
}

template<typename T> void from_string_unsigned(const char Str[], T &Obj)
{
  int i = 0;
  T result = 0;

  if (!isdigit(Str[i]))
    throw pqxx::failure("Could not convert string to unsigned integer: '" +
	string(Str) + "'");

  for (; isdigit(Str[i]); ++i)
    result = absorb_digit(result, digit_to_number(Str[i]));

  if (Str[i])
    throw pqxx::failure("Unexpected text after integer: '" + string(Str) + "'");

  Obj = result;
}

bool valid_infinity_string(const char str[])
{
  // TODO: Also accept less sensible case variations.
  return
	strcmp("infinity", str) == 0 ||
	strcmp("Infinity", str) == 0 ||
	strcmp("INFINITY", str) == 0 ||
	strcmp("inf", str) == 0;
}

/* These are hard.  Sacrifice performance of specialized, nonflexible,
 * non-localized code and lean on standard library.  Some special-case code
 * handles NaNs.
 */
template<typename T> inline void from_string_float(const char Str[], T &Obj)
{
  bool ok = false;
  T result;

  switch (Str[0])
  {
  case 'N':
  case 'n':
    // Accept "NaN," "nan," etc.
    ok = ((Str[1]=='A'||Str[1]=='a') && (Str[2]=='N'||Str[2]=='n') && !Str[3]);
    set_to_NaN(result);
    break;

  case 'I':
  case 'i':
    ok = valid_infinity_string(Str);
    set_to_Inf(result);
    break;

  default:
    if (Str[0] == '-' && valid_infinity_string(&Str[1]))
    {
      ok = true;
      set_to_Inf(result, -1);
    }
    else
    {
      stringstream S(Str);
#if defined(PQXX_HAVE_IMBUE)
      S.imbue(locale("C"));
#endif
      ok = static_cast<bool>(S >> result);
    }
    break;
  }

  if (!ok)
    throw pqxx::failure("Could not convert string to numeric value: '" +
	string(Str) + "'");

  Obj = result;
}

template<typename T> inline string to_string_unsigned(T Obj)
{
  if (!Obj) return "0";

  // Every byte of width on T adds somewhere between 3 and 4 digits to the
  // maximum length of our decimal string.
  char buf[4*sizeof(T)+1];

  char *p = &buf[sizeof(buf)];
  *--p = '\0';
  while (Obj > 0)
  {
    *--p = number_to_digit(int(Obj%10));
    Obj /= 10;
  }
  return p;
}

template<typename T> inline string to_string_fallback(T Obj)
{
  stringstream S;
#ifdef PQXX_HAVE_IMBUE
  S.imbue(locale("C"));
#endif

// Provide enough precision.
#ifdef PQXX_HAVE_LIMITS
  // Kirit reports getting two more digits of precision than
  // numeric_limits::digits10 would give him, so we try not to make him lose
  // those last few bits.
  S.precision(numeric_limits<T>::digits10 + 2);
#else
  // Guess: enough for an IEEE 754 double-precision value.
  S.precision(16);
#endif
  S << Obj;
  return S.str();
}

template<typename T> inline bool is_NaN(T Obj)
{
  return
#if defined(PQXX_HAVE_C_ISNAN)
    isnan(Obj);
#elif defined(PQXX_HAVE_LIMITS)
    !(Obj <= Obj+numeric_limits<T>::max());
#else
    !(Obj <= Obj + 1000);
#endif
}

template<typename T> inline bool is_Inf(T Obj)
{
  return
#if defined(PQXX_HAVE_C_ISINF)
    isinf(Obj);
#else
    Obj >= Obj+1 && Obj <= 2*Obj && Obj >= 2*Obj;
#endif
}

template<typename T> inline string to_string_float(T Obj)
{
  // TODO: Omit this special case if NaN is output as "nan"/"NAN"/"NaN"
#ifndef PQXX_HAVE_NAN_OUTPUT
  if (is_NaN(Obj)) return "nan";
#endif
  // TODO: Omit this special case if infinity is output as "infinity"
#ifndef PQXX_HAVE_INF_OUTPUT
  if (is_Inf(Obj)) return Obj > 0 ? "infinity" : "-infinity";
#endif
  return to_string_fallback(Obj);
}

template<typename T> inline string to_string_signed(T Obj)
{
  if (Obj < 0)
  {
    // Remember--the smallest negative number for a given two's-complement type
    // cannot be negated.
#if PQXX_HAVE_LIMITS
    const bool negatable = (Obj != numeric_limits<T>::min());
#else
    T Neg(-Obj);
    const bool negatable = Neg > 0;
#endif
    if (negatable)
      return '-' + to_string_unsigned(-Obj);
    else
      return to_string_fallback(Obj);
  }

  return to_string_unsigned(Obj);
}

} // namespace

namespace pqxx
{

namespace internal
{
void throw_null_conversion(const PGSTD::string &type)
{
  throw conversion_error("Attempt to convert null to " + type);
}
} // namespace pqxx::internal

void string_traits<bool>::from_string(const char Str[], bool &Obj)
{
  bool OK, result=false;

  switch (Str[0])
  {
  case 0:
    result = false;
    OK = true;
    break;

  case 'f':
  case 'F':
    result = false;
    OK = !(Str[1] &&
	   (strcmp(Str+1, "alse") != 0) &&
	   (strcmp(Str+1, "ALSE") != 0));
    break;

  case '0':
    {
      int I;
      string_traits<int>::from_string(Str, I);
      result = (I != 0);
      OK = ((I == 0) || (I == 1));
    }
    break;

  case '1':
    result = true;
    OK = !Str[1];
    break;

  case 't':
  case 'T':
    result = true;
    OK = !(Str[1] &&
	   (strcmp(Str+1, "rue") != 0) &&
	   (strcmp(Str+1, "RUE") != 0));
    break;

  default:
    OK = false;
  }

  if (!OK)
    throw argument_error("Failed conversion to bool: '" + string(Str) + "'");

  Obj = result;
}

string string_traits<bool>::to_string(bool Obj)
{
  return Obj ? "true" : "false";
}

void string_traits<short>::from_string(const char Str[], short &Obj)
{
  from_string_signed(Str, Obj);
}

string string_traits<short>::to_string(short Obj)
{
  return to_string_signed(Obj);
}

void string_traits<unsigned short>::from_string(
	const char Str[],
	unsigned short &Obj)
{
  from_string_unsigned(Str, Obj);
}

string string_traits<unsigned short>::to_string(unsigned short Obj)
{
  return to_string_unsigned(Obj);
}

void string_traits<int>::from_string(const char Str[], int &Obj)
{
  from_string_signed(Str, Obj);
}

string string_traits<int>::to_string(int Obj)
{
  return to_string_signed(Obj);
}

void string_traits<unsigned int>::from_string(
	const char Str[],
	unsigned int &Obj)
{
  from_string_unsigned(Str, Obj);
}

string string_traits<unsigned int>::to_string(unsigned int Obj)
{
  return to_string_unsigned(Obj);
}

void string_traits<long>::from_string(const char Str[], long &Obj)
{
  from_string_signed(Str, Obj);
}

string string_traits<long>::to_string(long Obj)
{
  return to_string_signed(Obj);
}

void string_traits<unsigned long>::from_string(
	const char Str[],
	unsigned long &Obj)
{
  from_string_unsigned(Str, Obj);
}

string string_traits<unsigned long>::to_string(unsigned long Obj)
{
  return to_string_unsigned(Obj);
}

#ifdef PQXX_HAVE_LONG_LONG
void string_traits<long long>::from_string(const char Str[], long long &Obj)
{
  from_string_signed(Str, Obj);
}

string string_traits<long long>::to_string(long long Obj)
{
  return to_string_signed(Obj);
}

void string_traits<unsigned long long>::from_string(
	const char Str[],
	unsigned long long &Obj)
{
  from_string_unsigned(Str, Obj);
}

string string_traits<unsigned long long>::to_string(unsigned long long Obj)
{
  return to_string_unsigned(Obj);
}
#endif

void string_traits<float>::from_string(const char Str[], float &Obj)
{
  from_string_float(Str, Obj);
}

string string_traits<float>::to_string(float Obj)
{
  return to_string_float(Obj);
}

void string_traits<double>::from_string(const char Str[], double &Obj)
{
  from_string_float(Str, Obj);
}

string string_traits<double>::to_string(double Obj)
{
  return to_string_float(Obj);
}

#ifdef PQXX_HAVE_LONG_DOUBLE
void string_traits<long double>::from_string(const char Str[], long double &Obj)
{
  from_string_float(Str, Obj);
}

string string_traits<long double>::to_string(long double Obj)
{
  return to_string_float(Obj);
}
#endif

} // namespace pqxx
<commit_msg>Copyright update.<commit_after>/*-------------------------------------------------------------------------
 *
 *   FILE
 *	strconv.cxx
 *
 *   DESCRIPTION
 *      implementation of string conversions
 *
 * Copyright (c) 2008-2013, Jeroen T. Vermeulen <jtv@xs4all.nl>
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 *
 *-------------------------------------------------------------------------
 */
#include "pqxx/compiler-internal.hxx"

#include <cstring>

#ifdef PQXX_HAVE_LIMITS
#include <limits>
#endif

#ifdef PQXX_HAVE_LOCALE
#include <locale>
#endif

#include "pqxx/except"
#include "pqxx/strconv"

using namespace PGSTD;
using namespace pqxx::internal;

namespace
{
// It turns out that NaNs are pretty hard to do portably.  If the appropriate
// C++ traits functions are not available, C99 defines a NAN macro (also widely
// supported in other dialects, I believe) and some functions that can do the
// same work.  But if none of those are available, we need to resort to
// compile-time "0/0" expressions.  Most compilers won't crash while compiling
// those anymore, but there may be unneeded warnings--which are almost as bad.
template<typename T> void set_to_NaN(T &);

#if defined(PQXX_HAVE_QUIET_NAN)
template<typename T> inline void set_to_NaN(T &t)
	{ t = numeric_limits<T>::quiet_NaN(); }
#elif defined(PQXX_HAVE_C_NAN)
template<typename T> inline void set_to_NaN(T &t) { t = NAN; }
#elif defined(PQXX_HAVE_NAN)
template<> inline void set_to_NaN(float &t) { t = fnan(""); }
template<> inline void set_to_NaN(double &t) { t = nan(""); }
#ifdef PQXX_HAVE_LONG_DOUBLE
template<> inline void set_to_NaN(long double &t) { t = lnan(""); }
#endif
#else
const float nan_f(0.0/0.0);
template<> inline void set_to_NaN(float &t) { t = nan_f; }
const double nan_d(0.0/0.0);
template<> inline void set_to_NaN(double &t) { t = nan_d; }
#ifdef PQXX_HAVE_LONG_DOUBLE
const long double nan_ld(0.0/0.0);
template<> inline void set_to_NaN(long double &t) { t = nan_ld; }
#endif
#endif

// TODO: This may need tweaking for various compilers.
template<typename T> inline void set_to_Inf(T &t, int sign=1)
{
#ifdef PQXX_HAVE_LIMITS
  T value = numeric_limits<T>::infinity();
#else
  T value = INFINITY;
#endif
  if (sign < 0) value = -value;
  t = value;
}

void report_overflow()
{
  throw pqxx::failure(
	"Could not convert string to integer: value out of range.");
}

/** Helper to check for underflow before multiplying a number by 10.
 *
 * Needed just so the compiler doesn't get to complain about an "if (n < 0)"
 * clause that's pointless for unsigned numbers.
 */
template<typename T, bool is_signed> struct underflow_check;

/* Specialization for signed types: check.
 */
template<typename T> struct underflow_check<T, true>
{
  static void check_before_adding_digit(T n)
  {
    const T ten(10);
    if (n < 0 && (numeric_limits<T>::min() / ten) > n) report_overflow();
  }
};

/* Specialization for unsigned types: no check needed becaue negative
 * numbers don't exist.
 */
template<typename T> struct underflow_check<T, false>
{
  static void check_before_adding_digit(T) {}
};

/// Return 10*n, or throw exception if it overflows.
template<typename T> T safe_multiply_by_ten(T n)
{
  const T ten(10);
  if (n > 0 && (numeric_limits<T>::max() / n) < ten) report_overflow();
  underflow_check<T, numeric_limits<T>::is_signed>::check_before_adding_digit(
	n);
  return T(n * ten);
}

/// Add a digit d to n, or throw exception if it overflows.
template<typename T> T safe_add_digit(T n, T d)
{
  assert((n >= 0 && d >= 0) || (n <=0 && d <= 0));
  if ((n > 0) && (n > (numeric_limits<T>::max() - d))) report_overflow();
  if ((n < 0) && (n < (numeric_limits<T>::min() - d))) report_overflow();
  return n + d;
}

/// For use in string parsing: add new numeric digit to intermediate value
template<typename L, typename R>
  inline L absorb_digit(L value, R digit)
{
  return L(safe_multiply_by_ten(value) + L(digit));
}

template<typename T> void from_string_signed(const char Str[], T &Obj)
{
  int i = 0;
  T result = 0;

  if (!isdigit(Str[i]))
  {
    if (Str[i] != '-')
      throw pqxx::failure("Could not convert string to integer: '" +
	string(Str) + "'");

    for (++i; isdigit(Str[i]); ++i)
      result = absorb_digit(result, -digit_to_number(Str[i]));
  }
  else for (; isdigit(Str[i]); ++i)
    result = absorb_digit(result, digit_to_number(Str[i]));

  if (Str[i])
    throw pqxx::failure("Unexpected text after integer: '" + string(Str) + "'");

  Obj = result;
}

template<typename T> void from_string_unsigned(const char Str[], T &Obj)
{
  int i = 0;
  T result = 0;

  if (!isdigit(Str[i]))
    throw pqxx::failure("Could not convert string to unsigned integer: '" +
	string(Str) + "'");

  for (; isdigit(Str[i]); ++i)
    result = absorb_digit(result, digit_to_number(Str[i]));

  if (Str[i])
    throw pqxx::failure("Unexpected text after integer: '" + string(Str) + "'");

  Obj = result;
}

bool valid_infinity_string(const char str[])
{
  // TODO: Also accept less sensible case variations.
  return
	strcmp("infinity", str) == 0 ||
	strcmp("Infinity", str) == 0 ||
	strcmp("INFINITY", str) == 0 ||
	strcmp("inf", str) == 0;
}

/* These are hard.  Sacrifice performance of specialized, nonflexible,
 * non-localized code and lean on standard library.  Some special-case code
 * handles NaNs.
 */
template<typename T> inline void from_string_float(const char Str[], T &Obj)
{
  bool ok = false;
  T result;

  switch (Str[0])
  {
  case 'N':
  case 'n':
    // Accept "NaN," "nan," etc.
    ok = ((Str[1]=='A'||Str[1]=='a') && (Str[2]=='N'||Str[2]=='n') && !Str[3]);
    set_to_NaN(result);
    break;

  case 'I':
  case 'i':
    ok = valid_infinity_string(Str);
    set_to_Inf(result);
    break;

  default:
    if (Str[0] == '-' && valid_infinity_string(&Str[1]))
    {
      ok = true;
      set_to_Inf(result, -1);
    }
    else
    {
      stringstream S(Str);
#if defined(PQXX_HAVE_IMBUE)
      S.imbue(locale("C"));
#endif
      ok = static_cast<bool>(S >> result);
    }
    break;
  }

  if (!ok)
    throw pqxx::failure("Could not convert string to numeric value: '" +
	string(Str) + "'");

  Obj = result;
}

template<typename T> inline string to_string_unsigned(T Obj)
{
  if (!Obj) return "0";

  // Every byte of width on T adds somewhere between 3 and 4 digits to the
  // maximum length of our decimal string.
  char buf[4*sizeof(T)+1];

  char *p = &buf[sizeof(buf)];
  *--p = '\0';
  while (Obj > 0)
  {
    *--p = number_to_digit(int(Obj%10));
    Obj /= 10;
  }
  return p;
}

template<typename T> inline string to_string_fallback(T Obj)
{
  stringstream S;
#ifdef PQXX_HAVE_IMBUE
  S.imbue(locale("C"));
#endif

// Provide enough precision.
#ifdef PQXX_HAVE_LIMITS
  // Kirit reports getting two more digits of precision than
  // numeric_limits::digits10 would give him, so we try not to make him lose
  // those last few bits.
  S.precision(numeric_limits<T>::digits10 + 2);
#else
  // Guess: enough for an IEEE 754 double-precision value.
  S.precision(16);
#endif
  S << Obj;
  return S.str();
}

template<typename T> inline bool is_NaN(T Obj)
{
  return
#if defined(PQXX_HAVE_C_ISNAN)
    isnan(Obj);
#elif defined(PQXX_HAVE_LIMITS)
    !(Obj <= Obj+numeric_limits<T>::max());
#else
    !(Obj <= Obj + 1000);
#endif
}

template<typename T> inline bool is_Inf(T Obj)
{
  return
#if defined(PQXX_HAVE_C_ISINF)
    isinf(Obj);
#else
    Obj >= Obj+1 && Obj <= 2*Obj && Obj >= 2*Obj;
#endif
}

template<typename T> inline string to_string_float(T Obj)
{
  // TODO: Omit this special case if NaN is output as "nan"/"NAN"/"NaN"
#ifndef PQXX_HAVE_NAN_OUTPUT
  if (is_NaN(Obj)) return "nan";
#endif
  // TODO: Omit this special case if infinity is output as "infinity"
#ifndef PQXX_HAVE_INF_OUTPUT
  if (is_Inf(Obj)) return Obj > 0 ? "infinity" : "-infinity";
#endif
  return to_string_fallback(Obj);
}

template<typename T> inline string to_string_signed(T Obj)
{
  if (Obj < 0)
  {
    // Remember--the smallest negative number for a given two's-complement type
    // cannot be negated.
#if PQXX_HAVE_LIMITS
    const bool negatable = (Obj != numeric_limits<T>::min());
#else
    T Neg(-Obj);
    const bool negatable = Neg > 0;
#endif
    if (negatable)
      return '-' + to_string_unsigned(-Obj);
    else
      return to_string_fallback(Obj);
  }

  return to_string_unsigned(Obj);
}

} // namespace

namespace pqxx
{

namespace internal
{
void throw_null_conversion(const PGSTD::string &type)
{
  throw conversion_error("Attempt to convert null to " + type);
}
} // namespace pqxx::internal

void string_traits<bool>::from_string(const char Str[], bool &Obj)
{
  bool OK, result=false;

  switch (Str[0])
  {
  case 0:
    result = false;
    OK = true;
    break;

  case 'f':
  case 'F':
    result = false;
    OK = !(Str[1] &&
	   (strcmp(Str+1, "alse") != 0) &&
	   (strcmp(Str+1, "ALSE") != 0));
    break;

  case '0':
    {
      int I;
      string_traits<int>::from_string(Str, I);
      result = (I != 0);
      OK = ((I == 0) || (I == 1));
    }
    break;

  case '1':
    result = true;
    OK = !Str[1];
    break;

  case 't':
  case 'T':
    result = true;
    OK = !(Str[1] &&
	   (strcmp(Str+1, "rue") != 0) &&
	   (strcmp(Str+1, "RUE") != 0));
    break;

  default:
    OK = false;
  }

  if (!OK)
    throw argument_error("Failed conversion to bool: '" + string(Str) + "'");

  Obj = result;
}

string string_traits<bool>::to_string(bool Obj)
{
  return Obj ? "true" : "false";
}

void string_traits<short>::from_string(const char Str[], short &Obj)
{
  from_string_signed(Str, Obj);
}

string string_traits<short>::to_string(short Obj)
{
  return to_string_signed(Obj);
}

void string_traits<unsigned short>::from_string(
	const char Str[],
	unsigned short &Obj)
{
  from_string_unsigned(Str, Obj);
}

string string_traits<unsigned short>::to_string(unsigned short Obj)
{
  return to_string_unsigned(Obj);
}

void string_traits<int>::from_string(const char Str[], int &Obj)
{
  from_string_signed(Str, Obj);
}

string string_traits<int>::to_string(int Obj)
{
  return to_string_signed(Obj);
}

void string_traits<unsigned int>::from_string(
	const char Str[],
	unsigned int &Obj)
{
  from_string_unsigned(Str, Obj);
}

string string_traits<unsigned int>::to_string(unsigned int Obj)
{
  return to_string_unsigned(Obj);
}

void string_traits<long>::from_string(const char Str[], long &Obj)
{
  from_string_signed(Str, Obj);
}

string string_traits<long>::to_string(long Obj)
{
  return to_string_signed(Obj);
}

void string_traits<unsigned long>::from_string(
	const char Str[],
	unsigned long &Obj)
{
  from_string_unsigned(Str, Obj);
}

string string_traits<unsigned long>::to_string(unsigned long Obj)
{
  return to_string_unsigned(Obj);
}

#ifdef PQXX_HAVE_LONG_LONG
void string_traits<long long>::from_string(const char Str[], long long &Obj)
{
  from_string_signed(Str, Obj);
}

string string_traits<long long>::to_string(long long Obj)
{
  return to_string_signed(Obj);
}

void string_traits<unsigned long long>::from_string(
	const char Str[],
	unsigned long long &Obj)
{
  from_string_unsigned(Str, Obj);
}

string string_traits<unsigned long long>::to_string(unsigned long long Obj)
{
  return to_string_unsigned(Obj);
}
#endif

void string_traits<float>::from_string(const char Str[], float &Obj)
{
  from_string_float(Str, Obj);
}

string string_traits<float>::to_string(float Obj)
{
  return to_string_float(Obj);
}

void string_traits<double>::from_string(const char Str[], double &Obj)
{
  from_string_float(Str, Obj);
}

string string_traits<double>::to_string(double Obj)
{
  return to_string_float(Obj);
}

#ifdef PQXX_HAVE_LONG_DOUBLE
void string_traits<long double>::from_string(const char Str[], long double &Obj)
{
  from_string_float(Str, Obj);
}

string string_traits<long double>::to_string(long double Obj)
{
  return to_string_float(Obj);
}
#endif

} // namespace pqxx
<|endoftext|>