<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

//          Jiashun Zhu(zhujiashun2010@gmail.com)

#include <google/protobuf/descriptor.h>         // MethodDescriptor
#include <google/protobuf/message.h>            // Message
#include <gflags/gflags.h>
#include "butil/logging.h"                       // LOG()
#include "butil/time.h"
#include "butil/iobuf.h"                         // butil::IOBuf
#include "brpc/controller.h"               // Controller
#include "brpc/details/controller_private_accessor.h"
#include "brpc/socket.h"                   // Socket
#include "brpc/server.h"                   // Server
#include "brpc/details/server_private_accessor.h"
#include "brpc/span.h"
#include "brpc/redis.h"
#include "brpc/redis_command.h"
#include "brpc/policy/redis_protocol.h"

namespace brpc {

DECLARE_bool(enable_rpcz);
DECLARE_bool(usercode_in_pthread);

namespace policy {

DEFINE_bool(redis_verbose, false,
            "[DEBUG] Print EVERY redis request/response");

struct InputResponse : public InputMessageBase {
    bthread_id_t id_wait;
    RedisResponse response;

    // @InputMessageBase
    void DestroyImpl() {
        delete this;
    }
};

// This class is as parsing_context in socket.
class RedisConnContext : public Destroyable  {
public:
    explicit RedisConnContext(const RedisService* rs)
        : redis_service(rs)
        , batched_size(0) {}

    ~RedisConnContext();
    // @Destroyable
    void Destroy() override;

    const RedisService* redis_service;
    // If user starts a transaction, transaction_handler indicates the
    // handler pointer that runs the transaction command.
    std::unique_ptr<RedisCommandHandler> transaction_handler;
    // >0 if command handler is run in batched mode.
    int batched_size;

    RedisCommandParser parser;
    butil::Arena arena;
};

int ConsumeCommand(RedisConnContext* ctx,
                   const std::vector<butil::StringPiece>& commands,
                   bool flush_batched,
                   butil::IOBufAppender* appender) {
    RedisReply output(&ctx->arena);
    RedisCommandHandlerResult result = REDIS_CMD_HANDLED;
    if (ctx->transaction_handler) {
        result = ctx->transaction_handler->Run(commands, &output, flush_batched);
        if (result == REDIS_CMD_HANDLED) {
            ctx->transaction_handler.reset(NULL);
        } else if (result == REDIS_CMD_BATCHED) {
            LOG(ERROR) << "BATCHED should not be returned by a transaction handler.";
            return -1;
        }
    } else {
        RedisCommandHandler* ch = ctx->redis_service->FindCommandHandler(commands[0].as_string());
        if (!ch) {
            char buf[64];
            snprintf(buf, sizeof(buf), "ERR unknown command `%s`", commands[0].as_string().c_str());
            output.SetError(buf);
        } else {
            result = ch->Run(commands, &output, flush_batched);
            if (result == REDIS_CMD_CONTINUE) {
                if (ctx->batched_size != 0) {
                    LOG(ERROR) << "CONTINUE should not be returned in a batched process.";
                    return -1;
                }
                ctx->transaction_handler.reset(ch->NewTransactionHandler());
            } else if (result == REDIS_CMD_BATCHED) {
                ctx->batched_size++;
            }
        }
    }
    if (result == REDIS_CMD_HANDLED) {
        if (ctx->batched_size) {
            if ((int)output.size() != (ctx->batched_size + 1)) {
                LOG(ERROR) << "reply array size can't be matched with batched size, "
                    << " expected=" << ctx->batched_size + 1 << " actual=" << output.size();
                return -1;
            }
            for (int i = 0; i < (int)output.size(); ++i) {
                output[i].SerializeTo(appender);
            }
            ctx->batched_size = 0;
        } else {
            output.SerializeTo(appender);
        }
    } else if (result == REDIS_CMD_CONTINUE) {
        output.SerializeTo(appender);
    } else if (result == REDIS_CMD_BATCHED) {
        // just do nothing and wait handler to return OK.
    } else {
        LOG(ERROR) << "unknown status=" << result;
        return -1;
    }
    return 0;
}

// ========== impl of RedisConnContext ==========

RedisConnContext::~RedisConnContext() { }

void RedisConnContext::Destroy() {
    delete this;
}

// ========== impl of RedisConnContext ==========

ParseResult ParseRedisMessage(butil::IOBuf* source, Socket* socket,
                              bool read_eof, const void* arg) {
    if (read_eof || source->empty()) {
        return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
    }
    const Server* server = static_cast<const Server*>(arg);
    if (server) {
        const RedisService* const rs = server->options().redis_service;
        if (!rs) {
            return MakeParseError(PARSE_ERROR_TRY_OTHERS);
        }
        RedisConnContext* ctx = static_cast<RedisConnContext*>(socket->parsing_context());
        if (ctx == NULL) {
            ctx = new RedisConnContext(rs);
            socket->reset_parsing_context(ctx);
        }
        std::vector<butil::StringPiece> current_commands;
        butil::IOBufAppender appender;
        ParseError err = PARSE_OK;

        err = ctx->parser.Consume(*source, &current_commands, &ctx->arena);
        if (err != PARSE_OK) {
            return MakeParseError(err);
        }
        while (true) {
            std::vector<butil::StringPiece> next_commands;
            err = ctx->parser.Consume(*source, &next_commands, &ctx->arena);
            if (err != PARSE_OK) {
                break;
            }
            if (ConsumeCommand(ctx, current_commands, false, &appender) != 0) {
                return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG);
            }
            current_commands.swap(next_commands);
        }
        if (ConsumeCommand(ctx, current_commands,
                    true /*must be the last message*/, &appender) != 0) {
            return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG);
        }
        butil::IOBuf sendbuf;
        appender.move_to(sendbuf);
        CHECK(!sendbuf.empty());
        Socket::WriteOptions wopt;
        wopt.ignore_eovercrowded = true;
        LOG_IF(WARNING, socket->Write(&sendbuf, &wopt) != 0)
            << "Fail to send redis reply";
        ctx->arena.clear();
        return MakeParseError(err);
    } else {
        // NOTE(gejun): PopPipelinedInfo() is actually more contended than what
        // I thought before. The Socket._pipeline_q is a SPSC queue pushed before
        // sending and popped when response comes back, being protected by a
        // mutex. Previously the mutex is shared with Socket._id_wait_list. When
        // 200 bthreads access one redis-server, ~1.5s in total is spent on
        // contention in 10-second duration. If the mutex is separated, the time
        // drops to ~0.25s. I further replaced PeekPipelinedInfo() with
        // GivebackPipelinedInfo() to lock only once(when receiving response)
        // in most cases, and the time decreases to ~0.14s.
        PipelinedInfo pi;
        if (!socket->PopPipelinedInfo(&pi)) {
            LOG(WARNING) << "No corresponding PipelinedInfo in socket";
            return MakeParseError(PARSE_ERROR_TRY_OTHERS);
        }

        do {
            InputResponse* msg = static_cast<InputResponse*>(socket->parsing_context());
            if (msg == NULL) {
                msg = new InputResponse;
                socket->reset_parsing_context(msg);
            }

            const int consume_count = (pi.with_auth ? 1 : pi.count);

            ParseError err = msg->response.ConsumePartialIOBuf(*source, consume_count);
            if (err != PARSE_OK) {
                socket->GivebackPipelinedInfo(pi);
                return MakeParseError(err);
            }

            if (pi.with_auth) {
                if (msg->response.reply_size() != 1 ||
                    !(msg->response.reply(0).type() == brpc::REDIS_REPLY_STATUS &&
                      msg->response.reply(0).data().compare("OK") == 0)) {
                    LOG(ERROR) << "Redis Auth failed: " << msg->response;
                    return MakeParseError(PARSE_ERROR_NO_RESOURCE,
                                          "Fail to authenticate with Redis");
                }

                DestroyingPtr<InputResponse> auth_msg(
                     static_cast<InputResponse*>(socket->release_parsing_context()));
                pi.with_auth = false;
                continue;
            }

            CHECK_EQ((uint32_t)msg->response.reply_size(), pi.count);
            msg->id_wait = pi.id_wait;
            socket->release_parsing_context();
            return MakeMessage(msg);
        } while(true);
    }

    return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG);
}

void ProcessRedisResponse(InputMessageBase* msg_base) {
    const int64_t start_parse_us = butil::cpuwide_time_us();
    DestroyingPtr<InputResponse> msg(static_cast<InputResponse*>(msg_base));

    const bthread_id_t cid = msg->id_wait;
    Controller* cntl = NULL;
    const int rc = bthread_id_lock(cid, (void**)&cntl);
    if (rc != 0) {
        LOG_IF(ERROR, rc != EINVAL && rc != EPERM)
            << "Fail to lock correlation_id=" << cid << ": " << berror(rc);
        return;
    }

    ControllerPrivateAccessor accessor(cntl);
    Span* span = accessor.span();
    if (span) {
        span->set_base_real_us(msg->base_real_us());
        span->set_received_us(msg->received_us());
        span->set_response_size(msg->response.ByteSize());
        span->set_start_parse_us(start_parse_us);
    }
    const int saved_error = cntl->ErrorCode();
    if (cntl->response() != NULL) {
        if (cntl->response()->GetDescriptor() != RedisResponse::descriptor()) {
            cntl->SetFailed(ERESPONSE, "Must be RedisResponse");
        } else {
            // We work around ParseFrom of pb which is just a placeholder.
            if (msg->response.reply_size() != (int)accessor.pipelined_count()) {
                cntl->SetFailed(ERESPONSE, "pipelined_count=%d of response does "
                                      "not equal request's=%d",
                                      msg->response.reply_size(), accessor.pipelined_count());
            }
            ((RedisResponse*)cntl->response())->Swap(&msg->response);
            if (FLAGS_redis_verbose) {
                LOG(INFO) << "\n[REDIS RESPONSE] "
                          << *((RedisResponse*)cntl->response());
            }
        }
    } // silently ignore the response.

    // Unlocks correlation_id inside. Revert controller's
    // error code if it version check of `cid' fails
    msg.reset();  // optional, just release resourse ASAP
    accessor.OnResponse(cid, saved_error);
}

void ProcessRedisRequest(InputMessageBase* msg_base) { }

void SerializeRedisRequest(butil::IOBuf* buf,
                           Controller* cntl,
                           const google::protobuf::Message* request) {
    if (request == NULL) {
        return cntl->SetFailed(EREQUEST, "request is NULL");
    }
    if (request->GetDescriptor() != RedisRequest::descriptor()) {
        return cntl->SetFailed(EREQUEST, "The request is not a RedisRequest");
    }
    const RedisRequest* rr = (const RedisRequest*)request;
    // We work around SerializeTo of pb which is just a placeholder.
    if (!rr->SerializeTo(buf)) {
        return cntl->SetFailed(EREQUEST, "Fail to serialize RedisRequest");
    }
    ControllerPrivateAccessor(cntl).set_pipelined_count(rr->command_size());
    if (FLAGS_redis_verbose) {
        LOG(INFO) << "\n[REDIS REQUEST] " << *rr;
    }
}

void PackRedisRequest(butil::IOBuf* buf,
                      SocketMessage**,
                      uint64_t /*correlation_id*/,
                      const google::protobuf::MethodDescriptor*,
                      Controller* cntl,
                      const butil::IOBuf& request,
                      const Authenticator* auth) {
    if (auth) {
        std::string auth_str;
        if (auth->GenerateCredential(&auth_str) != 0) {
            return cntl->SetFailed(EREQUEST, "Fail to generate credential");
        }
        buf->append(auth_str);
        ControllerPrivateAccessor(cntl).add_with_auth();
    } else {
        ControllerPrivateAccessor(cntl).clear_with_auth();
    }

    buf->append(request);
}

const std::string& GetRedisMethodName(
    const google::protobuf::MethodDescriptor*,
    const Controller*) {
    const static std::string REDIS_SERVER_STR = "redis-server";
    return REDIS_SERVER_STR;
}

}  // namespace policy
} // namespace brpc
<commit_msg>fix ParseRedisMessage: 'use after free'<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

//          Jiashun Zhu(zhujiashun2010@gmail.com)

#include <google/protobuf/descriptor.h>         // MethodDescriptor
#include <google/protobuf/message.h>            // Message
#include <gflags/gflags.h>
#include "butil/logging.h"                       // LOG()
#include "butil/time.h"
#include "butil/iobuf.h"                         // butil::IOBuf
#include "brpc/controller.h"               // Controller
#include "brpc/details/controller_private_accessor.h"
#include "brpc/socket.h"                   // Socket
#include "brpc/server.h"                   // Server
#include "brpc/details/server_private_accessor.h"
#include "brpc/span.h"
#include "brpc/redis.h"
#include "brpc/redis_command.h"
#include "brpc/policy/redis_protocol.h"

namespace brpc {

DECLARE_bool(enable_rpcz);
DECLARE_bool(usercode_in_pthread);

namespace policy {

DEFINE_bool(redis_verbose, false,
            "[DEBUG] Print EVERY redis request/response");

struct InputResponse : public InputMessageBase {
    bthread_id_t id_wait;
    RedisResponse response;

    // @InputMessageBase
    void DestroyImpl() {
        delete this;
    }
};

// This class is as parsing_context in socket.
class RedisConnContext : public Destroyable  {
public:
    explicit RedisConnContext(const RedisService* rs)
        : redis_service(rs)
        , batched_size(0) {}

    ~RedisConnContext();
    // @Destroyable
    void Destroy() override;

    const RedisService* redis_service;
    // If user starts a transaction, transaction_handler indicates the
    // handler pointer that runs the transaction command.
    std::unique_ptr<RedisCommandHandler> transaction_handler;
    // >0 if command handler is run in batched mode.
    int batched_size;

    RedisCommandParser parser;
    butil::Arena arena;
};

int ConsumeCommand(RedisConnContext* ctx,
                   const std::vector<butil::StringPiece>& commands,
                   bool flush_batched,
                   butil::IOBufAppender* appender) {
    RedisReply output(&ctx->arena);
    RedisCommandHandlerResult result = REDIS_CMD_HANDLED;
    if (ctx->transaction_handler) {
        result = ctx->transaction_handler->Run(commands, &output, flush_batched);
        if (result == REDIS_CMD_HANDLED) {
            ctx->transaction_handler.reset(NULL);
        } else if (result == REDIS_CMD_BATCHED) {
            LOG(ERROR) << "BATCHED should not be returned by a transaction handler.";
            return -1;
        }
    } else {
        RedisCommandHandler* ch = ctx->redis_service->FindCommandHandler(commands[0].as_string());
        if (!ch) {
            char buf[64];
            snprintf(buf, sizeof(buf), "ERR unknown command `%s`", commands[0].as_string().c_str());
            output.SetError(buf);
        } else {
            result = ch->Run(commands, &output, flush_batched);
            if (result == REDIS_CMD_CONTINUE) {
                if (ctx->batched_size != 0) {
                    LOG(ERROR) << "CONTINUE should not be returned in a batched process.";
                    return -1;
                }
                ctx->transaction_handler.reset(ch->NewTransactionHandler());
            } else if (result == REDIS_CMD_BATCHED) {
                ctx->batched_size++;
            }
        }
    }
    if (result == REDIS_CMD_HANDLED) {
        if (ctx->batched_size) {
            if ((int)output.size() != (ctx->batched_size + 1)) {
                LOG(ERROR) << "reply array size can't be matched with batched size, "
                    << " expected=" << ctx->batched_size + 1 << " actual=" << output.size();
                return -1;
            }
            for (int i = 0; i < (int)output.size(); ++i) {
                output[i].SerializeTo(appender);
            }
            ctx->batched_size = 0;
        } else {
            output.SerializeTo(appender);
        }
    } else if (result == REDIS_CMD_CONTINUE) {
        output.SerializeTo(appender);
    } else if (result == REDIS_CMD_BATCHED) {
        // just do nothing and wait handler to return OK.
    } else {
        LOG(ERROR) << "unknown status=" << result;
        return -1;
    }
    return 0;
}

// ========== impl of RedisConnContext ==========

RedisConnContext::~RedisConnContext() { }

void RedisConnContext::Destroy() {
    delete this;
}

// ========== impl of RedisConnContext ==========

ParseResult ParseRedisMessage(butil::IOBuf* source, Socket* socket,
                              bool read_eof, const void* arg) {
    if (read_eof || source->empty()) {
        return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
    }
    const Server* server = static_cast<const Server*>(arg);
    if (server) {
        const RedisService* const rs = server->options().redis_service;
        if (!rs) {
            return MakeParseError(PARSE_ERROR_TRY_OTHERS);
        }
        RedisConnContext* ctx = static_cast<RedisConnContext*>(socket->parsing_context());
        if (ctx == NULL) {
            ctx = new RedisConnContext(rs);
            socket->reset_parsing_context(ctx);
        }
        std::vector<butil::StringPiece> current_commands;
        butil::IOBufAppender appender;
        ParseError err = PARSE_OK;

        err = ctx->parser.Consume(*source, &current_commands, &ctx->arena);
        if (err != PARSE_OK) {
            return MakeParseError(err);
        }
        while (true) {
            std::vector<butil::StringPiece> next_commands;
            err = ctx->parser.Consume(*source, &next_commands, &ctx->arena);
            if (err != PARSE_OK) {
                break;
            }
            if (ConsumeCommand(ctx, current_commands, false, &appender) != 0) {
                return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG);
            }
            current_commands.swap(next_commands);
        }
        if (ConsumeCommand(ctx, current_commands,
                    true /*must be the last message*/, &appender) != 0) {
            return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG);
        }
        butil::IOBuf sendbuf;
        appender.move_to(sendbuf);
        CHECK(!sendbuf.empty());
        Socket::WriteOptions wopt;
        wopt.ignore_eovercrowded = true;
        LOG_IF(WARNING, socket->Write(&sendbuf, &wopt) != 0)
            << "Fail to send redis reply";
        if (err != PARSE_ERROR_NOT_ENOUGH_DATA) {
            ctx->arena.clear();
        }
        return MakeParseError(err);
    } else {
        // NOTE(gejun): PopPipelinedInfo() is actually more contended than what
        // I thought before. The Socket._pipeline_q is a SPSC queue pushed before
        // sending and popped when response comes back, being protected by a
        // mutex. Previously the mutex is shared with Socket._id_wait_list. When
        // 200 bthreads access one redis-server, ~1.5s in total is spent on
        // contention in 10-second duration. If the mutex is separated, the time
        // drops to ~0.25s. I further replaced PeekPipelinedInfo() with
        // GivebackPipelinedInfo() to lock only once(when receiving response)
        // in most cases, and the time decreases to ~0.14s.
        PipelinedInfo pi;
        if (!socket->PopPipelinedInfo(&pi)) {
            LOG(WARNING) << "No corresponding PipelinedInfo in socket";
            return MakeParseError(PARSE_ERROR_TRY_OTHERS);
        }

        do {
            InputResponse* msg = static_cast<InputResponse*>(socket->parsing_context());
            if (msg == NULL) {
                msg = new InputResponse;
                socket->reset_parsing_context(msg);
            }

            const int consume_count = (pi.with_auth ? 1 : pi.count);

            ParseError err = msg->response.ConsumePartialIOBuf(*source, consume_count);
            if (err != PARSE_OK) {
                socket->GivebackPipelinedInfo(pi);
                return MakeParseError(err);
            }

            if (pi.with_auth) {
                if (msg->response.reply_size() != 1 ||
                    !(msg->response.reply(0).type() == brpc::REDIS_REPLY_STATUS &&
                      msg->response.reply(0).data().compare("OK") == 0)) {
                    LOG(ERROR) << "Redis Auth failed: " << msg->response;
                    return MakeParseError(PARSE_ERROR_NO_RESOURCE,
                                          "Fail to authenticate with Redis");
                }

                DestroyingPtr<InputResponse> auth_msg(
                     static_cast<InputResponse*>(socket->release_parsing_context()));
                pi.with_auth = false;
                continue;
            }

            CHECK_EQ((uint32_t)msg->response.reply_size(), pi.count);
            msg->id_wait = pi.id_wait;
            socket->release_parsing_context();
            return MakeMessage(msg);
        } while(true);
    }

    return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG);
}

void ProcessRedisResponse(InputMessageBase* msg_base) {
    const int64_t start_parse_us = butil::cpuwide_time_us();
    DestroyingPtr<InputResponse> msg(static_cast<InputResponse*>(msg_base));

    const bthread_id_t cid = msg->id_wait;
    Controller* cntl = NULL;
    const int rc = bthread_id_lock(cid, (void**)&cntl);
    if (rc != 0) {
        LOG_IF(ERROR, rc != EINVAL && rc != EPERM)
            << "Fail to lock correlation_id=" << cid << ": " << berror(rc);
        return;
    }

    ControllerPrivateAccessor accessor(cntl);
    Span* span = accessor.span();
    if (span) {
        span->set_base_real_us(msg->base_real_us());
        span->set_received_us(msg->received_us());
        span->set_response_size(msg->response.ByteSize());
        span->set_start_parse_us(start_parse_us);
    }
    const int saved_error = cntl->ErrorCode();
    if (cntl->response() != NULL) {
        if (cntl->response()->GetDescriptor() != RedisResponse::descriptor()) {
            cntl->SetFailed(ERESPONSE, "Must be RedisResponse");
        } else {
            // We work around ParseFrom of pb which is just a placeholder.
            if (msg->response.reply_size() != (int)accessor.pipelined_count()) {
                cntl->SetFailed(ERESPONSE, "pipelined_count=%d of response does "
                                      "not equal request's=%d",
                                      msg->response.reply_size(), accessor.pipelined_count());
            }
            ((RedisResponse*)cntl->response())->Swap(&msg->response);
            if (FLAGS_redis_verbose) {
                LOG(INFO) << "\n[REDIS RESPONSE] "
                          << *((RedisResponse*)cntl->response());
            }
        }
    } // silently ignore the response.

    // Unlocks correlation_id inside. Revert controller's
    // error code if it version check of `cid' fails
    msg.reset();  // optional, just release resourse ASAP
    accessor.OnResponse(cid, saved_error);
}

void ProcessRedisRequest(InputMessageBase* msg_base) { }

void SerializeRedisRequest(butil::IOBuf* buf,
                           Controller* cntl,
                           const google::protobuf::Message* request) {
    if (request == NULL) {
        return cntl->SetFailed(EREQUEST, "request is NULL");
    }
    if (request->GetDescriptor() != RedisRequest::descriptor()) {
        return cntl->SetFailed(EREQUEST, "The request is not a RedisRequest");
    }
    const RedisRequest* rr = (const RedisRequest*)request;
    // We work around SerializeTo of pb which is just a placeholder.
    if (!rr->SerializeTo(buf)) {
        return cntl->SetFailed(EREQUEST, "Fail to serialize RedisRequest");
    }
    ControllerPrivateAccessor(cntl).set_pipelined_count(rr->command_size());
    if (FLAGS_redis_verbose) {
        LOG(INFO) << "\n[REDIS REQUEST] " << *rr;
    }
}

void PackRedisRequest(butil::IOBuf* buf,
                      SocketMessage**,
                      uint64_t /*correlation_id*/,
                      const google::protobuf::MethodDescriptor*,
                      Controller* cntl,
                      const butil::IOBuf& request,
                      const Authenticator* auth) {
    if (auth) {
        std::string auth_str;
        if (auth->GenerateCredential(&auth_str) != 0) {
            return cntl->SetFailed(EREQUEST, "Fail to generate credential");
        }
        buf->append(auth_str);
        ControllerPrivateAccessor(cntl).add_with_auth();
    } else {
        ControllerPrivateAccessor(cntl).clear_with_auth();
    }

    buf->append(request);
}

const std::string& GetRedisMethodName(
    const google::protobuf::MethodDescriptor*,
    const Controller*) {
    const static std::string REDIS_SERVER_STR = "redis-server";
    return REDIS_SERVER_STR;
}

}  // namespace policy
} // namespace brpc
<|endoftext|>


<commit_before>#include "TweeScanner.h"

using namespace Twee;<commit_msg>remove unnecessary TweeScanner implementation file<commit_after><|endoftext|>


<commit_before>#ifndef __MAPNIK_VECTOR_TILE_LAYER_H__
#define __MAPNIK_VECTOR_TILE_LAYER_H__

// mapnik-vector-tile
#include "vector_tile_config.hpp"

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/map.hpp>
#include <mapnik/projection.hpp>
#include <mapnik/proj_transform.hpp>
#include <mapnik/scale_denominator.hpp>
#include <mapnik/value.hpp>
#include <mapnik/view_transform.hpp>

// protozero
#include <protozero/pbf_writer.hpp>

// std
#include <map>
#include <unordered_map>
#include <utility>

namespace mapnik
{

namespace vector_tile_impl
{

struct layer_builder_pbf
{
    typedef std::map<std::string, unsigned> keys_container;
    typedef std::unordered_map<mapnik::value, unsigned> values_container;

    std::string feature_str;
    keys_container keys;
    values_container values;
    protozero::pbf_writer layer_writer;
    bool empty;
    bool painted;

    layer_builder_pbf(std::string const & name, std::uint32_t extent, std::string & layer_buffer)
        : feature_str(),
          keys(),
          values(),
          layer_writer(layer_buffer),
          empty(true),
          painted(false)
    {
        layer_writer.add_uint32(Layer_Encoding::VERSION, 2);
        layer_writer.add_string(Layer_Encoding::NAME, name);
        layer_writer.add_uint32(Layer_Encoding::EXTENT, extent);
    }

    void make_painted()
    {
        painted = true;
    }

    protozero::pbf_writer get_feature_writer()
    {
        feature_str.clear();
        return protozero::pbf_writer(feature_str);
    }

    MAPNIK_VECTOR_INLINE void add_feature(protozero::pbf_writer & feature_writer,
                                          mapnik::feature_impl const& mapnik_feature);
};

class tile_layer
{
private:
    bool valid_;
    mapnik::datasource_ptr ds_;
    mapnik::projection target_proj_;
    mapnik::projection source_proj_;
    mapnik::proj_transform prj_trans_;
    std::string buffer_;
    std::string name_;
    std::uint32_t layer_extent_;
    mapnik::box2d<double> target_buffered_extent_;
    mapnik::box2d<double> source_buffered_extent_;
    mapnik::query query_;
    mapnik::view_transform view_trans_;
    bool empty_;
    bool painted_;

public:
    tile_layer(mapnik::Map const& map,
               mapnik::layer const& lay,
               mapnik::box2d<double> const& tile_extent_bbox,
               std::uint32_t tile_size,
               std::int32_t buffer_size,
               double scale_factor,
               double scale_denom,
               int offset_x,
               int offset_y)
        : valid_(true),
          ds_(lay.datasource()),
          target_proj_(map.srs(), true),
          source_proj_(lay.srs(), true),
          prj_trans_(target_proj_, source_proj_),
          buffer_(),
          name_(lay.name()),
          layer_extent_(calc_extent(tile_size)),
          target_buffered_extent_(calc_target_buffered_extent(tile_extent_bbox, buffer_size, lay, map)),
          source_buffered_extent_(calc_source_buffered_extent()),
          query_(calc_query(scale_factor, scale_denom, tile_extent_bbox, lay)),
          view_trans_(layer_extent_, layer_extent_, tile_extent_bbox, offset_x, offset_y),
          empty_(true),
          painted_(false)
    {
        buffer_.reserve(35);
    }

    tile_layer(tile_layer && rhs)
        : valid_(std::move(rhs.valid_)),
          ds_(std::move(rhs.ds_)),
          target_proj_(std::move(rhs.target_proj_)),
          source_proj_(std::move(rhs.source_proj_)),
          prj_trans_(target_proj_, source_proj_),
          buffer_(std::move(rhs.buffer_)),
          name_(std::move(rhs.name_)),
          layer_extent_(std::move(rhs.layer_extent_)),
          target_buffered_extent_(std::move(rhs.target_buffered_extent_)),
          source_buffered_extent_(std::move(rhs.source_buffered_extent_)),
          query_(std::move(rhs.query_)),
          view_trans_(std::move(rhs.view_trans_)),
          empty_(std::move(rhs.empty_)),
          painted_(std::move(rhs.painted_)) {}

    tile_layer& operator=(tile_layer&&) = default;

    tile_layer(tile_layer const& rhs) = delete;
    tile_layer& operator=(const tile_layer&) = delete;

    std::uint32_t calc_extent(std::uint32_t layer_extent)
    {
        if (!ds_)
        {
            valid_ = false;
            return 4096;
        }
        auto ds_extent = ds_->params().template get<mapnik::value_integer>("vector_layer_extent");
        if (ds_extent)
        {
            layer_extent = *ds_extent;
        }
        if (layer_extent == 0)
        {
            valid_ = false;
            layer_extent = 4096;
        }
        return layer_extent;
    }

    mapnik::box2d<double> calc_target_buffered_extent(mapnik::box2d<double> const& tile_extent_bbox,
                                               std::int32_t buffer_size,
                                               mapnik::layer const& lay,
                                               mapnik::Map const& map) const
    {
        mapnik::box2d<double> ext(tile_extent_bbox);
        double scale = ext.width() / layer_extent_;
        double buffer_padding = 2.0 * scale;
        boost::optional<int> layer_buffer_size = lay.buffer_size();
        if (layer_buffer_size) // if layer overrides buffer size, use this value to compute buffered extent
        {
            buffer_padding *= *layer_buffer_size;
        }
        else
        {
            buffer_padding *= buffer_size;
        }
        double buffered_width = ext.width() + buffer_padding;
        double buffered_height = ext.height() + buffer_padding;
        if (buffered_width < 0.0)
        {
            buffered_width = 0.0;
        }
        if (buffered_height < 0.0)
        {
            buffered_height = 0.0;
        }
        ext.width(buffered_width);
        ext.height(buffered_height);

        boost::optional<box2d<double> > const& maximum_extent = map.maximum_extent();
        if (maximum_extent)
        {
            ext.clip(*maximum_extent);
        }
        return ext;
    }

    mapnik::box2d<double> calc_source_buffered_extent()
    {
        mapnik::box2d<double> new_extent(target_buffered_extent_);
        if (!prj_trans_.forward(new_extent, PROJ_ENVELOPE_POINTS))
        {
            // this modifies the layer_ext by clipping to the buffered_ext
            valid_ = false;
        }
        return new_extent;
    }

    mapnik::query calc_query(double scale_factor,
                             double scale_denom,
                             mapnik::box2d<double> const& tile_extent_bbox,
                             mapnik::layer const& lay)
    {
        // Adjust the scale denominator if required
        if (scale_denom <= 0.0)
        {
            double scale = tile_extent_bbox.width() / layer_extent_;
            scale_denom = mapnik::scale_denominator(scale, target_proj_.is_geographic());
        }
        scale_denom *= scale_factor;
        if (!lay.visible(scale_denom))
        {
            valid_ = false;
        }

        mapnik::box2d<double> query_extent(lay.envelope()); // source projection

        // first, try intersection of map extent forward projected into layer srs
        if (source_buffered_extent_.intersects(query_extent))
        {
            // this modifies the query_extent by clipping to the buffered_ext
            query_extent.clip(source_buffered_extent_);
        }
        // if no intersection and projections are also equal, early return
        else if (prj_trans_.equal())
        {
            valid_ = false;
        }
        // next try intersection of layer extent back projected into map srs
        else if (prj_trans_.backward(query_extent, PROJ_ENVELOPE_POINTS) && target_buffered_extent_.intersects(query_extent))
        {
            query_extent.clip(target_buffered_extent_);
            // forward project layer extent back into native projection
            if (!prj_trans_.forward(query_extent, PROJ_ENVELOPE_POINTS))
            {
                throw std::runtime_error("vector_tile_processor: layer extent did not repoject back to map projection");
            }
        }
        else
        {
            // if no intersection then nothing to do for layer
            valid_ = false;
        }
        double qw = tile_extent_bbox.width() > 0 ? tile_extent_bbox.width() : 1;
        double qh = tile_extent_bbox.height() > 0 ? tile_extent_bbox.height() : 1;
        mapnik::query::resolution_type res(layer_extent_ / qw, layer_extent_ / qh);
        mapnik::query q(query_extent, res, scale_denom, tile_extent_bbox);
        if (ds_)
        {
            mapnik::layer_descriptor lay_desc = ds_->get_descriptor();
            for (mapnik::attribute_descriptor const& desc : lay_desc.get_descriptors())
            {
                q.add_property_name(desc.get_name());
            }
        }
        return q;
    }

    mapnik::datasource_ptr get_ds() const
    {
        return ds_;
    }

    mapnik::featureset_ptr get_features() const
    {
        return ds_->features(query_);
    }

    mapnik::view_transform const& get_view_transform() const
    {
        return view_trans_;
    }

    mapnik::proj_transform const& get_proj_transform() const
    {
        return prj_trans_;
    }

    mapnik::box2d<double> const& get_source_buffered_extent() const
    {
        return source_buffered_extent_;
    }

    mapnik::box2d<double> const& get_target_buffered_extent() const
    {
        return target_buffered_extent_;
    }

    bool is_valid() const
    {
        return valid_;
    }

    std::uint32_t layer_extent() const
    {
        return layer_extent_;
    }

    std::string const& name() const
    {
        return name_;
    }

    void name(std::string const& val)
    {
        name_ = val;
    }

    bool is_empty() const
    {
        return empty_;
    }

    bool is_painted() const
    {
        return painted_;
    }

    void make_painted()
    {
        painted_ = true;
    }

    std::string const& get_data() const
    {
        return buffer_;
    }

    std::string & get_data()
    {
        return buffer_;
    }

    void build(layer_builder_pbf const& builder)
    {
        empty_ = builder.empty;
        painted_ = builder.painted;
    }
};

} // end ns vector_tile_impl

} // end ns mapnik

#if !defined(MAPNIK_VECTOR_TILE_LIBRARY)
#include "vector_tile_layer.ipp"
#endif

#endif // __MAPNIK_VECTOR_TILE_LAYER_H__
<commit_msg>Update so that layer buffer size is 16 times the value previously because originally the buffer size was based on a tile width and height of 256 rather then the actual layer extent size.<commit_after>#ifndef __MAPNIK_VECTOR_TILE_LAYER_H__
#define __MAPNIK_VECTOR_TILE_LAYER_H__

// mapnik-vector-tile
#include "vector_tile_config.hpp"

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/map.hpp>
#include <mapnik/projection.hpp>
#include <mapnik/proj_transform.hpp>
#include <mapnik/scale_denominator.hpp>
#include <mapnik/value.hpp>
#include <mapnik/view_transform.hpp>

// protozero
#include <protozero/pbf_writer.hpp>

// std
#include <map>
#include <unordered_map>
#include <utility>

namespace mapnik
{

namespace vector_tile_impl
{

struct layer_builder_pbf
{
    typedef std::map<std::string, unsigned> keys_container;
    typedef std::unordered_map<mapnik::value, unsigned> values_container;

    std::string feature_str;
    keys_container keys;
    values_container values;
    protozero::pbf_writer layer_writer;
    bool empty;
    bool painted;

    layer_builder_pbf(std::string const & name, std::uint32_t extent, std::string & layer_buffer)
        : feature_str(),
          keys(),
          values(),
          layer_writer(layer_buffer),
          empty(true),
          painted(false)
    {
        layer_writer.add_uint32(Layer_Encoding::VERSION, 2);
        layer_writer.add_string(Layer_Encoding::NAME, name);
        layer_writer.add_uint32(Layer_Encoding::EXTENT, extent);
    }

    void make_painted()
    {
        painted = true;
    }

    protozero::pbf_writer get_feature_writer()
    {
        feature_str.clear();
        return protozero::pbf_writer(feature_str);
    }

    MAPNIK_VECTOR_INLINE void add_feature(protozero::pbf_writer & feature_writer,
                                          mapnik::feature_impl const& mapnik_feature);
};

class tile_layer
{
private:
    bool valid_;
    mapnik::datasource_ptr ds_;
    mapnik::projection target_proj_;
    mapnik::projection source_proj_;
    mapnik::proj_transform prj_trans_;
    std::string buffer_;
    std::string name_;
    std::uint32_t layer_extent_;
    mapnik::box2d<double> target_buffered_extent_;
    mapnik::box2d<double> source_buffered_extent_;
    mapnik::query query_;
    mapnik::view_transform view_trans_;
    bool empty_;
    bool painted_;

public:
    tile_layer(mapnik::Map const& map,
               mapnik::layer const& lay,
               mapnik::box2d<double> const& tile_extent_bbox,
               std::uint32_t tile_size,
               std::int32_t buffer_size,
               double scale_factor,
               double scale_denom,
               int offset_x,
               int offset_y)
        : valid_(true),
          ds_(lay.datasource()),
          target_proj_(map.srs(), true),
          source_proj_(lay.srs(), true),
          prj_trans_(target_proj_, source_proj_),
          buffer_(),
          name_(lay.name()),
          layer_extent_(calc_extent(tile_size)),
          target_buffered_extent_(calc_target_buffered_extent(tile_extent_bbox, buffer_size, lay, map)),
          source_buffered_extent_(calc_source_buffered_extent()),
          query_(calc_query(scale_factor, scale_denom, tile_extent_bbox, lay)),
          view_trans_(layer_extent_, layer_extent_, tile_extent_bbox, offset_x, offset_y),
          empty_(true),
          painted_(false)
    {
        buffer_.reserve(35);
    }

    tile_layer(tile_layer && rhs)
        : valid_(std::move(rhs.valid_)),
          ds_(std::move(rhs.ds_)),
          target_proj_(std::move(rhs.target_proj_)),
          source_proj_(std::move(rhs.source_proj_)),
          prj_trans_(target_proj_, source_proj_),
          buffer_(std::move(rhs.buffer_)),
          name_(std::move(rhs.name_)),
          layer_extent_(std::move(rhs.layer_extent_)),
          target_buffered_extent_(std::move(rhs.target_buffered_extent_)),
          source_buffered_extent_(std::move(rhs.source_buffered_extent_)),
          query_(std::move(rhs.query_)),
          view_trans_(std::move(rhs.view_trans_)),
          empty_(std::move(rhs.empty_)),
          painted_(std::move(rhs.painted_)) {}

    tile_layer& operator=(tile_layer&&) = default;

    tile_layer(tile_layer const& rhs) = delete;
    tile_layer& operator=(const tile_layer&) = delete;

    std::uint32_t calc_extent(std::uint32_t layer_extent)
    {
        if (!ds_)
        {
            valid_ = false;
            return 4096;
        }
        auto ds_extent = ds_->params().template get<mapnik::value_integer>("vector_layer_extent");
        if (ds_extent)
        {
            layer_extent = *ds_extent;
        }
        if (layer_extent == 0)
        {
            valid_ = false;
            layer_extent = 4096;
        }
        return layer_extent;
    }

    mapnik::box2d<double> calc_target_buffered_extent(mapnik::box2d<double> const& tile_extent_bbox,
                                               std::int32_t buffer_size,
                                               mapnik::layer const& lay,
                                               mapnik::Map const& map) const
    {
        mapnik::box2d<double> ext(tile_extent_bbox);
        double scale = ext.width() / layer_extent_;
        double buffer_padding = 2.0 * scale;
        boost::optional<int> layer_buffer_size = lay.buffer_size();
        if (layer_buffer_size) // if layer overrides buffer size, use this value to compute buffered extent
        {
            buffer_padding *= (*layer_buffer_size) * 16.0;
        }
        else
        {
            buffer_padding *= buffer_size;
        }
        double buffered_width = ext.width() + buffer_padding;
        double buffered_height = ext.height() + buffer_padding;
        if (buffered_width < 0.0)
        {
            buffered_width = 0.0;
        }
        if (buffered_height < 0.0)
        {
            buffered_height = 0.0;
        }
        ext.width(buffered_width);
        ext.height(buffered_height);

        boost::optional<box2d<double> > const& maximum_extent = map.maximum_extent();
        if (maximum_extent)
        {
            ext.clip(*maximum_extent);
        }
        return ext;
    }

    mapnik::box2d<double> calc_source_buffered_extent()
    {
        mapnik::box2d<double> new_extent(target_buffered_extent_);
        if (!prj_trans_.forward(new_extent, PROJ_ENVELOPE_POINTS))
        {
            // this modifies the layer_ext by clipping to the buffered_ext
            valid_ = false;
        }
        return new_extent;
    }

    mapnik::query calc_query(double scale_factor,
                             double scale_denom,
                             mapnik::box2d<double> const& tile_extent_bbox,
                             mapnik::layer const& lay)
    {
        // Adjust the scale denominator if required
        if (scale_denom <= 0.0)
        {
            double scale = tile_extent_bbox.width() / layer_extent_;
            scale_denom = mapnik::scale_denominator(scale, target_proj_.is_geographic());
        }
        scale_denom *= scale_factor;
        if (!lay.visible(scale_denom))
        {
            valid_ = false;
        }

        mapnik::box2d<double> query_extent(lay.envelope()); // source projection

        // first, try intersection of map extent forward projected into layer srs
        if (source_buffered_extent_.intersects(query_extent))
        {
            // this modifies the query_extent by clipping to the buffered_ext
            query_extent.clip(source_buffered_extent_);
        }
        // if no intersection and projections are also equal, early return
        else if (prj_trans_.equal())
        {
            valid_ = false;
        }
        // next try intersection of layer extent back projected into map srs
        else if (prj_trans_.backward(query_extent, PROJ_ENVELOPE_POINTS) && target_buffered_extent_.intersects(query_extent))
        {
            query_extent.clip(target_buffered_extent_);
            // forward project layer extent back into native projection
            if (!prj_trans_.forward(query_extent, PROJ_ENVELOPE_POINTS))
            {
                throw std::runtime_error("vector_tile_processor: layer extent did not repoject back to map projection");
            }
        }
        else
        {
            // if no intersection then nothing to do for layer
            valid_ = false;
        }
        double qw = tile_extent_bbox.width() > 0 ? tile_extent_bbox.width() : 1;
        double qh = tile_extent_bbox.height() > 0 ? tile_extent_bbox.height() : 1;
        mapnik::query::resolution_type res(layer_extent_ / qw, layer_extent_ / qh);
        mapnik::query q(query_extent, res, scale_denom, tile_extent_bbox);
        if (ds_)
        {
            mapnik::layer_descriptor lay_desc = ds_->get_descriptor();
            for (mapnik::attribute_descriptor const& desc : lay_desc.get_descriptors())
            {
                q.add_property_name(desc.get_name());
            }
        }
        return q;
    }

    mapnik::datasource_ptr get_ds() const
    {
        return ds_;
    }

    mapnik::featureset_ptr get_features() const
    {
        return ds_->features(query_);
    }

    mapnik::view_transform const& get_view_transform() const
    {
        return view_trans_;
    }

    mapnik::proj_transform const& get_proj_transform() const
    {
        return prj_trans_;
    }

    mapnik::box2d<double> const& get_source_buffered_extent() const
    {
        return source_buffered_extent_;
    }

    mapnik::box2d<double> const& get_target_buffered_extent() const
    {
        return target_buffered_extent_;
    }

    bool is_valid() const
    {
        return valid_;
    }

    std::uint32_t layer_extent() const
    {
        return layer_extent_;
    }

    std::string const& name() const
    {
        return name_;
    }

    void name(std::string const& val)
    {
        name_ = val;
    }

    bool is_empty() const
    {
        return empty_;
    }

    bool is_painted() const
    {
        return painted_;
    }

    void make_painted()
    {
        painted_ = true;
    }

    std::string const& get_data() const
    {
        return buffer_;
    }

    std::string & get_data()
    {
        return buffer_;
    }

    void build(layer_builder_pbf const& builder)
    {
        empty_ = builder.empty;
        painted_ = builder.painted;
    }
};

} // end ns vector_tile_impl

} // end ns mapnik

#if !defined(MAPNIK_VECTOR_TILE_LIBRARY)
#include "vector_tile_layer.ipp"
#endif

#endif // __MAPNIK_VECTOR_TILE_LAYER_H__
<|endoftext|>


<commit_before>#include <fstream>
#include <iostream>
#include <string>

#include <Poco/Clock.h>
#include <Poco/Error.h>
#include <Poco/Exception.h>
#include <Poco/File.h>
#include <Poco/FileStream.h>
#include <Poco/Logger.h>
#include <Poco/LogStream.h>
#include <Poco/PipeStream.h>
#include <Poco/Process.h>
#include <Poco/RegularExpression.h>
#include <Poco/StreamCopier.h>
#include <Poco/String.h>
#include <Poco/TemporaryFile.h>
#include <Poco/Thread.h>

#include "di/Injectable.h"
#include "cassandra/CassandraStartup.h"

BEEEON_OBJECT_BEGIN(BeeeOn, CassandraStartup)
BEEEON_OBJECT_CASTABLE(StoppableLoop)
BEEEON_OBJECT_PROPERTY("cassandraBin", &CassandraStartup::setCassandraBin)
BEEEON_OBJECT_PROPERTY("host", &CassandraStartup::setHost)
BEEEON_OBJECT_PROPERTY("port", &CassandraStartup::setPort)
BEEEON_OBJECT_PROPERTY("rpcPort", &CassandraStartup::setRPCPort)
BEEEON_OBJECT_PROPERTY("jmxPort", &CassandraStartup::setJMXPort)
BEEEON_OBJECT_PROPERTY("clusterDir", &CassandraStartup::setClusterDir)
BEEEON_OBJECT_PROPERTY("clusterName", &CassandraStartup::setClusterName)
BEEEON_OBJECT_PROPERTY("clusterInit", &CassandraStartup::setClusterInit)
BEEEON_OBJECT_PROPERTY("stopTimeout", &CassandraStartup::setStopTimeout)
BEEEON_OBJECT_HOOK("done", &CassandraStartup::initCluster)
BEEEON_OBJECT_END(BeeeOn, CassandraStartup)

using namespace std;
using namespace Poco;
using namespace BeeeOn;

const string YAML_FILE = "cassandra.yaml";
const string PID_FILE = "pidfile";

CassandraStartup::CassandraStartup():
	m_cassandraBin("cassandra"),
	m_host("127.0.0.1"),
	m_port(59042),
	m_rpcPort(59160),
	m_jmxPort(57199),
	m_clusterDir(TemporaryFile::tempName(), "cassandra"),
	m_clusterName("Test Cluster"),
	m_clusterInit(false),
	m_stopTimeout(2 * Timespan::SECONDS),
	m_poutGrabber(*this, &CassandraStartup::grabPout)
{
}

CassandraStartup::~CassandraStartup()
{
	try {
		deleteCluster();
	}
	BEEEON_CATCH_CHAIN(logger())
}

void CassandraStartup::setCassandraBin(const string &file)
{
	m_cassandraBin = file;
}

void CassandraStartup::setHost(const string &host)
{
	m_host = host;
}

void CassandraStartup::setPort(const int port)
{
	if (port < 0)
		throw InvalidArgumentException("port must be non-negative");

	m_port = port;
}

void CassandraStartup::setRPCPort(const int port)
{
	if (port < 0)
		throw InvalidArgumentException("rpcPort must be non-negative");

	m_rpcPort = port;
}

void CassandraStartup::setJMXPort(const int port)
{
	if (port < 0)
		throw InvalidArgumentException("jmxPort must be non-negative");

	m_jmxPort = port;
}

void CassandraStartup::setClusterDir(const string &dir)
{
	m_clusterDir = dir;
}

void CassandraStartup::setClusterName(const string &name)
{
	m_clusterName = name;
}

void CassandraStartup::setClusterInit(bool init)
{
	m_clusterInit = init;
}

void CassandraStartup::setStopTimeout(const Timespan &timeout)
{
	if (timeout < 0)
		throw InvalidArgumentException("stopTimeout must be non-negative");

	m_stopTimeout = timeout;
}

void CassandraStartup::initCluster()
{
	if (!m_clusterInit)
		return;

	File dir(m_clusterDir);
	dir.createDirectories();

	const Path yaml(m_clusterDir, YAML_FILE);
	ofstream config(yaml.toString());
	dumpConfigFile(config);

	logger().notice("created the cassandra cluster at " + m_clusterDir.toString());
}

void CassandraStartup::deleteCluster()
{
	if (!m_clusterInit)
		return;

	logger().notice("deleting the cassandra cluster at " + m_clusterDir.toString());

	File dir(m_clusterDir);

	try {
		dir.remove(true);
	}
	BEEEON_CATCH_CHAIN(logger())
}

void CassandraStartup::start()
{
	logger().notice("starting cassandra...");

	FastMutex::ScopedLock guard(m_lock);

	File clusterFile(m_clusterDir);
	if (!clusterFile.exists()) {
		throw IllegalStateException(
			"directory " + m_clusterDir.toString() + " does not exists");
	}
	if (!clusterFile.isDirectory()) {
		throw IllegalStateException(
			"path " + m_clusterDir.toString() + " is not a directory");
	}
	if (!clusterFile.canWrite()) {
		throw IllegalStateException(
			"directory " + m_clusterDir.toString() + " is not writable");
	}

	const Path yaml(m_clusterDir, YAML_FILE);

	Process::Args args;
	args.emplace_back("-f");
	args.emplace_back("-Dcassandra.config=file:///" + yaml.toString());
	args.emplace_back("-Dcassandra.logdir=" + m_clusterDir.toString());
	args.emplace_back("-Dcassandra.jmx.local.port=" + to_string(m_jmxPort));
	args.emplace_back("-Dcom.sun.management.jmxremote.authenticate=true");

	Pipe pout;
	m_handle = new ProcessHandle(Process::launch(
	        m_cassandraBin, args, m_clusterDir.toString(), nullptr, &pout, nullptr));

	if (setpgid(m_handle->id(), 0) < 0)
	        logger().error(Error::getMessage(Error::last()), __FILE__, __LINE__);

	m_pout = new Pipe(pout);
	m_poutThread.start(m_poutGrabber);

	logger().notice(
		"cassandra pid is " + to_string(m_handle->id()),
		__FILE__, __LINE__);
}

void CassandraStartup::stop()
{
	logger().information("stopping cassandra...");

	FastMutex::ScopedLock guard(m_lock, m_stopTimeout.totalMilliseconds());

	if (logger().debug()) {
		logger().debug(
			"requesting stop of cassandra " + to_string(m_handle->id()),
			__FILE__, __LINE__);
	}

	try {
		Process::requestTermination(m_handle->id());
		m_handle->wait();
	}
	catch (const NotFoundException &e) {
		logger().log(e, __FILE__, __LINE__);
	}
	catch (const Exception &e) {
		logger().log(e, __FILE__, __LINE__);
	}

	m_handle = nullptr;
	m_pout = nullptr;
}

void CassandraStartup::grabPout()
{
	const auto pid = m_handle->id();

	if (logger().debug()) {
		logger().debug(
			"grab stdout of cassandra " + to_string(pid),
			__FILE__, __LINE__);
	}

	while (!m_pout.isNull()) {
		Pipe pipe(*m_pout);
		grab(pipe, pid);
	}
}

static Message::Priority preprocess(string &line)
{
	static RegularExpression labelExpr("(INFO|DEBUG|WARN|ERROR|TRACE|FATAL)");
	Message::Priority prio = Message::PRIO_INFORMATION;

	RegularExpression::MatchVec m;
	if (labelExpr.match(line, 0, m)) {
		const auto label = line.substr(m[1].offset, m[1].length);

		if (label == "INFO")
			prio = Message::PRIO_INFORMATION;
		else if (label == "DEBUG")
			prio = Message::PRIO_DEBUG;
		else if (label == "WARN")
			prio = Message::PRIO_WARNING;
		else if (label == "ERROR")
			prio = Message::PRIO_ERROR;
		else if (label == "TRACE")
			prio = Message::PRIO_TRACE;
		else if (label == "FATAL")
			prio = Message::PRIO_FATAL;

		line = line.substr(m[0].offset + m[0].length);
		trimInPlace(line);
	}

	return prio;
}

void CassandraStartup::grab(Pipe pipe, Process::PID pid) const
{
	PipeInputStream in(pipe);
	string line;

	while (getline(in, line)) {
		Message::Priority prio = preprocess(line);

		Message msg(
			logger().name(),
			line,
			prio,
			__FILE__,
			__LINE__
		);

		msg.setPid(pid);
		logger().log(msg);
	}
}

Process::PID CassandraStartup::readPID()
{
	const Path pidPath(m_clusterDir, PID_FILE);
	FileInputStream pidFile(pidPath.toString());

	int pid = -1;
	pidFile >> pid;
	pidFile.close();

	return pid;
}

static void dumpDataDir(Path clusterDir, ostream &config)
{
	File data(Path(clusterDir, Path("data")));
	data.createDirectories();

	config << "data_file_directories:" << endl;
	config << "    - " << data.path() << endl;
}

static void dumbSeedProvider(string host, ostream &config)
{
	config << "seed_provider:" << endl;
	config << "    - class_name: org.apache.cassandra.locator.SimpleSeedProvider" << endl;
	config << "      parameters:" << endl;
	config << "          - seeds: \"" << host <<  "\"" << endl;
}

void CassandraStartup::dumpConfigFile(ostream &config) const
{
	if (logger().debug()) {
		logger().debug(
			"creating configuration files...",
			__FILE__, __LINE__);
	}

	File hints(Path(m_clusterDir, Path("hints")));
	hints.createDirectories();

	File commitLog(Path(m_clusterDir, Path("commitlog")));
	commitLog.createDirectories();

	File cache(Path(m_clusterDir, Path("saved_caches")));
	cache.createDirectories();

	map<string, string> simpleEntries = {
		{"cluster_name", "\'" + m_clusterName +  "\'"},
		{"hints_directory", hints.path()},
		{"partitioner", "org.apache.cassandra.dht.Murmur3Partitioner"},
		{"commitlog_directory", commitLog.path()},
		{"saved_caches_directory", cache.path()},
		{"commitlog_sync", "periodic"},
		{"commitlog_sync_period_in_ms", "10000"},
		{"commitlog_segment_size_in_mb", "32"},
		{"listen_address", m_host},
		{"start_native_transport", "true"},
		{"native_transport_port", to_string(m_port)},
		{"start_rpc", "false"},
		{"rpc_address", m_host},
		{"rpc_port", to_string(m_rpcPort)},
		{"endpoint_snitch", "SimpleSnitch"}
	};

	for (const auto pair : simpleEntries)
		config << pair.first << ": " << pair.second << endl;

	dumpDataDir(m_clusterDir, config);
	dumbSeedProvider(m_host, config);
}
<commit_msg>CassandraStartup: configure log dir to point under tmp CLUSTER_DIR<commit_after>#include <fstream>
#include <iostream>
#include <string>

#include <Poco/Clock.h>
#include <Poco/Error.h>
#include <Poco/Exception.h>
#include <Poco/File.h>
#include <Poco/FileStream.h>
#include <Poco/Logger.h>
#include <Poco/LogStream.h>
#include <Poco/PipeStream.h>
#include <Poco/Process.h>
#include <Poco/RegularExpression.h>
#include <Poco/StreamCopier.h>
#include <Poco/String.h>
#include <Poco/TemporaryFile.h>
#include <Poco/Thread.h>

#include "di/Injectable.h"
#include "cassandra/CassandraStartup.h"

BEEEON_OBJECT_BEGIN(BeeeOn, CassandraStartup)
BEEEON_OBJECT_CASTABLE(StoppableLoop)
BEEEON_OBJECT_PROPERTY("cassandraBin", &CassandraStartup::setCassandraBin)
BEEEON_OBJECT_PROPERTY("host", &CassandraStartup::setHost)
BEEEON_OBJECT_PROPERTY("port", &CassandraStartup::setPort)
BEEEON_OBJECT_PROPERTY("rpcPort", &CassandraStartup::setRPCPort)
BEEEON_OBJECT_PROPERTY("jmxPort", &CassandraStartup::setJMXPort)
BEEEON_OBJECT_PROPERTY("clusterDir", &CassandraStartup::setClusterDir)
BEEEON_OBJECT_PROPERTY("clusterName", &CassandraStartup::setClusterName)
BEEEON_OBJECT_PROPERTY("clusterInit", &CassandraStartup::setClusterInit)
BEEEON_OBJECT_PROPERTY("stopTimeout", &CassandraStartup::setStopTimeout)
BEEEON_OBJECT_HOOK("done", &CassandraStartup::initCluster)
BEEEON_OBJECT_END(BeeeOn, CassandraStartup)

using namespace std;
using namespace Poco;
using namespace BeeeOn;

const string YAML_FILE = "cassandra.yaml";
const string PID_FILE = "pidfile";

CassandraStartup::CassandraStartup():
	m_cassandraBin("cassandra"),
	m_host("127.0.0.1"),
	m_port(59042),
	m_rpcPort(59160),
	m_jmxPort(57199),
	m_clusterDir(TemporaryFile::tempName(), "cassandra"),
	m_clusterName("Test Cluster"),
	m_clusterInit(false),
	m_stopTimeout(2 * Timespan::SECONDS),
	m_poutGrabber(*this, &CassandraStartup::grabPout)
{
}

CassandraStartup::~CassandraStartup()
{
	try {
		deleteCluster();
	}
	BEEEON_CATCH_CHAIN(logger())
}

void CassandraStartup::setCassandraBin(const string &file)
{
	m_cassandraBin = file;
}

void CassandraStartup::setHost(const string &host)
{
	m_host = host;
}

void CassandraStartup::setPort(const int port)
{
	if (port < 0)
		throw InvalidArgumentException("port must be non-negative");

	m_port = port;
}

void CassandraStartup::setRPCPort(const int port)
{
	if (port < 0)
		throw InvalidArgumentException("rpcPort must be non-negative");

	m_rpcPort = port;
}

void CassandraStartup::setJMXPort(const int port)
{
	if (port < 0)
		throw InvalidArgumentException("jmxPort must be non-negative");

	m_jmxPort = port;
}

void CassandraStartup::setClusterDir(const string &dir)
{
	m_clusterDir = dir;
}

void CassandraStartup::setClusterName(const string &name)
{
	m_clusterName = name;
}

void CassandraStartup::setClusterInit(bool init)
{
	m_clusterInit = init;
}

void CassandraStartup::setStopTimeout(const Timespan &timeout)
{
	if (timeout < 0)
		throw InvalidArgumentException("stopTimeout must be non-negative");

	m_stopTimeout = timeout;
}

void CassandraStartup::initCluster()
{
	if (!m_clusterInit)
		return;

	File dir(m_clusterDir);
	dir.createDirectories();

	const Path yaml(m_clusterDir, YAML_FILE);
	ofstream config(yaml.toString());
	dumpConfigFile(config);

	logger().notice("created the cassandra cluster at " + m_clusterDir.toString());
}

void CassandraStartup::deleteCluster()
{
	if (!m_clusterInit)
		return;

	logger().notice("deleting the cassandra cluster at " + m_clusterDir.toString());

	File dir(m_clusterDir);

	try {
		dir.remove(true);
	}
	BEEEON_CATCH_CHAIN(logger())
}

void CassandraStartup::start()
{
	logger().notice("starting cassandra...");

	FastMutex::ScopedLock guard(m_lock);

	File clusterFile(m_clusterDir);
	if (!clusterFile.exists()) {
		throw IllegalStateException(
			"directory " + m_clusterDir.toString() + " does not exists");
	}
	if (!clusterFile.isDirectory()) {
		throw IllegalStateException(
			"path " + m_clusterDir.toString() + " is not a directory");
	}
	if (!clusterFile.canWrite()) {
		throw IllegalStateException(
			"directory " + m_clusterDir.toString() + " is not writable");
	}

	const Path yaml(m_clusterDir, YAML_FILE);
	const Path logDir(m_clusterDir, "log");

	Process::Args args;
	args.emplace_back("-f");
	args.emplace_back("-Dcassandra.config=file:///" + yaml.toString());
	args.emplace_back("-Dcassandra.logdir=" + logDir.toString());
	args.emplace_back("-Dcassandra.jmx.local.port=" + to_string(m_jmxPort));
	args.emplace_back("-Dcom.sun.management.jmxremote.authenticate=true");

	Pipe pout;
	m_handle = new ProcessHandle(Process::launch(
	        m_cassandraBin, args, m_clusterDir.toString(), nullptr, &pout, nullptr));

	if (setpgid(m_handle->id(), 0) < 0)
	        logger().error(Error::getMessage(Error::last()), __FILE__, __LINE__);

	m_pout = new Pipe(pout);
	m_poutThread.start(m_poutGrabber);

	logger().notice(
		"cassandra pid is " + to_string(m_handle->id()),
		__FILE__, __LINE__);
}

void CassandraStartup::stop()
{
	logger().information("stopping cassandra...");

	FastMutex::ScopedLock guard(m_lock, m_stopTimeout.totalMilliseconds());

	if (logger().debug()) {
		logger().debug(
			"requesting stop of cassandra " + to_string(m_handle->id()),
			__FILE__, __LINE__);
	}

	try {
		Process::requestTermination(m_handle->id());
		m_handle->wait();
	}
	catch (const NotFoundException &e) {
		logger().log(e, __FILE__, __LINE__);
	}
	catch (const Exception &e) {
		logger().log(e, __FILE__, __LINE__);
	}

	m_handle = nullptr;
	m_pout = nullptr;
}

void CassandraStartup::grabPout()
{
	const auto pid = m_handle->id();

	if (logger().debug()) {
		logger().debug(
			"grab stdout of cassandra " + to_string(pid),
			__FILE__, __LINE__);
	}

	while (!m_pout.isNull()) {
		Pipe pipe(*m_pout);
		grab(pipe, pid);
	}
}

static Message::Priority preprocess(string &line)
{
	static RegularExpression labelExpr("(INFO|DEBUG|WARN|ERROR|TRACE|FATAL)");
	Message::Priority prio = Message::PRIO_INFORMATION;

	RegularExpression::MatchVec m;
	if (labelExpr.match(line, 0, m)) {
		const auto label = line.substr(m[1].offset, m[1].length);

		if (label == "INFO")
			prio = Message::PRIO_INFORMATION;
		else if (label == "DEBUG")
			prio = Message::PRIO_DEBUG;
		else if (label == "WARN")
			prio = Message::PRIO_WARNING;
		else if (label == "ERROR")
			prio = Message::PRIO_ERROR;
		else if (label == "TRACE")
			prio = Message::PRIO_TRACE;
		else if (label == "FATAL")
			prio = Message::PRIO_FATAL;

		line = line.substr(m[0].offset + m[0].length);
		trimInPlace(line);
	}

	return prio;
}

void CassandraStartup::grab(Pipe pipe, Process::PID pid) const
{
	PipeInputStream in(pipe);
	string line;

	while (getline(in, line)) {
		Message::Priority prio = preprocess(line);

		Message msg(
			logger().name(),
			line,
			prio,
			__FILE__,
			__LINE__
		);

		msg.setPid(pid);
		logger().log(msg);
	}
}

Process::PID CassandraStartup::readPID()
{
	const Path pidPath(m_clusterDir, PID_FILE);
	FileInputStream pidFile(pidPath.toString());

	int pid = -1;
	pidFile >> pid;
	pidFile.close();

	return pid;
}

static void dumpDataDir(Path clusterDir, ostream &config)
{
	File data(Path(clusterDir, Path("data")));
	data.createDirectories();

	config << "data_file_directories:" << endl;
	config << "    - " << data.path() << endl;
}

static void dumbSeedProvider(string host, ostream &config)
{
	config << "seed_provider:" << endl;
	config << "    - class_name: org.apache.cassandra.locator.SimpleSeedProvider" << endl;
	config << "      parameters:" << endl;
	config << "          - seeds: \"" << host <<  "\"" << endl;
}

void CassandraStartup::dumpConfigFile(ostream &config) const
{
	if (logger().debug()) {
		logger().debug(
			"creating configuration files...",
			__FILE__, __LINE__);
	}

	File hints(Path(m_clusterDir, Path("hints")));
	hints.createDirectories();

	File commitLog(Path(m_clusterDir, Path("commitlog")));
	commitLog.createDirectories();

	File cache(Path(m_clusterDir, Path("saved_caches")));
	cache.createDirectories();

	map<string, string> simpleEntries = {
		{"cluster_name", "\'" + m_clusterName +  "\'"},
		{"hints_directory", hints.path()},
		{"partitioner", "org.apache.cassandra.dht.Murmur3Partitioner"},
		{"commitlog_directory", commitLog.path()},
		{"saved_caches_directory", cache.path()},
		{"commitlog_sync", "periodic"},
		{"commitlog_sync_period_in_ms", "10000"},
		{"commitlog_segment_size_in_mb", "32"},
		{"listen_address", m_host},
		{"start_native_transport", "true"},
		{"native_transport_port", to_string(m_port)},
		{"start_rpc", "false"},
		{"rpc_address", m_host},
		{"rpc_port", to_string(m_rpcPort)},
		{"endpoint_snitch", "SimpleSnitch"}
	};

	for (const auto pair : simpleEntries)
		config << pair.first << ": " << pair.second << endl;

	dumpDataDir(m_clusterDir, config);
	dumbSeedProvider(m_host, config);
}
<|endoftext|>


<commit_before>// Copyright © 2011, Université catholique de Louvain
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// *  Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// *  Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 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 HOLDER 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.

#ifndef MOZART_MEMMANLIST_H
#define MOZART_MEMMANLIST_H

#include "memmanager.hh"

#include <utility>
#include <cassert>

namespace mozart {

inline
MemoryManager& virtualMMToActualMM(MemoryManager& mm) {
  return mm;
}

////////////////////
// MemManagedList //
////////////////////

template <class T, class MM = MemoryManager&>
class MemManagedList {
private:
  struct ListNode {
    ListNode* next;
    T item;
  };
public:
  struct iterator {
    iterator() {}

    iterator(ListNode* node) : node(node) {}

    bool operator==(const iterator& other) {
      return node == other.node;
    }

    bool operator!=(const iterator& other) {
      return node != other.node;
    }

    iterator operator++() {
      node = node->next;
      return *this;
    }

    iterator operator++(int) {
      iterator result = *this;
      node = node->next;
      return result;
    }

    iterator operator+(size_t count) {
      iterator result = *this;
      while (count != 0) {
        ++result;
        --count;
      }
      return result;
    }

    T& operator*() {
      return node->item;
    }

    T* operator->() {
      return &node->item;
    }
  private:
    friend class MemManagedList<T, MM>;

    ListNode* node;
  };

  struct removable_iterator {
    removable_iterator() {}

    removable_iterator(ListNode* node, ListNode* prev) :
      node(node), prev(prev) {}

    bool operator==(const removable_iterator& other) {
      return node == other.node;
    }

    bool operator!=(const removable_iterator& other) {
      return node != other.node;
    }

    removable_iterator operator++() {
      prev = node;
      node = node->next;
      return *this;
    }

    removable_iterator operator++(int) {
      removable_iterator result = *this;
      prev = node;
      node = node->next;
      return result;
    }

    T& operator*() {
      return node->item;
    }

    T* operator->() {
      return &node->item;
    }
  private:
    friend class MemManagedList<T, MM>;

    ListNode* node;
    ListNode* prev;
  };
public:
  MemManagedList() : first(nullptr), last(nullptr) {}

  bool empty() {
    return first == nullptr;
  }

  void push_back(MM mm, const T& item) {
    if (last == nullptr) {
      first = last = newNode(mm, nullptr, item);
    } else {
      last->next = newNode(mm, nullptr, item);
      last = last->next;
    }
  }

  template <class... Args>
  void push_back_new(MM mm, Args&&... args) {
    if (last == nullptr) {
      first = last = newNode_new(mm, nullptr, std::forward<Args>(args)...);
    } else {
      last->next = newNode_new(mm, nullptr, std::forward<Args>(args)...);
      last = last->next;
    }
  }

  void push_front(MM mm, const T& item) {
    if (last == nullptr) {
      first = last = newNode(mm, nullptr, item);
    } else {
      first = newNode(mm, first, item);
    }
  }

  template <class... Args>
  void push_front_new(MM mm, Args&&... args) {
    if (last == nullptr) {
      first = last = newNode_new(mm, nullptr, std::forward<Args>(args)...);
    } else {
      first = newNode_new(mm, first, std::forward<Args>(args)...);
    }
  }

  T pop_front(MM mm) {
    assert(!empty());
    ListNode* node = first;
    T result = node->item;
    first = node->next;
    if (first == nullptr)
      last = nullptr;
    freeNode(mm, node);
    return result;
  }

  T& front() {
    assert(!empty());
    return first->item;
  }

  T& back() {
    assert(!empty());
    return last->item;
  }

  void remove_front(MM mm) {
    assert(!empty());
    ListNode* node = first;
    first = node->next;
    if (first == nullptr)
      last = nullptr;
    freeNode(mm, node);
  }

  void clear(MM mm) {
    ListNode* node = first;

    while (node != nullptr) {
      ListNode* next = node->next;
      freeNode(mm, node);
      node = next;
    }

    first = last = nullptr;
  }

  removable_iterator remove(MM mm, removable_iterator iterator) {
    ListNode* node = iterator.node;
    internalRemove(iterator);
    freeNode(mm, node);
    return iterator;
  }

  void remove(MM mm, removable_iterator first, removable_iterator last) {
    while (first != last)
      first = remove(mm, first);
  }

  void remove_after(MM mm, iterator iter) {
    remove(removable_iterator(iter.node->next, iter.node));
  }

  void remove_after(MM mm, iterator first, iterator last) {
    remove(mm, removable_iterator(first.node->next, first.node),
           removable_iterator(last.node, nullptr /* unused */));
  }

  void splice(MM mm, MemManagedList<T, MM>& source) {
    if (last == nullptr)
      first = source.first;
    else
      last->next = source.first;

    last = source.last;
    source.first = source.last = nullptr;
  }

  void splice(MM mm, MemManagedList<T, MM>& source,
              removable_iterator& srcIterator) {
    ListNode* node = srcIterator.node;
    source.internalRemove(srcIterator);

    node->next = nullptr;

    if (last == nullptr) {
      first = last = node;
    } else {
      last->next = node;
      last = node;
    }
  }

  void insert_before(MM mm, removable_iterator& iterator, const T& item) {
    ListNode* node = newNode(mm, iterator.node, item);
    internalInsert_before(node, iterator);
  }

  template <class... Args>
  void insert_before_new(MM mm, removable_iterator& iterator, Args&&... args) {
    ListNode* node = newNode_new(mm, iterator.node,
                                 std::forward<Args>(args)...);
    internalInsert_before(node, iterator);
  }

  iterator begin() {
    return iterator(first);
  }

  iterator end() {
    return iterator(nullptr);
  }

  removable_iterator removable_begin() {
    return removable_iterator(first, nullptr);
  }

  removable_iterator removable_end() {
    return removable_iterator(nullptr, last);
  }

  /** Size of the list. O(n) operation. */
  size_t size() {
    size_t result = 0;
    for (auto iter = begin(); iter != end(); ++iter)
      result++;
    return result;
  }
private:
  void internalRemove(removable_iterator& iterator) {
    ListNode* node = iterator.node;
    ListNode* prev = iterator.prev;

    iterator.node = node->next;

    if (node == last)
      last = prev;

    if (prev == nullptr)
      first = node->next;
    else
      prev->next = node->next;
  }

  void internalInsert_before(ListNode* node, removable_iterator& iterator) {
    if (iterator.prev == nullptr)
      first = node;
    else
      iterator.prev->next = node;

    if (iterator.node == nullptr)
      last = node;

    iterator.prev = node;
  }
private:
  ListNode* newNode(MM mm, ListNode* next, const T& item) {
    ListNode* node = mallocNode(mm);
    node->next = next;
    node->item = item;
    return node;
  }

  template <class... Args>
  ListNode* newNode_new(MM mm, ListNode* next, Args&&... args) {
    ListNode* node = mallocNode(mm);
    node->next = next;
    new (&node->item) T(std::forward<Args>(args)...);
    return node;
  }

  ListNode* mallocNode(MM mm) {
    return static_cast<ListNode*>(
      virtualMMToActualMM(mm).malloc(sizeof(ListNode)));
  }

  void freeNode(MM mm, ListNode* node) {
    virtualMMToActualMM(mm).free(
      static_cast<void*>(node), sizeof(ListNode));
  }

  ListNode* first;
  ListNode* last;
};

}

#endif // MOZART_MEMMANLIST_H
<commit_msg>Fix missing argument in MemManagedList::remove_after()<commit_after>// Copyright © 2011, Université catholique de Louvain
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// *  Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// *  Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 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 HOLDER 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.

#ifndef MOZART_MEMMANLIST_H
#define MOZART_MEMMANLIST_H

#include "memmanager.hh"

#include <utility>
#include <cassert>

namespace mozart {

inline
MemoryManager& virtualMMToActualMM(MemoryManager& mm) {
  return mm;
}

////////////////////
// MemManagedList //
////////////////////

template <class T, class MM = MemoryManager&>
class MemManagedList {
private:
  struct ListNode {
    ListNode* next;
    T item;
  };
public:
  struct iterator {
    iterator() {}

    iterator(ListNode* node) : node(node) {}

    bool operator==(const iterator& other) {
      return node == other.node;
    }

    bool operator!=(const iterator& other) {
      return node != other.node;
    }

    iterator operator++() {
      node = node->next;
      return *this;
    }

    iterator operator++(int) {
      iterator result = *this;
      node = node->next;
      return result;
    }

    iterator operator+(size_t count) {
      iterator result = *this;
      while (count != 0) {
        ++result;
        --count;
      }
      return result;
    }

    T& operator*() {
      return node->item;
    }

    T* operator->() {
      return &node->item;
    }
  private:
    friend class MemManagedList<T, MM>;

    ListNode* node;
  };

  struct removable_iterator {
    removable_iterator() {}

    removable_iterator(ListNode* node, ListNode* prev) :
      node(node), prev(prev) {}

    bool operator==(const removable_iterator& other) {
      return node == other.node;
    }

    bool operator!=(const removable_iterator& other) {
      return node != other.node;
    }

    removable_iterator operator++() {
      prev = node;
      node = node->next;
      return *this;
    }

    removable_iterator operator++(int) {
      removable_iterator result = *this;
      prev = node;
      node = node->next;
      return result;
    }

    T& operator*() {
      return node->item;
    }

    T* operator->() {
      return &node->item;
    }
  private:
    friend class MemManagedList<T, MM>;

    ListNode* node;
    ListNode* prev;
  };
public:
  MemManagedList() : first(nullptr), last(nullptr) {}

  bool empty() {
    return first == nullptr;
  }

  void push_back(MM mm, const T& item) {
    if (last == nullptr) {
      first = last = newNode(mm, nullptr, item);
    } else {
      last->next = newNode(mm, nullptr, item);
      last = last->next;
    }
  }

  template <class... Args>
  void push_back_new(MM mm, Args&&... args) {
    if (last == nullptr) {
      first = last = newNode_new(mm, nullptr, std::forward<Args>(args)...);
    } else {
      last->next = newNode_new(mm, nullptr, std::forward<Args>(args)...);
      last = last->next;
    }
  }

  void push_front(MM mm, const T& item) {
    if (last == nullptr) {
      first = last = newNode(mm, nullptr, item);
    } else {
      first = newNode(mm, first, item);
    }
  }

  template <class... Args>
  void push_front_new(MM mm, Args&&... args) {
    if (last == nullptr) {
      first = last = newNode_new(mm, nullptr, std::forward<Args>(args)...);
    } else {
      first = newNode_new(mm, first, std::forward<Args>(args)...);
    }
  }

  T pop_front(MM mm) {
    assert(!empty());
    ListNode* node = first;
    T result = node->item;
    first = node->next;
    if (first == nullptr)
      last = nullptr;
    freeNode(mm, node);
    return result;
  }

  T& front() {
    assert(!empty());
    return first->item;
  }

  T& back() {
    assert(!empty());
    return last->item;
  }

  void remove_front(MM mm) {
    assert(!empty());
    ListNode* node = first;
    first = node->next;
    if (first == nullptr)
      last = nullptr;
    freeNode(mm, node);
  }

  void clear(MM mm) {
    ListNode* node = first;

    while (node != nullptr) {
      ListNode* next = node->next;
      freeNode(mm, node);
      node = next;
    }

    first = last = nullptr;
  }

  // Follows STL's erase() semantics: the given iterator is invalidated
  // and the returned iterator points to the next element.
  removable_iterator remove(MM mm, removable_iterator iterator) {
    ListNode* node = iterator.node;
    internalRemove(iterator);
    freeNode(mm, node);
    return iterator;
  }

  void remove(MM mm, removable_iterator first, removable_iterator last) {
    while (first != last)
      first = remove(mm, first);
  }

  void remove_after(MM mm, iterator iter) {
    remove(mm, removable_iterator(iter.node->next, iter.node));
  }

  void remove_after(MM mm, iterator first, iterator last) {
    remove(mm, removable_iterator(first.node->next, first.node),
           removable_iterator(last.node, nullptr /* unused */));
  }

  void splice(MM mm, MemManagedList<T, MM>& source) {
    if (last == nullptr)
      first = source.first;
    else
      last->next = source.first;

    last = source.last;
    source.first = source.last = nullptr;
  }

  void splice(MM mm, MemManagedList<T, MM>& source,
              removable_iterator& srcIterator) {
    ListNode* node = srcIterator.node;
    source.internalRemove(srcIterator);

    node->next = nullptr;

    if (last == nullptr) {
      first = last = node;
    } else {
      last->next = node;
      last = node;
    }
  }

  void insert_before(MM mm, removable_iterator& iterator, const T& item) {
    ListNode* node = newNode(mm, iterator.node, item);
    internalInsert_before(node, iterator);
  }

  template <class... Args>
  void insert_before_new(MM mm, removable_iterator& iterator, Args&&... args) {
    ListNode* node = newNode_new(mm, iterator.node,
                                 std::forward<Args>(args)...);
    internalInsert_before(node, iterator);
  }

  iterator begin() {
    return iterator(first);
  }

  iterator end() {
    return iterator(nullptr);
  }

  removable_iterator removable_begin() {
    return removable_iterator(first, nullptr);
  }

  removable_iterator removable_end() {
    return removable_iterator(nullptr, last);
  }

  /** Size of the list. O(n) operation. */
  size_t size() {
    size_t result = 0;
    for (auto iter = begin(); iter != end(); ++iter)
      result++;
    return result;
  }
private:
  void internalRemove(removable_iterator& iterator) {
    ListNode* node = iterator.node;
    ListNode* prev = iterator.prev;

    iterator.node = node->next;

    if (node == last)
      last = prev;

    if (prev == nullptr)
      first = node->next;
    else
      prev->next = node->next;
  }

  void internalInsert_before(ListNode* node, removable_iterator& iterator) {
    if (iterator.prev == nullptr)
      first = node;
    else
      iterator.prev->next = node;

    if (iterator.node == nullptr)
      last = node;

    iterator.prev = node;
  }
private:
  ListNode* newNode(MM mm, ListNode* next, const T& item) {
    ListNode* node = mallocNode(mm);
    node->next = next;
    node->item = item;
    return node;
  }

  template <class... Args>
  ListNode* newNode_new(MM mm, ListNode* next, Args&&... args) {
    ListNode* node = mallocNode(mm);
    node->next = next;
    new (&node->item) T(std::forward<Args>(args)...);
    return node;
  }

  ListNode* mallocNode(MM mm) {
    return static_cast<ListNode*>(
      virtualMMToActualMM(mm).malloc(sizeof(ListNode)));
  }

  void freeNode(MM mm, ListNode* node) {
    virtualMMToActualMM(mm).free(
      static_cast<void*>(node), sizeof(ListNode));
  }

  ListNode* first;
  ListNode* last;
};

}

#endif // MOZART_MEMMANLIST_H
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: tkpcont2.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 19:11:48 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <precomp.h>
#include <tokens/tkpcont2.hxx>

// NOT FULLY DECLARED SERVICES

TkpNullContex2 G_aNullContex2;

TkpNullContex2 &
TkpContext_Null2_()
{
    return G_aNullContex2;
}

TkpNullContex2::~TkpNullContex2()
{
}

void
TkpNullContex2::ReadCharChain( CharacterSource & )
{
}

bool
TkpNullContex2::PassNewToken()
{
    return false;
}

TkpContext &
TkpNullContex2::FollowUpContext()
{
     return *this;
}

<commit_msg>INTEGRATION: CWS pchfix02 (1.2.30); FILE MERGED 2006/09/01 17:15:53 kaib 1.2.30.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: tkpcont2.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-16 17:16:58 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_autodoc.hxx"

#include <precomp.h>
#include <tokens/tkpcont2.hxx>

// NOT FULLY DECLARED SERVICES

TkpNullContex2 G_aNullContex2;

TkpNullContex2 &
TkpContext_Null2_()
{
    return G_aNullContex2;
}

TkpNullContex2::~TkpNullContex2()
{
}

void
TkpNullContex2::ReadCharChain( CharacterSource & )
{
}

bool
TkpNullContex2::PassNewToken()
{
    return false;
}

TkpContext &
TkpNullContex2::FollowUpContext()
{
     return *this;
}

<|endoftext|>


<commit_before>// Copyright (C) 2017 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and University of
// of Connecticut School of Medicine.
// All rights reserved.

// Copyright (C) 2010 - 2016 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2002 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

#include <stdexcept>
#include <locale.h>

#include <QApplication>

#define COPASI_MAIN

#include "copasi.h"

#include "UI/CQCopasiApplication.h"
#include "UI/copasiui3window.h"
#include "UI/DataModelGUI.h"
#include "UI/CQMessageBox.h"
#include  <copasi/UI/CQUndoTester.h>

#include "CopasiDataModel/CDataModel.h"
#include "copasi/core/CRootContainer.h"
#include "function/CFunctionDB.h"
#include "function/CFunction.h"
#include "commandline/COptionParser.h"
#include "commandline/COptions.h"

#ifdef COPASI_SBW_INTEGRATION
// SBW includes
#include "SBW/SBW.h"
#include <sstream>
#endif // COPASI_SBW_INTEGRATION

#ifdef Darwin
# include <QtCore/QDir>
# include "UI/qtUtilities.h"
#endif // Darwin

int main(int argc, char *argv[])
{
  // Fix for Issue 1377, if locale is not set to the default one, some
  // numbers will be displayed in the systems default locale:
  setlocale(LC_ALL, "C");

  // Fix of Issue with Text centering on OSX Mavericks
#ifdef Q_OS_MACX

  if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8)
    {
      // fix Mac OS X 10.9 (mavericks) font issue
      // https://bugreports.qt-project.org/browse/QTBUG-32789
      QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
    }

#endif

  CQCopasiApplication a(argc, argv);

  a.setAttribute(Qt::AA_DontShowIconsInMenus, false);

  Q_INIT_RESOURCE(copasi);

  // Parse the commandline options
  try
    {
      // Create the root container.
      CRootContainer::init(argc, argv, true);
    }
  catch (copasi::option_error & msg)
    {
      CQMessageBox::critical(NULL, "Initialization Error",
                             msg.what(),
                             QMessageBox::Ok , QMessageBox::Ok);

      return 1;
    }

#ifdef Darwin
  std::string PluginDir;

  COptions::getValue("CopasiDir", PluginDir);
  PluginDir += "/Contents/plugins";

  QApplication::setLibraryPaths(QStringList(FROM_UTF8(PluginDir)));
#endif // Darwin

  // Create the main application window.
  CopasiUI3Window *pWindow = CopasiUI3Window::create();

#ifdef COPASI_SBW_INTEGRATION

  if (COptions::compareValue("SBWRegister", true))
    goto finish;

#endif // COPASI_SBW_INTEGRATION

  if (pWindow != NULL)
    {
      a.setMainWindow(pWindow);

      if (!COptions::getEnvironmentVariable("COPASI_TEST_UNDO").empty())
        {
          // test undo if environment variable is present
          CQUndoTester*  tester = new CQUndoTester(&a, NULL);
          tester->startTestIn(1000);
        }

      a.exec();
    }

finish:

  try // To suppress any access violations during destruction works only under Windows
    {
      pdelete(pWindow);
      CRootContainer::destroy();
    }
  catch (...)
    {}

  return 0;
}
<commit_msg>- issue 2781: set qt default locale to english, so the values are displayed as they can be entered<commit_after>// Copyright (C) 2019 by Pedro Mendes, Rector and Visitors of the
// University of Virginia, University of Heidelberg, and University
// of Connecticut School of Medicine.
// All rights reserved.

// Copyright (C) 2017 - 2018 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and University of
// of Connecticut School of Medicine.
// All rights reserved.

// Copyright (C) 2010 - 2016 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2002 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

#include <stdexcept>
#include <locale.h>

#include <QApplication>
#include <QLocale>

#define COPASI_MAIN

#include "copasi.h"

#include "UI/CQCopasiApplication.h"
#include "UI/copasiui3window.h"
#include "UI/DataModelGUI.h"
#include "UI/CQMessageBox.h"
#include  <copasi/UI/CQUndoTester.h>

#include "CopasiDataModel/CDataModel.h"
#include "copasi/core/CRootContainer.h"
#include "function/CFunctionDB.h"
#include "function/CFunction.h"
#include "commandline/COptionParser.h"
#include "commandline/COptions.h"

#ifdef COPASI_SBW_INTEGRATION
// SBW includes
#include "SBW/SBW.h"
#include <sstream>
#endif // COPASI_SBW_INTEGRATION

#ifdef Darwin
# include <QtCore/QDir>
# include "UI/qtUtilities.h"
#endif // Darwin

int main(int argc, char *argv[])
{
  // Fix for Issue 1377, if locale is not set to the default one, some
  // numbers will be displayed in the systems default locale:
  setlocale(LC_ALL, "C");
  QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));

  // Fix of Issue with Text centering on OSX Mavericks
#ifdef Q_OS_MACX

  if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8)
    {
      // fix Mac OS X 10.9 (mavericks) font issue
      // https://bugreports.qt-project.org/browse/QTBUG-32789
      QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
    }

#endif

  CQCopasiApplication a(argc, argv);

  a.setAttribute(Qt::AA_DontShowIconsInMenus, false);

  Q_INIT_RESOURCE(copasi);

  // Parse the commandline options
  try
    {
      // Create the root container.
      CRootContainer::init(argc, argv, true);
    }
  catch (copasi::option_error & msg)
    {
      CQMessageBox::critical(NULL, "Initialization Error",
                             msg.what(),
                             QMessageBox::Ok , QMessageBox::Ok);

      return 1;
    }

#ifdef Darwin
  std::string PluginDir;

  COptions::getValue("CopasiDir", PluginDir);
  PluginDir += "/Contents/plugins";

  QApplication::setLibraryPaths(QStringList(FROM_UTF8(PluginDir)));
#endif // Darwin

  // Create the main application window.
  CopasiUI3Window *pWindow = CopasiUI3Window::create();

#ifdef COPASI_SBW_INTEGRATION

  if (COptions::compareValue("SBWRegister", true))
    goto finish;

#endif // COPASI_SBW_INTEGRATION

  if (pWindow != NULL)
    {
      a.setMainWindow(pWindow);

      if (!COptions::getEnvironmentVariable("COPASI_TEST_UNDO").empty())
        {
          // test undo if environment variable is present
          CQUndoTester*  tester = new CQUndoTester(&a, NULL);
          tester->startTestIn(1000);
        }

      a.exec();
    }

finish:

  try // To suppress any access violations during destruction works only under Windows
    {
      pdelete(pWindow);
      CRootContainer::destroy();
    }
  catch (...)
    {}

  return 0;
}
<|endoftext|>