<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 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);
}

}
}
<commit_msg>Fix reference to temporary.<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(const 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>// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
#include "net/third_party/quiche/src/quic/core/quic_time.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
#include "net/third_party/quiche/src/quic/quartc/test/bidi_test_runner.h"
#include "net/third_party/quiche/src/quic/quartc/test/quartc_competing_endpoint.h"
#include "net/third_party/quiche/src/quic/quartc/test/quic_trace_interceptor.h"
#include "net/third_party/quiche/src/quic/quartc/test/random_packet_filter.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"

namespace quic {
namespace test {
namespace {

class QuartcBidiTest : public QuicTest {
 protected:
  QuartcBidiTest() {
    uint64_t seed = QuicRandom::GetInstance()->RandUint64();
    QUIC_LOG(INFO) << "Setting random seed to " << seed;
    random_.set_seed(seed);
    simulator_.set_random_generator(&random_);

    client_trace_interceptor_ = QuicMakeUnique<QuicTraceInterceptor>("client");
    server_trace_interceptor_ = QuicMakeUnique<QuicTraceInterceptor>("server");
  }

  void CreateTransports(QuicBandwidth bandwidth,
                        QuicTime::Delta propagation_delay,
                        QuicByteCount queue_length,
                        int loss_percent) {
    // Endpoints which serve as the transports for client and server.
    client_transport_ =
        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
            &simulator_, "client_transport", "server_transport", queue_length);
    server_transport_ =
        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
            &simulator_, "server_transport", "client_transport", queue_length);

    // Filters on each of the endpoints facilitate random packet loss.
    client_filter_ = QuicMakeUnique<simulator::RandomPacketFilter>(
        &simulator_, "client_filter", client_transport_.get());
    server_filter_ = QuicMakeUnique<simulator::RandomPacketFilter>(
        &simulator_, "server_filter", server_transport_.get());
    client_filter_->set_loss_percent(loss_percent);
    server_filter_->set_loss_percent(loss_percent);

    // Each endpoint connects directly to a switch.
    client_switch_ = QuicMakeUnique<simulator::Switch>(
        &simulator_, "client_switch", /*port_count=*/8, 2 * queue_length);
    server_switch_ = QuicMakeUnique<simulator::Switch>(
        &simulator_, "server_switch", /*port_count=*/8, 2 * queue_length);

    // Links to the switch have significantly higher bandwdith than the
    // bottleneck and insignificant propagation delay.
    client_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        client_filter_.get(), client_switch_->port(1), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
    server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        server_filter_.get(), server_switch_->port(1), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));

    // The bottleneck link connects the two switches with the bandwidth and
    // propagation delay specified by the test case.
    bottleneck_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        client_switch_->port(2), server_switch_->port(2), bandwidth,
        propagation_delay);
  }

  void SetupCompetingEndpoints(QuicBandwidth bandwidth,
                               QuicTime::Delta send_interval,
                               QuicByteCount bytes_per_interval) {
    competing_client_ = QuicMakeUnique<QuartcCompetingEndpoint>(
        &simulator_, send_interval, bytes_per_interval, "competing_client",
        "competing_server", quic::Perspective::IS_CLIENT,
        quic::test::TestConnectionId(3));
    competing_server_ = QuicMakeUnique<QuartcCompetingEndpoint>(
        &simulator_, send_interval, bytes_per_interval, "competing_server",
        "competing_client", quic::Perspective::IS_SERVER,
        quic::test::TestConnectionId(3));

    competing_client_link_ = QuicMakeUnique<quic::simulator::SymmetricLink>(
        competing_client_->endpoint(), client_switch_->port(3), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
    competing_server_link_ = QuicMakeUnique<quic::simulator::SymmetricLink>(
        competing_server_->endpoint(), server_switch_->port(3), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
  }

  simulator::Simulator simulator_;
  SimpleRandom random_;

  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
  std::unique_ptr<simulator::RandomPacketFilter> client_filter_;
  std::unique_ptr<simulator::RandomPacketFilter> server_filter_;
  std::unique_ptr<simulator::Switch> client_switch_;
  std::unique_ptr<simulator::Switch> server_switch_;
  std::unique_ptr<simulator::SymmetricLink> client_link_;
  std::unique_ptr<simulator::SymmetricLink> server_link_;
  std::unique_ptr<simulator::SymmetricLink> bottleneck_link_;

  std::unique_ptr<QuartcCompetingEndpoint> competing_client_;
  std::unique_ptr<QuartcCompetingEndpoint> competing_server_;
  std::unique_ptr<simulator::SymmetricLink> competing_client_link_;
  std::unique_ptr<simulator::SymmetricLink> competing_server_link_;

  std::unique_ptr<QuicTraceInterceptor> client_trace_interceptor_;
  std::unique_ptr<QuicTraceInterceptor> server_trace_interceptor_;
};

TEST_F(QuartcBidiTest, Basic300kbps200ms) {
  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
                   QuicTime::Delta::FromMilliseconds(200),
                   10 * kDefaultMaxPacketSize, /*loss_percent=*/0);
  BidiTestRunner runner(&simulator_, client_transport_.get(),
                        server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLoss) {
  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
                   QuicTime::Delta::FromMilliseconds(200),
                   10 * kDefaultMaxPacketSize, /*loss_percent=*/2);
  BidiTestRunner runner(&simulator_, client_transport_.get(),
                        server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLossCompetingBurst) {
  QuicBandwidth bandwidth = QuicBandwidth::FromKBitsPerSecond(300);
  CreateTransports(bandwidth, QuicTime::Delta::FromMilliseconds(200),
                   10 * quic::kDefaultMaxPacketSize, /*loss_percent=*/2);
  SetupCompetingEndpoints(bandwidth, QuicTime::Delta::FromSeconds(15),
                          /*bytes_per_interval=*/50 * 1024);

  quic::test::BidiTestRunner runner(&simulator_, client_transport_.get(),
                                    server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

}  // namespace
}  // namespace test
}  // namespace quic
<commit_msg>Add QuicBidiTest cases for aggregation and small, frequent competing bursts.<commit_after>// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
#include "net/third_party/quiche/src/quic/core/quic_time.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
#include "net/third_party/quiche/src/quic/quartc/test/bidi_test_runner.h"
#include "net/third_party/quiche/src/quic/quartc/test/quartc_competing_endpoint.h"
#include "net/third_party/quiche/src/quic/quartc/test/quic_trace_interceptor.h"
#include "net/third_party/quiche/src/quic/quartc/test/random_packet_filter.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"

namespace quic {
namespace test {
namespace {

class QuartcBidiTest : public QuicTest {
 protected:
  QuartcBidiTest() {
    uint64_t seed = QuicRandom::GetInstance()->RandUint64();
    QUIC_LOG(INFO) << "Setting random seed to " << seed;
    random_.set_seed(seed);
    simulator_.set_random_generator(&random_);

    client_trace_interceptor_ = QuicMakeUnique<QuicTraceInterceptor>("client");
    server_trace_interceptor_ = QuicMakeUnique<QuicTraceInterceptor>("server");
  }

  void CreateTransports(QuicBandwidth bandwidth,
                        QuicTime::Delta propagation_delay,
                        QuicByteCount queue_length,
                        int loss_percent) {
    // Endpoints which serve as the transports for client and server.
    client_transport_ =
        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
            &simulator_, "client_transport", "server_transport", queue_length);
    server_transport_ =
        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
            &simulator_, "server_transport", "client_transport", queue_length);

    // Filters on each of the endpoints facilitate random packet loss.
    client_filter_ = QuicMakeUnique<simulator::RandomPacketFilter>(
        &simulator_, "client_filter", client_transport_.get());
    server_filter_ = QuicMakeUnique<simulator::RandomPacketFilter>(
        &simulator_, "server_filter", server_transport_.get());
    client_filter_->set_loss_percent(loss_percent);
    server_filter_->set_loss_percent(loss_percent);

    // Each endpoint connects directly to a switch.
    client_switch_ = QuicMakeUnique<simulator::Switch>(
        &simulator_, "client_switch", /*port_count=*/8, 2 * queue_length);
    server_switch_ = QuicMakeUnique<simulator::Switch>(
        &simulator_, "server_switch", /*port_count=*/8, 2 * queue_length);

    // Links to the switch have significantly higher bandwdith than the
    // bottleneck and insignificant propagation delay.
    client_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        client_filter_.get(), client_switch_->port(1), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
    server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        server_filter_.get(), server_switch_->port(1), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));

    // The bottleneck link connects the two switches with the bandwidth and
    // propagation delay specified by the test case.
    bottleneck_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        client_switch_->port(2), server_switch_->port(2), bandwidth,
        propagation_delay);
  }

  void SetupCompetingEndpoints(QuicBandwidth bandwidth,
                               QuicTime::Delta send_interval,
                               QuicByteCount bytes_per_interval) {
    competing_client_ = QuicMakeUnique<QuartcCompetingEndpoint>(
        &simulator_, send_interval, bytes_per_interval, "competing_client",
        "competing_server", quic::Perspective::IS_CLIENT,
        quic::test::TestConnectionId(3));
    competing_server_ = QuicMakeUnique<QuartcCompetingEndpoint>(
        &simulator_, send_interval, bytes_per_interval, "competing_server",
        "competing_client", quic::Perspective::IS_SERVER,
        quic::test::TestConnectionId(3));

    competing_client_link_ = QuicMakeUnique<quic::simulator::SymmetricLink>(
        competing_client_->endpoint(), client_switch_->port(3), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
    competing_server_link_ = QuicMakeUnique<quic::simulator::SymmetricLink>(
        competing_server_->endpoint(), server_switch_->port(3), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
  }

  simulator::Simulator simulator_;
  SimpleRandom random_;

  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
  std::unique_ptr<simulator::RandomPacketFilter> client_filter_;
  std::unique_ptr<simulator::RandomPacketFilter> server_filter_;
  std::unique_ptr<simulator::Switch> client_switch_;
  std::unique_ptr<simulator::Switch> server_switch_;
  std::unique_ptr<simulator::SymmetricLink> client_link_;
  std::unique_ptr<simulator::SymmetricLink> server_link_;
  std::unique_ptr<simulator::SymmetricLink> bottleneck_link_;

  std::unique_ptr<QuartcCompetingEndpoint> competing_client_;
  std::unique_ptr<QuartcCompetingEndpoint> competing_server_;
  std::unique_ptr<simulator::SymmetricLink> competing_client_link_;
  std::unique_ptr<simulator::SymmetricLink> competing_server_link_;

  std::unique_ptr<QuicTraceInterceptor> client_trace_interceptor_;
  std::unique_ptr<QuicTraceInterceptor> server_trace_interceptor_;
};

TEST_F(QuartcBidiTest, Basic300kbps200ms) {
  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
                   QuicTime::Delta::FromMilliseconds(200),
                   10 * kDefaultMaxPacketSize, /*loss_percent=*/0);
  BidiTestRunner runner(&simulator_, client_transport_.get(),
                        server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLoss) {
  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
                   QuicTime::Delta::FromMilliseconds(200),
                   10 * kDefaultMaxPacketSize, /*loss_percent=*/2);
  BidiTestRunner runner(&simulator_, client_transport_.get(),
                        server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLossCompetingBurst) {
  QuicBandwidth bandwidth = QuicBandwidth::FromKBitsPerSecond(300);
  CreateTransports(bandwidth, QuicTime::Delta::FromMilliseconds(200),
                   10 * quic::kDefaultMaxPacketSize, /*loss_percent=*/2);
  SetupCompetingEndpoints(bandwidth, QuicTime::Delta::FromSeconds(15),
                          /*bytes_per_interval=*/50 * 1024);

  quic::test::BidiTestRunner runner(&simulator_, client_transport_.get(),
                                    server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLossSmallCompetingSpikes) {
  QuicBandwidth bandwidth = QuicBandwidth::FromKBitsPerSecond(300);
  CreateTransports(bandwidth, QuicTime::Delta::FromMilliseconds(200),
                   10 * quic::kDefaultMaxPacketSize, /*loss_percent=*/2);

  // Competition sends a small amount of data (10 kb) every 2 seconds.
  SetupCompetingEndpoints(bandwidth, QuicTime::Delta::FromSeconds(2),
                          /*bytes_per_interval=*/10 * 1024);

  quic::test::BidiTestRunner runner(&simulator_, client_transport_.get(),
                                    server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLossAggregation) {
  QuicBandwidth bandwidth = QuicBandwidth::FromKBitsPerSecond(300);
  CreateTransports(bandwidth, QuicTime::Delta::FromMilliseconds(200),
                   10 * quic::kDefaultMaxPacketSize, /*loss_percent=*/2);

  // Set aggregation on the queues at either end of the bottleneck.
  client_switch_->port_queue(2)->EnableAggregation(
      10 * 1024, QuicTime::Delta::FromMilliseconds(100));
  server_switch_->port_queue(2)->EnableAggregation(
      10 * 1024, QuicTime::Delta::FromMilliseconds(100));

  quic::test::BidiTestRunner runner(&simulator_, client_transport_.get(),
                                    server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

}  // namespace
}  // namespace test
}  // namespace quic
<|endoftext|>


<commit_before>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2005-2006 Refractions Research Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <geos/io/WKBReader.h>
#include <geos/io/WKBConstants.h>
#include <geos/io/ByteOrderValues.h>
#include <geos/io/ParseException.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/Point.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/LineString.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/MultiPoint.h>
#include <geos/geom/MultiLineString.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/CoordinateSequenceFactory.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/PrecisionModel.h>

#include <iomanip>
#include <ostream>
#include <sstream>
#include <string>

//#define DEBUG_WKB_READER 1

using namespace std;
using namespace geos::geom;

namespace geos {
namespace io { // geos.io

string WKBReader::BAD_GEOM_TYPE_MSG = "bad geometry type encountered in ";

WKBReader::WKBReader()
	:
	factory(*(GeometryFactory::getDefaultInstance()))
{}

ostream &
WKBReader::printHEX(istream &is, ostream &os)
{
	static const char hex[] = "0123456789ABCDEF";

	long pos = is.tellg(); // take note of input stream get pointer
	is.seekg(0, ios::beg); // rewind input stream

	char each=0;
	while(is.read(&each, 1))
	{
		const unsigned char c=each;
		int low = (c & 0x0F);
		int high = (c >> 4);
		os << hex[high] << hex[low];
	}

	is.clear(); // clear input stream eof flag
	is.seekg(pos); // reset input stream position

	return os;
}

Geometry *
WKBReader::readHEX(istream &is)
{
	// setup input/output stream
	stringstream os(ios_base::binary|ios_base::in|ios_base::out);

	unsigned char high, low, result_high, result_low, value;

	while(!is.eof())//readsome(&str[0], 2))
	{
		// get the high part of the byte
		is >> high;
		// geth the low part of the byte
		is >> low;

		switch (high)
		{
			case '0' :
				result_high = 0;
				break;
			case '1' :
				result_high = 1;
				break;
			case '2' :
				result_high = 2;
				break;
			case '3' :
				result_high = 3;
				break;
			case '4' :
				result_high = 4;
				break;
			case '5' :
				result_high = 5;
				break;
			case '6' :
				result_high = 6;
				break;
			case '7' :
				result_high = 7;
				break;
			case '8' :
				result_high = 8;
				break;
			case '9' :
				result_high = 9;
				break;
			case 'A' :
				result_high = 10;
				break;
			case 'B' :
				result_high = 11;
				break;
			case 'C' :
				result_high = 12;
				break;
			case 'D' :
				result_high = 13;
				break;
			case 'E' :
				result_high = 14;
				break;
			case 'F' :
				result_high = 15;
				break;
			default:
				throw  ParseException("Invalid HEX char");
		}

		switch (low)
		{
			case '0' :
				result_low = 0;
				break;
			case '1' :
				result_low = 1;
				break;
			case '2' :
				result_low = 2;
				break;
			case '3' :
				result_low = 3;
				break;
			case '4' :
				result_low = 4;
				break;
			case '5' :
				result_low = 5;
				break;
			case '6' :
				result_low = 6;
				break;
			case '7' :
				result_low = 7;
				break;
			case '8' :
				result_low = 8;
				break;
			case '9' :
				result_low = 9;
				break;
			case 'A' :
				result_low = 10;
				break;
			case 'B' :
				result_low = 11;
				break;
			case 'C' :
				result_low = 12;
				break;
			case 'D' :
				result_low = 13;
				break;
			case 'E' :
				result_low = 14;
				break;
			case 'F' :
				result_low = 15;
				break;
			default:
				throw  ParseException("Invalid HEX char");
		}

		value = (result_high<<4) + result_low;

#if DEBUG_HEX_READER
	cout<<"HEX "<<high<<low<<" -> DEC "<<(int)value<<endl;
#endif
		// write the value to the output stream
		os << value;
	}

	// now call read to convert the geometry
	return this->read(os);
}

Geometry *
WKBReader::read(istream &is)
{
	dis.setInStream(&is); // will default to machine endian
	return readGeometry();
}

Geometry *
WKBReader::readGeometry()
{
	// determine byte order
	unsigned char byteOrder = dis.readByte();

#if DEBUG_WKB_READER
	cout<<"WKB byteOrder: "<<(int)byteOrder<<endl;
#endif

	// default is machine endian
	if (byteOrder == WKBConstants::wkbNDR)
		dis.setOrder(ByteOrderValues::ENDIAN_LITTLE);
        else if (byteOrder == WKBConstants::wkbXDR)
                dis.setOrder(ByteOrderValues::ENDIAN_BIG);

	int typeInt = dis.readInt();
	int geometryType = typeInt & 0xff;

#if DEBUG_WKB_READER
	cout<<"WKB geometryType: "<<geometryType<<endl;
#endif

	bool hasZ = ((typeInt & 0x80000000) != 0);
	if (hasZ) inputDimension = 3;
	else inputDimension = 2; // doesn't handle M currently

#if DEBUG_WKB_READER
	cout<<"WKB hasZ: "<<hasZ<<endl;
#endif

#if DEBUG_WKB_READER
	cout<<"WKB dimensions: "<<inputDimension<<endl;
#endif

	bool hasSRID = ((typeInt & 0x20000000) != 0);

#if DEBUG_WKB_READER
	cout<<"WKB hasSRID: "<<hasZ<<endl;
#endif

	int SRID = -1;
	if (hasSRID) SRID = dis.readInt(); // read SRID

	// allocate space for ordValues
	if ( ordValues.size() < inputDimension )
		ordValues.resize(inputDimension);

	Geometry *result;

	switch (geometryType) {
		case WKBConstants::wkbPoint :
			result = readPoint();
			break;
		case WKBConstants::wkbLineString :
			result = readLineString();
			break;
		case WKBConstants::wkbPolygon :
			result = readPolygon();
			break;
		case WKBConstants::wkbMultiPoint :
			result = readMultiPoint();
			break;
		case WKBConstants::wkbMultiLineString :
			result = readMultiLineString();
			break;
		case WKBConstants::wkbMultiPolygon :
			result = readMultiPolygon();
			break;
		case WKBConstants::wkbGeometryCollection :
			result = readGeometryCollection();
			break;
		default:
			stringstream err;
			err << "Unknown WKB type " << geometryType;
			throw  ParseException(err.str());
	}

	result->setSRID(SRID);
	return result;
}

Point *
WKBReader::readPoint()
{
	readCoordinate();
	return factory.createPoint(Coordinate(ordValues[0], ordValues[1]));
}

LineString *
WKBReader::readLineString()
{
	int size = dis.readInt();
#if DEBUG_WKB_READER
	cout<<"WKB npoints: "<<size<<endl;
#endif
	CoordinateSequence *pts = readCoordinateSequence(size);
	return factory.createLineString(pts);
}

LinearRing *
WKBReader::readLinearRing()
{
	int size = dis.readInt();
#if DEBUG_WKB_READER
	cout<<"WKB npoints: "<<size<<endl;
#endif
	CoordinateSequence *pts = readCoordinateSequence(size);
	return factory.createLinearRing(pts);
}

Polygon *
WKBReader::readPolygon()
{
	int numRings = dis.readInt();

#if DEBUG_WKB_READER
	cout<<"WKB numRings: "<<numRings<<endl;
#endif

        LinearRing *shell = NULL;
        if( numRings > 0 )
            shell = readLinearRing();

	vector<Geometry *>*holes=NULL;
	if ( numRings > 1 )
	{
		try {
			holes = new vector<Geometry *>(numRings-1);
			for (int i=0; i<numRings-1; i++)
				(*holes)[i] = (Geometry *)readLinearRing();
		} catch (...) {
			for (unsigned int i=0; i<holes->size(); i++)
				delete (*holes)[i];
			delete holes;
			delete shell;
			throw;
		}
	}
	return factory.createPolygon(shell, holes);
}

MultiPoint *
WKBReader::readMultiPoint()
{
	int numGeoms = dis.readInt();
	vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);

	try {
		for (int i=0; i<numGeoms; i++)
		{
			Geometry *g = readGeometry();
			if (!dynamic_cast<Point *>(g))
			{
				stringstream err;
				err << BAD_GEOM_TYPE_MSG << " MultiPoint";
				throw ParseException(err.str());
			}
			(*geoms)[i] = g;
		}
	} catch (...) {
		for (unsigned int i=0; i<geoms->size(); i++)
			delete (*geoms)[i];
		delete geoms;
		throw;
	}
	return factory.createMultiPoint(geoms);
}

MultiLineString *
WKBReader::readMultiLineString()
{
	int numGeoms = dis.readInt();
	vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);

	try {
		for (int i=0; i<numGeoms; i++)
		{
			Geometry *g = readGeometry();
			if (!dynamic_cast<LineString *>(g))
			{
				stringstream err;
				err << BAD_GEOM_TYPE_MSG << " LineString";
				throw  ParseException(err.str());
			}
			(*geoms)[i] = g;
		}
	} catch (...) {
		for (unsigned int i=0; i<geoms->size(); i++)
			delete (*geoms)[i];
		delete geoms;
		throw;
	}
	return factory.createMultiLineString(geoms);
}

MultiPolygon *
WKBReader::readMultiPolygon()
{
	int numGeoms = dis.readInt();
	vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);

	try {
		for (int i=0; i<numGeoms; i++)
		{
			Geometry *g = readGeometry();
			if (!dynamic_cast<Polygon *>(g))
			{
				stringstream err;
				err << BAD_GEOM_TYPE_MSG << " Polygon";
				throw  ParseException(err.str());
			}
			(*geoms)[i] = g;
		}
	} catch (...) {
		for (unsigned int i=0; i<geoms->size(); i++)
			delete (*geoms)[i];
		delete geoms;
		throw;
	}
	return factory.createMultiPolygon(geoms);
}

GeometryCollection *
WKBReader::readGeometryCollection()
{
	int numGeoms = dis.readInt();
	vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);

	try {
		for (int i=0; i<numGeoms; i++)
			(*geoms)[i] = (readGeometry());
	} catch (...) {
		for (unsigned int i=0; i<geoms->size(); i++)
			delete (*geoms)[i];
		delete geoms;
		throw;
	}
	return factory.createGeometryCollection(geoms);
}

CoordinateSequence *
WKBReader::readCoordinateSequence(int size)
{
	CoordinateSequence *seq = factory.getCoordinateSequenceFactory()->create(size, inputDimension);
	unsigned int targetDim = seq->getDimension();
	if ( targetDim > inputDimension )
		targetDim = inputDimension;
	for (int i=0; i<size; i++) {
		readCoordinate();
		for (unsigned int j=0; j<targetDim; j++) {
			seq->setOrdinate(i, j, ordValues[j]);
		}
	}
	return seq;
}

void
WKBReader::readCoordinate()
{
	const PrecisionModel &pm = *factory.getPrecisionModel();
	for (unsigned int i=0; i<inputDimension; ++i)
	{
		if ( i <= 1 ) ordValues[i] = pm.makePrecise(dis.readDouble());
		else ordValues[i] = dis.readDouble();
	}
#if DEBUG_WKB_READER
	cout<<"WKB coordinate: "<<ordValues[0]<<","<<ordValues[1]<<endl;
#endif
}

} // namespace geos.io
} // namespace geos
<commit_msg>Set unknown SRID values to 0 instead of -1 to be more consistent with the rest of GEOS (srid values are initialized to 0 by default).<commit_after>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2005-2006 Refractions Research Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <geos/io/WKBReader.h>
#include <geos/io/WKBConstants.h>
#include <geos/io/ByteOrderValues.h>
#include <geos/io/ParseException.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/Point.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/LineString.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/MultiPoint.h>
#include <geos/geom/MultiLineString.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/CoordinateSequenceFactory.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/PrecisionModel.h>

#include <iomanip>
#include <ostream>
#include <sstream>
#include <string>

//#define DEBUG_WKB_READER 1

using namespace std;
using namespace geos::geom;

namespace geos {
namespace io { // geos.io

string WKBReader::BAD_GEOM_TYPE_MSG = "bad geometry type encountered in ";

WKBReader::WKBReader()
	:
	factory(*(GeometryFactory::getDefaultInstance()))
{}

ostream &
WKBReader::printHEX(istream &is, ostream &os)
{
	static const char hex[] = "0123456789ABCDEF";

	long pos = is.tellg(); // take note of input stream get pointer
	is.seekg(0, ios::beg); // rewind input stream

	char each=0;
	while(is.read(&each, 1))
	{
		const unsigned char c=each;
		int low = (c & 0x0F);
		int high = (c >> 4);
		os << hex[high] << hex[low];
	}

	is.clear(); // clear input stream eof flag
	is.seekg(pos); // reset input stream position

	return os;
}

Geometry *
WKBReader::readHEX(istream &is)
{
	// setup input/output stream
	stringstream os(ios_base::binary|ios_base::in|ios_base::out);

	unsigned char high, low, result_high, result_low, value;

	while(!is.eof())//readsome(&str[0], 2))
	{
		// get the high part of the byte
		is >> high;
		// geth the low part of the byte
		is >> low;

		switch (high)
		{
			case '0' :
				result_high = 0;
				break;
			case '1' :
				result_high = 1;
				break;
			case '2' :
				result_high = 2;
				break;
			case '3' :
				result_high = 3;
				break;
			case '4' :
				result_high = 4;
				break;
			case '5' :
				result_high = 5;
				break;
			case '6' :
				result_high = 6;
				break;
			case '7' :
				result_high = 7;
				break;
			case '8' :
				result_high = 8;
				break;
			case '9' :
				result_high = 9;
				break;
			case 'A' :
				result_high = 10;
				break;
			case 'B' :
				result_high = 11;
				break;
			case 'C' :
				result_high = 12;
				break;
			case 'D' :
				result_high = 13;
				break;
			case 'E' :
				result_high = 14;
				break;
			case 'F' :
				result_high = 15;
				break;
			default:
				throw  ParseException("Invalid HEX char");
		}

		switch (low)
		{
			case '0' :
				result_low = 0;
				break;
			case '1' :
				result_low = 1;
				break;
			case '2' :
				result_low = 2;
				break;
			case '3' :
				result_low = 3;
				break;
			case '4' :
				result_low = 4;
				break;
			case '5' :
				result_low = 5;
				break;
			case '6' :
				result_low = 6;
				break;
			case '7' :
				result_low = 7;
				break;
			case '8' :
				result_low = 8;
				break;
			case '9' :
				result_low = 9;
				break;
			case 'A' :
				result_low = 10;
				break;
			case 'B' :
				result_low = 11;
				break;
			case 'C' :
				result_low = 12;
				break;
			case 'D' :
				result_low = 13;
				break;
			case 'E' :
				result_low = 14;
				break;
			case 'F' :
				result_low = 15;
				break;
			default:
				throw  ParseException("Invalid HEX char");
		}

		value = (result_high<<4) + result_low;

#if DEBUG_HEX_READER
	cout<<"HEX "<<high<<low<<" -> DEC "<<(int)value<<endl;
#endif
		// write the value to the output stream
		os << value;
	}

	// now call read to convert the geometry
	return this->read(os);
}

Geometry *
WKBReader::read(istream &is)
{
	dis.setInStream(&is); // will default to machine endian
	return readGeometry();
}

Geometry *
WKBReader::readGeometry()
{
	// determine byte order
	unsigned char byteOrder = dis.readByte();

#if DEBUG_WKB_READER
	cout<<"WKB byteOrder: "<<(int)byteOrder<<endl;
#endif

	// default is machine endian
	if (byteOrder == WKBConstants::wkbNDR)
		dis.setOrder(ByteOrderValues::ENDIAN_LITTLE);
        else if (byteOrder == WKBConstants::wkbXDR)
                dis.setOrder(ByteOrderValues::ENDIAN_BIG);

	int typeInt = dis.readInt();
	int geometryType = typeInt & 0xff;

#if DEBUG_WKB_READER
	cout<<"WKB geometryType: "<<geometryType<<endl;
#endif

	bool hasZ = ((typeInt & 0x80000000) != 0);
	if (hasZ) inputDimension = 3;
	else inputDimension = 2; // doesn't handle M currently

#if DEBUG_WKB_READER
	cout<<"WKB hasZ: "<<hasZ<<endl;
#endif

#if DEBUG_WKB_READER
	cout<<"WKB dimensions: "<<inputDimension<<endl;
#endif

	bool hasSRID = ((typeInt & 0x20000000) != 0);

#if DEBUG_WKB_READER
	cout<<"WKB hasSRID: "<<hasZ<<endl;
#endif

	int SRID = 0;
	if (hasSRID) SRID = dis.readInt(); // read SRID

	// allocate space for ordValues
	if ( ordValues.size() < inputDimension )
		ordValues.resize(inputDimension);

	Geometry *result;

	switch (geometryType) {
		case WKBConstants::wkbPoint :
			result = readPoint();
			break;
		case WKBConstants::wkbLineString :
			result = readLineString();
			break;
		case WKBConstants::wkbPolygon :
			result = readPolygon();
			break;
		case WKBConstants::wkbMultiPoint :
			result = readMultiPoint();
			break;
		case WKBConstants::wkbMultiLineString :
			result = readMultiLineString();
			break;
		case WKBConstants::wkbMultiPolygon :
			result = readMultiPolygon();
			break;
		case WKBConstants::wkbGeometryCollection :
			result = readGeometryCollection();
			break;
		default:
			stringstream err;
			err << "Unknown WKB type " << geometryType;
			throw  ParseException(err.str());
	}

	result->setSRID(SRID);
	return result;
}

Point *
WKBReader::readPoint()
{
	readCoordinate();
	return factory.createPoint(Coordinate(ordValues[0], ordValues[1]));
}

LineString *
WKBReader::readLineString()
{
	int size = dis.readInt();
#if DEBUG_WKB_READER
	cout<<"WKB npoints: "<<size<<endl;
#endif
	CoordinateSequence *pts = readCoordinateSequence(size);
	return factory.createLineString(pts);
}

LinearRing *
WKBReader::readLinearRing()
{
	int size = dis.readInt();
#if DEBUG_WKB_READER
	cout<<"WKB npoints: "<<size<<endl;
#endif
	CoordinateSequence *pts = readCoordinateSequence(size);
	return factory.createLinearRing(pts);
}

Polygon *
WKBReader::readPolygon()
{
	int numRings = dis.readInt();

#if DEBUG_WKB_READER
	cout<<"WKB numRings: "<<numRings<<endl;
#endif

        LinearRing *shell = NULL;
        if( numRings > 0 )
            shell = readLinearRing();

	vector<Geometry *>*holes=NULL;
	if ( numRings > 1 )
	{
		try {
			holes = new vector<Geometry *>(numRings-1);
			for (int i=0; i<numRings-1; i++)
				(*holes)[i] = (Geometry *)readLinearRing();
		} catch (...) {
			for (unsigned int i=0; i<holes->size(); i++)
				delete (*holes)[i];
			delete holes;
			delete shell;
			throw;
		}
	}
	return factory.createPolygon(shell, holes);
}

MultiPoint *
WKBReader::readMultiPoint()
{
	int numGeoms = dis.readInt();
	vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);

	try {
		for (int i=0; i<numGeoms; i++)
		{
			Geometry *g = readGeometry();
			if (!dynamic_cast<Point *>(g))
			{
				stringstream err;
				err << BAD_GEOM_TYPE_MSG << " MultiPoint";
				throw ParseException(err.str());
			}
			(*geoms)[i] = g;
		}
	} catch (...) {
		for (unsigned int i=0; i<geoms->size(); i++)
			delete (*geoms)[i];
		delete geoms;
		throw;
	}
	return factory.createMultiPoint(geoms);
}

MultiLineString *
WKBReader::readMultiLineString()
{
	int numGeoms = dis.readInt();
	vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);

	try {
		for (int i=0; i<numGeoms; i++)
		{
			Geometry *g = readGeometry();
			if (!dynamic_cast<LineString *>(g))
			{
				stringstream err;
				err << BAD_GEOM_TYPE_MSG << " LineString";
				throw  ParseException(err.str());
			}
			(*geoms)[i] = g;
		}
	} catch (...) {
		for (unsigned int i=0; i<geoms->size(); i++)
			delete (*geoms)[i];
		delete geoms;
		throw;
	}
	return factory.createMultiLineString(geoms);
}

MultiPolygon *
WKBReader::readMultiPolygon()
{
	int numGeoms = dis.readInt();
	vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);

	try {
		for (int i=0; i<numGeoms; i++)
		{
			Geometry *g = readGeometry();
			if (!dynamic_cast<Polygon *>(g))
			{
				stringstream err;
				err << BAD_GEOM_TYPE_MSG << " Polygon";
				throw  ParseException(err.str());
			}
			(*geoms)[i] = g;
		}
	} catch (...) {
		for (unsigned int i=0; i<geoms->size(); i++)
			delete (*geoms)[i];
		delete geoms;
		throw;
	}
	return factory.createMultiPolygon(geoms);
}

GeometryCollection *
WKBReader::readGeometryCollection()
{
	int numGeoms = dis.readInt();
	vector<Geometry *> *geoms = new vector<Geometry *>(numGeoms);

	try {
		for (int i=0; i<numGeoms; i++)
			(*geoms)[i] = (readGeometry());
	} catch (...) {
		for (unsigned int i=0; i<geoms->size(); i++)
			delete (*geoms)[i];
		delete geoms;
		throw;
	}
	return factory.createGeometryCollection(geoms);
}

CoordinateSequence *
WKBReader::readCoordinateSequence(int size)
{
	CoordinateSequence *seq = factory.getCoordinateSequenceFactory()->create(size, inputDimension);
	unsigned int targetDim = seq->getDimension();
	if ( targetDim > inputDimension )
		targetDim = inputDimension;
	for (int i=0; i<size; i++) {
		readCoordinate();
		for (unsigned int j=0; j<targetDim; j++) {
			seq->setOrdinate(i, j, ordValues[j]);
		}
	}
	return seq;
}

void
WKBReader::readCoordinate()
{
	const PrecisionModel &pm = *factory.getPrecisionModel();
	for (unsigned int i=0; i<inputDimension; ++i)
	{
		if ( i <= 1 ) ordValues[i] = pm.makePrecise(dis.readDouble());
		else ordValues[i] = dis.readDouble();
	}
#if DEBUG_WKB_READER
	cout<<"WKB coordinate: "<<ordValues[0]<<","<<ordValues[1]<<endl;
#endif
}

} // namespace geos.io
} // namespace geos
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// CLASS HEADER
#include <dali/internal/render/gl-resources/texture.h>

// EXTERNAL INCLUDES
#include <math.h>
#include <memory.h>

// INTERNAL INCLUDES
#include <dali/integration-api/debug.h>
#include <dali/internal/render/common/vertex.h>
#include <dali/internal/render/gl-resources/context.h>
#include <dali/internal/common/image-sampler.h>

namespace Dali
{

namespace Internal
{

namespace
{

// These match the GL specification
const GLint SYSTEM_MINIFY_DEFAULT  = GL_NEAREST_MIPMAP_LINEAR;
const GLint SYSTEM_MAGNIFY_DEFAULT = GL_LINEAR;

// These are the Dali defaults
const GLint DALI_MINIFY_DEFAULT  = GL_LINEAR;
const GLint DALI_MAGNIFY_DEFAULT = GL_LINEAR;

} // namespace

/**
 * @brief Convert a FilterMode to it's corresponding GL type.
 *
 * @param[in] filterMode The FilterMode type.
 * @param[in] defaultfilterMode The filter mode to use if filterMode is DEFAULT.
 * @param[in] defaultSystemFilterMode The filter mode to use if filterMode is NONE.
 * @return Return the equivalent GL type.
 */
GLint FilterModeToGL( FilterMode::Type filterMode, GLint defaultfilterMode, GLint defaultSystemFilterMode )
{
  switch( filterMode )
  {
    case FilterMode::NEAREST:
    {
      return GL_NEAREST;
    }
    case FilterMode::LINEAR:
    {
      return GL_LINEAR;
    }
    case FilterMode::NONE:
    {
      return defaultSystemFilterMode;
    }
    case FilterMode::DEFAULT:
    {
      return defaultfilterMode;
    }
  }

  return GL_LINEAR;
}

using Dali::Internal::Vertex2D;
using Dali::Internal::Vertex3D;

using namespace Dali::Pixel;

Texture::Texture(Context&      context,
                 unsigned int  width,
                 unsigned int  height,
                 unsigned int  imageWidth,
                 unsigned int  imageHeight,
                 Pixel::Format pixelFormat)
: mContext(context),
  mId(0),
  mSamplerBitfield( 0 ),
  mWidth(width),
  mHeight(height),
  mImageWidth(imageWidth),
  mImageHeight(imageHeight),
  mPixelFormat(pixelFormat),
  mDiscarded(false)
{
}

Texture::~Texture()
{
  // GlCleanup() should already have been called by TextureCache ensuring the resource is destroyed
  // on the render thread. (And avoiding a potentially problematic virtual call in the destructor)
}

void Texture::SetTextureId(GLuint id)
{
  mId=id;
}

void Texture::Update(Integration::Bitmap* bitmap)
{
  DALI_ASSERT_DEBUG( "Updating incorrect texture type" == NULL );
}

void Texture::UpdateArea( const RectArea& area )
{
  DALI_ASSERT_DEBUG( "Updating incorrect texture type" == NULL );
}

bool Texture::UpdateOnCreate()
{
  return false;
}

bool Texture::Bind(GLenum target, GLenum textureunit )
{
  // This is the only supported type at the moment
  DALI_ASSERT_DEBUG( target == GL_TEXTURE_2D );
  bool created = false;

  if( mId == 0 )
  {
    if( CreateGlTexture() )
    {
      created = true;
    }
  }

  // Bind the texture id
  mContext.ActiveTexture(textureunit);
  mContext.Bind2dTexture(mId);

  return created;
}

void Texture::GlContextDestroyed()
{
  // texture is gone
  mId = 0;
}

void Texture::GlCleanup()
{
  // delete the gl texture
  if (mId != 0)
  {
    mContext.DeleteTextures(1,&mId);
    mId = 0;
  }
}

void Texture::MapUV(unsigned int numVerts,Vertex2D *verts, const PixelArea* pixelArea)
{
  MapUV(numVerts, (float*)(&verts->mU), sizeof(Vertex2D)/sizeof(float), pixelArea);
}

void Texture::MapUV(unsigned int numVerts,Vertex3D *verts, const PixelArea* pixelArea)
{
  MapUV(numVerts, (float*)(&verts->mU), sizeof(Vertex3D)/sizeof(float), pixelArea);
}

void Texture::MapUV(unsigned int numVerts, float* verts, unsigned int stride, const PixelArea* pixelArea)
{
  UvRect uv;

  GetTextureCoordinates(uv, pixelArea);

  float uScale = fabsf(uv.u2 - uv.u0);
  float vScale = fabsf(uv.v2 - uv.v0);

  for (unsigned int i = 0; i < numVerts; ++i)
  {
    verts[0] = uv.u0 + verts[0] * uScale;
    verts[1] = uv.v0 + verts[1] * vScale;
    verts += stride;
  }
}

unsigned int Texture::GetWidth() const
{
  return mWidth;
}

unsigned int Texture::GetHeight() const
{
  return mHeight;
}

Pixel::Format Texture::GetPixelFormat() const
{
  return mPixelFormat;
}

void Texture::GetTextureCoordinates(UvRect& uv, const PixelArea* pixelArea)
{
  if( pixelArea == NULL )
  {
     GetDefaultTextureCoordinates(uv);
     return;
  }

  // pre-calulate the normalized values

  const float uScale = 1.0f / float(mWidth);
  const float vScale = 1.0f / float(mHeight);
  const float x = uScale * float(pixelArea->x);
  const float y = vScale * float(pixelArea->y);
  const float width  = uScale * float(pixelArea->width);
  const float height = vScale * float(pixelArea->height);

  // bottom left
  uv.u0 = x;
  uv.v0 = y;

  // top right
  uv.u2 = x + width;
  uv.v2 = y + height;

};

void Texture::GetDefaultTextureCoordinates(UvRect& uv) const
{
  if ((mWidth == mImageWidth) && (mHeight == mImageHeight))
  {
    // set the uv's to display 0,0 to 1,1
    uv.Reset();
    return;
  }

  // the texture co-ordinates go from 0 to 1. But the image is smaller than the
  // texture, so we need to adjust the uv values.
  float uScale = float(mImageWidth)  / float(mWidth);
  float vScale = float(mImageHeight) / float(mHeight);

  // bottom left
  uv.u0 = 0.0f;
  uv.v0 = 0.0f;

  // top right
  uv.u2 = uScale;
  uv.v2 = vScale;

}

void Texture::ApplyTextureParameter( GLint filterType, FilterMode::Type currentFilterMode, FilterMode::Type newFilterMode, GLint daliDefault, GLint systemDefault )
{
  GLint newFilterModeGL = FilterModeToGL( newFilterMode, daliDefault, systemDefault );
  GLint currentFilterModeGL = FilterModeToGL( currentFilterMode, daliDefault, systemDefault );

  if( newFilterModeGL != currentFilterModeGL )
  {
    mContext.TexParameteri( GL_TEXTURE_2D, filterType, newFilterModeGL );
  }
}

void Texture::ApplySampler( unsigned int samplerBitfield )
{
  if( mSamplerBitfield != samplerBitfield )
  {
    ApplyTextureParameter( GL_TEXTURE_MIN_FILTER,
                           ImageSampler::GetMinifyFilterMode( mSamplerBitfield ),
                           ImageSampler::GetMinifyFilterMode( samplerBitfield ),
                           DALI_MINIFY_DEFAULT,
                           SYSTEM_MINIFY_DEFAULT );

    ApplyTextureParameter( GL_TEXTURE_MAG_FILTER,
                           ImageSampler::GetMagnifyFilterMode( mSamplerBitfield ),
                           ImageSampler::GetMagnifyFilterMode( samplerBitfield ),
                           DALI_MAGNIFY_DEFAULT,
                           SYSTEM_MAGNIFY_DEFAULT );

    mSamplerBitfield = samplerBitfield;
  }
}

} // namespace Internal

} // namespace Dali
<commit_msg>reset the sampler state in context loss<commit_after>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// CLASS HEADER
#include <dali/internal/render/gl-resources/texture.h>

// EXTERNAL INCLUDES
#include <math.h>
#include <memory.h>

// INTERNAL INCLUDES
#include <dali/integration-api/debug.h>
#include <dali/internal/render/common/vertex.h>
#include <dali/internal/render/gl-resources/context.h>
#include <dali/internal/common/image-sampler.h>

namespace Dali
{

namespace Internal
{

namespace
{

// These match the GL specification
const GLint SYSTEM_MINIFY_DEFAULT  = GL_NEAREST_MIPMAP_LINEAR;
const GLint SYSTEM_MAGNIFY_DEFAULT = GL_LINEAR;

// These are the Dali defaults
const GLint DALI_MINIFY_DEFAULT  = GL_LINEAR;
const GLint DALI_MAGNIFY_DEFAULT = GL_LINEAR;

} // namespace

/**
 * @brief Convert a FilterMode to it's corresponding GL type.
 *
 * @param[in] filterMode The FilterMode type.
 * @param[in] defaultfilterMode The filter mode to use if filterMode is DEFAULT.
 * @param[in] defaultSystemFilterMode The filter mode to use if filterMode is NONE.
 * @return Return the equivalent GL type.
 */
GLint FilterModeToGL( FilterMode::Type filterMode, GLint defaultfilterMode, GLint defaultSystemFilterMode )
{
  switch( filterMode )
  {
    case FilterMode::NEAREST:
    {
      return GL_NEAREST;
    }
    case FilterMode::LINEAR:
    {
      return GL_LINEAR;
    }
    case FilterMode::NONE:
    {
      return defaultSystemFilterMode;
    }
    case FilterMode::DEFAULT:
    {
      return defaultfilterMode;
    }
  }

  return GL_LINEAR;
}

using Dali::Internal::Vertex2D;
using Dali::Internal::Vertex3D;

using namespace Dali::Pixel;

Texture::Texture(Context&      context,
                 unsigned int  width,
                 unsigned int  height,
                 unsigned int  imageWidth,
                 unsigned int  imageHeight,
                 Pixel::Format pixelFormat)
: mContext(context),
  mId(0),
  mSamplerBitfield( 0 ),
  mWidth(width),
  mHeight(height),
  mImageWidth(imageWidth),
  mImageHeight(imageHeight),
  mPixelFormat(pixelFormat),
  mDiscarded(false)
{
}

Texture::~Texture()
{
  // GlCleanup() should already have been called by TextureCache ensuring the resource is destroyed
  // on the render thread. (And avoiding a potentially problematic virtual call in the destructor)
}

void Texture::SetTextureId(GLuint id)
{
  mId=id;
}

void Texture::Update(Integration::Bitmap* bitmap)
{
  DALI_ASSERT_DEBUG( "Updating incorrect texture type" == NULL );
}

void Texture::UpdateArea( const RectArea& area )
{
  DALI_ASSERT_DEBUG( "Updating incorrect texture type" == NULL );
}

bool Texture::UpdateOnCreate()
{
  return false;
}

bool Texture::Bind(GLenum target, GLenum textureunit )
{
  // This is the only supported type at the moment
  DALI_ASSERT_DEBUG( target == GL_TEXTURE_2D );
  bool created = false;

  if( mId == 0 )
  {
    if( CreateGlTexture() )
    {
      created = true;
    }
  }

  // Bind the texture id
  mContext.ActiveTexture(textureunit);
  mContext.Bind2dTexture(mId);

  return created;
}

void Texture::GlContextDestroyed()
{
  // texture is gone
  mId = 0;
  // reset sampler state as well
  mSamplerBitfield = 0;
}

void Texture::GlCleanup()
{
  // delete the gl texture
  if (mId != 0)
  {
    mContext.DeleteTextures(1,&mId);
    mId = 0;
  }
}

void Texture::MapUV(unsigned int numVerts,Vertex2D *verts, const PixelArea* pixelArea)
{
  MapUV(numVerts, (float*)(&verts->mU), sizeof(Vertex2D)/sizeof(float), pixelArea);
}

void Texture::MapUV(unsigned int numVerts,Vertex3D *verts, const PixelArea* pixelArea)
{
  MapUV(numVerts, (float*)(&verts->mU), sizeof(Vertex3D)/sizeof(float), pixelArea);
}

void Texture::MapUV(unsigned int numVerts, float* verts, unsigned int stride, const PixelArea* pixelArea)
{
  UvRect uv;

  GetTextureCoordinates(uv, pixelArea);

  float uScale = fabsf(uv.u2 - uv.u0);
  float vScale = fabsf(uv.v2 - uv.v0);

  for (unsigned int i = 0; i < numVerts; ++i)
  {
    verts[0] = uv.u0 + verts[0] * uScale;
    verts[1] = uv.v0 + verts[1] * vScale;
    verts += stride;
  }
}

unsigned int Texture::GetWidth() const
{
  return mWidth;
}

unsigned int Texture::GetHeight() const
{
  return mHeight;
}

Pixel::Format Texture::GetPixelFormat() const
{
  return mPixelFormat;
}

void Texture::GetTextureCoordinates(UvRect& uv, const PixelArea* pixelArea)
{
  if( pixelArea == NULL )
  {
     GetDefaultTextureCoordinates(uv);
     return;
  }

  // pre-calulate the normalized values

  const float uScale = 1.0f / float(mWidth);
  const float vScale = 1.0f / float(mHeight);
  const float x = uScale * float(pixelArea->x);
  const float y = vScale * float(pixelArea->y);
  const float width  = uScale * float(pixelArea->width);
  const float height = vScale * float(pixelArea->height);

  // bottom left
  uv.u0 = x;
  uv.v0 = y;

  // top right
  uv.u2 = x + width;
  uv.v2 = y + height;

};

void Texture::GetDefaultTextureCoordinates(UvRect& uv) const
{
  if ((mWidth == mImageWidth) && (mHeight == mImageHeight))
  {
    // set the uv's to display 0,0 to 1,1
    uv.Reset();
    return;
  }

  // the texture co-ordinates go from 0 to 1. But the image is smaller than the
  // texture, so we need to adjust the uv values.
  float uScale = float(mImageWidth)  / float(mWidth);
  float vScale = float(mImageHeight) / float(mHeight);

  // bottom left
  uv.u0 = 0.0f;
  uv.v0 = 0.0f;

  // top right
  uv.u2 = uScale;
  uv.v2 = vScale;

}

void Texture::ApplyTextureParameter( GLint filterType, FilterMode::Type currentFilterMode, FilterMode::Type newFilterMode, GLint daliDefault, GLint systemDefault )
{
  GLint newFilterModeGL = FilterModeToGL( newFilterMode, daliDefault, systemDefault );
  GLint currentFilterModeGL = FilterModeToGL( currentFilterMode, daliDefault, systemDefault );

  if( newFilterModeGL != currentFilterModeGL )
  {
    mContext.TexParameteri( GL_TEXTURE_2D, filterType, newFilterModeGL );
  }
}

void Texture::ApplySampler( unsigned int samplerBitfield )
{
  if( mSamplerBitfield != samplerBitfield )
  {
    ApplyTextureParameter( GL_TEXTURE_MIN_FILTER,
                           ImageSampler::GetMinifyFilterMode( mSamplerBitfield ),
                           ImageSampler::GetMinifyFilterMode( samplerBitfield ),
                           DALI_MINIFY_DEFAULT,
                           SYSTEM_MINIFY_DEFAULT );

    ApplyTextureParameter( GL_TEXTURE_MAG_FILTER,
                           ImageSampler::GetMagnifyFilterMode( mSamplerBitfield ),
                           ImageSampler::GetMagnifyFilterMode( samplerBitfield ),
                           DALI_MAGNIFY_DEFAULT,
                           SYSTEM_MAGNIFY_DEFAULT );

    mSamplerBitfield = samplerBitfield;
  }
}

} // namespace Internal

} // namespace Dali
<|endoftext|>


<commit_before>/*!
  \copyright (c) RDO-Team, 2011
  \file      rdofile.cpp
  \authors   Урусов Андрей (rdo@rk9.bmstu.ru)
  \authors   Пройдаков Евгений (lord.tiran@gmail.com)
  \date      07.11.2020
  \brief
  \indent    4T
*/

// ---------------------------------------------------------------------------- PCH
// ----------------------------------------------------------------------- PLATFORM
#include "utils/platform.h"
// ----------------------------------------------------------------------- INCLUDES
#ifdef COMPILER_VISUAL_STUDIO
#   include <Windows.h>
#   include <io.h>
#else
#   include <unistd.h>
#endif // COMPILER_VISUAL_STUDIO
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
// ----------------------------------------------------------------------- SYNOPSIS
#include "utils/rdofile.h"
#include "utils/rdocommon.h"
#include "utils/rdolocale.h"
// --------------------------------------------------------------------------------

OPEN_RDO_NAMESPACE

rbool File::create(CREF(tstring) name)
{
	return create(name, "");
}

rbool File::create(CREF(tstring) name, CREF(tstring) content)
{
	boost::filesystem::fstream file(rdo::locale::convertToWStr(name).c_str(), std::ios::out | std::ios::binary);
	file << content << std::endl;
	file.close();
	return true;
}

rbool File::exist(CREF(tstring) name)
{
	return  boost::filesystem::exists(rdo::locale::convertToWStr(name));
}

rbool File::read_only(CREF(tstring) name)
{
#ifdef COMPILER_VISUAL_STUDIO
	return _access(name.c_str(), 04) == 0 && _access(name.c_str(), 06) == -1;
#endif  // COMPILER_VISUAL_STUDIO
#ifdef COMPILER_GCC
	return access(name.c_str(), R_OK) == 0 && access(name.c_str(), W_OK) == -1;
#endif // COMPILER_GCC
}

rbool File::unlink(CREF(tstring) name)
{
	return boost::filesystem::remove(rdo::locale::convertToWStr(name));
}

rbool File::splitpath(CREF(tstring) name, REF(tstring) fileDir, REF(tstring) fileName, REF(tstring) fileExt)
{
	boost::filesystem::path from(name);
	boost::filesystem::path parentDir(from.parent_path());
	boost::filesystem::path rootName      = parentDir.root_name();
	boost::filesystem::path rootDirectory = parentDir.root_directory();
	if ((rootName.empty() && rootDirectory.empty()) || parentDir != (rootName / rootDirectory))
	{
		parentDir /= boost::filesystem::path("/");
	}

	fileDir  = parentDir.make_preferred().string();
	fileName = from.stem().string();
	fileExt  = from.extension().string();

	return true;
}

tstring File::getTempFileName()
{
#ifdef COMPILER_VISUAL_STUDIO
	const ruint BUFSIZE = 4096;
	tchar lpPathBuffer[BUFSIZE];

	if (::GetTempPath(BUFSIZE, lpPathBuffer) == 0)
	{
		return tstring();
	}
	tchar szTempName[MAX_PATH];
	if (::GetTempFileName(lpPathBuffer, NULL, 0, szTempName) == 0)
	{
		return tstring();
	}
	return szTempName;
#endif // COMPILER_VISUAL_STUDIO
#ifdef COMPILER_GCC
	boost::uuids::random_generator random_gen;
	tstring tempFileName = tstring("/tmp/rdo_temp_file_num_") + boost::uuids::to_string(random_gen());
	create(tempFileName);
	return tempFileName;
#endif // COMPILER_GCC
}

tstring File::extractFilePath(CREF(tstring) fileName)
{
	boost::filesystem::path fullFileName(fileName);
	tstring result = (fullFileName.make_preferred().parent_path() / boost::filesystem::path("/").make_preferred()).string();
	return result;
}

rbool File::trimLeft(CREF(tstring) name)
{
	boost::filesystem::ifstream inputStream(rdo::locale::convertToWStr(name).c_str(), std::ios::binary);
	std::stringstream sstream;

	if (!inputStream.good())
	{
		return false;
	}

	rbool empty = true;
	while (!inputStream.eof())
	{
		char byte;
		inputStream.get(byte);

		if (empty)
		{
			if (byte != ' ' && byte != '\t' && byte != '\n' && byte != '\r')
			{
				empty = false;
			}
		}

		if (!empty)
		{
			sstream.write(&byte, 1);
		}
	}

	inputStream.close();

	boost::filesystem::path to(name);

	try
	{
		if (!boost::filesystem::remove(to))
		{
			return false;
		}
		boost::filesystem::ofstream outStream(name.c_str(), std::ios::binary);
		outStream << sstream.str();
	}
	catch (CREF(boost::system::error_code))
	{
		return false;
	}
	return true;
}

CLOSE_RDO_NAMESPACE
<commit_msg> - чистка кода<commit_after>/*!
  \copyright (c) RDO-Team, 2011
  \file      rdofile.cpp
  \authors   Урусов Андрей (rdo@rk9.bmstu.ru)
  \authors   Пройдаков Евгений (lord.tiran@gmail.com)
  \date      07.11.2020
  \brief
  \indent    4T
*/

// ---------------------------------------------------------------------------- PCH
// ----------------------------------------------------------------------- PLATFORM
#include "utils/platform.h"
// ----------------------------------------------------------------------- INCLUDES
#ifdef COMPILER_VISUAL_STUDIO
#   include <Windows.h>
#   include <io.h>
#else
#   include <unistd.h>
#endif // COMPILER_VISUAL_STUDIO
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
// ----------------------------------------------------------------------- SYNOPSIS
#include "utils/rdofile.h"
#include "utils/rdocommon.h"
#include "utils/rdolocale.h"
// --------------------------------------------------------------------------------

OPEN_RDO_NAMESPACE

rbool File::create(CREF(tstring) name)
{
	return create(name, "");
}

rbool File::create(CREF(tstring) name, CREF(tstring) content)
{
	boost::filesystem::fstream file(rdo::locale::convertToWStr(name), std::ios::out | std::ios::binary);
	file << content << std::endl;
	file.close();
	return true;
}

rbool File::exist(CREF(tstring) name)
{
	return  boost::filesystem::exists(rdo::locale::convertToWStr(name));
}

rbool File::read_only(CREF(tstring) name)
{
#ifdef COMPILER_VISUAL_STUDIO
	return _access(name.c_str(), 04) == 0 && _access(name.c_str(), 06) == -1;
#endif  // COMPILER_VISUAL_STUDIO
#ifdef COMPILER_GCC
	return access(name.c_str(), R_OK) == 0 && access(name.c_str(), W_OK) == -1;
#endif // COMPILER_GCC
}

rbool File::unlink(CREF(tstring) name)
{
	return boost::filesystem::remove(rdo::locale::convertToWStr(name));
}

rbool File::splitpath(CREF(tstring) name, REF(tstring) fileDir, REF(tstring) fileName, REF(tstring) fileExt)
{
	boost::filesystem::path from(name);
	boost::filesystem::path parentDir(from.parent_path());
	boost::filesystem::path rootName      = parentDir.root_name();
	boost::filesystem::path rootDirectory = parentDir.root_directory();
	if ((rootName.empty() && rootDirectory.empty()) || parentDir != (rootName / rootDirectory))
	{
		parentDir /= boost::filesystem::path("/");
	}

	fileDir  = parentDir.make_preferred().string();
	fileName = from.stem().string();
	fileExt  = from.extension().string();

	return true;
}

tstring File::getTempFileName()
{
#ifdef COMPILER_VISUAL_STUDIO
	const ruint BUFSIZE = 4096;
	tchar lpPathBuffer[BUFSIZE];

	if (::GetTempPath(BUFSIZE, lpPathBuffer) == 0)
	{
		return tstring();
	}
	tchar szTempName[MAX_PATH];
	if (::GetTempFileName(lpPathBuffer, NULL, 0, szTempName) == 0)
	{
		return tstring();
	}
	return szTempName;
#endif // COMPILER_VISUAL_STUDIO
#ifdef COMPILER_GCC
	boost::uuids::random_generator random_gen;
	tstring tempFileName = tstring("/tmp/rdo_temp_file_num_") + boost::uuids::to_string(random_gen());
	create(tempFileName);
	return tempFileName;
#endif // COMPILER_GCC
}

tstring File::extractFilePath(CREF(tstring) fileName)
{
	boost::filesystem::path fullFileName(fileName);
	tstring result = (fullFileName.make_preferred().parent_path() / boost::filesystem::path("/").make_preferred()).string();
	return result;
}

rbool File::trimLeft(CREF(tstring) name)
{
	boost::filesystem::ifstream inputStream(rdo::locale::convertToWStr(name), std::ios::binary);
	std::stringstream sstream;

	if (!inputStream.good())
	{
		return false;
	}

	rbool empty = true;
	while (!inputStream.eof())
	{
		char byte;
		inputStream.get(byte);

		if (empty)
		{
			if (byte != ' ' && byte != '\t' && byte != '\n' && byte != '\r')
			{
				empty = false;
			}
		}

		if (!empty)
		{
			sstream.write(&byte, 1);
		}
	}

	inputStream.close();

	boost::filesystem::path to(name);

	try
	{
		if (!boost::filesystem::remove(to))
		{
			return false;
		}
		boost::filesystem::ofstream outStream(name, std::ios::binary);
		outStream << sstream.str();
	}
	catch (CREF(boost::system::error_code))
	{
		return false;
	}
	return true;
}

CLOSE_RDO_NAMESPACE
<|endoftext|>


<commit_before>#include "Rainback.hpp"

#include <iostream>

#include <functional>
#include <QTextStream>
#include <QApplication>
#include <QPushButton>
#include <QLineEdit>
#include <QTextEdit>
#include <QRegExp>
#include <QFileSystemWatcher>
#include <QTcpSocket>

#include <lua-cxx/LuaValue.hpp>
#include <lua-cxx/loaders.hpp>
#include <lua-cxx/userdata.hpp>
#include <lua-cxx/QObjectObserver.hpp>

#include "LuaPainter.hpp"
#include "LuaFont.hpp"
#include "proxy/TCPServer.hpp"
#include "protocol/Human.hpp"
#include "protocol/Pascal.hpp"

#include "proxy/QObject.hpp"
#include "proxy/Socket.hpp"
#include "proxy/TCPServer.hpp"
#include "proxy/QWidget.hpp"
#include "proxy/AbstractLine.hpp"

using namespace rainback;

std::shared_ptr<LuaFont> newFont(LuaStack& stack)
{
    switch (stack.size()) {
    case 0:
        return std::make_shared<LuaFont>();
    case 1:
        return std::make_shared<LuaFont>(stack.as<QString>(1));
    default:
        return std::make_shared<LuaFont>(
            stack.as<QString>(1),
            stack.as<int>(2)
        );
    }
}

namespace rainback {

void Rainback::assertWidget()
{
    if (!_widget) {
        throw LuaException("This function must not be used when no widget is available");
    }
}

Rainback::Rainback(Lua& lua) :
    _widget(nullptr),
    _lua(lua)
{
	if (_lua["unpack"].isNil()) {
		_lua["unpack"] = _lua["table"]["unpack"];
	}

    _lua["Rainback"] = lua::value::table;

    _lua["error"] = lua::LuaCallable([](LuaStack& stack) {
        stack.error(stack.as<std::string>(-1));
    });

    _lua["Rainback"]["globals"] = lua::value::table;

    _lua["Rainback"]["AddModuleDirectory"] =
    std::function<void(const std::string&, const std::string&)>(
        [this](const std::string& root, const std::string& prefix) {
            auto loader = std::make_shared<DirectoryModuleLoader>();
            loaders.push_back(loader);
            loader->setRoot(QDir(root.c_str()));
            loader->setPrefix(prefix);
            _lua.addModuleLoader(loader.get());
        }
    );

    _lua["Rainback"]["LoadDirectory"] = std::function<void(const std::string&, const bool)>(
        [this](const std::string& path, const bool recurse) {
            lua::load_dir(_lua, QDir(path.c_str()), recurse);
        }
    );

    _lua["Rainback"]["Font"] = newFont;

    _lua["Rainback"]["Button"] = lua::LuaCallable([this](LuaStack& stack) {
            assertWidget();
            stack.clear();
            lua::push<QWidget*>(stack, new QPushButton(_widget), true);
        }
    );

    _lua["Rainback"]["LineEdit"] = lua::LuaCallable([this](LuaStack& stack) {
            assertWidget();
            stack.clear();
            lua::push<QWidget*>(stack, new QLineEdit(_widget), true);
        }
    );

    _lua["Rainback"]["TextEdit"] = lua::LuaCallable([this](LuaStack& stack) {
            assertWidget();
            stack.clear();
            lua::push<QWidget*>(stack, new QTextEdit(_widget), true);
        }
    );

    _lua["Rainback" ]["ScreenWidth"] = std::function<int()>([this]() {
        assertWidget();
        return _widget->width();
    });

    _lua["Rainback" ]["ScreenHeight"] = std::function<int()>([this]() {
        assertWidget();
        return _widget->height();
    });

    _lua["Rainback"]["GetTime"] = std::function<long()>([this]() {
        return elapsed.elapsed();
    });

    _lua["Rainback"]["StartTick"] =
    std::function<std::function<void()>(const int)>([this](const int framerate) {
        timer.start(1000 / framerate);
        return [this]() {
            timer.stop();
        };
    });

    _lua["Rainback"]["GetCursorPosition"] =
    std::function<void(LuaStack&)>([this](LuaStack& stack) {
        assertWidget();
        QPoint pos(_widget->mapFromGlobal(QCursor::pos()));
        stack.clear();
        lua::push(stack, pos.x());
        lua::push(stack, pos.y());
    });

    _lua["Rainback"]["Update"] = std::function<void()>([this]() {
        assertWidget();
        _widget->update();
    });

    _lua["Rainback"]["Close"] = std::function<void()>([this]() {
        assertWidget();
        qApp->closeAllWindows();
    });

    _lua["Rainback"]["SetBackgroundColor"] = std::function<void(const int, const int, const int)>(
        [this](const int r, const int g, const int b) {
            assertWidget();
            _widget->setAutoFillBackground(true);
            QPalette p(_widget->palette());
            p.setColor(QPalette::Background, QColor(r, g, b));
            _widget->setPalette(p);
        }
    );

    _lua["Rainback"]["FileExists"] = std::function<bool(const QString&)>(
        [this](const QString& fileName) {
            return QFile(fileName).exists();
        }
    );

    _lua["Rainback"]["ReadFile"] = std::function<QString(const QString&)>(
        [this](const QString& fileName) {
            QFile file(fileName);
            openFile(file, QIODevice::ReadOnly, true);
            // Using QTextStream here breaks for Unicode strings
            return QString(file.readAll());
        }
    );

    _lua["Rainback"]["ReadCompressedFile"] = std::function<QString(const QString&)>(
        [this](const QString& fileName) {
            QFile file(fileName);
            openFile(file, QIODevice::ReadOnly, true);
            return QString(qUncompress(file.readAll()));
        }
    );

    _lua["Rainback"]["WriteFile"] = std::function<void(const QString&, const QString&)>(
        [this](const QString& fileName, const QString& data) {
            QFile file(fileName);
            openFile(file, QIODevice::WriteOnly | QIODevice::Truncate, false);
            QTextStream(&file) << data;
        }
    );

    _lua["Rainback"]["WriteCompressedFile"] = std::function<void(const QString&, const QString&)>(
        [this](const QString& fileName, const QString& data) {
            QFile file(fileName);
            openFile(file, QIODevice::WriteOnly | QIODevice::Truncate, false);
            file.write(qCompress(data.toUtf8()));
        }
    );

    _lua["Rainback"]["WatchFile"] = std::function<void(LuaStack&)>(
        [this](LuaStack& stack) {
            auto watcher = new QFileSystemWatcher(this);

            for (int i=1; i <= stack.size(); ++i) {
                watcher->addPath(stack.as<QString>(i));
            }
            stack.clear();

            lua::push<QObject*>(stack, watcher, true);
            proxy::observeToDestroy(watcher, stack.as<LuaUserdata*>(-1));
        }
    );

    _lua["string"]["grep"] = lua::LuaCallable([](LuaStack& stack) {
        auto str = stack.as<QString>(1);
        QRegExp re(stack.as<QString>(2));
        stack.clear();

        if (re.indexIn(str) == -1) {
            return;
        }

        for (int i = 0; i <= re.captureCount(); ++i) {
            lua::push(stack, re.cap(i));
        }
    });

    _lua["Rainback"]["Network"] = lua::value::table;

    auto buildSocket = [](LuaStack& stack, QAbstractSocket* socket) {
        proxy::observeToDestroy(socket, stack.as<LuaUserdata*>(-1));
        connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
    };

    _lua["Rainback"]["Network"]["serveTCP"] = lua::LuaCallable(
        [this](LuaStack& stack) {
            if (stack.size() < 1) {
                throw LuaException("serverTCP requires 1 argument, but the stack has none");
            }

            auto port = stack.as<int>(1);
            proxy::validatePort(port);
            stack.clear();

            auto server = new proxy::TCPServer;
            auto socketServer = new QTcpServer(server);
            server->setServer(socketServer);

            lua::push<QObject*>(stack, server, true);
            proxy::observeToDestroy(server, stack.as<LuaUserdata*>(-1));

            socketServer->listen(QHostAddress::Any, port);
        }
    );

    _lua["Rainback"]["Network"]["connectTCP"] = lua::LuaCallable(
        [this, buildSocket](LuaStack& stack) {
            if (stack.size() < 2) {
                std::stringstream str;
                str << "connectTCP requires 2 arguments, but was given only " << stack.size();
                throw LuaException(str.str());
            }
            auto hostname = stack.as<QString>(1);
            auto port = stack.as<int>(2);
            proxy::validatePort(port);
            stack.clear();

            QAbstractSocket* socket = new QTcpSocket(this);
            lua::push<QAbstractSocket*>(stack, socket, true);
            buildSocket(stack, socket);

            socket->connectToHost(hostname, static_cast<quint16>(port));

            return socket;
        }
    );

    _lua["Rainback"]["Network"]["pascalProtocol"] = std::function<std::shared_ptr<protocol::AbstractLine>(LuaStack& stack)>(
        [this](LuaStack& stack) {
            std::shared_ptr<protocol::AbstractLine> ptl(std::make_shared<protocol::Pascal>());

            if (stack.size() > 0) {
                ptl->listen(stack.as<QAbstractSocket*>());
            }

            return ptl;
        }
    );

    _lua["Rainback"]["Network"]["humanProtocol"] = std::function<std::shared_ptr<protocol::AbstractLine>(LuaStack& stack)>(
        [this](LuaStack& stack) {
            std::shared_ptr<protocol::AbstractLine> ptl(std::make_shared<protocol::Human>());

            if (stack.size() > 0) {
                ptl->listen(stack.as<QAbstractSocket*>());
            }

            return ptl;
        }
    );

    lua::qvariantPusher(QVariant::Size, [](LuaStack& stack, const QVariant& source) {
        lua::push(stack, lua::value::table);

        auto size = source.toSize();
        stack.set("width", size.width());
        stack.set("height", size.height());
    });

    elapsed.start();

    connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
    connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(close()));
}

void Rainback::openFile(QFile& file, const QIODevice::OpenMode& flags, const bool checkExists)
{
    if (checkExists && !file.exists()) {
        throw LuaException(
            (QString("The specified file '") + file.fileName() + "' does not exist").toStdString()
        );
    }
    if (!file.open(flags)) {
        throw LuaException(
            (QString("The specified file '") + file.fileName() + "' failed to open: " + file.error()).toStdString()
        );
    }
}

void Rainback::timeout()
{
    rainback::dispatch(_lua, "UPDATE");
}

void Rainback::close()
{
    rainback::dispatch(_lua, "CLOSE");
}

} // namespace rainback

// vim: set ts=4 sw=4 :
<commit_msg>Removed a redundant call<commit_after>#include "Rainback.hpp"

#include <iostream>

#include <functional>
#include <QTextStream>
#include <QApplication>
#include <QPushButton>
#include <QLineEdit>
#include <QTextEdit>
#include <QRegExp>
#include <QFileSystemWatcher>
#include <QTcpSocket>

#include <lua-cxx/LuaValue.hpp>
#include <lua-cxx/loaders.hpp>
#include <lua-cxx/userdata.hpp>
#include <lua-cxx/QObjectObserver.hpp>

#include "LuaPainter.hpp"
#include "LuaFont.hpp"
#include "proxy/TCPServer.hpp"
#include "protocol/Human.hpp"
#include "protocol/Pascal.hpp"

#include "proxy/QObject.hpp"
#include "proxy/Socket.hpp"
#include "proxy/TCPServer.hpp"
#include "proxy/QWidget.hpp"
#include "proxy/AbstractLine.hpp"

using namespace rainback;

std::shared_ptr<LuaFont> newFont(LuaStack& stack)
{
    switch (stack.size()) {
    case 0:
        return std::make_shared<LuaFont>();
    case 1:
        return std::make_shared<LuaFont>(stack.as<QString>(1));
    default:
        return std::make_shared<LuaFont>(
            stack.as<QString>(1),
            stack.as<int>(2)
        );
    }
}

namespace rainback {

void Rainback::assertWidget()
{
    if (!_widget) {
        throw LuaException("This function must not be used when no widget is available");
    }
}

Rainback::Rainback(Lua& lua) :
    _widget(nullptr),
    _lua(lua)
{
	if (_lua["unpack"].isNil()) {
		_lua["unpack"] = _lua["table"]["unpack"];
	}

    _lua["Rainback"] = lua::value::table;

    _lua["error"] = lua::LuaCallable([](LuaStack& stack) {
        stack.error(stack.as<std::string>(-1));
    });

    _lua["Rainback"]["globals"] = lua::value::table;

    _lua["Rainback"]["AddModuleDirectory"] =
    std::function<void(const std::string&, const std::string&)>(
        [this](const std::string& root, const std::string& prefix) {
            auto loader = std::make_shared<DirectoryModuleLoader>();
            loaders.push_back(loader);
            loader->setRoot(QDir(root.c_str()));
            loader->setPrefix(prefix);
            _lua.addModuleLoader(loader.get());
        }
    );

    _lua["Rainback"]["LoadDirectory"] = std::function<void(const std::string&, const bool)>(
        [this](const std::string& path, const bool recurse) {
            lua::load_dir(_lua, QDir(path.c_str()), recurse);
        }
    );

    _lua["Rainback"]["Font"] = newFont;

    _lua["Rainback"]["Button"] = lua::LuaCallable([this](LuaStack& stack) {
            assertWidget();
            stack.clear();
            lua::push<QWidget*>(stack, new QPushButton(_widget), true);
        }
    );

    _lua["Rainback"]["LineEdit"] = lua::LuaCallable([this](LuaStack& stack) {
            assertWidget();
            stack.clear();
            lua::push<QWidget*>(stack, new QLineEdit(_widget), true);
        }
    );

    _lua["Rainback"]["TextEdit"] = lua::LuaCallable([this](LuaStack& stack) {
            assertWidget();
            stack.clear();
            lua::push<QWidget*>(stack, new QTextEdit(_widget), true);
        }
    );

    _lua["Rainback" ]["ScreenWidth"] = std::function<int()>([this]() {
        assertWidget();
        return _widget->width();
    });

    _lua["Rainback" ]["ScreenHeight"] = std::function<int()>([this]() {
        assertWidget();
        return _widget->height();
    });

    _lua["Rainback"]["GetTime"] = std::function<long()>([this]() {
        return elapsed.elapsed();
    });

    _lua["Rainback"]["StartTick"] =
    std::function<std::function<void()>(const int)>([this](const int framerate) {
        timer.start(1000 / framerate);
        return [this]() {
            timer.stop();
        };
    });

    _lua["Rainback"]["GetCursorPosition"] =
    std::function<void(LuaStack&)>([this](LuaStack& stack) {
        assertWidget();
        QPoint pos(_widget->mapFromGlobal(QCursor::pos()));
        stack.clear();
        lua::push(stack, pos.x());
        lua::push(stack, pos.y());
    });

    _lua["Rainback"]["Update"] = std::function<void()>([this]() {
        assertWidget();
        _widget->update();
    });

    _lua["Rainback"]["Close"] = std::function<void()>([this]() {
        assertWidget();
        qApp->closeAllWindows();
    });

    _lua["Rainback"]["SetBackgroundColor"] = std::function<void(const int, const int, const int)>(
        [this](const int r, const int g, const int b) {
            assertWidget();
            _widget->setAutoFillBackground(true);
            QPalette p(_widget->palette());
            p.setColor(QPalette::Background, QColor(r, g, b));
            _widget->setPalette(p);
        }
    );

    _lua["Rainback"]["FileExists"] = std::function<bool(const QString&)>(
        [this](const QString& fileName) {
            return QFile(fileName).exists();
        }
    );

    _lua["Rainback"]["ReadFile"] = std::function<QString(const QString&)>(
        [this](const QString& fileName) {
            QFile file(fileName);
            openFile(file, QIODevice::ReadOnly, true);
            // Using QTextStream here breaks for Unicode strings
            return QString(file.readAll());
        }
    );

    _lua["Rainback"]["ReadCompressedFile"] = std::function<QString(const QString&)>(
        [this](const QString& fileName) {
            QFile file(fileName);
            openFile(file, QIODevice::ReadOnly, true);
            return QString(qUncompress(file.readAll()));
        }
    );

    _lua["Rainback"]["WriteFile"] = std::function<void(const QString&, const QString&)>(
        [this](const QString& fileName, const QString& data) {
            QFile file(fileName);
            openFile(file, QIODevice::WriteOnly | QIODevice::Truncate, false);
            QTextStream(&file) << data;
        }
    );

    _lua["Rainback"]["WriteCompressedFile"] = std::function<void(const QString&, const QString&)>(
        [this](const QString& fileName, const QString& data) {
            QFile file(fileName);
            openFile(file, QIODevice::WriteOnly | QIODevice::Truncate, false);
            file.write(qCompress(data.toUtf8()));
        }
    );

    _lua["Rainback"]["WatchFile"] = std::function<void(LuaStack&)>(
        [this](LuaStack& stack) {
            auto watcher = new QFileSystemWatcher(this);

            for (int i=1; i <= stack.size(); ++i) {
                watcher->addPath(stack.as<QString>(i));
            }
            stack.clear();

            lua::push<QObject*>(stack, watcher, true);
            proxy::observeToDestroy(watcher, stack.as<LuaUserdata*>(-1));
        }
    );

    _lua["string"]["grep"] = lua::LuaCallable([](LuaStack& stack) {
        auto str = stack.as<QString>(1);
        QRegExp re(stack.as<QString>(2));
        stack.clear();

        if (re.indexIn(str) == -1) {
            return;
        }

        for (int i = 0; i <= re.captureCount(); ++i) {
            lua::push(stack, re.cap(i));
        }
    });

    _lua["Rainback"]["Network"] = lua::value::table;

    _lua["Rainback"]["Network"]["serveTCP"] = lua::LuaCallable(
        [this](LuaStack& stack) {
            if (stack.size() < 1) {
                throw LuaException("serverTCP requires 1 argument, but the stack has none");
            }

            auto port = stack.as<int>(1);
            proxy::validatePort(port);
            stack.clear();

            auto server = new proxy::TCPServer;
            auto socketServer = new QTcpServer(server);
            server->setServer(socketServer);

            lua::push<QObject*>(stack, server, true);
            proxy::observeToDestroy(server, stack.as<LuaUserdata*>(-1));

            socketServer->listen(QHostAddress::Any, port);
        }
    );

    _lua["Rainback"]["Network"]["connectTCP"] = lua::LuaCallable(
        [this](LuaStack& stack) {
            if (stack.size() < 2) {
                std::stringstream str;
                str << "connectTCP requires 2 arguments, but was given only " << stack.size();
                throw LuaException(str.str());
            }
            auto hostname = stack.as<QString>(1);
            auto port = stack.as<int>(2);
            proxy::validatePort(port);
            stack.clear();

            QAbstractSocket* socket = new QTcpSocket(this);
            lua::push<QAbstractSocket*>(stack, socket, true);

            socket->connectToHost(hostname, static_cast<quint16>(port));

            return socket;
        }
    );

    _lua["Rainback"]["Network"]["pascalProtocol"] = std::function<std::shared_ptr<protocol::AbstractLine>(LuaStack& stack)>(
        [this](LuaStack& stack) {
            std::shared_ptr<protocol::AbstractLine> ptl(std::make_shared<protocol::Pascal>());

            if (stack.size() > 0) {
                ptl->listen(stack.as<QAbstractSocket*>());
            }

            return ptl;
        }
    );

    _lua["Rainback"]["Network"]["humanProtocol"] = std::function<std::shared_ptr<protocol::AbstractLine>(LuaStack& stack)>(
        [this](LuaStack& stack) {
            std::shared_ptr<protocol::AbstractLine> ptl(std::make_shared<protocol::Human>());

            if (stack.size() > 0) {
                ptl->listen(stack.as<QAbstractSocket*>());
            }

            return ptl;
        }
    );

    lua::qvariantPusher(QVariant::Size, [](LuaStack& stack, const QVariant& source) {
        lua::push(stack, lua::value::table);

        auto size = source.toSize();
        stack.set("width", size.width());
        stack.set("height", size.height());
    });

    elapsed.start();

    connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
    connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(close()));
}

void Rainback::openFile(QFile& file, const QIODevice::OpenMode& flags, const bool checkExists)
{
    if (checkExists && !file.exists()) {
        throw LuaException(
            (QString("The specified file '") + file.fileName() + "' does not exist").toStdString()
        );
    }
    if (!file.open(flags)) {
        throw LuaException(
            (QString("The specified file '") + file.fileName() + "' failed to open: " + file.error()).toStdString()
        );
    }
}

void Rainback::timeout()
{
    rainback::dispatch(_lua, "UPDATE");
}

void Rainback::close()
{
    rainback::dispatch(_lua, "CLOSE");
}

} // namespace rainback

// vim: set ts=4 sw=4 :
<|endoftext|>