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

#include "DeviceKey.h"
#include "mbedtls/config.h"
#include "mbedtls/cmac.h"
#include "nvstore.h"
#include "trng_api.h"
#include "mbed_wait_api.h"
#include "stdlib.h"

#if !defined(MBEDTLS_CMAC_C)
#error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver
#else

#if NVSTORE_ENABLED

namespace mbed {

#define DEVKEY_WRITE_UINT32_LE( dst, src )                              \
    do                                                                  \
    {                                                                   \
        (dst)[0] = ( (src) >> 0 ) & 0xFF;                               \
        (dst)[1] = ( (src) >> 8 ) & 0xFF;                               \
        (dst)[2] = ( (src) >> 16 ) & 0xFF;                              \
        (dst)[3] = ( (src) >> 24 ) & 0xFF;                              \
    } while( 0 )

#define DEVKEY_WRITE_UINT8_LE( dst, src )                               \
    do                                                                  \
    {                                                                   \
        (dst)[0] = (src) & 0xFF;                                        \
    } while( 0 )

DeviceKey::DeviceKey()
{
    return;
}

DeviceKey::~DeviceKey()
{
    return;
}

int DeviceKey::generate_derived_key(const unsigned char *salt, size_t isalt_size, unsigned char *output,
                                    uint16_t ikey_type)
{
    uint32_t key_buff[DEVICE_KEY_32BYTE / sizeof(uint32_t)];
    size_t actual_size = DEVICE_KEY_32BYTE;

    if (DEVICE_KEY_16BYTE != ikey_type && DEVICE_KEY_32BYTE != ikey_type) {
        return DEVICEKEY_INVALID_KEY_TYPE;
    }

    //First try to read the key from NVStore
    int ret = read_key_from_nvstore(key_buff, actual_size);
    if (DEVICEKEY_SUCCESS != ret && DEVICEKEY_NOT_FOUND != ret) {
        return ret;
    }

    if (DEVICE_KEY_16BYTE != actual_size && DEVICE_KEY_32BYTE != actual_size) {
        return DEVICEKEY_READ_FAILED;
    }

    //If the key was not found in NVStore we will create it by using TRNG and then save it to NVStore
    if (DEVICEKEY_NOT_FOUND == ret) {
        ret = generate_key_by_trng(key_buff, actual_size);
        if (DEVICEKEY_SUCCESS != ret) {
            return ret;
        }

        ret = device_inject_root_of_trust(key_buff, actual_size);
        if (DEVICEKEY_SUCCESS != ret) {
            return ret;
        }
    }

    ret = get_derived_key(key_buff, actual_size, salt, isalt_size, output, ikey_type);
    return ret;
}

int DeviceKey::device_inject_root_of_trust(uint32_t *value, size_t isize)
{
    return write_key_to_nvstore(value, isize);
}

int DeviceKey::write_key_to_nvstore(uint32_t *input, size_t isize)
{
    if (DEVICE_KEY_16BYTE != isize && DEVICE_KEY_32BYTE != isize) {
        return DEVICEKEY_INVALID_KEY_SIZE;
    }

    //First we read if key exist. If it is exists, we return DEVICEKEY_ALREADY_EXIST error
    uint32_t read_key[DEVICE_KEY_32BYTE / sizeof(uint32_t)] = {0};
    size_t read_size = DEVICE_KEY_32BYTE;
    int ret = read_key_from_nvstore(read_key, read_size);
    if (DEVICEKEY_SUCCESS == ret) {
        return DEVICEKEY_ALREADY_EXIST;
    }
    if (DEVICEKEY_NOT_FOUND != ret) {
        return ret;
    }

    NVStore& nvstore = NVStore::get_instance();
    ret = nvstore.set(NVSTORE_DEVICEKEY_KEY, (uint16_t)isize, input);
    if (NVSTORE_WRITE_ERROR == ret || NVSTORE_BUFF_TOO_SMALL == ret) {
        return DEVICEKEY_SAVE_FAILED;
    }

    if (NVSTORE_SUCCESS != ret) {
        return DEVICEKEY_NVSTORE_UNPREDICTED_ERROR;
    }

    return DEVICEKEY_SUCCESS;
}

int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size)
{
    if (size > UINT16_MAX) {
        return DEVICEKEY_INVALID_PARAM;
    }

    uint16_t in_size = size;
    uint16_t out_size = 0;
    NVStore& nvstore = NVStore::get_instance();
    int nvStatus = nvstore.get(NVSTORE_DEVICEKEY_KEY, in_size, output, out_size);
    if (NVSTORE_NOT_FOUND == nvStatus) {
        return DEVICEKEY_NOT_FOUND;
    }

    if (NVSTORE_READ_ERROR == nvStatus || NVSTORE_BUFF_TOO_SMALL == nvStatus) {
        return DEVICEKEY_READ_FAILED;
    }

    if (NVSTORE_SUCCESS != nvStatus) {
        return DEVICEKEY_NVSTORE_UNPREDICTED_ERROR;
    }

    size = out_size;
    return DEVICEKEY_SUCCESS;
}

int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsigned char *isalt,
                               size_t isalt_size, unsigned char *output, uint32_t ikey_type)
{
    //KDF in counter mode implementation as described in Section 5.1
    //of NIST SP 800-108, Recommendation for Key Derivation Using Pseudorandom Functions
    int ret;
    size_t counter = 0;
    char separator = 0x00;
    mbedtls_cipher_context_t ctx;
    unsigned char output_len_enc[ 4 ] = {0};
    unsigned char counter_enc[ 1 ] = {0};

    DEVKEY_WRITE_UINT32_LE(output_len_enc, ikey_type);

    mbedtls_cipher_type_t mbedtls_cipher_type = MBEDTLS_CIPHER_AES_128_ECB;
    if (DEVICE_KEY_32BYTE == ikey_size) {
        mbedtls_cipher_type = MBEDTLS_CIPHER_AES_256_ECB;
    }

    const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_type(mbedtls_cipher_type);

    mbedtls_cipher_init(&ctx);
    ret = mbedtls_cipher_setup(&ctx, cipher_info);
    if (ret != 0) {
        goto finish;
    }

    do {

        ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8);
        if (ret != 0) {
            goto finish;
        }

        DEVKEY_WRITE_UINT8_LE(counter_enc, (counter+1));

        ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)counter_enc, sizeof(counter_enc));
        if (ret != 0) {
            goto finish;
        }

        ret = mbedtls_cipher_cmac_update(&ctx, isalt, isalt_size);
        if (ret != 0) {
            goto finish;
        }

        ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)&separator, sizeof(char));
        if (ret != 0) {
            goto finish;
        }

        ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)&output_len_enc, sizeof(output_len_enc));
        if (ret != 0) {
            goto finish;
        }

        ret = mbedtls_cipher_cmac_finish(&ctx, output + (DEVICE_KEY_16BYTE * (counter)));
        if (ret != 0) {
            goto finish;
        }

        counter++;

    } while (DEVICE_KEY_16BYTE * counter < ikey_type);

finish:
    mbedtls_cipher_free( &ctx );

    if (DEVICEKEY_SUCCESS != ret) {
        return DEVICEKEY_ERR_CMAC_GENERIC_FAILURE;
    }

    return DEVICEKEY_SUCCESS;
}

int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size)
{
#if defined(DEVICE_TRNG)
    size_t in_size;
    size_t ongoing_size;
    size_t final_size;
    trng_t trng_obj;
    int ret = DEVICEKEY_SUCCESS;
    unsigned char *pBuffer = (unsigned char *)output;

    memset(output, 0, size);

    if (DEVICE_KEY_16BYTE > size) {
        return DEVICEKEY_BUFFER_TOO_SMALL;
    } else if (DEVICE_KEY_16BYTE <= size && DEVICE_KEY_32BYTE > size) {
        size = DEVICE_KEY_16BYTE;
    } else {
        size = DEVICE_KEY_32BYTE;
    }

    trng_init(&trng_obj);

    final_size = 0;
    in_size = size;
    while (final_size < size) {

        ongoing_size = 0;
        ret = trng_get_bytes(&trng_obj, (unsigned char *)pBuffer, in_size, &ongoing_size);
        final_size += ongoing_size;
        if (0 != ret) {
            ret = DEVICEKEY_TRNG_ERROR;
            goto finish;
        }

        pBuffer += ongoing_size;
        in_size -= ongoing_size;
    }

    ret = DEVICEKEY_SUCCESS;

finish:
    trng_free(&trng_obj);
    size = final_size;
    return ret;

#else
    return DEVICEKEY_NO_KEY_INJECTED;
#endif
}

} // namespace mbed

#endif //NVSTORE_ENABLED
#endif

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

#include "DeviceKey.h"
#include "mbedtls/config.h"
#include "mbedtls/cmac.h"
#include "nvstore.h"
#include "trng_api.h"
#include "mbed_wait_api.h"
#include "stdlib.h"

#if !defined(MBEDTLS_CMAC_C)
#error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver
#else

#if NVSTORE_ENABLED

namespace mbed {

#define DEVKEY_WRITE_UINT32_LE( dst, src )                              \
    do                                                                  \
    {                                                                   \
        (dst)[0] = ( (src) >> 0 ) & 0xFF;                               \
        (dst)[1] = ( (src) >> 8 ) & 0xFF;                               \
        (dst)[2] = ( (src) >> 16 ) & 0xFF;                              \
        (dst)[3] = ( (src) >> 24 ) & 0xFF;                              \
    } while( 0 )

#define DEVKEY_WRITE_UINT8_LE( dst, src )                               \
    do                                                                  \
    {                                                                   \
        (dst)[0] = (src) & 0xFF;                                        \
    } while( 0 )

DeviceKey::DeviceKey()
{
    return;
}

DeviceKey::~DeviceKey()
{
    return;
}

int DeviceKey::generate_derived_key(const unsigned char *salt, size_t isalt_size, unsigned char *output,
                                    uint16_t ikey_type)
{
    uint32_t key_buff[DEVICE_KEY_32BYTE / sizeof(uint32_t)];
    size_t actual_size = DEVICE_KEY_32BYTE;

    if (DEVICE_KEY_16BYTE != ikey_type && DEVICE_KEY_32BYTE != ikey_type) {
        return DEVICEKEY_INVALID_KEY_TYPE;
    }

    //First try to read the key from NVStore
    int ret = read_key_from_nvstore(key_buff, actual_size);
    if (DEVICEKEY_SUCCESS != ret && DEVICEKEY_NOT_FOUND != ret) {
        return ret;
    }

    if (DEVICE_KEY_16BYTE != actual_size && DEVICE_KEY_32BYTE != actual_size) {
        return DEVICEKEY_READ_FAILED;
    }

    //If the key was not found in NVStore we will create it by using TRNG and then save it to NVStore
    if (DEVICEKEY_NOT_FOUND == ret) {
        ret = generate_key_by_trng(key_buff, actual_size);
        if (DEVICEKEY_SUCCESS != ret) {
            return ret;
        }

        ret = device_inject_root_of_trust(key_buff, actual_size);
        if (DEVICEKEY_SUCCESS != ret) {
            return ret;
        }
    }

    ret = get_derived_key(key_buff, actual_size, salt, isalt_size, output, ikey_type);
    return ret;
}

int DeviceKey::device_inject_root_of_trust(uint32_t *value, size_t isize)
{
    return write_key_to_nvstore(value, isize);
}

int DeviceKey::write_key_to_nvstore(uint32_t *input, size_t isize)
{
    if (DEVICE_KEY_16BYTE != isize && DEVICE_KEY_32BYTE != isize) {
        return DEVICEKEY_INVALID_KEY_SIZE;
    }

    //First we read if key exist. If it is exists, we return DEVICEKEY_ALREADY_EXIST error
    uint32_t read_key[DEVICE_KEY_32BYTE / sizeof(uint32_t)] = {0};
    size_t read_size = DEVICE_KEY_32BYTE;
    int ret = read_key_from_nvstore(read_key, read_size);
    if (DEVICEKEY_SUCCESS == ret) {
        return DEVICEKEY_ALREADY_EXIST;
    }
    if (DEVICEKEY_NOT_FOUND != ret) {
        return ret;
    }

    NVStore& nvstore = NVStore::get_instance();
    ret = nvstore.set(NVSTORE_DEVICEKEY_KEY, (uint16_t)isize, input);
    if (NVSTORE_WRITE_ERROR == ret || NVSTORE_BUFF_TOO_SMALL == ret) {
        return DEVICEKEY_SAVE_FAILED;
    }

    if (NVSTORE_SUCCESS != ret) {
        return DEVICEKEY_NVSTORE_UNPREDICTED_ERROR;
    }

    return DEVICEKEY_SUCCESS;
}

int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size)
{
    if (size > UINT16_MAX) {
        return DEVICEKEY_INVALID_PARAM;
    }

    uint16_t in_size = size;
    uint16_t out_size = 0;
    NVStore& nvstore = NVStore::get_instance();
    int nvStatus = nvstore.get(NVSTORE_DEVICEKEY_KEY, in_size, output, out_size);
    if (NVSTORE_NOT_FOUND == nvStatus) {
        return DEVICEKEY_NOT_FOUND;
    }

    if (NVSTORE_READ_ERROR == nvStatus || NVSTORE_BUFF_TOO_SMALL == nvStatus) {
        return DEVICEKEY_READ_FAILED;
    }

    if (NVSTORE_SUCCESS != nvStatus) {
        return DEVICEKEY_NVSTORE_UNPREDICTED_ERROR;
    }

    size = out_size;
    return DEVICEKEY_SUCCESS;
}

int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsigned char *isalt,
                               size_t isalt_size, unsigned char *output, uint32_t ikey_type)
{
    //KDF in counter mode implementation as described in Section 5.1
    //of NIST SP 800-108, Recommendation for Key Derivation Using Pseudorandom Functions
    int ret;
    size_t counter = 0;
    char separator = 0x00;
    mbedtls_cipher_context_t ctx;
    unsigned char output_len_enc[ 4 ] = {0};
    unsigned char counter_enc[ 1 ] = {0};

    DEVKEY_WRITE_UINT32_LE(output_len_enc, ikey_type);

    mbedtls_cipher_type_t mbedtls_cipher_type = MBEDTLS_CIPHER_AES_128_ECB;
    if (DEVICE_KEY_32BYTE == ikey_size) {
        mbedtls_cipher_type = MBEDTLS_CIPHER_AES_256_ECB;
    }

    const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_type(mbedtls_cipher_type);

    mbedtls_cipher_init(&ctx);
    ret = mbedtls_cipher_setup(&ctx, cipher_info);
    if (ret != 0) {
        goto finish;
    }

    do {

        ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8);
        if (ret != 0) {
            goto finish;
        }

        DEVKEY_WRITE_UINT8_LE(counter_enc, (counter+1));

        ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)counter_enc, sizeof(counter_enc));
        if (ret != 0) {
            goto finish;
        }

        ret = mbedtls_cipher_cmac_update(&ctx, isalt, isalt_size);
        if (ret != 0) {
            goto finish;
        }

        ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)&separator, sizeof(char));
        if (ret != 0) {
            goto finish;
        }

        ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)&output_len_enc, sizeof(output_len_enc));
        if (ret != 0) {
            goto finish;
        }

        ret = mbedtls_cipher_cmac_finish(&ctx, output + (DEVICE_KEY_16BYTE * (counter)));
        if (ret != 0) {
            goto finish;
        }

        counter++;

    } while (DEVICE_KEY_16BYTE * counter < ikey_type);

finish:
    mbedtls_cipher_free( &ctx );

    if (DEVICEKEY_SUCCESS != ret) {
        return DEVICEKEY_ERR_CMAC_GENERIC_FAILURE;
    }

    return DEVICEKEY_SUCCESS;
}

int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size)
{
#if defined(DEVICE_TRNG)
    size_t in_size;
    size_t ongoing_size;
    size_t final_size;
    trng_t trng_obj;
    int ret = DEVICEKEY_SUCCESS;
    unsigned char *pBuffer = (unsigned char *)output;

    memset(output, 0, size);

    if (DEVICE_KEY_16BYTE > size) {
        return DEVICEKEY_BUFFER_TOO_SMALL;
    } else if (DEVICE_KEY_16BYTE != size && DEVICE_KEY_32BYTE != size) {
        return DEVICEKEY_INVALID_PARAM;
    }

    trng_init(&trng_obj);

    final_size = 0;
    in_size = size;
    while (final_size < size) {

        ongoing_size = 0;
        ret = trng_get_bytes(&trng_obj, (unsigned char *)pBuffer, in_size, &ongoing_size);
        final_size += ongoing_size;
        if (0 != ret) {
            ret = DEVICEKEY_TRNG_ERROR;
            goto finish;
        }

        pBuffer += ongoing_size;
        in_size -= ongoing_size;
    }

    ret = DEVICEKEY_SUCCESS;

finish:
    trng_free(&trng_obj);
    size = final_size;
    return ret;

#else
    return DEVICEKEY_NO_KEY_INJECTED;
#endif
}

} // namespace mbed

#endif //NVSTORE_ENABLED
#endif

<|endoftext|>


<commit_before>//
//
// Copyright (c) 2017 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
//
// Distributed under the MIT Software License
//
#pragma once

#include "common.ipp"
#include <algorithm>
#include <cassert>
#include <ostream>
#include <type_traits>

namespace bredis {

template <typename NextLayer>
template <typename DynamicBuffer, typename WriteCallback>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteCallback,
                              void(boost::system::error_code, std::size_t))
Connection<NextLayer>::async_write(DynamicBuffer &tx_buff,
                                   const command_wrapper_t &command,
                                   WriteCallback &&write_callback) {
    namespace asio = boost::asio;
    namespace sys = boost::system;
    using boost::asio::async_write;
    using Signature = void(boost::system::error_code, std::size_t);
    using real_handler_t =
        typename asio::handler_type<WriteCallback, Signature>::type;

    std::ostream os(&tx_buff);
    auto string = boost::apply_visitor(command_serializer_visitor(), command);
    os.write(string.c_str(), string.size());
    tx_buff.commit(string.size());

    real_handler_t handler(std::forward<WriteCallback>(write_callback));
    return async_write(stream_, tx_buff, handler);
}

template <typename NextLayer>
template <typename DynamicBuffer, typename ReadCallback>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadCallback,
                              void(const boost::system::error_code,
                                   BREDIS_PARSE_RESULT(DynamicBuffer)))
Connection<NextLayer>::async_read(DynamicBuffer &rx_buff,
                                  ReadCallback &&read_callback,
                                  std::size_t replies_count) {

    namespace asio = boost::asio;
    namespace sys = boost::system;
    using boost::asio::async_read_until;
    using Iterator = typename to_iterator<DynamicBuffer>::iterator_t;
    using Signature =
        void(boost::system::error_code, BREDIS_PARSE_RESULT(DynamicBuffer));
    using real_handler_t =
        typename asio::handler_type<ReadCallback, Signature>::type;
    using result_t = ::boost::asio::async_result<real_handler_t>;

    real_handler_t real_handler(std::forward<ReadCallback>(read_callback));
    asio::async_result<real_handler_t> async_result(real_handler);

    async_read_until(stream_, rx_buff, MatchResult<Iterator>(replies_count), [
        handler = std::move(real_handler), &rx_buff, replies_count
    ](const sys::error_code &error_code, std::size_t bytes_transferred) {

        real_handler_t real_handler(std::move(handler));
        positive_parse_result_t<Iterator> result;

        if (error_code) {
            real_handler(error_code, std::move(result));
            return;
        }
        auto const_buff = rx_buff.data();
        auto begin = Iterator::begin(const_buff);
        auto end = Iterator::end(const_buff);

        markers::array_holder_t<Iterator> results;
        results.elements.reserve(replies_count);
        size_t cumulative_consumption = 0;
        boost::system::error_code ec;

        do {
            auto from = begin + cumulative_consumption;
            auto parse_result = Protocol::parse(from, end);
            auto *parse_error = boost::get<protocol_error_t>(&parse_result);
            if (parse_error) {
                auto parse_error_code =
                    Error::make_error_code(bredis_errors::protocol_error);
                real_handler(parse_error_code, std::move(result));
                return;
            }
            auto &positive_result =
                boost::get<positive_parse_result_t<Iterator>>(parse_result);
            results.elements.emplace_back(positive_result.result);
            cumulative_consumption += positive_result.consumed;
        } while (results.elements.size() < replies_count);

        if (replies_count == 1) {
            real_handler(ec, positive_parse_result_t<Iterator>{
                                 std::move(results.elements[0]),
                                 cumulative_consumption});
        } else {
            real_handler(ec, positive_parse_result_t<Iterator>{
                                 std::move(results), cumulative_consumption});
        }
    });
    return async_result.get();
}

template <typename NextLayer>
void Connection<NextLayer>::write(const command_wrapper_t &command,
                                  boost::system::error_code &ec) {
    namespace asio = boost::asio;
    auto str = boost::apply_visitor(command_serializer_visitor(), command);
    auto const output_buf = asio::buffer(str.c_str(), str.size());
    asio::write(stream_, output_buf, ec);
}

template <typename NextLayer>
void Connection<NextLayer>::write(const command_wrapper_t &command) {
    boost::system::error_code ec;
    this->write(command, ec);
    if (ec) {
        throw boost::system::system_error{ec};
    }
}

template <typename NextLayer>
template <typename DynamicBuffer>
positive_parse_result_t<typename to_iterator<DynamicBuffer>::iterator_t>
Connection<NextLayer>::read(DynamicBuffer &rx_buff,
                            boost::system::error_code &ec) {
    namespace asio = boost::asio;
    using boost::asio::read_until;
    using Iterator = typename to_iterator<DynamicBuffer>::iterator_t;
    using result_t = positive_parse_result_t<
        typename to_iterator<DynamicBuffer>::iterator_t>;

    auto rx_bytes = read_until(stream_, rx_buff, MatchResult<Iterator>(1), ec);
    if (ec) {
        return result_t{{}, 0};
    }

    auto const_buff = rx_buff.data();
    auto begin = Iterator::begin(const_buff);
    auto end = Iterator::end(const_buff);

    auto parse_result = Protocol::parse(begin, end);

    auto *parse_error = boost::get<protocol_error_t>(&parse_result);
    if (parse_error) {
        ec = Error::make_error_code(bredis_errors::protocol_error);
        return result_t{};
    }
    return boost::get<positive_parse_result_t<Iterator>>(parse_result);
}

template <typename NextLayer>
template <typename DynamicBuffer>
positive_parse_result_t<typename to_iterator<DynamicBuffer>::iterator_t>
Connection<NextLayer>::read(DynamicBuffer &rx_buff) {
    namespace asio = boost::asio;

    boost::system::error_code ec;
    auto result = this->read(rx_buff, ec);
    if (ec) {
        throw boost::system::system_error{ec};
    }
    return result;
}

} // namespace bredis
<commit_msg>Avoid double forwarding<commit_after>//
//
// Copyright (c) 2017 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
//
// Distributed under the MIT Software License
//
#pragma once

#include "common.ipp"
#include <algorithm>
#include <cassert>
#include <ostream>
#include <type_traits>

namespace bredis {

template <typename NextLayer>
template <typename DynamicBuffer, typename WriteCallback>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteCallback,
                              void(boost::system::error_code, std::size_t))
Connection<NextLayer>::async_write(DynamicBuffer &tx_buff,
                                   const command_wrapper_t &command,
                                   WriteCallback &&write_callback) {
    namespace asio = boost::asio;
    namespace sys = boost::system;
    using boost::asio::async_write;
    using Signature = void(boost::system::error_code, std::size_t);
    using real_handler_t =
        typename asio::handler_type<WriteCallback, Signature>::type;

    std::ostream os(&tx_buff);
    auto string = boost::apply_visitor(command_serializer_visitor(), command);
    os.write(string.c_str(), string.size());
    tx_buff.commit(string.size());

    real_handler_t handler(std::forward<WriteCallback>(write_callback));
    return async_write(stream_, tx_buff, handler);
}

template <typename NextLayer>
template <typename DynamicBuffer, typename ReadCallback>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadCallback,
                              void(const boost::system::error_code,
                                   BREDIS_PARSE_RESULT(DynamicBuffer)))
Connection<NextLayer>::async_read(DynamicBuffer &rx_buff,
                                  ReadCallback &&read_callback,
                                  std::size_t replies_count) {

    namespace asio = boost::asio;
    namespace sys = boost::system;
    using boost::asio::async_read_until;
    using Iterator = typename to_iterator<DynamicBuffer>::iterator_t;
    using Signature =
        void(boost::system::error_code, BREDIS_PARSE_RESULT(DynamicBuffer));
    using real_handler_t =
        typename asio::handler_type<ReadCallback, Signature>::type;
    using result_t = ::boost::asio::async_result<real_handler_t>;

    real_handler_t real_handler(std::forward<ReadCallback>(read_callback));
    asio::async_result<real_handler_t> async_result(real_handler);

    async_read_until(stream_, rx_buff, MatchResult<Iterator>(replies_count), [
        real_handler = std::move(real_handler), &rx_buff, replies_count
    ](const sys::error_code &error_code, std::size_t bytes_transferred) mutable {
        positive_parse_result_t<Iterator> result;

        if (error_code) {
            real_handler(error_code, std::move(result));
            return;
        }
        auto const_buff = rx_buff.data();
        auto begin = Iterator::begin(const_buff);
        auto end = Iterator::end(const_buff);

        markers::array_holder_t<Iterator> results;
        results.elements.reserve(replies_count);
        size_t cumulative_consumption = 0;
        boost::system::error_code ec;

        do {
            auto from = begin + cumulative_consumption;
            auto parse_result = Protocol::parse(from, end);
            auto *parse_error = boost::get<protocol_error_t>(&parse_result);
            if (parse_error) {
                auto parse_error_code =
                    Error::make_error_code(bredis_errors::protocol_error);
                real_handler(parse_error_code, std::move(result));
                return;
            }
            auto &positive_result =
                boost::get<positive_parse_result_t<Iterator>>(parse_result);
            results.elements.emplace_back(positive_result.result);
            cumulative_consumption += positive_result.consumed;
        } while (results.elements.size() < replies_count);

        if (replies_count == 1) {
            real_handler(ec, positive_parse_result_t<Iterator>{
                                 std::move(results.elements[0]),
                                 cumulative_consumption});
        } else {
            real_handler(ec, positive_parse_result_t<Iterator>{
                                 std::move(results), cumulative_consumption});
        }
    });
    return async_result.get();
}

template <typename NextLayer>
void Connection<NextLayer>::write(const command_wrapper_t &command,
                                  boost::system::error_code &ec) {
    namespace asio = boost::asio;
    auto str = boost::apply_visitor(command_serializer_visitor(), command);
    auto const output_buf = asio::buffer(str.c_str(), str.size());
    asio::write(stream_, output_buf, ec);
}

template <typename NextLayer>
void Connection<NextLayer>::write(const command_wrapper_t &command) {
    boost::system::error_code ec;
    this->write(command, ec);
    if (ec) {
        throw boost::system::system_error{ec};
    }
}

template <typename NextLayer>
template <typename DynamicBuffer>
positive_parse_result_t<typename to_iterator<DynamicBuffer>::iterator_t>
Connection<NextLayer>::read(DynamicBuffer &rx_buff,
                            boost::system::error_code &ec) {
    namespace asio = boost::asio;
    using boost::asio::read_until;
    using Iterator = typename to_iterator<DynamicBuffer>::iterator_t;
    using result_t = positive_parse_result_t<
        typename to_iterator<DynamicBuffer>::iterator_t>;

    auto rx_bytes = read_until(stream_, rx_buff, MatchResult<Iterator>(1), ec);
    if (ec) {
        return result_t{{}, 0};
    }

    auto const_buff = rx_buff.data();
    auto begin = Iterator::begin(const_buff);
    auto end = Iterator::end(const_buff);

    auto parse_result = Protocol::parse(begin, end);

    auto *parse_error = boost::get<protocol_error_t>(&parse_result);
    if (parse_error) {
        ec = Error::make_error_code(bredis_errors::protocol_error);
        return result_t{};
    }
    return boost::get<positive_parse_result_t<Iterator>>(parse_result);
}

template <typename NextLayer>
template <typename DynamicBuffer>
positive_parse_result_t<typename to_iterator<DynamicBuffer>::iterator_t>
Connection<NextLayer>::read(DynamicBuffer &rx_buff) {
    namespace asio = boost::asio;

    boost::system::error_code ec;
    auto result = this->read(rx_buff, ec);
    if (ec) {
        throw boost::system::system_error{ec};
    }
    return result;
}

} // namespace bredis
<|endoftext|>


<commit_before>#include <hadouken/scripting/modules/file_system_module.hpp>

#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/log/trivial.hpp>

#include <sstream>

#include "../duktape.h"

using namespace hadouken::scripting;
using namespace hadouken::scripting::modules;

namespace fs = boost::filesystem;

duk_ret_t file_system_module::initialize(duk_context* ctx)
{
    duk_function_list_entry functions[] =
    {
        { "combine",           combine,            DUK_VARARGS },
        { "createDirectories", create_directories, 1 },
        { "deleteFile",        delete_file,        1 },
        { "directoryExists",   directory_exists,   1 },
        { "fileExists",        file_exists,        1 },
        { "getFiles",          get_files,          1 },
        { "isRelative",        is_relative,        1 },
        { "makeAbsolute",      make_absolute,      1 },
        { "readBuffer",        read_buffer,        1 },
        { "readText",          read_text,          1 },
        { "rename",            rename,             2 },
        { "space",             space,              1 },
        { "writeBuffer",       write_buffer,       2 },
        { "writeText",         write_text,         2 },
        { NULL, NULL, 0 }
    };

    duk_put_function_list(ctx, 0 /* exports */, functions);
    return 0;
}

duk_ret_t file_system_module::combine(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    int args = duk_get_top(ctx);

    fs::path path(duk_require_string(ctx, 0), facet);

    for (int i = 1; i < duk_get_top(ctx); i++)
    {
        path /= duk_get_string(ctx, i);
    }

    duk_push_string(ctx, path.normalize().string(facet).c_str());
    return 1;
}

duk_ret_t file_system_module::create_directories(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    fs::create_directories(path);
    return 0;
}

duk_ret_t file_system_module::delete_file(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);

    if (!fs::exists(path))
    {
        duk_push_error_object(ctx, DUK_ERR_ERROR, "File not found: %s", path.string(facet).c_str());
        return 1;
    }

    if (!fs::is_regular_file(path))
    {
        duk_push_error_object(ctx, DUK_ERR_ERROR, "Path is not a file: %s", path.string(facet).c_str());
        return 1;
    }

    duk_push_boolean(ctx, fs::remove(path));
    return 1;
}

duk_ret_t file_system_module::directory_exists(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    duk_push_boolean(ctx, (fs::exists(path) && fs::is_directory(path)));
    return 1;
}

duk_ret_t file_system_module::file_exists(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    duk_push_boolean(ctx, (fs::exists(path) && fs::is_regular_file(path)));
    return 1;
}

duk_ret_t file_system_module::get_files(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);

    if (!fs::exists(path))
    {
        return 0;
    }

    int arrayIndex = duk_push_array(ctx);
    int i = 0;

    for (fs::path p : fs::directory_iterator(path))
    {
        duk_push_string(ctx, p.string(facet).c_str());
        duk_put_prop_index(ctx, arrayIndex, i);

        ++i;
    }

    return 1;
}

duk_ret_t file_system_module::is_relative(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    duk_push_boolean(ctx, path.is_relative());
    return 1;
}

duk_ret_t file_system_module::make_absolute(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    duk_push_string(ctx, fs::absolute(path).string(facet).c_str());
    return 1;
}

duk_ret_t file_system_module::rename(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path prev(duk_require_string(ctx, 0), facet);
    fs::path next(duk_require_string(ctx, 1), facet);

    fs::rename(prev, next);

    return 0;
}

duk_ret_t file_system_module::read_buffer(duk_context* ctx)
{
    void* buffer = nullptr;

    try
    {
        boost::filesystem::detail::utf8_codecvt_facet facet;
        fs::path path(duk_require_string(ctx, 0), facet);

        if (fs::exists(path))
        {
            boost::filesystem::ifstream file(path, std::ios::binary);

            file.seekg(0, std::ios_base::end);
            size_t size = file.tellg();
            buffer = duk_push_buffer(ctx, size, false);

            file.seekg(0, std::ios_base::beg);
            file.read(reinterpret_cast<char*>(buffer), size);

            return 1;
        }

        return 0;
    }
    catch (boost::filesystem::filesystem_error& ex)
    {
        BOOST_LOG_TRIVIAL(error) << "Failed to read file: " << ex.what();

        if (buffer)
        {
            duk_pop(ctx);
        }

        return 0;
    }
}

duk_ret_t file_system_module::read_text(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);

    if (fs::exists(path))
    {
        std::ifstream reader(path.string(), std::ios::binary);
        std::stringstream ss;

        std::copy(
            std::istreambuf_iterator<char>(reader),
            std::istreambuf_iterator<char>(),
            std::ostreambuf_iterator<char>(ss));

        duk_push_string(ctx, ss.str().c_str());
        return 1;
    }

    return 0;
}

duk_ret_t file_system_module::space(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    fs::space_info info = fs::space(path);

    duk_idx_t idx = duk_push_object(ctx);
    duk_push_number(ctx, info.available);
    duk_put_prop_string(ctx, idx, "available");

    duk_push_number(ctx, info.capacity);
    duk_put_prop_string(ctx, idx, "capacity");

    duk_push_number(ctx, info.free);
    duk_put_prop_string(ctx, idx, "free");

    return 1;
}

duk_ret_t file_system_module::write_buffer(duk_context* ctx)
{
    void* buffer = nullptr;

    try
    {
        boost::filesystem::detail::utf8_codecvt_facet facet;
        fs::path path(duk_require_string(ctx, 0), facet);

        duk_size_t size;
        const char* buffer = static_cast<const char*>(duk_require_buffer(ctx, 1, &size));

        boost::filesystem::ofstream file(path, std::ios::binary);

        file.write(buffer, size);
        duk_push_number(ctx, size);

        return 1;
    }
    catch (boost::filesystem::filesystem_error& ex)
    {
        BOOST_LOG_TRIVIAL(error) << "Failed to read file: " << ex.what();

        if (buffer)
        {
            duk_pop(ctx);
        }

        return 0;
    }
}

duk_ret_t file_system_module::write_text(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);

    duk_size_t size;
    const char* text = duk_require_lstring(ctx, 1, &size);

    std::FILE *fp = std::fopen(path.string().c_str(), "wb");

    if (fp)
    {
        size_t written = std::fwrite(text, sizeof(char), size, fp);
        std::fclose(fp);

        duk_push_number(ctx, written);
        return 1;
    }

    return 0;
}
<commit_msg>Use boost fstream functions for all duk file operations<commit_after>#include <hadouken/scripting/modules/file_system_module.hpp>

#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/log/trivial.hpp>
#include <boost/scoped_array.hpp>

#include <sstream>
#include <functional>
#include <memory>

#include "../duktape.h"

using namespace hadouken::scripting;
using namespace hadouken::scripting::modules;

namespace fs = boost::filesystem;

size_t read_impl(const fs::path& path, std::function<char*(size_t)> alloc)
{
    try
    {
        if (fs::exists(path))
        {
            boost::filesystem::ifstream file(path, std::ios::binary);

            file.seekg(0, std::ios_base::end);
            size_t size = file.tellg();
            char* buffer = alloc(size);

            file.seekg(0, std::ios_base::beg);
            file.read(buffer, size);

            return size;
        }
    }
    catch (boost::filesystem::filesystem_error& ex)
    {
        BOOST_LOG_TRIVIAL(error) << "Failed to read file: " << ex.what();

        return 0;
    }
}

size_t write_impl(const fs::path& path, const char* data, size_t size)
{
    try
    {
        boost::filesystem::ofstream file(path, std::ios::binary);

        file.write(data, size);

        return size;
    }
    catch (boost::filesystem::filesystem_error& ex)
    {
        BOOST_LOG_TRIVIAL(error) << "Failed to write file: " << ex.what();

        return 0;
    }
}

duk_ret_t file_system_module::initialize(duk_context* ctx)
{
    duk_function_list_entry functions[] =
    {
        { "combine",           combine,            DUK_VARARGS },
        { "createDirectories", create_directories, 1 },
        { "deleteFile",        delete_file,        1 },
        { "directoryExists",   directory_exists,   1 },
        { "fileExists",        file_exists,        1 },
        { "getFiles",          get_files,          1 },
        { "isRelative",        is_relative,        1 },
        { "makeAbsolute",      make_absolute,      1 },
        { "readBuffer",        read_buffer,        1 },
        { "readText",          read_text,          1 },
        { "rename",            rename,             2 },
        { "space",             space,              1 },
        { "writeBuffer",       write_buffer,       2 },
        { "writeText",         write_text,         2 },
        { NULL, NULL, 0 }
    };

    duk_put_function_list(ctx, 0 /* exports */, functions);
    return 0;
}

duk_ret_t file_system_module::combine(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    int args = duk_get_top(ctx);

    fs::path path(duk_require_string(ctx, 0), facet);

    for (int i = 1; i < duk_get_top(ctx); i++)
    {
        path /= duk_get_string(ctx, i);
    }

    duk_push_string(ctx, path.normalize().string(facet).c_str());
    return 1;
}

duk_ret_t file_system_module::create_directories(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    fs::create_directories(path);
    return 0;
}

duk_ret_t file_system_module::delete_file(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);

    if (!fs::exists(path))
    {
        duk_push_error_object(ctx, DUK_ERR_ERROR, "File not found: %s", path.string(facet).c_str());
        return 1;
    }

    if (!fs::is_regular_file(path))
    {
        duk_push_error_object(ctx, DUK_ERR_ERROR, "Path is not a file: %s", path.string(facet).c_str());
        return 1;
    }

    duk_push_boolean(ctx, fs::remove(path));
    return 1;
}

duk_ret_t file_system_module::directory_exists(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    duk_push_boolean(ctx, (fs::exists(path) && fs::is_directory(path)));
    return 1;
}

duk_ret_t file_system_module::file_exists(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    duk_push_boolean(ctx, (fs::exists(path) && fs::is_regular_file(path)));
    return 1;
}

duk_ret_t file_system_module::get_files(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);

    if (!fs::exists(path))
    {
        return 0;
    }

    int arrayIndex = duk_push_array(ctx);
    int i = 0;

    for (fs::path p : fs::directory_iterator(path))
    {
        duk_push_string(ctx, p.string(facet).c_str());
        duk_put_prop_index(ctx, arrayIndex, i);

        ++i;
    }

    return 1;
}

duk_ret_t file_system_module::is_relative(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    duk_push_boolean(ctx, path.is_relative());
    return 1;
}

duk_ret_t file_system_module::make_absolute(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    duk_push_string(ctx, fs::absolute(path).string(facet).c_str());
    return 1;
}

duk_ret_t file_system_module::rename(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path prev(duk_require_string(ctx, 0), facet);
    fs::path next(duk_require_string(ctx, 1), facet);

    fs::rename(prev, next);

    return 0;
}

duk_ret_t file_system_module::read_buffer(duk_context* ctx)
{
    try
    {
        boost::filesystem::detail::utf8_codecvt_facet facet;
        fs::path path(duk_require_string(ctx, 0), facet);

        auto deleter = [&ctx](char*){ duk_pop(ctx); }; // In case of failure, make sure any allocated buffer is popped off the stack before returning to duk
        std::unique_ptr<char, decltype(deleter)> buffer(nullptr, deleter);

        size_t size_read = read_impl(path, [&ctx, &buffer](size_t size)-> char*
        {
            void* buf = duk_push_buffer(ctx, size, false);
            buffer.reset(reinterpret_cast<char*>(buf));
            return buffer.get();
        });

        if (size_read > 0 && buffer)
        {
            buffer.release();
            return 1;
        }
    }
    catch (std::exception&)
    {
    }

    return 0;
}

duk_ret_t file_system_module::read_text(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);

    try
    {
        boost::scoped_array<char> data;
        size_t size_read = read_impl(path, [&data](size_t size)-> char*
        {
            data.reset(new char[size + 1 /*+1 for null-terminator*/]);
            return data.get();
        });

        if (size_read > 0 && data)
        {
            data[size_read] = '\0'; // Data is not guaranteed to be null-terminated, so add an explicit null at the end
            duk_push_string(ctx, data.get());
            return 1;
        }
    }
    catch (std::exception&)
    {
    }

    return 0;
}

duk_ret_t file_system_module::space(duk_context* ctx)
{
    boost::filesystem::detail::utf8_codecvt_facet facet;
    fs::path path(duk_require_string(ctx, 0), facet);
    fs::space_info info = fs::space(path);

    duk_idx_t idx = duk_push_object(ctx);
    duk_push_number(ctx, info.available);
    duk_put_prop_string(ctx, idx, "available");

    duk_push_number(ctx, info.capacity);
    duk_put_prop_string(ctx, idx, "capacity");

    duk_push_number(ctx, info.free);
    duk_put_prop_string(ctx, idx, "free");

    return 1;
}

duk_ret_t file_system_module::write_buffer(duk_context* ctx)
{
    try
    {
        boost::filesystem::detail::utf8_codecvt_facet facet;
        fs::path path(duk_require_string(ctx, 0), facet);

        duk_size_t size;
        const char* buffer = static_cast<const char*>(duk_require_buffer(ctx, 1, &size));

        duk_size_t size_written = write_impl(path, buffer, size);

        if (size_written == size)
        {
            duk_push_number(ctx, size_written);
            return 1;
        }
    }
    catch (std::exception&)
    {
    }

    return 0;
}

duk_ret_t file_system_module::write_text(duk_context* ctx)
{
    try
    {
        boost::filesystem::detail::utf8_codecvt_facet facet;
        fs::path path(duk_require_string(ctx, 0), facet);

        duk_size_t size;
        const char* text = duk_require_lstring(ctx, 1, &size);

        duk_size_t size_written = write_impl(path, text, size);

        if (size_written == size)
        {
            duk_push_number(ctx, size_written);
            return 1;
        }
    }
    catch (std::exception&)
    {
    }

    return 0;
}
<|endoftext|>


<commit_before>#include <Arduino.h>
#include "HaskinoCodeBlock.h"
#include "HaskinoComm.h"
#include "HaskinoCommands.h"
#include "HaskinoConfig.h"
#include "HaskinoExpr.h"
#include "HaskinoRefs.h"

typedef struct ref_record {
    void *ref;
    byte type;
    byte len;   // Number of type elements in ref
} REF_RECORD;

static REF_RECORD haskinoRefs[MAX_REFS];
static int nextFreeRefIndex = 0;

static bool handleNewRef(int type, int size, const byte *msg, byte *local);
static bool handleReadRef(int type, int size, const byte *msg, byte *local);
static bool handleWriteRef(int type, int size, const byte *msg);
static bool handleWriteEffectRef(int type, int size, const byte *msg);

bool parseRefMessage(int size, const byte *msg, byte *local)
    {
    byte cmd = msg[0];
    byte type = msg[1];

    switch (cmd)
        {
        case REF_CMD_NEW:
            handleNewRef(type, size, msg, local);
            break;
        case REF_CMD_READ:
            handleReadRef(type, size, msg, local);
            break;
        case REF_CMD_WRITE:
            handleWriteRef(type, size, msg);
            break;
        case REF_CMD_WRITE_EFFECT:
            handleWriteEffectRef(type, size, msg);
            break;
        }
    return false;
    }

bool readRefBool(int refIndex)
    {
    if (refIndex < nextFreeRefIndex)
        return *((bool *) haskinoRefs[refIndex].ref);
    else
        {
        sendString("readRefBool: Invalid ref index");
        return false;
        }
    }

uint8_t readRefWord8(int refIndex)
    {
    if (refIndex < nextFreeRefIndex)
        return *((uint8_t *) haskinoRefs[refIndex].ref);
    else
        {
        sendString("readRefWord8: Invalid ref index");
        return false;
        }
    }

uint16_t readRefWord16(int refIndex)
    {
    if (refIndex < nextFreeRefIndex)
        return *((uint16_t *) haskinoRefs[refIndex].ref);
    else
        {
        sendString("readRefWord16: Invalid ref index");
        return false;
        }
    }

uint32_t readRefWord32(int refIndex)
    {
    if (refIndex < nextFreeRefIndex)
        return *((uint32_t *) haskinoRefs[refIndex].ref);
    else
        {
        sendString("readRefWord32: Invalid ref index");
        return false;
        }
    }

static int typeToSize(int type)
    {
    switch(type)
        {
        case EXPR_BOOL:
        case EXPR_WORD16:
            return 2;
        case EXPR_WORD8:
            return 1;
        case EXPR_WORD32:
        default:
            return 4;
        }
    }

static bool handleNewRef(int type, int size, const byte *msg, byte *local)
    {
    int count = 1;
    int spaceNeeded = typeToSize(type) * count;
    void *memory;
    byte refIndex;

    if ((nextFreeRefIndex >= MAX_REFS) ||
        ((memory = malloc(spaceNeeded)) == NULL))
        {
        // ToDo:  Verify sending empty message can indicate error
        sendReply(0, REF_RESP_NEW, NULL, local);
        }
    else
        {
        haskinoRefs[nextFreeRefIndex].ref = memory;
        haskinoRefs[nextFreeRefIndex].type = type;
        haskinoRefs[nextFreeRefIndex].len = count;
        refIndex = nextFreeRefIndex;
        sendReply(sizeof(byte), REF_RESP_NEW, (byte *) &refIndex, local);
        nextFreeRefIndex++;
        }
    return false;
    }

// ToDo:  Is this needed?  Probably not, perhaps a debug mechanism.
static bool handleReadRef(int type, int size, const byte *msg, byte *local)
    {
    byte refIndex = msg[2];
    bool bVal;
    uint8_t w8Val;
    uint16_t w16Val;
    uint32_t w32Val;

    // ToDo:  Check for param errors

    switch (type)
        {
        case EXPR_BOOL:
            bVal = *((bool *) haskinoRefs[refIndex].ref);
            sendReply(sizeof(bool), REF_RESP_READ, (byte *) &bVal, local);
            break;
        case EXPR_WORD8:
            w8Val = *((uint8_t *) haskinoRefs[refIndex].ref);
            sendReply(sizeof(uint8_t), REF_RESP_READ, (byte *) &w8Val, local);
            break;
        case EXPR_WORD16:
            w16Val = *((uint16_t *) haskinoRefs[refIndex].ref);
            sendReply(sizeof(uint16_t), REF_RESP_READ, (byte *) &w16Val, local);
            break;
        case EXPR_WORD32:
            w32Val = *((uint32_t *) haskinoRefs[refIndex].ref);
            sendReply(sizeof(uint32_t), REF_RESP_READ, (byte *) &w32Val, local);
            break;
        }
    return false;
    }

static bool handleWriteRef(int type, int size, const byte *msg)
    {
    byte refIndex = msg[2];
    byte *expr = (byte *) &msg[3];
    bool bVal;
    uint8_t w8Val;
    uint16_t w16Val;
    uint32_t w32Val;

    // ToDo:  Check for param errors

    switch (type)
        {
        case EXPR_BOOL:
            bVal = evalBoolExpr(&expr);
            *((bool *) haskinoRefs[refIndex].ref) = bVal;
            break;
        case EXPR_WORD8:
            w8Val = evalWord8Expr(&expr);
            *((uint8_t *) haskinoRefs[refIndex].ref) = w8Val;
            break;
        case EXPR_WORD16:
            w16Val = evalWord16Expr(&expr);
            *((uint16_t *) haskinoRefs[refIndex].ref) = w16Val;
            break;
        case EXPR_WORD32:
            w32Val = evalWord32Expr(&expr);
            *((uint32_t *) haskinoRefs[refIndex].ref) = w32Val;
            break;
        }
    return false;
    }

static bool handleWriteEffectRef(int type, int size, const byte *msg)
    {
    byte refIndex = msg[2];
    byte *codeBlock = (byte *) &msg[3];
    int blockSize = size - 3;
    bool bVal;
    uint8_t w8Val;
    uint16_t w16Val;
    uint32_t w32Val;
    uint32_t val;

    // ToDo:  Check for param errors
    runCodeBlock(blockSize, codeBlock,(byte *) &val);

    switch (type)
        {
        case EXPR_BOOL:
            memcpy((byte *) &bVal, (const byte *) &val, sizeof(bVal));
            *((bool *) haskinoRefs[refIndex].ref) = bVal;
            break;
        case EXPR_WORD8:
            memcpy((byte *) &w8Val, (const byte *) &val, sizeof(w8Val));
            *((uint8_t *) haskinoRefs[refIndex].ref) = w8Val;
            break;
        case EXPR_WORD16:
            memcpy((byte *) &w16Val, (const byte *) &val, sizeof(w16Val));
            *((uint16_t *) haskinoRefs[refIndex].ref) = w16Val;
            break;
        case EXPR_WORD32:
            memcpy((byte *) &w32Val, (const byte *) &val, sizeof(w32Val));
            *((uint32_t *) haskinoRefs[refIndex].ref) = w32Val;
            break;
        }
    return false;
    }
<commit_msg>Added formatted sendStringf to firmware<commit_after>#include <Arduino.h>
#include "HaskinoCodeBlock.h"
#include "HaskinoComm.h"
#include "HaskinoCommands.h"
#include "HaskinoConfig.h"
#include "HaskinoExpr.h"
#include "HaskinoRefs.h"

typedef struct ref_record {
    void *ref;
    byte type;
    byte len;   // Number of type elements in ref
} REF_RECORD;

static REF_RECORD haskinoRefs[MAX_REFS];
static int nextFreeRefIndex = 0;

static bool handleNewRef(int type, int size, const byte *msg, byte *local);
static bool handleReadRef(int type, int size, const byte *msg, byte *local);
static bool handleWriteRef(int type, int size, const byte *msg);
static bool handleWriteEffectRef(int type, int size, const byte *msg);

bool parseRefMessage(int size, const byte *msg, byte *local)
    {
    byte cmd = msg[0];
    byte type = msg[1];

    switch (cmd)
        {
        case REF_CMD_NEW:
            handleNewRef(type, size, msg, local);
            break;
        case REF_CMD_READ:
            handleReadRef(type, size, msg, local);
            break;
        case REF_CMD_WRITE:
            handleWriteRef(type, size, msg);
            break;
        case REF_CMD_WRITE_EFFECT:
            handleWriteEffectRef(type, size, msg);
            break;
        }
    return false;
    }

bool readRefBool(int refIndex)
    {
    if (refIndex < nextFreeRefIndex)
        return *((bool *) haskinoRefs[refIndex].ref);
    else
        {
        sendStringf("readRefBool: Invalid ref index %d", refIndex);
        return false;
        }
    }

uint8_t readRefWord8(int refIndex)
    {
    if (refIndex < nextFreeRefIndex)
        return *((uint8_t *) haskinoRefs[refIndex].ref);
    else
        {
        sendStringf("readRefWord8: Invalid ref index %d", refIndex);
        return false;
        }
    }

uint16_t readRefWord16(int refIndex)
    {
    if (refIndex < nextFreeRefIndex)
        return *((uint16_t *) haskinoRefs[refIndex].ref);
    else
        {
        sendStringf("readRefWord16: Invalid ref index %d", refIndex);
        return false;
        }
    }

uint32_t readRefWord32(int refIndex)
    {
    if (refIndex < nextFreeRefIndex)
        return *((uint32_t *) haskinoRefs[refIndex].ref);
    else
        {
        sendStringf("readRefWord32: Invalid ref index %d", refIndex);
        return false;
        }
    }

static int typeToSize(int type)
    {
    switch(type)
        {
        case EXPR_BOOL:
        case EXPR_WORD16:
            return 2;
        case EXPR_WORD8:
            return 1;
        case EXPR_WORD32:
        default:
            return 4;
        }
    }

static bool handleNewRef(int type, int size, const byte *msg, byte *local)
    {
    int count = 1;
    int spaceNeeded = typeToSize(type) * count;
    void *memory;
    byte refIndex;

    if ((nextFreeRefIndex >= MAX_REFS) ||
        ((memory = malloc(spaceNeeded)) == NULL))
        {
        // ToDo:  Verify sending empty message can indicate error
        sendReply(0, REF_RESP_NEW, NULL, local);
        }
    else
        {
        haskinoRefs[nextFreeRefIndex].ref = memory;
        haskinoRefs[nextFreeRefIndex].type = type;
        haskinoRefs[nextFreeRefIndex].len = count;
        refIndex = nextFreeRefIndex;
        sendReply(sizeof(byte), REF_RESP_NEW, (byte *) &refIndex, local);
        nextFreeRefIndex++;
        }
    return false;
    }

// ToDo:  Is this needed?  Probably not, perhaps a debug mechanism.
static bool handleReadRef(int type, int size, const byte *msg, byte *local)
    {
    byte refIndex = msg[2];
    bool bVal;
    uint8_t w8Val;
    uint16_t w16Val;
    uint32_t w32Val;

    // ToDo:  Check for param errors

    switch (type)
        {
        case EXPR_BOOL:
            bVal = *((bool *) haskinoRefs[refIndex].ref);
            sendReply(sizeof(bool), REF_RESP_READ, (byte *) &bVal, local);
            break;
        case EXPR_WORD8:
            w8Val = *((uint8_t *) haskinoRefs[refIndex].ref);
            sendReply(sizeof(uint8_t), REF_RESP_READ, (byte *) &w8Val, local);
            break;
        case EXPR_WORD16:
            w16Val = *((uint16_t *) haskinoRefs[refIndex].ref);
            sendReply(sizeof(uint16_t), REF_RESP_READ, (byte *) &w16Val, local);
            break;
        case EXPR_WORD32:
            w32Val = *((uint32_t *) haskinoRefs[refIndex].ref);
            sendReply(sizeof(uint32_t), REF_RESP_READ, (byte *) &w32Val, local);
            break;
        }
    return false;
    }

static bool handleWriteRef(int type, int size, const byte *msg)
    {
    byte refIndex = msg[2];
    byte *expr = (byte *) &msg[3];
    bool bVal;
    uint8_t w8Val;
    uint16_t w16Val;
    uint32_t w32Val;

    // ToDo:  Check for param errors

    switch (type)
        {
        case EXPR_BOOL:
            bVal = evalBoolExpr(&expr);
            *((bool *) haskinoRefs[refIndex].ref) = bVal;
            break;
        case EXPR_WORD8:
            w8Val = evalWord8Expr(&expr);
            *((uint8_t *) haskinoRefs[refIndex].ref) = w8Val;
            break;
        case EXPR_WORD16:
            w16Val = evalWord16Expr(&expr);
            *((uint16_t *) haskinoRefs[refIndex].ref) = w16Val;
            break;
        case EXPR_WORD32:
            w32Val = evalWord32Expr(&expr);
            *((uint32_t *) haskinoRefs[refIndex].ref) = w32Val;
            break;
        }
    return false;
    }

static bool handleWriteEffectRef(int type, int size, const byte *msg)
    {
    byte refIndex = msg[2];
    byte *codeBlock = (byte *) &msg[3];
    int blockSize = size - 3;
    bool bVal;
    uint8_t w8Val;
    uint16_t w16Val;
    uint32_t w32Val;
    uint32_t val;

    // ToDo:  Check for param errors
    runCodeBlock(blockSize, codeBlock,(byte *) &val);

    switch (type)
        {
        case EXPR_BOOL:
            memcpy((byte *) &bVal, (const byte *) &val, sizeof(bVal));
            *((bool *) haskinoRefs[refIndex].ref) = bVal;
            break;
        case EXPR_WORD8:
            memcpy((byte *) &w8Val, (const byte *) &val, sizeof(w8Val));
            *((uint8_t *) haskinoRefs[refIndex].ref) = w8Val;
            break;
        case EXPR_WORD16:
            memcpy((byte *) &w16Val, (const byte *) &val, sizeof(w16Val));
            *((uint16_t *) haskinoRefs[refIndex].ref) = w16Val;
            break;
        case EXPR_WORD32:
            memcpy((byte *) &w32Val, (const byte *) &val, sizeof(w32Val));
            *((uint32_t *) haskinoRefs[refIndex].ref) = w32Val;
            break;
        }
    return false;
    }
<|endoftext|>


<commit_before>/****************************************************************************

 This file is part of the GLC-lib library.
 Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
 Version 1.2.0, packaged on September 2009.

 http://glc-lib.sourceforge.net

 GLC-lib is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 GLC-lib is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with GLC-lib; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

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

//! \file glc_extendedgeomengine.cpp Implementation for the GLC_ExtendedGeomEngine class.

#include "glc_extendedgeomengine.h"
#include "../glc_state.h"

// Default constructor
GLC_ExtendedGeomEngine::GLC_ExtendedGeomEngine()
: GLC_GeomEngine()
, m_Positions()
, m_Normals()
, m_Texels()
, m_Colors()
, m_NormalVboId(0)
, m_TexelVboId(0)
, m_ColorVboId(0)
, m_EngineLodList()
, m_PositionSize(0)
, m_TexelsSize(0)
, m_ColorSize(0)
{
	//m_EngineLodList.append(new GLC_EngineLod());

}

// Copy constructor
GLC_ExtendedGeomEngine::GLC_ExtendedGeomEngine(const GLC_ExtendedGeomEngine& engine)
: GLC_GeomEngine(engine)
, m_Positions(engine.positionVector())
, m_Normals(engine.normalVector())
, m_Texels(engine.texelVector())
, m_Colors(engine.colorVector())
, m_NormalVboId(0)
, m_TexelVboId(0)
, m_ColorVboId(0)
, m_EngineLodList()
, m_PositionSize(engine.m_PositionSize)
, m_TexelsSize(engine.m_TexelsSize)
, m_ColorSize(engine.m_ColorSize)
{

	const int size= engine.m_EngineLodList.size();
	for (int i= 0; i < size; ++i)
	{
		m_EngineLodList.append(new GLC_EngineLod(*engine.m_EngineLodList.at(i)));
	}

}

GLC_ExtendedGeomEngine::~GLC_ExtendedGeomEngine()
{

	// Delete Normal VBO
	if (0 != m_NormalVboId)
	{
		glDeleteBuffers(1, &m_NormalVboId);
	}

	// Delete Texel VBO
	if (0 != m_TexelVboId)
	{
		glDeleteBuffers(1, &m_TexelVboId);
	}

	// Delete Color VBO
	if (0 != m_ColorVboId)
	{
		glDeleteBuffers(1, &m_ColorVboId);
	}

	const int size= m_EngineLodList.size();
	for (int i= 0; i < size; ++i)
	{
		delete m_EngineLodList.at(i);
	}
}
//////////////////////////////////////////////////////////////////////
// Get Functions
//////////////////////////////////////////////////////////////////////

// Return the Position Vector
GLfloatVector GLC_ExtendedGeomEngine::positionVector() const
{
	if (0 != m_VboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= m_PositionSize;
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(float);
		GLfloatVector positionVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_VboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(positionVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return positionVector;
	}
	else
	{
		return m_Positions;
	}

}

// Return the normal Vector
GLfloatVector GLC_ExtendedGeomEngine::normalVector() const
{
	if (0 != m_NormalVboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= m_PositionSize;
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat);
		GLfloatVector normalVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_NormalVboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(normalVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return normalVector;
	}
	else
	{
		return m_Normals;
	}
}

// Return the texel Vector
GLfloatVector GLC_ExtendedGeomEngine::texelVector() const
{
	if (0 != m_TexelVboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= m_TexelsSize;
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat);
		GLfloatVector texelVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_TexelVboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(texelVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return texelVector;
	}
	else
	{
		return m_Texels;
	}
}

// Return the color Vector
GLfloatVector GLC_ExtendedGeomEngine::colorVector() const
{
	if (0 != m_ColorVboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= m_ColorSize;
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat);
		GLfloatVector normalVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_ColorVboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(normalVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return normalVector;
	}
	else
	{
		return m_Colors;
	}
}

//////////////////////////////////////////////////////////////////////
// Set Functions
//////////////////////////////////////////////////////////////////////

// The mesh wich use this engine is finished
void GLC_ExtendedGeomEngine::finished()
{
	m_PositionSize= m_Positions.size();
	m_Positions.clear();
	m_Normals.clear();
	m_TexelsSize= m_Texels.size();
	m_Texels.clear();
	m_ColorSize= m_Colors.size();
	m_Colors.clear();

	// Finish the LOD
	const int size= m_EngineLodList.size();
	for (int i= 0; i < size; ++i)
	{
		m_EngineLodList[i]->finished();
	}
}
// If the there is more than 2 LOD Swap the first and last
void GLC_ExtendedGeomEngine::finishedLod()
{
	// PLace the master LOD at the beginning of the list
	const int size= m_EngineLodList.size();
	if (size > 1)
	{
		GLC_EngineLod* PMasterLod= m_EngineLodList.at(size - 1);
		m_EngineLodList.removeAt(size - 1);
		m_EngineLodList.prepend(PMasterLod);
	}
}

// Clear the engine
void GLC_ExtendedGeomEngine::clear()
{
	GLC_GeomEngine::clear();
	m_Positions.clear();
	m_Normals.clear();
	m_Texels.clear();
	m_PositionSize= 0;
	m_TexelsSize= 0;

	// Delete Normal VBO
	if (0 != m_NormalVboId)
	{
		glDeleteBuffers(1, &m_NormalVboId);
		m_NormalVboId= 0;
	}

	// Delete Texel VBO
	if (0 != m_TexelVboId)
	{
		glDeleteBuffers(1, &m_TexelVboId);
		m_TexelVboId= 0;
	}

	const int size= m_EngineLodList.size();
	for (int i= 0; i < size; ++i)
	{
		delete m_EngineLodList.at(i);
	}
	m_EngineLodList.clear();
}

//////////////////////////////////////////////////////////////////////
// OpenGL Functions
//////////////////////////////////////////////////////////////////////
//! Vbo creation
void GLC_ExtendedGeomEngine::createVBOs()
{
	// Create position VBO
	if (0 == m_VboId)
	{
		glGenBuffers(1, &m_VboId);
		glGenBuffers(1, &m_NormalVboId);

		// Create Texel VBO
		if (0 == m_TexelVboId && !m_Texels.isEmpty())
		{
			glGenBuffers(1, &m_TexelVboId);
		}

		// Create Color VBO
		if (0 == m_ColorVboId && !m_Colors.isEmpty())
		{
			glGenBuffers(1, &m_ColorVboId);
		}

		const int size= m_EngineLodList.size();
		for (int i= 0; i < size; ++i)
		{
			m_EngineLodList.at(i)->createIBO();
		}
	}
}
//! Ibo Usage
bool GLC_ExtendedGeomEngine::useVBO(bool use, GLC_ExtendedGeomEngine::VboType type)
{
	bool result= true;
	if (use)
	{
		// Chose the right VBO
		if (type == GLC_ExtendedGeomEngine::GLC_Vertex)
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_VboId);
		}
		else if (type == GLC_ExtendedGeomEngine::GLC_Normal)
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_NormalVboId);
		}
		else if ((type == GLC_ExtendedGeomEngine::GLC_Texel) && (0 != m_TexelVboId))
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_TexelVboId);
		}
		else if ((type == GLC_ExtendedGeomEngine::GLC_Color) && (0 != m_ColorVboId))
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_ColorVboId);
		}

		else result= false;
	}
	else
	{
		// Unbind VBO
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}
	return result;

}
// Non Member methods
#define GLC_BINARY_CHUNK_ID 0xA704

// Non-member stream operator
QDataStream &operator<<(QDataStream &stream, const GLC_ExtendedGeomEngine &engine)
{
	quint32 chunckId= GLC_BINARY_CHUNK_ID;
	stream << chunckId;

	stream << engine.positionVector();
	stream << engine.normalVector();
	stream << engine.texelVector();
	stream << engine.colorVector();

	// List of lod serialisation
	const int lodCount= engine.m_EngineLodList.size();
	QList<GLC_EngineLod> lodsList;
	for (int i= 0; i < lodCount; ++i)
	{
		lodsList.append(*(engine.m_EngineLodList[i]));
	}
	stream << lodsList;

	return stream;
}
QDataStream &operator>>(QDataStream &stream, GLC_ExtendedGeomEngine &engine)
{
	quint32 chunckId;
	stream >> chunckId;
	Q_ASSERT(chunckId == GLC_BINARY_CHUNK_ID);

	engine.clear();

	GLfloatVector position, normal, texel, color;
	stream >> position >> normal >> texel >> color;
	*(engine.positionVectorHandle())= position;
	*(engine.normalVectorHandle())= normal;
	*(engine.texelVectorHandle())= texel;
	*(engine.colorVectorHandle())= color;

	// List of lod serialisation
	QList<GLC_EngineLod> lodsList;
	stream >> lodsList;
	const int lodCount= lodsList.size();
	for (int i= 0; i < lodCount; ++i)
	{
		engine.m_EngineLodList.append(new GLC_EngineLod(lodsList.at(i)));
	}

	return stream;
}

<commit_msg>Minor presentation changes.<commit_after>/****************************************************************************

 This file is part of the GLC-lib library.
 Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
 Version 1.2.0, packaged on September 2009.

 http://glc-lib.sourceforge.net

 GLC-lib is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 GLC-lib is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with GLC-lib; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

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

//! \file glc_extendedgeomengine.cpp Implementation for the GLC_ExtendedGeomEngine class.

#include "glc_extendedgeomengine.h"
#include "../glc_state.h"

// Default constructor
GLC_ExtendedGeomEngine::GLC_ExtendedGeomEngine()
: GLC_GeomEngine()
, m_Positions()
, m_Normals()
, m_Texels()
, m_Colors()
, m_NormalVboId(0)
, m_TexelVboId(0)
, m_ColorVboId(0)
, m_EngineLodList()
, m_PositionSize(0)
, m_TexelsSize(0)
, m_ColorSize(0)
{
	//m_EngineLodList.append(new GLC_EngineLod());

}

// Copy constructor
GLC_ExtendedGeomEngine::GLC_ExtendedGeomEngine(const GLC_ExtendedGeomEngine& engine)
: GLC_GeomEngine(engine)
, m_Positions(engine.positionVector())
, m_Normals(engine.normalVector())
, m_Texels(engine.texelVector())
, m_Colors(engine.colorVector())
, m_NormalVboId(0)
, m_TexelVboId(0)
, m_ColorVboId(0)
, m_EngineLodList()
, m_PositionSize(engine.m_PositionSize)
, m_TexelsSize(engine.m_TexelsSize)
, m_ColorSize(engine.m_ColorSize)
{

	const int size= engine.m_EngineLodList.size();
	for (int i= 0; i < size; ++i)
	{
		m_EngineLodList.append(new GLC_EngineLod(*engine.m_EngineLodList.at(i)));
	}

}

GLC_ExtendedGeomEngine::~GLC_ExtendedGeomEngine()
{

	// Delete Normal VBO
	if (0 != m_NormalVboId)
	{
		glDeleteBuffers(1, &m_NormalVboId);
	}

	// Delete Texel VBO
	if (0 != m_TexelVboId)
	{
		glDeleteBuffers(1, &m_TexelVboId);
	}

	// Delete Color VBO
	if (0 != m_ColorVboId)
	{
		glDeleteBuffers(1, &m_ColorVboId);
	}

	const int size= m_EngineLodList.size();
	for (int i= 0; i < size; ++i)
	{
		delete m_EngineLodList.at(i);
	}
}
//////////////////////////////////////////////////////////////////////
// Get Functions
//////////////////////////////////////////////////////////////////////

// Return the Position Vector
GLfloatVector GLC_ExtendedGeomEngine::positionVector() const
{
	if (0 != m_VboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= m_PositionSize;
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(float);
		GLfloatVector positionVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_VboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(positionVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return positionVector;
	}
	else
	{
		return m_Positions;
	}

}

// Return the normal Vector
GLfloatVector GLC_ExtendedGeomEngine::normalVector() const
{
	if (0 != m_NormalVboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= m_PositionSize;
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat);
		GLfloatVector normalVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_NormalVboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(normalVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return normalVector;
	}
	else
	{
		return m_Normals;
	}
}

// Return the texel Vector
GLfloatVector GLC_ExtendedGeomEngine::texelVector() const
{
	if (0 != m_TexelVboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= m_TexelsSize;
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat);
		GLfloatVector texelVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_TexelVboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(texelVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return texelVector;
	}
	else
	{
		return m_Texels;
	}
}

// Return the color Vector
GLfloatVector GLC_ExtendedGeomEngine::colorVector() const
{
	if (0 != m_ColorVboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= m_ColorSize;
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat);
		GLfloatVector normalVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_ColorVboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(normalVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return normalVector;
	}
	else
	{
		return m_Colors;
	}
}

//////////////////////////////////////////////////////////////////////
// Set Functions
//////////////////////////////////////////////////////////////////////

// The mesh wich use this engine is finished
void GLC_ExtendedGeomEngine::finished()
{
	m_PositionSize= m_Positions.size();
	m_Positions.clear();
	m_Normals.clear();
	m_TexelsSize= m_Texels.size();
	m_Texels.clear();
	m_ColorSize= m_Colors.size();
	m_Colors.clear();

	// Finish the LOD
	const int size= m_EngineLodList.size();
	for (int i= 0; i < size; ++i)
	{
		m_EngineLodList[i]->finished();
	}
}
// If the there is more than 2 LOD Swap the first and last
void GLC_ExtendedGeomEngine::finishedLod()
{
	// PLace the master LOD at the beginning of the list
	const int size= m_EngineLodList.size();
	if (size > 1)
	{
		GLC_EngineLod* PMasterLod= m_EngineLodList.at(size - 1);
		m_EngineLodList.removeAt(size - 1);
		m_EngineLodList.prepend(PMasterLod);
	}
}

// Clear the engine
void GLC_ExtendedGeomEngine::clear()
{
	GLC_GeomEngine::clear();
	m_Positions.clear();
	m_Normals.clear();
	m_Texels.clear();
	m_PositionSize= 0;
	m_TexelsSize= 0;

	// Delete Normal VBO
	if (0 != m_NormalVboId)
	{
		glDeleteBuffers(1, &m_NormalVboId);
		m_NormalVboId= 0;
	}

	// Delete Texel VBO
	if (0 != m_TexelVboId)
	{
		glDeleteBuffers(1, &m_TexelVboId);
		m_TexelVboId= 0;
	}

	const int size= m_EngineLodList.size();
	for (int i= 0; i < size; ++i)
	{
		delete m_EngineLodList.at(i);
	}
	m_EngineLodList.clear();
}

//////////////////////////////////////////////////////////////////////
// OpenGL Functions
//////////////////////////////////////////////////////////////////////
//! Vbo creation
void GLC_ExtendedGeomEngine::createVBOs()
{
	// Create position VBO
	if (0 == m_VboId)
	{
		glGenBuffers(1, &m_VboId);
		glGenBuffers(1, &m_NormalVboId);

		// Create Texel VBO
		if (0 == m_TexelVboId && !m_Texels.isEmpty())
		{
			glGenBuffers(1, &m_TexelVboId);
		}

		// Create Color VBO
		if (0 == m_ColorVboId && !m_Colors.isEmpty())
		{
			glGenBuffers(1, &m_ColorVboId);
		}

		const int size= m_EngineLodList.size();
		for (int i= 0; i < size; ++i)
		{
			m_EngineLodList.at(i)->createIBO();
		}
	}
}
//! Ibo Usage
bool GLC_ExtendedGeomEngine::useVBO(bool use, GLC_ExtendedGeomEngine::VboType type)
{
	bool result= true;
	if (use)
	{
		// Chose the right VBO
		if (type == GLC_ExtendedGeomEngine::GLC_Vertex)
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_VboId);
		}
		else if (type == GLC_ExtendedGeomEngine::GLC_Normal)
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_NormalVboId);
		}
		else if ((type == GLC_ExtendedGeomEngine::GLC_Texel) && (0 != m_TexelVboId))
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_TexelVboId);
		}
		else if ((type == GLC_ExtendedGeomEngine::GLC_Color) && (0 != m_ColorVboId))
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_ColorVboId);
		}

		else result= false;
	}
	else
	{
		// Unbind VBO
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}
	return result;

}
// Non Member methods
#define GLC_BINARY_CHUNK_ID 0xA704

// Non-member stream operator
QDataStream &operator<<(QDataStream &stream, const GLC_ExtendedGeomEngine &engine)
{
	quint32 chunckId= GLC_BINARY_CHUNK_ID;
	stream << chunckId;

	stream << engine.positionVector();
	stream << engine.normalVector();
	stream << engine.texelVector();
	stream << engine.colorVector();

	// List of lod serialisation
	const int lodCount= engine.m_EngineLodList.size();
	QList<GLC_EngineLod> lodsList;
	for (int i= 0; i < lodCount; ++i)
	{
		lodsList.append(*(engine.m_EngineLodList[i]));
	}
	stream << lodsList;

	return stream;
}
QDataStream &operator>>(QDataStream &stream, GLC_ExtendedGeomEngine &engine)
{
	quint32 chunckId;
	stream >> chunckId;
	Q_ASSERT(chunckId == GLC_BINARY_CHUNK_ID);

	engine.clear();

	GLfloatVector position, normal, texel, color;

	stream >> position;
	stream >> normal;
	stream >> texel;
	stream >> color;

	*(engine.positionVectorHandle())= position;
	*(engine.normalVectorHandle())= normal;
	*(engine.texelVectorHandle())= texel;
	*(engine.colorVectorHandle())= color;

	// List of lod serialisation
	QList<GLC_EngineLod> lodsList;
	stream >> lodsList;
	const int lodCount= lodsList.size();
	for (int i= 0; i < lodCount; ++i)
	{
		engine.m_EngineLodList.append(new GLC_EngineLod(lodsList.at(i)));
	}

	return stream;
}

<|endoftext|>


<commit_before>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnordmetric/metricdb/httpapi.h>
#include <fnordmetric/query/queryservice.h>
#include <fnordmetric/metricdb/metricrepository.h>
#include <fnordmetric/metricdb/metrictablerepository.h>
#include <fnordmetric/util/stringutil.h>

namespace fnordmetric {
namespace metricdb {

static const char kMetricsUrl[] = "/metrics";
static const char kMetricsUrlPrefix[] = "/metrics/";
static const char kQueryUrl[] = "/query";
static const char kLabelParamPrefix[] = "label[";

HTTPAPI::HTTPAPI(IMetricRepository* metric_repo) : metric_repo_(metric_repo) {}

bool HTTPAPI::handleHTTPRequest(
    http::HTTPRequest* request,
    http::HTTPResponse* response) {
  util::URI uri(request->getUrl());
  auto path = uri.path();
  fnord::util::StringUtil::stripTrailingSlashes(&path);

  response->addHeader("Access-Control-Allow-Origin", "*");

  // PATH: ^/metrics/?$
  if (path == kMetricsUrl) {
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
        renderMetricList(request, response, &uri);
        return true;
      case http::HTTPRequest::M_POST:
        insertSample(request, response, &uri);
        return true;
      default:
        return false;
    }
  }

  // PATH: ^/metrics/.*
  if (path.compare(0, sizeof(kMetricsUrlPrefix) - 1, kMetricsUrlPrefix) == 0) {
    // PATH: ^/metrics/(.*)$
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
        renderMetricSampleScan(request, response, &uri);
        return true;
      default:
        return false;
    }
  }

  // PATH: ^/query/?*
  if (path == kQueryUrl) {
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
      case http::HTTPRequest::M_POST:
        executeQuery(request, response, &uri);
        return true;
      default:
        return false;
    }
    return true;
  }

  return false;
}

void HTTPAPI::renderMetricList(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    util::URI* uri) {
  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");
  util::JSONOutputStream json(response->getBodyOutputStream());

  json.beginObject();
  json.addObjectEntry("metrics");
  json.beginArray();

  int i = 0;
  for (const auto& metric : metric_repo_->listMetrics()) {
    if (i++ > 0) { json.addComma(); }
    renderMetricJSON(metric, &json);
  }

  json.endArray();
  json.endObject();
}

void HTTPAPI::insertSample(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    util::URI* uri) {
  auto params = uri->queryParams();

  std::string metric_key;
  if (!util::URI::getParam(params, "metric", &metric_key)) {
    response->addBody("error: invalid metric key: " + metric_key);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  std::string value_str;
  if (!util::URI::getParam(params, "value", &value_str)) {
    response->addBody("error: missing ?value=... parameter");
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  std::vector<std::pair<std::string, std::string>> labels;
  for (const auto& param : params) {
    const auto& key = param.first;
    const auto& value = param.second;

    if (key.compare(0, sizeof(kLabelParamPrefix) - 1, kLabelParamPrefix) == 0 &&
        key.back() == ']') {
      auto label_key = key.substr(
          sizeof(kLabelParamPrefix) - 1,
          key.size() - sizeof(kLabelParamPrefix));

      labels.emplace_back(label_key, value);
    }
  }

  double sample_value;
  try {
    sample_value = std::stod(value_str);
  } catch (std::exception& e) {
    response->addBody("error: invalid value: " + value_str);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  auto metric = metric_repo_->findOrCreateMetric(metric_key);
  metric->insertSample(sample_value, labels);
  response->setStatus(http::kStatusCreated);
}

void HTTPAPI::renderMetricSampleScan(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    util::URI* uri) {
  auto metric_key = uri->path().substr(sizeof(kMetricsUrlPrefix) - 1);
  if (metric_key.size() < 3) {
    response->addBody("error: invalid metric key: " + metric_key);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  auto metric = metric_repo_->findMetric(metric_key);
  if (metric == nullptr) {
    response->addBody("metric not found: " + metric_key);
    response->setStatus(http::kStatusNotFound);
    return;
  }

  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");
  util::JSONOutputStream json(response->getBodyOutputStream());

  json.beginObject();

  json.addObjectEntry("metric");
  renderMetricJSON(metric, &json);
  json.addComma();

  json.addObjectEntry("samples");
  json.beginArray();

  int i = 0;
  metric->scanSamples(
      fnord::util::DateTime::epoch(),
      fnord::util::DateTime::now(),
      [&json, &i] (Sample* sample) -> bool {
        if (i++ > 0) { json.addComma(); }
        json.beginObject();

        json.addObjectEntry("time");
        json.addLiteral<uint64_t>(static_cast<uint64_t>(sample->time()));
        json.addComma();

        json.addObjectEntry("value");
        json.addLiteral<double>(sample->value());
        json.addComma();

        json.addObjectEntry("labels");
        json.beginObject();
        auto labels = sample->labels();
        for (int n = 0; n < labels.size(); n++) {
          if (n > 0) {
            json.addComma();
          }

          json.addObjectEntry(labels[n].first);
          json.addString(labels[n].second);
        }
        json.endObject();

        json.endObject();
        return true;
      });

  json.endArray();
  json.endObject();
}

void HTTPAPI::executeQuery(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    util::URI* uri) {
  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");

  std::shared_ptr<util::InputStream> input_stream =
      request->getBodyInputStream();

  std::shared_ptr<util::OutputStream> output_stream =
      response->getBodyOutputStream();

  query::QueryService query_service;
  std::unique_ptr<query::TableRepository> table_repo(
      new MetricTableRepository(metric_repo_));

  try {
    query_service.executeQuery(
        input_stream,
        query::QueryService::FORMAT_JSON,
        output_stream,
        std::move(table_repo));

  } catch (util::RuntimeException e) {
    response->clearBody();

    util::JSONOutputStream json(std::move(output_stream));
    json.beginObject();
    json.addObjectEntry("status");
    json.addString("error");
    json.addComma();
    json.addObjectEntry("error");
    json.addString(e.getMessage());
    json.endObject();
  }
}

void HTTPAPI::renderMetricJSON(
    IMetric* metric,
    util::JSONOutputStream* json) const {
  json->beginObject();

  json->addObjectEntry("key");
  json->addString(metric->key());
  json->addComma();

  json->addObjectEntry("total_bytes");
  json->addLiteral<size_t>(metric->totalBytes());
  json->addComma();

  json->addObjectEntry("last_insert");
  json->addLiteral<uint64_t>(static_cast<uint64_t>(metric->lastInsertTime()));
  json->addComma();

  json->addObjectEntry("labels");
  json->beginArray();
  auto labels = metric->labels();
  for (auto cur = labels.begin(); cur != labels.end(); ++cur) {
    if (cur != labels.begin()) {
      json->addComma();
    }
    json->addString(*cur);
  }
  json->endArray();

  json->endObject();
}

}
}
<commit_msg>implement final POST /metrics API<commit_after>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnordmetric/metricdb/httpapi.h>
#include <fnordmetric/query/queryservice.h>
#include <fnordmetric/metricdb/metricrepository.h>
#include <fnordmetric/metricdb/metrictablerepository.h>
#include <fnordmetric/util/stringutil.h>

namespace fnordmetric {
namespace metricdb {

static const char kMetricsUrl[] = "/metrics";
static const char kMetricsUrlPrefix[] = "/metrics/";
static const char kQueryUrl[] = "/query";
static const char kLabelParamPrefix[] = "label[";

HTTPAPI::HTTPAPI(IMetricRepository* metric_repo) : metric_repo_(metric_repo) {}

bool HTTPAPI::handleHTTPRequest(
    http::HTTPRequest* request,
    http::HTTPResponse* response) {
  util::URI uri(request->getUrl());
  auto path = uri.path();
  fnord::util::StringUtil::stripTrailingSlashes(&path);

  response->addHeader("Access-Control-Allow-Origin", "*");

  // PATH: ^/metrics/?$
  if (path == kMetricsUrl) {
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
        renderMetricList(request, response, &uri);
        return true;
      case http::HTTPRequest::M_POST:
        insertSample(request, response, &uri);
        return true;
      default:
        return false;
    }
  }

  // PATH: ^/metrics/.*
  if (path.compare(0, sizeof(kMetricsUrlPrefix) - 1, kMetricsUrlPrefix) == 0) {
    // PATH: ^/metrics/(.*)$
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
        renderMetricSampleScan(request, response, &uri);
        return true;
      default:
        return false;
    }
  }

  // PATH: ^/query/?*
  if (path == kQueryUrl) {
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
      case http::HTTPRequest::M_POST:
        executeQuery(request, response, &uri);
        return true;
      default:
        return false;
    }
    return true;
  }

  return false;
}

void HTTPAPI::renderMetricList(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    util::URI* uri) {
  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");
  util::JSONOutputStream json(response->getBodyOutputStream());

  json.beginObject();
  json.addObjectEntry("metrics");
  json.beginArray();

  int i = 0;
  for (const auto& metric : metric_repo_->listMetrics()) {
    if (i++ > 0) { json.addComma(); }
    renderMetricJSON(metric, &json);
  }

  json.endArray();
  json.endObject();
}

void HTTPAPI::insertSample(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    util::URI* uri) {
  const auto& postbody = request->getBody();
  util::URI::ParamList params;

  if (postbody.size() > 0) {
    util::URI::parseQueryString(postbody, &params);
  } else {
    params = uri->queryParams();
  }

  std::string metric_key;
  if (!util::URI::getParam(params, "metric", &metric_key)) {
    response->addBody("error: invalid metric key: " + metric_key);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  std::string value_str;
  if (!util::URI::getParam(params, "value", &value_str)) {
    response->addBody("error: missing ?value=... parameter");
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  std::vector<std::pair<std::string, std::string>> labels;
  for (const auto& param : params) {
    const auto& key = param.first;
    const auto& value = param.second;

    if (key.compare(0, sizeof(kLabelParamPrefix) - 1, kLabelParamPrefix) == 0 &&
        key.back() == ']') {
      auto label_key = key.substr(
          sizeof(kLabelParamPrefix) - 1,
          key.size() - sizeof(kLabelParamPrefix));

      labels.emplace_back(label_key, value);
    }
  }

  double sample_value;
  try {
    sample_value = std::stod(value_str);
  } catch (std::exception& e) {
    response->addBody("error: invalid value: " + value_str);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  auto metric = metric_repo_->findOrCreateMetric(metric_key);
  metric->insertSample(sample_value, labels);
  response->setStatus(http::kStatusCreated);
}

void HTTPAPI::renderMetricSampleScan(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    util::URI* uri) {
  auto metric_key = uri->path().substr(sizeof(kMetricsUrlPrefix) - 1);
  if (metric_key.size() < 3) {
    response->addBody("error: invalid metric key: " + metric_key);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  auto metric = metric_repo_->findMetric(metric_key);
  if (metric == nullptr) {
    response->addBody("metric not found: " + metric_key);
    response->setStatus(http::kStatusNotFound);
    return;
  }

  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");
  util::JSONOutputStream json(response->getBodyOutputStream());

  json.beginObject();

  json.addObjectEntry("metric");
  renderMetricJSON(metric, &json);
  json.addComma();

  json.addObjectEntry("samples");
  json.beginArray();

  int i = 0;
  metric->scanSamples(
      fnord::util::DateTime::epoch(),
      fnord::util::DateTime::now(),
      [&json, &i] (Sample* sample) -> bool {
        if (i++ > 0) { json.addComma(); }
        json.beginObject();

        json.addObjectEntry("time");
        json.addLiteral<uint64_t>(static_cast<uint64_t>(sample->time()));
        json.addComma();

        json.addObjectEntry("value");
        json.addLiteral<double>(sample->value());
        json.addComma();

        json.addObjectEntry("labels");
        json.beginObject();
        auto labels = sample->labels();
        for (int n = 0; n < labels.size(); n++) {
          if (n > 0) {
            json.addComma();
          }

          json.addObjectEntry(labels[n].first);
          json.addString(labels[n].second);
        }
        json.endObject();

        json.endObject();
        return true;
      });

  json.endArray();
  json.endObject();
}

void HTTPAPI::executeQuery(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    util::URI* uri) {
  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");

  std::shared_ptr<util::InputStream> input_stream =
      request->getBodyInputStream();

  std::shared_ptr<util::OutputStream> output_stream =
      response->getBodyOutputStream();

  query::QueryService query_service;
  std::unique_ptr<query::TableRepository> table_repo(
      new MetricTableRepository(metric_repo_));

  try {
    query_service.executeQuery(
        input_stream,
        query::QueryService::FORMAT_JSON,
        output_stream,
        std::move(table_repo));

  } catch (util::RuntimeException e) {
    response->clearBody();

    util::JSONOutputStream json(std::move(output_stream));
    json.beginObject();
    json.addObjectEntry("status");
    json.addString("error");
    json.addComma();
    json.addObjectEntry("error");
    json.addString(e.getMessage());
    json.endObject();
  }
}

void HTTPAPI::renderMetricJSON(
    IMetric* metric,
    util::JSONOutputStream* json) const {
  json->beginObject();

  json->addObjectEntry("key");
  json->addString(metric->key());
  json->addComma();

  json->addObjectEntry("total_bytes");
  json->addLiteral<size_t>(metric->totalBytes());
  json->addComma();

  json->addObjectEntry("last_insert");
  json->addLiteral<uint64_t>(static_cast<uint64_t>(metric->lastInsertTime()));
  json->addComma();

  json->addObjectEntry("labels");
  json->beginArray();
  auto labels = metric->labels();
  for (auto cur = labels.begin(); cur != labels.end(); ++cur) {
    if (cur != labels.begin()) {
      json->addComma();
    }
    json->addString(*cur);
  }
  json->endArray();

  json->endObject();
}

}
}
<|endoftext|>