<commit_before>/*
 *
 *  Resource loader, functions for loading all supported data types
 *      - Loads scene information from json
 *      - Loads textures form TGA's
 *      - Loads models from 3ds
 *
 *  TODO Change all functions to throw errors instead of return values
 *
 */

#include "resourceloader.hpp"

#include <glm/glm.hpp>
#include <FreeImage.h>
#include <memory>
#include <vector>
#include <fstream>
#include <iostream>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <json.hpp>

#include <Utilities/utilities.hpp>
#include <Core/glapi.hpp>
#include <Resources/model3d.hpp>
#include <Models/line.hpp>
#include <Models/staticmodel.hpp>

using namespace gl;
using namespace Utilities;
using json = nlohmann::json;

std::shared_ptr<IRenderObject> LoadStaticModelJSON(json object) {
    std::string type = "";

    try {
        type = object["datatype"];
    } catch (std::domain_error& error) {
        type = "normal";
    }

    if(type == "normal") {
        return std::make_shared<StaticModel>(ResourceLoader::LoadModel3D(object["filename"]));
    } else if(type == "raw") {
        std::vector<glm::vec3> verts;
        std::vector<std::vector<float>> vertsf = object["vertices"];
        for(std::vector<float> vert : vertsf) {
            if(vert.size() != 3) {
                continue;
            }
            verts.push_back(glm::vec3(vert[0], vert[1], vert[2]));
        }

        std::vector<GLuint> indices;
        std::vector<uint> indicesf = object["indices"];
        for(uint index : indicesf) {
            indices.push_back(index);
        }

        std::vector<glm::vec3> norms;
        try {
            std::vector<std::vector<float>> normsf = object["normals"];
            for(std::vector<float> norm : normsf) {
                if(norm.size() != 3) {
                    continue;
                }
                norms.push_back(glm::vec3(norm[0], norm[1], norm[2]));
            }
        } catch (std::domain_error& error) {
            for(uint i = 0; i < verts.size(); i++) {
                norms.push_back(glm::vec3(0.0f));
            }
        }

        std::vector<glm::vec2> uvs;
        try {
            std::vector<std::vector<float>> uvsf = object["uvs"];
            for(std::vector<float> uv : uvsf) {
                if(uv.size() != 2) {
                    continue;
                }
                uvs.push_back(glm::vec2(uv[0], uv[1]));
            }
        } catch (std::domain_error& error) {
            for(uint i = 0; i < verts.size(); i++) {
                uvs.push_back(glm::vec2(0.0f));
            }
        }

        std::vector<Model3D::Vertex> vertices;
        for(uint i = 0; i < verts.size(); i++) {
            vertices.push_back(Model3D::Vertex(verts[i], norms[i], uvs[i]));
        }

        std::vector<std::shared_ptr<Model3D::Mesh>> meshes;
        meshes.push_back(std::make_shared<Model3D::Mesh>(vertices, indices));
        meshes[0]->SetMatName("texture");

        std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
        textures["texture"] = ResourceLoader::LoadTexture(object["matname"], true);
        return std::make_shared<StaticModel>(std::make_shared<Model3D>(meshes, textures));
    } else {
        throw Exception("Unknown data type");
    }
}

std::shared_ptr<IRenderObject> LoadLineJSON(json object) {
    glm::vec3 head, tail, color;

    std::vector<float> head3f = object["head"];
    if(head3f.size() == 3) {
        head = glm::vec3(head3f[0], head3f[1], head3f[2]);
    } else {
        throw Exception("head element size is incorrect");
    }

    std::vector<float> tail3f = object["tail"];
    if(tail3f.size() == 3) {
        tail = glm::vec3(tail3f[0], tail3f[1], tail3f[2]);
    } else {
        throw Exception("tail element size is incorrect");
    }

    std::vector<float> color3f = object["color"];
    if(head3f.size() == 3) {
        color = glm::vec3(color3f[0], color3f[1], color3f[2]);
    } else {
        throw Exception("color element size is incorrect");
        return nullptr;
    }

    return std::make_shared<Line>(head, tail, color);
}

std::vector<std::shared_ptr<IRenderObject>> ResourceLoader::LoadScene(std::string filename) {
    std::vector<std::shared_ptr<IRenderObject>> renderObjects;

    std::string contents;
    std::ifstream in(filename, std::ios::in | std::ios::binary);
    if (in) {
        in.seekg(0, std::ios::end);
        contents.resize(in.tellg());
        in.seekg(0, std::ios::beg);
        in.read(&contents[0], contents.size());
        in.close();
    } else {
        throw Exception("Couldn't load scene " + filename);
    }

    try {
        json scene = json::parse(contents);
        std::vector<json> objects = scene["staticObjects"];

        for(json object : objects) {
            std::string type = "";
            std::string name = "";

            try {
                try {
                    name = object["name"];
                } catch (std::domain_error& error) {
                    name = "null";
                }

                type = object["type"];

                std::shared_ptr<IRenderObject> rObject;
                if(type == "line") {
                    rObject = LoadLineJSON(object);
                } else if(type == "staticmodel") {
                    rObject = LoadStaticModelJSON(object);
                }
                if(rObject != nullptr) {
                    renderObjects.push_back(rObject);
                }
            } catch (Exception& error) {
                std::cout << name << " : " << error.what() << std::endl;
            }
        }
    } catch(std::exception& error) {
        std::cout << "Parsing of " << filename << " failed : " << error.what() << std::endl;
    }

    return renderObjects;
}

std::shared_ptr<Texture> ResourceLoader::LoadTexture(std::string filename, bool genMipMaps) {
    FIBITMAP *img;
    filename = "./Textures/" + filename + ".tga";
    FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(filename.c_str());

    if(!FreeImage_FIFSupportsReading(format)) {
        throw Exception("FreeImage can't read from file: " + filename);
    }

    if(format == FIF_UNKNOWN) {
        throw Exception("Unknown format for file: " + filename);
    }

    img = FreeImage_Load(format, filename.c_str());

    if(!img) {
        throw Exception("Couldn't load image data for file: " + filename);
    }

    if(FreeImage_GetBPP(img) != 32) {
        FIBITMAP* oldImg = img;
        img = FreeImage_ConvertTo32Bits(oldImg);
        FreeImage_Unload(oldImg);
    }

    int height, width;
    width = FreeImage_GetWidth(img);
    height = FreeImage_GetHeight(img);

    unsigned char* bytes = FreeImage_GetBits(img);

    if(bytes == nullptr) {
        FreeImage_Unload(img);
        throw Exception("couldn't load image bytes for file: " + filename);
    }

    GLuint glTexture;
    glBindTexture(GL_TEXTURE_2D, 0);
    glGenTextures(1, &glTexture);
    glBindTexture(GL_TEXTURE_2D, glTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, bytes);

    if(genMipMaps) {
        glGenerateMipmap(GL_TEXTURE_2D);
    }

    glBindTexture(GL_TEXTURE_2D, 0);

    if(!glIsTexture(glTexture)) {
        FreeImage_Unload(img);
        throw Exception("texture is not valid for file: " + filename);
    }

    FreeImage_Unload(img);

    std::shared_ptr<Texture> texture = std::make_shared<Texture>();
    texture->SetFileName(filename);
    texture->SetTexture(glTexture);
    return texture;
}

void CopyaiMat(const aiMatrix4x4* from, glm::mat4& to) {
    to[0][0] = from->a1; to[1][0] = from->a2;
    to[2][0] = from->a3; to[3][0] = from->a4;
    to[0][1] = from->b1; to[1][1] = from->b2;
    to[2][1] = from->b3; to[3][1] = from->b4;
    to[0][2] = from->c1; to[1][2] = from->c2;
    to[2][2] = from->c3; to[3][2] = from->c4;
    to[0][3] = from->d1; to[1][3] = from->d2;
    to[2][3] = from->d3; to[3][3] = from->d4;
}

void LoadNode(const aiScene* scene, const aiNode* node, std::vector<std::shared_ptr<Model3D::Mesh>>& meshes) {
    for(uint i = 0; i < node->mNumChildren; i++) {
        LoadNode(scene, node->mChildren[i], meshes);
    }

    for(uint i = 0; i < node->mNumMeshes; i++) {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        std::vector<Model3D::Vertex> vertices;

        for(uint j = 0; j < mesh->mNumVertices; j++) {
            Model3D::Vertex vertex;

            vertex.position = glm::vec3(mesh->mVertices[j].x, mesh->mVertices[j].y, mesh->mVertices[j].z);

            vertex.normal = (mesh->HasNormals())
                ? glm::vec3(mesh->mNormals[j].x, mesh->mNormals[j].y, mesh->mNormals[j].z)
                : glm::vec3(0.0f, 0.0f, 0.0f);

            vertex.texCoord = (mesh->HasTextureCoords(0))
                ? glm::vec2(mesh->mTextureCoords[0][j].y, mesh->mTextureCoords[0][j].y)
                : glm::vec2(0.0f, 0.0f);

            vertices.push_back(vertex);
        }

        std::vector<uint> indices;
        if(mesh->HasFaces()) {
            for(uint j = 0; j < mesh->mNumFaces; j++) {
                aiFace face = mesh->mFaces[j];
                for(uint k = 0; k < face.mNumIndices; k++) {
                    indices.push_back(face.mIndices[k]);
                }
            }
        } else {
            std::cout << "Node was missing faces, load cancled" << std::endl;
        }

        glm::mat4x4 transformation;
        CopyaiMat(&node->mTransformation, transformation);
        std::shared_ptr<Model3D::Mesh> rmesh = std::make_shared<Model3D::Mesh>(vertices, indices);
        rmesh->SetTransformation(transformation);
        rmesh->SetMatIndex(mesh->mMaterialIndex);

        meshes.push_back(rmesh);
    }
}

std::shared_ptr<Model3D> ResourceLoader::LoadModel3D(std::string modelname) {
    std::string filename = "Models/" + modelname + ".3ds";
    Assimp::Importer importer;
    const aiScene* scene = importer.ReadFile(filename.c_str(),
        aiProcess_CalcTangentSpace |
        aiProcess_Triangulate |
        aiProcess_JoinIdenticalVertices |
        aiProcess_SortByPType);

    if(!scene || !scene->mRootNode || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE) {
        std::cout << "Couldn't open model: " << filename << std::endl;
        std::cout << "Assimp: " << importer.GetErrorString() << std::endl;
        return nullptr;
    }

    auto model = std::make_shared<Model3D>();

    aiMaterial** materials = scene->mMaterials;
    std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
    for(uint i = 0; i < scene->mNumMaterials; i++) {
        aiString aName;
        materials[i]->Get(AI_MATKEY_NAME, aName);
        std::string name = std::string(aName.C_Str());

        if(textures.find(name) == textures.end()) {
            try {
                std::shared_ptr<Texture> temp = LoadTexture(modelname + "/" + name, true);
                if(temp != nullptr) {
                    textures[name] = temp;
                }
            } catch (Exception& error) {
                error.PrintError();
            }
        }
    }
    model->SetTextures(textures);

    std::vector<std::shared_ptr<Model3D::Mesh>> meshes;
    LoadNode(scene, scene->mRootNode, meshes);

    for(std::shared_ptr<Model3D::Mesh>& mesh : meshes) {
        aiString aName;
        uint index = mesh->GetMatIndex();
        if(index < scene->mNumMaterials) {
            materials[mesh->GetMatIndex()]->Get(AI_MATKEY_NAME, aName);
            mesh->SetMatName(std::string(aName.C_Str()));
        }
    }

    model->SetMeshes(meshes);

    return model;
}
<commit_msg>fixed a bug<commit_after>/*
 *
 *  Resource loader, functions for loading all supported data types
 *      - Loads scene information from json
 *      - Loads textures form TGA's
 *      - Loads models from 3ds
 *
 *  TODO Change all functions to throw errors instead of return values
 *
 */

#include "resourceloader.hpp"

#include <glm/glm.hpp>
#include <FreeImage.h>
#include <memory>
#include <vector>
#include <fstream>
#include <iostream>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <json.hpp>

#include <Utilities/utilities.hpp>
#include <Core/glapi.hpp>
#include <Resources/model3d.hpp>
#include <Models/line.hpp>
#include <Models/staticmodel.hpp>

using namespace gl;
using namespace Utilities;
using json = nlohmann::json;

std::shared_ptr<IRenderObject> LoadStaticModelJSON(json object) {
    std::string type = "";

    try {
        type = object["datatype"];
    } catch (std::domain_error& error) {
        type = "normal";
    }

    if(type == "normal") {
        return std::make_shared<StaticModel>(ResourceLoader::LoadModel3D(object["filename"]));
    } else if(type == "raw") {
        std::vector<glm::vec3> verts;
        std::vector<std::vector<float>> vertsf = object["vertices"];
        for(std::vector<float> vert : vertsf) {
            if(vert.size() != 3) {
                continue;
            }
            verts.push_back(glm::vec3(vert[0], vert[1], vert[2]));
        }

        std::vector<GLuint> indices;
        std::vector<uint> indicesf = object["indices"];
        for(uint index : indicesf) {
            indices.push_back(index);
        }

        std::vector<glm::vec3> norms;
        try {
            std::vector<std::vector<float>> normsf = object["normals"];
            for(std::vector<float> norm : normsf) {
                if(norm.size() != 3) {
                    continue;
                }
                norms.push_back(glm::vec3(norm[0], norm[1], norm[2]));
            }
        } catch (std::domain_error& error) {
            for(uint i = 0; i < verts.size(); i++) {
                norms.push_back(glm::vec3(0.0f));
            }
        }

        std::vector<glm::vec2> uvs;
        try {
            std::vector<std::vector<float>> uvsf = object["uvs"];
            for(std::vector<float> uv : uvsf) {
                if(uv.size() != 2) {
                    continue;
                }
                uvs.push_back(glm::vec2(uv[0], uv[1]));
            }
        } catch (std::domain_error& error) {
            for(uint i = 0; i < verts.size(); i++) {
                uvs.push_back(glm::vec2(0.0f));
            }
        }

        std::vector<Model3D::Vertex> vertices;
        for(uint i = 0; i < verts.size(); i++) {
            vertices.push_back(Model3D::Vertex(verts[i], norms[i], uvs[i]));
        }

        std::vector<std::shared_ptr<Model3D::Mesh>> meshes;
        meshes.push_back(std::make_shared<Model3D::Mesh>(vertices, indices));
        meshes[0]->SetMatName("texture");

        std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
        textures["texture"] = ResourceLoader::LoadTexture(object["matname"], true);
        return std::make_shared<StaticModel>(std::make_shared<Model3D>(meshes, textures));
    } else {
        throw Exception("Unknown data type");
    }
}

std::shared_ptr<IRenderObject> LoadLineJSON(json object) {
    glm::vec3 head, tail, color;

    std::vector<float> head3f = object["head"];
    if(head3f.size() == 3) {
        head = glm::vec3(head3f[0], head3f[1], head3f[2]);
    } else {
        throw Exception("head element size is incorrect");
    }

    std::vector<float> tail3f = object["tail"];
    if(tail3f.size() == 3) {
        tail = glm::vec3(tail3f[0], tail3f[1], tail3f[2]);
    } else {
        throw Exception("tail element size is incorrect");
    }

    std::vector<float> color3f = object["color"];
    if(head3f.size() == 3) {
        color = glm::vec3(color3f[0], color3f[1], color3f[2]);
    } else {
        throw Exception("color element size is incorrect");
        return nullptr;
    }

    return std::make_shared<Line>(head, tail, color);
}

std::vector<std::shared_ptr<IRenderObject>> ResourceLoader::LoadScene(std::string filename) {
    std::vector<std::shared_ptr<IRenderObject>> renderObjects;

    std::string contents;
    std::ifstream in(filename, std::ios::in | std::ios::binary);
    if (in) {
        in.seekg(0, std::ios::end);
        contents.resize(in.tellg());
        in.seekg(0, std::ios::beg);
        in.read(&contents[0], contents.size());
        in.close();
    } else {
        throw Exception("Couldn't load scene " + filename);
    }

    try {
        json scene = json::parse(contents);
        std::vector<json> objects = scene["staticObjects"];

        for(json object : objects) {
            std::string type = "";
            std::string name = "";

            try {
                try {
                    name = object["name"];
                } catch (std::domain_error& error) {
                    name = "null";
                }

                type = object["type"];

                std::shared_ptr<IRenderObject> rObject;
                if(type == "line") {
                    rObject = LoadLineJSON(object);
                } else if(type == "staticmodel") {
                    rObject = LoadStaticModelJSON(object);
                }
                if(rObject != nullptr) {
                    renderObjects.push_back(rObject);
                }
            } catch (Exception& error) {
                std::cout << name << " : " << error.what() << std::endl;
            }
        }
    } catch(std::exception& error) {
        std::cout << "Parsing of " << filename << " failed : " << error.what() << std::endl;
    }

    return renderObjects;
}

std::shared_ptr<Texture> ResourceLoader::LoadTexture(std::string filename, bool genMipMaps) {
    FIBITMAP *img;
    filename = "./Textures/" + filename + ".tga";
    FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(filename.c_str());

    if(!FreeImage_FIFSupportsReading(format)) {
        throw Exception("FreeImage can't read from file: " + filename);
    }

    if(format == FIF_UNKNOWN) {
        throw Exception("Unknown format for file: " + filename);
    }

    img = FreeImage_Load(format, filename.c_str());

    if(!img) {
        throw Exception("Couldn't load image data for file: " + filename);
    }

    if(FreeImage_GetBPP(img) != 32) {
        FIBITMAP* oldImg = img;
        img = FreeImage_ConvertTo32Bits(oldImg);
        FreeImage_Unload(oldImg);
    }

    int height, width;
    width = FreeImage_GetWidth(img);
    height = FreeImage_GetHeight(img);

    unsigned char* bytes = FreeImage_GetBits(img);

    if(bytes == nullptr) {
        FreeImage_Unload(img);
        throw Exception("couldn't load image bytes for file: " + filename);
    }

    GLuint glTexture;
    glBindTexture(GL_TEXTURE_2D, 0);
    glGenTextures(1, &glTexture);
    glBindTexture(GL_TEXTURE_2D, glTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, bytes);

    if(genMipMaps) {
        glGenerateMipmap(GL_TEXTURE_2D);
    }

    glBindTexture(GL_TEXTURE_2D, 0);

    if(!glIsTexture(glTexture)) {
        FreeImage_Unload(img);
        throw Exception("texture is not valid for file: " + filename);
    }

    FreeImage_Unload(img);

    std::shared_ptr<Texture> texture = std::make_shared<Texture>();
    texture->SetFileName(filename);
    texture->SetTexture(glTexture);
    return texture;
}

void CopyaiMat(const aiMatrix4x4* from, glm::mat4& to) {
    to[0][0] = from->a1; to[1][0] = from->a2;
    to[2][0] = from->a3; to[3][0] = from->a4;
    to[0][1] = from->b1; to[1][1] = from->b2;
    to[2][1] = from->b3; to[3][1] = from->b4;
    to[0][2] = from->c1; to[1][2] = from->c2;
    to[2][2] = from->c3; to[3][2] = from->c4;
    to[0][3] = from->d1; to[1][3] = from->d2;
    to[2][3] = from->d3; to[3][3] = from->d4;
}

void LoadNode(const aiScene* scene, const aiNode* node, std::vector<std::shared_ptr<Model3D::Mesh>>& meshes) {
    for(uint i = 0; i < node->mNumChildren; i++) {
        LoadNode(scene, node->mChildren[i], meshes);
    }

    for(uint i = 0; i < node->mNumMeshes; i++) {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        std::vector<Model3D::Vertex> vertices;

        for(uint j = 0; j < mesh->mNumVertices; j++) {
            Model3D::Vertex vertex;

            vertex.position = glm::vec3(mesh->mVertices[j].x, mesh->mVertices[j].y, mesh->mVertices[j].z);

            vertex.normal = (mesh->HasNormals())
                ? glm::vec3(mesh->mNormals[j].x, mesh->mNormals[j].y, mesh->mNormals[j].z)
                : glm::vec3(0.0f, 0.0f, 0.0f);

            vertex.texCoord = (mesh->HasTextureCoords(0))
                ? glm::vec2(mesh->mTextureCoords[0][j].y, mesh->mTextureCoords[0][j].y)
                : glm::vec2(0.0f, 0.0f);

            vertices.push_back(vertex);
        }

        std::vector<uint> indices;
        if(mesh->HasFaces()) {
            for(uint j = 0; j < mesh->mNumFaces; j++) {
                aiFace face = mesh->mFaces[j];
                for(uint k = 0; k < face.mNumIndices; k++) {
                    indices.push_back(face.mIndices[k]);
                }
            }
        } else {
            throw Exception("Node was missing faces, load cancled");
        }

        glm::mat4x4 transformation;
        CopyaiMat(&node->mTransformation, transformation);
        std::shared_ptr<Model3D::Mesh> rmesh = std::make_shared<Model3D::Mesh>(vertices, indices);
        rmesh->SetTransformation(transformation);
        rmesh->SetMatIndex(mesh->mMaterialIndex);

        meshes.push_back(rmesh);
    }
}

std::shared_ptr<Model3D> ResourceLoader::LoadModel3D(std::string modelname) {
    std::string filename = "Models/" + modelname + ".3ds";
    Assimp::Importer importer;
    const aiScene* scene = importer.ReadFile(filename.c_str(),
        aiProcess_CalcTangentSpace |
        aiProcess_Triangulate |
        aiProcess_JoinIdenticalVertices |
        aiProcess_SortByPType);

    if(!scene || !scene->mRootNode || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE) {
        std::cout << "Couldn't open model: " << filename << std::endl;
        std::cout << "Assimp: " << importer.GetErrorString() << std::endl;
        return nullptr;
    }

    auto model = std::make_shared<Model3D>();

    aiMaterial** materials = scene->mMaterials;
    std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
    for(uint i = 0; i < scene->mNumMaterials; i++) {
        aiString aName;
        materials[i]->Get(AI_MATKEY_NAME, aName);
        std::string name = std::string(aName.C_Str());

        if(textures.find(name) == textures.end()) {
            try {
                std::shared_ptr<Texture> temp = LoadTexture(modelname + "/" + name, true);
                if(temp != nullptr) {
                    textures[name] = temp;
                }
            } catch (Exception& error) {
                error.PrintError();
            }
        }
    }
    model->SetTextures(textures);

    std::vector<std::shared_ptr<Model3D::Mesh>> meshes;
    LoadNode(scene, scene->mRootNode, meshes);

    for(std::shared_ptr<Model3D::Mesh>& mesh : meshes) {
        aiString aName;
        uint index = mesh->GetMatIndex();
        if(index < scene->mNumMaterials) {
            materials[mesh->GetMatIndex()]->Get(AI_MATKEY_NAME, aName);
            mesh->SetMatName(std::string(aName.C_Str()));
        }
    }

    model->SetMeshes(meshes);

    return model;
}
<|endoftext|>


<commit_before>#ifdef _WIN32
#include "Socket.h"
#include "System/Log.h"
#include "System/Common.h"
#include "System/Platform.h"

#include <winsock2.h>

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif

unsigned long iftonl(const char* interface_);

/*
 * The Socket implementation is tightly coupled with the
 * IOProcessor because Windows asynchronous mechanism and
 * the IO completion uses the same Windows specific OVERLAPPED
 * structures, therefore these functions are imported here.
 */
bool IOProcessorRegisterSocket(FD& fd);
bool IOProcessorUnregisterSocket(FD& fd);
bool IOProcessorAccept(const FD& listeningFd, FD& fd);
bool IOProcessorConnect(FD& fd, Endpoint& endpoint);
extern unsigned SEND_BUFFER_SIZE;

Socket::Socket()
{
    fd = INVALID_FD;
    listening = false;
}

bool Socket::Create()
{
    BOOL    trueval = TRUE;

    if (fd.handle != INVALID_SOCKET)
    {
        Log_Trace("Called Create() on existing socket");
        return false;
    }

    listening = false;

    // create the socket with WSA_FLAG_OVERLAPPED to support async operations
    fd.handle = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (fd.handle == INVALID_SOCKET)
        return false;

    if (setsockopt(fd.handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&trueval, sizeof(BOOL)))
    {
        Log_Errno();
        Close();
        return false;
    }

    if (setsockopt(fd.handle, SOL_SOCKET, SO_SNDBUF, (char *) &SEND_BUFFER_SIZE, sizeof(SEND_BUFFER_SIZE)))
    {
        Log_Errno();
        Close();
        return false;
    }

    // set FD index too
    IOProcessorRegisterSocket(fd);

    return true;
}

bool Socket::Bind(int port)
{
    int                 ret;
    struct sockaddr_in  sa;

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons((uint16_t)port);
    sa.sin_addr.s_addr = htonl(INADDR_ANY);

    ret = bind(fd.handle, (struct sockaddr *)&sa, sizeof(struct sockaddr_in));
    if (ret < 0)
    {
        Log_Errno();
        Close();
        return false;
    }

    return true;
}

bool Socket::SetNonblocking()
{
    u_long  nonblocking;

    if (fd.handle == INVALID_SOCKET)
    {
        Log_Trace("SetNonblocking on invalid file descriptor");
        return false;
    }

    nonblocking = 1;
    if (ioctlsocket(fd.handle, FIONBIO, &nonblocking) == SOCKET_ERROR)
        return false;

    return true;
}

bool Socket::SetNodelay()
{
    BOOL    nodelay;

    if (fd.handle == INVALID_SOCKET)
    {
        Log_Trace("SetNodelay on invalid file descriptor");
        return false;
    }

    // Nagle algorithm is disabled if TCP_NODELAY is enabled.
    nodelay = TRUE;
    if (setsockopt(fd.handle, IPPROTO_TCP, TCP_NODELAY, (char *) &nodelay, sizeof(nodelay)) == SOCKET_ERROR)
    {
        Log_Trace("setsockopt() failed");
        return false;
    }

    return true;

}

bool Socket::Listen(int port, int backlog)
{
    int ret;

    if (!Bind(port))
        return false;

    ret = listen(fd.handle, backlog);
    if (ret < 0)
    {
        Log_Errno();
        Close();
        return false;
    }

    listening = true;

    return true;
}

bool Socket::Accept(Socket *newSocket)
{
    if (!IOProcessorAccept(fd, newSocket->fd))
    {
        Log_Errno();
        Close();
        return false;
    }

    // register the newly created socket
    if (!IOProcessorRegisterSocket(newSocket->fd))
    {
        Log_Message("Ran out of IO connections, please increase io.maxfd in config file!");
        return false;
    }

    return true;
}

bool Socket::Connect(Endpoint &endpoint)
{
    if (!IOProcessorConnect(fd, endpoint))
    {
        Log_Errno();
        return false;
    }

    return true;
}

bool Socket::GetEndpoint(Endpoint &endpoint)
{
    int                 ret;
    int                 len = ENDPOINT_SOCKADDR_SIZE;
    struct sockaddr*    sa = (struct sockaddr*) endpoint.GetSockAddr();

    ret = getpeername(fd.handle, sa, &len);

    if (ret == SOCKET_ERROR)
    {
        Log_Errno();
        Close();
        return false;
    }

    return true;
}

const char* Socket::ToString(char s[ENDPOINT_STRING_SIZE])
{
    Endpoint    endpoint;

    if (!GetEndpoint(endpoint))
        return "";

    return endpoint.ToString(s);
}

bool Socket::SendTo(void *data, int count, const Endpoint &endpoint)
{
    int                     ret;
    const struct sockaddr*  sa = (const struct sockaddr*) ((Endpoint &) endpoint).GetSockAddr();

    ret = sendto(fd.handle, (const char*) data, count, 0,
                 sa,
                 ENDPOINT_SOCKADDR_SIZE);

    if (ret < 0)
    {
        Log_Errno();
        return false;
    }

    return true;
}

int Socket::Send(const char* data, int count, int /*timeout*/)
{
    size_t      left;
    int         nwritten;

    left = count;
    while (left > 0)
    {
        if ((nwritten = send((SOCKET) fd.handle, (char*) data, count, 0)) == SOCKET_ERROR)
        {
            // TODO: timeout and error handling
            if (WSAGetLastError() == WSAEWOULDBLOCK)
                return 0;

            return -1;
        }

        left -= nwritten;
        data += nwritten;
    }

    return count;
}

int Socket::Read(char* data, int count, int /*timeout*/)
{
    int     ret;

    ret = recv((SOCKET)fd.handle, (char *)data, count, 0);
    // TODO: timeout and better error handling
    if (ret == SOCKET_ERROR)
    {
        if (WSAGetLastError() == WSAEWOULDBLOCK)
            return 0;
        return -1;
    }
    else if (ret == 0)
    {
        // graceful disconnection
        return -1;
    }
    return ret;
}

void Socket::Close()
{
    int ret;

    if (fd.handle != INVALID_SOCKET)
    {
        IOProcessorUnregisterSocket(fd);
        ret = closesocket(fd.handle);

        if (ret < 0)
            Log_Errno();

        fd.handle = INVALID_SOCKET;
        fd.index = -1;
    }
}

unsigned long iftonl(const char* interface_)
{
    int pos;
    int len;
    unsigned long a;
    unsigned long b;
    unsigned long c;
    unsigned long d;
    unsigned long addr;
    unsigned nread;

    nread = 0;
    pos = 0;
    len = strlen(interface_);

    a = BufferToUInt64(interface_ + pos, len - pos, &nread);
    if (nread < 0 || a > 255)
        return INADDR_NONE;

    pos += nread;
    if (interface_[pos++] != '.')
        return INADDR_NONE;

    b = BufferToUInt64(interface_ + pos, len - pos, &nread);
    if (nread < 0 || b > 255)
        return INADDR_NONE;

    pos += nread;
    if (interface_[pos++] != '.')
        return INADDR_NONE;

    c = BufferToUInt64(interface_ + pos, len - pos, &nread);
    if (nread < 0 || c > 255)
        return INADDR_NONE;

    pos += nread;
    if (interface_[pos++] != '.')
        return INADDR_NONE;

    d = BufferToUInt64(interface_ + pos, len - pos, &nread);
    if (nread < 0 || d > 255)
        return INADDR_NONE;

    pos += nread;
    if (interface_[pos] != '\0' &&
        interface_[pos] != ':')
        return INADDR_NONE;

    addr = (d & 0xff) << 24 | (c & 0xff) << 16 | (b & 0xff) << 8 | (a & 0xff);
    return addr;
}

#endif
<commit_msg>Fixed error handling in iftonl.<commit_after>#ifdef _WIN32
#include "Socket.h"
#include "System/Log.h"
#include "System/Common.h"
#include "System/Platform.h"

#include <winsock2.h>

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif

unsigned long iftonl(const char* interface_);

/*
 * The Socket implementation is tightly coupled with the
 * IOProcessor because Windows asynchronous mechanism and
 * the IO completion uses the same Windows specific OVERLAPPED
 * structures, therefore these functions are imported here.
 */
bool IOProcessorRegisterSocket(FD& fd);
bool IOProcessorUnregisterSocket(FD& fd);
bool IOProcessorAccept(const FD& listeningFd, FD& fd);
bool IOProcessorConnect(FD& fd, Endpoint& endpoint);
extern unsigned SEND_BUFFER_SIZE;

Socket::Socket()
{
    fd = INVALID_FD;
    listening = false;
}

bool Socket::Create()
{
    BOOL    trueval = TRUE;

    if (fd.handle != INVALID_SOCKET)
    {
        Log_Trace("Called Create() on existing socket");
        return false;
    }

    listening = false;

    // create the socket with WSA_FLAG_OVERLAPPED to support async operations
    fd.handle = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (fd.handle == INVALID_SOCKET)
        return false;

    if (setsockopt(fd.handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&trueval, sizeof(BOOL)))
    {
        Log_Errno();
        Close();
        return false;
    }

    if (setsockopt(fd.handle, SOL_SOCKET, SO_SNDBUF, (char *) &SEND_BUFFER_SIZE, sizeof(SEND_BUFFER_SIZE)))
    {
        Log_Errno();
        Close();
        return false;
    }

    // set FD index too
    IOProcessorRegisterSocket(fd);

    return true;
}

bool Socket::Bind(int port)
{
    int                 ret;
    struct sockaddr_in  sa;

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons((uint16_t)port);
    sa.sin_addr.s_addr = htonl(INADDR_ANY);

    ret = bind(fd.handle, (struct sockaddr *)&sa, sizeof(struct sockaddr_in));
    if (ret < 0)
    {
        Log_Errno();
        Close();
        return false;
    }

    return true;
}

bool Socket::SetNonblocking()
{
    u_long  nonblocking;

    if (fd.handle == INVALID_SOCKET)
    {
        Log_Trace("SetNonblocking on invalid file descriptor");
        return false;
    }

    nonblocking = 1;
    if (ioctlsocket(fd.handle, FIONBIO, &nonblocking) == SOCKET_ERROR)
        return false;

    return true;
}

bool Socket::SetNodelay()
{
    BOOL    nodelay;

    if (fd.handle == INVALID_SOCKET)
    {
        Log_Trace("SetNodelay on invalid file descriptor");
        return false;
    }

    // Nagle algorithm is disabled if TCP_NODELAY is enabled.
    nodelay = TRUE;
    if (setsockopt(fd.handle, IPPROTO_TCP, TCP_NODELAY, (char *) &nodelay, sizeof(nodelay)) == SOCKET_ERROR)
    {
        Log_Trace("setsockopt() failed");
        return false;
    }

    return true;

}

bool Socket::Listen(int port, int backlog)
{
    int ret;

    if (!Bind(port))
        return false;

    ret = listen(fd.handle, backlog);
    if (ret < 0)
    {
        Log_Errno();
        Close();
        return false;
    }

    listening = true;

    return true;
}

bool Socket::Accept(Socket *newSocket)
{
    if (!IOProcessorAccept(fd, newSocket->fd))
    {
        Log_Errno();
        Close();
        return false;
    }

    // register the newly created socket
    if (!IOProcessorRegisterSocket(newSocket->fd))
    {
        Log_Message("Ran out of IO connections, please increase io.maxfd in config file!");
        return false;
    }

    return true;
}

bool Socket::Connect(Endpoint &endpoint)
{
    if (!IOProcessorConnect(fd, endpoint))
    {
        Log_Errno();
        return false;
    }

    return true;
}

bool Socket::GetEndpoint(Endpoint &endpoint)
{
    int                 ret;
    int                 len = ENDPOINT_SOCKADDR_SIZE;
    struct sockaddr*    sa = (struct sockaddr*) endpoint.GetSockAddr();

    ret = getpeername(fd.handle, sa, &len);

    if (ret == SOCKET_ERROR)
    {
        Log_Errno();
        Close();
        return false;
    }

    return true;
}

const char* Socket::ToString(char s[ENDPOINT_STRING_SIZE])
{
    Endpoint    endpoint;

    if (!GetEndpoint(endpoint))
        return "";

    return endpoint.ToString(s);
}

bool Socket::SendTo(void *data, int count, const Endpoint &endpoint)
{
    int                     ret;
    const struct sockaddr*  sa = (const struct sockaddr*) ((Endpoint &) endpoint).GetSockAddr();

    ret = sendto(fd.handle, (const char*) data, count, 0,
                 sa,
                 ENDPOINT_SOCKADDR_SIZE);

    if (ret < 0)
    {
        Log_Errno();
        return false;
    }

    return true;
}

int Socket::Send(const char* data, int count, int /*timeout*/)
{
    size_t      left;
    int         nwritten;

    left = count;
    while (left > 0)
    {
        if ((nwritten = send((SOCKET) fd.handle, (char*) data, count, 0)) == SOCKET_ERROR)
        {
            // TODO: timeout and error handling
            if (WSAGetLastError() == WSAEWOULDBLOCK)
                return 0;

            return -1;
        }

        left -= nwritten;
        data += nwritten;
    }

    return count;
}

int Socket::Read(char* data, int count, int /*timeout*/)
{
    int     ret;

    ret = recv((SOCKET)fd.handle, (char *)data, count, 0);
    // TODO: timeout and better error handling
    if (ret == SOCKET_ERROR)
    {
        if (WSAGetLastError() == WSAEWOULDBLOCK)
            return 0;
        return -1;
    }
    else if (ret == 0)
    {
        // graceful disconnection
        return -1;
    }
    return ret;
}

void Socket::Close()
{
    int ret;

    if (fd.handle != INVALID_SOCKET)
    {
        IOProcessorUnregisterSocket(fd);
        ret = closesocket(fd.handle);

        if (ret < 0)
            Log_Errno();

        fd.handle = INVALID_SOCKET;
        fd.index = -1;
    }
}

unsigned long iftonl(const char* interface_)
{
    int pos;
    int len;
    unsigned long a;
    unsigned long b;
    unsigned long c;
    unsigned long d;
    unsigned long addr;
    unsigned nread;

    nread = 0;
    pos = 0;
    len = strlen(interface_);

    a = BufferToUInt64(interface_ + pos, len - pos, &nread);
    if (nread == 0 || a > 255)
        return INADDR_NONE;

    pos += nread;
    if (interface_[pos++] != '.')
        return INADDR_NONE;

    b = BufferToUInt64(interface_ + pos, len - pos, &nread);
    if (nread == 0 || b > 255)
        return INADDR_NONE;

    pos += nread;
    if (interface_[pos++] != '.')
        return INADDR_NONE;

    c = BufferToUInt64(interface_ + pos, len - pos, &nread);
    if (nread == 0 || c > 255)
        return INADDR_NONE;

    pos += nread;
    if (interface_[pos++] != '.')
        return INADDR_NONE;

    d = BufferToUInt64(interface_ + pos, len - pos, &nread);
    if (nread == 0 || d > 255)
        return INADDR_NONE;

    pos += nread;
    if (interface_[pos] != '\0' &&
        interface_[pos] != ':')
        return INADDR_NONE;

    addr = (d & 0xff) << 24 | (c & 0xff) << 16 | (b & 0xff) << 8 | (a & 0xff);
    return addr;
}

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013-2016 Alexander Saprykin <xelfium@gmail.com>
 *
 * 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 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, see <http://www.gnu.org/licenses/>.
 */

#ifndef PLIBSYS_TESTS_STATIC
#  define BOOST_TEST_DYN_LINK
#endif

#define BOOST_TEST_MODULE pshmbuffer_test

#include "plibsys.h"

#include <string.h>

#ifdef PLIBSYS_TESTS_STATIC
#  include <boost/test/included/unit_test.hpp>
#else
#  include <boost/test/unit_test.hpp>
#endif

static pchar test_str[]    = "This is a test string!";
static pint is_thread_exit = 0;
static pint read_count     = 0;
static pint write_count    = 0;

#ifndef P_OS_HPUX
volatile static pboolean is_working = FALSE;

static void * shm_buffer_test_write_thread (void *)
{
	PShmBuffer *buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);

	if (buffer == NULL)
		p_uthread_exit (1);

	while (is_working == TRUE) {
		p_uthread_sleep (3);

		pssize op_result = p_shm_buffer_get_free_space (buffer, NULL);

		if (op_result < 0) {
			if (is_thread_exit > 0)
				break;
			else {
				++is_thread_exit;
				p_shm_buffer_free (buffer);
				p_uthread_exit (1);
			}
		}

		if ((psize) op_result < sizeof (test_str))
			continue;

		op_result = p_shm_buffer_write (buffer, (ppointer) test_str, sizeof (test_str), NULL);

		if (op_result < 0) {
			if (is_thread_exit > 0)
				break;
			else {
				++is_thread_exit;
				p_shm_buffer_free (buffer);
				p_uthread_exit (1);
			}
		}

		if (op_result != sizeof (test_str)) {
			++is_thread_exit;
			p_shm_buffer_free (buffer);
			p_uthread_exit (1);
		}

		++read_count;
	}

	++is_thread_exit;

	p_shm_buffer_free (buffer);
	p_uthread_exit (0);

	return NULL;
}

static void * shm_buffer_test_read_thread (void *)
{
	PShmBuffer	*buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	pchar		test_buf[sizeof (test_str)];

	if (buffer == NULL)
		p_uthread_exit (1);

	while (is_working == TRUE) {
		p_uthread_sleep (3);

		pssize op_result = p_shm_buffer_get_used_space (buffer, NULL);

		if (op_result < 0) {
			if (is_thread_exit > 0)
				break;
			else {
				++is_thread_exit;
				p_shm_buffer_free (buffer);
				p_uthread_exit (1);
			}
		}

		if ((psize) op_result < sizeof (test_str))
			continue;

		op_result = p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL);

		if (op_result < 0) {
			if (is_thread_exit > 0)
				break;
			else {
				++is_thread_exit;
				p_shm_buffer_free (buffer);
				p_uthread_exit (1);
			}
		}

		if (op_result != sizeof (test_buf)) {
			++is_thread_exit;
			p_shm_buffer_free (buffer);
			p_uthread_exit (1);
		}

		if (strncmp (test_buf, test_str, sizeof (test_buf)) != 0) {
			++is_thread_exit;
			p_shm_buffer_free (buffer);
			p_uthread_exit (1);
		}

		++write_count;
	}

	++is_thread_exit;

	p_shm_buffer_free (buffer);
	p_uthread_exit (0);

	return NULL;
}
#endif /* !P_OS_HPUX */

extern "C" ppointer pmem_alloc (psize nbytes)
{
	P_UNUSED (nbytes);
	return (ppointer) NULL;
}

extern "C" ppointer pmem_realloc (ppointer block, psize nbytes)
{
	P_UNUSED (block);
	P_UNUSED (nbytes);
	return (ppointer) NULL;
}

extern "C" void pmem_free (ppointer block)
{
	P_UNUSED (block);
}

BOOST_AUTO_TEST_SUITE (BOOST_TEST_MODULE)

BOOST_AUTO_TEST_CASE (pshmbuffer_nomem_test)
{
	p_libsys_init ();

	PMemVTable vtable;

	vtable.free    = pmem_free;
	vtable.malloc  = pmem_alloc;
	vtable.realloc = pmem_realloc;

	BOOST_CHECK (p_mem_set_vtable (&vtable) == TRUE);

	BOOST_CHECK (p_shm_buffer_new ("pshm_test_buffer", 1024, NULL) == NULL);

	p_mem_restore_vtable ();

	p_libsys_shutdown ();
}

BOOST_AUTO_TEST_CASE (pshmbuffer_bad_input_test)
{
	p_libsys_init ();

	BOOST_CHECK (p_shm_buffer_new (NULL, 0, NULL) == NULL);
	BOOST_CHECK (p_shm_buffer_read (NULL, NULL, 0, NULL) == -1);
	BOOST_CHECK (p_shm_buffer_write (NULL, NULL, 0, NULL) == -1);
	BOOST_CHECK (p_shm_buffer_get_free_space (NULL, NULL) == -1);
	BOOST_CHECK (p_shm_buffer_get_used_space (NULL, NULL) == -1);

	PShmBuffer *buf = p_shm_buffer_new ("pshm_invalid_buffer", 0, NULL);
	p_shm_buffer_take_ownership (buf);
	p_shm_buffer_free (buf);

	p_shm_buffer_clear (NULL);
	p_shm_buffer_free (NULL);

	p_libsys_shutdown ();
}

BOOST_AUTO_TEST_CASE (pshmbuffer_general_test)
{
	p_libsys_init ();

	pchar		test_buf[sizeof (test_str)];
	pchar		*large_buf;
	PShmBuffer	*buffer = NULL;

	/* Buffer may be from the previous test on UNIX systems */
	buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	BOOST_REQUIRE (buffer != NULL);
	p_shm_buffer_take_ownership (buffer);
	p_shm_buffer_free (buffer);
	buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	BOOST_REQUIRE (buffer != NULL);

	BOOST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024);
	BOOST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0);
	p_shm_buffer_clear (buffer);
	BOOST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024);
	BOOST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0);

	memset (test_buf, 0, sizeof (test_buf));

	BOOST_CHECK (p_shm_buffer_write (buffer, (ppointer) test_str, sizeof (test_str), NULL) == sizeof (test_str));
	BOOST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == (1024 - sizeof (test_str)));
	BOOST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == sizeof (test_str));
	BOOST_CHECK (p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL) == sizeof (test_str));
	BOOST_CHECK (p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL) == 0);

	BOOST_CHECK (strncmp (test_buf, test_str, sizeof (test_str)) == 0);
	BOOST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024);
	BOOST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0);

	p_shm_buffer_clear (buffer);

	large_buf = (pchar *) p_malloc0 (2048);
	BOOST_REQUIRE (large_buf != NULL);
	BOOST_CHECK (p_shm_buffer_write (buffer, (ppointer) large_buf, 2048, NULL) == 0);

	p_free (large_buf);
	p_shm_buffer_free (buffer);

	p_libsys_shutdown ();
}

#ifndef P_OS_HPUX
BOOST_AUTO_TEST_CASE (pshmbuffer_thread_test)
{
	p_libsys_init ();

	PShmBuffer	*buffer = NULL;
	PUThread	*thr1, *thr2;

	/* Buffer may be from the previous test on UNIX systems */
	buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	BOOST_REQUIRE (buffer != NULL);
	p_shm_buffer_take_ownership (buffer);
	p_shm_buffer_free (buffer);

	is_thread_exit = 0;
	read_count     = 0;
	write_count    = 0;
	is_working     = TRUE;

	buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	BOOST_REQUIRE (buffer != NULL);

	thr1 = p_uthread_create ((PUThreadFunc) shm_buffer_test_write_thread, NULL, TRUE);
	BOOST_REQUIRE (thr1 != NULL);

	thr2 = p_uthread_create ((PUThreadFunc) shm_buffer_test_read_thread, NULL, TRUE);
	BOOST_REQUIRE (thr1 != NULL);

	p_uthread_sleep (5000);

	is_working = FALSE;

	BOOST_CHECK (p_uthread_join (thr1) == 0);
	BOOST_CHECK (p_uthread_join (thr2) == 0);

	BOOST_CHECK (read_count > 0);
	BOOST_CHECK (write_count > 0);

	p_shm_buffer_free (buffer);
	p_uthread_unref (thr1);
	p_uthread_unref (thr2);

	p_libsys_shutdown ();
}
#endif /* !P_OS_HPUX */

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>tests: Move shared memory buffer on new testing macros<commit_after>/*
 * Copyright (C) 2013-2017 Alexander Saprykin <xelfium@gmail.com>
 *
 * 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 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, see <http://www.gnu.org/licenses/>.
 */

#include "plibsys.h"
#include "ptestmacros.h"

#include <string.h>

P_TEST_MODULE_INIT ();

static pchar test_str[]    = "This is a test string!";
static pint is_thread_exit = 0;
static pint read_count     = 0;
static pint write_count    = 0;

#ifndef P_OS_HPUX
volatile static pboolean is_working = FALSE;

static void * shm_buffer_test_write_thread (void *)
{
	PShmBuffer *buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);

	if (buffer == NULL)
		p_uthread_exit (1);

	while (is_working == TRUE) {
		p_uthread_sleep (3);

		pssize op_result = p_shm_buffer_get_free_space (buffer, NULL);

		if (op_result < 0) {
			if (is_thread_exit > 0)
				break;
			else {
				++is_thread_exit;
				p_shm_buffer_free (buffer);
				p_uthread_exit (1);
			}
		}

		if ((psize) op_result < sizeof (test_str))
			continue;

		op_result = p_shm_buffer_write (buffer, (ppointer) test_str, sizeof (test_str), NULL);

		if (op_result < 0) {
			if (is_thread_exit > 0)
				break;
			else {
				++is_thread_exit;
				p_shm_buffer_free (buffer);
				p_uthread_exit (1);
			}
		}

		if (op_result != sizeof (test_str)) {
			++is_thread_exit;
			p_shm_buffer_free (buffer);
			p_uthread_exit (1);
		}

		++read_count;
	}

	++is_thread_exit;

	p_shm_buffer_free (buffer);
	p_uthread_exit (0);

	return NULL;
}

static void * shm_buffer_test_read_thread (void *)
{
	PShmBuffer	*buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	pchar		test_buf[sizeof (test_str)];

	if (buffer == NULL)
		p_uthread_exit (1);

	while (is_working == TRUE) {
		p_uthread_sleep (3);

		pssize op_result = p_shm_buffer_get_used_space (buffer, NULL);

		if (op_result < 0) {
			if (is_thread_exit > 0)
				break;
			else {
				++is_thread_exit;
				p_shm_buffer_free (buffer);
				p_uthread_exit (1);
			}
		}

		if ((psize) op_result < sizeof (test_str))
			continue;

		op_result = p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL);

		if (op_result < 0) {
			if (is_thread_exit > 0)
				break;
			else {
				++is_thread_exit;
				p_shm_buffer_free (buffer);
				p_uthread_exit (1);
			}
		}

		if (op_result != sizeof (test_buf)) {
			++is_thread_exit;
			p_shm_buffer_free (buffer);
			p_uthread_exit (1);
		}

		if (strncmp (test_buf, test_str, sizeof (test_buf)) != 0) {
			++is_thread_exit;
			p_shm_buffer_free (buffer);
			p_uthread_exit (1);
		}

		++write_count;
	}

	++is_thread_exit;

	p_shm_buffer_free (buffer);
	p_uthread_exit (0);

	return NULL;
}
#endif /* !P_OS_HPUX */

extern "C" ppointer pmem_alloc (psize nbytes)
{
	P_UNUSED (nbytes);
	return (ppointer) NULL;
}

extern "C" ppointer pmem_realloc (ppointer block, psize nbytes)
{
	P_UNUSED (block);
	P_UNUSED (nbytes);
	return (ppointer) NULL;
}

extern "C" void pmem_free (ppointer block)
{
	P_UNUSED (block);
}

P_TEST_CASE_BEGIN (pshmbuffer_nomem_test)
{
	p_libsys_init ();

	PMemVTable vtable;

	vtable.free    = pmem_free;
	vtable.malloc  = pmem_alloc;
	vtable.realloc = pmem_realloc;

	P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE);

	P_TEST_CHECK (p_shm_buffer_new ("pshm_test_buffer", 1024, NULL) == NULL);

	p_mem_restore_vtable ();

	p_libsys_shutdown ();
}
P_TEST_CASE_END ()

P_TEST_CASE_BEGIN (pshmbuffer_bad_input_test)
{
	p_libsys_init ();

	P_TEST_CHECK (p_shm_buffer_new (NULL, 0, NULL) == NULL);
	P_TEST_CHECK (p_shm_buffer_read (NULL, NULL, 0, NULL) == -1);
	P_TEST_CHECK (p_shm_buffer_write (NULL, NULL, 0, NULL) == -1);
	P_TEST_CHECK (p_shm_buffer_get_free_space (NULL, NULL) == -1);
	P_TEST_CHECK (p_shm_buffer_get_used_space (NULL, NULL) == -1);

	PShmBuffer *buf = p_shm_buffer_new ("pshm_invalid_buffer", 0, NULL);
	p_shm_buffer_take_ownership (buf);
	p_shm_buffer_free (buf);

	p_shm_buffer_clear (NULL);
	p_shm_buffer_free (NULL);

	p_libsys_shutdown ();
}
P_TEST_CASE_END ()

P_TEST_CASE_BEGIN (pshmbuffer_general_test)
{
	p_libsys_init ();

	pchar		test_buf[sizeof (test_str)];
	pchar		*large_buf;
	PShmBuffer	*buffer = NULL;

	/* Buffer may be from the previous test on UNIX systems */
	buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	P_TEST_REQUIRE (buffer != NULL);
	p_shm_buffer_take_ownership (buffer);
	p_shm_buffer_free (buffer);
	buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	P_TEST_REQUIRE (buffer != NULL);

	P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024);
	P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0);
	p_shm_buffer_clear (buffer);
	P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024);
	P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0);

	memset (test_buf, 0, sizeof (test_buf));

	P_TEST_CHECK (p_shm_buffer_write (buffer, (ppointer) test_str, sizeof (test_str), NULL) == sizeof (test_str));
	P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == (1024 - sizeof (test_str)));
	P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == sizeof (test_str));
	P_TEST_CHECK (p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL) == sizeof (test_str));
	P_TEST_CHECK (p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL) == 0);

	P_TEST_CHECK (strncmp (test_buf, test_str, sizeof (test_str)) == 0);
	P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024);
	P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0);

	p_shm_buffer_clear (buffer);

	large_buf = (pchar *) p_malloc0 (2048);
	P_TEST_REQUIRE (large_buf != NULL);
	P_TEST_CHECK (p_shm_buffer_write (buffer, (ppointer) large_buf, 2048, NULL) == 0);

	p_free (large_buf);
	p_shm_buffer_free (buffer);

	p_libsys_shutdown ();
}
P_TEST_CASE_END ()

#ifndef P_OS_HPUX
P_TEST_CASE_BEGIN (pshmbuffer_thread_test)
{
	p_libsys_init ();

	PShmBuffer	*buffer = NULL;
	PUThread	*thr1, *thr2;

	/* Buffer may be from the previous test on UNIX systems */
	buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	P_TEST_REQUIRE (buffer != NULL);
	p_shm_buffer_take_ownership (buffer);
	p_shm_buffer_free (buffer);

	is_thread_exit = 0;
	read_count     = 0;
	write_count    = 0;
	is_working     = TRUE;

	buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL);
	P_TEST_REQUIRE (buffer != NULL);

	thr1 = p_uthread_create ((PUThreadFunc) shm_buffer_test_write_thread, NULL, TRUE);
	P_TEST_REQUIRE (thr1 != NULL);

	thr2 = p_uthread_create ((PUThreadFunc) shm_buffer_test_read_thread, NULL, TRUE);
	P_TEST_REQUIRE (thr1 != NULL);

	p_uthread_sleep (5000);

	is_working = FALSE;

	P_TEST_CHECK (p_uthread_join (thr1) == 0);
	P_TEST_CHECK (p_uthread_join (thr2) == 0);

	P_TEST_CHECK (read_count > 0);
	P_TEST_CHECK (write_count > 0);

	p_shm_buffer_free (buffer);
	p_uthread_unref (thr1);
	p_uthread_unref (thr2);

	p_libsys_shutdown ();
}
P_TEST_CASE_END ()
#endif /* !P_OS_HPUX */

P_TEST_SUITE_BEGIN()
{
	P_TEST_SUITE_RUN_CASE (pshmbuffer_nomem_test);
	P_TEST_SUITE_RUN_CASE (pshmbuffer_bad_input_test);
	P_TEST_SUITE_RUN_CASE (pshmbuffer_general_test);

#ifndef P_OS_HPUX
	P_TEST_SUITE_RUN_CASE (pshmbuffer_thread_test);
#endif
}
P_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include <popt.h>

#include "defs.hh"
#include "StringSet.hh"
#include "FactorEncoder.hh"
#include "GreedyUnigrams.hh"

using namespace std;

void assert_single_chars(map<string, flt_type> &vocab,
                         const map<string, flt_type> &chars,
                         flt_type val)
{
    for (auto it = chars.cbegin(); it != chars.cend(); ++it)
        if (vocab.find(it->first) == vocab.end())
            vocab[it->first] = val;
}

int main(int argc, char* argv[]) {

    float cutoff_value = 0.0;
    int target_vocab_size = 50000;
    flt_type one_char_min_lp = -25.0;
    string vocab_fname;
    string wordlist_fname;

    // Popt documentation:
    // http://linux.die.net/man/3/popt
    // http://privatemisc.blogspot.fi/2012/12/popt-basic-example.html
    poptContext pc;
    struct poptOption po[] = {
        {"cutoff", 'u', POPT_ARG_FLOAT, &cutoff_value, 11001, NULL, "Cutoff value for each iteration"},
        {"vocab_size", 'g', POPT_ARG_INT, &target_vocab_size, 11007, NULL, "Target vocabulary size (stopping criterion)"},
        POPT_AUTOHELP
        {NULL}
    };

    pc = poptGetContext(NULL, argc, (const char **)argv, po, 0);
    poptSetOtherOptionHelp(pc, "[INITIAL VOCABULARY] [WORDLIST]");

    int val;
    while ((val = poptGetNextOpt(pc)) >= 0)
        continue;

    // poptGetNextOpt returns -1 when the final argument has been parsed
    // otherwise an error occured
    if (val != -1) {
        switch (val) {
        case POPT_ERROR_NOARG:
            cerr << "Argument missing for an option" << endl;
            exit(1);
        case POPT_ERROR_BADOPT:
            cerr << "Option's argument could not be parsed" << endl;
            exit(1);
        case POPT_ERROR_BADNUMBER:
        case POPT_ERROR_OVERFLOW:
            cerr << "Option could not be converted to number" << endl;
            exit(1);
        default:
            cerr << "Unknown error in option processing" << endl;
            exit(1);
        }
    }

    // Handle ARG part of command line
    if (poptPeekArg(pc) != NULL)
        vocab_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Initial vocabulary file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        wordlist_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Wordlist file not set" << endl;
        exit(1);
    }

    cerr << "parameters, initial vocabulary: " << vocab_fname << endl;
    cerr << "parameters, wordlist: " << wordlist_fname << endl;
    cerr << "parameters, cutoff: " << setprecision(15) << cutoff_value << endl;
    cerr << "parameters, target vocab size: " << target_vocab_size << endl;

    int maxlen, word_maxlen;
    map<string, flt_type> all_chars;
    map<string, flt_type> vocab;
    map<string, flt_type> freqs;
    map<string, flt_type> words;

    cerr << "Reading vocabulary " << vocab_fname << endl;
    int retval = read_vocab(vocab_fname, vocab, maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading vocabulary" << endl;
        exit(0);
    }
    cerr << "\t" << "size: " << vocab.size() << endl;
    cerr << "\t" << "maximum string length: " << maxlen << endl;
    for (auto it = vocab.cbegin(); it != vocab.end(); ++it)
        if (it->first.length() == 1)
            all_chars[it->first] = 0.0;

    cerr << "Reading word list " << wordlist_fname << endl;
    retval = read_vocab(wordlist_fname, words, word_maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading word list" << endl;
        exit(0);
    }
    cerr << "\t" << "wordlist size: " << words.size() << endl;
    cerr << "\t" << "maximum word length: " << word_maxlen << endl;

    GreedyUnigrams gg;
    gg.set_segmentation_method(forward_backward);

    cerr << "Initial cutoff" << endl;
    gg.resegment_words(words, vocab, freqs);
    flt_type densum = gg.get_sum(freqs);
    flt_type cost = gg.get_cost(freqs, densum);
    cerr << "cost: " << cost << endl;

    gg.cutoff(freqs, (flt_type)cutoff_value);
    cerr << "\tcutoff: " << cutoff_value << "\t" << "vocabulary size: " << freqs.size() << endl;
    vocab = freqs;
    densum = gg.get_sum(vocab);
    gg.freqs_to_logprobs(vocab, densum);
    assert_single_chars(vocab, all_chars, one_char_min_lp);

    StringSet<flt_type> ss_vocab(vocab);
    map<string, FactorGraph*> fg_words;
    string start_end_symbol("*");
    map<pair<string,string>, flt_type> total_stats;
    vocab[start_end_symbol] = 0.0;
    for (auto it=words.cbegin(); it !=words.cend(); ++it) {
        FactorGraph *fg = new FactorGraph(it->first, start_end_symbol, ss_vocab);
        map<pair<string,string>, flt_type> stats;
        forward_backward(vocab, *fg, stats);
        for (auto statit = stats.cbegin(); statit != stats.cend(); ++statit)
            total_stats[statit->first] += statit->second;
        delete fg;
    }
    cout << "amount of transitions: " << total_stats.size() << endl;

    exit(1);
}

<commit_msg>Iterating transition stats estimation, no pruning yet.<commit_after>#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include <popt.h>

#include "defs.hh"
#include "StringSet.hh"
#include "FactorEncoder.hh"
#include "GreedyUnigrams.hh"

using namespace std;

void assert_single_chars(map<string, flt_type> &vocab,
                         const map<string, flt_type> &chars,
                         flt_type val)
{
    for (auto it = chars.cbegin(); it != chars.cend(); ++it)
        if (vocab.find(it->first) == vocab.end())
            vocab[it->first] = val;
}

void collect_trans_stats(const map<pair<string,string>, flt_type> &transitions,
                         const map<string, flt_type> &words,
                         map<string, FactorGraph*> &fg_words,
                         map<pair<string,string>, flt_type> &trans_stats,
                         map<string, flt_type> &trans_normalizers)
{
    trans_stats.clear();
    trans_normalizers.clear();
    for (auto it = fg_words.begin(); it != fg_words.end(); ++it) {
        map<pair<string,string>, flt_type> stats;
        forward_backward(transitions, *it->second, stats);
        for (auto statit = stats.cbegin(); statit != stats.cend(); ++statit) {
            trans_stats[statit->first] += words.at(it->first) * statit->second;
            trans_normalizers[statit->first.first] += words.at(it->first) * statit->second;
        }
    }

}

void normalize(map<pair<string,string>, flt_type> &trans_stats,
               map<string, flt_type> &trans_normalizers)
{
    auto iter = trans_stats.begin();
    while (iter != trans_stats.end()) {
        iter->second /= trans_normalizers[iter->first.first];
        if (iter->second <= 0.0 || std::isnan(iter->second)) trans_stats.erase(iter++);
        else {
            iter->second = log(iter->second);
            ++iter;
        }
    }

}

void write_transitions(const map<pair<string,string>, flt_type> &transitions,
                       const string &filename)
{
    ofstream transfile(filename);
    if (!transfile) return;

    for (auto it = transitions.begin(); it != transitions.end(); ++it)
        transfile << it->first.first << " " << it->first.second << " " << it->second << endl;

    transfile.close();
}

flt_type bigram_cost(const map<pair<string,string>, flt_type> &transitions,
                     const map<pair<string,string>, flt_type> &trans_stats)
{
    flt_type total = 0.0;
    flt_type tmp = 0.0;
    for (auto iter = transitions.cbegin(); iter != transitions.cend(); ++iter) {
        tmp = iter->second * trans_stats.at(iter->first);
        if (!std::isnan(tmp)) total += tmp;
    }
    return total;
}

int main(int argc, char* argv[]) {

    float cutoff_value = 0.0;
    int target_vocab_size = 50000;
    flt_type one_char_min_lp = -25.0;
    string vocab_fname;
    string wordlist_fname;

    // Popt documentation:
    // http://linux.die.net/man/3/popt
    // http://privatemisc.blogspot.fi/2012/12/popt-basic-example.html
    poptContext pc;
    struct poptOption po[] = {
        {"cutoff", 'u', POPT_ARG_FLOAT, &cutoff_value, 11001, NULL, "Cutoff value for each iteration"},
        {"vocab_size", 'g', POPT_ARG_INT, &target_vocab_size, 11007, NULL, "Target vocabulary size (stopping criterion)"},
        POPT_AUTOHELP
        {NULL}
    };

    pc = poptGetContext(NULL, argc, (const char **)argv, po, 0);
    poptSetOtherOptionHelp(pc, "[INITIAL VOCABULARY] [WORDLIST]");

    int val;
    while ((val = poptGetNextOpt(pc)) >= 0)
        continue;

    // poptGetNextOpt returns -1 when the final argument has been parsed
    // otherwise an error occured
    if (val != -1) {
        switch (val) {
        case POPT_ERROR_NOARG:
            cerr << "Argument missing for an option" << endl;
            exit(1);
        case POPT_ERROR_BADOPT:
            cerr << "Option's argument could not be parsed" << endl;
            exit(1);
        case POPT_ERROR_BADNUMBER:
        case POPT_ERROR_OVERFLOW:
            cerr << "Option could not be converted to number" << endl;
            exit(1);
        default:
            cerr << "Unknown error in option processing" << endl;
            exit(1);
        }
    }

    // Handle ARG part of command line
    if (poptPeekArg(pc) != NULL)
        vocab_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Initial vocabulary file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        wordlist_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Wordlist file not set" << endl;
        exit(1);
    }

    cerr << "parameters, initial vocabulary: " << vocab_fname << endl;
    cerr << "parameters, wordlist: " << wordlist_fname << endl;
    cerr << "parameters, cutoff: " << setprecision(15) << cutoff_value << endl;
    cerr << "parameters, target vocab size: " << target_vocab_size << endl;

    int maxlen, word_maxlen;
    map<string, flt_type> all_chars;
    map<string, flt_type> vocab;
    map<string, flt_type> freqs;
    map<string, flt_type> words;

    cerr << "Reading vocabulary " << vocab_fname << endl;
    int retval = read_vocab(vocab_fname, vocab, maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading vocabulary" << endl;
        exit(0);
    }
    cerr << "\t" << "size: " << vocab.size() << endl;
    cerr << "\t" << "maximum string length: " << maxlen << endl;
    for (auto it = vocab.cbegin(); it != vocab.end(); ++it)
        if (it->first.length() == 1)
            all_chars[it->first] = 0.0;

    cerr << "Reading word list " << wordlist_fname << endl;
    retval = read_vocab(wordlist_fname, words, word_maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading word list" << endl;
        exit(0);
    }
    cerr << "\t" << "wordlist size: " << words.size() << endl;
    cerr << "\t" << "maximum word length: " << word_maxlen << endl;

    GreedyUnigrams gg;
    gg.set_segmentation_method(forward_backward);

    cerr << "Initial cutoff" << endl;
    gg.resegment_words(words, vocab, freqs);
    flt_type densum = gg.get_sum(freqs);
    flt_type cost = gg.get_cost(freqs, densum);
    cerr << "cost: " << cost << endl;

    gg.cutoff(freqs, (flt_type)cutoff_value);
    cerr << "\tcutoff: " << cutoff_value << "\t" << "vocabulary size: " << freqs.size() << endl;
    vocab = freqs;
    densum = gg.get_sum(vocab);
    gg.freqs_to_logprobs(vocab, densum);
    assert_single_chars(vocab, all_chars, one_char_min_lp);

    StringSet<flt_type> ss_vocab(vocab);
    map<string, FactorGraph*> fg_words;
    string start_end_symbol("*");
    map<pair<string,string>, flt_type> transitions;
    map<pair<string,string>, flt_type> trans_stats;
    map<string, flt_type> trans_normalizers;
    vocab[start_end_symbol] = 0.0;

    // Initial segmentation using unigram model
    for (auto it=words.cbegin(); it != words.cend(); ++it) {
        FactorGraph *fg = new FactorGraph(it->first, start_end_symbol, ss_vocab);
        fg_words[it->first] = fg;
        map<pair<string,string>, flt_type> stats;
        forward_backward(vocab, *fg, stats);
        for (auto statit = stats.cbegin(); statit != stats.cend(); ++statit) {
            trans_stats[statit->first] += it->second * statit->second;
            trans_normalizers[statit->first.first] += it->second * statit->second;
        }
    }
    cerr << "amount of transitions: " << trans_stats.size() << endl;
    transitions = trans_stats;
    normalize(transitions, trans_normalizers);
    cost = bigram_cost(transitions, trans_stats);
    cerr << "cost: " << cost << endl;

    // Re-estimate using bigram stats
    for (int i=0; i<50; i++) {
        collect_trans_stats(transitions, words, fg_words, trans_stats, trans_normalizers);
        transitions = trans_stats;
        normalize(transitions, trans_normalizers);
        cost = bigram_cost(transitions, trans_stats);
        cerr << "cost: " << cost << endl;
    }

    // Viterbi segmentations
    for (auto it=fg_words.begin(); it != fg_words.cend(); ++it) {
        std::vector<std::string> best_path;
        viterbi(transitions, *it->second, best_path);
        for (int i=0; i<best_path.size(); i++) {
            cout << best_path[i];
            if (i<best_path.size()-1) cout << " ";
        }
        cout << endl;
    }
    write_transitions(transitions, "transitions.out");

    // Clean up
    for (auto it = fg_words.begin(); it != fg_words.cend(); ++it)
        delete it->second;

    exit(1);
}

<|endoftext|>


<commit_before>/*!
*	@file	CatmullClark.cpp
*	@brief	Implementation of Catmull and Clark's subdivision scheme
*/

#include "CatmullClark.h"

namespace psalm
{

/*!
*	Applies Catmull and Clark's subdivision algorithm to the given mesh.
*	The mesh will be irreversibly _changed_ by this function.
*
*	@param	input_mesh Mesh on which the algorithm is applied
*	@return	true on success, else false
*/

bool CatmullClark::apply_to(mesh& input_mesh)
{
	mesh output_mesh;

	create_face_points(input_mesh, output_mesh);
	create_edge_points(input_mesh, output_mesh);

	if(non_quadrangular_face)
		create_vertex_points_parametrically(input_mesh, output_mesh);
	else
		create_vertex_points_geometrically(input_mesh, output_mesh);

	/*
		Create new topology of the mesh by connecting

			vertex -- edge -- face -- edge

		points.
	*/

	for(size_t i = 0; i < input_mesh.num_vertices(); i++)
	{
		print_progress("Creating topology",
				i,
				input_mesh.num_vertices()-1);

		vertex* v = input_mesh.get_vertex(i);

		if(v->vertex_point == NULL)
			continue; // ignore degenerate vertices

		for(size_t j = 0; j < v->num_adjacent_faces(); j++)
		{
			const face* f = v->get_face(j);

			// Find the two incident edges of the current vertex
			// that are also part of the current adjacent face

			const edge* e1 = NULL;
			const edge* e2 = NULL;

			for(size_t k = 0; k < v->valency(); k++)
			{
				const edge* e = v->get_edge(k);
				if(e->get_f() == f || e->get_g() == f)
				{
					if(e1 == NULL)
						e1 = e;
					else
					{
						e2 = e;
						break;
					}
				}
			}

			// For non-manifold meshes, we may not be able to find
			// adjacent faces for every combination of vertices and
			// edges
			if(	e1 == NULL ||
				e2 == NULL)
				continue;

			// If crease handling is not enabled, we may not have
			// edge points everywhere. These faces need to be
			// skipped, of course.
			if(	e1->edge_point == NULL ||
				e2->edge_point == NULL)
			{
				if(preserve_boundaries)
				{
					bool u1 = (e1->get_u() == v);
					bool u2 = (e2->get_u() == v);

					vertex* v10;
					vertex* v11;
					vertex* v20;
					vertex* v21;

					if(e1->edge_point == NULL && e2->edge_point == NULL)
					{
						v10 = output_mesh.add_vertex(e1->get_u()->get_position());
						v11 = output_mesh.add_vertex(e1->get_v()->get_position());
						v20 = output_mesh.add_vertex(e2->get_u()->get_position());
						v21 = output_mesh.add_vertex(e2->get_v()->get_position());

						v10->set_on_boundary();
						v11->set_on_boundary();
						v20->set_on_boundary();
						v21->set_on_boundary();

						output_mesh.add_face(v10, f->face_point, v11);
						output_mesh.add_face(v20, f->face_point, v21);
					}
					else if(e1->edge_point != NULL)
					{
						output_mesh.add_face(v->vertex_point, f->face_point, e1->edge_point);
						if(u2)
						{
							v20 = output_mesh.add_vertex(e2->get_v()->get_position());
							v20->set_on_boundary();
							output_mesh.add_face(v->vertex_point, f->face_point, v20);
						}
						else
						{
							v20 = output_mesh.add_vertex(e2->get_u()->get_position());
							v20->set_on_boundary();
							output_mesh.add_face(v->vertex_point, f->face_point, v20);
						}
					}
					else
					{
						output_mesh.add_face(v->vertex_point, f->face_point, e2->edge_point);
						if(u1)
						{
							v10 = output_mesh.add_vertex(e1->get_v()->get_position());
							v10->set_on_boundary();
							output_mesh.add_face(v->vertex_point, f->face_point, v10);
						}
						else
						{
							v10 = output_mesh.add_vertex(e1->get_u()->get_position());
							v10->set_on_boundary();
							output_mesh.add_face(v->vertex_point, f->face_point, v10);
						}
					}

					continue;
				}
				else
					continue; // skip it if boundaries are _not_ to be preserved
			}

			/*
				Check which edge needs to be used first in
				order to orient the new face properly. The
				rationale behind this is to ensure that e1 is
				the _first_ edge that needs to be visited in
				order to get CCW orientation.
			*/

			if(	(e1->get_u()->get_id() == v->get_id() && e1->get_g() == f) ||
				(e1->get_v()->get_id() == v->get_id() && e1->get_f() == f) ||
				(e2->get_u()->get_id() == v->get_id() && e2->get_f() == f) ||
				(e2->get_v()->get_id() == v->get_id() && e2->get_g() == f))
				std::swap(e1, e2);

			output_mesh.add_face(	v->vertex_point,
						e1->edge_point,
						f->face_point,
						e2->edge_point);
		}
	}

	input_mesh.replace_with(output_mesh);
	return(true);
}

/*!
*	Creates face points of Catmull-Clark subdivision and stores them in the
*	new mesh.
*
*	@param input_mesh	Original input mesh, will not be modified
*	@param output_mesh	Mesh that will contain the new vertex points
*/

void CatmullClark::create_face_points(mesh& input_mesh, mesh& output_mesh)
{
	for(size_t i = 0; i < input_mesh.num_faces(); i++)
	{
		print_progress("Creating face points",
				i,
				input_mesh.num_faces()-1);

		face* f = input_mesh.get_face(i);

		v3ctor centroid;
		for(size_t j = 0; j < f->num_vertices(); j++)
			centroid += f->get_vertex(j)->get_position();

		centroid /= f->num_vertices();

		f->face_point = output_mesh.add_vertex(centroid);

		if(!non_quadrangular_face && f->num_vertices() != 4)
			non_quadrangular_face = true;
	}
}

/*!
*	Creates edge points of Catmull-Clark subdivision and stores them in the
*	new mesh.
*
*	@param input_mesh	Original input mesh, will not be modified
*	@param output_mesh	Mesh that will contain the new vertex points
*/

void CatmullClark::create_edge_points(mesh& input_mesh, mesh& output_mesh)
{
	for(size_t i = 0; i < input_mesh.num_edges(); i++)
	{
		print_progress("Creating edge points",
				i,
				input_mesh.num_edges()-1);

		edge* e = input_mesh.get_edge(i);
		v3ctor edge_point;

		// Border/crease edge: Use midpoint of edge for the edge point
		// if crease handling is enabled
		if(e->get_g() == NULL)
		{
			e->edge_point = NULL;
			if(handle_creases && !e->get_u()->is_on_boundary() && !e->get_v()->is_on_boundary())
			{
				edge_point = (	e->get_u()->get_position()+
						e->get_v()->get_position())*0.5;

				e->edge_point = output_mesh.add_vertex(edge_point);
			}

			// Preserve the original boundaries of the object
			else if(preserve_boundaries && e->is_on_boundary())
			// FIXME: else if(preserve_boundaries && e->get_u()->is_on_boundary() && e->get_v()->is_on_boundary())
			{
				/*
				edge_point = (	e->get_u()->get_position()+
						e->get_v()->get_position())*0.5;

				e->edge_point = M.add_vertex(edge_point);
				e->edge_point->set_on_boundary();
				*/

				e->edge_point = NULL;
			}
			else
			{
				// Remove start and end vertex of edge; we will
				// not be able to connect them correctly.
				/*

				TODO: This requires implementing new methods
				for the mesh class

				std::vector<vertex*>::iterator it;
				if((it = find(V.begin(), V.end(), e->get_u())) != V.end())
					V.erase(it);
				if((it = find(V.begin(), V.end(), e->get_v())) != V.end())
					V.erase(it);
				*/
			}
		}

		// Normal edge
		else
		{
			edge_point = (	e->get_u()->get_position()+
					e->get_v()->get_position()+
					e->get_f()->face_point->get_position()+
					e->get_g()->face_point->get_position())*0.25;

			e->edge_point = output_mesh.add_vertex(edge_point);
		}
	}
}

/*!
*	Creates vertex points of the Catmull-Clark scheme parametrically and
*	stores them in the new mesh.
*
*	@param input_mesh	Original input mesh, will not be modified
*	@param output_mesh	Mesh that will contain the new vertex points
*/

void CatmullClark::create_vertex_points_parametrically(mesh& input_mesh, mesh& output_mesh)
{
}

/*!
*	Creates vertex points of the Catmull-Clark scheme geometrically and
*	stores them in the new mesh.
*
*	@param input_mesh	Original input mesh, will not be modified
*	@param output_mesh	Mesh that will contain the new vertex points
*/

void CatmullClark::create_vertex_points_geometrically(mesh& input_mesh, mesh& output_mesh)
{
	for(size_t i = 0; i < input_mesh.num_vertices(); i++)
	{
		print_progress("Creating vertex points [geometrically]",
				i,
				input_mesh.num_vertices()-1);

		vertex* v = input_mesh.get_vertex(i);

		// Keep boundary vertices if the user chose this behaviour
		if(preserve_boundaries && v->is_on_boundary())
		{
			v->vertex_point = output_mesh.add_vertex(v->get_position());
			v->vertex_point->set_on_boundary();
			continue;
		}

		// This follows the original terminology as described by
		// Catmull and Clark

		v3ctor Q;
		v3ctor R;
		v3ctor S;

		size_t n = v->valency();
		if(n < 3)
			continue; // ignore degenerate vertices

		// Q is the average of the new face points of all faces
		// adjacent to the old vertex point
		for(size_t j = 0; j < v->num_adjacent_faces(); j++)
			Q += v->get_face(j)->face_point->get_position();

		Q /= v->num_adjacent_faces();

		// R is the average of the midpoints of all old edges incident
		// on the current vertex
		for(size_t j = 0; j < n; j++)
		{
			const edge* e = v->get_edge(j);
			R += (e->get_u()->get_position()+e->get_v()->get_position())*0.5;
		}

		R /= n;

		// S is the current vertex
		S = v->get_position();

		v3ctor vertex_point = (Q+R*2+S*(n-3))/n;
		v->vertex_point = output_mesh.add_vertex(vertex_point);
	}
}

} // end of namespace "psalm"
<commit_msg>Added parametrical point creation to `CatmullClark`<commit_after>/*!
*	@file	CatmullClark.cpp
*	@brief	Implementation of Catmull and Clark's subdivision scheme
*/

#include "CatmullClark.h"

namespace psalm
{

/*!
*	Applies Catmull and Clark's subdivision algorithm to the given mesh.
*	The mesh will be irreversibly _changed_ by this function.
*
*	@param	input_mesh Mesh on which the algorithm is applied
*	@return	true on success, else false
*/

bool CatmullClark::apply_to(mesh& input_mesh)
{
	mesh output_mesh;

	create_face_points(input_mesh, output_mesh);
	create_edge_points(input_mesh, output_mesh);

	if(non_quadrangular_face)
		create_vertex_points_parametrically(input_mesh, output_mesh);
	else
		create_vertex_points_geometrically(input_mesh, output_mesh);

	/*
		Create new topology of the mesh by connecting

			vertex -- edge -- face -- edge

		points.
	*/

	for(size_t i = 0; i < input_mesh.num_vertices(); i++)
	{
		print_progress("Creating topology",
				i,
				input_mesh.num_vertices()-1);

		vertex* v = input_mesh.get_vertex(i);

		if(v->vertex_point == NULL)
			continue; // ignore degenerate vertices

		for(size_t j = 0; j < v->num_adjacent_faces(); j++)
		{
			const face* f = v->get_face(j);

			// Find the two incident edges of the current vertex
			// that are also part of the current adjacent face

			const edge* e1 = NULL;
			const edge* e2 = NULL;

			for(size_t k = 0; k < v->valency(); k++)
			{
				const edge* e = v->get_edge(k);
				if(e->get_f() == f || e->get_g() == f)
				{
					if(e1 == NULL)
						e1 = e;
					else
					{
						e2 = e;
						break;
					}
				}
			}

			// For non-manifold meshes, we may not be able to find
			// adjacent faces for every combination of vertices and
			// edges
			if(	e1 == NULL ||
				e2 == NULL)
				continue;

			// If crease handling is not enabled, we may not have
			// edge points everywhere. These faces need to be
			// skipped, of course.
			if(	e1->edge_point == NULL ||
				e2->edge_point == NULL)
			{
				if(preserve_boundaries)
				{
					bool u1 = (e1->get_u() == v);
					bool u2 = (e2->get_u() == v);

					vertex* v10;
					vertex* v11;
					vertex* v20;
					vertex* v21;

					if(e1->edge_point == NULL && e2->edge_point == NULL)
					{
						v10 = output_mesh.add_vertex(e1->get_u()->get_position());
						v11 = output_mesh.add_vertex(e1->get_v()->get_position());
						v20 = output_mesh.add_vertex(e2->get_u()->get_position());
						v21 = output_mesh.add_vertex(e2->get_v()->get_position());

						v10->set_on_boundary();
						v11->set_on_boundary();
						v20->set_on_boundary();
						v21->set_on_boundary();

						output_mesh.add_face(v10, f->face_point, v11);
						output_mesh.add_face(v20, f->face_point, v21);
					}
					else if(e1->edge_point != NULL)
					{
						output_mesh.add_face(v->vertex_point, f->face_point, e1->edge_point);
						if(u2)
						{
							v20 = output_mesh.add_vertex(e2->get_v()->get_position());
							v20->set_on_boundary();
							output_mesh.add_face(v->vertex_point, f->face_point, v20);
						}
						else
						{
							v20 = output_mesh.add_vertex(e2->get_u()->get_position());
							v20->set_on_boundary();
							output_mesh.add_face(v->vertex_point, f->face_point, v20);
						}
					}
					else
					{
						output_mesh.add_face(v->vertex_point, f->face_point, e2->edge_point);
						if(u1)
						{
							v10 = output_mesh.add_vertex(e1->get_v()->get_position());
							v10->set_on_boundary();
							output_mesh.add_face(v->vertex_point, f->face_point, v10);
						}
						else
						{
							v10 = output_mesh.add_vertex(e1->get_u()->get_position());
							v10->set_on_boundary();
							output_mesh.add_face(v->vertex_point, f->face_point, v10);
						}
					}

					continue;
				}
				else
					continue; // skip it if boundaries are _not_ to be preserved
			}

			/*
				Check which edge needs to be used first in
				order to orient the new face properly. The
				rationale behind this is to ensure that e1 is
				the _first_ edge that needs to be visited in
				order to get CCW orientation.
			*/

			if(	(e1->get_u()->get_id() == v->get_id() && e1->get_g() == f) ||
				(e1->get_v()->get_id() == v->get_id() && e1->get_f() == f) ||
				(e2->get_u()->get_id() == v->get_id() && e2->get_f() == f) ||
				(e2->get_v()->get_id() == v->get_id() && e2->get_g() == f))
				std::swap(e1, e2);

			output_mesh.add_face(	v->vertex_point,
						e1->edge_point,
						f->face_point,
						e2->edge_point);
		}
	}

	input_mesh.replace_with(output_mesh);
	return(true);
}

/*!
*	Creates face points of Catmull-Clark subdivision and stores them in the
*	new mesh.
*
*	@param input_mesh	Original input mesh, will not be modified
*	@param output_mesh	Mesh that will contain the new vertex points
*/

void CatmullClark::create_face_points(mesh& input_mesh, mesh& output_mesh)
{
	for(size_t i = 0; i < input_mesh.num_faces(); i++)
	{
		print_progress("Creating face points",
				i,
				input_mesh.num_faces()-1);

		face* f = input_mesh.get_face(i);

		v3ctor centroid;
		for(size_t j = 0; j < f->num_vertices(); j++)
			centroid += f->get_vertex(j)->get_position();

		centroid /= f->num_vertices();

		f->face_point = output_mesh.add_vertex(centroid);

		if(!non_quadrangular_face && f->num_vertices() != 4)
			non_quadrangular_face = true;
	}
}

/*!
*	Creates edge points of Catmull-Clark subdivision and stores them in the
*	new mesh.
*
*	@param input_mesh	Original input mesh, will not be modified
*	@param output_mesh	Mesh that will contain the new vertex points
*/

void CatmullClark::create_edge_points(mesh& input_mesh, mesh& output_mesh)
{
	for(size_t i = 0; i < input_mesh.num_edges(); i++)
	{
		print_progress("Creating edge points",
				i,
				input_mesh.num_edges()-1);

		edge* e = input_mesh.get_edge(i);
		v3ctor edge_point;

		// Border/crease edge: Use midpoint of edge for the edge point
		// if crease handling is enabled
		if(e->get_g() == NULL)
		{
			e->edge_point = NULL;
			if(handle_creases && !e->get_u()->is_on_boundary() && !e->get_v()->is_on_boundary())
			{
				edge_point = (	e->get_u()->get_position()+
						e->get_v()->get_position())*0.5;

				e->edge_point = output_mesh.add_vertex(edge_point);
			}

			// Preserve the original boundaries of the object
			else if(preserve_boundaries && e->is_on_boundary())
			// FIXME: else if(preserve_boundaries && e->get_u()->is_on_boundary() && e->get_v()->is_on_boundary())
			{
				/*
				edge_point = (	e->get_u()->get_position()+
						e->get_v()->get_position())*0.5;

				e->edge_point = M.add_vertex(edge_point);
				e->edge_point->set_on_boundary();
				*/

				e->edge_point = NULL;
			}
			else
			{
				// Remove start and end vertex of edge; we will
				// not be able to connect them correctly.
				/*

				TODO: This requires implementing new methods
				for the mesh class

				std::vector<vertex*>::iterator it;
				if((it = find(V.begin(), V.end(), e->get_u())) != V.end())
					V.erase(it);
				if((it = find(V.begin(), V.end(), e->get_v())) != V.end())
					V.erase(it);
				*/
			}
		}

		// Normal edge
		else
		{
			edge_point = (	e->get_u()->get_position()+
					e->get_v()->get_position()+
					e->get_f()->face_point->get_position()+
					e->get_g()->face_point->get_position())*0.25;

			e->edge_point = output_mesh.add_vertex(edge_point);
		}
	}
}

/*!
*	Creates vertex points of the Catmull-Clark scheme parametrically and
*	stores them in the new mesh.
*
*	@param input_mesh	Original input mesh, will not be modified
*	@param output_mesh	Mesh that will contain the new vertex points
*/

void CatmullClark::create_vertex_points_parametrically(mesh& input_mesh, mesh& output_mesh)
{
	for(size_t i = 0; i < input_mesh.num_vertices(); i++)
	{
		print_progress("Creating vertex points [parametrically]",
				i,
				input_mesh.num_vertices()-1);

		vertex* v = input_mesh.get_vertex(i);

		// Keep boundary vertices if the user chose this behaviour
		if(preserve_boundaries && v->is_on_boundary())
		{
			v->vertex_point = output_mesh.add_vertex(v->get_position());
			v->vertex_point->set_on_boundary();
			continue;
		}

		// will be used later for determining the real weights; for the
		// regular case, we will always use the standard weights
		size_t n = v->valency();
		if(n < 3)
			continue; // ignore degenerate vertices

		double gamma	= 0.0;
		double beta	= 0.0;
		double alpha	= 0.0;

		if(n == 4 && use_bspline_weights)
		{
			gamma	= 1.0/16.0;
			beta	= 3.0/8.0;
			alpha	= 9.0/16.0;
		}
		else
		{
			std::pair<double, double> weights = weight_function(n);

			gamma	= weights.second;
			beta	= weights.first;
			alpha	= 1.0-beta-gamma;
		}

		// sets of vertices with weights beta and gamma
		std::set<const vertex*> vertices_beta;
		std::set<const vertex*> vertices_gamma;

		// All vertices that are connected via an edge with the current
		// vertex will be assigned the weight beta.
		for(size_t j = 0; j < n; j++)
		{
			edge* e = v->get_edge(j);
			if(e->get_u()->get_id() != v->get_id())
				vertices_beta.insert(e->get_u());
			else
				vertices_beta.insert(e->get_v());
		}

		// All remaining vertices of all adjacent faces to the current
		// vertex will be assigned the weight gamma.
		for(size_t j = 0; j < n; j++)
		{
			const face* f = v->get_face(j);
			if(f == NULL)
				continue;

			for(size_t k = 0; k < f->num_vertices(); k++)
			{
				const vertex* f_v = f->get_vertex(k);

				// Insert the vertex only if it is not already
				// counted within in the "beta" set (and if it
				// is not the current vertex)
				if(f_v->get_id() != v->get_id() && vertices_beta.find(f_v) == vertices_beta.end())
					vertices_gamma.insert(f_v);
			}
		}

		// Apply weights; since this is O(n), the function checks
		// whether the weights for beta and gamma are applicable at all

		v3ctor vertex_point = v->get_position()*alpha;

		if(beta != 0.0)
		{
			for(std::set<const vertex*>::iterator it = vertices_beta.begin(); it != vertices_beta.end(); it++)
				vertex_point += (*it)->get_position()*beta/n;
		}

		if(gamma != 0.0)
		{
			for(std::set<const vertex*>::iterator it = vertices_gamma.begin(); it != vertices_gamma.end(); it++)
				vertex_point += (*it)->get_position()*gamma/n;
		}

		v->vertex_point = output_mesh.add_vertex(vertex_point);
	}
}

/*!
*	Creates vertex points of the Catmull-Clark scheme geometrically and
*	stores them in the new mesh.
*
*	@param input_mesh	Original input mesh, will not be modified
*	@param output_mesh	Mesh that will contain the new vertex points
*/

void CatmullClark::create_vertex_points_geometrically(mesh& input_mesh, mesh& output_mesh)
{
	for(size_t i = 0; i < input_mesh.num_vertices(); i++)
	{
		print_progress("Creating vertex points [geometrically]",
				i,
				input_mesh.num_vertices()-1);

		vertex* v = input_mesh.get_vertex(i);

		// Keep boundary vertices if the user chose this behaviour
		if(preserve_boundaries && v->is_on_boundary())
		{
			v->vertex_point = output_mesh.add_vertex(v->get_position());
			v->vertex_point->set_on_boundary();
			continue;
		}

		// This follows the original terminology as described by
		// Catmull and Clark

		v3ctor Q;
		v3ctor R;
		v3ctor S;

		size_t n = v->valency();
		if(n < 3)
			continue; // ignore degenerate vertices

		// Q is the average of the new face points of all faces
		// adjacent to the old vertex point
		for(size_t j = 0; j < v->num_adjacent_faces(); j++)
			Q += v->get_face(j)->face_point->get_position();

		Q /= v->num_adjacent_faces();

		// R is the average of the midpoints of all old edges incident
		// on the current vertex
		for(size_t j = 0; j < n; j++)
		{
			const edge* e = v->get_edge(j);
			R += (e->get_u()->get_position()+e->get_v()->get_position())*0.5;
		}

		R /= n;

		// S is the current vertex
		S = v->get_position();

		v3ctor vertex_point = (Q+R*2+S*(n-3))/n;
		v->vertex_point = output_mesh.add_vertex(vertex_point);
	}
}

} // end of namespace "psalm"
<|endoftext|>