<commit_before>#include <algorithm>
#include <map>
#include <string>

#include "Prefetch.h"
#include "IRMutator.h"
#include "Bounds.h"
#include "Scope.h"
#include "Util.h"

namespace Halide {
namespace Internal {

using std::map;
using std::string;
using std::vector;

namespace {

// We need to be able to make loads from a buffer that refer to the
// same original image/param/etc. This visitor finds a load to the
// buffer we want to load from, and generates a similar load, but with
// different args.
class MakeSimilarLoad : public IRVisitor {
public:
    const std::string &buf_name;
    const std::vector<Expr> &args;
    Expr load;

    MakeSimilarLoad(std::string name, const std::vector<Expr> &args)
        : buf_name(name), args(args) {}

private:
    using IRVisitor::visit;

    void visit(const Call *op) {
        if (op->name == buf_name) {
            load = Call::make(op->type, op->name, args, op->call_type, op->func, op->value_index, op->image, op->param);
        } else {
            IRVisitor::visit(op);
        }
    }
};

Expr make_similar_load(Stmt s, const std::string &name, const std::vector<Expr> &args) {
    MakeSimilarLoad v(name, args);
    s.accept(&v);
    return v.load;
}

// Build a Box representing the bounds of a buffer.
Box buffer_bounds(const string &buf_name, int dims) {
    Box bounds;
    for (int i = 0; i < dims; i++) {
        string dim_name = std::to_string(i);

        Expr buf_min_i = Variable::make(Int(32), buf_name + ".min." + dim_name);
        Expr buf_extent_i = Variable::make(Int(32), buf_name + ".extent." + dim_name);
        Expr buf_max_i = buf_min_i + buf_extent_i - 1;

        bounds.push_back(Interval(buf_min_i, buf_max_i));
    }
    return bounds;
}

class InjectPrefetch : public IRMutator {
public:
    InjectPrefetch(const map<string, Function> &e) : env(e) { }

private:
    const map<string, Function> &env;
    const vector<Prefetch> *prefetches = nullptr;
    Scope<Interval> bounds;

private:
    using IRMutator::visit;

    void visit(const Let *op) {
        Interval in = bounds_of_expr_in_scope(op->value, bounds);
        bounds.push(op->name, in);
        IRMutator::visit(op);
        bounds.pop(op->name);
    }

    void visit(const LetStmt *op) {
        Interval in = bounds_of_expr_in_scope(op->value, bounds);
        bounds.push(op->name, in);
        IRMutator::visit(op);
        bounds.pop(op->name);
    }

    void visit(const ProducerConsumer *op) {
        const vector<Prefetch> *old_prefetches = prefetches;

        map<string, Function>::const_iterator iter = env.find(op->name);
        internal_assert(iter != env.end()) << "function not in environment.\n";
        prefetches = &iter->second.schedule().prefetches();
        IRMutator::visit(op);
        prefetches = old_prefetches;
    }

    Stmt add_prefetch(const string &buf_name, const Box &box, Stmt body) {
        // Construct the bounds to be prefetched.
        vector<Expr> prefetch_min;
        vector<Expr> prefetch_extent;
        for (size_t i = 0; i < box.size(); i++) {
            prefetch_min.push_back(box[i].min);
            prefetch_extent.push_back(box[i].max - box[i].min + 1);
        }

        // Construct an array of index variables. The first 2 dimensions are handled
        // by (up to) 2D prefetches, the rest we will generate loops to define.
        vector<string> index_names(box.size());
        vector<Expr> indices(box.size());
        for (size_t i = 0; i < box.size(); i++) {
            index_names[i] = "prefetch_" + buf_name + "." + std::to_string(i);
            indices[i] = i < 2 ? prefetch_min[i] : Variable::make(Int(32), index_names[i]);
        }

        // Make a load at the index and get the address.
        Expr prefetch_load = make_similar_load(body, buf_name, indices);
        internal_assert(prefetch_load.defined());
        Type type = prefetch_load.type();
        Expr prefetch_addr = Call::make(Handle(), Call::address_of, {prefetch_load}, Call::Intrinsic);

        Stmt prefetch;
        Expr stride_0 = Variable::make(Int(32), buf_name + ".stride.0");
        // TODO: This is only correct if stride_0 is 1. We need to assert that this is true.
        Expr extent_0_bytes = prefetch_extent[0] * type.bytes();
        if (box.size() == 1) {
            // The prefetch is only 1 dimensional, just emit a flat prefetch.
            prefetch = Evaluate::make(Call::make(Int(32), Call::prefetch,
                                                 {prefetch_addr, extent_0_bytes},
                                                 Call::PureIntrinsic));
        } else {
            // Make a 2D prefetch.
            Expr stride_1 = Variable::make(Int(32), buf_name + ".stride.1");
            Expr stride_1_bytes = stride_1 * type.bytes();
            prefetch = Evaluate::make(Call::make(Int(32), Call::prefetch_2d,
                                                 {prefetch_addr, extent_0_bytes, prefetch_extent[1], stride_1_bytes},
                                                 Call::PureIntrinsic));

            // Make loops for the rest of the dimensions (possibly zero).
            for (size_t i = 2; i < box.size(); i++) {
                prefetch = For::make(index_names[i], prefetch_min[i], prefetch_extent[i],
                                     ForType::Serial, DeviceAPI::None,
                                     prefetch);
            }
        }

        // We should only prefetch buffers that are used.
        if (box.maybe_unused()) {
            prefetch = IfThenElse::make(box.used, prefetch);
        }

        return Block::make({prefetch, body});
    }

    void visit(const For *op) {
        // Add loop variable to interval scope for any inner loop prefetch
        Expr loop_var = Variable::make(Int(32), op->name);
        bounds.push(op->name, Interval(loop_var, loop_var));
        Stmt body = mutate(op->body);
        bounds.pop(op->name);

        if (prefetches) {
            for (const Prefetch &p : *prefetches) {
                if (!ends_with(op->name, "." + p.var)) {
                    continue;
                }
                // Add loop variable + prefetch offset to interval scope for box computation
                Expr fetch_at = loop_var + p.offset;
                bounds.push(op->name, Interval(fetch_at, fetch_at));
                map<string, Box> boxes_read = boxes_required(body, bounds);
                bounds.pop(op->name);

                // Don't prefetch buffers that are written to. We assume that these already
                // have good locality.
                // TODO: This is not a good assumption. It would be better to have the
                // prefetch directive specify the buffer that we want to prefetch, instead
                // of trying to figure out which buffers should be prefetched.
                map<string, Box> boxes_written = boxes_provided(body, bounds);
                for (const auto &b : boxes_written) {
                    auto it = boxes_read.find(b.first);
                    if (it != boxes_read.end()) {
                        debug(2) << "Not prefetching buffer " << it->first
                                 << " also written in loop " << op->name << "\n";
                        boxes_read.erase(it);
                    }
                }

                // TODO: Only prefetch the newly accessed data from the previous iteration.
                // This should use boxes_touched (instead of boxes_required) so we exclude memory
                // either read or written.
                for (const auto &b : boxes_read) {
                    const std::string &buf_name = b.first;

                    // Only prefetch the region that is in bounds.
                    Box bounds = buffer_bounds(buf_name, b.second.size());
                    Box prefetch_box = box_intersection(b.second, bounds);

                    body = add_prefetch(buf_name, prefetch_box, body);
                }
            }
        }

        if (!body.same_as(op->body)) {
            stmt = For::make(op->name, op->min, op->extent, op->for_type, op->device_api, body);
        } else {
            stmt = op;
        }
    }

};

} // namespace

Stmt inject_prefetch(Stmt s, const std::map<std::string, Function> &env) {
    return InjectPrefetch(env).mutate(s);
}

}
}
<commit_msg>Add more comments/TODO description.<commit_after>#include <algorithm>
#include <map>
#include <string>

#include "Prefetch.h"
#include "IRMutator.h"
#include "Bounds.h"
#include "Scope.h"
#include "Util.h"

namespace Halide {
namespace Internal {

using std::map;
using std::string;
using std::vector;

namespace {

// We need to be able to make loads from a buffer that refer to the
// same original image/param/etc. This visitor finds a load to the
// buffer we want to load from, and generates a similar load, but with
// different args.
class MakeSimilarLoad : public IRVisitor {
public:
    const std::string &buf_name;
    const std::vector<Expr> &args;
    Expr load;

    MakeSimilarLoad(std::string name, const std::vector<Expr> &args)
        : buf_name(name), args(args) {}

private:
    using IRVisitor::visit;

    void visit(const Call *op) {
        if (op->name == buf_name) {
            load = Call::make(op->type, op->name, args, op->call_type, op->func, op->value_index, op->image, op->param);
        } else {
            IRVisitor::visit(op);
        }
    }
};

Expr make_similar_load(Stmt s, const std::string &name, const std::vector<Expr> &args) {
    MakeSimilarLoad v(name, args);
    s.accept(&v);
    return v.load;
}

// Build a Box representing the bounds of a buffer.
Box buffer_bounds(const string &buf_name, int dims) {
    Box bounds;
    for (int i = 0; i < dims; i++) {
        string dim_name = std::to_string(i);

        Expr buf_min_i = Variable::make(Int(32), buf_name + ".min." + dim_name);
        Expr buf_extent_i = Variable::make(Int(32), buf_name + ".extent." + dim_name);
        Expr buf_max_i = buf_min_i + buf_extent_i - 1;

        bounds.push_back(Interval(buf_min_i, buf_max_i));
    }
    return bounds;
}

class InjectPrefetch : public IRMutator {
public:
    InjectPrefetch(const map<string, Function> &e) : env(e) { }

private:
    const map<string, Function> &env;
    const vector<Prefetch> *prefetches = nullptr;
    Scope<Interval> bounds;

private:
    using IRMutator::visit;

    void visit(const Let *op) {
        Interval in = bounds_of_expr_in_scope(op->value, bounds);
        bounds.push(op->name, in);
        IRMutator::visit(op);
        bounds.pop(op->name);
    }

    void visit(const LetStmt *op) {
        Interval in = bounds_of_expr_in_scope(op->value, bounds);
        bounds.push(op->name, in);
        IRMutator::visit(op);
        bounds.pop(op->name);
    }

    void visit(const ProducerConsumer *op) {
        const vector<Prefetch> *old_prefetches = prefetches;

        map<string, Function>::const_iterator iter = env.find(op->name);
        internal_assert(iter != env.end()) << "function not in environment.\n";
        prefetches = &iter->second.schedule().prefetches();
        IRMutator::visit(op);
        prefetches = old_prefetches;
    }

    Stmt add_prefetch(const string &buf_name, const Box &box, Stmt body) {
        // Construct the bounds to be prefetched.
        vector<Expr> prefetch_min;
        vector<Expr> prefetch_extent;
        for (size_t i = 0; i < box.size(); i++) {
            prefetch_min.push_back(box[i].min);
            prefetch_extent.push_back(box[i].max - box[i].min + 1);
        }

        // Construct an array of index expressions to construct
        // address_of calls with. The first 2 dimensions are handled
        // by (up to) 2D prefetches, the rest we will generate loops
        // to define.
        vector<string> index_names(box.size());
        vector<Expr> indices(box.size());
        for (size_t i = 0; i < box.size(); i++) {
            index_names[i] = "prefetch_" + buf_name + "." + std::to_string(i);
            indices[i] = i < 2 ? prefetch_min[i] : Variable::make(Int(32), index_names[i]);
        }

        // Make a load at the index and get the address.
        Expr prefetch_load = make_similar_load(body, buf_name, indices);
        internal_assert(prefetch_load.defined());
        Type type = prefetch_load.type();
        Expr prefetch_addr = Call::make(Handle(), Call::address_of, {prefetch_load}, Call::Intrinsic);

        Stmt prefetch;
        Expr stride_0 = Variable::make(Int(32), buf_name + ".stride.0");
        // TODO: This is only correct if stride_0 is 1. We need to assert that this is true.
        Expr extent_0_bytes = prefetch_extent[0] * type.bytes();
        if (box.size() == 1) {
            // The prefetch is only 1 dimensional, just emit a flat prefetch.
            prefetch = Evaluate::make(Call::make(Int(32), Call::prefetch,
                                                 {prefetch_addr, extent_0_bytes},
                                                 Call::PureIntrinsic));
        } else {
            // Make a 2D prefetch.
            Expr stride_1 = Variable::make(Int(32), buf_name + ".stride.1");
            Expr stride_1_bytes = stride_1 * type.bytes();
            prefetch = Evaluate::make(Call::make(Int(32), Call::prefetch_2d,
                                                 {prefetch_addr, extent_0_bytes, prefetch_extent[1], stride_1_bytes},
                                                 Call::PureIntrinsic));

            // Make loops for the rest of the dimensions (possibly zero).
            for (size_t i = 2; i < box.size(); i++) {
                prefetch = For::make(index_names[i], prefetch_min[i], prefetch_extent[i],
                                     ForType::Serial, DeviceAPI::None,
                                     prefetch);
            }
        }

        // We should only prefetch buffers that are used.
        if (box.maybe_unused()) {
            prefetch = IfThenElse::make(box.used, prefetch);
        }

        return Block::make({prefetch, body});
    }

    void visit(const For *op) {
        // Add loop variable to interval scope for any inner loop prefetch
        Expr loop_var = Variable::make(Int(32), op->name);
        bounds.push(op->name, Interval(loop_var, loop_var));
        Stmt body = mutate(op->body);
        bounds.pop(op->name);

        if (prefetches) {
            for (const Prefetch &p : *prefetches) {
                if (!ends_with(op->name, "." + p.var)) {
                    continue;
                }
                // Add loop variable + prefetch offset to interval scope for box computation
                Expr fetch_at = loop_var + p.offset;
                bounds.push(op->name, Interval(fetch_at, fetch_at));
                map<string, Box> boxes_read = boxes_required(body, bounds);
                bounds.pop(op->name);

                // Don't prefetch buffers that are written to. We assume that these already
                // have good locality.
                // TODO: This is not a good assumption. It would be better to have the
                // prefetch directive specify the buffer that we want to prefetch, instead
                // of trying to figure out which buffers should be prefetched. This would also
                // mean that we don't need the "make_similar_load" hack, because we can make
                // calls the standard way (using the ImageParam/Function object referenced in
                // the prefetch).
                map<string, Box> boxes_written = boxes_provided(body, bounds);
                for (const auto &b : boxes_written) {
                    auto it = boxes_read.find(b.first);
                    if (it != boxes_read.end()) {
                        debug(2) << "Not prefetching buffer " << it->first
                                 << " also written in loop " << op->name << "\n";
                        boxes_read.erase(it);
                    }
                }

                // TODO: Only prefetch the newly accessed data from the previous iteration.
                // This should use boxes_touched (instead of boxes_required) so we exclude memory
                // either read or written.
                for (const auto &b : boxes_read) {
                    const std::string &buf_name = b.first;

                    // Only prefetch the region that is in bounds.
                    Box bounds = buffer_bounds(buf_name, b.second.size());
                    Box prefetch_box = box_intersection(b.second, bounds);

                    body = add_prefetch(buf_name, prefetch_box, body);
                }
            }
        }

        if (!body.same_as(op->body)) {
            stmt = For::make(op->name, op->min, op->extent, op->for_type, op->device_api, body);
        } else {
            stmt = op;
        }
    }

};

} // namespace

Stmt inject_prefetch(Stmt s, const std::map<std::string, Function> &env) {
    return InjectPrefetch(env).mutate(s);
}

}
}
<|endoftext|>


<commit_before>/*
 * Universe
 *
 * Copyright 2015 Lubosz Sarnecki <lubosz@gmail.com>
 *
 */

#include "Renderer.h"

#include "util.h"
#include <gli/gli.hpp>

Renderer::Renderer(int width, int height) {
    translate_z = -1.f;
    rotate_x = 0.0;
    rotate_y = 0.0;

    if (gl3wInit()) {
        fprintf(stderr, "failed to initialize gl3w\n");
        return;
    }
    if (!gl3wIsSupported(4, 5)) {
        fprintf(stderr, "OpenGL 4.5 not supported\n");
        return;
    }

    printContextInfo();
    initShaders();
    glUseProgram(shader_programm);

    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

    GLuint tex = initTexture("media/cloud.dds");
    glBindTexture(GL_TEXTURE_2D, tex);

    GLint texLoc = glGetUniformLocation(shader_programm, "cloud");
    glUniform1i(texLoc, tex-1);

    updateModel();
    updateProjection(width, height);

    glClearColor(0.0, 0.0, 0.0, 1.0);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindVertexArray(vao);

    glError;
}

Renderer::~Renderer() {}

void Renderer::draw(GLuint positionVBO, GLuint colorVBO, int particleCount) {
  // render the particles from VBOs
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);
  glEnableVertexAttribArray(0);
  glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
  glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);

  glEnableVertexAttribArray(1);
  glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
  glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, NULL);

  glDrawArrays(GL_POINTS, 0, particleCount);
}

void Renderer::printContextInfo() {
    printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
    printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
    printf("GL_SHADING_LANGUAGE_VERSION: %s\n",
           glGetString(GL_SHADING_LANGUAGE_VERSION));
}

GLuint Renderer::initTexture(char const* Filename) {
    gli::texture Texture = gli::load(Filename);
    if (Texture.empty())
        return 0;

    gli::gl GL;
    gli::gl::format const Format = GL.translate(Texture.format());
    GLenum Target = GL.translate(Texture.target());

    GLuint TextureName = 0;
    glGenTextures(1, &TextureName);
    glBindTexture(Target, TextureName);
    glTexParameteri(Target, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(Target,
        GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(Texture.levels() - 1));
    /*
    glTexParameteri(Target, GL_TEXTURE_SWIZZLE_R, Format.Swizzle[0]);
    glTexParameteri(Target, GL_TEXTURE_SWIZZLE_G, Format.Swizzle[1]);
    glTexParameteri(Target, GL_TEXTURE_SWIZZLE_B, Format.Swizzle[2]);
    glTexParameteri(Target, GL_TEXTURE_SWIZZLE_A, Format.Swizzle[3]);
    */
    glm::tvec3<GLsizei> const Dimensions(Texture.dimensions());
    GLsizei const FaceTotal =
            static_cast<GLsizei>(Texture.layers() * Texture.faces());

    switch (Texture.target()) {
        case gli::TARGET_1D:
            glTexStorage1D(
                Target,
                static_cast<GLint>(Texture.levels()),
                Format.Internal, Dimensions.x);
            break;
        case gli::TARGET_1D_ARRAY:
        case gli::TARGET_2D:
        case gli::TARGET_CUBE:
            glTexStorage2D(
                Target, static_cast<GLint>(Texture.levels()), Format.Internal,
                Dimensions.x,
                Texture.target() == gli::TARGET_2D ? Dimensions.y : FaceTotal);
            break;
        case gli::TARGET_2D_ARRAY:
        case gli::TARGET_3D:
        case gli::TARGET_CUBE_ARRAY:
            glTexStorage3D(
                Target, static_cast<GLint>(Texture.levels()), Format.Internal,
                Dimensions.x, Dimensions.y,
                Texture.target() == gli::TARGET_3D ? Dimensions.z : FaceTotal);
            break;
        default:
            assert(0);
            break;
    }

    for (std::size_t Layer = 0; Layer < Texture.layers(); ++Layer)
    for (std::size_t Face = 0; Face < Texture.faces(); ++Face)
    for (std::size_t Level = 0; Level < Texture.levels(); ++Level) {
        GLsizei const LayerGL = static_cast<GLsizei>(Layer);
        glm::tvec3<GLsizei> Dimensions(Texture.dimensions(Level));
        Target = gli::is_target_cube(Texture.target())
            ? static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + Face)
            : Target;

        switch (Texture.target()) {
        case gli::TARGET_1D:
            if (gli::is_compressed(Texture.format()))
                glCompressedTexSubImage1D(
                    Target, static_cast<GLint>(Level), 0, Dimensions.x,
                    Format.Internal, static_cast<GLsizei>(Texture.size(Level)),
                    Texture.data(Layer, Face, Level));
            else
                glTexSubImage1D(
                    Target, static_cast<GLint>(Level), 0, Dimensions.x,
                    Format.External, Format.Type,
                    Texture.data(Layer, Face, Level));
            break;
        case gli::TARGET_1D_ARRAY:
        case gli::TARGET_2D:
        case gli::TARGET_CUBE:
            if (gli::is_compressed(Texture.format()))
                glCompressedTexSubImage2D(
                    Target, static_cast<GLint>(Level),
                    0, 0,
                    Dimensions.x,
                    Dimensions.y,
                    Format.Internal, static_cast<GLsizei>(Texture.size(Level)),
                    Texture.data(Layer, Face, Level));
            else
                glTexSubImage2D(
                    Target, static_cast<GLint>(Level),
                    0, 0,
                    Dimensions.x,
                    Dimensions.y,
                    Format.External, Format.Type,
                    Texture.data(Layer, Face, Level));
            break;
        case gli::TARGET_2D_ARRAY:
        case gli::TARGET_3D:
        case gli::TARGET_CUBE_ARRAY:
            if (gli::is_compressed(Texture.format()))
                glCompressedTexSubImage3D(
                    Target, static_cast<GLint>(Level),
                    0, 0, 0,
                    Dimensions.x, Dimensions.y,
                    Texture.target() == LayerGL,
                    Format.Internal, static_cast<GLsizei>(Texture.size(Level)),
                    Texture.data(Layer, Face, Level));
            else
                glTexSubImage3D(
                    Target, static_cast<GLint>(Level),
                    0, 0, 0,
                    Dimensions.x, Dimensions.y,
                    Texture.target() == LayerGL,
                    Format.External, Format.Type,
                    Texture.data(Layer, Face, Level));
            break;
        default: assert(0); break;
        }
    }
    return TextureName;
}

void Renderer::initShaders() {
    shaders.push_back(readFile("gpu/MVP.vert"));
    shaders.push_back(readFile("gpu/color.frag"));

    const char* vertex[] = {shaders[0].c_str()};
    const char* fragment[] = {shaders[1].c_str()};

    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vs, 1, vertex, NULL);
    glCompileShader(vs);
    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fs, 1, fragment, NULL);
    glCompileShader(fs);

    shader_programm = glCreateProgram();
    glAttachShader(shader_programm, fs);
    glAttachShader(shader_programm, vs);
    glLinkProgram(shader_programm);
}

void Renderer::rotate(float x, float y) {
    rotate_x += y * 0.2;
    rotate_y += x * 0.2;
    updateModel();
}

void Renderer::translate(float z) {
    translate_z += z * 0.1;
    updateModel();
}

void Renderer::updateModel() {
    model = glm::mat4(1.0f);
    model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, translate_z));
    model = glm::rotate(model, rotate_x, glm::vec3(1.0f, 0.0f, 0.0f));
    model = glm::rotate(model, rotate_y, glm::vec3(0.0f, 1.0f, 0.0f));
    updateMVP();
}

void Renderer::updateProjection(int width, int height) {
    glViewport(0, 0, width, height);
    float aspect = static_cast<GLfloat>(width)
            / static_cast<GLfloat>(height);
    projection = glm::perspective(90.0f, aspect, 0.1f, 1000.f);
    updateMVP();
}

void Renderer::updateMVP() {
    glm::mat4 mvp = projection * model;
    GLint UniformMVP = glGetUniformLocation(shader_programm, "mvp");
    glUniformMatrix4fv(UniformMVP, 1, GL_FALSE, &mvp[0][0]);
}

void Renderer::checkGlError(const char* file, int line) {
    GLenum err(glGetError());

    while (err != GL_NO_ERROR) {
        std::string error;
        switch (err) {
            case GL_INVALID_OPERATION:
                error = "INVALID_OPERATION";
                break;
            case GL_INVALID_ENUM:
                error = "INVALID_ENUM";
                break;
            case GL_INVALID_VALUE:
                error = "INVALID_VALUE";
                break;
            case GL_OUT_OF_MEMORY:
                error = "OUT_OF_MEMORY";
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                error = "INVALID_FRAMEBUFFER_OPERATION";
                break;
            default:
                error = "Unknown error";
                break;
        }
        std::cout
                << "GL ERROR: GL_"
                << error << " "
                << file << " "
                << line << "\n";
        err = glGetError();
    }
}

GLuint Renderer::createVBO(
        const void* data,
        int dataSize,
        GLenum target,
        GLenum usage) {
    GLuint id;
    glGenBuffers(1, &id);
    glBindBuffer(target, id);
    glBufferData(target, dataSize, data, usage);

    // check data size in VBO is same as input array
    // if not return 0 and delete VBO
    int bufferSize = 0;
    glGetBufferParameteriv(target, GL_BUFFER_SIZE, &bufferSize);
    if (dataSize != bufferSize) {
        glDeleteBuffers(1, &id);
        id = 0;
        std::cout << "[createVBO()] Data size is mismatch with input array\n";
    }
    glBindBuffer(target, 0);
    return id;
}
<commit_msg>renderer: make image loading shorter<commit_after>/*
 * Universe
 *
 * Copyright 2015 Lubosz Sarnecki <lubosz@gmail.com>
 *
 */

#include "Renderer.h"

#include "util.h"
#include <gli/gli.hpp>

Renderer::Renderer(int width, int height) {
    translate_z = -1.f;
    rotate_x = 0.0;
    rotate_y = 0.0;

    if (gl3wInit()) {
        fprintf(stderr, "failed to initialize gl3w\n");
        return;
    }
    if (!gl3wIsSupported(4, 5)) {
        fprintf(stderr, "OpenGL 4.5 not supported\n");
        return;
    }

    printContextInfo();
    initShaders();
    glUseProgram(shader_programm);

    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

    GLuint tex = initTexture("media/cloud.dds");
    glBindTexture(GL_TEXTURE_2D, tex);

    GLint texLoc = glGetUniformLocation(shader_programm, "cloud");
    glUniform1i(texLoc, tex-1);

    updateModel();
    updateProjection(width, height);

    glClearColor(0.0, 0.0, 0.0, 1.0);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindVertexArray(vao);

    glError;
}

Renderer::~Renderer() {}

void Renderer::draw(GLuint positionVBO, GLuint colorVBO, int particleCount) {
  // render the particles from VBOs
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);
  glEnableVertexAttribArray(0);
  glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
  glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);

  glEnableVertexAttribArray(1);
  glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
  glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, NULL);

  glDrawArrays(GL_POINTS, 0, particleCount);
}

void Renderer::printContextInfo() {
    printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
    printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
    printf("GL_SHADING_LANGUAGE_VERSION: %s\n",
           glGetString(GL_SHADING_LANGUAGE_VERSION));
}

GLuint Renderer::initTexture(char const* Filename) {
    gli::texture Texture = gli::load(Filename);
    if (Texture.empty())
        return 0;

    gli::gl GL;
    gli::gl::format const Format = GL.translate(Texture.format());
    GLenum Target = GL.translate(Texture.target());

    GLuint TextureName = 0;
    glGenTextures(1, &TextureName);
    glBindTexture(Target, TextureName);
    glTexParameteri(Target, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(Target,
                    GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(Texture.levels() - 1));

    glm::tvec3<GLsizei> const Dimensions(Texture.dimensions());
    GLsizei const FaceTotal =
            static_cast<GLsizei>(Texture.layers() * Texture.faces());

    glTexStorage2D(
                Target, static_cast<GLint>(Texture.levels()), Format.Internal,
                Dimensions.x,
                Texture.target() == gli::TARGET_2D ? Dimensions.y : FaceTotal);

    for (std::size_t Layer = 0;
         Layer < Texture.layers(); ++Layer)
        for (std::size_t Face = 0;
             Face < Texture.faces(); ++Face)
            for (std::size_t Level = 0;
                 Level < Texture.levels(); ++Level) {
                glm::tvec3<GLsizei> Dimensions(Texture.dimensions(Level));

                /*
                printf("layer %ld, face %ld, level %ld, iscompressed %d\n",
                       Layer, Face, Level,
                       gli::is_compressed(Texture.format()));
                */
                if (gli::is_compressed(Texture.format()))
                    glCompressedTexSubImage2D(
                                Target, static_cast<GLint>(Level),
                                0, 0,
                                Dimensions.x,
                                Dimensions.y,
                                Format.Internal, static_cast<GLsizei>(Texture.size(Level)),
                                Texture.data(Layer, Face, Level));
                else
                    glTexSubImage2D(
                                Target, static_cast<GLint>(Level),
                                0, 0,
                                Dimensions.x,
                                Dimensions.y,
                                Format.External, Format.Type,
                                Texture.data(Layer, Face, Level));
            }
    return TextureName;
}

void Renderer::initShaders() {
    shaders.push_back(readFile("gpu/MVP.vert"));
    shaders.push_back(readFile("gpu/color.frag"));

    const char* vertex[] = {shaders[0].c_str()};
    const char* fragment[] = {shaders[1].c_str()};

    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vs, 1, vertex, NULL);
    glCompileShader(vs);
    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fs, 1, fragment, NULL);
    glCompileShader(fs);

    shader_programm = glCreateProgram();
    glAttachShader(shader_programm, fs);
    glAttachShader(shader_programm, vs);
    glLinkProgram(shader_programm);
}

void Renderer::rotate(float x, float y) {
    rotate_x += y * 0.2;
    rotate_y += x * 0.2;
    updateModel();
}

void Renderer::translate(float z) {
    translate_z += z * 0.1;
    updateModel();
}

void Renderer::updateModel() {
    model = glm::mat4(1.0f);
    model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, translate_z));
    model = glm::rotate(model, rotate_x, glm::vec3(1.0f, 0.0f, 0.0f));
    model = glm::rotate(model, rotate_y, glm::vec3(0.0f, 1.0f, 0.0f));
    updateMVP();
}

void Renderer::updateProjection(int width, int height) {
    glViewport(0, 0, width, height);
    float aspect = static_cast<GLfloat>(width)
            / static_cast<GLfloat>(height);
    projection = glm::perspective(90.0f, aspect, 0.1f, 1000.f);
    updateMVP();
}

void Renderer::updateMVP() {
    glm::mat4 mvp = projection * model;
    GLint UniformMVP = glGetUniformLocation(shader_programm, "mvp");
    glUniformMatrix4fv(UniformMVP, 1, GL_FALSE, &mvp[0][0]);
}

void Renderer::checkGlError(const char* file, int line) {
    GLenum err(glGetError());

    while (err != GL_NO_ERROR) {
        std::string error;
        switch (err) {
            case GL_INVALID_OPERATION:
                error = "INVALID_OPERATION";
                break;
            case GL_INVALID_ENUM:
                error = "INVALID_ENUM";
                break;
            case GL_INVALID_VALUE:
                error = "INVALID_VALUE";
                break;
            case GL_OUT_OF_MEMORY:
                error = "OUT_OF_MEMORY";
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                error = "INVALID_FRAMEBUFFER_OPERATION";
                break;
            default:
                error = "Unknown error";
                break;
        }
        std::cout
                << "GL ERROR: GL_"
                << error << " "
                << file << " "
                << line << "\n";
        err = glGetError();
    }
}

GLuint Renderer::createVBO(
        const void* data,
        int dataSize,
        GLenum target,
        GLenum usage) {
    GLuint id;
    glGenBuffers(1, &id);
    glBindBuffer(target, id);
    glBufferData(target, dataSize, data, usage);

    // check data size in VBO is same as input array
    // if not return 0 and delete VBO
    int bufferSize = 0;
    glGetBufferParameteriv(target, GL_BUFFER_SIZE, &bufferSize);
    if (dataSize != bufferSize) {
        glDeleteBuffers(1, &id);
        id = 0;
        std::cout << "[createVBO()] Data size is mismatch with input array\n";
    }
    glBindBuffer(target, 0);
    return id;
}
<|endoftext|>


<commit_before>/**
 * Copyright 2013 Da Zheng
 *
 * This file is part of SAFSlib.
 *
 * SAFSlib 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.
 *
 * SAFSlib 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 SAFSlib.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "rand_buf.h"

rand_buf::rand_buf(int buf_size, int entry_size, int nodeid)
#ifdef MEMCHECK
	: allocator(entry_size)
#else
	// We don't initialize pages but pin them.
	: allocator(std::string("rand_buf-") + itoa(nodeid), entry_size,
			buf_size, buf_size, nodeid, false, true, 0, false)
#endif
{
	this->entry_size = entry_size;
}

char *rand_buf::next_entry(int size) {
#ifdef MEMCHECK
	return (char *) allocator.alloc(size);
#else
	if (size > entry_size)
		return (char *) valloc(size);
	return allocator.alloc();
#if 0
	pthread_spin_lock(&lock);
	assert(!free_refs.is_empty());
	int off = free_refs.pop_front();
	assert(marks[off / entry_size] == 0);
	marks[off / entry_size] = 1;
	char *ret = &buf[off];
	pthread_spin_unlock(&lock);
	return ret;
#endif
#endif
}

void rand_buf::free_entry(char *buf) {
#ifdef MEMCHECK
	allocator.dealloc(buf);
#else
	if (allocator.contains(buf))
		allocator.free(buf);
	else
		free(buf);
#if 0
	pthread_spin_lock(&lock);
	off_t off = buf - this->buf;
	free_refs.push_back(off);
	off /= entry_size;
	if (marks[off] == 0)
		printf("%ld: free %p error\n", pthread_self(), buf);
	assert(marks[off]);
	marks[off] = 0;
	pthread_spin_unlock(&lock);
#endif
#endif
}
<commit_msg>[SAFS]: replace valloc with posix_memalign for larger mem alloc.<commit_after>/**
 * Copyright 2013 Da Zheng
 *
 * This file is part of SAFSlib.
 *
 * SAFSlib 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.
 *
 * SAFSlib 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 SAFSlib.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "rand_buf.h"

rand_buf::rand_buf(int buf_size, int entry_size, int nodeid)
#ifdef MEMCHECK
	: allocator(entry_size)
#else
	// We don't initialize pages but pin them.
	: allocator(std::string("rand_buf-") + itoa(nodeid), entry_size,
			buf_size, buf_size, nodeid, false, true, 0, false)
#endif
{
	this->entry_size = entry_size;
}

char *rand_buf::next_entry(int size) {
#ifdef MEMCHECK
	return (char *) allocator.alloc(size);
#else
	if (size > entry_size) {
		void *buf;
		int ret = posix_memalign(&buf, MIN_BLOCK_SIZE, size);
		assert(ret == 0);
		return (char *) buf;
	}
	return allocator.alloc();
#if 0
	pthread_spin_lock(&lock);
	assert(!free_refs.is_empty());
	int off = free_refs.pop_front();
	assert(marks[off / entry_size] == 0);
	marks[off / entry_size] = 1;
	char *ret = &buf[off];
	pthread_spin_unlock(&lock);
	return ret;
#endif
#endif
}

void rand_buf::free_entry(char *buf) {
#ifdef MEMCHECK
	allocator.dealloc(buf);
#else
	if (allocator.contains(buf))
		allocator.free(buf);
	else
		free(buf);
#if 0
	pthread_spin_lock(&lock);
	off_t off = buf - this->buf;
	free_refs.push_back(off);
	off /= entry_size;
	if (marks[off] == 0)
		printf("%ld: free %p error\n", pthread_self(), buf);
	assert(marks[off]);
	marks[off] = 0;
	pthread_spin_unlock(&lock);
#endif
#endif
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include "SurfaceHandler.h"

#include <react/debug/react_native_assert.h>
#include <react/renderer/scheduler/Scheduler.h>
#include <react/renderer/uimanager/UIManager.h>

namespace facebook {
namespace react {

using Status = SurfaceHandler::Status;
using DisplayMode = SurfaceHandler::DisplayMode;

SurfaceHandler::SurfaceHandler(
    std::string const &moduleName,
    SurfaceId surfaceId) noexcept {
  parameters_.moduleName = moduleName;
  parameters_.surfaceId = surfaceId;
}

SurfaceHandler::SurfaceHandler(SurfaceHandler &&other) noexcept {
  operator=(std::move(other));
}

SurfaceHandler &SurfaceHandler::operator=(SurfaceHandler &&other) noexcept {
  std::unique_lock<better::shared_mutex> lock1(linkMutex_, std::defer_lock);
  std::unique_lock<better::shared_mutex> lock2(
      parametersMutex_, std::defer_lock);
  std::unique_lock<better::shared_mutex> lock3(
      other.linkMutex_, std::defer_lock);
  std::unique_lock<better::shared_mutex> lock4(
      other.parametersMutex_, std::defer_lock);
  std::lock(lock1, lock2, lock3, lock4);

  link_ = other.link_;
  parameters_ = other.parameters_;

  other.link_ = Link{};
  other.parameters_ = Parameters{};
  return *this;
}

#pragma mark - Surface Life-Cycle Management

Status SurfaceHandler::getStatus() const noexcept {
  std::shared_lock<better::shared_mutex> lock(linkMutex_);
  return link_.status;
}

void SurfaceHandler::start() const noexcept {
  {
    std::unique_lock<better::shared_mutex> lock(linkMutex_);
    react_native_assert(
        link_.status == Status::Registered && "Surface must be registered.");
    react_native_assert(
        getLayoutConstraints().layoutDirection != LayoutDirection::Undefined &&
        "layoutDirection must be set.");

    auto parameters = Parameters{};
    {
      std::shared_lock<better::shared_mutex> parametersLock(parametersMutex_);
      parameters = parameters_;
    }

    auto shadowTree = std::make_unique<ShadowTree>(
        parameters.surfaceId,
        parameters.layoutConstraints,
        parameters.layoutContext,
        *link_.uiManager);

    link_.shadowTree = shadowTree.get();

    link_.uiManager->startSurface(
        std::move(shadowTree), parameters.moduleName, parameters.props);

    link_.status = Status::Running;

    applyDisplayMode(parameters.displayMode);
  }
}

void SurfaceHandler::stop() const noexcept {
  auto shadowTree = ShadowTree::Unique{};
  {
    std::unique_lock<better::shared_mutex> lock(linkMutex_);
    react_native_assert(
        link_.status == Status::Running && "Surface must be running.");

    link_.status = Status::Registered;
    link_.shadowTree = nullptr;
    shadowTree = link_.uiManager->stopSurface(parameters_.surfaceId);
  }

  // As part of stopping a Surface, we need to properly destroy all
  // mounted views, so we need to commit an empty tree to trigger all
  // side-effects (including destroying and removing mounted views).
  react_native_assert(shadowTree && "`shadowTree` must not be null.");
  shadowTree->commitEmptyTree();
}

void SurfaceHandler::setDisplayMode(DisplayMode displayMode) const noexcept {
  {
    std::unique_lock<better::shared_mutex> lock(parametersMutex_);
    if (parameters_.displayMode == displayMode) {
      return;
    }

    parameters_.displayMode = displayMode;
  }

  {
    std::shared_lock<better::shared_mutex> lock(linkMutex_);

    if (link_.status != Status::Running) {
      return;
    }

    applyDisplayMode(displayMode);
  }
}

DisplayMode SurfaceHandler::getDisplayMode() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.displayMode;
}

#pragma mark - Accessors

SurfaceId SurfaceHandler::getSurfaceId() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.surfaceId;
}

void SurfaceHandler::setSurfaceId(SurfaceId surfaceId) const noexcept {
  std::unique_lock<better::shared_mutex> lock(parametersMutex_);
  parameters_.surfaceId = surfaceId;
}

std::string SurfaceHandler::getModuleName() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.moduleName;
}

void SurfaceHandler::setProps(folly::dynamic const &props) const noexcept {
  std::unique_lock<better::shared_mutex> lock(parametersMutex_);
  parameters_.props = props;
}

folly::dynamic SurfaceHandler::getProps() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.props;
}

std::shared_ptr<MountingCoordinator const>
SurfaceHandler::getMountingCoordinator() const noexcept {
  std::shared_lock<better::shared_mutex> lock(linkMutex_);
  react_native_assert(
      link_.status != Status::Unregistered && "Surface must be registered.");
  react_native_assert(
      link_.shadowTree && "`link_.shadowTree` must not be null.");
  return link_.shadowTree->getMountingCoordinator();
}

#pragma mark - Layout

Size SurfaceHandler::measure(
    LayoutConstraints const &layoutConstraints,
    LayoutContext const &layoutContext) const noexcept {
  std::shared_lock<better::shared_mutex> lock(linkMutex_);

  if (link_.status != Status::Running) {
    return layoutConstraints.clamp({0, 0});
  }

  react_native_assert(
      link_.shadowTree && "`link_.shadowTree` must not be null.");

  auto currentRootShadowNode =
      link_.shadowTree->getCurrentRevision().rootShadowNode;

  auto rootShadowNode =
      currentRootShadowNode->clone(layoutConstraints, layoutContext);
  rootShadowNode->layoutIfNeeded();
  return rootShadowNode->getLayoutMetrics().frame.size;
}

void SurfaceHandler::constraintLayout(
    LayoutConstraints const &layoutConstraints,
    LayoutContext const &layoutContext) const noexcept {
  {
    std::unique_lock<better::shared_mutex> lock(parametersMutex_);

    if (parameters_.layoutConstraints == layoutConstraints &&
        parameters_.layoutContext == layoutContext) {
      return;
    }

    parameters_.layoutConstraints = layoutConstraints;
    parameters_.layoutContext = layoutContext;
  }

  {
    std::shared_lock<better::shared_mutex> lock(linkMutex_);

    if (link_.status != Status::Running) {
      return;
    }

    react_native_assert(
        link_.shadowTree && "`link_.shadowTree` must not be null.");
    link_.shadowTree->commit([&](RootShadowNode const &oldRootShadowNode) {
      return oldRootShadowNode.clone(layoutConstraints, layoutContext);
    });
  }
}

LayoutConstraints SurfaceHandler::getLayoutConstraints() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.layoutConstraints;
}

LayoutContext SurfaceHandler::getLayoutContext() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.layoutContext;
}

#pragma mark - Private

void SurfaceHandler::applyDisplayMode(DisplayMode displayMode) const noexcept {
  react_native_assert(
      link_.status == Status::Running && "Surface must be running.");
  react_native_assert(
      link_.shadowTree && "`link_.shadowTree` must not be null.");

  switch (displayMode) {
    case DisplayMode::Visible:
      link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Normal);
      break;
    case DisplayMode::Suspended:
      link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Suspended);
      break;
    case DisplayMode::Hidden:
      link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Normal);
      // Getting a current revision.
      auto revision = link_.shadowTree->getCurrentRevision();
      // Committing an empty tree to force mounting to disassemble view
      // hierarchy.
      link_.shadowTree->commitEmptyTree();
      link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Suspended);
      // Committing the current revision back. It will be mounted only when
      // `DisplayMode` is changed back to `Normal`.
      link_.shadowTree->commit([&](RootShadowNode const &oldRootShadowNode) {
        return std::static_pointer_cast<RootShadowNode>(
            revision.rootShadowNode->ShadowNode::clone(ShadowNodeFragment{}));
      });
      break;
  }
}

void SurfaceHandler::setUIManager(UIManager const *uiManager) const noexcept {
  std::unique_lock<better::shared_mutex> lock(linkMutex_);

  react_native_assert(
      link_.status != Status::Running && "Surface must not be running.");

  if (link_.uiManager == uiManager) {
    return;
  }

  link_.uiManager = uiManager;
  link_.status = uiManager ? Status::Registered : Status::Unregistered;
}

SurfaceHandler::~SurfaceHandler() noexcept {
  // TODO(T88046056): Fix Android memory leak before uncommenting changes
  //  react_native_assert(
  //      link_.status == Status::Unregistered &&
  //      "`SurfaceHandler` must be unregistered (or moved-from) before
  //      deallocation.");
}

} // namespace react
} // namespace facebook
<commit_msg>Update initial props when DisplayMode changes in Fabric<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include "SurfaceHandler.h"

#include <react/debug/react_native_assert.h>
#include <react/renderer/scheduler/Scheduler.h>
#include <react/renderer/uimanager/UIManager.h>

namespace facebook {
namespace react {

using Status = SurfaceHandler::Status;
using DisplayMode = SurfaceHandler::DisplayMode;

SurfaceHandler::SurfaceHandler(
    std::string const &moduleName,
    SurfaceId surfaceId) noexcept {
  parameters_.moduleName = moduleName;
  parameters_.surfaceId = surfaceId;
}

SurfaceHandler::SurfaceHandler(SurfaceHandler &&other) noexcept {
  operator=(std::move(other));
}

SurfaceHandler &SurfaceHandler::operator=(SurfaceHandler &&other) noexcept {
  std::unique_lock<better::shared_mutex> lock1(linkMutex_, std::defer_lock);
  std::unique_lock<better::shared_mutex> lock2(
      parametersMutex_, std::defer_lock);
  std::unique_lock<better::shared_mutex> lock3(
      other.linkMutex_, std::defer_lock);
  std::unique_lock<better::shared_mutex> lock4(
      other.parametersMutex_, std::defer_lock);
  std::lock(lock1, lock2, lock3, lock4);

  link_ = other.link_;
  parameters_ = other.parameters_;

  other.link_ = Link{};
  other.parameters_ = Parameters{};
  return *this;
}

#pragma mark - Surface Life-Cycle Management

Status SurfaceHandler::getStatus() const noexcept {
  std::shared_lock<better::shared_mutex> lock(linkMutex_);
  return link_.status;
}

void SurfaceHandler::start() const noexcept {
  {
    std::unique_lock<better::shared_mutex> lock(linkMutex_);
    react_native_assert(
        link_.status == Status::Registered && "Surface must be registered.");
    react_native_assert(
        getLayoutConstraints().layoutDirection != LayoutDirection::Undefined &&
        "layoutDirection must be set.");

    auto parameters = Parameters{};
    {
      std::shared_lock<better::shared_mutex> parametersLock(parametersMutex_);
      parameters = parameters_;
    }

    auto shadowTree = std::make_unique<ShadowTree>(
        parameters.surfaceId,
        parameters.layoutConstraints,
        parameters.layoutContext,
        *link_.uiManager);

    link_.shadowTree = shadowTree.get();

    link_.uiManager->startSurface(
        std::move(shadowTree), parameters.moduleName, parameters.props);

    link_.status = Status::Running;

    applyDisplayMode(parameters.displayMode);
  }
}

void SurfaceHandler::stop() const noexcept {
  auto shadowTree = ShadowTree::Unique{};
  {
    std::unique_lock<better::shared_mutex> lock(linkMutex_);
    react_native_assert(
        link_.status == Status::Running && "Surface must be running.");

    link_.status = Status::Registered;
    link_.shadowTree = nullptr;
    shadowTree = link_.uiManager->stopSurface(parameters_.surfaceId);
  }

  // As part of stopping a Surface, we need to properly destroy all
  // mounted views, so we need to commit an empty tree to trigger all
  // side-effects (including destroying and removing mounted views).
  react_native_assert(shadowTree && "`shadowTree` must not be null.");
  shadowTree->commitEmptyTree();
}

void SurfaceHandler::setDisplayMode(DisplayMode displayMode) const noexcept {
  {
    std::unique_lock<better::shared_mutex> lock(parametersMutex_);
    if (parameters_.displayMode == displayMode) {
      return;
    }

    parameters_.displayMode = displayMode;
  }

  {
    std::shared_lock<better::shared_mutex> lock(linkMutex_);

    if (link_.status != Status::Running) {
      return;
    }

    link_.uiManager->setSurfaceProps(
        parameters_.surfaceId, parameters_.moduleName, parameters_.props);

    applyDisplayMode(displayMode);
  }
}

DisplayMode SurfaceHandler::getDisplayMode() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.displayMode;
}

#pragma mark - Accessors

SurfaceId SurfaceHandler::getSurfaceId() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.surfaceId;
}

void SurfaceHandler::setSurfaceId(SurfaceId surfaceId) const noexcept {
  std::unique_lock<better::shared_mutex> lock(parametersMutex_);
  parameters_.surfaceId = surfaceId;
}

std::string SurfaceHandler::getModuleName() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.moduleName;
}

void SurfaceHandler::setProps(folly::dynamic const &props) const noexcept {
  std::unique_lock<better::shared_mutex> lock(parametersMutex_);
  parameters_.props = props;
}

folly::dynamic SurfaceHandler::getProps() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.props;
}

std::shared_ptr<MountingCoordinator const>
SurfaceHandler::getMountingCoordinator() const noexcept {
  std::shared_lock<better::shared_mutex> lock(linkMutex_);
  react_native_assert(
      link_.status != Status::Unregistered && "Surface must be registered.");
  react_native_assert(
      link_.shadowTree && "`link_.shadowTree` must not be null.");
  return link_.shadowTree->getMountingCoordinator();
}

#pragma mark - Layout

Size SurfaceHandler::measure(
    LayoutConstraints const &layoutConstraints,
    LayoutContext const &layoutContext) const noexcept {
  std::shared_lock<better::shared_mutex> lock(linkMutex_);

  if (link_.status != Status::Running) {
    return layoutConstraints.clamp({0, 0});
  }

  react_native_assert(
      link_.shadowTree && "`link_.shadowTree` must not be null.");

  auto currentRootShadowNode =
      link_.shadowTree->getCurrentRevision().rootShadowNode;

  auto rootShadowNode =
      currentRootShadowNode->clone(layoutConstraints, layoutContext);
  rootShadowNode->layoutIfNeeded();
  return rootShadowNode->getLayoutMetrics().frame.size;
}

void SurfaceHandler::constraintLayout(
    LayoutConstraints const &layoutConstraints,
    LayoutContext const &layoutContext) const noexcept {
  {
    std::unique_lock<better::shared_mutex> lock(parametersMutex_);

    if (parameters_.layoutConstraints == layoutConstraints &&
        parameters_.layoutContext == layoutContext) {
      return;
    }

    parameters_.layoutConstraints = layoutConstraints;
    parameters_.layoutContext = layoutContext;
  }

  {
    std::shared_lock<better::shared_mutex> lock(linkMutex_);

    if (link_.status != Status::Running) {
      return;
    }

    react_native_assert(
        link_.shadowTree && "`link_.shadowTree` must not be null.");
    link_.shadowTree->commit([&](RootShadowNode const &oldRootShadowNode) {
      return oldRootShadowNode.clone(layoutConstraints, layoutContext);
    });
  }
}

LayoutConstraints SurfaceHandler::getLayoutConstraints() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.layoutConstraints;
}

LayoutContext SurfaceHandler::getLayoutContext() const noexcept {
  std::shared_lock<better::shared_mutex> lock(parametersMutex_);
  return parameters_.layoutContext;
}

#pragma mark - Private

void SurfaceHandler::applyDisplayMode(DisplayMode displayMode) const noexcept {
  react_native_assert(
      link_.status == Status::Running && "Surface must be running.");
  react_native_assert(
      link_.shadowTree && "`link_.shadowTree` must not be null.");

  switch (displayMode) {
    case DisplayMode::Visible:
      link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Normal);
      break;
    case DisplayMode::Suspended:
      link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Suspended);
      break;
    case DisplayMode::Hidden:
      link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Normal);
      // Getting a current revision.
      auto revision = link_.shadowTree->getCurrentRevision();
      // Committing an empty tree to force mounting to disassemble view
      // hierarchy.
      link_.shadowTree->commitEmptyTree();
      link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Suspended);
      // Committing the current revision back. It will be mounted only when
      // `DisplayMode` is changed back to `Normal`.
      link_.shadowTree->commit([&](RootShadowNode const &oldRootShadowNode) {
        return std::static_pointer_cast<RootShadowNode>(
            revision.rootShadowNode->ShadowNode::clone(ShadowNodeFragment{}));
      });
      break;
  }
}

void SurfaceHandler::setUIManager(UIManager const *uiManager) const noexcept {
  std::unique_lock<better::shared_mutex> lock(linkMutex_);

  react_native_assert(
      link_.status != Status::Running && "Surface must not be running.");

  if (link_.uiManager == uiManager) {
    return;
  }

  link_.uiManager = uiManager;
  link_.status = uiManager ? Status::Registered : Status::Unregistered;
}

SurfaceHandler::~SurfaceHandler() noexcept {
  // TODO(T88046056): Fix Android memory leak before uncommenting changes
  //  react_native_assert(
  //      link_.status == Status::Unregistered &&
  //      "`SurfaceHandler` must be unregistered (or moved-from) before
  //      deallocation.");
}

} // namespace react
} // namespace facebook
<|endoftext|>


<commit_before>// Copyright (C) 2016 xaizek <xaizek@openmailbox.org>
//
// This file is part of uncov.
//
// uncov is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// uncov is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with uncov.  If not, see <http://www.gnu.org/licenses/>.

#ifndef UNCOV__COVERAGE_HPP__
#define UNCOV__COVERAGE_HPP__

#include <string>

class CovInfo
{
    friend class CovChange;

public:
    CovInfo() = default;
    template <typename T>
    explicit CovInfo(const T &coverable)
        : coveredCount(coverable.getCoveredCount()),
          missedCount(coverable.getMissedCount())
    {
    }

public:
    void add(const CovInfo &rhs);
    std::string formatCoverageRate() const;
    std::string formatLines(const std::string &separator) const;

private:
    float getCoverage() const;
    int getRelevantLines() const;

private:
    int coveredCount = 0;
    int missedCount = 0;
};

class CovChange
{
public:
    CovChange(const CovInfo &oldCov, const CovInfo &newCov);

public:
    bool isChanged() const;
    std::string formatCoverageRate() const;
    std::string formatLines(const std::string &separator, int width = 0) const;

private:
    float coverageChange;
    int coveredChange;
    int missedChange;
    int relevantChange;
};

#endif // UNCOV__COVERAGE_HPP__
<commit_msg>Document coverage.hpp header<commit_after>// Copyright (C) 2016 xaizek <xaizek@openmailbox.org>
//
// This file is part of uncov.
//
// uncov is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// uncov is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with uncov.  If not, see <http://www.gnu.org/licenses/>.

#ifndef UNCOV__COVERAGE_HPP__
#define UNCOV__COVERAGE_HPP__

#include <string>

/**
 * @file coverage.hpp
 *
 * Coverage computation and formatting.
 */

/**
 * @brief Computes and formats coverage conformation.
 */
class CovInfo
{
    //! To give access to @c coveredCount and @c missedCount.
    friend class CovChange;

public:
    /**
     * @brief Constructs empty coverage information.
     */
    CovInfo() = default;
    /**
     * @brief Constructs coverage information from a Coverable.
     *
     * Coverable is any class that implements @c getCoveredCount() and
     * @c getMissedCount() functions that return @c int.
     *
     * @tparam T Type of the Coverable.
     *
     * @param coverable Source of the coverage information.
     */
    template <typename T>
    explicit CovInfo(const T &coverable)
        : coveredCount(coverable.getCoveredCount()),
          missedCount(coverable.getMissedCount())
    {
    }

public:
    /**
     * @brief Adds coverage information.
     *
     * @param rhs Another coverage information.
     */
    void add(const CovInfo &rhs);
    /**
     * @brief Formats coverage rate in the new state as a string.
     *
     * @returns Formatted string.
     */
    std::string formatCoverageRate() const;
    /**
     * @brief Formats coverage statistics in lines.
     *
     * Format is covered/missed/relevant.
     *
     * @param separator Separator of statistics.
     *
     * @returns Formatted string.
     */
    std::string formatLines(const std::string &separator) const;

private:
    /**
     * @brief Retrieves rate of source coverage.
     *
     * @returns The rate.
     */
    float getCoverage() const;
    /**
     * @brief Retrieves number of lines that are relevant for code coverage.
     *
     * @returns The number.
     */
    int getRelevantLines() const;

private:
    int coveredCount = 0; //!< Number of covered lines.
    int missedCount = 0;  //!< Number of missed lines.
};

/**
 * @brief Computes and formats coverage change.
 */
class CovChange
{
public:
    /**
     * @brief Computes coverage change between two states.
     *
     * @param oldCov Old coverage information.
     * @param newCov New coverage information.
     */
    CovChange(const CovInfo &oldCov, const CovInfo &newCov);

public:
    /**
     * @brief Whether new coverage information differs from the old one.
     *
     * @returns @c true if so, @c false otherwise.
     */
    bool isChanged() const;
    /**
     * @brief Formats change of coverage rate in the new state as a string.
     *
     * @returns Formatted string.
     */
    std::string formatCoverageRate() const;
    /**
     * @brief Formats changes coverage statistics in lines.
     *
     * Format is covered/missed/relevant.
     *
     * @param separator Separator of statistics.
     * @param width     Minimal width for first and second statistics.
     *
     * @returns Formatted string.
     */
    std::string formatLines(const std::string &separator, int width = 0) const;

private:
    float coverageChange; //!< Change of covered lines in percents.
    int coveredChange;    //!< Change of covered lines in lines.
    int missedChange;     //!< Change of missed lines in lines.
    int relevantChange;   //!< Change of relevant lines in lines.
};

#endif // UNCOV__COVERAGE_HPP__
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2020 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <tuple>
#include <utility>
#include <iostream>

#include "currency.hpp"
#include "assets.hpp" // For get_default_currency
#include "http.hpp"
#include "date.hpp"
#include "config.hpp"
#include "server_lock.hpp"

namespace {

struct currency_cache_key {
    budget::date date;
    std::string from;
    std::string to;

    currency_cache_key(budget::date date, std::string from, std::string to) : date(date), from(from), to(to) {}

    friend bool operator==(const currency_cache_key & lhs, const currency_cache_key & rhs){
        if (lhs.date != rhs.date) {
            return false;
        }

        if (lhs.from != rhs.from) {
            return false;
        }

        return lhs.to == rhs.to;
    }
};

} // end of anonymous namespace

namespace std {

template <>
struct hash<currency_cache_key> {
    std::size_t operator()(const currency_cache_key & key) const noexcept {
        auto seed = std::hash<budget::date>()(key.date);
        seed ^= std::hash<std::string>()(key.from) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        seed ^= std::hash<std::string>()(key.to) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        return seed;
    }
};

} // end of namespace std

namespace {

// OPTIM: If necessary, this could be made faster with a two layer cache
// We can take advantages from the low number of currency pair in the first layer
// and then the quick hash from the high number of dates

std::unordered_map<currency_cache_key, double> exchanges;
budget::server_lock exchanges_lock;

// V1 is using free.currencyconverterapi.com
double get_rate_v1(const std::string& from, const std::string& to){
    httplib::Client cli("free.currencyconverterapi.com", 80);

    std::string api_complete = "/api/v3/convert?q=" + from + "_" + to + "&compact=ultra";

    auto res = cli.Get(api_complete.c_str());

    if (!res) {
        std::cout << "Error accessing exchange rates (no response), setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

        return  1.0;
    } else if (res->status != 200) {
        std::cout << "Error accessing exchange rates (not OK), setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

        return  1.0;
    } else {
        auto& buffer = res->body;

        if (buffer.find(':') == std::string::npos || buffer.find('}') == std::string::npos) {
            std::cout << "Error parsing exchange rates, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

            return  1.0;
        } else {
            std::string ratio_result(buffer.begin() + buffer.find(':') + 1, buffer.begin() + buffer.find('}'));

            return atof(ratio_result.c_str());
        }
    }
}

// V2 is using api.exchangeratesapi.io
double get_rate_v2(const std::string& from, const std::string& to, const std::string& date = "latest") {
    httplib::SSLClient cli("api.exchangeratesapi.io", 443);

    std::string api_complete = "/" + date + "?symbols=" + to + "&base=" + from;

    auto res = cli.Get(api_complete.c_str());

    if (!res) {
        std::cout << "ERROR: Currency(v2): No response, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
        std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;

        return  1.0;
    } else if (res->status != 200) {
        std::cout << "ERROR: Currency(v2): Error response " << res->status << ", setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
        std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;
        std::cout << "ERROR: Currency(v2): Response is " << res->body << std::endl;

        return  1.0;
    } else {
        auto& buffer = res->body;
        auto index   = "\"" + to + "\":";

        if (buffer.find(index) == std::string::npos || buffer.find('}') == std::string::npos) {
            std::cout << "ERROR: Currency(v2): Error parsing exchange rates, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
            std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;
            std::cout << "ERROR: Currency(v2): Response is " << res->body << std::endl;

            return  1.0;
        } else {
            std::string ratio_result(buffer.begin() + buffer.find(index) + index.size(), buffer.begin() + buffer.find('}'));

            return atof(ratio_result.c_str());
        }
    }
}

} // end of anonymous namespace

void budget::load_currency_cache(){
    std::string file_path = budget::path_to_budget_file("currency.cache");
    std::ifstream file(file_path);

    if (!file.is_open() || !file.good()){
        std::cout << "INFO: Impossible to load Currency Cache" << std::endl;
        return;
    }

    std::string line;
    while (file.good() && getline(file, line)) {
        if (line.empty()) {
            continue;
        }

        auto parts = split(line, ':');

        currency_cache_key key(date_from_string(parts[0]), parts[1], parts[2]);
        exchanges[key] = budget::to_number<double>(parts[3]);
    }

    if (budget::is_server_running()) {
        std::cout << "INFO: Currency Cache has been loaded from " << file_path << std::endl;
        std::cout << "INFO: Currency Cache has " << exchanges.size() << " entries " << std::endl;
    }
}

void budget::save_currency_cache() {
    std::string file_path = budget::path_to_budget_file("currency.cache");
    std::ofstream file(file_path);

    if (!file.is_open() || !file.good()){
        std::cout << "INFO: Impossible to save Currency Cache" << std::endl;
        return;
    }

    {
        server_lock_guard l(exchanges_lock);

        for (auto & [key, value] : exchanges) {
            if (value != 1.0) {
                file << key.date << ':' << key.from << ':' << key.to << ':' << value << std::endl;
            }
        }
    }

    if (budget::is_server_running()) {
        std::cout << "INFO: Currency Cache has been saved to " << file_path << std::endl;
        std::cout << "INFO: Currency Cache has " << exchanges.size() << " entries " << std::endl;
    }
}

void budget::refresh_currency_cache(){
    std::unordered_map<currency_cache_key, double> copy;

    {
        server_lock_guard l(exchanges_lock);

        copy = exchanges;
    }

    // Refresh/Prefetch the current exchange rates
    for (auto & pair : copy) {
        auto& key = pair.first;

        exchange_rate(key.from, key.to);
    }

    if (budget::is_server_running()) {
        std::cout << "INFO: Currency Cache has been refreshed" << std::endl;
        std::cout << "INFO: Currency Cache has " << exchanges.size() << " entries " << std::endl;
    }
}

double budget::exchange_rate(const std::string& from){
    return exchange_rate(from, get_default_currency());
}

double budget::exchange_rate(const std::string& from, const std::string& to){
    return exchange_rate(from, to, budget::local_day());
}

double budget::exchange_rate(const std::string& from, budget::date d){
    return exchange_rate(from, get_default_currency(), d);
}

double budget::exchange_rate(const std::string& from, const std::string& to, budget::date d){
    assert(from != "DESIRED" && to != "DESIRED");

    if (from == to) {
        return 1.0;
    } else {
        currency_cache_key key(d, from, to);

        // Return directly if we already have the data in cache
        {
            server_lock_guard l(exchanges_lock);

            if (exchanges.find(key) != exchanges.end()) {
                return exchanges[key];
            }
        }

        // Otherwise, make the API call without the lock

        auto rate = get_rate_v2(from, to, date_to_string(d));

        if (budget::is_server_running()) {
            std::cout << "INFO: Currency: Rate (" << d << ")"
                << " from " << from << " to " << to << " = " << rate << std::endl;
        }

        // Update the cache and the reverse cache with the lock

        currency_cache_key reverse_key(d, to, from);

        {
            server_lock_guard l(exchanges_lock);

            exchanges[key]         = rate;
            exchanges[reverse_key] = 1.0 / rate;
        }

        return rate;
    }
}
<commit_msg>Do not query exchange rate in the future<commit_after>//=======================================================================
// Copyright (c) 2013-2020 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <tuple>
#include <utility>
#include <iostream>

#include "currency.hpp"
#include "assets.hpp" // For get_default_currency
#include "http.hpp"
#include "date.hpp"
#include "config.hpp"
#include "server_lock.hpp"

namespace {

struct currency_cache_key {
    budget::date date;
    std::string from;
    std::string to;

    currency_cache_key(budget::date date, std::string from, std::string to) : date(date), from(from), to(to) {}

    friend bool operator==(const currency_cache_key & lhs, const currency_cache_key & rhs){
        if (lhs.date != rhs.date) {
            return false;
        }

        if (lhs.from != rhs.from) {
            return false;
        }

        return lhs.to == rhs.to;
    }
};

} // end of anonymous namespace

namespace std {

template <>
struct hash<currency_cache_key> {
    std::size_t operator()(const currency_cache_key & key) const noexcept {
        auto seed = std::hash<budget::date>()(key.date);
        seed ^= std::hash<std::string>()(key.from) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        seed ^= std::hash<std::string>()(key.to) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        return seed;
    }
};

} // end of namespace std

namespace {

// OPTIM: If necessary, this could be made faster with a two layer cache
// We can take advantages from the low number of currency pair in the first layer
// and then the quick hash from the high number of dates

std::unordered_map<currency_cache_key, double> exchanges;
budget::server_lock exchanges_lock;

// V1 is using free.currencyconverterapi.com
double get_rate_v1(const std::string& from, const std::string& to){
    httplib::Client cli("free.currencyconverterapi.com", 80);

    std::string api_complete = "/api/v3/convert?q=" + from + "_" + to + "&compact=ultra";

    auto res = cli.Get(api_complete.c_str());

    if (!res) {
        std::cout << "Error accessing exchange rates (no response), setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

        return  1.0;
    } else if (res->status != 200) {
        std::cout << "Error accessing exchange rates (not OK), setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

        return  1.0;
    } else {
        auto& buffer = res->body;

        if (buffer.find(':') == std::string::npos || buffer.find('}') == std::string::npos) {
            std::cout << "Error parsing exchange rates, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

            return  1.0;
        } else {
            std::string ratio_result(buffer.begin() + buffer.find(':') + 1, buffer.begin() + buffer.find('}'));

            return atof(ratio_result.c_str());
        }
    }
}

// V2 is using api.exchangeratesapi.io
double get_rate_v2(const std::string& from, const std::string& to, const std::string& date = "latest") {
    httplib::SSLClient cli("api.exchangeratesapi.io", 443);

    std::string api_complete = "/" + date + "?symbols=" + to + "&base=" + from;

    auto res = cli.Get(api_complete.c_str());

    if (!res) {
        std::cout << "ERROR: Currency(v2): No response, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
        std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;

        return  1.0;
    } else if (res->status != 200) {
        std::cout << "ERROR: Currency(v2): Error response " << res->status << ", setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
        std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;
        std::cout << "ERROR: Currency(v2): Response is " << res->body << std::endl;

        return  1.0;
    } else {
        auto& buffer = res->body;
        auto index   = "\"" + to + "\":";

        if (buffer.find(index) == std::string::npos || buffer.find('}') == std::string::npos) {
            std::cout << "ERROR: Currency(v2): Error parsing exchange rates, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
            std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;
            std::cout << "ERROR: Currency(v2): Response is " << res->body << std::endl;

            return  1.0;
        } else {
            std::string ratio_result(buffer.begin() + buffer.find(index) + index.size(), buffer.begin() + buffer.find('}'));

            return atof(ratio_result.c_str());
        }
    }
}

} // end of anonymous namespace

void budget::load_currency_cache(){
    std::string file_path = budget::path_to_budget_file("currency.cache");
    std::ifstream file(file_path);

    if (!file.is_open() || !file.good()){
        std::cout << "INFO: Impossible to load Currency Cache" << std::endl;
        return;
    }

    std::string line;
    while (file.good() && getline(file, line)) {
        if (line.empty()) {
            continue;
        }

        auto parts = split(line, ':');

        currency_cache_key key(date_from_string(parts[0]), parts[1], parts[2]);
        exchanges[key] = budget::to_number<double>(parts[3]);
    }

    if (budget::is_server_running()) {
        std::cout << "INFO: Currency Cache has been loaded from " << file_path << std::endl;
        std::cout << "INFO: Currency Cache has " << exchanges.size() << " entries " << std::endl;
    }
}

void budget::save_currency_cache() {
    std::string file_path = budget::path_to_budget_file("currency.cache");
    std::ofstream file(file_path);

    if (!file.is_open() || !file.good()){
        std::cout << "INFO: Impossible to save Currency Cache" << std::endl;
        return;
    }

    {
        server_lock_guard l(exchanges_lock);

        for (auto & [key, value] : exchanges) {
            if (value != 1.0) {
                file << key.date << ':' << key.from << ':' << key.to << ':' << value << std::endl;
            }
        }
    }

    if (budget::is_server_running()) {
        std::cout << "INFO: Currency Cache has been saved to " << file_path << std::endl;
        std::cout << "INFO: Currency Cache has " << exchanges.size() << " entries " << std::endl;
    }
}

void budget::refresh_currency_cache(){
    std::unordered_map<currency_cache_key, double> copy;

    {
        server_lock_guard l(exchanges_lock);

        copy = exchanges;
    }

    // Refresh/Prefetch the current exchange rates
    for (auto & pair : copy) {
        auto& key = pair.first;

        exchange_rate(key.from, key.to);
    }

    if (budget::is_server_running()) {
        std::cout << "INFO: Currency Cache has been refreshed" << std::endl;
        std::cout << "INFO: Currency Cache has " << exchanges.size() << " entries " << std::endl;
    }
}

double budget::exchange_rate(const std::string& from){
    return exchange_rate(from, get_default_currency());
}

double budget::exchange_rate(const std::string& from, const std::string& to){
    return exchange_rate(from, to, budget::local_day());
}

double budget::exchange_rate(const std::string& from, budget::date d){
    return exchange_rate(from, get_default_currency(), d);
}

double budget::exchange_rate(const std::string& from, const std::string& to, budget::date d){
    assert(from != "DESIRED" && to != "DESIRED");

    if (from == to) {
        return 1.0;
    } else if (d > budget::local_day()) {
        return exchange_rate(from, to, budget::local_day());
    } else {
        currency_cache_key key(d, from, to);

        // Return directly if we already have the data in cache
        {
            server_lock_guard l(exchanges_lock);

            if (exchanges.find(key) != exchanges.end()) {
                return exchanges[key];
            }
        }

        // Otherwise, make the API call without the lock

        auto rate = get_rate_v2(from, to, date_to_string(d));

        if (budget::is_server_running()) {
            std::cout << "INFO: Currency: Rate (" << d << ")"
                << " from " << from << " to " << to << " = " << rate << std::endl;
        }

        // Update the cache and the reverse cache with the lock

        currency_cache_key reverse_key(d, to, from);

        {
            server_lock_guard l(exchanges_lock);

            exchanges[key]         = rate;
            exchanges[reverse_key] = 1.0 / rate;
        }

        return rate;
    }
}
<|endoftext|>


<commit_before>#include "database.h"

MYSQL *DB_conn = NULL;
MYSQL_RES *DB_result = NULL;
FILE * DB_log = NULL;

char DB_buf[16384];

void DB_connect() {
    DB_log = fopen("database.log","w");

    DB_conn = mysql_init(NULL);

    if (DB_conn == NULL) {
        fprintf(DB_log, "Error %u: %s\n", mysql_errno(DB_conn), mysql_error(DB_conn));
        return;
    }

    if (mysql_real_connect(DB_conn, DB_HOST, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0) == NULL) {
        fprintf(DB_log,"Error %u: %s\n", mysql_errno(DB_conn), mysql_error(DB_conn));
        return;
    }

    fprintf(DB_log,"Database connecton established.\n");
}

void DB_query(char* item ...) {
    char query[1024];
    int i = 0;

    query[0] = 0x00;

    /// Connect to the DB if necessary

    if(!DB_conn) DB_connect();

    /// Assemble the query

    va_list v;
    va_start(v, item);

    while( item[i] != 0x00 && i < 1000) {
        if(item[i] == '%' && item[i+1] == 'd') {
            sprintf(query,"%s%d",query,va_arg(v,int));
            i++;
        } else if(item[i] == '%' && item[i+1] == 'f') {
            sprintf(query,"%s%f",query,va_arg(v,double));
            i++;
        } else if(item[i] == '%' && item[i+1] == 's') {
            sprintf(query,"%s%s",query,va_arg(v,char*));
            i++;
        } else {
            sprintf(query,"%s%c",query,item[i]);
        }
        i++;
    }

    va_end(v);
    fprintf(DB_log, "Executing Query: %s\n",query);

    /// Execute the query

    mysql_query(DB_conn, query);
    DB_result = mysql_store_result(DB_conn);

    fprintf(DB_log, "Rows Returned: %d\n",mysql_num_rows(DB_result));
}

char* DB_resultAsText() {
    int i = 0;
    MYSQL_ROW row;
    int num_fields;

    DB_buf[0] = 0x00;

    num_fields = mysql_num_fields(DB_result);

    while ((row = mysql_fetch_row(DB_result)))
    {
      for(i = 0; i < num_fields; i++)
      {
          sprintf(DB_buf,"%s%s ",DB_buf, row[i] ? row[i] : "NULL");
      }
      sprintf(DB_buf,"%s\n",DB_buf);
    }

    mysql_free_result(DB_result);

    return DB_buf;
}

vector<string> DB_getAllDevices() {
    DB_query("select distinct DeviceID from aip order by DeviceID ASC;");

    int i = 0;
    MYSQL_ROW row;
    int num_fields;

    vector<string> result;

    num_fields = mysql_num_fields(DB_result);

    while ((row = mysql_fetch_row(DB_result))) {
          DB_buf[0] = 0x00;
          sprintf(DB_buf,"%s%s ",DB_buf, row[i] ? row[i] : "NULL");
          string tmp = string(DB_buf);
          result.push_back(tmp);
    }

    mysql_free_result(DB_result);

    return result;
}

map<string, string> DB_getMostRecentGPS(int device_id) {
    DB_query("select Altitude, Rate, Lat, LatRef, Lon, LonRef, Spd, Hdg, Status from gps where DeviceID=%d and Lat != 0 order by Timestamp desc limit 1;",device_id);

    MYSQL_ROW row;
    int num_fields;

    map<string, string> result;

    num_fields = mysql_num_fields(DB_result);

    if (row = mysql_fetch_row(DB_result)) {
        result["Altitude"] = string(row[0]);
        result["Rate"] = string(row[1]);
        result["Lat"] = string(row[2]);
        result["LatRef"] = string(row[3]);
        result["Latitude"] = string(row[2]) + string(row[3]);
        result["Lon"] = string(row[4]);
        result["LonRef"] = string(row[5]);
        result["Longitude"] = string(row[4]) + string(row[5]);
        result["Spd"] = string(row[6]);
        result["Hdg"] = string(row[7]);
        result["Status"] = string(row[8]);
    }

    mysql_free_result(DB_result);

    return result;
}

Plot DB_getPlotData(char* table, char* data_column, int device_id) {
    DB_query("select UNIX_TIMESTAMP(Timestamp)-UNIX_TIMESTAMP(), Altitude, %s from %s "
             "where DeviceId=%d "
             "order by Timestamp asc;",
             data_column, table, device_id);
    Plot result;

    MYSQL_ROW row;

    while (row = mysql_fetch_row(DB_result)) {
        result.time.push_back(strtod(row[0],NULL));
        result.altitude.push_back(strtod(row[1],NULL));
        result.data.push_back(strtod(row[2],NULL));
    }

    mysql_free_result(DB_result);

    return result;
}
<commit_msg>- added units for status display<commit_after>#include "database.h"

MYSQL *DB_conn = NULL;
MYSQL_RES *DB_result = NULL;
FILE * DB_log = NULL;

char DB_buf[16384];

void DB_connect() {
    DB_log = fopen("database.log","w");

    DB_conn = mysql_init(NULL);

    if (DB_conn == NULL) {
        fprintf(DB_log, "Error %u: %s\n", mysql_errno(DB_conn), mysql_error(DB_conn));
        return;
    }

    if (mysql_real_connect(DB_conn, DB_HOST, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0) == NULL) {
        fprintf(DB_log,"Error %u: %s\n", mysql_errno(DB_conn), mysql_error(DB_conn));
        return;
    }

    fprintf(DB_log,"Database connecton established.\n");
}

void DB_query(char* item ...) {
    char query[1024];
    int i = 0;

    query[0] = 0x00;

    /// Connect to the DB if necessary

    if(!DB_conn) DB_connect();

    /// Assemble the query

    va_list v;
    va_start(v, item);

    while( item[i] != 0x00 && i < 1000) {
        if(item[i] == '%' && item[i+1] == 'd') {
            sprintf(query,"%s%d",query,va_arg(v,int));
            i++;
        } else if(item[i] == '%' && item[i+1] == 'f') {
            sprintf(query,"%s%f",query,va_arg(v,double));
            i++;
        } else if(item[i] == '%' && item[i+1] == 's') {
            sprintf(query,"%s%s",query,va_arg(v,char*));
            i++;
        } else {
            sprintf(query,"%s%c",query,item[i]);
        }
        i++;
    }

    va_end(v);
    fprintf(DB_log, "Executing Query: %s\n",query);

    /// Execute the query

    mysql_query(DB_conn, query);
    DB_result = mysql_store_result(DB_conn);

    fprintf(DB_log, "Rows Returned: %d\n",mysql_num_rows(DB_result));
}

char* DB_resultAsText() {
    int i = 0;
    MYSQL_ROW row;
    int num_fields;

    DB_buf[0] = 0x00;

    num_fields = mysql_num_fields(DB_result);

    while ((row = mysql_fetch_row(DB_result)))
    {
      for(i = 0; i < num_fields; i++)
      {
          sprintf(DB_buf,"%s%s ",DB_buf, row[i] ? row[i] : "NULL");
      }
      sprintf(DB_buf,"%s\n",DB_buf);
    }

    mysql_free_result(DB_result);

    return DB_buf;
}

vector<string> DB_getAllDevices() {
    DB_query("select distinct DeviceID from aip order by DeviceID ASC;");

    int i = 0;
    MYSQL_ROW row;
    int num_fields;

    vector<string> result;

    num_fields = mysql_num_fields(DB_result);

    while ((row = mysql_fetch_row(DB_result))) {
          DB_buf[0] = 0x00;
          sprintf(DB_buf,"%s%s ",DB_buf, row[i] ? row[i] : "NULL");
          string tmp = string(DB_buf);
          result.push_back(tmp);
    }

    mysql_free_result(DB_result);

    return result;
}

map<string, string> DB_getMostRecentGPS(int device_id) {
    DB_query("select Altitude, Rate, Lat, LatRef, Lon, LonRef, Spd, Hdg, Status from gps where DeviceID=%d and Lat != 0 order by Timestamp desc limit 1;",device_id);

    MYSQL_ROW row;
    int num_fields;

    char buf[64];

    const float M_TO_FT = 3.2808399;
    const float KNOT_TO_MPS = 0.514444444;

    map<string, string> result;

    num_fields = mysql_num_fields(DB_result);

    if (row = mysql_fetch_row(DB_result)) {
        result["Altitude"] = string(row[0]);
        result["Altitude_m"] = string(row[0]);
        sprintf(buf,"%d",(int)(atoi(row[0])*M_TO_FT));
        result["Altitude_ft"] = string(buf);
        result["Rate"] = string(row[1]);
        sprintf(buf,"%d",atoi(row[1]) - 10000);
        result["Rate_m"] = string(buf);
        sprintf(buf,"%d",(int)((atoi(row[1]) - 10000)*M_TO_FT));
        result["Rate_ft"] = string(buf);
        result["Lat"] = string(row[2]);
        result["LatRef"] = string(row[3]);
        result["Latitude"] = string(row[2]) + string(row[3]);
        result["Lon"] = string(row[4]);
        result["LonRef"] = string(row[5]);
        result["Longitude"] = string(row[4]) + string(row[5]);
        result["Spd"] = string(row[6]);
        result["Spd_knots"] = string(row[6]);
        sprintf(buf,"%d",(int)(atoi(row[6])*KNOT_TO_MPS));
        result["Spd_mps"] = string(buf);
        result["Hdg"] = string(row[7]);
        result["Status"] = string(row[8]);
    }

    mysql_free_result(DB_result);

    return result;
}

Plot DB_getPlotData(char* table, char* data_column, int device_id) {
    DB_query("select UNIX_TIMESTAMP(Timestamp)-UNIX_TIMESTAMP(), Altitude, %s from %s "
             "where DeviceId=%d "
             "order by Timestamp asc;",
             data_column, table, device_id);
    Plot result;

    MYSQL_ROW row;

    while (row = mysql_fetch_row(DB_result)) {
        result.time.push_back(strtod(row[0],NULL));
        result.altitude.push_back(strtod(row[1],NULL));
        result.data.push_back(strtod(row[2],NULL));
    }

    mysql_free_result(DB_result);

    return result;
}
<|endoftext|>


<commit_before>// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    delegate.c

    Templates and classes to enable delegates for callbacks.

***************************************************************************/

#include <assert.h>
#include <cstdint>
#include <stdio.h>
#include "delegate.h"

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)

delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { {0 }};

#endif

//**************************************************************************
//  INTERNAL DELEGATE HELPERS
//**************************************************************************

#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL)

//-------------------------------------------------
//  delegate_convert_raw - given an object and an raw function, adjust the object base
//  and return the actual final code pointer
//-------------------------------------------------//

delegate_generic_function delegate_mfp::convert_to_generic(delegate_generic_class *&object) const
{
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
	// apply the "this" delta to the object first
	object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object));

	// if the low bit of the vtable index is clear, then it is just a raw function pointer
	if (m_this_delta==0) {
#if defined(LOG_DELEGATES)
		printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function));
#endif
		return reinterpret_cast<delegate_generic_function>(m_function);
	}
	object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object));

	// otherwise, it is the byte index into the vtable where the actual function lives
	std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
#if defined(LOG_DELEGATES)
	printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1)));
#endif
	return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1);
#else
	// apply the "this" delta to the object first
	object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);

	// if the low bit of the vtable index is clear, then it is just a raw function pointer
	if (!(m_function & 1)) {
#if defined(LOG_DELEGATES)
		printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function));
#endif
		return reinterpret_cast<delegate_generic_function>(m_function);
	}

	// otherwise, it is the byte index into the vtable where the actual function lives
	std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
#if defined(LOG_DELEGATES)
	printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1)));
#endif
	return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1);
#endif
}

#endif
<commit_msg>Works now on MIPS as well, tested on Creator Ci20<commit_after>// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    delegate.c

    Templates and classes to enable delegates for callbacks.

***************************************************************************/

#include <assert.h>
#include <cstdint>
#include <stdio.h>
#include "delegate.h"

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)

delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { {0 }};

#endif

//**************************************************************************
//  INTERNAL DELEGATE HELPERS
//**************************************************************************

#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL)

//-------------------------------------------------
//  delegate_convert_raw - given an object and an raw function, adjust the object base
//  and return the actual final code pointer
//-------------------------------------------------//

delegate_generic_function delegate_mfp::convert_to_generic(delegate_generic_class *&object) const
{
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
	// apply the "this" delta to the object first
	object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object));

	// if the low bit of the vtable index is clear, then it is just a raw function pointer
	if (m_this_delta==0) {
#if defined(LOG_DELEGATES)
		printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function));
#endif
		return reinterpret_cast<delegate_generic_function>(m_function);
	}
	object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object));

	// otherwise, it is the byte index into the vtable where the actual function lives
	std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
#if defined(LOG_DELEGATES)
	printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1)));
#endif
	return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1);
#else
	// apply the "this" delta to the object first
	object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);

	// if the low bit of the vtable index is clear, then it is just a raw function pointer
	if (!(m_function & 1)) {
#if defined(LOG_DELEGATES)
		printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function));
#endif
		return reinterpret_cast<delegate_generic_function>(m_function);
	}

	// otherwise, it is the byte index into the vtable where the actual function lives
	std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
#if defined(LOG_DELEGATES)
	printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1)));
#endif
	return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1);
#endif
}

#endif
<|endoftext|>