<commit_before>#include "llvmruby.h"

extern VALUE cLLVMBasicBlock;
extern VALUE cLLVMBuilder;

extern "C" {
VALUE
llvm_basic_block_wrap(BasicBlock* bb) {
  return Data_Wrap_Struct(cLLVMBasicBlock, NULL, NULL, bb);
}

VALUE
llvm_basic_block_builder(VALUE self) {
  BasicBlock* bb;
  Data_Get_Struct(self, BasicBlock, bb);
  IRBuilder<> *builder = new IRBuilder<>(bb);
  return Data_Wrap_Struct(cLLVMBuilder, NULL, NULL, builder);
}

#define DATA_GET_BUILDER IRBuilder<> *builder; Data_Get_Struct(self, IRBuilder<>, builder);
#define DATA_GET_BLOCK   BasicBlock *bb; CHECK_TYPE(rbb, cLLVMBasicBlock); Data_Get_Struct(rbb, BasicBlock, bb);

VALUE
llvm_builder_set_insert_point(VALUE self, VALUE rbb) {
  DATA_GET_BUILDER
  DATA_GET_BLOCK
  builder->SetInsertPoint(bb);
  return self;
}

VALUE
llvm_builder_bin_op(VALUE self, VALUE rbin_op, VALUE rv1, VALUE rv2) {
  //CHECK_TYPE(rv1, cLLVMValue);
  //CHECK_TYPE(rv2, cLLVMValue);
  DATA_GET_BUILDER

  Instruction::BinaryOps bin_op = (Instruction::BinaryOps)FIX2INT(rbin_op);

  Value *v1, *v2;
  Data_Get_Struct(rv1, Value, v1);
  Data_Get_Struct(rv2, Value, v2);
  Value *res = builder->CreateBinOp(bin_op, v1, v2);
  return llvm_value_wrap(res);
}

VALUE
llvm_builder_phi(VALUE self, VALUE type) {
  CHECK_TYPE(type, cLLVMType);
  DATA_GET_BUILDER
  PHINode *v = builder->CreatePHI(LLVM_TYPE(type));
  return Data_Wrap_Struct(cLLVMPhi, NULL, NULL, v);
}

VALUE
llvm_phi_add_incoming(VALUE self, VALUE val, VALUE rbb) {
  CHECK_TYPE(val, cLLVMValue);
  DATA_GET_BLOCK
  PHINode *phi = LLVM_PHI(self);
  phi->addIncoming(LLVM_VAL(val), bb);
  return self;
}

VALUE
llvm_builder_return(VALUE self, VALUE rv) {
  //CHECK_TYPE(rv, cLLVMValue);
  DATA_GET_BUILDER
  return llvm_value_wrap(builder->CreateRet(LLVM_VAL(rv)));
}

VALUE
llvm_builder_br(VALUE self, VALUE rblock) {
  DATA_GET_BUILDER

  BasicBlock *bb;
  Data_Get_Struct(rblock, BasicBlock, bb);
  return llvm_value_wrap(builder->CreateBr(bb));
}

VALUE
llvm_builder_cond_br(VALUE self, VALUE rcond, VALUE rtrue_block, VALUE rfalse_block) {
  DATA_GET_BUILDER

  Value *cond;
  Data_Get_Struct(rcond, Value, cond);

  BasicBlock *true_block, *false_block;
  Data_Get_Struct(rtrue_block, BasicBlock, true_block);
  Data_Get_Struct(rfalse_block, BasicBlock, false_block);

  return llvm_value_wrap(builder->CreateCondBr(cond, true_block, false_block));
}

VALUE
llvm_builder_alloca(VALUE self, VALUE rtype, VALUE rsize) {
  DATA_GET_BUILDER

  const Type* type;
  Data_Get_Struct(rtype, Type, type);

  Value *size = ConstantInt::get(Type::Int32Ty, FIX2INT(rsize));
  Value *v = builder->CreateAlloca(type, size);
  return llvm_value_wrap(v);
}

VALUE
llvm_builder_load(VALUE self, VALUE rptr) {
  DATA_GET_BUILDER

  Value *ptr;
  Data_Get_Struct(rptr, Value, ptr);
  return llvm_value_wrap(builder->CreateLoad(ptr));
}

VALUE
llvm_builder_store(VALUE self, VALUE rv, VALUE rptr) {
  DATA_GET_BUILDER

  Value *v, *ptr;
  Data_Get_Struct(rv, Value, v);
  Data_Get_Struct(rptr, Value, ptr);
  return llvm_value_wrap(builder->CreateStore(v, ptr));
}

VALUE
llvm_builder_icmp(VALUE self, VALUE pred, VALUE lhs, VALUE rhs) {
  DATA_GET_BUILDER

  CmpInst::Predicate p = (CmpInst::Predicate)FIX2INT(pred);
  Value *v = builder->CreateICmp(p, LLVM_VAL(lhs), LLVM_VAL(rhs));
  return llvm_value_wrap(v);
}

VALUE
llvm_builder_gep(VALUE self, VALUE rptr, VALUE ridx) {
  DATA_GET_BUILDER

  Value *ptr, *idx;
  Data_Get_Struct(rptr, Value, ptr);
  Data_Get_Struct(ridx, Value, idx);
  return llvm_value_wrap(builder->CreateGEP(ptr, idx));
}

VALUE
llvm_builder_struct_gep(VALUE self, VALUE rptr, VALUE ridx) {
  DATA_GET_BUILDER

  Value *ptr;
  Data_Get_Struct(rptr, Value, ptr);
  return llvm_value_wrap(builder->CreateStructGEP(ptr, FIX2INT(ridx)));
}

VALUE
llvm_builder_int_to_ptr(VALUE self, VALUE ri, VALUE rtype) {
  DATA_GET_BUILDER

  Value *i;
  Data_Get_Struct(ri, Value, i);

  const Type* type;
  Data_Get_Struct(rtype, Type, type);

  return llvm_value_wrap(builder->CreateIntToPtr(i, type));
}

VALUE llvm_builder_int_cast(VALUE self, VALUE i, VALUE type, VALUE sign) {
  DATA_GET_BUILDER
  bool isSigned = (sign != Qnil && sign != Qfalse);
  return llvm_value_wrap(builder->CreateIntCast(LLVM_VAL(i), LLVM_TYPE(type), isSigned));
}

VALUE
llvm_builder_call(int argc, VALUE* argv, VALUE self) {
  DATA_GET_BUILDER

  Function *callee = LLVM_FUNCTION(argv[0]);
  int num_args = argc-1;
  Value** args = (Value**)alloca(num_args*sizeof(Value*));
  for(int i = 0; i < num_args; ++i) {
    args[i] = LLVM_VAL(argv[i+1]);
  }
  return llvm_value_wrap(builder->CreateCall(callee, args, args+num_args));
}

VALUE
llvm_builder_get_global(VALUE self) {
  GlobalVariable *g = new GlobalVariable(Type::Int64Ty, false, GlobalValue::ExternalLinkage, 0, "shakalaka");
  return llvm_value_wrap(g);
}
}
<commit_msg>check bin_op type, fix test failure<commit_after>#include "llvmruby.h"

extern VALUE cLLVMBasicBlock;
extern VALUE cLLVMBuilder;

extern "C" {
VALUE
llvm_basic_block_wrap(BasicBlock* bb) {
  return Data_Wrap_Struct(cLLVMBasicBlock, NULL, NULL, bb);
}

VALUE
llvm_basic_block_builder(VALUE self) {
  BasicBlock* bb;
  Data_Get_Struct(self, BasicBlock, bb);
  IRBuilder<> *builder = new IRBuilder<>(bb);
  return Data_Wrap_Struct(cLLVMBuilder, NULL, NULL, builder);
}

#define DATA_GET_BUILDER IRBuilder<> *builder; Data_Get_Struct(self, IRBuilder<>, builder);
#define DATA_GET_BLOCK   BasicBlock *bb; CHECK_TYPE(rbb, cLLVMBasicBlock); Data_Get_Struct(rbb, BasicBlock, bb);

VALUE
llvm_builder_set_insert_point(VALUE self, VALUE rbb) {
  DATA_GET_BUILDER
  DATA_GET_BLOCK
  builder->SetInsertPoint(bb);
  return self;
}

VALUE
llvm_builder_bin_op(VALUE self, VALUE rbin_op, VALUE rv1, VALUE rv2) {
  Check_Type(rbin_op, T_FIXNUM);
  //CHECK_TYPE(rv1, cLLVMValue);
  //CHECK_TYPE(rv2, cLLVMValue);
  DATA_GET_BUILDER

  Instruction::BinaryOps bin_op = (Instruction::BinaryOps)FIX2INT(rbin_op);

  Value *v1, *v2;
  Data_Get_Struct(rv1, Value, v1);
  Data_Get_Struct(rv2, Value, v2);
  Value *res = builder->CreateBinOp(bin_op, v1, v2);
  return llvm_value_wrap(res);
}

VALUE
llvm_builder_phi(VALUE self, VALUE type) {
  CHECK_TYPE(type, cLLVMType);
  DATA_GET_BUILDER
  PHINode *v = builder->CreatePHI(LLVM_TYPE(type));
  return Data_Wrap_Struct(cLLVMPhi, NULL, NULL, v);
}

VALUE
llvm_phi_add_incoming(VALUE self, VALUE val, VALUE rbb) {
  CHECK_TYPE(val, cLLVMValue);
  DATA_GET_BLOCK
  PHINode *phi = LLVM_PHI(self);
  phi->addIncoming(LLVM_VAL(val), bb);
  return self;
}

VALUE
llvm_builder_return(VALUE self, VALUE rv) {
  //CHECK_TYPE(rv, cLLVMValue);
  DATA_GET_BUILDER
  return llvm_value_wrap(builder->CreateRet(LLVM_VAL(rv)));
}

VALUE
llvm_builder_br(VALUE self, VALUE rblock) {
  DATA_GET_BUILDER

  BasicBlock *bb;
  Data_Get_Struct(rblock, BasicBlock, bb);
  return llvm_value_wrap(builder->CreateBr(bb));
}

VALUE
llvm_builder_cond_br(VALUE self, VALUE rcond, VALUE rtrue_block, VALUE rfalse_block) {
  DATA_GET_BUILDER

  Value *cond;
  Data_Get_Struct(rcond, Value, cond);

  BasicBlock *true_block, *false_block;
  Data_Get_Struct(rtrue_block, BasicBlock, true_block);
  Data_Get_Struct(rfalse_block, BasicBlock, false_block);

  return llvm_value_wrap(builder->CreateCondBr(cond, true_block, false_block));
}

VALUE
llvm_builder_alloca(VALUE self, VALUE rtype, VALUE rsize) {
  DATA_GET_BUILDER

  const Type* type;
  Data_Get_Struct(rtype, Type, type);

  Value *size = ConstantInt::get(Type::Int32Ty, FIX2INT(rsize));
  Value *v = builder->CreateAlloca(type, size);
  return llvm_value_wrap(v);
}

VALUE
llvm_builder_load(VALUE self, VALUE rptr) {
  DATA_GET_BUILDER

  Value *ptr;
  Data_Get_Struct(rptr, Value, ptr);
  return llvm_value_wrap(builder->CreateLoad(ptr));
}

VALUE
llvm_builder_store(VALUE self, VALUE rv, VALUE rptr) {
  DATA_GET_BUILDER

  Value *v, *ptr;
  Data_Get_Struct(rv, Value, v);
  Data_Get_Struct(rptr, Value, ptr);
  return llvm_value_wrap(builder->CreateStore(v, ptr));
}

VALUE
llvm_builder_icmp(VALUE self, VALUE pred, VALUE lhs, VALUE rhs) {
  DATA_GET_BUILDER

  CmpInst::Predicate p = (CmpInst::Predicate)FIX2INT(pred);
  Value *v = builder->CreateICmp(p, LLVM_VAL(lhs), LLVM_VAL(rhs));
  return llvm_value_wrap(v);
}

VALUE
llvm_builder_gep(VALUE self, VALUE rptr, VALUE ridx) {
  DATA_GET_BUILDER

  Value *ptr, *idx;
  Data_Get_Struct(rptr, Value, ptr);
  Data_Get_Struct(ridx, Value, idx);
  return llvm_value_wrap(builder->CreateGEP(ptr, idx));
}

VALUE
llvm_builder_struct_gep(VALUE self, VALUE rptr, VALUE ridx) {
  DATA_GET_BUILDER

  Value *ptr;
  Data_Get_Struct(rptr, Value, ptr);
  return llvm_value_wrap(builder->CreateStructGEP(ptr, FIX2INT(ridx)));
}

VALUE
llvm_builder_int_to_ptr(VALUE self, VALUE ri, VALUE rtype) {
  DATA_GET_BUILDER

  Value *i;
  Data_Get_Struct(ri, Value, i);

  const Type* type;
  Data_Get_Struct(rtype, Type, type);

  return llvm_value_wrap(builder->CreateIntToPtr(i, type));
}

VALUE llvm_builder_int_cast(VALUE self, VALUE i, VALUE type, VALUE sign) {
  DATA_GET_BUILDER
  bool isSigned = (sign != Qnil && sign != Qfalse);
  return llvm_value_wrap(builder->CreateIntCast(LLVM_VAL(i), LLVM_TYPE(type), isSigned));
}

VALUE
llvm_builder_call(int argc, VALUE* argv, VALUE self) {
  DATA_GET_BUILDER

  Function *callee = LLVM_FUNCTION(argv[0]);
  int num_args = argc-1;
  Value** args = (Value**)alloca(num_args*sizeof(Value*));
  for(int i = 0; i < num_args; ++i) {
    args[i] = LLVM_VAL(argv[i+1]);
  }
  return llvm_value_wrap(builder->CreateCall(callee, args, args+num_args));
}

VALUE
llvm_builder_get_global(VALUE self) {
  GlobalVariable *g = new GlobalVariable(Type::Int64Ty, false, GlobalValue::ExternalLinkage, 0, "shakalaka");
  return llvm_value_wrap(g);
}
}
<|endoftext|>


<commit_before>/* -*- MODE: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017 Couchbase, Inc
 *
 *   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.
 */

#include "config.h"

#include "custom_rotating_file_sink.h"

#include "dedupe_sink.h"
#include "logger.h"

#include <memcached/engine.h>
#include <memcached/extension.h>
#include <phosphor/phosphor.h>
#include <platform/processclock.h>
#include <spdlog/sinks/dist_sink.h>
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/spdlog.h>
#include <chrono>
#include <cstdio>
#include <mutex>

#ifndef WIN32
#include <spdlog/sinks/ansicolor_sink.h>
#endif

static const std::string logger_name{"spdlog_file_logger"};
static const size_t default_buffer_size{2048};
static const std::chrono::milliseconds default_sleep_time{500};
static std::mutex create_lock;

static EXTENSION_LOGGER_DESCRIPTOR descriptor;

/**
 * Custom log pattern which the loggers will use.
 * This pattern is duplicated for some test cases. If you need to update it,
 * please also update in all relevant places.
 * TODO: Remove the duplication in the future, by (maybe) moving
 *       the const to a header file.
 */
static const std::string log_pattern{"%Y-%m-%dT%T.%fZ %l %v"};

spdlog::level::level_enum cb::logger::convertToSpdSeverity(
        EXTENSION_LOG_LEVEL sev) {
    using namespace spdlog::level;
    switch (sev) {
    case EXTENSION_LOG_DEBUG:
        return level_enum::debug;
    case EXTENSION_LOG_INFO:
        return level_enum::info;
    case EXTENSION_LOG_NOTICE:
        return level_enum::info;
    case EXTENSION_LOG_WARNING:
        return level_enum::warn;
    case EXTENSION_LOG_FATAL:
        return level_enum::critical;
    }
    throw std::invalid_argument("Unknown severity level");
}

/**
 * Instances of spdlog (async) file logger.
 * The files logger requires a rotating file sink which is manually configured
 * from the parsed settings.
 * The loggers act as a handle to the sinks. They do the processing of log
 * messages and send them to the sinks, which do the actual writing (to file,
 * to stream etc.) or further processing.
 */
static std::shared_ptr<spdlog::logger> file_logger;

/** Returns the name of the file logger */
static const char* get_name() {
    return file_logger->name().c_str();
}

/**
 * Retrieves a message, applies formatting and then logs it to stderr and
 * to file, according to the severity.
 */
static void log(EXTENSION_LOG_LEVEL mcd_severity,
                const void* client_cookie,
                const char* fmt,
                ...) {
    const auto severity = cb::logger::convertToSpdSeverity(mcd_severity);

    // Retrieve formatted log message
    char msg[2048];
    int len;
    va_list va;
    va_start(va, fmt);
    len = vsnprintf(msg, 2048, fmt, va);
    va_end(va);

    // Something went wrong during formatting, so return
    if (len < 0) {
        return;
    }
    // len does not include '\0', hence >= and not >
    if (len >= int(sizeof(msg))) {
        // Crop message for logging
        const char cropped[] = " [cut]";
        snprintf(msg + (sizeof(msg) - sizeof(cropped)),
                 sizeof(cropped),
                 "%s",
                 cropped);
    } else {
        msg[len] = '\0';
    }

    file_logger->log(severity, msg);
}

/**
 * (Synchronously) flushes  all the messages in the loggers' queue
 * and dereferences the loggers.
 */
static void logger_shutdown(bool force) {
    if (file_logger) {
        spdlog::drop(file_logger->name());
    }

    file_logger.reset();
    cb::logger::createBlackholeLogger();
}

static void logger_flush() {
    file_logger->flush();
}

/**
 * Initialises the loggers. Called if the logger configuration is
 * specified in a separate settings object.
 */
boost::optional<std::string> cb::logger::initialize(
        const Config& logger_settings, GET_SERVER_API get_server_api) {
    auto* sapi = get_server_api();
    if (sapi == nullptr) {
        return boost::optional<std::string>{"Failed to get server API"};
    }

    auto fname = logger_settings.filename;
    auto buffersz = logger_settings.buffersize;
    auto cyclesz = logger_settings.cyclesize;
    auto sleeptime = logger_settings.sleeptime;

    if (getenv("CB_MINIMIZE_LOGGER_SLEEPTIME") != nullptr) {
        sleeptime = 1;
    }

    if (getenv("CB_MAXIMIZE_LOGGER_CYCLE_SIZE") != nullptr) {
        cyclesz = 1024 * 1024 * 1024; // use up to 1 GB log file size
    }

    if (getenv("CB_MAXIMIZE_LOGGER_BUFFER_SIZE") != nullptr) {
        buffersz = 8 * 1024 * 1024; // use an 8MB log buffer
    }

    std::lock_guard<std::mutex> guard(create_lock);

    try {
        /* Initialise the loggers.
         *
         * The structure is as follows:
         *
         * file_logger = sends log messages to sink
         *   |__dist_sink_mt = Distribute log messages to multiple sinks
         *       | |__dedupe_sink_mt = deduplicates log messages
         *       |     |__custom_rotating_file_sink_mt = adds opening & closing
         *       |                                       hooks to the file
         *       |__ (color)__stderr_sink_mt = Send log messages to consloe
         */

        auto sink = std::make_shared<spdlog::sinks::dist_sink_mt>();

        if (!fname.empty()) {
            auto fsink = std::make_shared<custom_rotating_file_sink_mt>(
                    fname, cyclesz, log_pattern);
            sink->add_sink(
                    std::make_shared<dedupe_sink_mt>(fsink, log_pattern));
        }

        if (logger_settings.console) {
#ifdef WIN32
            auto stderrsink = std::make_shared<spdlog::sinks::stderr_sink_mt>();
#else
            auto stderrsink =
                    std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
#endif
            stderrsink->set_level(spdlog::level::warn);
            sink->add_sink(stderrsink);
        } else {
            sink->add_sink(std::make_shared<spdlog::sinks::null_sink_mt>());
        }

        spdlog::drop(logger_name);
        file_logger =
                spdlog::create_async(logger_name,
                                     sink,
                                     buffersz,
                                     spdlog::async_overflow_policy::block_retry,
                                     nullptr,
                                     std::chrono::seconds(sleeptime));
    } catch (const spdlog::spdlog_ex& ex) {
        std::string msg =
                std::string{"Log initialization failed: "} + ex.what();
        return boost::optional<std::string>{msg};
    }

    file_logger->set_pattern(log_pattern);
    file_logger->set_level(convertToSpdSeverity(sapi->log->get_level()));
    return {};
}

std::shared_ptr<spdlog::logger> cb::logger::get() {
    if (!file_logger) {
        createBlackholeLogger();
    }

    return file_logger;
}

void cb::logger::createBlackholeLogger() {
    std::lock_guard<std::mutex> guard(create_lock);
    // delete if already exists
    spdlog::drop(logger_name);

    file_logger = spdlog::create_async(
            logger_name,
            std::make_shared<spdlog::sinks::null_sink_mt>(),
            default_buffer_size,
            spdlog::async_overflow_policy::block_retry,
            nullptr,
            default_sleep_time);

    file_logger->set_level(spdlog::level::off);
    file_logger->set_pattern(log_pattern);
}

void cb::logger::createConsoleLogger() {
    std::lock_guard<std::mutex> guard(create_lock);
    // delete if already exists
    spdlog::drop(logger_name);

#ifdef WIN32
    auto stderrsink = std::make_shared<spdlog::sinks::stderr_sink_mt>();
#else
    auto stderrsink =
            std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
#endif
    file_logger =
            spdlog::create_async(logger_name,
                                 stderrsink,
                                 default_buffer_size,
                                 spdlog::async_overflow_policy::block_retry,
                                 nullptr,
                                 default_sleep_time);
    file_logger->set_level(spdlog::level::info);
    file_logger->set_pattern(log_pattern);
}

LOGGER_PUBLIC_API
EXTENSION_LOGGER_DESCRIPTOR& cb::logger::getLoggerDescriptor() {
    descriptor.get_name = get_name;
    descriptor.log = log;
    descriptor.shutdown = logger_shutdown;
    descriptor.flush = logger_flush;
    return descriptor;
}
<commit_msg>Don't use a buffered logger for console logger<commit_after>/* -*- MODE: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017 Couchbase, Inc
 *
 *   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.
 */

#include "config.h"

#include "custom_rotating_file_sink.h"

#include "dedupe_sink.h"
#include "logger.h"

#include <memcached/engine.h>
#include <memcached/extension.h>
#include <phosphor/phosphor.h>
#include <platform/processclock.h>
#include <spdlog/sinks/dist_sink.h>
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/spdlog.h>
#include <chrono>
#include <cstdio>
#include <mutex>

#ifndef WIN32
#include <spdlog/sinks/ansicolor_sink.h>
#endif

static const std::string logger_name{"spdlog_file_logger"};
static const size_t default_buffer_size{2048};
static const std::chrono::milliseconds default_sleep_time{500};
static std::mutex create_lock;

static EXTENSION_LOGGER_DESCRIPTOR descriptor;

/**
 * Custom log pattern which the loggers will use.
 * This pattern is duplicated for some test cases. If you need to update it,
 * please also update in all relevant places.
 * TODO: Remove the duplication in the future, by (maybe) moving
 *       the const to a header file.
 */
static const std::string log_pattern{"%Y-%m-%dT%T.%fZ %l %v"};

spdlog::level::level_enum cb::logger::convertToSpdSeverity(
        EXTENSION_LOG_LEVEL sev) {
    using namespace spdlog::level;
    switch (sev) {
    case EXTENSION_LOG_DEBUG:
        return level_enum::debug;
    case EXTENSION_LOG_INFO:
        return level_enum::info;
    case EXTENSION_LOG_NOTICE:
        return level_enum::info;
    case EXTENSION_LOG_WARNING:
        return level_enum::warn;
    case EXTENSION_LOG_FATAL:
        return level_enum::critical;
    }
    throw std::invalid_argument("Unknown severity level");
}

/**
 * Instances of spdlog (async) file logger.
 * The files logger requires a rotating file sink which is manually configured
 * from the parsed settings.
 * The loggers act as a handle to the sinks. They do the processing of log
 * messages and send them to the sinks, which do the actual writing (to file,
 * to stream etc.) or further processing.
 */
static std::shared_ptr<spdlog::logger> file_logger;

/** Returns the name of the file logger */
static const char* get_name() {
    return file_logger->name().c_str();
}

/**
 * Retrieves a message, applies formatting and then logs it to stderr and
 * to file, according to the severity.
 */
static void log(EXTENSION_LOG_LEVEL mcd_severity,
                const void* client_cookie,
                const char* fmt,
                ...) {
    const auto severity = cb::logger::convertToSpdSeverity(mcd_severity);

    // Retrieve formatted log message
    char msg[2048];
    int len;
    va_list va;
    va_start(va, fmt);
    len = vsnprintf(msg, 2048, fmt, va);
    va_end(va);

    // Something went wrong during formatting, so return
    if (len < 0) {
        return;
    }
    // len does not include '\0', hence >= and not >
    if (len >= int(sizeof(msg))) {
        // Crop message for logging
        const char cropped[] = " [cut]";
        snprintf(msg + (sizeof(msg) - sizeof(cropped)),
                 sizeof(cropped),
                 "%s",
                 cropped);
    } else {
        msg[len] = '\0';
    }

    file_logger->log(severity, msg);
}

/**
 * (Synchronously) flushes  all the messages in the loggers' queue
 * and dereferences the loggers.
 */
static void logger_shutdown(bool force) {
    if (file_logger) {
        spdlog::drop(file_logger->name());
    }

    file_logger.reset();
    cb::logger::createBlackholeLogger();
}

static void logger_flush() {
    file_logger->flush();
}

/**
 * Initialises the loggers. Called if the logger configuration is
 * specified in a separate settings object.
 */
boost::optional<std::string> cb::logger::initialize(
        const Config& logger_settings, GET_SERVER_API get_server_api) {
    auto* sapi = get_server_api();
    if (sapi == nullptr) {
        return boost::optional<std::string>{"Failed to get server API"};
    }

    auto fname = logger_settings.filename;
    auto buffersz = logger_settings.buffersize;
    auto cyclesz = logger_settings.cyclesize;
    auto sleeptime = logger_settings.sleeptime;

    if (getenv("CB_MINIMIZE_LOGGER_SLEEPTIME") != nullptr) {
        sleeptime = 1;
    }

    if (getenv("CB_MAXIMIZE_LOGGER_CYCLE_SIZE") != nullptr) {
        cyclesz = 1024 * 1024 * 1024; // use up to 1 GB log file size
    }

    if (getenv("CB_MAXIMIZE_LOGGER_BUFFER_SIZE") != nullptr) {
        buffersz = 8 * 1024 * 1024; // use an 8MB log buffer
    }

    std::lock_guard<std::mutex> guard(create_lock);

    try {
        /* Initialise the loggers.
         *
         * The structure is as follows:
         *
         * file_logger = sends log messages to sink
         *   |__dist_sink_mt = Distribute log messages to multiple sinks
         *       | |__dedupe_sink_mt = deduplicates log messages
         *       |     |__custom_rotating_file_sink_mt = adds opening & closing
         *       |                                       hooks to the file
         *       |__ (color)__stderr_sink_mt = Send log messages to consloe
         */

        auto sink = std::make_shared<spdlog::sinks::dist_sink_mt>();

        if (!fname.empty()) {
            auto fsink = std::make_shared<custom_rotating_file_sink_mt>(
                    fname, cyclesz, log_pattern);
            sink->add_sink(
                    std::make_shared<dedupe_sink_mt>(fsink, log_pattern));
        }

        if (logger_settings.console) {
#ifdef WIN32
            auto stderrsink = std::make_shared<spdlog::sinks::stderr_sink_mt>();
#else
            auto stderrsink =
                    std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
#endif
            stderrsink->set_level(spdlog::level::warn);
            sink->add_sink(stderrsink);
        } else {
            sink->add_sink(std::make_shared<spdlog::sinks::null_sink_mt>());
        }

        spdlog::drop(logger_name);
        file_logger =
                spdlog::create_async(logger_name,
                                     sink,
                                     buffersz,
                                     spdlog::async_overflow_policy::block_retry,
                                     nullptr,
                                     std::chrono::seconds(sleeptime));
    } catch (const spdlog::spdlog_ex& ex) {
        std::string msg =
                std::string{"Log initialization failed: "} + ex.what();
        return boost::optional<std::string>{msg};
    }

    file_logger->set_pattern(log_pattern);
    file_logger->set_level(convertToSpdSeverity(sapi->log->get_level()));
    return {};
}

std::shared_ptr<spdlog::logger> cb::logger::get() {
    if (!file_logger) {
        createBlackholeLogger();
    }

    return file_logger;
}

void cb::logger::createBlackholeLogger() {
    std::lock_guard<std::mutex> guard(create_lock);
    // delete if already exists
    spdlog::drop(logger_name);

    file_logger = spdlog::create_async(
            logger_name,
            std::make_shared<spdlog::sinks::null_sink_mt>(),
            default_buffer_size,
            spdlog::async_overflow_policy::block_retry,
            nullptr,
            default_sleep_time);

    file_logger->set_level(spdlog::level::off);
    file_logger->set_pattern(log_pattern);
}

void cb::logger::createConsoleLogger() {
    std::lock_guard<std::mutex> guard(create_lock);
    // delete if already exists
    spdlog::drop(logger_name);
    file_logger = spdlog::stderr_color_mt(logger_name);
    file_logger->set_level(spdlog::level::info);
    file_logger->set_pattern(log_pattern);
}

LOGGER_PUBLIC_API
EXTENSION_LOGGER_DESCRIPTOR& cb::logger::getLoggerDescriptor() {
    descriptor.get_name = get_name;
    descriptor.log = log;
    descriptor.shutdown = logger_shutdown;
    descriptor.flush = logger_flush;
    return descriptor;
}
<|endoftext|>


<commit_before><commit_msg>Adds the SW2 switch to the tiva launchpad HwInit. This needs special handling, because it is muxed to the NMI pin and a special unlock sequence is needed for us to reconfigure it as an input.<commit_after><|endoftext|>


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

/**
 * @file vdev_posix.cpp
 *
 * POSIX-like API for virtual character device
 */

#include <px4_log.h>
#include <px4_posix.h>
#include <px4_time.h>
#include "device.h"
#include "vfile.h"

#include <hrt_work.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

using namespace device;

pthread_mutex_t filemutex = PTHREAD_MUTEX_INITIALIZER;

extern "C" {

#define PX4_MAX_FD 200
	static device::file_t *filemap[PX4_MAX_FD] = {};

	int px4_errno;

	inline bool valid_fd(int fd)
	{
		pthread_mutex_lock(&filemutex);
		bool ret = (fd < PX4_MAX_FD && fd >= 0 && filemap[fd] != NULL);
		pthread_mutex_unlock(&filemutex);
		return ret;
	}

	inline VDev *get_vdev(int fd)
	{
		pthread_mutex_lock(&filemutex);
		bool valid = (fd < PX4_MAX_FD && fd >= 0 && filemap[fd] != NULL);
		VDev *dev;

		if (valid) {
			dev = (VDev *)(filemap[fd]->vdev);

		} else {
			dev = nullptr;
		}

		pthread_mutex_unlock(&filemutex);
		return dev;
	}

	int px4_open(const char *path, int flags, ...)
	{
		PX4_DEBUG("px4_open");
		VDev *dev = VDev::getDev(path);
		int ret = 0;
		int i;
		mode_t mode;

		if (!dev && (flags & (PX4_F_WRONLY | PX4_F_CREAT)) != 0 &&
		    strncmp(path, "/obj/", 5) != 0 &&
		    strncmp(path, "/dev/", 5) != 0) {
			va_list p;
			va_start(p, flags);
			mode = va_arg(p, mode_t);
			va_end(p);

			// Create the file
			PX4_DEBUG("Creating virtual file %s", path);
			dev = VFile::createFile(path, mode);
		}

		if (dev) {

			pthread_mutex_lock(&filemutex);

			for (i = 0; i < PX4_MAX_FD; ++i) {
				if (filemap[i] == 0) {
					filemap[i] = new device::file_t(flags, dev, i);
					break;
				}
			}

			pthread_mutex_unlock(&filemutex);

			if (i < PX4_MAX_FD) {
				ret = dev->open(filemap[i]);

			} else {
				PX4_WARN("exceeded maximum number of file descriptors!");
				ret = -ENOENT;
			}

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			return -1;
		}

		PX4_DEBUG("px4_open fd = %d", filemap[i]->fd);
		return filemap[i]->fd;
	}

	int px4_close(int fd)
	{
		int ret;

		VDev *dev = get_vdev(fd);

		if (dev) {
			pthread_mutex_lock(&filemutex);
			ret = dev->close(filemap[fd]);
			filemap[fd] = nullptr;
			pthread_mutex_unlock(&filemutex);
			PX4_DEBUG("px4_close fd = %d", fd);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	ssize_t px4_read(int fd, void *buffer, size_t buflen)
	{
		int ret;

		VDev *dev = get_vdev(fd);

		if (dev) {
			PX4_DEBUG("px4_read fd = %d", fd);
			ret = dev->read(filemap[fd], (char *)buffer, buflen);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	ssize_t px4_write(int fd, const void *buffer, size_t buflen)
	{
		int ret;

		VDev *dev = get_vdev(fd);

		if (dev) {
			PX4_DEBUG("px4_write fd = %d", fd);
			ret = dev->write(filemap[fd], (const char *)buffer, buflen);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	int px4_ioctl(int fd, int cmd, unsigned long arg)
	{
		PX4_DEBUG("px4_ioctl fd = %d", fd);
		int ret = 0;

		VDev *dev = get_vdev(fd);

		if (dev) {
			ret = dev->ioctl(filemap[fd], cmd, arg);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
		}

		return ret;
	}

	int px4_poll(px4_pollfd_struct_t *fds, nfds_t nfds, int timeout)
	{
		if (nfds == 0) {
			PX4_WARN("px4_poll with no fds");
			return -1;
		}

		px4_sem_t sem;
		int count = 0;
		int ret = -1;
		unsigned int i;

		const unsigned NAMELEN = 32;
		char thread_name[NAMELEN] = {};
		int nret = pthread_getname_np(pthread_self(), thread_name, NAMELEN);

		if (nret || thread_name[0] == 0) {
			PX4_WARN("failed getting thread name");
		}

		PX4_DEBUG("Called px4_poll timeout = %d", timeout);
		px4_sem_init(&sem, 0, 0);

		// Go through all fds and check them for a pollable state
		bool fd_pollable = false;
		for (i = 0; i < nfds; ++i) {
			fds[i].sem     = &sem;
			fds[i].revents = 0;
			fds[i].priv    = NULL;

			VDev *dev = get_vdev(fds[i].fd);

			// If fd is valid
			if (dev) {
				PX4_DEBUG("%s: px4_poll: VDev->poll(setup) %d", thread_name, fds[i].fd);
				ret = dev->poll(filemap[fds[i].fd], &fds[i], true);

				if (ret < 0) {
					PX4_WARN("%s: px4_poll() error: %s",
						thread_name, strerror(errno));
					break;
				}

				if (ret >= 0) {
					fd_pollable = true;
				}
			}
		}

		// If any FD can be polled, lock the semaphore and
		// check for new data
		if (fd_pollable) {
			if (timeout > 0) {

				// Get the current time
				struct timespec ts;
				px4_clock_gettime(CLOCK_REALTIME, &ts);

				// Calculate an absolute time in the future
				const unsigned billion = (1000 * 1000 * 1000);
				unsigned tdiff = timeout;
				uint64_t nsecs = ts.tv_nsec + (tdiff * 1000 * 1000);
				ts.tv_sec += nsecs / billion;
				nsecs -= (nsecs / billion) * billion;
				ts.tv_nsec = nsecs;

				// Execute a blocking wait for that time in the future
				errno = 0;
				ret = px4_sem_timedwait(&sem, &ts);

				// Ensure ret is negative on failure
				if (ret > 0) {
					ret = -ret;
				}

				if (ret && ret != -ETIMEDOUT) {
					PX4_WARN("%s: px4_poll() sem error", thread_name);
				}

			} else if (timeout < 0) {
				px4_sem_wait(&sem);
			}

			// We have waited now (or not, depending on timeout),
			// go through all fds and count how many have data
			for (i = 0; i < nfds; ++i) {

				VDev *dev = get_vdev(fds[i].fd);

				// If fd is valid
				if (dev) {
					PX4_DEBUG("%s: px4_poll: VDev->poll(teardown) %d", thread_name, fds[i].fd);
					ret = dev->poll(filemap[fds[i].fd], &fds[i], false);

					if (ret < 0) {
						PX4_WARN("%s: px4_poll() 2nd poll fail", thread_name);
						break;
					}

					if (fds[i].revents) {
						count += 1;
					}
				}
			}
		}

		px4_sem_destroy(&sem);

		// Return the positive count if present,
		// return the negative error number if failed
		return (count) ? count : ret;
	}

	int px4_fsync(int fd)
	{
		return 0;
	}

	int px4_access(const char *pathname, int mode)
	{
		if (mode != F_OK) {
			errno = EINVAL;
			return -1;
		}

		VDev *dev = VDev::getDev(pathname);
		return (dev != nullptr) ? 0 : -1;
	}

	void px4_show_devices()
	{
		VDev::showDevices();
	}

	void px4_show_topics()
	{
		VDev::showTopics();
	}

	void px4_show_files()
	{
		VDev::showFiles();
	}

	const char *px4_get_device_names(unsigned int *handle)
	{
		return VDev::devList(handle);
	}

	const char *px4_get_topic_names(unsigned int *handle)
	{
		return VDev::topicList(handle);
	}

}

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

/**
 * @file vdev_posix.cpp
 *
 * POSIX-like API for virtual character device
 */

#include <px4_log.h>
#include <px4_posix.h>
#include <px4_time.h>
#include "device.h"
#include "vfile.h"

#include <hrt_work.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

using namespace device;

pthread_mutex_t filemutex = PTHREAD_MUTEX_INITIALIZER;

extern "C" {

#define PX4_MAX_FD 200
	static device::file_t *filemap[PX4_MAX_FD] = {};

	int px4_errno;

	inline bool valid_fd(int fd)
	{
		pthread_mutex_lock(&filemutex);
		bool ret = (fd < PX4_MAX_FD && fd >= 0 && filemap[fd] != NULL);
		pthread_mutex_unlock(&filemutex);
		return ret;
	}

	inline VDev *get_vdev(int fd)
	{
		pthread_mutex_lock(&filemutex);
		bool valid = (fd < PX4_MAX_FD && fd >= 0 && filemap[fd] != NULL);
		VDev *dev;

		if (valid) {
			dev = (VDev *)(filemap[fd]->vdev);

		} else {
			dev = nullptr;
		}

		pthread_mutex_unlock(&filemutex);
		return dev;
	}

	int px4_open(const char *path, int flags, ...)
	{
		PX4_DEBUG("px4_open");
		VDev *dev = VDev::getDev(path);
		int ret = 0;
		int i;
		mode_t mode;

		if (!dev && (flags & (PX4_F_WRONLY | PX4_F_CREAT)) != 0 &&
		    strncmp(path, "/obj/", 5) != 0 &&
		    strncmp(path, "/dev/", 5) != 0) {
			va_list p;
			va_start(p, flags);
			mode = va_arg(p, mode_t);
			va_end(p);

			// Create the file
			PX4_DEBUG("Creating virtual file %s", path);
			dev = VFile::createFile(path, mode);
		}

		if (dev) {

			pthread_mutex_lock(&filemutex);

			for (i = 0; i < PX4_MAX_FD; ++i) {
				if (filemap[i] == 0) {
					filemap[i] = new device::file_t(flags, dev, i);
					break;
				}
			}

			pthread_mutex_unlock(&filemutex);

			if (i < PX4_MAX_FD) {
				ret = dev->open(filemap[i]);

			} else {
				PX4_WARN("exceeded maximum number of file descriptors!");
				ret = -ENOENT;
			}

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			return -1;
		}

		PX4_DEBUG("px4_open fd = %d", filemap[i]->fd);
		return filemap[i]->fd;
	}

	int px4_close(int fd)
	{
		int ret;

		VDev *dev = get_vdev(fd);

		if (dev) {
			pthread_mutex_lock(&filemutex);
			ret = dev->close(filemap[fd]);
			filemap[fd] = nullptr;
			pthread_mutex_unlock(&filemutex);
			PX4_DEBUG("px4_close fd = %d", fd);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	ssize_t px4_read(int fd, void *buffer, size_t buflen)
	{
		int ret;

		VDev *dev = get_vdev(fd);

		if (dev) {
			PX4_DEBUG("px4_read fd = %d", fd);
			ret = dev->read(filemap[fd], (char *)buffer, buflen);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	ssize_t px4_write(int fd, const void *buffer, size_t buflen)
	{
		int ret;

		VDev *dev = get_vdev(fd);

		if (dev) {
			PX4_DEBUG("px4_write fd = %d", fd);
			ret = dev->write(filemap[fd], (const char *)buffer, buflen);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	int px4_ioctl(int fd, int cmd, unsigned long arg)
	{
		PX4_DEBUG("px4_ioctl fd = %d", fd);
		int ret = 0;

		VDev *dev = get_vdev(fd);

		if (dev) {
			ret = dev->ioctl(filemap[fd], cmd, arg);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
		}

		return ret;
	}

	int px4_poll(px4_pollfd_struct_t *fds, nfds_t nfds, int timeout)
	{
		if (nfds == 0) {
			PX4_WARN("px4_poll with no fds");
			return -1;
		}

		px4_sem_t sem;
		int count = 0;
		int ret = -1;
		unsigned int i;

		const unsigned NAMELEN = 32;
		char thread_name[NAMELEN] = {};
		int nret = pthread_getname_np(pthread_self(), thread_name, NAMELEN);

		if (nret || thread_name[0] == 0) {
			PX4_WARN("failed getting thread name");
		}

		PX4_DEBUG("Called px4_poll timeout = %d", timeout);
		px4_sem_init(&sem, 0, 0);

		// Go through all fds and check them for a pollable state
		bool fd_pollable = false;

		for (i = 0; i < nfds; ++i) {
			fds[i].sem     = &sem;
			fds[i].revents = 0;
			fds[i].priv    = NULL;

			VDev *dev = get_vdev(fds[i].fd);

			// If fd is valid
			if (dev) {
				PX4_DEBUG("%s: px4_poll: VDev->poll(setup) %d", thread_name, fds[i].fd);
				ret = dev->poll(filemap[fds[i].fd], &fds[i], true);

				if (ret < 0) {
					PX4_WARN("%s: px4_poll() error: %s",
						 thread_name, strerror(errno));
					break;
				}

				if (ret >= 0) {
					fd_pollable = true;
				}
			}
		}

		// If any FD can be polled, lock the semaphore and
		// check for new data
		if (fd_pollable) {
			if (timeout > 0) {

				// Get the current time
				struct timespec ts;
				px4_clock_gettime(CLOCK_REALTIME, &ts);

				// Calculate an absolute time in the future
				const unsigned billion = (1000 * 1000 * 1000);
				unsigned tdiff = timeout;
				uint64_t nsecs = ts.tv_nsec + (tdiff * 1000 * 1000);
				ts.tv_sec += nsecs / billion;
				nsecs -= (nsecs / billion) * billion;
				ts.tv_nsec = nsecs;

				// Execute a blocking wait for that time in the future
				errno = 0;
				ret = px4_sem_timedwait(&sem, &ts);

				// Ensure ret is negative on failure
				if (ret > 0) {
					ret = -ret;
				}

				if (ret && ret != -ETIMEDOUT) {
					PX4_WARN("%s: px4_poll() sem error", thread_name);
				}

			} else if (timeout < 0) {
				px4_sem_wait(&sem);
			}

			// We have waited now (or not, depending on timeout),
			// go through all fds and count how many have data
			for (i = 0; i < nfds; ++i) {

				VDev *dev = get_vdev(fds[i].fd);

				// If fd is valid
				if (dev) {
					PX4_DEBUG("%s: px4_poll: VDev->poll(teardown) %d", thread_name, fds[i].fd);
					ret = dev->poll(filemap[fds[i].fd], &fds[i], false);

					if (ret < 0) {
						PX4_WARN("%s: px4_poll() 2nd poll fail", thread_name);
						break;
					}

					if (fds[i].revents) {
						count += 1;
					}
				}
			}
		}

		px4_sem_destroy(&sem);

		// Return the positive count if present,
		// return the negative error number if failed
		return (count) ? count : ret;
	}

	int px4_fsync(int fd)
	{
		return 0;
	}

	int px4_access(const char *pathname, int mode)
	{
		if (mode != F_OK) {
			errno = EINVAL;
			return -1;
		}

		VDev *dev = VDev::getDev(pathname);
		return (dev != nullptr) ? 0 : -1;
	}

	void px4_show_devices()
	{
		VDev::showDevices();
	}

	void px4_show_topics()
	{
		VDev::showTopics();
	}

	void px4_show_files()
	{
		VDev::showFiles();
	}

	const char *px4_get_device_names(unsigned int *handle)
	{
		return VDev::devList(handle);
	}

	const char *px4_get_topic_names(unsigned int *handle)
	{
		return VDev::topicList(handle);
	}

}

<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// This file contains the WebRTC suppressions for ThreadSanitizer.
// Please refer to
// http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
// for more info.

#if defined(THREAD_SANITIZER)

// Please make sure the code below declares a single string variable
// kTSanDefaultSuppressions contains TSan suppressions delimited by newlines.
// See http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
// for the instructions on writing suppressions.
char kTSanDefaultSuppressions[] =

// WebRTC specific suppressions.

// Split up suppressions covered previously by thread.cc and messagequeue.cc.
"race:rtc::MessageQueue::Quit\n"
"race:vp8cx_remove_encoder_threads\n"
"race:third_party/libvpx/source/libvpx/vp9/common/vp9_scan.h\n"

// Usage of trace callback and trace level is racy in rtc_media_unittests.
// https://code.google.com/p/webrtc/issues/detail?id=3372
"race:webrtc::TraceImpl::WriteToFile\n"
"race:webrtc::VideoEngine::SetTraceFilter\n"
"race:webrtc::VoiceEngine::SetTraceFilter\n"
"race:webrtc::Trace::set_level_filter\n"
"race:webrtc::GetStaticInstance<webrtc::TraceImpl>\n"

// Audio processing
// https://code.google.com/p/webrtc/issues/detail?id=2521 for details.
"race:webrtc/modules/audio_processing/aec/aec_core.c\n"
"race:webrtc/modules/audio_processing/aec/aec_rdft.c\n"

// Race in pulse initialization.
// https://code.google.com/p/webrtc/issues/detail?id=5152
"race:webrtc::AudioDeviceLinuxPulse::Init\n"

// rtc_unittests
// https://code.google.com/p/webrtc/issues/detail?id=3911 for details.
"race:rtc::AsyncInvoker::OnMessage\n"
"race:rtc::FireAndForgetAsyncClosure<FunctorB>::Execute\n"
"race:rtc::MessageQueueManager::Clear\n"
"race:rtc::Thread::Clear\n"
// https://code.google.com/p/webrtc/issues/detail?id=3914
"race:rtc::AsyncInvoker::~AsyncInvoker\n"
// https://code.google.com/p/webrtc/issues/detail?id=2080
"race:webrtc/base/logging.cc\n"
"race:webrtc/base/sharedexclusivelock_unittest.cc\n"
"race:webrtc/base/signalthread_unittest.cc\n"
// https://code.google.com/p/webrtc/issues/detail?id=4456
"deadlock:rtc::MessageQueueManager::Clear\n"
"deadlock:rtc::MessageQueueManager::ClearInternal\n"

// rtc_pc_unittests
// https://code.google.com/p/webrtc/issues/detail?id=2079
"race:webrtc/base/testclient.cc\n"
"race:webrtc/base/virtualsocketserver.cc\n"
"race:talk/p2p/base/stunserver_unittest.cc\n"

// third_party/usrsctp
// TODO(jiayl): https://code.google.com/p/webrtc/issues/detail?id=3492
"race:user_sctp_timer_iterate\n"

// https://code.google.com/p/webrtc/issues/detail?id=5151
"race:sctp_close\n"

// Potential deadlocks detected after roll in r6516.
// https://code.google.com/p/webrtc/issues/detail?id=3509
"deadlock:webrtc::RTCPReceiver::SetSsrcs\n"
"deadlock:webrtc::test::UdpSocketManagerPosixImpl::RemoveSocket\n"
"deadlock:webrtc::vcm::VideoReceiver::RegisterPacketRequestCallback\n"

// TODO(pbos): Trace events are racy due to lack of proper POD atomics.
// https://code.google.com/p/webrtc/issues/detail?id=2497
"race:*trace_event_unique_catstatic*\n"

// https://code.google.com/p/webrtc/issues/detail?id=4719
"race:webrtc::voe::TransmitMixer::PrepareDemux\n"
"race:webrtc::voe::TransmitMixer::EnableStereoChannelSwapping\n"

// Race between InitCpuFlags and TestCpuFlag in libyuv.
// https://code.google.com/p/libyuv/issues/detail?id=508
"race:InitCpuFlags\n"

// End of suppressions.
;  // Please keep this semicolon.

#endif  // THREAD_SANITIZER
<commit_msg>Remove some TSan deadlock suppressions.<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// This file contains the WebRTC suppressions for ThreadSanitizer.
// Please refer to
// http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
// for more info.

#if defined(THREAD_SANITIZER)

// Please make sure the code below declares a single string variable
// kTSanDefaultSuppressions contains TSan suppressions delimited by newlines.
// See http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
// for the instructions on writing suppressions.
char kTSanDefaultSuppressions[] =

// WebRTC specific suppressions.

// Split up suppressions covered previously by thread.cc and messagequeue.cc.
"race:rtc::MessageQueue::Quit\n"
"race:vp8cx_remove_encoder_threads\n"
"race:third_party/libvpx/source/libvpx/vp9/common/vp9_scan.h\n"

// Usage of trace callback and trace level is racy in rtc_media_unittests.
// https://code.google.com/p/webrtc/issues/detail?id=3372
"race:webrtc::TraceImpl::WriteToFile\n"
"race:webrtc::VideoEngine::SetTraceFilter\n"
"race:webrtc::VoiceEngine::SetTraceFilter\n"
"race:webrtc::Trace::set_level_filter\n"
"race:webrtc::GetStaticInstance<webrtc::TraceImpl>\n"

// Audio processing
// https://code.google.com/p/webrtc/issues/detail?id=2521 for details.
"race:webrtc/modules/audio_processing/aec/aec_core.c\n"
"race:webrtc/modules/audio_processing/aec/aec_rdft.c\n"

// Race in pulse initialization.
// https://code.google.com/p/webrtc/issues/detail?id=5152
"race:webrtc::AudioDeviceLinuxPulse::Init\n"

// rtc_unittests
// https://code.google.com/p/webrtc/issues/detail?id=3911 for details.
"race:rtc::AsyncInvoker::OnMessage\n"
"race:rtc::FireAndForgetAsyncClosure<FunctorB>::Execute\n"
"race:rtc::MessageQueueManager::Clear\n"
"race:rtc::Thread::Clear\n"
// https://code.google.com/p/webrtc/issues/detail?id=3914
"race:rtc::AsyncInvoker::~AsyncInvoker\n"
// https://code.google.com/p/webrtc/issues/detail?id=2080
"race:webrtc/base/logging.cc\n"
"race:webrtc/base/sharedexclusivelock_unittest.cc\n"
"race:webrtc/base/signalthread_unittest.cc\n"
// https://code.google.com/p/webrtc/issues/detail?id=4456
"deadlock:rtc::MessageQueueManager::Clear\n"
"deadlock:rtc::MessageQueueManager::ClearInternal\n"

// rtc_pc_unittests
// https://code.google.com/p/webrtc/issues/detail?id=2079
"race:webrtc/base/testclient.cc\n"
"race:webrtc/base/virtualsocketserver.cc\n"
"race:talk/p2p/base/stunserver_unittest.cc\n"

// third_party/usrsctp
// TODO(jiayl): https://code.google.com/p/webrtc/issues/detail?id=3492
"race:user_sctp_timer_iterate\n"

// https://code.google.com/p/webrtc/issues/detail?id=5151
"race:sctp_close\n"

// Potential deadlocks detected after roll in r6516.
// https://code.google.com/p/webrtc/issues/detail?id=3509
"deadlock:webrtc::test::UdpSocketManagerPosixImpl::RemoveSocket\n"

// TODO(pbos): Trace events are racy due to lack of proper POD atomics.
// https://code.google.com/p/webrtc/issues/detail?id=2497
"race:*trace_event_unique_catstatic*\n"

// https://code.google.com/p/webrtc/issues/detail?id=4719
"race:webrtc::voe::TransmitMixer::PrepareDemux\n"
"race:webrtc::voe::TransmitMixer::EnableStereoChannelSwapping\n"

// Race between InitCpuFlags and TestCpuFlag in libyuv.
// https://code.google.com/p/libyuv/issues/detail?id=508
"race:InitCpuFlags\n"

// End of suppressions.
;  // Please keep this semicolon.

#endif  // THREAD_SANITIZER
<|endoftext|>


<commit_before>/***********************************************************************
    filename:   CEGUIFalTextComponent.cpp
    created:    Sun Jun 19 2005
    author:     Paul D Turner <paul@cegui.org.uk>
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#ifdef HAVE_CONFIG_H
#   include "config.h"
#endif

#include "falagard/CEGUIFalTextComponent.h"
#include "falagard/CEGUIFalXMLEnumHelper.h"
#include "CEGUIFontManager.h"
#include "CEGUIExceptions.h"
#include "CEGUIPropertyHelper.h"
#include "CEGUIFont.h"
#include "CEGUILeftAlignedRenderedString.h"
#include "CEGUIRightAlignedRenderedString.h"
#include "CEGUICentredRenderedString.h"
#include "CEGUIJustifiedRenderedString.h"
#include "CEGUIRenderedStringWordWrapper.h"
#include <iostream>

#if defined (CEGUI_USE_FRIBIDI)
    #include "CEGUIFribidiVisualMapping.h"
#elif defined (CEGUI_USE_MINIBIDI)
    #include "CEGUIMinibidiVisualMapping.h"
#else
    #include "CEGUIBiDiVisualMapping.h"
#endif

// Start of CEGUI namespace section
namespace CEGUI
{
    TextComponent::TextComponent() :
#ifndef CEGUI_BIDI_SUPPORT
        d_bidiVisualMapping(0),
#elif defined (CEGUI_USE_FRIBIDI)
        d_bidiVisualMapping(new FribidiVisualMapping),
#elif defined (CEGUI_USE_MINIBIDI)
        d_bidiVisualMapping(new MinibidiVisualMapping),
#else
    #error "BIDI Configuration is inconsistant, check your config!"
#endif
        d_bidiDataValid(false),
        d_formattedRenderedString(new LeftAlignedRenderedString(d_renderedString)),
        d_lastHorzFormatting(HTF_LEFT_ALIGNED),
        d_vertFormatting(VTF_TOP_ALIGNED),
        d_horzFormatting(HTF_LEFT_ALIGNED)
    {}

    TextComponent::~TextComponent()
    {
        delete d_bidiVisualMapping;
    }

    TextComponent::TextComponent(const TextComponent& obj) :
        FalagardComponentBase(obj),
        d_textLogical(obj.d_textLogical),
#ifndef CEGUI_BIDI_SUPPORT
        d_bidiVisualMapping(0),
#elif defined (CEGUI_USE_FRIBIDI)
        d_bidiVisualMapping(new FribidiVisualMapping),
#elif defined (CEGUI_USE_MINIBIDI)
        d_bidiVisualMapping(new MinibidiVisualMapping),
#endif
        d_bidiDataValid(false),
        d_renderedString(obj.d_renderedString),
        d_formattedRenderedString(obj.d_formattedRenderedString),
        d_lastHorzFormatting(obj.d_lastHorzFormatting),
        d_font(obj.d_font),
        d_vertFormatting(obj.d_vertFormatting),
        d_horzFormatting(obj.d_horzFormatting),
        d_textPropertyName(obj.d_textPropertyName),
        d_fontPropertyName(obj.d_fontPropertyName)
    {
    }

    TextComponent& TextComponent::operator=(const TextComponent& other)
    {
        if (this == &other)
            return *this;

        FalagardComponentBase::operator=(other);

        d_textLogical = other.d_textLogical;
        // note we do not assign the BiDiVisualMapping object, we just mark our
        // existing one as invalid so it's data gets regenerated next time it's
        // needed.
        d_bidiDataValid = false;
        d_renderedString = other.d_renderedString;
        d_formattedRenderedString = other.d_formattedRenderedString;
        d_lastHorzFormatting = other.d_lastHorzFormatting;
        d_font = other.d_font;
        d_vertFormatting = other.d_vertFormatting;
        d_horzFormatting = other.d_horzFormatting;
        d_textPropertyName = other.d_textPropertyName;
        d_fontPropertyName = other.d_fontPropertyName;

        return *this;
    }

    const String& TextComponent::getText() const
    {
        return d_textLogical;
    }

    void TextComponent::setText(const String& text)
    {
        d_textLogical = text;
        d_bidiDataValid = false;
    }

    const String& TextComponent::getFont() const
    {
        return d_font;
    }

    void TextComponent::setFont(const String& font)
    {
        d_font = font;
    }

    VerticalTextFormatting TextComponent::getVerticalFormatting() const
    {
        return d_vertFormatting;
    }

    void TextComponent::setVerticalFormatting(VerticalTextFormatting fmt)
    {
        d_vertFormatting = fmt;
    }

    HorizontalTextFormatting TextComponent::getHorizontalFormatting() const
    {
        return d_horzFormatting;
    }

    void TextComponent::setHorizontalFormatting(HorizontalTextFormatting fmt)
    {
        d_horzFormatting = fmt;
    }

    void TextComponent::setupStringFormatter(const Window& window,
                                             const RenderedString& rendered_string) const
    {
        const HorizontalTextFormatting horzFormatting = d_horzFormatPropertyName.empty() ? d_horzFormatting :
            FalagardXMLHelper::stringToHorzTextFormat(window.getProperty(d_horzFormatPropertyName));

        // no formatting change
        if (horzFormatting == d_lastHorzFormatting)
        {
            d_formattedRenderedString->setRenderedString(rendered_string);
            return;
        }

        d_lastHorzFormatting = horzFormatting;

        switch(horzFormatting)
        {
        case HTF_LEFT_ALIGNED:
            d_formattedRenderedString =
                new LeftAlignedRenderedString(rendered_string);
            break;

        case HTF_CENTRE_ALIGNED:
            d_formattedRenderedString =
                new CentredRenderedString(rendered_string);
            break;

        case HTF_RIGHT_ALIGNED:
            d_formattedRenderedString =
                new RightAlignedRenderedString(rendered_string);
            break;

        case HTF_JUSTIFIED:
            d_formattedRenderedString =
                new JustifiedRenderedString(rendered_string);
            break;

        case HTF_WORDWRAP_LEFT_ALIGNED:
            d_formattedRenderedString =
                new RenderedStringWordWrapper
                    <LeftAlignedRenderedString>(rendered_string);
            break;

        case HTF_WORDWRAP_CENTRE_ALIGNED:
            d_formattedRenderedString =
                new RenderedStringWordWrapper
                    <CentredRenderedString>(rendered_string);
            break;

        case HTF_WORDWRAP_RIGHT_ALIGNED:
            d_formattedRenderedString =
                new RenderedStringWordWrapper
                    <RightAlignedRenderedString>(rendered_string);
            break;

        case HTF_WORDWRAP_JUSTIFIED:
            d_formattedRenderedString =
                new RenderedStringWordWrapper
                    <JustifiedRenderedString>(rendered_string);
            break;
        }
    }

    void TextComponent::render_impl(Window& srcWindow, Rect& destRect, const CEGUI::ColourRect* modColours, const Rect* clipper, bool /*clipToDisplay*/) const
    {
        // get font to use
        Font* font;

        CEGUI_TRY
        {
            font = d_fontPropertyName.empty() ?
                (d_font.empty() ? srcWindow.getFont() : &FontManager::getSingleton().get(d_font))
                : &FontManager::getSingleton().get(srcWindow.getProperty(d_fontPropertyName));
        }
        CEGUI_CATCH (UnknownObjectException&)
        {
            font = 0;
        }

        // exit if we have no font to use.
        if (!font)
            return;

        const RenderedString* rs = &d_renderedString;
        // do we fetch text from a property
        if (!d_textPropertyName.empty())
        {
            // fetch text & do bi-directional reordering as needed
            String vis;
            #ifdef CEGUI_BIDI_SUPPORT
                BiDiVisualMapping::StrIndexList l2v, v2l;
                d_bidiVisualMapping->reorderFromLogicalToVisual(
                    srcWindow.getProperty(d_textPropertyName), vis, l2v, v2l);
            #else
                vis = srcWindow.getProperty(d_textPropertyName);
            #endif
            // parse string using parser from Window.
            d_renderedString =
                srcWindow.getRenderedStringParser().parse(vis, font, modColours);
        }
        // do we use a static text string from the looknfeel
        else if (!getTextVisual().empty())
            // parse string using parser from Window.
            d_renderedString = srcWindow.getRenderedStringParser().
                parse(getTextVisual(), font, modColours);
        // do we have to override the font?
        else if (font != srcWindow.getFont())
            d_renderedString = srcWindow.getRenderedStringParser().
                parse(srcWindow.getTextVisual(), font, modColours);
        // use ready-made RenderedString from the Window itself
        else
            rs = &srcWindow.getRenderedString();

        setupStringFormatter(srcWindow, *rs);
        d_formattedRenderedString->format(destRect.getSize());

        // Get total formatted height.
        const float textHeight = d_formattedRenderedString->getVerticalExtent();

        // handle dest area adjustments for vertical formatting.
        VerticalTextFormatting vertFormatting = d_vertFormatPropertyName.empty() ? d_vertFormatting :
            FalagardXMLHelper::stringToVertTextFormat(srcWindow.getProperty(d_vertFormatPropertyName));

        switch(vertFormatting)
        {
        case VTF_CENTRE_ALIGNED:
            destRect.d_top += (destRect.getHeight() - textHeight) * 0.5f;
            break;

        case VTF_BOTTOM_ALIGNED:
            destRect.d_top = destRect.d_bottom - textHeight;
            break;

        default:
            // default is VTF_TOP_ALIGNED, for which we take no action.
            break;
        }

        // calculate final colours to be used
        ColourRect finalColours;
        initColoursRect(srcWindow, modColours, finalColours);

        // offset the font little down so that it's centered within its own spacing
//        destRect.d_top += (font->getLineSpacing() - font->getFontHeight()) * 0.5f;
        // add geometry for text to the target window.
        d_formattedRenderedString->draw(srcWindow.getGeometryBuffer(),
                                        destRect.getPosition(),
                                        &finalColours, clipper);
    }

    void TextComponent::writeXMLToStream(XMLSerializer& xml_stream) const
    {
        // opening tag
        xml_stream.openTag("TextComponent");
        // write out area
        d_area.writeXMLToStream(xml_stream);

        // write text element
        if (!d_font.empty() && !getText().empty())
        {
            xml_stream.openTag("Text");
            if (!d_font.empty())
                xml_stream.attribute("font", d_font);
            if (!getText().empty())
                xml_stream.attribute("string", getText());
            xml_stream.closeTag();
        }

        // write text property element
        if (!d_textPropertyName.empty())
        {
            xml_stream.openTag("TextProperty")
                .attribute("name", d_textPropertyName)
                .closeTag();
        }

        // write font property element
        if (!d_fontPropertyName.empty())
        {
            xml_stream.openTag("FontProperty")
                .attribute("name", d_fontPropertyName)
                .closeTag();
        }

        // get base class to write colours
        writeColoursXML(xml_stream);

        // write vert format, allowing base class to do this for us if a propety is in use
        if (!writeVertFormatXML(xml_stream))
        {
            // was not a property, so write out explicit formatting in use
            xml_stream.openTag("VertFormat")
                .attribute("type", FalagardXMLHelper::vertTextFormatToString(d_vertFormatting))
                .closeTag();
        }

        // write horz format, allowing base class to do this for us if a propety is in use
        if (!writeHorzFormatXML(xml_stream))
        {
            // was not a property, so write out explicit formatting in use
            xml_stream.openTag("HorzFormat")
                .attribute("type", FalagardXMLHelper::horzTextFormatToString(d_horzFormatting))
                .closeTag();
        }

        // closing tag
        xml_stream.closeTag();
    }

    bool TextComponent::isTextFetchedFromProperty() const
    {
        return !d_textPropertyName.empty();
    }

    const String& TextComponent::getTextPropertySource() const
    {
        return d_textPropertyName;
    }

    void TextComponent::setTextPropertySource(const String& property)
    {
        d_textPropertyName = property;
    }

    bool TextComponent::isFontFetchedFromProperty() const
    {
        return !d_fontPropertyName.empty();
    }

    const String& TextComponent::getFontPropertySource() const
    {
        return d_fontPropertyName;
    }

    void TextComponent::setFontPropertySource(const String& property)
    {
        d_fontPropertyName = property;
    }

    const String& TextComponent::getTextVisual() const
    {
        // no bidi support
        if (!d_bidiVisualMapping)
            return d_textLogical;

        if (!d_bidiDataValid)
        {
            d_bidiVisualMapping->updateVisual(d_textLogical);
            d_bidiDataValid = true;
        }

        return d_bidiVisualMapping->getTextVisual();
    }

    float TextComponent::getHorizontalTextExtent() const
    {
        return d_formattedRenderedString->getHorizontalExtent();
    }

    float TextComponent::getVerticalTextExtent() const
    {
        return d_formattedRenderedString->getVerticalExtent();
    }

} // End of  CEGUI namespace section
<commit_msg>FIX: We were applying text colours twice in Falagard TextComponent when string to be drawn or font to use were sourced anywhere other than from the default places. Many thanks to forum member 'BrightBit' for providing test case datafiles to produce this issue. See: http://www.cegui.org.uk/mantis/view.php?id=774.<commit_after>/***********************************************************************
    filename:   CEGUIFalTextComponent.cpp
    created:    Sun Jun 19 2005
    author:     Paul D Turner <paul@cegui.org.uk>
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#ifdef HAVE_CONFIG_H
#   include "config.h"
#endif

#include "falagard/CEGUIFalTextComponent.h"
#include "falagard/CEGUIFalXMLEnumHelper.h"
#include "CEGUIFontManager.h"
#include "CEGUIExceptions.h"
#include "CEGUIPropertyHelper.h"
#include "CEGUIFont.h"
#include "CEGUILeftAlignedRenderedString.h"
#include "CEGUIRightAlignedRenderedString.h"
#include "CEGUICentredRenderedString.h"
#include "CEGUIJustifiedRenderedString.h"
#include "CEGUIRenderedStringWordWrapper.h"
#include <iostream>

#if defined (CEGUI_USE_FRIBIDI)
    #include "CEGUIFribidiVisualMapping.h"
#elif defined (CEGUI_USE_MINIBIDI)
    #include "CEGUIMinibidiVisualMapping.h"
#else
    #include "CEGUIBiDiVisualMapping.h"
#endif

// Start of CEGUI namespace section
namespace CEGUI
{
    TextComponent::TextComponent() :
#ifndef CEGUI_BIDI_SUPPORT
        d_bidiVisualMapping(0),
#elif defined (CEGUI_USE_FRIBIDI)
        d_bidiVisualMapping(new FribidiVisualMapping),
#elif defined (CEGUI_USE_MINIBIDI)
        d_bidiVisualMapping(new MinibidiVisualMapping),
#else
    #error "BIDI Configuration is inconsistant, check your config!"
#endif
        d_bidiDataValid(false),
        d_formattedRenderedString(new LeftAlignedRenderedString(d_renderedString)),
        d_lastHorzFormatting(HTF_LEFT_ALIGNED),
        d_vertFormatting(VTF_TOP_ALIGNED),
        d_horzFormatting(HTF_LEFT_ALIGNED)
    {}

    TextComponent::~TextComponent()
    {
        delete d_bidiVisualMapping;
    }

    TextComponent::TextComponent(const TextComponent& obj) :
        FalagardComponentBase(obj),
        d_textLogical(obj.d_textLogical),
#ifndef CEGUI_BIDI_SUPPORT
        d_bidiVisualMapping(0),
#elif defined (CEGUI_USE_FRIBIDI)
        d_bidiVisualMapping(new FribidiVisualMapping),
#elif defined (CEGUI_USE_MINIBIDI)
        d_bidiVisualMapping(new MinibidiVisualMapping),
#endif
        d_bidiDataValid(false),
        d_renderedString(obj.d_renderedString),
        d_formattedRenderedString(obj.d_formattedRenderedString),
        d_lastHorzFormatting(obj.d_lastHorzFormatting),
        d_font(obj.d_font),
        d_vertFormatting(obj.d_vertFormatting),
        d_horzFormatting(obj.d_horzFormatting),
        d_textPropertyName(obj.d_textPropertyName),
        d_fontPropertyName(obj.d_fontPropertyName)
    {
    }

    TextComponent& TextComponent::operator=(const TextComponent& other)
    {
        if (this == &other)
            return *this;

        FalagardComponentBase::operator=(other);

        d_textLogical = other.d_textLogical;
        // note we do not assign the BiDiVisualMapping object, we just mark our
        // existing one as invalid so it's data gets regenerated next time it's
        // needed.
        d_bidiDataValid = false;
        d_renderedString = other.d_renderedString;
        d_formattedRenderedString = other.d_formattedRenderedString;
        d_lastHorzFormatting = other.d_lastHorzFormatting;
        d_font = other.d_font;
        d_vertFormatting = other.d_vertFormatting;
        d_horzFormatting = other.d_horzFormatting;
        d_textPropertyName = other.d_textPropertyName;
        d_fontPropertyName = other.d_fontPropertyName;

        return *this;
    }

    const String& TextComponent::getText() const
    {
        return d_textLogical;
    }

    void TextComponent::setText(const String& text)
    {
        d_textLogical = text;
        d_bidiDataValid = false;
    }

    const String& TextComponent::getFont() const
    {
        return d_font;
    }

    void TextComponent::setFont(const String& font)
    {
        d_font = font;
    }

    VerticalTextFormatting TextComponent::getVerticalFormatting() const
    {
        return d_vertFormatting;
    }

    void TextComponent::setVerticalFormatting(VerticalTextFormatting fmt)
    {
        d_vertFormatting = fmt;
    }

    HorizontalTextFormatting TextComponent::getHorizontalFormatting() const
    {
        return d_horzFormatting;
    }

    void TextComponent::setHorizontalFormatting(HorizontalTextFormatting fmt)
    {
        d_horzFormatting = fmt;
    }

    void TextComponent::setupStringFormatter(const Window& window,
                                             const RenderedString& rendered_string) const
    {
        const HorizontalTextFormatting horzFormatting = d_horzFormatPropertyName.empty() ? d_horzFormatting :
            FalagardXMLHelper::stringToHorzTextFormat(window.getProperty(d_horzFormatPropertyName));

        // no formatting change
        if (horzFormatting == d_lastHorzFormatting)
        {
            d_formattedRenderedString->setRenderedString(rendered_string);
            return;
        }

        d_lastHorzFormatting = horzFormatting;

        switch(horzFormatting)
        {
        case HTF_LEFT_ALIGNED:
            d_formattedRenderedString =
                new LeftAlignedRenderedString(rendered_string);
            break;

        case HTF_CENTRE_ALIGNED:
            d_formattedRenderedString =
                new CentredRenderedString(rendered_string);
            break;

        case HTF_RIGHT_ALIGNED:
            d_formattedRenderedString =
                new RightAlignedRenderedString(rendered_string);
            break;

        case HTF_JUSTIFIED:
            d_formattedRenderedString =
                new JustifiedRenderedString(rendered_string);
            break;

        case HTF_WORDWRAP_LEFT_ALIGNED:
            d_formattedRenderedString =
                new RenderedStringWordWrapper
                    <LeftAlignedRenderedString>(rendered_string);
            break;

        case HTF_WORDWRAP_CENTRE_ALIGNED:
            d_formattedRenderedString =
                new RenderedStringWordWrapper
                    <CentredRenderedString>(rendered_string);
            break;

        case HTF_WORDWRAP_RIGHT_ALIGNED:
            d_formattedRenderedString =
                new RenderedStringWordWrapper
                    <RightAlignedRenderedString>(rendered_string);
            break;

        case HTF_WORDWRAP_JUSTIFIED:
            d_formattedRenderedString =
                new RenderedStringWordWrapper
                    <JustifiedRenderedString>(rendered_string);
            break;
        }
    }

    void TextComponent::render_impl(Window& srcWindow, Rect& destRect, const CEGUI::ColourRect* modColours, const Rect* clipper, bool /*clipToDisplay*/) const
    {
        // get font to use
        Font* font;

        CEGUI_TRY
        {
            font = d_fontPropertyName.empty() ?
                (d_font.empty() ? srcWindow.getFont() : &FontManager::getSingleton().get(d_font))
                : &FontManager::getSingleton().get(srcWindow.getProperty(d_fontPropertyName));
        }
        CEGUI_CATCH (UnknownObjectException&)
        {
            font = 0;
        }

        // exit if we have no font to use.
        if (!font)
            return;

        const RenderedString* rs = &d_renderedString;
        // do we fetch text from a property
        if (!d_textPropertyName.empty())
        {
            // fetch text & do bi-directional reordering as needed
            String vis;
            #ifdef CEGUI_BIDI_SUPPORT
                BiDiVisualMapping::StrIndexList l2v, v2l;
                d_bidiVisualMapping->reorderFromLogicalToVisual(
                    srcWindow.getProperty(d_textPropertyName), vis, l2v, v2l);
            #else
                vis = srcWindow.getProperty(d_textPropertyName);
            #endif
            // parse string using parser from Window.
            d_renderedString =
                srcWindow.getRenderedStringParser().parse(vis, font, 0);
        }
        // do we use a static text string from the looknfeel
        else if (!getTextVisual().empty())
            // parse string using parser from Window.
            d_renderedString = srcWindow.getRenderedStringParser().
                parse(getTextVisual(), font, 0);
        // do we have to override the font?
        else if (font != srcWindow.getFont())
            d_renderedString = srcWindow.getRenderedStringParser().
                parse(srcWindow.getTextVisual(), font, 0);
        // use ready-made RenderedString from the Window itself
        else
            rs = &srcWindow.getRenderedString();

        setupStringFormatter(srcWindow, *rs);
        d_formattedRenderedString->format(destRect.getSize());

        // Get total formatted height.
        const float textHeight = d_formattedRenderedString->getVerticalExtent();

        // handle dest area adjustments for vertical formatting.
        VerticalTextFormatting vertFormatting = d_vertFormatPropertyName.empty() ? d_vertFormatting :
            FalagardXMLHelper::stringToVertTextFormat(srcWindow.getProperty(d_vertFormatPropertyName));

        switch(vertFormatting)
        {
        case VTF_CENTRE_ALIGNED:
            destRect.d_top += (destRect.getHeight() - textHeight) * 0.5f;
            break;

        case VTF_BOTTOM_ALIGNED:
            destRect.d_top = destRect.d_bottom - textHeight;
            break;

        default:
            // default is VTF_TOP_ALIGNED, for which we take no action.
            break;
        }

        // calculate final colours to be used
        ColourRect finalColours;
        initColoursRect(srcWindow, modColours, finalColours);

        // offset the font little down so that it's centered within its own spacing
//        destRect.d_top += (font->getLineSpacing() - font->getFontHeight()) * 0.5f;
        // add geometry for text to the target window.
        d_formattedRenderedString->draw(srcWindow.getGeometryBuffer(),
                                        destRect.getPosition(),
                                        &finalColours, clipper);
    }

    void TextComponent::writeXMLToStream(XMLSerializer& xml_stream) const
    {
        // opening tag
        xml_stream.openTag("TextComponent");
        // write out area
        d_area.writeXMLToStream(xml_stream);

        // write text element
        if (!d_font.empty() && !getText().empty())
        {
            xml_stream.openTag("Text");
            if (!d_font.empty())
                xml_stream.attribute("font", d_font);
            if (!getText().empty())
                xml_stream.attribute("string", getText());
            xml_stream.closeTag();
        }

        // write text property element
        if (!d_textPropertyName.empty())
        {
            xml_stream.openTag("TextProperty")
                .attribute("name", d_textPropertyName)
                .closeTag();
        }

        // write font property element
        if (!d_fontPropertyName.empty())
        {
            xml_stream.openTag("FontProperty")
                .attribute("name", d_fontPropertyName)
                .closeTag();
        }

        // get base class to write colours
        writeColoursXML(xml_stream);

        // write vert format, allowing base class to do this for us if a propety is in use
        if (!writeVertFormatXML(xml_stream))
        {
            // was not a property, so write out explicit formatting in use
            xml_stream.openTag("VertFormat")
                .attribute("type", FalagardXMLHelper::vertTextFormatToString(d_vertFormatting))
                .closeTag();
        }

        // write horz format, allowing base class to do this for us if a propety is in use
        if (!writeHorzFormatXML(xml_stream))
        {
            // was not a property, so write out explicit formatting in use
            xml_stream.openTag("HorzFormat")
                .attribute("type", FalagardXMLHelper::horzTextFormatToString(d_horzFormatting))
                .closeTag();
        }

        // closing tag
        xml_stream.closeTag();
    }

    bool TextComponent::isTextFetchedFromProperty() const
    {
        return !d_textPropertyName.empty();
    }

    const String& TextComponent::getTextPropertySource() const
    {
        return d_textPropertyName;
    }

    void TextComponent::setTextPropertySource(const String& property)
    {
        d_textPropertyName = property;
    }

    bool TextComponent::isFontFetchedFromProperty() const
    {
        return !d_fontPropertyName.empty();
    }

    const String& TextComponent::getFontPropertySource() const
    {
        return d_fontPropertyName;
    }

    void TextComponent::setFontPropertySource(const String& property)
    {
        d_fontPropertyName = property;
    }

    const String& TextComponent::getTextVisual() const
    {
        // no bidi support
        if (!d_bidiVisualMapping)
            return d_textLogical;

        if (!d_bidiDataValid)
        {
            d_bidiVisualMapping->updateVisual(d_textLogical);
            d_bidiDataValid = true;
        }

        return d_bidiVisualMapping->getTextVisual();
    }

    float TextComponent::getHorizontalTextExtent() const
    {
        return d_formattedRenderedString->getHorizontalExtent();
    }

    float TextComponent::getVerticalTextExtent() const
    {
        return d_formattedRenderedString->getVerticalExtent();
    }

} // End of  CEGUI namespace section
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrBatchFontCache.h"
#include "GrContext.h"
#include "GrGpu.h"
#include "GrRectanizer.h"
#include "GrResourceProvider.h"
#include "GrSurfacePriv.h"
#include "SkString.h"

#include "SkDistanceFieldGen.h"

///////////////////////////////////////////////////////////////////////////////

bool GrBatchFontCache::initAtlas(GrMaskFormat format) {
    int index = MaskFormatToAtlasIndex(format);
    if (!fAtlases[index]) {
        GrPixelConfig config = MaskFormatToPixelConfig(format);
        int width = fAtlasConfigs[index].fWidth;
        int height = fAtlasConfigs[index].fHeight;
        int numPlotsX = fAtlasConfigs[index].numPlotsX();
        int numPlotsY = fAtlasConfigs[index].numPlotsY();

        fAtlases[index] =
                fContext->resourceProvider()->createAtlas(config, width, height,
                                                          numPlotsX, numPlotsY,
                                                          &GrBatchFontCache::HandleEviction,
                                                          (void*)this);
        if (!fAtlases[index]) {
            return false;
        }
    }
    return true;
}

GrBatchFontCache::GrBatchFontCache(GrContext* context)
    : fContext(context)
    , fPreserveStrike(nullptr) {
    for (int i = 0; i < kMaskFormatCount; ++i) {
        fAtlases[i] = nullptr;
    }

    // setup default atlas configs
    fAtlasConfigs[kA8_GrMaskFormat].fWidth = 2048;
    fAtlasConfigs[kA8_GrMaskFormat].fHeight = 2048;
    fAtlasConfigs[kA8_GrMaskFormat].fLog2Width = 11;
    fAtlasConfigs[kA8_GrMaskFormat].fLog2Height = 11;
    fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = 512;
    fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = 256;

    fAtlasConfigs[kA565_GrMaskFormat].fWidth = 1024;
    fAtlasConfigs[kA565_GrMaskFormat].fHeight = 2048;
    fAtlasConfigs[kA565_GrMaskFormat].fLog2Width = 10;
    fAtlasConfigs[kA565_GrMaskFormat].fLog2Height = 11;
    fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = 256;
    fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = 256;

    fAtlasConfigs[kARGB_GrMaskFormat].fWidth = 1024;
    fAtlasConfigs[kARGB_GrMaskFormat].fHeight = 2048;
    fAtlasConfigs[kARGB_GrMaskFormat].fLog2Width = 10;
    fAtlasConfigs[kARGB_GrMaskFormat].fLog2Height = 11;
    fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = 256;
    fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = 256;
}

GrBatchFontCache::~GrBatchFontCache() {
    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
    while (!iter.done()) {
        (*iter).fIsAbandoned = true;
        (*iter).unref();
        ++iter;
    }
    for (int i = 0; i < kMaskFormatCount; ++i) {
        delete fAtlases[i];
    }
}

void GrBatchFontCache::freeAll() {
    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
    while (!iter.done()) {
        (*iter).fIsAbandoned = true;
        (*iter).unref();
        ++iter;
    }
    fCache.rewind();
    for (int i = 0; i < kMaskFormatCount; ++i) {
        delete fAtlases[i];
        fAtlases[i] = nullptr;
    }
}

void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
    GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);

    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
    for (; !iter.done(); ++iter) {
        GrBatchTextStrike* strike = &*iter;
        strike->removeID(id);

        // clear out any empty strikes.  We will preserve the strike whose call to addToAtlas
        // triggered the eviction
        if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
            fontCache->fCache.remove(*(strike->fFontScalerKey));
            strike->fIsAbandoned = true;
            strike->unref();
        }
    }
}

void GrBatchFontCache::dump() const {
    static int gDumpCount = 0;
    for (int i = 0; i < kMaskFormatCount; ++i) {
        if (fAtlases[i]) {
            GrTexture* texture = fAtlases[i]->getTexture();
            if (texture) {
                SkString filename;
#ifdef SK_BUILD_FOR_ANDROID
                filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
#else
                filename.printf("fontcache_%d%d.png", gDumpCount, i);
#endif
                texture->surfacePriv().savePixels(filename.c_str());
            }
        }
    }
    ++gDumpCount;
}

void GrBatchFontCache::setAtlasSizes_ForTesting(const GrBatchAtlasConfig configs[3]) {
    // delete any old atlases, this should be safe to do as long as we are not in the middle of a
    // flush
    for (int i = 0; i < kMaskFormatCount; i++) {
        if (fAtlases[i]) {
            delete fAtlases[i];
            fAtlases[i] = nullptr;
        }
    }
    memcpy(fAtlasConfigs, configs, sizeof(fAtlasConfigs));
}

///////////////////////////////////////////////////////////////////////////////

/*
    The text strike is specific to a given font/style/matrix setup, which is
    represented by the GrHostFontScaler object we are given in getGlyph().

    We map a 32bit glyphID to a GrGlyph record, which in turn points to a
    atlas and a position within that texture.
 */

GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
    : fFontScalerKey(SkRef(key))
    , fPool(9/*start allocations at 512 bytes*/)
    , fAtlasedGlyphs(0)
    , fIsAbandoned(false) {

    fBatchFontCache = cache;     // no need to ref, it won't go away before we do
}

GrBatchTextStrike::~GrBatchTextStrike() {
    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    while (!iter.done()) {
        (*iter).reset();
        ++iter;
    }
}

GrGlyph* GrBatchTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
                                          GrFontScaler* scaler) {
    SkIRect bounds;
    if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
        if (!scaler->getPackedGlyphDFBounds(skGlyph, &bounds)) {
            return nullptr;
        }
    } else {
        if (!scaler->getPackedGlyphBounds(skGlyph, &bounds)) {
            return nullptr;
        }
    }
    GrMaskFormat format = scaler->getPackedGlyphMaskFormat(skGlyph);

    GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph));
    glyph->init(packed, bounds, format);
    fCache.add(glyph);
    return glyph;
}

void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    while (!iter.done()) {
        if (id == (*iter).fID) {
            (*iter).fID = GrBatchAtlas::kInvalidAtlasID;
            fAtlasedGlyphs--;
            SkASSERT(fAtlasedGlyphs >= 0);
        }
        ++iter;
    }
}

bool GrBatchTextStrike::addGlyphToAtlas(GrDrawBatch::Target* target,
                                        GrGlyph* glyph,
                                        GrFontScaler* scaler,
                                        GrMaskFormat expectedMaskFormat) {
    SkASSERT(glyph);
    SkASSERT(scaler);
    SkASSERT(fCache.find(glyph->fPackedID));

    SkAutoUnref ar(SkSafeRef(scaler));

    int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);

    size_t size = glyph->fBounds.area() * bytesPerPixel;
    SkAutoSMalloc<1024> storage(size);

    const SkGlyph& skGlyph = scaler->grToSkGlyph(glyph->fPackedID);
    if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
        if (!scaler->getPackedGlyphDFImage(skGlyph, glyph->width(), glyph->height(),
                                           storage.get())) {
            return false;
        }
    } else {
        if (!scaler->getPackedGlyphImage(skGlyph, glyph->width(), glyph->height(),
                                         glyph->width() * bytesPerPixel, expectedMaskFormat,
                                         storage.get())) {
            return false;
        }
    }

    bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, target, expectedMaskFormat,
                                               glyph->width(), glyph->height(),
                                               storage.get(), &glyph->fAtlasLocation);
    if (success) {
        SkASSERT(GrBatchAtlas::kInvalidAtlasID != glyph->fID);
        fAtlasedGlyphs++;
    }
    return success;
}
<commit_msg>Remove unneeded reference count churn.<commit_after>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrBatchFontCache.h"
#include "GrContext.h"
#include "GrGpu.h"
#include "GrRectanizer.h"
#include "GrResourceProvider.h"
#include "GrSurfacePriv.h"
#include "SkString.h"

#include "SkDistanceFieldGen.h"

///////////////////////////////////////////////////////////////////////////////

bool GrBatchFontCache::initAtlas(GrMaskFormat format) {
    int index = MaskFormatToAtlasIndex(format);
    if (!fAtlases[index]) {
        GrPixelConfig config = MaskFormatToPixelConfig(format);
        int width = fAtlasConfigs[index].fWidth;
        int height = fAtlasConfigs[index].fHeight;
        int numPlotsX = fAtlasConfigs[index].numPlotsX();
        int numPlotsY = fAtlasConfigs[index].numPlotsY();

        fAtlases[index] =
                fContext->resourceProvider()->createAtlas(config, width, height,
                                                          numPlotsX, numPlotsY,
                                                          &GrBatchFontCache::HandleEviction,
                                                          (void*)this);
        if (!fAtlases[index]) {
            return false;
        }
    }
    return true;
}

GrBatchFontCache::GrBatchFontCache(GrContext* context)
    : fContext(context)
    , fPreserveStrike(nullptr) {
    for (int i = 0; i < kMaskFormatCount; ++i) {
        fAtlases[i] = nullptr;
    }

    // setup default atlas configs
    fAtlasConfigs[kA8_GrMaskFormat].fWidth = 2048;
    fAtlasConfigs[kA8_GrMaskFormat].fHeight = 2048;
    fAtlasConfigs[kA8_GrMaskFormat].fLog2Width = 11;
    fAtlasConfigs[kA8_GrMaskFormat].fLog2Height = 11;
    fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = 512;
    fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = 256;

    fAtlasConfigs[kA565_GrMaskFormat].fWidth = 1024;
    fAtlasConfigs[kA565_GrMaskFormat].fHeight = 2048;
    fAtlasConfigs[kA565_GrMaskFormat].fLog2Width = 10;
    fAtlasConfigs[kA565_GrMaskFormat].fLog2Height = 11;
    fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = 256;
    fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = 256;

    fAtlasConfigs[kARGB_GrMaskFormat].fWidth = 1024;
    fAtlasConfigs[kARGB_GrMaskFormat].fHeight = 2048;
    fAtlasConfigs[kARGB_GrMaskFormat].fLog2Width = 10;
    fAtlasConfigs[kARGB_GrMaskFormat].fLog2Height = 11;
    fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = 256;
    fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = 256;
}

GrBatchFontCache::~GrBatchFontCache() {
    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
    while (!iter.done()) {
        (*iter).fIsAbandoned = true;
        (*iter).unref();
        ++iter;
    }
    for (int i = 0; i < kMaskFormatCount; ++i) {
        delete fAtlases[i];
    }
}

void GrBatchFontCache::freeAll() {
    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
    while (!iter.done()) {
        (*iter).fIsAbandoned = true;
        (*iter).unref();
        ++iter;
    }
    fCache.rewind();
    for (int i = 0; i < kMaskFormatCount; ++i) {
        delete fAtlases[i];
        fAtlases[i] = nullptr;
    }
}

void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
    GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);

    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
    for (; !iter.done(); ++iter) {
        GrBatchTextStrike* strike = &*iter;
        strike->removeID(id);

        // clear out any empty strikes.  We will preserve the strike whose call to addToAtlas
        // triggered the eviction
        if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
            fontCache->fCache.remove(*(strike->fFontScalerKey));
            strike->fIsAbandoned = true;
            strike->unref();
        }
    }
}

void GrBatchFontCache::dump() const {
    static int gDumpCount = 0;
    for (int i = 0; i < kMaskFormatCount; ++i) {
        if (fAtlases[i]) {
            GrTexture* texture = fAtlases[i]->getTexture();
            if (texture) {
                SkString filename;
#ifdef SK_BUILD_FOR_ANDROID
                filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
#else
                filename.printf("fontcache_%d%d.png", gDumpCount, i);
#endif
                texture->surfacePriv().savePixels(filename.c_str());
            }
        }
    }
    ++gDumpCount;
}

void GrBatchFontCache::setAtlasSizes_ForTesting(const GrBatchAtlasConfig configs[3]) {
    // delete any old atlases, this should be safe to do as long as we are not in the middle of a
    // flush
    for (int i = 0; i < kMaskFormatCount; i++) {
        if (fAtlases[i]) {
            delete fAtlases[i];
            fAtlases[i] = nullptr;
        }
    }
    memcpy(fAtlasConfigs, configs, sizeof(fAtlasConfigs));
}

///////////////////////////////////////////////////////////////////////////////

/*
    The text strike is specific to a given font/style/matrix setup, which is
    represented by the GrHostFontScaler object we are given in getGlyph().

    We map a 32bit glyphID to a GrGlyph record, which in turn points to a
    atlas and a position within that texture.
 */

GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
    : fFontScalerKey(SkRef(key))
    , fPool(9/*start allocations at 512 bytes*/)
    , fAtlasedGlyphs(0)
    , fIsAbandoned(false) {

    fBatchFontCache = cache;     // no need to ref, it won't go away before we do
}

GrBatchTextStrike::~GrBatchTextStrike() {
    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    while (!iter.done()) {
        (*iter).reset();
        ++iter;
    }
}

GrGlyph* GrBatchTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
                                          GrFontScaler* scaler) {
    SkIRect bounds;
    if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
        if (!scaler->getPackedGlyphDFBounds(skGlyph, &bounds)) {
            return nullptr;
        }
    } else {
        if (!scaler->getPackedGlyphBounds(skGlyph, &bounds)) {
            return nullptr;
        }
    }
    GrMaskFormat format = scaler->getPackedGlyphMaskFormat(skGlyph);

    GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph));
    glyph->init(packed, bounds, format);
    fCache.add(glyph);
    return glyph;
}

void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    while (!iter.done()) {
        if (id == (*iter).fID) {
            (*iter).fID = GrBatchAtlas::kInvalidAtlasID;
            fAtlasedGlyphs--;
            SkASSERT(fAtlasedGlyphs >= 0);
        }
        ++iter;
    }
}

bool GrBatchTextStrike::addGlyphToAtlas(GrDrawBatch::Target* target,
                                        GrGlyph* glyph,
                                        GrFontScaler* scaler,
                                        GrMaskFormat expectedMaskFormat) {
    SkASSERT(glyph);
    SkASSERT(scaler);
    SkASSERT(fCache.find(glyph->fPackedID));

    int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);

    size_t size = glyph->fBounds.area() * bytesPerPixel;
    SkAutoSMalloc<1024> storage(size);

    const SkGlyph& skGlyph = scaler->grToSkGlyph(glyph->fPackedID);
    if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
        if (!scaler->getPackedGlyphDFImage(skGlyph, glyph->width(), glyph->height(),
                                           storage.get())) {
            return false;
        }
    } else {
        if (!scaler->getPackedGlyphImage(skGlyph, glyph->width(), glyph->height(),
                                         glyph->width() * bytesPerPixel, expectedMaskFormat,
                                         storage.get())) {
            return false;
        }
    }

    bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, target, expectedMaskFormat,
                                               glyph->width(), glyph->height(),
                                               storage.get(), &glyph->fAtlasLocation);
    if (success) {
        SkASSERT(GrBatchAtlas::kInvalidAtlasID != glyph->fID);
        fAtlasedGlyphs++;
    }
    return success;
}
<|endoftext|>