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

/**
 * @file battery.cpp
 *
 * Library calls for battery functionality.
 *
 * @author Julian Oes <julian@oes.ch>
 */

#include "battery.h"
#include <mathlib/mathlib.h>
#include <cstring>
#include <px4_platform_common/defines.h>

Battery::Battery() :
	ModuleParams(nullptr),
	_warning(battery_status_s::BATTERY_WARNING_NONE),
	_last_timestamp(0)
{
}

void
Battery::reset(battery_status_s *battery_status)
{
	memset(battery_status, 0, sizeof(*battery_status));
	battery_status->current_a = -1.f;
	battery_status->remaining = 1.f;
	battery_status->scale = 1.f;
	battery_status->cell_count = _param_bat_n_cells.get();
	// TODO: check if it is sane to reset warning to NONE
	battery_status->warning = battery_status_s::BATTERY_WARNING_NONE;
	battery_status->connected = false;
}

void
Battery::updateBatteryStatus(hrt_abstime timestamp, float voltage_v, float current_a,
			     bool connected, bool selected_source, int priority,
			     float throttle_normalized,
			     bool armed, battery_status_s *battery_status)
{
	reset(battery_status);
	battery_status->timestamp = timestamp;
	filterVoltage(voltage_v);
	filterThrottle(throttle_normalized);
	filterCurrent(current_a);
	sumDischarged(timestamp, current_a);
	estimateRemaining(_voltage_filtered_v, _current_filtered_a, _throttle_filtered, armed);
	computeScale();

	if (_battery_initialized) {
		determineWarning(connected);
	}

	if (_voltage_filtered_v > 2.1f) {
		_battery_initialized = true;
		battery_status->voltage_v = voltage_v;
		battery_status->voltage_filtered_v = _voltage_filtered_v;
		battery_status->scale = _scale;
		battery_status->current_a = current_a;
		battery_status->current_filtered_a = _current_filtered_a;
		battery_status->discharged_mah = _discharged_mah;
		battery_status->warning = _warning;
		battery_status->remaining = _remaining;
		battery_status->connected = connected;
		battery_status->system_source = selected_source;
		battery_status->priority = priority;
	}

	battery_status->temperature = NAN;
}

void
Battery::filterVoltage(float voltage_v)
{
	if (!_battery_initialized) {
		_voltage_filtered_v = voltage_v;
	}

	// TODO: inspect that filter performance
	const float filtered_next = _voltage_filtered_v * 0.99f + voltage_v * 0.01f;

	if (PX4_ISFINITE(filtered_next)) {
		_voltage_filtered_v = filtered_next;
	}
}

void
Battery::filterCurrent(float current_a)
{
	if (!_battery_initialized) {
		_current_filtered_a = current_a;
	}

	// ADC poll is at 100Hz, this will perform a low pass over approx 500ms
	const float filtered_next = _current_filtered_a * 0.98f + current_a * 0.02f;

	if (PX4_ISFINITE(filtered_next)) {
		_current_filtered_a = filtered_next;
	}
}

void Battery::filterThrottle(float throttle)
{
	if (!_battery_initialized) {
		_throttle_filtered = throttle;
	}

	const float filtered_next = _throttle_filtered * 0.99f + throttle * 0.01f;

	if (PX4_ISFINITE(filtered_next)) {
		_throttle_filtered = filtered_next;
	}
}

void
Battery::sumDischarged(hrt_abstime timestamp, float current_a)
{
	// Not a valid measurement
	if (current_a < 0.f) {
		// Because the measurement was invalid we need to stop integration
		// and re-initialize with the next valid measurement
		_last_timestamp = 0;
		return;
	}

	// Ignore first update because we don't know dt.
	if (_last_timestamp != 0) {
		const float dt = (timestamp - _last_timestamp) / 1e6;
		// mAh since last loop: (current[A] * 1000 = [mA]) * (dt[s] / 3600 = [h])
		_discharged_mah_loop = (current_a * 1e3f) * (dt / 3600.f);
		_discharged_mah += _discharged_mah_loop;
	}

	_last_timestamp = timestamp;
}

void
Battery::estimateRemaining(float voltage_v, float current_a, float throttle, bool armed)
{
	// remaining battery capacity based on voltage
	float cell_voltage = voltage_v / _param_bat_n_cells.get();

	// correct battery voltage locally for load drop to avoid estimation fluctuations
	if (_param_bat_r_internal.get() >= 0.f) {
		cell_voltage += _param_bat_r_internal.get() * current_a;

	} else {
		// assume linear relation between throttle and voltage drop
		cell_voltage += throttle * _param_bat_v_load_drop.get();
	}

	_remaining_voltage = math::gradual(cell_voltage, _param_bat_v_empty.get(), _param_bat_v_charged.get(), 0.f, 1.f);

	// choose which quantity we're using for final reporting
	if (_param_bat_capacity.get() > 0.f) {
		// if battery capacity is known, fuse voltage measurement with used capacity
		if (!_battery_initialized) {
			// initialization of the estimation state
			_remaining = _remaining_voltage;

		} else {
			// The lower the voltage the more adjust the estimate with it to avoid deep discharge
			const float weight_v = 3e-4f * (1 - _remaining_voltage);
			_remaining = (1 - weight_v) * _remaining + weight_v * _remaining_voltage;
			// directly apply current capacity slope calculated using current
			_remaining -= _discharged_mah_loop / _param_bat_capacity.get();
			_remaining = math::max(_remaining, 0.f);
		}

	} else {
		// else use voltage
		_remaining = _remaining_voltage;
	}
}

void
Battery::determineWarning(bool connected)
{
	if (connected) {
		// propagate warning state only if the state is higher, otherwise remain in current warning state
		if (_remaining < _param_bat_emergen_thr.get() || (_warning == battery_status_s::BATTERY_WARNING_EMERGENCY)) {
			_warning = battery_status_s::BATTERY_WARNING_EMERGENCY;

		} else if (_remaining < _param_bat_crit_thr.get() || (_warning == battery_status_s::BATTERY_WARNING_CRITICAL)) {
			_warning = battery_status_s::BATTERY_WARNING_CRITICAL;

		} else if (_remaining < _param_bat_low_thr.get() || (_warning == battery_status_s::BATTERY_WARNING_LOW)) {
			_warning = battery_status_s::BATTERY_WARNING_LOW;
		}
	}
}

void
Battery::computeScale()
{
	const float voltage_range = (_param_bat_v_charged.get() - _param_bat_v_empty.get());

	// reusing capacity calculation to get single cell voltage before drop
	const float bat_v = _param_bat_v_empty.get() + (voltage_range * _remaining_voltage);

	_scale = _param_bat_v_charged.get() / bat_v;

	if (_scale > 1.3f) { // Allow at most 30% compensation
		_scale = 1.3f;

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

/**
 * @file battery.cpp
 *
 * Library calls for battery functionality.
 *
 * @author Julian Oes <julian@oes.ch>
 */

#include "battery.h"
#include <mathlib/mathlib.h>
#include <cstring>
#include <px4_platform_common/defines.h>

Battery::Battery() :
	ModuleParams(nullptr),
	_warning(battery_status_s::BATTERY_WARNING_NONE),
	_last_timestamp(0)
{
}

void
Battery::reset(battery_status_s *battery_status)
{
	memset(battery_status, 0, sizeof(*battery_status));
	battery_status->current_a = -1.f;
	battery_status->remaining = 1.f;
	battery_status->scale = 1.f;
	battery_status->cell_count = _param_bat_n_cells.get();
	// TODO: check if it is sane to reset warning to NONE
	battery_status->warning = battery_status_s::BATTERY_WARNING_NONE;
	battery_status->connected = false;
}

void
Battery::updateBatteryStatus(hrt_abstime timestamp, float voltage_v, float current_a,
			     bool connected, bool selected_source, int priority,
			     float throttle_normalized,
			     bool armed, battery_status_s *battery_status)
{
	reset(battery_status);
	battery_status->timestamp = timestamp;
	filterVoltage(voltage_v);
	filterThrottle(throttle_normalized);
	filterCurrent(current_a);
	sumDischarged(timestamp, current_a);
	estimateRemaining(_voltage_filtered_v, _current_filtered_a, _throttle_filtered, armed);
	computeScale();

	if (_battery_initialized) {
		determineWarning(connected);
	}

	if (_voltage_filtered_v > 2.1f) {
		_battery_initialized = true;
		battery_status->voltage_v = voltage_v;
		battery_status->voltage_filtered_v = _voltage_filtered_v;
		battery_status->scale = _scale;
		battery_status->current_a = current_a;
		battery_status->current_filtered_a = _current_filtered_a;
		battery_status->discharged_mah = _discharged_mah;
		battery_status->warning = _warning;
		battery_status->remaining = _remaining;
		battery_status->connected = connected;
		battery_status->system_source = selected_source;
		battery_status->priority = priority;
	}

	battery_status->temperature = NAN;
}

void
Battery::filterVoltage(float voltage_v)
{
	if (!_battery_initialized) {
		_voltage_filtered_v = voltage_v;
	}

	// TODO: inspect that filter performance
	const float filtered_next = _voltage_filtered_v * 0.99f + voltage_v * 0.01f;

	if (PX4_ISFINITE(filtered_next)) {
		_voltage_filtered_v = filtered_next;
	}
}

void
Battery::filterCurrent(float current_a)
{
	if (!_battery_initialized) {
		_current_filtered_a = current_a;
	}

	// ADC poll is at 100Hz, this will perform a low pass over approx 500ms
	const float filtered_next = _current_filtered_a * 0.98f + current_a * 0.02f;

	if (PX4_ISFINITE(filtered_next)) {
		_current_filtered_a = filtered_next;
	}
}

void Battery::filterThrottle(float throttle)
{
	if (!_battery_initialized) {
		_throttle_filtered = throttle;
	}

	const float filtered_next = _throttle_filtered * 0.99f + throttle * 0.01f;

	if (PX4_ISFINITE(filtered_next)) {
		_throttle_filtered = filtered_next;
	}
}

void
Battery::sumDischarged(hrt_abstime timestamp, float current_a)
{
	// Not a valid measurement
	if (current_a < 0.f) {
		// Because the measurement was invalid we need to stop integration
		// and re-initialize with the next valid measurement
		_last_timestamp = 0;
		return;
	}

	// Ignore first update because we don't know dt.
	if (_last_timestamp != 0) {
		const float dt = (timestamp - _last_timestamp) / 1e6;
		// mAh since last loop: (current[A] * 1000 = [mA]) * (dt[s] / 3600 = [h])
		_discharged_mah_loop = (current_a * 1e3f) * (dt / 3600.f);
		_discharged_mah += _discharged_mah_loop;
	}

	_last_timestamp = timestamp;
}

void
Battery::estimateRemaining(float voltage_v, float current_a, float throttle, bool armed)
{
	// remaining battery capacity based on voltage
	float cell_voltage = voltage_v / _param_bat_n_cells.get();

	// correct battery voltage locally for load drop to avoid estimation fluctuations
	if (_param_bat_r_internal.get() >= 0.f) {
		cell_voltage += _param_bat_r_internal.get() * current_a;

	} else {
		// assume linear relation between throttle and voltage drop
		cell_voltage += throttle * _param_bat_v_load_drop.get();
	}

	_remaining_voltage = math::gradual(cell_voltage, _param_bat_v_empty.get(), _param_bat_v_charged.get(), 0.f, 1.f);

	// choose which quantity we're using for final reporting
	if (_param_bat_capacity.get() > 0.f) {
		// if battery capacity is known, fuse voltage measurement with used capacity
		if (!_battery_initialized) {
			// initialization of the estimation state
			_remaining = _remaining_voltage;

		} else {
			// The lower the voltage the more adjust the estimate with it to avoid deep discharge
			const float weight_v = 3e-4f * (1 - _remaining_voltage);
			_remaining = (1 - weight_v) * _remaining + weight_v * _remaining_voltage;
			// directly apply current capacity slope calculated using current
			_remaining -= _discharged_mah_loop / _param_bat_capacity.get();
			_remaining = math::max(_remaining, 0.f);
		}

	} else {
		// else use voltage
		_remaining = _remaining_voltage;
	}
}

void
Battery::determineWarning(bool connected)
{
	if (connected) {
		// propagate warning state only if the state is higher, otherwise remain in current warning state
		if (_remaining < _param_bat_emergen_thr.get()) {
			_warning = battery_status_s::BATTERY_WARNING_EMERGENCY;

		} else if (_remaining < _param_bat_crit_thr.get()) {
			_warning = battery_status_s::BATTERY_WARNING_CRITICAL;

		} else if (_remaining < _param_bat_low_thr.get()) {
			_warning = battery_status_s::BATTERY_WARNING_LOW;

		} else {
			_warning = battery_status_s::BATTERY_WARNING_NONE;
		}
	}
}

void
Battery::computeScale()
{
	const float voltage_range = (_param_bat_v_charged.get() - _param_bat_v_empty.get());

	// reusing capacity calculation to get single cell voltage before drop
	const float bat_v = _param_bat_v_empty.get() + (voltage_range * _remaining_voltage);

	_scale = _param_bat_v_charged.get() / bat_v;

	if (_scale > 1.3f) { // Allow at most 30% compensation
		_scale = 1.3f;

	} else if (!PX4_ISFINITE(_scale) || _scale < 1.f) { // Shouldn't ever be more than the power at full battery
		_scale = 1.f;
	}
}
<|endoftext|>


<commit_before>#include "Tesselate.hpp"

#include "ExPolygon.hpp"

#include <glu-libtess.h>

namespace Slic3r {

class GluTessWrapper {
public:
    Pointf3s tesselate(const ExPolygon &expoly, double z_, bool flipped_)
    {
        z = z_;
        flipped = flipped_;
        triangles.clear();
        intersection_points.clear();
        std::vector<GLdouble> coords;
        {
            size_t num_coords = expoly.contour.points.size();
            for (const Polygon &poly : expoly.holes)
                num_coords += poly.points.size();
            coords.reserve(num_coords * 3);
        }
        GLUtesselator *tess = gluNewTess(); // create a tessellator
        // register callback functions
#ifndef _GLUfuncptr
    #ifdef _MSC_VER
        typedef void (__stdcall *_GLUfuncptr)(void);
    #else /* _MSC_VER */
        #ifdef GLAPIENTRYP
            typedef void (GLAPIENTRYP _GLUfuncptr)(void);
        #else /* GLAPIENTRYP */
            typedef void (*_GLUfuncptr)(void);
        #endif
    #endif /* _MSC_VER */
#endif /* _GLUfuncptr */
        gluTessCallback(tess, GLU_TESS_BEGIN_DATA,   (_GLUfuncptr)tessBeginCB);
        gluTessCallback(tess, GLU_TESS_END_DATA,     (_GLUfuncptr)tessEndCB);
        gluTessCallback(tess, GLU_TESS_ERROR_DATA,   (_GLUfuncptr)tessErrorCB);
        gluTessCallback(tess, GLU_TESS_VERTEX_DATA,  (_GLUfuncptr)tessVertexCB);
        gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)tessCombineCB);
        gluTessBeginPolygon(tess, (void*)this);
        gluTessBeginContour(tess);
        for (const Point &pt : expoly.contour.points) {
            coords.emplace_back(unscale<double>(pt[0]));
            coords.emplace_back(unscale<double>(pt[1]));
            coords.emplace_back(0.);
            gluTessVertex(tess, &coords[coords.size() - 3], &coords[coords.size() - 3]);
        }
        gluTessEndContour(tess);
        for (const Polygon &poly : expoly.holes) {
            gluTessBeginContour(tess);
            for (const Point &pt : poly.points) {
                coords.emplace_back(unscale<double>(pt[0]));
                coords.emplace_back(unscale<double>(pt[1]));
                coords.emplace_back(0.);
                gluTessVertex(tess, &coords[coords.size() - 3], &coords[coords.size() - 3]);
            }
            gluTessEndContour(tess);
        }
        gluTessEndPolygon(tess);
        gluDeleteTess(tess);
        return std::move(triangles);
    }

    Pointf3s tesselate(const ExPolygons &expolygons, double z_, bool flipped_)
    {
        z = z_;
        flipped = flipped_;
        triangles.clear();
        intersection_points.clear();
        std::vector<GLdouble> coords;
        {
            size_t num_coords = 0;
            for (const ExPolygon &expoly : expolygons) {
                size_t num_coords_this = expoly.contour.points.size();
                for (const Polygon &poly : expoly.holes)
                    num_coords_this += poly.points.size();
                num_coords = std::max(num_coords, num_coords_this);
            }
            coords.assign(num_coords * 3, 0);
        }
        GLUtesselator *tess = gluNewTess(); // create a tessellator
        // register callback functions
#ifndef _GLUfuncptr
    #ifdef _MSC_VER
        typedef void (__stdcall *_GLUfuncptr)(void);
    #else /* _MSC_VER */
        #ifdef GLAPIENTRYP
            typedef void (GLAPIENTRYP _GLUfuncptr)(void);
        #else /* GLAPIENTRYP */
            typedef void (*_GLUfuncptr)(void);
        #endif
    #endif /* _MSC_VER */
#endif /* _GLUfuncptr */
        gluTessCallback(tess, GLU_TESS_BEGIN_DATA,   (_GLUfuncptr)tessBeginCB);
        gluTessCallback(tess, GLU_TESS_END_DATA,     (_GLUfuncptr)tessEndCB);
        gluTessCallback(tess, GLU_TESS_ERROR_DATA,   (_GLUfuncptr)tessErrorCB);
        gluTessCallback(tess, GLU_TESS_VERTEX_DATA,  (_GLUfuncptr)tessVertexCB);
        gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)tessCombineCB);
        for (const ExPolygon &expoly : expolygons) {
            gluTessBeginPolygon(tess, (void*)this);
            gluTessBeginContour(tess);
            size_t idx = 0;
            for (const Point &pt : expoly.contour.points) {
                coords[idx ++] = unscale<double>(pt[0]);
                coords[idx ++] = unscale<double>(pt[1]);
                coords[idx ++] = 0.;
                gluTessVertex(tess, &coords[idx - 3], &coords[idx - 3]);
            }
            gluTessEndContour(tess);
            for (const Polygon &poly : expoly.holes) {
                gluTessBeginContour(tess);
                for (const Point &pt : poly.points) {
                    coords[idx ++] = unscale<double>(pt[0]);
                    coords[idx ++] = unscale<double>(pt[1]);
                    coords[idx ++] = 0.;
                    gluTessVertex(tess, &coords[idx - 3], &coords[idx - 3]);
                }
                gluTessEndContour(tess);
            }
            gluTessEndPolygon(tess);
        }
        gluDeleteTess(tess);
        return std::move(triangles);
    }

private:
    static void tessBeginCB(GLenum which, void *polygonData)        { reinterpret_cast<GluTessWrapper*>(polygonData)->tessBegin(which); }
    static void tessEndCB(void *polygonData)                        { reinterpret_cast<GluTessWrapper*>(polygonData)->tessEnd(); }
    static void tessVertexCB(const GLvoid *data, void *polygonData) { reinterpret_cast<GluTessWrapper*>(polygonData)->tessVertex(data); }
    static void tessCombineCB(const GLdouble newVertex[3], const GLdouble *neighborVertex[4], const GLfloat neighborWeight[4], GLdouble **outData, void *polygonData)
                                                                    { reinterpret_cast<GluTessWrapper*>(polygonData)->tessCombine(newVertex, neighborVertex, neighborWeight, outData); }
    static void tessErrorCB(GLenum errorCode, void *polygonData)    { reinterpret_cast<GluTessWrapper*>(polygonData)->tessError(errorCode); }

    void tessBegin(GLenum which)
    {
        assert(which == GL_TRIANGLES || which == GL_TRIANGLE_FAN || which == GL_TRIANGLE_STRIP);
        if (!(which == GL_TRIANGLES || which == GL_TRIANGLE_FAN || which == GL_TRIANGLE_STRIP))
            printf("Co je to za haluz!?\n");
        primitive_type = which;
        num_points = 0;
    }

    void tessEnd()
    {
        num_points = 0;
    }

    void tessVertex(const GLvoid *data)
    {
        if (data == nullptr)
            return;
        const GLdouble *ptr = (const GLdouble*)data;
        ++ num_points;
        if (num_points == 1) {
            memcpy(pt0, ptr, sizeof(GLdouble) * 3);
        } else if (num_points == 2) {
            memcpy(pt1, ptr, sizeof(GLdouble) * 3);
        } else {
            bool flip = flipped;
            if (primitive_type == GL_TRIANGLE_STRIP && num_points == 4) {
                flip = !flip;
                num_points = 2;
            }
            triangles.emplace_back(pt0[0], pt0[1], z);
            if (flip) {
                triangles.emplace_back(ptr[0], ptr[1], z);
                triangles.emplace_back(pt1[0], pt1[1], z);
            } else {
                triangles.emplace_back(pt1[0], pt1[1], z);
                triangles.emplace_back(ptr[0], ptr[1], z);
            }
            if (primitive_type == GL_TRIANGLE_STRIP) {
                memcpy(pt0, pt1, sizeof(GLdouble) * 3);
                memcpy(pt1, ptr, sizeof(GLdouble) * 3);
            } else if (primitive_type == GL_TRIANGLE_FAN) {
                memcpy(pt1, ptr, sizeof(GLdouble) * 3);
            } else {
                assert(primitive_type == GL_TRIANGLES);
                assert(num_points == 3);
                num_points = 0;
            }
        }
    }

    void tessCombine(const GLdouble newVertex[3], const GLdouble *neighborVertex[4], const GLfloat neighborWeight[4], GLdouble **outData)
    {
        intersection_points.emplace_back(newVertex[0], newVertex[1], newVertex[2]);
        *outData = intersection_points.back().data();
    }

    static void tessError(GLenum errorCode)
    {
//        const GLubyte *errorStr;
//        errorStr = gluErrorString(errorCode);
//        printf("Error: %s\n", (const char*)errorStr);
    }

    GLenum   primitive_type;
    GLdouble pt0[3];
    GLdouble pt1[3];
    int      num_points;
    Pointf3s triangles;
    std::deque<Vec3d> intersection_points;
    double   z;
    bool     flipped;
};

Pointf3s triangulate_expolygons_3df(const ExPolygon &poly, coordf_t z, bool flip)
{
    GluTessWrapper tess;
    return tess.tesselate(poly, z, flip);
}

Pointf3s triangulate_expolygons_3df(const ExPolygons &polys, coordf_t z, bool flip)
{
	GluTessWrapper tess;
    return tess.tesselate(polys, z, flip);
}

} // namespace Slic3r
<commit_msg>Polished up the tesselator.<commit_after>#include "Tesselate.hpp"

#include "ExPolygon.hpp"

#include <glu-libtess.h>

namespace Slic3r {

class GluTessWrapper {
public:
    GluTessWrapper() : m_tesselator(gluNewTess()) {
        // register callback functions
        gluTessCallback(m_tesselator, GLU_TESS_BEGIN_DATA,   (_GLUfuncptr)tessBeginCB);
        gluTessCallback(m_tesselator, GLU_TESS_END_DATA,     (_GLUfuncptr)tessEndCB);
        gluTessCallback(m_tesselator, GLU_TESS_ERROR_DATA,   (_GLUfuncptr)tessErrorCB);
        gluTessCallback(m_tesselator, GLU_TESS_VERTEX_DATA,  (_GLUfuncptr)tessVertexCB);
        gluTessCallback(m_tesselator, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)tessCombineCB);
    }
    ~GluTessWrapper() {
        gluDeleteTess(m_tesselator);
    }

    Pointf3s tesselate(const ExPolygon &expoly, double z_, bool flipped_)
    {
        m_z = z_;
        m_flipped = flipped_;
        std::vector<GLdouble> coords;
        {
            size_t num_coords = expoly.contour.points.size();
            for (const Polygon &poly : expoly.holes)
                num_coords += poly.points.size();
            coords.reserve(num_coords * 3);
        }
        gluTessBeginPolygon(m_tesselator, (void*)this);
        gluTessBeginContour(m_tesselator);
        for (const Point &pt : expoly.contour.points) {
            coords.emplace_back(unscale<double>(pt[0]));
            coords.emplace_back(unscale<double>(pt[1]));
            coords.emplace_back(0.);
            gluTessVertex(m_tesselator, &coords[coords.size() - 3], &coords[coords.size() - 3]);
        }
        gluTessEndContour(m_tesselator);
        for (const Polygon &poly : expoly.holes) {
            gluTessBeginContour(m_tesselator);
            for (const Point &pt : poly.points) {
                coords.emplace_back(unscale<double>(pt[0]));
                coords.emplace_back(unscale<double>(pt[1]));
                coords.emplace_back(0.);
                gluTessVertex(m_tesselator, &coords[coords.size() - 3], &coords[coords.size() - 3]);
            }
            gluTessEndContour(m_tesselator);
        }
        gluTessEndPolygon(m_tesselator);
        m_output_triangles.clear();
        m_intersection_points.clear();
        return std::move(m_output_triangles);
    }

    Pointf3s tesselate(const ExPolygons &expolygons, double z_, bool flipped_)
    {
        m_z = z_;
        m_flipped = flipped_;
        std::vector<GLdouble> coords;
        {
            size_t num_coords = 0;
            for (const ExPolygon &expoly : expolygons) {
                size_t num_coords_this = expoly.contour.points.size();
                for (const Polygon &poly : expoly.holes)
                    num_coords_this += poly.points.size();
                num_coords = std::max(num_coords, num_coords_this);
            }
            coords.assign(num_coords * 3, 0);
        }
        for (const ExPolygon &expoly : expolygons) {
            gluTessBeginPolygon(m_tesselator, (void*)this);
            gluTessBeginContour(m_tesselator);
            size_t idx = 0;
            for (const Point &pt : expoly.contour.points) {
                coords[idx ++] = unscale<double>(pt[0]);
                coords[idx ++] = unscale<double>(pt[1]);
                coords[idx ++] = 0.;
                gluTessVertex(m_tesselator, &coords[idx - 3], &coords[idx - 3]);
            }
            gluTessEndContour(m_tesselator);
            for (const Polygon &poly : expoly.holes) {
                gluTessBeginContour(m_tesselator);
                for (const Point &pt : poly.points) {
                    coords[idx ++] = unscale<double>(pt[0]);
                    coords[idx ++] = unscale<double>(pt[1]);
                    coords[idx ++] = 0.;
                    gluTessVertex(m_tesselator, &coords[idx - 3], &coords[idx - 3]);
                }
                gluTessEndContour(m_tesselator);
            }
            gluTessEndPolygon(m_tesselator);
        }
        m_output_triangles.clear();
        m_intersection_points.clear();
        return std::move(m_output_triangles);
    }

private:
    static void tessBeginCB(GLenum which, void *polygonData)        { reinterpret_cast<GluTessWrapper*>(polygonData)->tessBegin(which); }
    static void tessEndCB(void *polygonData)                        { reinterpret_cast<GluTessWrapper*>(polygonData)->tessEnd(); }
    static void tessVertexCB(const GLvoid *data, void *polygonData) { reinterpret_cast<GluTessWrapper*>(polygonData)->tessVertex(data); }
    static void tessCombineCB(const GLdouble newVertex[3], const GLdouble *neighborVertex[4], const GLfloat neighborWeight[4], GLdouble **outData, void *polygonData)
                                                                    { reinterpret_cast<GluTessWrapper*>(polygonData)->tessCombine(newVertex, neighborVertex, neighborWeight, outData); }
    static void tessErrorCB(GLenum errorCode, void *polygonData)    { reinterpret_cast<GluTessWrapper*>(polygonData)->tessError(errorCode); }

    void tessBegin(GLenum which)
    {
        assert(which == GL_TRIANGLES || which == GL_TRIANGLE_FAN || which == GL_TRIANGLE_STRIP);
        m_primitive_type = which;
        m_num_points = 0;
    }

    void tessEnd()
    {
        m_num_points = 0;
    }

    void tessVertex(const GLvoid *data)
    {
        if (data == nullptr)
            return;
        const GLdouble *ptr = (const GLdouble*)data;
        ++ m_num_points;
        if (m_num_points == 1) {
            memcpy(m_pt0, ptr, sizeof(GLdouble) * 3);
        } else if (m_num_points == 2) {
            memcpy(m_pt1, ptr, sizeof(GLdouble) * 3);
        } else {
            bool flip = m_flipped;
            if (m_primitive_type == GL_TRIANGLE_STRIP && m_num_points == 4) {
                flip = !flip;
                m_num_points = 2;
            }
            m_output_triangles.emplace_back(m_pt0[0], m_pt0[1], m_z);
            if (flip) {
                m_output_triangles.emplace_back(ptr[0],   ptr[1],   m_z);
                m_output_triangles.emplace_back(m_pt1[0], m_pt1[1], m_z);
            } else {
                m_output_triangles.emplace_back(m_pt1[0], m_pt1[1], m_z);
                m_output_triangles.emplace_back(ptr[0],   ptr[1],   m_z);
            }
            if (m_primitive_type == GL_TRIANGLE_STRIP) {
                memcpy(m_pt0, m_pt1, sizeof(GLdouble) * 3);
                memcpy(m_pt1, ptr,   sizeof(GLdouble) * 3);
            } else if (m_primitive_type == GL_TRIANGLE_FAN) {
                memcpy(m_pt1, ptr,   sizeof(GLdouble) * 3);
            } else {
                assert(m_primitive_type == GL_TRIANGLES);
                assert(m_num_points == 3);
                m_num_points = 0;
            }
        }
    }

    void tessCombine(const GLdouble newVertex[3], const GLdouble *neighborVertex[4], const GLfloat neighborWeight[4], GLdouble **outData)
    {
        m_intersection_points.emplace_back(newVertex[0], newVertex[1], m_z);
        *outData = m_intersection_points.back().data();
    }

    static void tessError(GLenum errorCode)
    {
//        const GLubyte *errorStr;
//        errorStr = gluErrorString(errorCode);
//        printf("Error: %s\n", (const char*)errorStr);
    }

    // Instance owned over the life time of this wrapper.
    GLUtesselator  *m_tesselator;

    // Currently processed primitive type.
    GLenum          m_primitive_type;
    // Two last vertices received for m_primitive_type. Used for processing triangle strips, fans etc.
    GLdouble        m_pt0[3];
    GLdouble        m_pt1[3];
    // Number of points processed over m_primitive_type.
    int             m_num_points;
    // Triangles generated by the tesselator.
    Pointf3s        m_output_triangles;
    // Intersection points generated by tessCombine callback. There should be none if the input contour is not self intersecting.
    std::deque<Vec3d> m_intersection_points;
    // Fixed third coordinate.
    double          m_z;
    // Output triangles shall be flipped (normal points down).
    bool            m_flipped;
};

Pointf3s triangulate_expolygons_3df(const ExPolygon &poly, coordf_t z, bool flip)
{
    GluTessWrapper tess;
    return tess.tesselate(poly, z, flip);
}

Pointf3s triangulate_expolygons_3df(const ExPolygons &polys, coordf_t z, bool flip)
{
	GluTessWrapper tess;
    return tess.tesselate(polys, z, flip);
}

} // namespace Slic3r
<|endoftext|>


<commit_before>#include <stdint.h>
#include <kernel/itoa.h>
#include <kernel/screen.h>
#include <kernel/ata.h>
#include <kernel/pager.h>
#include <arch/x86/a20.h>

enum class ELFVersion : uint8_t {
	None = 0,
	ELF1 = 1
};

enum class ELFClass : uint8_t {
	None = 0,
	x32  = 1,
	x64  = 2
};

enum class ELFEncoding : uint8_t {
	None         = 0,
	LittleEndian = 1,
	BigEndian    = 2
};

enum class ELFABI : uint8_t {
	SystemV = 0x00,
	Linux   = 0x03,
	SHK     = 0x7F
};

enum class ELFType : uint16_t {
	None        = 0,
	Relocatable = 1,
	Executable  = 2,
	Dynamic     = 3,
	Core        = 4,
	LowProc     = 0xFF00,
	HighProc    = 0xFFFF
};

enum class ELFMachine : uint16_t {
	None     = 0,
	Intel386 = 3
};

struct ELFIdent {
	uint8_t     magic[4];
	ELFClass    class_;
	ELFEncoding encoding;
	ELFVersion  version;
	ELFABI      abi;
	uint8_t     abi_version;
	uint8_t     padding[7];
} __attribute__((packed));

struct ELFHeader {
	ELFIdent   ident;
	ELFType    type;
	ELFMachine machine;
	uint32_t   version;   // we can just use ELFIdent.version which is a uint8_t
	uint32_t   entry_ptr;
	uint32_t   ph_offset; // program header offset
	uint32_t   sh_offset; // section header offset
	uint32_t   flags;
	uint16_t   elf_header_size;
	uint16_t   ph_size;
	uint16_t   ph_count;
	uint16_t   sh_size;
	uint16_t   sh_count;
	uint16_t   sh_str_idx;
} __attribute__((packed));

struct ELFProgramHeader {
	uint32_t type;
	uint32_t offset;
	uint32_t v_addr;
	uint32_t p_addr;
	uint32_t file_size;
	uint32_t mem_size;
	uint32_t flags;
	uint32_t align;
} __attribute__((packed));

#define SCREEN_CASE(X)       case X:  return screen << #X
#define SCREEN_DEFAULT(X, Y) default: return screen << #X << "::<INVALID> (" << static_cast<uint32_t>(Y) << ')'

class Screen {
public:
	Screen(void) {
		screen_init();
	}

	friend Screen & operator<<(Screen &screen, const char c) {
		screen_put(c);
		return screen;
	}

	friend Screen & operator<<(Screen &screen, const uint8_t i) {
		char sz[4] = {0};
		itoa(static_cast<int>(i), sz, 16);
		return screen << "0x" << sz;
	}

	friend Screen & operator<<(Screen &screen, const uint16_t i) {
		char sz[8] = {0};
		itoa(static_cast<int>(i), sz, 16);
		return screen << "0x" << sz;
	}

	friend Screen & operator<<(Screen &screen, const uint32_t i) {
		char sz[16] = {0};
		itoa(static_cast<int>(i), sz, 16);
		return screen << "0x" << sz;
	}

	friend Screen & operator<<(Screen &screen, const char *sz) {
		screen_print(sz);
		return screen;
	}

	friend Screen & operator<<(Screen &screen, const ELFClass class_) {
		switch(class_) {
			SCREEN_CASE(ELFClass::None);
			SCREEN_CASE(ELFClass::x32);
			SCREEN_CASE(ELFClass::x64);
			SCREEN_DEFAULT(ELFClass, class_);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFEncoding encoding) {
		switch(encoding) {
			SCREEN_CASE(ELFEncoding::None);
			SCREEN_CASE(ELFEncoding::LittleEndian);
			SCREEN_CASE(ELFEncoding::BigEndian);
			SCREEN_DEFAULT(ELFEncoding, encoding);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFVersion version) {
		switch(version) {
			SCREEN_CASE(ELFVersion::None);
			SCREEN_CASE(ELFVersion::ELF1);
			SCREEN_DEFAULT(ELFVersion, version);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFABI abi) {
		switch(abi) {
			SCREEN_CASE(ELFABI::SystemV);
			SCREEN_CASE(ELFABI::Linux);
			SCREEN_CASE(ELFABI::SHK);
			SCREEN_DEFAULT(ELFABI, abi);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFType type) {
		switch(type) {
			SCREEN_CASE(ELFType::None);
			SCREEN_CASE(ELFType::Relocatable);
			SCREEN_CASE(ELFType::Executable);
			SCREEN_CASE(ELFType::Dynamic);
			SCREEN_CASE(ELFType::Core);
			SCREEN_CASE(ELFType::LowProc);
			SCREEN_CASE(ELFType::HighProc);
			SCREEN_DEFAULT(ELFType, type);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFMachine machine) {
		switch(machine) {
			SCREEN_CASE(ELFMachine::None);
			SCREEN_CASE(ELFMachine::Intel386);
			SCREEN_DEFAULT(ELFMachine, machine);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFIdent &ident) {
		screen << "ELFIdent::magic       = " << static_cast<char>(ident.magic[0])
		                                     << static_cast<char>(ident.magic[1])
		                                     << static_cast<char>(ident.magic[2])
		                                     << static_cast<char>(ident.magic[3])
		                                                          << '\n';
		screen << "ELFIdent::class       = " << ident.class_      << '\n';
		screen << "ELFIdent::encoding    = " << ident.encoding    << '\n';
		screen << "ELFIdent::version     = " << ident.version     << '\n';
		screen << "ELFIdent::abi         = " << ident.abi         << '\n';
		screen << "ELFIdent::abi_version = " << ident.abi_version << '\n';
		return screen;
	}

	friend Screen & operator<<(Screen &screen, const ELFHeader &header) {
		screen << header.ident;
		screen << "ELFHeader::type       = " << header.type       << '\n';
		screen << "ELFHeader::machine    = " << header.machine    << '\n';
		//screen << "ELFHeader::version    = " << header.version    << '\n';
		screen << "ELFHeader::entry_ptr  = " << header.entry_ptr  << '\n';
		screen << "ELFHeader::ph_offset  = " << header.ph_offset  << '\n';
		screen << "ELFHeader::sh_offset  = " << header.sh_offset  << '\n';
		screen << "ELFHeader::flags      = " << header.flags      << '\n';
		screen << "ELFHeader::ph_size    = " << header.ph_size    << '\n';
		screen << "ELFHeader::ph_count   = " << header.ph_count   << '\n';
		screen << "ELFHeader::sh_size    = " << header.sh_size    << '\n';
		screen << "ELFHeader::sh_count   = " << header.sh_count   << '\n';
		screen << "ELFHeader::sh_str_idx = " << header.sh_str_idx << '\n';
		return screen;
	}

	friend Screen & operator<<(Screen &screen, const ELFProgramHeader &ph) {
		screen << "ELFProgramHeader::type      = " << ph.type      << '\n';
		screen << "ELFProgramHeader::offset    = " << ph.offset    << '\n';
		screen << "ELFProgramHeader::v_addr    = " << ph.v_addr    << '\n';
		screen << "ELFProgramHeader::p_addr    = " << ph.p_addr    << '\n';
		screen << "ELFProgramHeader::file_size = " << ph.file_size << '\n';
		screen << "ELFProgramHeader::mem_size  = " << ph.mem_size  << '\n';
		screen << "ELFProgramHeader::flags     = " << ph.flags     << '\n';
		screen << "ELFProgramHeader::align     = " << ph.align     << '\n';
		return screen;
	}
};

extern "C" void loader_entry(void) __attribute__((noreturn));
void loader_entry(void) {
	a20_enable();

	struct Pager *pager = pager_init();
	pager_reload(pager);
	pager_enable();

	ata_init();
	Screen screen;

	ELFHeader header;
	ata_pio_read(17, 1, &header);
	screen << header;

	uint8_t ph_sector[512 * 2];
	ata_pio_read(17 + header.ph_offset / 512, 2, ph_sector);
	ELFProgramHeader *ph = reinterpret_cast<ELFProgramHeader *>(
		&ph_sector[header.ph_offset % 512]
	);
	screen << *ph;

	for(;;) { __asm__ ("hlt"); }
}
<commit_msg>Tidy up and add ELFProgramType enum<commit_after>#include <stdint.h>
#include <kernel/itoa.h>
#include <kernel/screen.h>
#include <kernel/ata.h>
#include <kernel/pager.h>
#include <arch/x86/a20.h>

enum class ELFIdentVersion : uint8_t {
	None = 0,
	ELF1 = 1
};

enum class ELFIdentClass : uint8_t {
	None = 0,
	x32  = 1,
	x64  = 2
};

enum class ELFIdentEncoding : uint8_t {
	None         = 0,
	LittleEndian = 1,
	BigEndian    = 2
};

enum class ELFIdentABI : uint8_t {
	SystemV = 0x00,
	Linux   = 0x03,
	SHK     = 0x7F
};

enum class ELFType : uint16_t {
	None        = 0,
	Relocatable = 1,
	Executable  = 2,
	Dynamic     = 3,
	Core        = 4,
};

enum class ELFMachine : uint16_t {
	None     = 0,
	Intel386 = 3
};

struct ELFIdent {
	uint8_t          magic[4];
	ELFIdentClass    class_;
	ELFIdentEncoding encoding;
	ELFIdentVersion  version;
	ELFIdentABI      abi;
	uint8_t          abi_version;
	uint8_t          padding[7];
} __attribute__((packed));

struct ELFHeader {
	ELFIdent   ident;
	ELFType    type;
	ELFMachine machine;
	uint32_t   version;   // we can just use ELFIdent.version which is a uint8_t
	uint32_t   entry_ptr;
	uint32_t   ph_offset; // program header offset
	uint32_t   sh_offset; // section header offset
	uint32_t   flags;
	uint16_t   elf_header_size;
	uint16_t   ph_size;
	uint16_t   ph_count;
	uint16_t   sh_size;
	uint16_t   sh_count;
	uint16_t   sh_str_idx;
} __attribute__((packed));

enum class ELFProgramType : uint32_t {
	Null    = 0,
	Load    = 1,
	Dynamic = 2,
	Interp  = 3,
	Note    = 4,
	Shared  = 5,
	Header  = 6
};

struct ELFProgramHeader {
	ELFProgramType type;
	uint32_t offset;
	uint32_t v_addr;
	uint32_t p_addr;
	uint32_t file_size;
	uint32_t mem_size;
	uint32_t flags;
	uint32_t align;
} __attribute__((packed));

#define SCREEN_CASE(X)       case X:  return screen << #X
#define SCREEN_DEFAULT(X, Y) default: return screen << #X << "::<INVALID> (" << static_cast<uint32_t>(Y) << ')'

class Screen {
public:
	Screen(void) {
		screen_init();
	}

	friend Screen & operator<<(Screen &screen, const char c) {
		screen_put(c);
		return screen;
	}

	friend Screen & operator<<(Screen &screen, const uint8_t i) {
		char sz[4] = {0};
		itoa(static_cast<int>(i), sz, 16);
		return screen << "0x" << sz;
	}

	friend Screen & operator<<(Screen &screen, const uint16_t i) {
		char sz[8] = {0};
		itoa(static_cast<int>(i), sz, 16);
		return screen << "0x" << sz;
	}

	friend Screen & operator<<(Screen &screen, const uint32_t i) {
		char sz[16] = {0};
		itoa(static_cast<int>(i), sz, 16);
		return screen << "0x" << sz;
	}

	friend Screen & operator<<(Screen &screen, const char *sz) {
		screen_print(sz);
		return screen;
	}

	friend Screen & operator<<(Screen &screen, const ELFIdentClass class_) {
		switch(class_) {
			SCREEN_CASE   (ELFIdentClass::None);
			SCREEN_CASE   (ELFIdentClass::x32);
			SCREEN_CASE   (ELFIdentClass::x64);
			SCREEN_DEFAULT(ELFIdentClass, class_);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFIdentEncoding encoding) {
		switch(encoding) {
			SCREEN_CASE   (ELFIdentEncoding::None);
			SCREEN_CASE   (ELFIdentEncoding::LittleEndian);
			SCREEN_CASE   (ELFIdentEncoding::BigEndian);
			SCREEN_DEFAULT(ELFIdentEncoding, encoding);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFIdentVersion version) {
		switch(version) {
			SCREEN_CASE   (ELFIdentVersion::None);
			SCREEN_CASE   (ELFIdentVersion::ELF1);
			SCREEN_DEFAULT(ELFIdentVersion, version);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFIdentABI abi) {
		switch(abi) {
			SCREEN_CASE   (ELFIdentABI::SystemV);
			SCREEN_CASE   (ELFIdentABI::Linux);
			SCREEN_CASE   (ELFIdentABI::SHK);
			SCREEN_DEFAULT(ELFIdentABI, abi);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFType type) {
		switch(type) {
			SCREEN_CASE   (ELFType::None);
			SCREEN_CASE   (ELFType::Relocatable);
			SCREEN_CASE   (ELFType::Executable);
			SCREEN_CASE   (ELFType::Dynamic);
			SCREEN_CASE   (ELFType::Core);
			SCREEN_DEFAULT(ELFType, type);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFMachine machine) {
		switch(machine) {
			SCREEN_CASE   (ELFMachine::None);
			SCREEN_CASE   (ELFMachine::Intel386);
			SCREEN_DEFAULT(ELFMachine, machine);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFIdent &ident) {
		screen << "ELFIdent::magic       = " << static_cast<char>(ident.magic[0])
		                                     << static_cast<char>(ident.magic[1])
		                                     << static_cast<char>(ident.magic[2])
		                                     << static_cast<char>(ident.magic[3])
		                                                          << '\n';
		screen << "ELFIdent::class       = " << ident.class_      << '\n';
		screen << "ELFIdent::encoding    = " << ident.encoding    << '\n';
		screen << "ELFIdent::version     = " << ident.version     << '\n';
		screen << "ELFIdent::abi         = " << ident.abi         << '\n';
		screen << "ELFIdent::abi_version = " << ident.abi_version << '\n';
		return screen;
	}

	friend Screen & operator<<(Screen &screen, const ELFHeader &header) {
		screen << header.ident;
		screen << "ELFHeader::type       = " << header.type       << '\n';
		screen << "ELFHeader::machine    = " << header.machine    << '\n';
		//screen << "ELFHeader::version    = " << header.version    << '\n';
		screen << "ELFHeader::entry_ptr  = " << header.entry_ptr  << '\n';
		screen << "ELFHeader::ph_offset  = " << header.ph_offset  << '\n';
		screen << "ELFHeader::sh_offset  = " << header.sh_offset  << '\n';
		screen << "ELFHeader::flags      = " << header.flags      << '\n';
		screen << "ELFHeader::ph_size    = " << header.ph_size    << '\n';
		screen << "ELFHeader::ph_count   = " << header.ph_count   << '\n';
		screen << "ELFHeader::sh_size    = " << header.sh_size    << '\n';
		screen << "ELFHeader::sh_count   = " << header.sh_count   << '\n';
		screen << "ELFHeader::sh_str_idx = " << header.sh_str_idx << '\n';
		return screen;
	}

	friend Screen & operator<<(Screen &screen, const ELFProgramType type) {
		switch(type) {
			SCREEN_CASE   (ELFProgramType::Null);
			SCREEN_CASE   (ELFProgramType::Load);
			SCREEN_CASE   (ELFProgramType::Dynamic);
			SCREEN_CASE   (ELFProgramType::Interp);
			SCREEN_CASE   (ELFProgramType::Note);
			SCREEN_CASE   (ELFProgramType::Shared);
			SCREEN_CASE   (ELFProgramType::Header);
			SCREEN_DEFAULT(ELFProgramType, type);
		}
	}

	friend Screen & operator<<(Screen &screen, const ELFProgramHeader &ph) {
		screen << "ELFProgramHeader::type      = " << ph.type      << '\n';
		screen << "ELFProgramHeader::offset    = " << ph.offset    << '\n';
		screen << "ELFProgramHeader::v_addr    = " << ph.v_addr    << '\n';
		screen << "ELFProgramHeader::p_addr    = " << ph.p_addr    << '\n';
		screen << "ELFProgramHeader::file_size = " << ph.file_size << '\n';
		screen << "ELFProgramHeader::mem_size  = " << ph.mem_size  << '\n';
		screen << "ELFProgramHeader::flags     = " << ph.flags     << '\n';
		screen << "ELFProgramHeader::align     = " << ph.align     << '\n';
		return screen;
	}
};

extern "C" void loader_entry(void) __attribute__((noreturn));
void loader_entry(void) {
	a20_enable();

	struct Pager *pager = pager_init();
	pager_reload(pager);
	pager_enable();

	ata_init();
	Screen screen;

	ELFHeader header;
	ata_pio_read(17, 1, &header);
	//screen << header;

	uint8_t ph_sector[512 * 2];
	ata_pio_read(17 + header.ph_offset / 512, 2, ph_sector);
	ELFProgramHeader *ph = reinterpret_cast<ELFProgramHeader *>(
		&ph_sector[header.ph_offset % 512]
	);
	screen << *ph;

	for(;;) { __asm__ ("hlt"); }
}
<|endoftext|>


<commit_before>#include <ncurses.h>
//#include <string.h>

#include "display/Window.h"

//class Window::WIN_PTR
//{
//	public:
//		WINDOW* getPtr() { return _win; }
//		void setPtr(WINDOW* win) { _win = win; }
//	private:
//		WINDOW* _win;
//};

Window::Window(uint32_t width, uint32_t height)
{
	_width = width;
	_height = height;

	_x = 0;
	_y = 0;

	//_win->setPtr(newwin(_height, _width, _y, _x));
	_win = newwin(_height, _width, _y, _x);
}

Window::Window(uint32_t width, uint32_t height, uint32_t x, uint32_t y)
{
	_width = width;
	_height = height;

	_x = x;
	_y = y;

	_win = newwin(_height, _width, _y, _x);
}

uint32_t Window::getWidth()
{
	return _width;
}

uint32_t Window::getHeight()
{
	return _height;
}

uint32_t Window::getX()
{
	return _x;
}

uint32_t Window::getY()
{
	return _y;
}

void Window::refresh()
{
	wrefresh(_win);
}

void Window::add(uint32_t x, uint32_t y, char c)
{
	/// @todo Add bounds-checking
	mvwaddch(_win, y, x, c);
}

void Window::add(uint32_t x, uint32_t y, std::string str)
{
	/// @todo Add bounds-checking
	mvwprintw(_win, y, x, str.c_str());
}

void Window::erase(uint32_t x, uint32_t y)
{
	/// @todo Is there a "proper" erase?
	add(y, x, ' ');
}

<commit_msg>Clean up and document Window implementation.<commit_after>#include <ncurses.h>

#include "display/Window.h"

Window::Window(uint32_t width, uint32_t height)
{
	//Stash width and height for later reference.
	_width = width;
	_height = height;

	//Set x and y to 0.
	_x = 0;
	_y = 0;

	//Create the window and store its pointer.
	_win = newwin(_height, _width, _y, _x);
}

Window::Window(uint32_t width, uint32_t height, uint32_t x, uint32_t y)
{
	//Stash width and height for later reference.
	_width = width;
	_height = height;

	//Set x and y to 0.
	_x = x;
	_y = y;

	//Create the window and store its pointer.
	_win = newwin(_height, _width, _y, _x);
}

uint32_t Window::getWidth()
{
	return _width;
}

uint32_t Window::getHeight()
{
	return _height;
}

uint32_t Window::getX()
{
	return _x;
}

uint32_t Window::getY()
{
	return _y;
}

void Window::refresh()
{
	wrefresh(_win);
}

void Window::add(uint32_t x, uint32_t y, char c)
{
	if(x >= 0 && x < _width && y >= 0 && y < _height)
	{
		mvwaddch(_win, y, x, c);
	}
}

void Window::add(uint32_t x, uint32_t y, std::string str)
{
	if(x >= 0 && x < _width && y >= 0 && y < _height)
	{
		mvwprintw(_win, y, x, str.c_str());
	}
}

void Window::erase(uint32_t x, uint32_t y)
{
	if(x >= 0 && x < _width && y >= 0 && y < _height)
	{
		/// @todo Is there a "proper" erase?
		add(y, x, ' ');
	}
}

<|endoftext|>


<commit_before>/*
    This tutorial shows how to write a simple matrix multiplication in gpu (C = A * B)

    for i = 0 .. N
        for j = 0 .. N
            C[i,j] = 0;
            for k = 0 .. N
                C[i,j] = C[i,j] + A[i,k] * B[k,j];

     To run this tutorial

     cd build/
     make run_developers_tutorial_04gpu

*/

#include <tiramisu/tiramisu.h>

#define SIZE0 1000
using namespace tiramisu;

int main(int argc, char **argv)
{
    // Set default tiramisu options.
    tiramisu::init();

    // -------------------------------------------------------
    // Layer I
    // -------------------------------------------------------

    /*
     * Declare a function matmul.
     */
    function matmul("matmul");

    constant p0("N", expr((int32_t) SIZE0), p_int32, true, NULL, 0, &matmul);

    // Declare loop iterators
    var i("i"), j("j"), k("k");

    // Declare cpu buffers.
    buffer b_A("b_A", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_input, &matmul);
    buffer b_B("b_B", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_input, &matmul);
    buffer b_C("b_C", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_output, &matmul);
    // Declare gpu buffers.
    buffer b_A_gpu("b_A_gpu", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_temporary, &matmul);
    buffer b_B_gpu("b_B_gpu", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_temporary, &matmul);
    buffer b_C_gpu("b_C_gpu", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_temporary, &matmul);
    b_A_gpu.tag_gpu_global();
    b_B_gpu.tag_gpu_global();
    b_C_gpu.tag_gpu_global();

    // Declare computations that represents the input buffers (b_A and b_B)
    computation c_A("[N]->{c_A[i,j]: 0<=i<N and 0<=j<N}", expr(), false, p_uint8, &matmul);
    computation c_B("[N]->{c_B[i,j]: 0<=i<N and 0<=j<N}", expr(), false, p_uint8, &matmul);

    // Declare a computation to initialize the reduction c[i,j]
    computation C_init("[N]->{C_init[i,j,-1]: 0<=i<N and 0<=j<N}", expr((uint8_t) 0), true, p_uint8, &matmul);

    // Declare the reduction operation.
    computation c_C("[N]->{c_C[i,j,k]: 0<=i<N and 0<=j<N and 0<=k<N}", expr(), true, p_uint8, &matmul);
    // Note that the previous computation has an empty expression (because we can only use c_C in an expression after its declaration)
    c_C.set_expression(c_C(i, j, k - 1) + c_A(i, k) * c_B(k, j));

    // Declare host-gpu transfer computations.
    computation copy_A_to_device("{copy_A_to_device[0]}", memcpy(b_A, b_A_gpu), true, p_none, &matmul);
    computation copy_B_to_device("{copy_B_to_device[0]}", memcpy(b_B, b_B_gpu), true, p_none, &matmul);
    computation copy_C_to_device("{copy_C_to_device[0]}", memcpy(b_C, b_C_gpu), true, p_none, &matmul);
    computation copy_A_to_host("{copy_A_to_host[0]}", memcpy(b_A_gpu, b_A), true, p_none, &matmul);
    computation copy_B_to_host("{copy_B_to_host[0]}", memcpy(b_B_gpu, b_B), true, p_none, &matmul);
    computation copy_C_to_host("{copy_C_to_host[0]}", memcpy(b_C_gpu, b_C), true, p_none, &matmul);

    // -------------------------------------------------------
    // Layer II
    // -------------------------------------------------------

    // Scheduling commands
    // TODO: optimizations
    copy_B_to_device.after(copy_A_to_device, computation::root);
    copy_C_to_device.after(copy_B_to_device, computation::root);
    C_init.after(copy_C_to_device, computation::root);
    c_C.after(C_init, computation::root);
    copy_A_to_host.after(c_C, computation::root);
    copy_B_to_host.after(copy_A_to_host, computation::root);
    copy_C_to_host.after(copy_B_to_host, computation::root);

    // TODO: Optimizations
    C_init.gpu_tile(i, j, 16, 16);
    c_C.gpu_tile(i, j, 16, 16);

    // -------------------------------------------------------
    // Layer III
    // -------------------------------------------------------

    // Map the computations to a buffer.
    c_A.store_in(&b_A_gpu);
    c_B.store_in(&b_B_gpu);

    // Store C_init[i,j,k] in b_C[i,j]
    C_init.store_in(&b_C_gpu, {i,j});
    // Store c_C[i,j,k] in b_C[i,j]
    c_C.store_in(&b_C_gpu, {i,j});
    // Note that both of the computations C_init and c_C store their
    // results in the buffer b_C.

    // -------------------------------------------------------
    // Code Generation
    // -------------------------------------------------------

    // Generate object files. Last argument triggers cuda compilation.
    matmul.codegen({&b_A, &b_B, &b_C}, "build/generated_fct_developers_tutorial_04gpu.o", true);

    // Dump the generated Halide statement (just for debugging).
    matmul.dump_halide_stmt();

    return 0;
}
<commit_msg>GPU tutorial fix<commit_after>/*
    This tutorial shows how to write a simple matrix multiplication in gpu (C = A * B)

    for i = 0 .. N
        for j = 0 .. N
            C[i,j] = 0;
            for k = 0 .. N
                C[i,j] = C[i,j] + A[i,k] * B[k,j];

     To run this tutorial

     cd build/
     make run_developers_tutorial_04gpu

*/

#include <tiramisu/tiramisu.h>

#define SIZE0 1000
using namespace tiramisu;

int main(int argc, char **argv)
{
    // Set default tiramisu options.
    tiramisu::init();

    // -------------------------------------------------------
    // Layer I
    // -------------------------------------------------------

    /*
     * Declare a function matmul.
     */
    function matmul("matmul");

    constant p0("N", expr((int32_t) SIZE0), p_int32, true, NULL, 0, &matmul);

    // Declare loop iterators
    var i("i"), j("j"), k("k");

    // Declare cpu buffers.
    buffer b_A("b_A", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_input, &matmul);
    buffer b_B("b_B", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_input, &matmul);
    buffer b_C("b_C", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_output, &matmul);
    // Declare gpu buffers.
    buffer b_A_gpu("b_A_gpu", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_temporary, &matmul);
    buffer b_B_gpu("b_B_gpu", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_temporary, &matmul);
    buffer b_C_gpu("b_C_gpu", {expr(SIZE0), expr(SIZE0)}, p_uint8, a_temporary, &matmul);
    b_A_gpu.tag_gpu_global();
    b_B_gpu.tag_gpu_global();
    b_C_gpu.tag_gpu_global();

    // Declare computations that represents the input buffers (b_A and b_B)
    computation c_A("[N]->{c_A[i,j]: 0<=i<N and 0<=j<N}", expr(), false, p_uint8, &matmul);
    computation c_B("[N]->{c_B[i,j]: 0<=i<N and 0<=j<N}", expr(), false, p_uint8, &matmul);

    // Declare a computation to initialize the reduction c[i,j]
    computation C_init("[N]->{C_init[i,j,-1]: 0<=i<N and 0<=j<N}", expr((uint8_t) 0), true, p_uint8, &matmul);

    // Declare the reduction operation.
    computation c_C("[N]->{c_C[i,j,k]: 0<=i<N and 0<=j<N and 0<=k<N}", expr(), true, p_uint8, &matmul);
    // Note that the previous computation has an empty expression (because we can only use c_C in an expression after its declaration)
    c_C.set_expression(c_C(i, j, k - 1) + c_A(i, k) * c_B(k, j));

    // Declare host-gpu transfer computations.
    computation copy_A_to_device("{copy_A_to_device[0]}", memcpy(b_A, b_A_gpu), true, p_none, &matmul);
    computation copy_B_to_device("{copy_B_to_device[0]}", memcpy(b_B, b_B_gpu), true, p_none, &matmul);
    computation copy_C_to_host("{copy_C_to_host[0]}", memcpy(b_C_gpu, b_C), true, p_none, &matmul);

    // -------------------------------------------------------
    // Layer II
    // -------------------------------------------------------

    // Scheduling commands
    copy_B_to_device.after(copy_A_to_device, computation::root);
    C_init.after(copy_B_to_device, computation::root);
    c_C.after(C_init, computation::root);
    copy_C_to_host.after(c_C, computation::root);

    // A simple tiling.
    C_init.gpu_tile(i, j, 16, 16);
    c_C.gpu_tile(i, j, 16, 16);

    // -------------------------------------------------------
    // Layer III
    // -------------------------------------------------------

    // Map the computations to a buffer.
    c_A.store_in(&b_A_gpu);
    c_B.store_in(&b_B_gpu);

    // Store C_init[i,j,k] in b_C[i,j]
    C_init.store_in(&b_C_gpu, {i,j});
    // Store c_C[i,j,k] in b_C[i,j]
    c_C.store_in(&b_C_gpu, {i,j});
    // Note that both of the computations C_init and c_C store their
    // results in the buffer b_C.

    // -------------------------------------------------------
    // Code Generation
    // -------------------------------------------------------

    // Generate object files. Last argument triggers cuda compilation.
    matmul.codegen({&b_A, &b_B, &b_C}, "build/generated_fct_developers_tutorial_04gpu.o", true);

    // Dump the generated Halide statement (just for debugging).
    matmul.dump_halide_stmt();

    return 0;
}
<|endoftext|>


<commit_before>/*
 * A very bad streaming TOML parser
 */
#include "toml.h"
#include "debug.h"
#include <cassert>
#include <string>

struct Token
{
    enum class Type
    {
        Eof,
        SquareOpen,
        SquareClose,
        BraceOpen,
        BraceClose,
        Assign,
        Newline,
        Comma,
        Dot,

        Ident,
        String,
        Integer,
    };

    Type    m_type;
    ::std::string   m_data;
    int64_t m_intval = 0;

    static Token lex_from(::std::ifstream& is);
    static Token lex_from_inner(::std::ifstream& is);

    const ::std::string& as_string() const {
        assert(m_type == Type::Ident || m_type == Type::String);
        return m_data;
    }

    friend ::std::ostream& operator<<(::std::ostream& os, const Token& x) {
        switch(x.m_type)
        {
        case Type::Eof:   os << "Eof";    break;
        case Type::SquareOpen:  os << "SquareOpen"; break;
        case Type::SquareClose: os << "SquareClose"; break;
        case Type::BraceOpen:   os << "BraceOpen"; break;
        case Type::BraceClose:  os << "BraceClose"; break;
        case Type::Assign:      os << "Assign";   break;
        case Type::Newline:     os << "Newline";  break;
        case Type::Comma:       os << "Comma";    break;
        case Type::Dot:         os << "Dot";      break;
        case Type::Ident:  os << "Ident(" << x.m_data << ")";  break;
        case Type::String: os << "String(" << x.m_data << ")"; break;
        case Type::Integer: os << "Integer(" << x.m_intval << ")"; break;
        }
        return os;
    }
};

TomlFile::TomlFile(const ::std::string& filename):
    m_if(filename)
{
    if( !m_if.is_open() ) {
        throw ::std::runtime_error("Unable to open file '" + filename + "'");
    }
}
TomlFileIter TomlFile::begin()
{
    TomlFileIter rv { *this };
    ++rv;
    return rv;
}
TomlFileIter TomlFile::end()
{
    return TomlFileIter { *this };
}

TomlKeyValue TomlFile::get_next_value()
{
    auto t = Token::lex_from(m_if);

    if(m_current_composite.empty())
    {
        while( t.m_type == Token::Type::Newline )
            t = Token::lex_from(m_if);

        // Expect '[', a string, or an identifier
        switch(t.m_type)
        {
        case Token::Type::Eof:
            // Empty return indicates the end of the list
            return TomlKeyValue {};
        case Token::Type::SquareOpen:
            m_current_block.clear();
            do
            {
                t = Token::lex_from(m_if);
                bool is_array = false;
                if(t.m_type == Token::Type::SquareOpen)
                {
                    is_array = true;
                    t = Token::lex_from(m_if);
                }
                assert(t.m_type == Token::Type::Ident || t.m_type == Token::Type::String);
                m_current_block.push_back(t.as_string());
                if(is_array)
                {
                    m_current_block.push_back(::format(m_array_counts[t.as_string()]++));
                    t = Token::lex_from(m_if);
                    assert(t.m_type == Token::Type::SquareClose);
                }

                t = Token::lex_from(m_if);
            } while(t.m_type == Token::Type::Dot);
            if( t.m_type != Token::Type::SquareClose )
            {
                throw ::std::runtime_error(::format("Unexpected token in block header - ", t));
            }
            t = Token::lex_from(m_if);
            if (t.m_type != Token::Type::Newline)
            {
                throw ::std::runtime_error(::format("Unexpected token after block block - ", t));
            }
            DEBUG("Start block " << m_current_block);
            // TODO: Are empty sections allowed?
            //goto recurse;

            while( t.m_type == Token::Type::Newline )
                t = Token::lex_from(m_if);
            break ;
        default:
            break;
        }
    }
    else
    {
        // Expect a string or an identifier
        if( t.m_type == Token::Type::Eof )
        {
            // EOF isn't allowed here
            throw ::std::runtime_error(::format("Unexpected EOF in composite"));
        }
    }
    switch (t.m_type)
    {
    case Token::Type::String:
    case Token::Type::Ident:
        break;
    default:
        throw ::std::runtime_error(::format("Unexpected token for key - ", t));
    }
    ::std::string   key_name = t.as_string();
    t = Token::lex_from(m_if);

    if(t.m_type != Token::Type::Assign)
        throw ::std::runtime_error(::format("Unexpected token after key - ", t));
    t = Token::lex_from(m_if);

    TomlKeyValue    rv;
    switch(t.m_type)
    {
    case Token::Type::String:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);

        rv.value = TomlValue { t.m_data };
        break;
    case Token::Type::SquareOpen:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);

        rv.value.m_type = TomlValue::Type::List;
        while( (t = Token::lex_from(m_if)).m_type != Token::Type::SquareClose )
        {
            while( t.m_type == Token::Type::Newline )
                t = Token::lex_from(m_if);
            if( t.m_type == Token::Type::SquareClose )
                break;

            // TODO: Recurse parse a value
            switch(t.m_type)
            {
            case Token::Type::String:
                rv.value.m_sub_values.push_back(TomlValue { t.as_string() });
                break;
            default:
                throw ::std::runtime_error(::format("Unexpected token in array value position - ", t));
            }

            t = Token::lex_from(m_if);
            if(t.m_type != Token::Type::Comma)
                break;
        }
        if(t.m_type != Token::Type::SquareClose)
            throw ::std::runtime_error(::format("Unexpected token after array - ", t));
        break;
    case Token::Type::BraceOpen:
        m_current_composite.push_back(key_name);
        DEBUG("Enter composite block " << m_current_block << ", " << m_current_composite);
        // Recurse to restart parse
        return get_next_value();
    case Token::Type::Integer:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);
        rv.value = TomlValue { t.m_intval };
        return rv;
    case Token::Type::Ident:
        if( t.m_data == "true" )
        {
            rv.path = m_current_block;
            rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
            rv.path.push_back(key_name);
            rv.value = TomlValue { true };
        }
        else if( t.m_data == "false" )
        {
            rv.path = m_current_block;
            rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
            rv.path.push_back(key_name);

            rv.value = TomlValue { false };
        }
        else
        {
            throw ::std::runtime_error(::format("Unexpected identifier in value position - ", t));
        }
        break;
    default:
        throw ::std::runtime_error(::format("Unexpected token in value position - ", t));
    }

    t = Token::lex_from(m_if);
    while (!m_current_composite.empty() && t.m_type == Token::Type::BraceClose)
    {
        DEBUG("Leave composite block " << m_current_block << ", " << m_current_composite);
        m_current_composite.pop_back();
        t = Token::lex_from(m_if);
    }
    if( m_current_composite.empty() )
    {
        // TODO: Allow EOF?
        if(t.m_type != Token::Type::Newline)
            throw ::std::runtime_error(::format("Unexpected token in TOML file after entry - ", t));
    }
    else
    {
        if( t.m_type != Token::Type::Comma )
            throw ::std::runtime_error(::format("Unexpected token in TOML file after composite entry - ", t));
    }
    return rv;
}

Token Token::lex_from(::std::ifstream& is)
{
    auto rv = Token::lex_from_inner(is);
    //DEBUG("lex_from: " << rv);
    return rv;
}
Token Token::lex_from_inner(::std::ifstream& is)
{
    int c;
    do
    {
        c = is.get();
    } while( c != EOF && c != '\n' && isspace(c) );

    ::std::string   str;
    switch(c)
    {
    case EOF:   return Token { Type::Eof };
    case '[':   return Token { Type::SquareOpen };
    case ']':   return Token { Type::SquareClose };
    case '{':   return Token { Type::BraceOpen };
    case '}':   return Token { Type::BraceClose };
    case ',':   return Token { Type::Comma };
    case '.':   return Token { Type::Dot };
    case '=':   return Token { Type::Assign };
    case '\n':  return Token { Type::Newline };
    case '#':
        while(c != '\n')
        {
            c = is.get();
            if(c == EOF)
                return Token { Type::Eof };
        }
        return Token { Type::Newline };
    case '\'':
        c = is.get();
        while (c != '\'')
        {
            if (c == EOF)
                throw ::std::runtime_error("Unexpected EOF in single-quoted string");
            if (c == '\\')
            {
                // TODO: Escaped strings
                throw ::std::runtime_error("TODO: Escaped sequences in strings (single)");
            }
            str += (char)c;
            c = is.get();
        }
        return Token { Type::String, str };
    case '"':
        c = is.get();
        if(c == '"')
        {
            c = is.get();
            if( c != '"' )
            {
                is.putback(c);
                return Token { Type::String, "" };
            }
            else
            {
                // Keep reading until """
                for(;;)
                {
                    c = is.get();
                    if(c == '"')
                    {
                        c = is.get();
                        if(c == '"')
                        {
                            c = is.get();
                            if(c == '"')
                            {
                                break;
                            }
                            str += '"';
                        }
                        str += '"';
                    }
                    if( c == EOF )
                        throw ::std::runtime_error("Unexpected EOF in triple-quoted string");
                    if(c == '\\')
                    {
                        // TODO: Escaped strings
                        throw ::std::runtime_error("TODO: Escaped sequences in strings (triple)");
                    }
                    str += (char)c;
                }
            }
        }
        else
        {
            while(c != '"')
            {
                if (c == EOF)
                    throw ::std::runtime_error("Unexpected EOF in double-quoted string");
                if (c == '\\')
                {
                    // TODO: Escaped strings
                    c = is.get();
                    switch(c)
                    {
                    case '"':  str += '"'; break;
                    case 'n':  str += '\n'; break;
                    default:
                        throw ::std::runtime_error("TODO: Escaped sequences in strings");
                    }
                    c = is.get();
                    continue ;
                }
                str += (char)c;
                c = is.get();
            }
        }
        return Token { Type::String, str };
    default:
        if(isalpha(c))
        {
            // Identifier
            while(isalnum(c) || c == '-' || c == '_')
            {
                str += (char)c;
                c = is.get();
            }
            is.putback(c);
            return Token { Type::Ident, str };
        }
        else if( isdigit(c) )
        {
            int64_t val = 0;
            while(isdigit(c))
            {
                val *= 10;
                val += c - '0';
                c = is.get();
            }
            is.putback(c);
            return Token { Type::Integer, "", val };
        }
        else
        {
            throw ::std::runtime_error(::format("Unexpected chracter '", (char)c, "' in file"));
        }
    }
}
<commit_msg>minicargo toml - Handle empty sections<commit_after>/*
 * A very bad streaming TOML parser
 */
#include "toml.h"
#include "debug.h"
#include <cassert>
#include <string>

struct Token
{
    enum class Type
    {
        Eof,
        SquareOpen,
        SquareClose,
        BraceOpen,
        BraceClose,
        Assign,
        Newline,
        Comma,
        Dot,

        Ident,
        String,
        Integer,
    };

    Type    m_type;
    ::std::string   m_data;
    int64_t m_intval = 0;

    static Token lex_from(::std::ifstream& is);
    static Token lex_from_inner(::std::ifstream& is);

    const ::std::string& as_string() const {
        assert(m_type == Type::Ident || m_type == Type::String);
        return m_data;
    }

    friend ::std::ostream& operator<<(::std::ostream& os, const Token& x) {
        switch(x.m_type)
        {
        case Type::Eof:   os << "Eof";    break;
        case Type::SquareOpen:  os << "SquareOpen"; break;
        case Type::SquareClose: os << "SquareClose"; break;
        case Type::BraceOpen:   os << "BraceOpen"; break;
        case Type::BraceClose:  os << "BraceClose"; break;
        case Type::Assign:      os << "Assign";   break;
        case Type::Newline:     os << "Newline";  break;
        case Type::Comma:       os << "Comma";    break;
        case Type::Dot:         os << "Dot";      break;
        case Type::Ident:  os << "Ident(" << x.m_data << ")";  break;
        case Type::String: os << "String(" << x.m_data << ")"; break;
        case Type::Integer: os << "Integer(" << x.m_intval << ")"; break;
        }
        return os;
    }
};

TomlFile::TomlFile(const ::std::string& filename):
    m_if(filename)
{
    if( !m_if.is_open() ) {
        throw ::std::runtime_error("Unable to open file '" + filename + "'");
    }
}
TomlFileIter TomlFile::begin()
{
    TomlFileIter rv { *this };
    ++rv;
    return rv;
}
TomlFileIter TomlFile::end()
{
    return TomlFileIter { *this };
}

TomlKeyValue TomlFile::get_next_value()
{
    auto t = Token::lex_from(m_if);

    if(m_current_composite.empty())
    {
        while( t.m_type == Token::Type::Newline )
            t = Token::lex_from(m_if);

        // Expect '[', a string, or an identifier
        switch(t.m_type)
        {
        case Token::Type::Eof:
            // Empty return indicates the end of the list
            return TomlKeyValue {};
        case Token::Type::SquareOpen:
            m_current_block.clear();
            do
            {
                t = Token::lex_from(m_if);
                bool is_array = false;
                if(t.m_type == Token::Type::SquareOpen)
                {
                    is_array = true;
                    t = Token::lex_from(m_if);
                }
                assert(t.m_type == Token::Type::Ident || t.m_type == Token::Type::String);
                m_current_block.push_back(t.as_string());
                if(is_array)
                {
                    m_current_block.push_back(::format(m_array_counts[t.as_string()]++));
                    t = Token::lex_from(m_if);
                    assert(t.m_type == Token::Type::SquareClose);
                }

                t = Token::lex_from(m_if);
            } while(t.m_type == Token::Type::Dot);
            if( t.m_type != Token::Type::SquareClose )
            {
                throw ::std::runtime_error(::format("Unexpected token in block header - ", t));
            }
            t = Token::lex_from(m_if);
            if (t.m_type != Token::Type::Newline)
            {
                throw ::std::runtime_error(::format("Unexpected token after block block - ", t));
            }
            DEBUG("Start block " << m_current_block);
            // Recurse!
            return get_next_value();
        default:
            break;
        }
    }
    else
    {
        // Expect a string or an identifier
        if( t.m_type == Token::Type::Eof )
        {
            // EOF isn't allowed here
            throw ::std::runtime_error(::format("Unexpected EOF in composite"));
        }
    }
    switch (t.m_type)
    {
    case Token::Type::String:
    case Token::Type::Ident:
        break;
    default:
        throw ::std::runtime_error(::format("Unexpected token for key - ", t));
    }
    ::std::string   key_name = t.as_string();
    t = Token::lex_from(m_if);

    if(t.m_type != Token::Type::Assign)
        throw ::std::runtime_error(::format("Unexpected token after key - ", t));
    t = Token::lex_from(m_if);

    TomlKeyValue    rv;
    switch(t.m_type)
    {
    case Token::Type::String:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);

        rv.value = TomlValue { t.m_data };
        break;
    case Token::Type::SquareOpen:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);

        rv.value.m_type = TomlValue::Type::List;
        while( (t = Token::lex_from(m_if)).m_type != Token::Type::SquareClose )
        {
            while( t.m_type == Token::Type::Newline )
                t = Token::lex_from(m_if);
            if( t.m_type == Token::Type::SquareClose )
                break;

            // TODO: Recurse parse a value
            switch(t.m_type)
            {
            case Token::Type::String:
                rv.value.m_sub_values.push_back(TomlValue { t.as_string() });
                break;
            default:
                throw ::std::runtime_error(::format("Unexpected token in array value position - ", t));
            }

            t = Token::lex_from(m_if);
            if(t.m_type != Token::Type::Comma)
                break;
        }
        if(t.m_type != Token::Type::SquareClose)
            throw ::std::runtime_error(::format("Unexpected token after array - ", t));
        break;
    case Token::Type::BraceOpen:
        m_current_composite.push_back(key_name);
        DEBUG("Enter composite block " << m_current_block << ", " << m_current_composite);
        // Recurse to restart parse
        return get_next_value();
    case Token::Type::Integer:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);
        rv.value = TomlValue { t.m_intval };
        return rv;
    case Token::Type::Ident:
        if( t.m_data == "true" )
        {
            rv.path = m_current_block;
            rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
            rv.path.push_back(key_name);
            rv.value = TomlValue { true };
        }
        else if( t.m_data == "false" )
        {
            rv.path = m_current_block;
            rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
            rv.path.push_back(key_name);

            rv.value = TomlValue { false };
        }
        else
        {
            throw ::std::runtime_error(::format("Unexpected identifier in value position - ", t));
        }
        break;
    default:
        throw ::std::runtime_error(::format("Unexpected token in value position - ", t));
    }

    t = Token::lex_from(m_if);
    while (!m_current_composite.empty() && t.m_type == Token::Type::BraceClose)
    {
        DEBUG("Leave composite block " << m_current_block << ", " << m_current_composite);
        m_current_composite.pop_back();
        t = Token::lex_from(m_if);
    }
    if( m_current_composite.empty() )
    {
        // TODO: Allow EOF?
        if(t.m_type != Token::Type::Newline)
            throw ::std::runtime_error(::format("Unexpected token in TOML file after entry - ", t));
    }
    else
    {
        if( t.m_type != Token::Type::Comma )
            throw ::std::runtime_error(::format("Unexpected token in TOML file after composite entry - ", t));
    }
    return rv;
}

Token Token::lex_from(::std::ifstream& is)
{
    auto rv = Token::lex_from_inner(is);
    //DEBUG("lex_from: " << rv);
    return rv;
}
Token Token::lex_from_inner(::std::ifstream& is)
{
    int c;
    do
    {
        c = is.get();
    } while( c != EOF && c != '\n' && isspace(c) );

    ::std::string   str;
    switch(c)
    {
    case EOF:   return Token { Type::Eof };
    case '[':   return Token { Type::SquareOpen };
    case ']':   return Token { Type::SquareClose };
    case '{':   return Token { Type::BraceOpen };
    case '}':   return Token { Type::BraceClose };
    case ',':   return Token { Type::Comma };
    case '.':   return Token { Type::Dot };
    case '=':   return Token { Type::Assign };
    case '\n':  return Token { Type::Newline };
    case '#':
        while(c != '\n')
        {
            c = is.get();
            if(c == EOF)
                return Token { Type::Eof };
        }
        return Token { Type::Newline };
    case '\'':
        c = is.get();
        while (c != '\'')
        {
            if (c == EOF)
                throw ::std::runtime_error("Unexpected EOF in single-quoted string");
            if (c == '\\')
            {
                // TODO: Escaped strings
                throw ::std::runtime_error("TODO: Escaped sequences in strings (single)");
            }
            str += (char)c;
            c = is.get();
        }
        return Token { Type::String, str };
    case '"':
        c = is.get();
        if(c == '"')
        {
            c = is.get();
            if( c != '"' )
            {
                is.putback(c);
                return Token { Type::String, "" };
            }
            else
            {
                // Keep reading until """
                for(;;)
                {
                    c = is.get();
                    if(c == '"')
                    {
                        c = is.get();
                        if(c == '"')
                        {
                            c = is.get();
                            if(c == '"')
                            {
                                break;
                            }
                            str += '"';
                        }
                        str += '"';
                    }
                    if( c == EOF )
                        throw ::std::runtime_error("Unexpected EOF in triple-quoted string");
                    if(c == '\\')
                    {
                        // TODO: Escaped strings
                        throw ::std::runtime_error("TODO: Escaped sequences in strings (triple)");
                    }
                    str += (char)c;
                }
            }
        }
        else
        {
            while(c != '"')
            {
                if (c == EOF)
                    throw ::std::runtime_error("Unexpected EOF in double-quoted string");
                if (c == '\\')
                {
                    // TODO: Escaped strings
                    c = is.get();
                    switch(c)
                    {
                    case '"':  str += '"'; break;
                    case 'n':  str += '\n'; break;
                    default:
                        throw ::std::runtime_error("TODO: Escaped sequences in strings");
                    }
                    c = is.get();
                    continue ;
                }
                str += (char)c;
                c = is.get();
            }
        }
        return Token { Type::String, str };
    default:
        if(isalpha(c))
        {
            // Identifier
            while(isalnum(c) || c == '-' || c == '_')
            {
                str += (char)c;
                c = is.get();
            }
            is.putback(c);
            return Token { Type::Ident, str };
        }
        else if( isdigit(c) )
        {
            int64_t val = 0;
            while(isdigit(c))
            {
                val *= 10;
                val += c - '0';
                c = is.get();
            }
            is.putback(c);
            return Token { Type::Integer, "", val };
        }
        else
        {
            throw ::std::runtime_error(::format("Unexpected chracter '", (char)c, "' in file"));
        }
    }
}
<|endoftext|>