<commit_before>#include "drivers/mpu6000.hpp"

#include "unit_config.hpp"

void MPU6000::init() {
  // Reset device.
  txbuf[0] = mpu6000::PWR_MGMT_1;
  txbuf[1] = 0x80;
  exchange(2);

  chThdSleepMilliseconds(100);   // TODO(yoos): Check whether or not datasheet specifies this holdoff.

  // Set sample rate to 1 kHz.
  txbuf[0] = mpu6000::SMPLRT_DIV;
  txbuf[1] = 0x00;
  exchange(2);

  // Set DLPF to 4 (20 Hz gyro bandwidth1 Hz accelerometer bandwidth)
  txbuf[0] = mpu6000::CONFIG;
  txbuf[1] = 0x04;
  exchange(2);

  // Wake up device and set clock source to Gyro Z.
  txbuf[0] = mpu6000::PWR_MGMT_1;
  txbuf[1] = 0x03;
  exchange(2);

  // Set gyro full range to 2000 dps. We first read in the current register
  // value so we can change what we need and leave everything else alone.
  // See DS p. 12.
  txbuf[0] = mpu6000::GYRO_CONFIG | (1<<7);
  exchange(2);
  chThdSleepMicroseconds(0);   // TODO(yoos): Without this, the GYRO_CONFIG register does not get set. This was not the case in the old C firmware. Why?
  txbuf[0] = mpu6000::GYRO_CONFIG;
  txbuf[1] = (rxbuf[1] & ~0x18) | 0x18;
  exchange(2);

  // Set accelerometer full range to 16 g. See DS p. 13.
  txbuf[0] = mpu6000::ACCEL_CONFIG | (1<<7);
  exchange(2);
  txbuf[0] = mpu6000::ACCEL_CONFIG;
  txbuf[1] = (rxbuf[1] & ~0x18) | 0x18;
  exchange(2);

  // Read once to clear out bad data?
  readGyro();
  readAccel();
}

gyroscope_reading_t MPU6000::readGyro() {
  gyroscope_reading_t reading;

  // Poll gyro
  txbuf[0] = mpu6000::GYRO_XOUT_H | (1<<7);
  exchange(7);

  // TODO(yoos): correct for thermal bias.
  reading.axes[0] = ((int16_t) ((rxbuf[1]<<8) | rxbuf[2])) / 16.384 * 3.1415926535 / 180.0 + unit_config::GYR_X_OFFSET;
  reading.axes[1] = ((int16_t) ((rxbuf[3]<<8) | rxbuf[4])) / 16.384 * 3.1415926535 / 180.0 + unit_config::GYR_Y_OFFSET;
  reading.axes[2] = ((int16_t) ((rxbuf[5]<<8) | rxbuf[6])) / 16.384 * 3.1415926535 / 180.0 + unit_config::GYR_Z_OFFSET;

  // Poll temp
  txbuf[0] = mpu6000::TEMP_OUT_H | (1<<7);
  exchange(3);

  float temp = ((int16_t) ((rxbuf[1]<<8) | rxbuf[2])) / 340 + 36.53;

  // DEBUG
  //char dbg_buf[16];
  //for (int i=0; i<16; i++) {
  //  dbg_buf[i] = 0;
  //}
  //chsnprintf(dbg_buf, 12, "%0.6f\r\n", reading.axes[2]);
  //chnWriteTimeout((BaseChannel*)&SD3, (uint8_t*)dbg_buf, 12, MS2ST(20));

  return reading;
}

accelerometer_reading_t MPU6000::readAccel() {
  accelerometer_reading_t reading;

  // Get data
  txbuf[0] = mpu6000::ACCEL_XOUT_H | (1<<7);
  exchange(7);
  reading.axes[0] = ((int16_t) ((rxbuf[1]<<8) | rxbuf[2])) / 16384.0 + unit_config::ACC_X_OFFSET;
  reading.axes[1] = ((int16_t) ((rxbuf[3]<<8) | rxbuf[4])) / 16384.0 + unit_config::ACC_Y_OFFSET;
  reading.axes[2] = ((int16_t) ((rxbuf[5]<<8) | rxbuf[6])) / 16384.0 + unit_config::ACC_Z_OFFSET;

  return reading;
}
<commit_msg>Change MPU6000 range to 4g.<commit_after>#include "drivers/mpu6000.hpp"

#include "unit_config.hpp"

void MPU6000::init() {
  // Reset device.
  txbuf[0] = mpu6000::PWR_MGMT_1;
  txbuf[1] = 0x80;
  exchange(2);

  chThdSleepMilliseconds(100);   // TODO(yoos): Check whether or not datasheet specifies this holdoff.

  // Set sample rate to 1 kHz.
  txbuf[0] = mpu6000::SMPLRT_DIV;
  txbuf[1] = 0x00;
  exchange(2);

  // Set DLPF to 4 (20 Hz gyro bandwidth1 Hz accelerometer bandwidth)
  txbuf[0] = mpu6000::CONFIG;
  txbuf[1] = 0x04;
  exchange(2);

  // Wake up device and set clock source to Gyro Z.
  txbuf[0] = mpu6000::PWR_MGMT_1;
  txbuf[1] = 0x03;
  exchange(2);

  // Set gyro full range to 2000 dps. We first read in the current register
  // value so we can change what we need and leave everything else alone.
  // See DS p. 12.
  txbuf[0] = mpu6000::GYRO_CONFIG | (1<<7);
  exchange(2);
  chThdSleepMicroseconds(0);   // TODO(yoos): Without this, the GYRO_CONFIG register does not get set. This was not the case in the old C firmware. Why?
  txbuf[0] = mpu6000::GYRO_CONFIG;
  txbuf[1] = (rxbuf[1] & ~0x18) | 0x18;
  exchange(2);

  // Set accelerometer full range to 4 g. See DS p. 13.
  txbuf[0] = mpu6000::ACCEL_CONFIG | (1<<7);
  exchange(2);
  txbuf[0] = mpu6000::ACCEL_CONFIG;
  txbuf[1] = (rxbuf[1] & ~0x18) | 0x08;
  exchange(2);

  // Read once to clear out bad data?
  readGyro();
  readAccel();
}

gyroscope_reading_t MPU6000::readGyro() {
  gyroscope_reading_t reading;

  // Poll gyro
  txbuf[0] = mpu6000::GYRO_XOUT_H | (1<<7);
  exchange(7);

  // TODO(yoos): correct for thermal bias.
  reading.axes[0] = ((int16_t) ((rxbuf[1]<<8) | rxbuf[2])) / 16.384 * 3.1415926535 / 180.0 + unit_config::GYR_X_OFFSET;
  reading.axes[1] = ((int16_t) ((rxbuf[3]<<8) | rxbuf[4])) / 16.384 * 3.1415926535 / 180.0 + unit_config::GYR_Y_OFFSET;
  reading.axes[2] = ((int16_t) ((rxbuf[5]<<8) | rxbuf[6])) / 16.384 * 3.1415926535 / 180.0 + unit_config::GYR_Z_OFFSET;

  // Poll temp
  txbuf[0] = mpu6000::TEMP_OUT_H | (1<<7);
  exchange(3);

  float temp = ((int16_t) ((rxbuf[1]<<8) | rxbuf[2])) / 340 + 36.53;

  // DEBUG
  //char dbg_buf[16];
  //for (int i=0; i<16; i++) {
  //  dbg_buf[i] = 0;
  //}
  //chsnprintf(dbg_buf, 12, "%0.6f\r\n", reading.axes[2]);
  //chnWriteTimeout((BaseChannel*)&SD3, (uint8_t*)dbg_buf, 12, MS2ST(20));

  return reading;
}

accelerometer_reading_t MPU6000::readAccel() {
  accelerometer_reading_t reading;

  // Get data
  txbuf[0] = mpu6000::ACCEL_XOUT_H | (1<<7);
  exchange(7);
  reading.axes[0] = ((int16_t) ((rxbuf[1]<<8) | rxbuf[2])) / 8192.0 + unit_config::ACC_X_OFFSET;
  reading.axes[1] = ((int16_t) ((rxbuf[3]<<8) | rxbuf[4])) / 8192.0 + unit_config::ACC_Y_OFFSET;
  reading.axes[2] = ((int16_t) ((rxbuf[5]<<8) | rxbuf[6])) / 8192.0 + unit_config::ACC_Z_OFFSET;

  return reading;
}
<|endoftext|>


<commit_before>//// License: Apache 2.0. See LICENSE file in root directory.
//// Copyright(c) 2015 Intel Corporation. All Rights Reserved.

#include "ds5-private.h"

using namespace std;

#define intrinsics_string(res) #res << "\t" << array2str((float_4&)table->rect_params[res]) << endl

namespace librealsense
{
    namespace ds
    {
        ds5_rect_resolutions width_height_to_ds5_rect_resolutions(uint32_t width, uint32_t height)
        {
            for (auto& elem : resolutions_list)
            {
                if (elem.second.x == width && elem.second.y == height)
                    return elem.first;
            }
            throw invalid_value_exception("resolution not found.");
        }

        rs2_intrinsics get_intrinsic_by_resolution_coefficients_table(const std::vector<uint8_t> & raw_data, uint32_t width, uint32_t height)
        {
            auto table = check_calib<ds::coefficients_table>(raw_data);

            LOG_DEBUG(endl
                << "baseline = " << table->baseline << " mm" << endl
                << "Rect params:  \t fX\t\t fY\t\t ppX\t\t ppY \n"
                << intrinsics_string(res_1920_1080)
                << intrinsics_string(res_1280_720)
                << intrinsics_string(res_640_480)
                << intrinsics_string(res_848_480)
                << intrinsics_string(res_424_240)
                << intrinsics_string(res_640_360)
                << intrinsics_string(res_320_240)
                << intrinsics_string(res_480_270)
                << intrinsics_string(res_1280_800)
                << intrinsics_string(res_960_540));

            auto resolution = width_height_to_ds5_rect_resolutions(width ,height);
            rs2_intrinsics intrinsics;
            intrinsics.width = resolutions_list[resolution].x;
            intrinsics.height = resolutions_list[resolution].y;

            auto rect_params = static_cast<const float4>(table->rect_params[resolution]);
            // DS5U - assume ideal intrinsic params
            if ((rect_params.x == rect_params.y) && (rect_params.z == rect_params.w))
            {
                rect_params.x = rect_params.y = intrinsics.width * 1.5f;
                rect_params.z = intrinsics.width * 0.5f;
                rect_params.w = intrinsics.height * 0.5f;
            }
            intrinsics.fx = rect_params[0];
            intrinsics.fy = rect_params[1];
            intrinsics.ppx = rect_params[2];
            intrinsics.ppy = rect_params[3];
            intrinsics.model = RS2_DISTORTION_BROWN_CONRADY;
            memset(intrinsics.coeffs, 0, sizeof(intrinsics.coeffs));  // All coefficients are zeroed since rectified depth is defined as CS origin
            return intrinsics;
        }

        rs2_intrinsics get_intrinsic_fisheye_table(const std::vector<uint8_t>& raw_data, uint32_t width, uint32_t height)
        {
             auto table = check_calib<ds::fisheye_intrinsics_table>(raw_data);

             rs2_intrinsics intrinsics;
             auto intrin = table->intrinsic;
             intrinsics.fx = intrin(0,0);
             intrinsics.fy = intrin(1,1);
             intrinsics.ppx = intrin(2,0);
             intrinsics.ppy = intrin(2,1);
             intrinsics.model = RS2_DISTORTION_FTHETA;

             intrinsics.height = height;
             intrinsics.width = width;

             librealsense::copy(intrinsics.coeffs, table->distortion, sizeof(table->distortion));

             LOG_DEBUG(endl<< array2str((float_4&)(intrinsics.fx, intrinsics.fy, intrinsics.ppx, intrinsics.ppy)) << endl);

             return intrinsics;
        }

        rs2_intrinsics get_color_stream_intrinsic(const std::vector<uint8_t>& raw_data, uint32_t width, uint32_t height)
        {
             auto table = check_calib<ds::rgb_calibration_table>(raw_data);

             // Compensate for aspect ratio as the normalized intrinsic is calculated with 16/9 factor
             float3x3 intrin = table->intrinsic;
             static const float base_aspect_ratio_factor = 16.f / 9.f;

             // Use intrinsic from RGB->Depth projection table
            for (int i = 0; i < 3; i++)
                for (int j = 0; j < 3; j++)
                    intrin(j, i) = table->projection[i * 4 + j];

             // Compensate for aspect ratio
             intrin(0, 0) *= base_aspect_ratio_factor * (height / (float)width);
             intrin(2, 0) *= base_aspect_ratio_factor * (height / (float)width);

            // Calculate specific intrinsic parameters based on the normalized intrinsic and the sensor's resolution
            rs2_intrinsics calc_intrinsic{
                static_cast<int>(width),
                static_cast<int>(height),
                ((1 + intrin(2, 0))*width) / 2.f,
                ((1 + intrin(2, 1))*height) / 2.f,
                intrin(0, 0) * width / 2.f,
                intrin(1, 1) * height / 2.f,
                RS2_DISTORTION_BROWN_CONRADY
            };
            librealsense::copy(calc_intrinsic.coeffs, table->distortion, sizeof(table->distortion));
            LOG_DEBUG(endl << array2str((float_4&)(calc_intrinsic.fx, calc_intrinsic.fy, calc_intrinsic.ppx, calc_intrinsic.ppy)) << endl);

            return calc_intrinsic;
        }

        rs2_intrinsics get_intrinsic_by_resolution(const vector<uint8_t> & raw_data, calibration_table_id table_id, uint32_t width, uint32_t height)
        {
            switch (table_id)
            {
            case coefficients_table_id:
            {
                return get_intrinsic_by_resolution_coefficients_table(raw_data, width, height);
            }
            case fisheye_calibration_id:
            {
                return get_intrinsic_fisheye_table(raw_data, width, height);
            }
            case rgb_calibration_id:
            {
                return get_color_stream_intrinsic(raw_data, width, height);
            }
            default:
                throw invalid_value_exception(to_string() << "Parsing Calibration table type " << table_id << " is not supported");
            }
        }

        pose get_fisheye_extrinsics_data(const vector<uint8_t> & raw_data)
        {
            auto table = check_calib<fisheye_extrinsics_table>(raw_data);

            auto rot = table->rotation;
            auto trans = table->translation;

            pose ex = {{rot(0,0), rot(1,0),rot(2,0),rot(1,0), rot(1,1),rot(2,1),rot(0,2), rot(1,2),rot(2,2)},
                       {trans[0], trans[1], trans[2]}};
            return ex;
        }

        pose get_color_stream_extrinsic(const std::vector<uint8_t>& raw_data)
        {
            auto table = check_calib<rgb_calibration_table>(raw_data);
            float3 trans_vector = table->translation_rect;
            float3x3 rect_rot_mat = table->rotation_matrix_rect;
            float trans_scale = 0.001f; // Convert units from mm to meter
            if (table->translation.x > 0.f) // Extrinsic of color is referenced to the Depth Sensor CS
            {
                trans_scale *= -1;
            }
            trans_vector.x *= trans_scale;
            trans_vector.y *= trans_scale;
            trans_vector.z *= trans_scale;

            return{ rect_rot_mat,trans_vector };
        }

        bool try_fetch_usb_device(std::vector<platform::usb_device_info>& devices,
                                         const platform::uvc_device_info& info, platform::usb_device_info& result)
        {
            for (auto it = devices.begin(); it != devices.end(); ++it)
            {
                if (it->unique_id == info.unique_id)
                {
                    bool found = false;
                    result = *it;
                    switch (info.pid)
                    {
                    case RS_USB2_PID:
                    case RS400_PID:
                    case RS405_PID:
                    case RS410_PID:
                    case RS460_PID:
                    case RS430_PID:
                    case RS420_PID:
                        found = (result.mi == 3);
                        break;
                    case RS430_MM_PID:
                    case RS420_MM_PID:
                        found = (result.mi == 6);
                        break;
                    case RS415_PID:
                    case RS435_RGB_PID:
                        found = (result.mi == 5);
                        break;
                    default:
                        throw not_implemented_exception(to_string() << "USB device " << info.pid << ":" << info.vid << " is not supported.");
                        break;
                    }

                    if (found)
                    {
                        devices.erase(it);
                        return true;
                    }
                }
            }
            return false;
        }
    } // librealsense::ds
} // namespace librealsense
<commit_msg>Fixing obsolete code from commit 34d6edf6627a223dbe7f23cdc1ada8a47356adb9 Tracked On: DSO-8308<commit_after>//// License: Apache 2.0. See LICENSE file in root directory.
//// Copyright(c) 2015 Intel Corporation. All Rights Reserved.

#include "ds5-private.h"

using namespace std;

#define intrinsics_string(res) #res << "\t" << array2str((float_4&)table->rect_params[res]) << endl

namespace librealsense
{
    namespace ds
    {
        ds5_rect_resolutions width_height_to_ds5_rect_resolutions(uint32_t width, uint32_t height)
        {
            for (auto& elem : resolutions_list)
            {
                if (elem.second.x == width && elem.second.y == height)
                    return elem.first;
            }
            throw invalid_value_exception("resolution not found.");
        }

        rs2_intrinsics get_intrinsic_by_resolution_coefficients_table(const std::vector<uint8_t> & raw_data, uint32_t width, uint32_t height)
        {
            auto table = check_calib<ds::coefficients_table>(raw_data);

            LOG_DEBUG(endl
                << "baseline = " << table->baseline << " mm" << endl
                << "Rect params:  \t fX\t\t fY\t\t ppX\t\t ppY \n"
                << intrinsics_string(res_1920_1080)
                << intrinsics_string(res_1280_720)
                << intrinsics_string(res_640_480)
                << intrinsics_string(res_848_480)
                << intrinsics_string(res_424_240)
                << intrinsics_string(res_640_360)
                << intrinsics_string(res_320_240)
                << intrinsics_string(res_480_270)
                << intrinsics_string(res_1280_800)
                << intrinsics_string(res_960_540));

            auto resolution = width_height_to_ds5_rect_resolutions(width ,height);
            rs2_intrinsics intrinsics;
            intrinsics.width = resolutions_list[resolution].x;
            intrinsics.height = resolutions_list[resolution].y;

            auto rect_params = static_cast<const float4>(table->rect_params[resolution]);
            // DS5U - assume ideal intrinsic params
            if ((rect_params.x == rect_params.y) && (rect_params.z == rect_params.w))
            {
                rect_params.x = rect_params.y = intrinsics.width * 1.5f;
                rect_params.z = intrinsics.width * 0.5f;
                rect_params.w = intrinsics.height * 0.5f;
            }
            intrinsics.fx = rect_params[0];
            intrinsics.fy = rect_params[1];
            intrinsics.ppx = rect_params[2];
            intrinsics.ppy = rect_params[3];
            intrinsics.model = RS2_DISTORTION_BROWN_CONRADY;
            memset(intrinsics.coeffs, 0, sizeof(intrinsics.coeffs));  // All coefficients are zeroed since rectified depth is defined as CS origin
            return intrinsics;
        }

        rs2_intrinsics get_intrinsic_fisheye_table(const std::vector<uint8_t>& raw_data, uint32_t width, uint32_t height)
        {
             auto table = check_calib<ds::fisheye_intrinsics_table>(raw_data);

             rs2_intrinsics intrinsics;
             auto intrin = table->intrinsic;
             intrinsics.fx = intrin(0,0);
             intrinsics.fy = intrin(1,1);
             intrinsics.ppx = intrin(2,0);
             intrinsics.ppy = intrin(2,1);
             intrinsics.model = RS2_DISTORTION_FTHETA;

             intrinsics.height = height;
             intrinsics.width = width;

             librealsense::copy(intrinsics.coeffs, table->distortion, sizeof(table->distortion));

             LOG_DEBUG(endl<< array2str((float_4&)(intrinsics.fx, intrinsics.fy, intrinsics.ppx, intrinsics.ppy)) << endl);

             return intrinsics;
        }

        rs2_intrinsics get_color_stream_intrinsic(const std::vector<uint8_t>& raw_data, uint32_t width, uint32_t height)
        {
             auto table = check_calib<ds::rgb_calibration_table>(raw_data);

             // Compensate for aspect ratio as the normalized intrinsic is calculated with 16/9 factor
             float3x3 intrin = table->intrinsic;
             static const float base_aspect_ratio_factor = 16.f / 9.f;

             // Compensate for aspect ratio
             intrin(0, 0) *= base_aspect_ratio_factor * (height / (float)width);
             intrin(2, 0) *= base_aspect_ratio_factor * (height / (float)width);

            // Calculate specific intrinsic parameters based on the normalized intrinsic and the sensor's resolution
            rs2_intrinsics calc_intrinsic{
                static_cast<int>(width),
                static_cast<int>(height),
                ((1 + intrin(2, 0))*width) / 2.f,
                ((1 + intrin(2, 1))*height) / 2.f,
                intrin(0, 0) * width / 2.f,
                intrin(1, 1) * height / 2.f,
                RS2_DISTORTION_BROWN_CONRADY
            };
            librealsense::copy(calc_intrinsic.coeffs, table->distortion, sizeof(table->distortion));
            LOG_DEBUG(endl << array2str((float_4&)(calc_intrinsic.fx, calc_intrinsic.fy, calc_intrinsic.ppx, calc_intrinsic.ppy)) << endl);

            return calc_intrinsic;
        }

        rs2_intrinsics get_intrinsic_by_resolution(const vector<uint8_t> & raw_data, calibration_table_id table_id, uint32_t width, uint32_t height)
        {
            switch (table_id)
            {
            case coefficients_table_id:
            {
                return get_intrinsic_by_resolution_coefficients_table(raw_data, width, height);
            }
            case fisheye_calibration_id:
            {
                return get_intrinsic_fisheye_table(raw_data, width, height);
            }
            case rgb_calibration_id:
            {
                return get_color_stream_intrinsic(raw_data, width, height);
            }
            default:
                throw invalid_value_exception(to_string() << "Parsing Calibration table type " << table_id << " is not supported");
            }
        }

        pose get_fisheye_extrinsics_data(const vector<uint8_t> & raw_data)
        {
            auto table = check_calib<fisheye_extrinsics_table>(raw_data);

            auto rot = table->rotation;
            auto trans = table->translation;

            pose ex = {{rot(0,0), rot(1,0),rot(2,0),rot(1,0), rot(1,1),rot(2,1),rot(0,2), rot(1,2),rot(2,2)},
                       {trans[0], trans[1], trans[2]}};
            return ex;
        }

        pose get_color_stream_extrinsic(const std::vector<uint8_t>& raw_data)
        {
            auto table = check_calib<rgb_calibration_table>(raw_data);
            float3 trans_vector = table->translation_rect;
            float3x3 rect_rot_mat = table->rotation_matrix_rect;
            float trans_scale = 0.001f; // Convert units from mm to meter
            if (table->translation.x > 0.f) // Extrinsic of color is referenced to the Depth Sensor CS
            {
                trans_scale *= -1;
            }
            trans_vector.x *= trans_scale;
            trans_vector.y *= trans_scale;
            trans_vector.z *= trans_scale;

            return{ rect_rot_mat,trans_vector };
        }

        bool try_fetch_usb_device(std::vector<platform::usb_device_info>& devices,
                                         const platform::uvc_device_info& info, platform::usb_device_info& result)
        {
            for (auto it = devices.begin(); it != devices.end(); ++it)
            {
                if (it->unique_id == info.unique_id)
                {
                    bool found = false;
                    result = *it;
                    switch (info.pid)
                    {
                    case RS_USB2_PID:
                    case RS400_PID:
                    case RS405_PID:
                    case RS410_PID:
                    case RS460_PID:
                    case RS430_PID:
                    case RS420_PID:
                        found = (result.mi == 3);
                        break;
                    case RS430_MM_PID:
                    case RS420_MM_PID:
                        found = (result.mi == 6);
                        break;
                    case RS415_PID:
                    case RS435_RGB_PID:
                        found = (result.mi == 5);
                        break;
                    default:
                        throw not_implemented_exception(to_string() << "USB device " << info.pid << ":" << info.vid << " is not supported.");
                        break;
                    }

                    if (found)
                    {
                        devices.erase(it);
                        return true;
                    }
                }
            }
            return false;
        }
    } // librealsense::ds
} // namespace librealsense
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993-2016 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "ServerList.h"

/* system headers */
#include <iostream>
#include <vector>
#include <string>
#include <string.h>
#if !defined(_WIN32)
#include <errno.h>
#endif
#include <ctype.h>

/* common implementation headers */
#include "version.h"
#include "bzsignal.h"
#include "Ping.h"
#include "Protocol.h"
#include "TimeKeeper.h"
#include "TextUtils.h"
#include "ErrorHandler.h"

/* local implementation headers */
#include "ServerListCache.h"
#include "StartupInfo.h"

ServerList::ServerList() :
	phase(-1),
	serverCache(ServerListCache::get()),
	pingBcastSocket(-1)
{
}

ServerList::~ServerList() {
  _shutDown();
}

void ServerList::startServerPings(StartupInfo *info) {

  // schedule lookup of server list url.  dereference URL chain every
  // time instead of only first time just in case one of the pointers
  // has changed.
  if (phase > -1 && phase < 4)
    return;
  if (info->listServerURL.size() == 0)
    phase = -1;
  else
    phase = 0;

  // also try broadcast
  pingBcastSocket = openBroadcast(BroadcastPort, NULL, &pingBcastAddr);
  if (pingBcastSocket != -1)
    PingPacket::sendRequest(pingBcastSocket, &pingBcastAddr);
}

void ServerList::readServerList()
{
  char *base = (char *)theData;
  char *endS = base + theLen;
  const char tokenIdentifier[]   = "TOKEN: ";
  const char noTokenIdentifier[] = "NOTOK: ";
  const char errorIdentifier[]   = "ERROR: ";
  const char noticeIdentifier[]  = "NOTICE: ";
  // walks entire reply including HTTP headers
  while (base < endS) {
    // find next newline
    char* scan = base;
    while (scan < endS && *scan != '\n')
      scan++;

    // if no newline then no more complete replies
    if (scan >= endS)
      break;
    *scan++ = '\0';

    // look for TOKEN: and save token if found also look for NOTOK:
    // and record "badtoken" into the token string and print an
    // error
    if (strncmp(base, tokenIdentifier, strlen(tokenIdentifier)) == 0) {
      strncpy(startupInfo->token, (char *)(base + strlen(tokenIdentifier)),
	      TokenLen - 1);
      startupInfo->token[TokenLen - 1] = '\0';
#ifdef DEBUG
      printError("got token:");
      printError(startupInfo->token);
#endif
      base = scan;
      continue;
    } else if (!strncmp(base, noTokenIdentifier,
			strlen(noTokenIdentifier))) {
      printError("ERROR: did not get token:");
      printError(base);
      strcpy(startupInfo->token, "badtoken\0");
      base = scan;
      continue;
    } else if (!strncmp(base, errorIdentifier, strlen(errorIdentifier))) {
      printError(base);
      strcpy(startupInfo->token, "badtoken\0");
      base = scan;
      continue;
    } else if (!strncmp(base, noticeIdentifier, strlen(noticeIdentifier))) {
      printError(base);
      base = scan;
      continue;
    }
    // parse server info
    char *scan2, *name, *version, *infoServer, *address, *title;
    name = base;
    version = name;
    while (*version && !isspace(*version))  version++;
    while (*version &&  isspace(*version)) *version++ = 0;
    infoServer = version;
    while (*infoServer && !isspace(*infoServer))  infoServer++;
    while (*infoServer &&  isspace(*infoServer)) *infoServer++ = 0;
    address = infoServer;
    while (*address && !isspace(*address))  address++;
    while (*address &&  isspace(*address)) *address++ = 0;
    title = address;
    while (*title && !isspace(*title))  title++;
    while (*title &&  isspace(*title)) *title++ = 0;

    // extract port number from address
    int port = ServerPort;
    scan2 = strchr(name, ':');
    if (scan2) {
      port = atoi(scan2 + 1);
      *scan2 = 0;
    }

    // check info
    if (strcmp(version, getServerVersion()) == 0 &&
	(int)strlen(infoServer) == PingPacketHexPackedSize &&
	port >= 1 && port <= 65535) {
      // store info
      ServerItem serverInfo;
      serverInfo.ping.unpackHex(infoServer);
      int dot[4] = {127,0,0,1};
      if (sscanf(address, "%d.%d.%d.%d", dot+0, dot+1, dot+2, dot+3) == 4) {
	if (dot[0] >= 0 && dot[0] <= 255 &&
	    dot[1] >= 0 && dot[1] <= 255 &&
	    dot[2] >= 0 && dot[2] <= 255 &&
	    dot[3] >= 0 && dot[3] <= 255) {
	  InAddr addr;
	  unsigned char* paddr = (unsigned char*)&addr.s_addr;
	  paddr[0] = (unsigned char)dot[0];
	  paddr[1] = (unsigned char)dot[1];
	  paddr[2] = (unsigned char)dot[2];
	  paddr[3] = (unsigned char)dot[3];
	  serverInfo.ping.serverId.serverHost = addr;
	}
      }
      serverInfo.ping.serverId.port = htons((int16_t)port);
      serverInfo.name = name;

      // construct description
      serverInfo.description = serverInfo.name;
      if (port != ServerPort) {
	char portBuf[20];
	sprintf(portBuf, "%d", port);
	serverInfo.description += ":";
	serverInfo.description += portBuf;
      }
      if (strlen(title) > 0) {
	serverInfo.description += "; ";
	serverInfo.description += title;
      }

      serverInfo.cached = false;
      // add to list & add it to the server cache
      addToList(serverInfo, true);
    }

    // next reply
    base = scan;
  }

  // remove parsed replies
  theLen -= int(base - (char *)theData);
  memmove(theData, base, theLen);
}

void ServerList::addToList(ServerItem info, bool doCache)
{
  // update if we already have it
  int i;

  // search and delete entry for this item if it exists
  // (updating info in place may "unsort" the list)
  for (i = 0; i < (int)servers.size(); i++) {
    ServerItem& server = servers[i];
    if (server.ping.serverId.serverHost.s_addr
	== info.ping.serverId.serverHost.s_addr
	&& server.ping.serverId.port == info.ping.serverId.port) {
      servers.erase(servers.begin() + i); // erase this item
      break;
    }
  }

  // find point to insert new player at
  int insertPoint = -1; // point to insert server into

  // insert new item before the first serveritem with is deemed to be less
  // in value than the item to be inserted -- cached items are less than
  // non-cached, items that have more players are more, etc..
  for (i = 0; i < (int)servers.size(); i++) {
    ServerItem& server = servers[i];
    if (server < info){
      insertPoint = i;
      break;
    }
  }

  // mark server in current list if it is a favorite server
  std::string serverAddress = info.getAddrName();
  if (serverCache->isFavorite(serverAddress))
    info.favorite = true;

  if (insertPoint == -1){ // no spot to insert it into -- goes on back
    servers.push_back(info);
  } else {  // found a spot to insert it into
    servers.insert(servers.begin() + insertPoint, info);
  }

  // update display
  /*
  char buffer [80];
  std::vector<std::string> args;
  sprintf(buffer, "%d", (int)servers.size());
  args.push_back(buffer);
  setStatus("Servers found: {1}", &args);
  */

  // force update
  /*
  const int oldSelectedIndex = selectedIndex;
  selectedIndex = -1;
  setSelected(oldSelectedIndex);
  */

  if (doCache) {
    info.cached = true; // values in cache are "cached"
    // update the last updated time to now
    info.setUpdateTime();

    ServerListCache::SRV_STR_MAP::iterator iter;
    iter = serverCache->find(serverAddress);  // find entry to allow update
    if (iter != serverCache->end()){ // if we find it, update it
      iter->second = info;
    } else {
      // insert into cache -- wasn't found
      serverCache->insert(serverAddress, info);
    }
  }
}

// mark server identified by host:port string as favorite
void		    ServerList::markFav(const std::string &serverAddress, bool fav)
{
  for (int i = 0; i < (int)servers.size(); i++) {
    if (serverAddress == servers[i].getAddrName()) {
      servers[i].favorite = fav;
      break;
    }
  }
}

void			ServerList::checkEchos(StartupInfo *info)
{
  startupInfo = info;

  // *** NOTE *** searching spinner update was here

  // lookup server list in phase 0
  if (phase == 0) {

    std::string url = info->listServerURL;

    std::string msg = "action=LIST&version=";
    msg	    += getServerVersion();
    msg	    += "&callsign=";
    msg	    += TextUtils::url_encode(info->callsign);
    msg	    += "&password=";
    msg	    += TextUtils::url_encode(info->password);
    setPostMode(msg);
    setURLwithNonce(url);
    addHandle();

    // do phase 1 only if we found a valid list server url
    phase = 1;
  }

  // get echo messages
  while (true) {
    // *** NOTE *** searching spinner update was here

    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 250;

    fd_set read_set, write_set;
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
    if (pingBcastSocket != -1) {
      FD_SET((unsigned int)pingBcastSocket, &read_set);
    }
    int fdMax = pingBcastSocket;

    const int nfound = select(fdMax+1, (fd_set*)&read_set,
					(fd_set*)&write_set, 0, &timeout);
    if (nfound <= 0)
      break;

    // check broadcast sockets
    ServerItem serverInfo;
    sockaddr_in addr;

    if (pingBcastSocket != -1 && FD_ISSET(pingBcastSocket, &read_set)) {
      if (serverInfo.ping.read(pingBcastSocket, &addr)) {
	serverInfo.ping.serverId.serverHost = addr.sin_addr;
	serverInfo.cached = false;
	addToListWithLookup(serverInfo);
      }
    }
  } // end loop waiting for input/output on any list server
}

void			ServerList::addToListWithLookup(ServerItem& info)
{
  info.name = Address::getHostByAddress(info.ping.serverId.serverHost);

  // tack on port number to description if not default
  info.description = info.name;
  const int port = (int)ntohs((unsigned short)info.ping.serverId.port);
  if (port != ServerPort) {
    char portBuf[20];
    sprintf(portBuf, "%d", port);
    info.description += ":";
    info.description += portBuf;
  }

  addToList(info); // do not cache network lan - etc. servers
}

// add the entire cache to the server list
void			ServerList::addCacheToList()
{
  if (addedCacheToList)
    return;
  addedCacheToList = true;
  for (ServerListCache::SRV_STR_MAP::iterator iter = serverCache->begin();
       iter != serverCache->end(); ++iter) {
    addToList(iter->second);
  }
}

void ServerList::collectData(char *ptr, int len)
{
  phase = 2;

  cURLManager::collectData(ptr, len);

  readServerList();
}

void ServerList::finalization(char *, unsigned int, bool good)
{
  if (!good) {
    printError("Can't talk with list server");
    addCacheToList();
    phase = -1;
  } else {
    phase = 4;
  }
}

const std::vector<ServerItem>& ServerList::getServers() {
  return servers;
}

std::vector<ServerItem>::size_type ServerList::size() {
  return servers.size();
}

void ServerList::clear() {
  servers.clear();
}

int ServerList::updateFromCache() {
  // clear server list
  clear();

  int numItemsAdded = 0;

  for (ServerListCache::SRV_STR_MAP::const_iterator iter = serverCache->begin();
       iter != serverCache->end(); ++iter) {
    // if maxCacheAge is 0 we add nothing
    // if the item is young enough we add it
    if (serverCache->getMaxCacheAge() != 0
	&& iter->second.getAgeMinutes() < serverCache->getMaxCacheAge()) {
      addToList(iter->second);
      numItemsAdded ++;
    }
  }

  return numItemsAdded;
}

bool ServerList::searchActive() const {
  return (phase < 4) ? true : false;
}

bool ServerList::serverFound() const {
  return (phase >= 2) ? true : false;
}

void ServerList::_shutDown() {
  // close broadcast socket
  closeBroadcast(pingBcastSocket);
  pingBcastSocket = -1;
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Fixed an uninitialized member variable.<commit_after>/* bzflag
 * Copyright (c) 1993-2016 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "ServerList.h"

/* system headers */
#include <iostream>
#include <vector>
#include <string>
#include <string.h>
#if !defined(_WIN32)
#include <errno.h>
#endif
#include <ctype.h>

/* common implementation headers */
#include "version.h"
#include "bzsignal.h"
#include "Ping.h"
#include "Protocol.h"
#include "TimeKeeper.h"
#include "TextUtils.h"
#include "ErrorHandler.h"

/* local implementation headers */
#include "ServerListCache.h"
#include "StartupInfo.h"

ServerList::ServerList() :
	addedCacheToList(false),
	phase(-1),
	serverCache(ServerListCache::get()),
	pingBcastSocket(-1)
{
}

ServerList::~ServerList() {
  _shutDown();
}

void ServerList::startServerPings(StartupInfo *info) {

  // schedule lookup of server list url.  dereference URL chain every
  // time instead of only first time just in case one of the pointers
  // has changed.
  if (phase > -1 && phase < 4)
    return;
  if (info->listServerURL.size() == 0)
    phase = -1;
  else
    phase = 0;

  // also try broadcast
  pingBcastSocket = openBroadcast(BroadcastPort, NULL, &pingBcastAddr);
  if (pingBcastSocket != -1)
    PingPacket::sendRequest(pingBcastSocket, &pingBcastAddr);
}

void ServerList::readServerList()
{
  char *base = (char *)theData;
  char *endS = base + theLen;
  const char tokenIdentifier[]   = "TOKEN: ";
  const char noTokenIdentifier[] = "NOTOK: ";
  const char errorIdentifier[]   = "ERROR: ";
  const char noticeIdentifier[]  = "NOTICE: ";
  // walks entire reply including HTTP headers
  while (base < endS) {
    // find next newline
    char* scan = base;
    while (scan < endS && *scan != '\n')
      scan++;

    // if no newline then no more complete replies
    if (scan >= endS)
      break;
    *scan++ = '\0';

    // look for TOKEN: and save token if found also look for NOTOK:
    // and record "badtoken" into the token string and print an
    // error
    if (strncmp(base, tokenIdentifier, strlen(tokenIdentifier)) == 0) {
      strncpy(startupInfo->token, (char *)(base + strlen(tokenIdentifier)),
	      TokenLen - 1);
      startupInfo->token[TokenLen - 1] = '\0';
#ifdef DEBUG
      printError("got token:");
      printError(startupInfo->token);
#endif
      base = scan;
      continue;
    } else if (!strncmp(base, noTokenIdentifier,
			strlen(noTokenIdentifier))) {
      printError("ERROR: did not get token:");
      printError(base);
      strcpy(startupInfo->token, "badtoken\0");
      base = scan;
      continue;
    } else if (!strncmp(base, errorIdentifier, strlen(errorIdentifier))) {
      printError(base);
      strcpy(startupInfo->token, "badtoken\0");
      base = scan;
      continue;
    } else if (!strncmp(base, noticeIdentifier, strlen(noticeIdentifier))) {
      printError(base);
      base = scan;
      continue;
    }
    // parse server info
    char *scan2, *name, *version, *infoServer, *address, *title;
    name = base;
    version = name;
    while (*version && !isspace(*version))  version++;
    while (*version &&  isspace(*version)) *version++ = 0;
    infoServer = version;
    while (*infoServer && !isspace(*infoServer))  infoServer++;
    while (*infoServer &&  isspace(*infoServer)) *infoServer++ = 0;
    address = infoServer;
    while (*address && !isspace(*address))  address++;
    while (*address &&  isspace(*address)) *address++ = 0;
    title = address;
    while (*title && !isspace(*title))  title++;
    while (*title &&  isspace(*title)) *title++ = 0;

    // extract port number from address
    int port = ServerPort;
    scan2 = strchr(name, ':');
    if (scan2) {
      port = atoi(scan2 + 1);
      *scan2 = 0;
    }

    // check info
    if (strcmp(version, getServerVersion()) == 0 &&
	(int)strlen(infoServer) == PingPacketHexPackedSize &&
	port >= 1 && port <= 65535) {
      // store info
      ServerItem serverInfo;
      serverInfo.ping.unpackHex(infoServer);
      int dot[4] = {127,0,0,1};
      if (sscanf(address, "%d.%d.%d.%d", dot+0, dot+1, dot+2, dot+3) == 4) {
	if (dot[0] >= 0 && dot[0] <= 255 &&
	    dot[1] >= 0 && dot[1] <= 255 &&
	    dot[2] >= 0 && dot[2] <= 255 &&
	    dot[3] >= 0 && dot[3] <= 255) {
	  InAddr addr;
	  unsigned char* paddr = (unsigned char*)&addr.s_addr;
	  paddr[0] = (unsigned char)dot[0];
	  paddr[1] = (unsigned char)dot[1];
	  paddr[2] = (unsigned char)dot[2];
	  paddr[3] = (unsigned char)dot[3];
	  serverInfo.ping.serverId.serverHost = addr;
	}
      }
      serverInfo.ping.serverId.port = htons((int16_t)port);
      serverInfo.name = name;

      // construct description
      serverInfo.description = serverInfo.name;
      if (port != ServerPort) {
	char portBuf[20];
	sprintf(portBuf, "%d", port);
	serverInfo.description += ":";
	serverInfo.description += portBuf;
      }
      if (strlen(title) > 0) {
	serverInfo.description += "; ";
	serverInfo.description += title;
      }

      serverInfo.cached = false;
      // add to list & add it to the server cache
      addToList(serverInfo, true);
    }

    // next reply
    base = scan;
  }

  // remove parsed replies
  theLen -= int(base - (char *)theData);
  memmove(theData, base, theLen);
}

void ServerList::addToList(ServerItem info, bool doCache)
{
  // update if we already have it
  int i;

  // search and delete entry for this item if it exists
  // (updating info in place may "unsort" the list)
  for (i = 0; i < (int)servers.size(); i++) {
    ServerItem& server = servers[i];
    if (server.ping.serverId.serverHost.s_addr
	== info.ping.serverId.serverHost.s_addr
	&& server.ping.serverId.port == info.ping.serverId.port) {
      servers.erase(servers.begin() + i); // erase this item
      break;
    }
  }

  // find point to insert new player at
  int insertPoint = -1; // point to insert server into

  // insert new item before the first serveritem with is deemed to be less
  // in value than the item to be inserted -- cached items are less than
  // non-cached, items that have more players are more, etc..
  for (i = 0; i < (int)servers.size(); i++) {
    ServerItem& server = servers[i];
    if (server < info){
      insertPoint = i;
      break;
    }
  }

  // mark server in current list if it is a favorite server
  std::string serverAddress = info.getAddrName();
  if (serverCache->isFavorite(serverAddress))
    info.favorite = true;

  if (insertPoint == -1){ // no spot to insert it into -- goes on back
    servers.push_back(info);
  } else {  // found a spot to insert it into
    servers.insert(servers.begin() + insertPoint, info);
  }

  // update display
  /*
  char buffer [80];
  std::vector<std::string> args;
  sprintf(buffer, "%d", (int)servers.size());
  args.push_back(buffer);
  setStatus("Servers found: {1}", &args);
  */

  // force update
  /*
  const int oldSelectedIndex = selectedIndex;
  selectedIndex = -1;
  setSelected(oldSelectedIndex);
  */

  if (doCache) {
    info.cached = true; // values in cache are "cached"
    // update the last updated time to now
    info.setUpdateTime();

    ServerListCache::SRV_STR_MAP::iterator iter;
    iter = serverCache->find(serverAddress);  // find entry to allow update
    if (iter != serverCache->end()){ // if we find it, update it
      iter->second = info;
    } else {
      // insert into cache -- wasn't found
      serverCache->insert(serverAddress, info);
    }
  }
}

// mark server identified by host:port string as favorite
void		    ServerList::markFav(const std::string &serverAddress, bool fav)
{
  for (int i = 0; i < (int)servers.size(); i++) {
    if (serverAddress == servers[i].getAddrName()) {
      servers[i].favorite = fav;
      break;
    }
  }
}

void			ServerList::checkEchos(StartupInfo *info)
{
  startupInfo = info;

  // *** NOTE *** searching spinner update was here

  // lookup server list in phase 0
  if (phase == 0) {

    std::string url = info->listServerURL;

    std::string msg = "action=LIST&version=";
    msg	    += getServerVersion();
    msg	    += "&callsign=";
    msg	    += TextUtils::url_encode(info->callsign);
    msg	    += "&password=";
    msg	    += TextUtils::url_encode(info->password);
    setPostMode(msg);
    setURLwithNonce(url);
    addHandle();

    // do phase 1 only if we found a valid list server url
    phase = 1;
  }

  // get echo messages
  while (true) {
    // *** NOTE *** searching spinner update was here

    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 250;

    fd_set read_set, write_set;
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
    if (pingBcastSocket != -1) {
      FD_SET((unsigned int)pingBcastSocket, &read_set);
    }
    int fdMax = pingBcastSocket;

    const int nfound = select(fdMax+1, (fd_set*)&read_set,
					(fd_set*)&write_set, 0, &timeout);
    if (nfound <= 0)
      break;

    // check broadcast sockets
    ServerItem serverInfo;
    sockaddr_in addr;

    if (pingBcastSocket != -1 && FD_ISSET(pingBcastSocket, &read_set)) {
      if (serverInfo.ping.read(pingBcastSocket, &addr)) {
	serverInfo.ping.serverId.serverHost = addr.sin_addr;
	serverInfo.cached = false;
	addToListWithLookup(serverInfo);
      }
    }
  } // end loop waiting for input/output on any list server
}

void			ServerList::addToListWithLookup(ServerItem& info)
{
  info.name = Address::getHostByAddress(info.ping.serverId.serverHost);

  // tack on port number to description if not default
  info.description = info.name;
  const int port = (int)ntohs((unsigned short)info.ping.serverId.port);
  if (port != ServerPort) {
    char portBuf[20];
    sprintf(portBuf, "%d", port);
    info.description += ":";
    info.description += portBuf;
  }

  addToList(info); // do not cache network lan - etc. servers
}

// add the entire cache to the server list
void			ServerList::addCacheToList()
{
  if (addedCacheToList)
    return;
  addedCacheToList = true;
  for (ServerListCache::SRV_STR_MAP::iterator iter = serverCache->begin();
       iter != serverCache->end(); ++iter) {
    addToList(iter->second);
  }
}

void ServerList::collectData(char *ptr, int len)
{
  phase = 2;

  cURLManager::collectData(ptr, len);

  readServerList();
}

void ServerList::finalization(char *, unsigned int, bool good)
{
  if (!good) {
    printError("Can't talk with list server");
    addCacheToList();
    phase = -1;
  } else {
    phase = 4;
  }
}

const std::vector<ServerItem>& ServerList::getServers() {
  return servers;
}

std::vector<ServerItem>::size_type ServerList::size() {
  return servers.size();
}

void ServerList::clear() {
  servers.clear();
}

int ServerList::updateFromCache() {
  // clear server list
  clear();

  int numItemsAdded = 0;

  for (ServerListCache::SRV_STR_MAP::const_iterator iter = serverCache->begin();
       iter != serverCache->end(); ++iter) {
    // if maxCacheAge is 0 we add nothing
    // if the item is young enough we add it
    if (serverCache->getMaxCacheAge() != 0
	&& iter->second.getAgeMinutes() < serverCache->getMaxCacheAge()) {
      addToList(iter->second);
      numItemsAdded ++;
    }
  }

  return numItemsAdded;
}

bool ServerList::searchActive() const {
  return (phase < 4) ? true : false;
}

bool ServerList::serverFound() const {
  return (phase >= 2) ? true : false;
}

void ServerList::_shutDown() {
  // close broadcast socket
  closeBroadcast(pingBcastSocket);
  pingBcastSocket = -1;
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>#ifndef TOPAZ_GL_API_RENDERER_HPP
#define TOPAZ_GL_API_RENDERER_HPP
#include "core/containers/basic_list.hpp"
#include "core/interfaces/cloneable.hpp"
#include "core/vector.hpp"
#include "gl/impl/common/renderer.hpp"
#include "gl/render_pass.hpp"
#include "gl/resource.hpp"
#include "gl/shader.hpp"
#include <cstdint>
#include <concepts>

namespace tz::gl
{
    /**
	 * \addtogroup tz_gl Topaz Graphics Library (tz::gl)
	 * A collection of low-level renderer-agnostic graphical interfaces.
	 * @{
	 */
    /**
     * @brief Describes the layout of a vertex attribute in memory.
     */
    struct RendererAttributeFormat
    {
        /// How many bytes between the beginning of the element and this attribute location (offsetof)
        std::size_t element_attribute_offset;
        /// What is the data type of this attribute?
        RendererComponentType type;
    };

    /**
     * @brief Describes the nature of the vertex data in memory. Vertex data is organised as an array of elements. Each element has one or more attributes.
     */
    struct RendererElementFormat
    {
        /// What is the total size of this element? Includes all attributes and any padding.
        std::size_t binding_size;
        /// How often should we expect to see these elements? Per vertex, or per instance?
        RendererInputFrequency basis;
        /// List of all attributes. These must be in order.
        tz::BasicList<RendererAttributeFormat> binding_attributes; // TODO: (C++20 constexpr std::vector support) replace with std::vector so we are a LiteralType. Then IRendererInput::get_format() can be constexpr.
    };

    /**
     * @brief A renderer is always provided some input data. This data is always sorted into vertex/index buffers eventually, but there may be custom setups where you need more control over how this data is represented in memory.
     * @details Renderer inputs can vary wildly in their nature depending on what sort of rendering you'd like to do. Topaz does not mandate a specific renderer input type, but the most common use-case is for storing mesh data. A class already exists for this purpose: @ref MeshInput
     *
     * @pre IRendererInput declares `IRendererInput::unique_clone()` which derived types must implement. If you are implementing derived type `D`, if `D` is copy-constructible then implement `IRendererInputCopyable<D>` instead. If `D` is not copy-constructible then you must implement `IRendererInput::unique_clone()` yourself.
     *
     */
    class IRendererInput : public tz::IUniqueCloneable<IRendererInput>
    {
    public:
        /**
         * @brief Retrieve the data access specifier for this render input type.
         * @note Inputs derived from @ref IRendererInput are `StaticFixed` by default, but this can be overriden. Inputs derived from @ref IRendererDynamicInput are always `DynamicFixed` and this cannot be overridden.
         *
         * @return Access specifier for the data relative to the @ref IRenderer.
         */
        virtual constexpr RendererInputDataAccess data_access() const {return RendererInputDataAccess::StaticFixed;}
        /**
         * @brief Obtain the format of the input elements.
         *
         * @return RenderElementFormat corresponding to layout of a single vertex data element.
         */
        virtual RendererElementFormat get_format() const = 0;
        /**
         * @brief Retrieve the vertex data as bytes. The data within the span is immutable.
         * @note See @ref IRendererDynamicInput::get_vertex_bytes_dynamic() for the option of mutable vertex data.
         * @note It is vaild to interpret these bytes as a `VertexType`, where VertexType is the type of the vertex data.
         * @return std::span<const std::byte> displaying the byte-representation of vertex data.
         */
        virtual std::span<const std::byte> get_vertex_bytes() const = 0;
        /**
         * @brief Retrieve the index data.
         *
         * @return std::span<const unsigned int> displaying an array of all the indices.
         */
        virtual std::span<const unsigned int> get_indices() const = 0;
    };

    /**
     * @brief Identical to @ref IRendererInput, but `IRendererInputCopyable<T>::unique_clone()` need not be implemented.
     * @pre Derived must be copy-constructible. Otherwise, the program is ill-formed.
     *
     * @tparam Derived Renderer input type. It must be copy-constructible.
     */
    template<class Derived>
    class IRendererInputCopyable : public IRendererInput
    {
    public:
        /// Invokes `Derived::Derived(const Derived&)`
        [[nodiscard]] virtual std::unique_ptr<IRendererInput> unique_clone() const final
        {
            static_assert(std::is_copy_constructible_v<Derived>, "IRendererInputCopyable<T>: T must be copyable. Derive from IRendererInput and implement unique_clone if not copyable.");
            return std::make_unique<Derived>(static_cast<const Derived&>(*this));
        }
    };

    /**
     * @brief Similar to @ref IRendererInput, but the vertex/index data can be changed at any point, even while used by a @ref IRenderer.
     */
    class IRendererDynamicInput : public IRendererInput
    {
    public:
        /**
         * @brief Retrieve the data access specifier for this render input type.
         * @note Inputs derived from @ref IRendererInput are `StaticFixed` by default. Inputs derived from @ref IRendererDynamicInput are always `DynamicFixed`.
         *
         * @return constexpr RendererInputDataAccess
         */
        virtual constexpr RendererInputDataAccess data_access() const final{return RendererInputDataAccess::DynamicFixed;}
        /**
         * @brief Retrieve the vertex data as bytes. The data within the span is mutable.
         * @note Aside from mutablility, this is functionally identical to @ref IRendererInput::get_vertex_bytes().
         * @note Dynamic vertex data can be edited on-the-fly -- It is valid to edit data even while the input is in-use by a renderer, in which case the updated values are guaranteed to be visible in the next render invocation.
         * @return std::span<std::byte> displaying the byte-representation of vertex data.
         */
        virtual std::span<std::byte> get_vertex_bytes_dynamic() = 0;

        #if TZ_VULKAN
            friend class RendererBufferManagerVulkan;
        #elif TZ_OGL
            friend class RendererOGL;
        #endif
    private:
        // Only intended to be used by the Renderer.
        virtual void set_vertex_data(std::byte* vertex_data) = 0;
        // Only intended to be used by the Renderer.
        virtual void set_index_data(unsigned int* index_data) = 0;
    };

    /**
     * @brief Identical to @ref IRendererDynamicInput, but `IRendererDynamicInputCopyable<T>::unique_clone()` need not be implemented.
     * @pre Derived must be copy-constructible. Otherwise, the program is ill-formed.
     *
     * @tparam Derived Renderer input type. It must be copy-constructible.
     */
    template<class Derived>
    class IRendererDynamicInputCopyable : public IRendererDynamicInput
    {
    public:
        /// Invokes `Derived::Derived(const Derived&)`
        [[nodiscard]] virtual std::unique_ptr<IRendererInput> unique_clone() const final
        {
            static_assert(std::is_copy_constructible_v<Derived>, "IRendererInputCopyable<T>: T must be copyable. Derive from IRendererInput and implement unique_clone if not copyable.");
            return std::make_unique<Derived>(static_cast<const Derived&>(*this));
        }
    };

    class IRendererOutput
    {
    public:
        virtual ~IRendererOutput() = default;
        virtual RendererOutputType get_type() const = 0;
    };

    /**
     * @brief Structure describing the nature of a renderer.
     * @note There is no default element format. You must specify one before creating a renderer, otherwise the behaviour is undefined.
     * @note The default culling strategy is no culling. You are likely to improve performance by utilising a culling strategy.
     */
    class IRendererBuilder
    {
    public:
        /**
         * @brief Provide initial input data for the renderer.
         * It is an error to retain this reference to dynamic input data and expect to change it later. To do that, create the Renderer as normal and invoke @ref IRenderer::get_input() to retrieve the Renderer's own copy of the input and perform your processing there.
         * @note When the Renderer is constructed, it will have its own copy of the input.
         *
         * @param input Reference to an existing @ref IRendererInput
         */
        virtual void set_input(const IRendererInput& input) = 0;
        /**
         * @brief Retrieve the format of the vertex data elements.
         *
         * @return RendererElementFormat describing how vertex data is laid out in memory.
         */
        virtual const IRendererInput* get_input() const = 0;

        virtual void set_output(const IRendererOutput& output) = 0;
        virtual const IRendererOutput* get_output() const = 0;

        virtual ResourceHandle add_resource(const IResource& resource) = 0;

        /**
         * @brief Set the culling strategy used during rendering.
         *
         * @param culling_strategy Which faces of elements should be culled during rendering?
         */
        virtual void set_culling_strategy(RendererCullingStrategy culling_strategy) = 0;
        /**
         * @brief Retrieve the current culling strategy.
         *
         * @return Culling strategy that the renderer will use.
         */
        virtual RendererCullingStrategy get_culling_strategy() const = 0;
        /**
         * @brief Renderers must reference an existing RenderPass. Renderers will render each stage of the render pass in the expected order.
         *
         * @param render_pass Render pass that will be ran
         */
        virtual void set_render_pass(const RenderPass& render_pass) = 0;
        /**
         * @brief Retrieve the existing render pass associated with this renderer.
         * @pre A render pass must have previously been associated with this renderer via IRendererBuilder::set_render_pass
         *
         * @return The current render pass object referenced by this renderer.
         */
        virtual const RenderPass& get_render_pass() const = 0;

        virtual void set_shader(const Shader& shader) = 0;
        virtual const Shader& get_shader() const = 0;
    };

    /**
     * @brief High-level object used to render geometry. Renderers can be described as follows:
     * - A Renderer has exactly one @ref IRendererInput. It is planned to allow Renderers without inputs (for example, if vertices are hard-coded within the shader) but this is not yet implemented.
     * - A Renderer has exactly one @ref IRendererOutput.
     * - Renderers will never change their input or its corresponding input data.
     * - Renderers cannot have their input/output changed. However, dynamic input data is supported via @ref IRendererDynamicInput.
     * - Renderers have an initial clear-colour but this can be changed after construction. If it is changed during rendering, this may reconstruct the entire render pipeline and incur a large spike in latency.
     * - Renderers will be able to use any number of resources (aka uniform buffers, textures etc...) This is not yet implemented.
     * - Renderers will be able to edit data for a given resource if the resource is writable (e.g SSBOs).
     */
    class IRenderer
    {
    public:
        /**
         * @brief Set the clear colour for any colour attachments throughout the renderpass.
         *
         * @param clear_colour Clear colour value, as a normalised Vec4.
         */
        virtual void set_clear_colour(tz::Vec4 clear_colour) = 0;
        /**
         * @brief Retrieve the clear colour for any colour attachments throughout the renderpass.
         * @note The default clear colour is {0, 0, 0, 0} (solid black) on all platforms.
         *
         * @return Clear colour value, as a normalised Vec4.
         */
        virtual tz::Vec4 get_clear_colour() const = 0;

        /**
         * @brief Retrieve the renderer input.
         * @note Each renderer takes a copy of the input it was given in its corresponding `IRendererBuilder`. This is NOT the same input as the one you gave to the builder.
         * @note The pointer returned is valid until this Renderer reaches the end of its lifetime.
         * @details For static renderer inputs, you will rarely find this useful. If you have dynamic renderer inputs, you should retrieve a pointer to the input here and edit its data as you wish.
         *
         * @return IRendererInput* pointing to the renderer's input.
         */
        virtual IRendererInput* get_input() = 0;

        virtual IResource* get_resource(ResourceHandle handle) = 0;

        /**
         * @brief Proceed through the provided render-pass using any inputs and resources.
         */
        virtual void render() = 0;
    };
    /**
     * @}
     */
}

#endif // TOPAZ_GL_API_RENDERER_HPP<commit_msg>* IRendererInput now has a virtual destructor as Renderer will unique_clone it<commit_after>#ifndef TOPAZ_GL_API_RENDERER_HPP
#define TOPAZ_GL_API_RENDERER_HPP
#include "core/containers/basic_list.hpp"
#include "core/interfaces/cloneable.hpp"
#include "core/vector.hpp"
#include "gl/impl/common/renderer.hpp"
#include "gl/render_pass.hpp"
#include "gl/resource.hpp"
#include "gl/shader.hpp"
#include <cstdint>
#include <concepts>

namespace tz::gl
{
    /**
	 * \addtogroup tz_gl Topaz Graphics Library (tz::gl)
	 * A collection of low-level renderer-agnostic graphical interfaces.
	 * @{
	 */
    /**
     * @brief Describes the layout of a vertex attribute in memory.
     */
    struct RendererAttributeFormat
    {
        /// How many bytes between the beginning of the element and this attribute location (offsetof)
        std::size_t element_attribute_offset;
        /// What is the data type of this attribute?
        RendererComponentType type;
    };

    /**
     * @brief Describes the nature of the vertex data in memory. Vertex data is organised as an array of elements. Each element has one or more attributes.
     */
    struct RendererElementFormat
    {
        /// What is the total size of this element? Includes all attributes and any padding.
        std::size_t binding_size;
        /// How often should we expect to see these elements? Per vertex, or per instance?
        RendererInputFrequency basis;
        /// List of all attributes. These must be in order.
        tz::BasicList<RendererAttributeFormat> binding_attributes; // TODO: (C++20 constexpr std::vector support) replace with std::vector so we are a LiteralType. Then IRendererInput::get_format() can be constexpr.
    };

    /**
     * @brief A renderer is always provided some input data. This data is always sorted into vertex/index buffers eventually, but there may be custom setups where you need more control over how this data is represented in memory.
     * @details Renderer inputs can vary wildly in their nature depending on what sort of rendering you'd like to do. Topaz does not mandate a specific renderer input type, but the most common use-case is for storing mesh data. A class already exists for this purpose: @ref MeshInput
     *
     * @pre IRendererInput declares `IRendererInput::unique_clone()` which derived types must implement. If you are implementing derived type `D`, if `D` is copy-constructible then implement `IRendererInputCopyable<D>` instead. If `D` is not copy-constructible then you must implement `IRendererInput::unique_clone()` yourself.
     *
     */
    class IRendererInput : public tz::IUniqueCloneable<IRendererInput>
    {
    public:
        virtual ~IRendererInput() = default;
        /**
         * @brief Retrieve the data access specifier for this render input type.
         * @note Inputs derived from @ref IRendererInput are `StaticFixed` by default, but this can be overriden. Inputs derived from @ref IRendererDynamicInput are always `DynamicFixed` and this cannot be overridden.
         *
         * @return Access specifier for the data relative to the @ref IRenderer.
         */
        virtual constexpr RendererInputDataAccess data_access() const {return RendererInputDataAccess::StaticFixed;}
        /**
         * @brief Obtain the format of the input elements.
         *
         * @return RenderElementFormat corresponding to layout of a single vertex data element.
         */
        virtual RendererElementFormat get_format() const = 0;
        /**
         * @brief Retrieve the vertex data as bytes. The data within the span is immutable.
         * @note See @ref IRendererDynamicInput::get_vertex_bytes_dynamic() for the option of mutable vertex data.
         * @note It is vaild to interpret these bytes as a `VertexType`, where VertexType is the type of the vertex data.
         * @return std::span<const std::byte> displaying the byte-representation of vertex data.
         */
        virtual std::span<const std::byte> get_vertex_bytes() const = 0;
        /**
         * @brief Retrieve the index data.
         *
         * @return std::span<const unsigned int> displaying an array of all the indices.
         */
        virtual std::span<const unsigned int> get_indices() const = 0;
    };

    /**
     * @brief Identical to @ref IRendererInput, but `IRendererInputCopyable<T>::unique_clone()` need not be implemented.
     * @pre Derived must be copy-constructible. Otherwise, the program is ill-formed.
     *
     * @tparam Derived Renderer input type. It must be copy-constructible.
     */
    template<class Derived>
    class IRendererInputCopyable : public IRendererInput
    {
    public:
        /// Invokes `Derived::Derived(const Derived&)`
        [[nodiscard]] virtual std::unique_ptr<IRendererInput> unique_clone() const final
        {
            static_assert(std::is_copy_constructible_v<Derived>, "IRendererInputCopyable<T>: T must be copyable. Derive from IRendererInput and implement unique_clone if not copyable.");
            return std::make_unique<Derived>(static_cast<const Derived&>(*this));
        }
    };

    /**
     * @brief Similar to @ref IRendererInput, but the vertex/index data can be changed at any point, even while used by a @ref IRenderer.
     */
    class IRendererDynamicInput : public IRendererInput
    {
    public:
        /**
         * @brief Retrieve the data access specifier for this render input type.
         * @note Inputs derived from @ref IRendererInput are `StaticFixed` by default. Inputs derived from @ref IRendererDynamicInput are always `DynamicFixed`.
         *
         * @return constexpr RendererInputDataAccess
         */
        virtual constexpr RendererInputDataAccess data_access() const final{return RendererInputDataAccess::DynamicFixed;}
        /**
         * @brief Retrieve the vertex data as bytes. The data within the span is mutable.
         * @note Aside from mutablility, this is functionally identical to @ref IRendererInput::get_vertex_bytes().
         * @note Dynamic vertex data can be edited on-the-fly -- It is valid to edit data even while the input is in-use by a renderer, in which case the updated values are guaranteed to be visible in the next render invocation.
         * @return std::span<std::byte> displaying the byte-representation of vertex data.
         */
        virtual std::span<std::byte> get_vertex_bytes_dynamic() = 0;

        #if TZ_VULKAN
            friend class RendererBufferManagerVulkan;
        #elif TZ_OGL
            friend class RendererOGL;
        #endif
    private:
        // Only intended to be used by the Renderer.
        virtual void set_vertex_data(std::byte* vertex_data) = 0;
        // Only intended to be used by the Renderer.
        virtual void set_index_data(unsigned int* index_data) = 0;
    };

    /**
     * @brief Identical to @ref IRendererDynamicInput, but `IRendererDynamicInputCopyable<T>::unique_clone()` need not be implemented.
     * @pre Derived must be copy-constructible. Otherwise, the program is ill-formed.
     *
     * @tparam Derived Renderer input type. It must be copy-constructible.
     */
    template<class Derived>
    class IRendererDynamicInputCopyable : public IRendererDynamicInput
    {
    public:
        /// Invokes `Derived::Derived(const Derived&)`
        [[nodiscard]] virtual std::unique_ptr<IRendererInput> unique_clone() const final
        {
            static_assert(std::is_copy_constructible_v<Derived>, "IRendererInputCopyable<T>: T must be copyable. Derive from IRendererInput and implement unique_clone if not copyable.");
            return std::make_unique<Derived>(static_cast<const Derived&>(*this));
        }
    };

    class IRendererOutput
    {
    public:
        virtual ~IRendererOutput() = default;
        virtual RendererOutputType get_type() const = 0;
    };

    /**
     * @brief Structure describing the nature of a renderer.
     * @note There is no default element format. You must specify one before creating a renderer, otherwise the behaviour is undefined.
     * @note The default culling strategy is no culling. You are likely to improve performance by utilising a culling strategy.
     */
    class IRendererBuilder
    {
    public:
        /**
         * @brief Provide initial input data for the renderer.
         * It is an error to retain this reference to dynamic input data and expect to change it later. To do that, create the Renderer as normal and invoke @ref IRenderer::get_input() to retrieve the Renderer's own copy of the input and perform your processing there.
         * @note When the Renderer is constructed, it will have its own copy of the input.
         *
         * @param input Reference to an existing @ref IRendererInput
         */
        virtual void set_input(const IRendererInput& input) = 0;
        /**
         * @brief Retrieve the format of the vertex data elements.
         *
         * @return RendererElementFormat describing how vertex data is laid out in memory.
         */
        virtual const IRendererInput* get_input() const = 0;

        virtual void set_output(const IRendererOutput& output) = 0;
        virtual const IRendererOutput* get_output() const = 0;

        virtual ResourceHandle add_resource(const IResource& resource) = 0;

        /**
         * @brief Set the culling strategy used during rendering.
         *
         * @param culling_strategy Which faces of elements should be culled during rendering?
         */
        virtual void set_culling_strategy(RendererCullingStrategy culling_strategy) = 0;
        /**
         * @brief Retrieve the current culling strategy.
         *
         * @return Culling strategy that the renderer will use.
         */
        virtual RendererCullingStrategy get_culling_strategy() const = 0;
        /**
         * @brief Renderers must reference an existing RenderPass. Renderers will render each stage of the render pass in the expected order.
         *
         * @param render_pass Render pass that will be ran
         */
        virtual void set_render_pass(const RenderPass& render_pass) = 0;
        /**
         * @brief Retrieve the existing render pass associated with this renderer.
         * @pre A render pass must have previously been associated with this renderer via IRendererBuilder::set_render_pass
         *
         * @return The current render pass object referenced by this renderer.
         */
        virtual const RenderPass& get_render_pass() const = 0;

        virtual void set_shader(const Shader& shader) = 0;
        virtual const Shader& get_shader() const = 0;
    };

    /**
     * @brief High-level object used to render geometry. Renderers can be described as follows:
     * - A Renderer has exactly one @ref IRendererInput. It is planned to allow Renderers without inputs (for example, if vertices are hard-coded within the shader) but this is not yet implemented.
     * - A Renderer has exactly one @ref IRendererOutput.
     * - Renderers will never change their input or its corresponding input data.
     * - Renderers cannot have their input/output changed. However, dynamic input data is supported via @ref IRendererDynamicInput.
     * - Renderers have an initial clear-colour but this can be changed after construction. If it is changed during rendering, this may reconstruct the entire render pipeline and incur a large spike in latency.
     * - Renderers will be able to use any number of resources (aka uniform buffers, textures etc...) This is not yet implemented.
     * - Renderers will be able to edit data for a given resource if the resource is writable (e.g SSBOs).
     */
    class IRenderer
    {
    public:
        /**
         * @brief Set the clear colour for any colour attachments throughout the renderpass.
         *
         * @param clear_colour Clear colour value, as a normalised Vec4.
         */
        virtual void set_clear_colour(tz::Vec4 clear_colour) = 0;
        /**
         * @brief Retrieve the clear colour for any colour attachments throughout the renderpass.
         * @note The default clear colour is {0, 0, 0, 0} (solid black) on all platforms.
         *
         * @return Clear colour value, as a normalised Vec4.
         */
        virtual tz::Vec4 get_clear_colour() const = 0;

        /**
         * @brief Retrieve the renderer input.
         * @note Each renderer takes a copy of the input it was given in its corresponding `IRendererBuilder`. This is NOT the same input as the one you gave to the builder.
         * @note The pointer returned is valid until this Renderer reaches the end of its lifetime.
         * @details For static renderer inputs, you will rarely find this useful. If you have dynamic renderer inputs, you should retrieve a pointer to the input here and edit its data as you wish.
         *
         * @return IRendererInput* pointing to the renderer's input.
         */
        virtual IRendererInput* get_input() = 0;

        virtual IResource* get_resource(ResourceHandle handle) = 0;

        /**
         * @brief Proceed through the provided render-pass using any inputs and resources.
         */
        virtual void render() = 0;
    };
    /**
     * @}
     */
}

#endif // TOPAZ_GL_API_RENDERER_HPP<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <asio/ip/tcp.hpp>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout)
{
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout)
{
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_limiter_timer_active = false;
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<commit_msg>fix in http_connection where the handler was sometimes not called when rate limited and aborted<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <asio/ip/tcp.hpp>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout)
{
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout)
{
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_limiter_timer_active = false;
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if (e == asio::error::operation_aborted
		&& m_limiter_timer_active)
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<|endoftext|>