<commit_before>// Copyright (c) 2012-2015 Dano Pernis
// See LICENSE for details

#include <gtk/gtk.h>
#include <glib.h>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <cassert>
#include "CPU.h"

namespace hcc {

struct ROM : public IROM {
    unsigned short *data;
    static const unsigned int size = 0x8000;
    ROM() {
        data = new unsigned short[size];
    }
    virtual ~ROM() {
        delete[] data;
    }

    bool load(const char *filename) {
        std::ifstream input(filename);
        std::string line;
        unsigned int counter = 0;
        while (input.good() && counter < size) {
            getline(input, line);
            if (line.size() == 0)
                continue;
            if (line.size() != 16)
                return false;

            unsigned int instruction = 0;
            for (unsigned int i = 0; i<16; ++i) {
                instruction <<= 1;
                switch (line[i]) {
                case '0':
                    break;
                case '1':
                    instruction |= 1;
                    break;
                default:
                    return false;
                }
            }
            data[counter++] = instruction;
        }
        // clear the rest
        while (counter < size) {
            data[counter++] = 0;
        }

        return true;
    }
    virtual unsigned short get(unsigned int address) const {
        if (address < size) {
            return data[address];
        } else {
            std::cerr << "requested memory at " << address << '\n';
            throw std::runtime_error("Memory::get");
        }
    }
};

} // end namespace

struct GUIEmulatorRAM : public hcc::IRAM {
    static const unsigned int CHANNELS = 3;
    static const unsigned int SCREEN_WIDTH = 512;
    static const unsigned int SCREEN_HEIGHT = 256;
    static const unsigned int size = 0x6001;

    unsigned short *data;
    unsigned char *vram;
    GdkPixbuf *pixbuf;
    GtkWidget *screen;

    void putpixel(unsigned short x, unsigned short y, bool black) {
        unsigned int offset = CHANNELS*(SCREEN_WIDTH*y + x);
        for (unsigned int channel = 0; channel<CHANNELS; ++channel) {
            vram[offset++] = black ? 0x00 : 0xff;
        }
    }
public:
    GUIEmulatorRAM() {
        data = new unsigned short[size];
        vram = new unsigned char[CHANNELS*SCREEN_WIDTH*SCREEN_HEIGHT];
        pixbuf = gdk_pixbuf_new_from_data(vram, GDK_COLORSPACE_RGB, FALSE, 8, SCREEN_WIDTH, SCREEN_HEIGHT, CHANNELS*SCREEN_WIDTH, NULL, NULL);
        screen = gtk_image_new_from_pixbuf(pixbuf);
    }
    virtual ~GUIEmulatorRAM() {
        delete[] data;
        delete[] vram;
    }
    void keyboard(unsigned short value) {
        data[0x6000] = value;
    }
    GtkWidget* getScreenWidget() {
        return screen;
    }
    virtual void set(unsigned int address, unsigned short value) {
        if (address >= size) {
            throw std::runtime_error("RAM::set");
        }

        data[address] = value;

        // check if we are writing to video RAM
        if (0x4000 <= address && address <0x6000) {
            address -= 0x4000;

            unsigned short y = address / 32;
            unsigned short x = 16*(address % 32);
            for (int bit = 0; bit<16; ++bit) {
                putpixel(x + bit, y, value & 1);
                value = value >> 1;
            }
            gdk_threads_enter();
            gtk_widget_queue_draw(screen);
            gdk_threads_leave();
        }
    }
    virtual unsigned short get(unsigned int address) const {
        if (address >= size) {
            throw std::runtime_error("RAM::get");
        }

        return data[address];
    }
};

struct emulator {
    emulator();
    void load_clicked();
    void run_clicked();
    void pause_clicked();
    gboolean keyboard_callback(GdkEventKey* event);
    void run_thread();
    GtkToolItem* create_button(const gchar* stock_id, const gchar* text, GCallback callback);

    hcc::ROM rom;
    GUIEmulatorRAM ram;
    hcc::CPU cpu;

    bool running = false;

    GtkWidget* window;
    GtkWidget* load_dialog;
    GtkWidget* error_dialog;
    GtkToolItem* button_load;
    GtkToolItem* button_run;
    GtkToolItem* button_pause;
};

gboolean c_keyboard_callback(GtkWidget*, GdkEventKey *event, gpointer user_data)
{ return reinterpret_cast<emulator*>(user_data)->keyboard_callback(event); }

void c_load_clicked(GtkButton*, gpointer user_data)
{ reinterpret_cast<emulator*>(user_data)->load_clicked(); }

void c_run_clicked(GtkButton*, gpointer user_data)
{ reinterpret_cast<emulator*>(user_data)->run_clicked(); }

void c_pause_clicked(GtkButton*, gpointer user_data)
{ reinterpret_cast<emulator*>(user_data)->pause_clicked(); }

gpointer c_run_thread(gpointer user_data)
{
    reinterpret_cast<emulator*>(user_data)->run_thread();
    return NULL;
}

// Translate special keys. See Figure 5.6 in TECS book.
unsigned short translate(guint keyval)
{
    switch (keyval) {
    case GDK_KEY_Return:    return 128;
    case GDK_KEY_BackSpace: return 129;
    case GDK_KEY_Left:      return 130;
    case GDK_KEY_Up:        return 131;
    case GDK_KEY_Right:     return 132;
    case GDK_KEY_Down:      return 133;
    case GDK_KEY_Home:      return 134;
    case GDK_KEY_End:       return 135;
    case GDK_KEY_Page_Up:   return 136;
    case GDK_KEY_Page_Down: return 137;
    case GDK_KEY_Insert:    return 138;
    case GDK_KEY_Delete:    return 139;
    case GDK_KEY_Escape:    return 140;
    case GDK_KEY_F1:        return 141;
    case GDK_KEY_F2:        return 142;
    case GDK_KEY_F3:        return 143;
    case GDK_KEY_F4:        return 144;
    case GDK_KEY_F5:        return 145;
    case GDK_KEY_F6:        return 146;
    case GDK_KEY_F7:        return 147;
    case GDK_KEY_F8:        return 148;
    case GDK_KEY_F9:        return 149;
    case GDK_KEY_F10:       return 150;
    case GDK_KEY_F11:       return 151;
    case GDK_KEY_F12:       return 152;
    }
    return keyval;
}

emulator::emulator()
{
    /* toolbar buttons */
    button_load    = create_button("document-open",        "Load...", G_CALLBACK(c_load_clicked));
    button_run     = create_button("media-playback-start", "Run",     G_CALLBACK(c_run_clicked));
    button_pause   = create_button("media-playback-pause", "Pause",   G_CALLBACK(c_pause_clicked));

    GtkToolItem *separator1 = gtk_separator_tool_item_new();

    gtk_widget_set_sensitive(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), FALSE);

    /* toolbar itself */
    GtkWidget *toolbar = gtk_toolbar_new();
    gtk_widget_set_hexpand(toolbar, TRUE);
    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_load,  -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), separator1,   -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_run,   -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_pause, -1);

    /* keyboard */
    GtkWidget *keyboard = gtk_toggle_button_new_with_label("Grab keyboard focus");
    gtk_widget_add_events(keyboard, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
    g_signal_connect(keyboard, "key-press-event",   G_CALLBACK(c_keyboard_callback), this);
    g_signal_connect(keyboard, "key-release-event", G_CALLBACK(c_keyboard_callback), this);

    /* main layout */
    GtkWidget *grid = gtk_grid_new();
    gtk_grid_attach(GTK_GRID(grid), toolbar, 0, 0, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), ram.getScreenWidget(), 0, 1, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), keyboard, 0, 2, 1, 1);

    /* main window */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "HACK emulator");
    gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
    gtk_window_set_focus(GTK_WINDOW(window), NULL);
    g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
    gtk_container_add(GTK_CONTAINER(window), grid);
    gtk_widget_show_all(window);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), FALSE);

    load_dialog = gtk_file_chooser_dialog_new(
        "Load ROM", GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_OPEN,
        "gtk-cancel", GTK_RESPONSE_CANCEL,
        "gtk-open", GTK_RESPONSE_ACCEPT,
        NULL);

    error_dialog = gtk_message_dialog_new(GTK_WINDOW(window),
        GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
        "Error loading program");
}

GtkToolItem* emulator::create_button(const gchar* stock_id, const gchar* text, GCallback callback)
{
    GtkToolItem *button = gtk_tool_button_new(NULL, text);
    gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(button), stock_id);
    gtk_tool_item_set_tooltip_text(button, text);
    g_signal_connect(button, "clicked", callback, this);
    return button;
}

void emulator::load_clicked()
{
    const gint result = gtk_dialog_run(GTK_DIALOG(load_dialog));
    gtk_widget_hide(load_dialog);

    if (result != GTK_RESPONSE_ACCEPT) {
        return;
    }

    char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(load_dialog));
    const bool loaded = rom.load(filename);
    g_free(filename);

    if (!loaded) {
        gtk_dialog_run(GTK_DIALOG(error_dialog));
        gtk_widget_hide(error_dialog);
        return;
    }

    cpu.reset();
    gtk_widget_set_sensitive(GTK_WIDGET(button_run), TRUE);
}

void emulator::run_clicked()
{
    assert(!running);

    running = true;
    gtk_widget_set_sensitive(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_visible(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), TRUE);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), TRUE);

    g_thread_create(c_run_thread, this, FALSE, NULL);
}

void emulator::pause_clicked()
{
    assert(running);

    running = false;
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), FALSE);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_run), TRUE);
    gtk_widget_set_visible(GTK_WIDGET(button_run), TRUE);
}

gboolean emulator::keyboard_callback(GdkEventKey* event)
{
    if (event->type == GDK_KEY_RELEASE) {
        ram.keyboard(0);
    } else {
        ram.keyboard(translate(event->keyval));
    }
    return TRUE;
}

void emulator::run_thread()
{
    int steps = 0;
    while (running) {
        cpu.step(&rom, &ram);
        if (steps>100) {
            g_usleep(10);
            steps = 0;
        }
        ++steps;
    }
}

int main(int argc, char *argv[])
{
    gtk_init(&argc, &argv);
    emulator e;
    gtk_main();
    return 0;
}
<commit_msg>Use GtkDrawinfArea and Cairo<commit_after>// Copyright (c) 2012-2015 Dano Pernis
// See LICENSE for details

#include <gtk/gtk.h>
#include <glib.h>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <cassert>
#include "CPU.h"

namespace hcc {

struct ROM : public IROM {
    unsigned short *data;
    static const unsigned int size = 0x8000;
    ROM() {
        data = new unsigned short[size];
    }
    virtual ~ROM() {
        delete[] data;
    }

    bool load(const char *filename) {
        std::ifstream input(filename);
        std::string line;
        unsigned int counter = 0;
        while (input.good() && counter < size) {
            getline(input, line);
            if (line.size() == 0)
                continue;
            if (line.size() != 16)
                return false;

            unsigned int instruction = 0;
            for (unsigned int i = 0; i<16; ++i) {
                instruction <<= 1;
                switch (line[i]) {
                case '0':
                    break;
                case '1':
                    instruction |= 1;
                    break;
                default:
                    return false;
                }
            }
            data[counter++] = instruction;
        }
        // clear the rest
        while (counter < size) {
            data[counter++] = 0;
        }

        return true;
    }
    virtual unsigned short get(unsigned int address) const {
        if (address < size) {
            return data[address];
        } else {
            std::cerr << "requested memory at " << address << '\n';
            throw std::runtime_error("Memory::get");
        }
    }
};

} // end namespace

gboolean on_draw(GtkWidget*, cairo_t* cr, gpointer data)
{
    GdkPixbuf* pixbuf = reinterpret_cast<GdkPixbuf*>(data);
    gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
    cairo_paint(cr);
    return FALSE;
}

struct GUIEmulatorRAM : public hcc::IRAM {
    static const unsigned int CHANNELS = 3;
    static const unsigned int SCREEN_WIDTH = 512;
    static const unsigned int SCREEN_HEIGHT = 256;
    static const unsigned int size = 0x6001;

    unsigned short *data;
    GdkPixbuf *pixbuf;
    GtkWidget *screen;

    void putpixel(unsigned short x, unsigned short y, bool black) {
        int color = black ? 0x00 : 0xff;

        int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
        int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
        guchar* pixels = gdk_pixbuf_get_pixels(pixbuf);
        guchar* p = pixels + y * rowstride + x * n_channels;
        p[0] = color;
        p[1] = color;
        p[2] = color;
    }
public:
    GUIEmulatorRAM() {
        data = new unsigned short[size];

        pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, SCREEN_WIDTH, SCREEN_HEIGHT);
        gdk_pixbuf_fill(pixbuf, 0x0000000);

        screen = gtk_drawing_area_new();
        gtk_widget_set_size_request(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
        g_signal_connect(screen, "draw", G_CALLBACK(on_draw), pixbuf);
    }
    virtual ~GUIEmulatorRAM() {
        delete[] data;
    }
    void keyboard(unsigned short value) {
        data[0x6000] = value;
    }
    GtkWidget* getScreenWidget() {
        return screen;
    }
    virtual void set(unsigned int address, unsigned short value) {
        if (address >= size) {
            throw std::runtime_error("RAM::set");
        }

        data[address] = value;

        // check if we are writing to video RAM
        if (0x4000 <= address && address <0x6000) {
            address -= 0x4000;

            unsigned short y = address / 32;
            unsigned short x = 16*(address % 32);
            for (int bit = 0; bit<16; ++bit) {
                putpixel(x + bit, y, value & 1);
                value = value >> 1;
            }
            gdk_threads_enter();
            gtk_widget_queue_draw(screen);
            gdk_threads_leave();
        }
    }
    virtual unsigned short get(unsigned int address) const {
        if (address >= size) {
            throw std::runtime_error("RAM::get");
        }

        return data[address];
    }
};

struct emulator {
    emulator();
    void load_clicked();
    void run_clicked();
    void pause_clicked();
    gboolean keyboard_callback(GdkEventKey* event);
    void run_thread();
    GtkToolItem* create_button(const gchar* stock_id, const gchar* text, GCallback callback);

    hcc::ROM rom;
    GUIEmulatorRAM ram;
    hcc::CPU cpu;

    bool running = false;

    GtkWidget* window;
    GtkWidget* load_dialog;
    GtkWidget* error_dialog;
    GtkToolItem* button_load;
    GtkToolItem* button_run;
    GtkToolItem* button_pause;
};

gboolean c_keyboard_callback(GtkWidget*, GdkEventKey *event, gpointer user_data)
{ return reinterpret_cast<emulator*>(user_data)->keyboard_callback(event); }

void c_load_clicked(GtkButton*, gpointer user_data)
{ reinterpret_cast<emulator*>(user_data)->load_clicked(); }

void c_run_clicked(GtkButton*, gpointer user_data)
{ reinterpret_cast<emulator*>(user_data)->run_clicked(); }

void c_pause_clicked(GtkButton*, gpointer user_data)
{ reinterpret_cast<emulator*>(user_data)->pause_clicked(); }

gpointer c_run_thread(gpointer user_data)
{
    reinterpret_cast<emulator*>(user_data)->run_thread();
    return NULL;
}

// Translate special keys. See Figure 5.6 in TECS book.
unsigned short translate(guint keyval)
{
    switch (keyval) {
    case GDK_KEY_Return:    return 128;
    case GDK_KEY_BackSpace: return 129;
    case GDK_KEY_Left:      return 130;
    case GDK_KEY_Up:        return 131;
    case GDK_KEY_Right:     return 132;
    case GDK_KEY_Down:      return 133;
    case GDK_KEY_Home:      return 134;
    case GDK_KEY_End:       return 135;
    case GDK_KEY_Page_Up:   return 136;
    case GDK_KEY_Page_Down: return 137;
    case GDK_KEY_Insert:    return 138;
    case GDK_KEY_Delete:    return 139;
    case GDK_KEY_Escape:    return 140;
    case GDK_KEY_F1:        return 141;
    case GDK_KEY_F2:        return 142;
    case GDK_KEY_F3:        return 143;
    case GDK_KEY_F4:        return 144;
    case GDK_KEY_F5:        return 145;
    case GDK_KEY_F6:        return 146;
    case GDK_KEY_F7:        return 147;
    case GDK_KEY_F8:        return 148;
    case GDK_KEY_F9:        return 149;
    case GDK_KEY_F10:       return 150;
    case GDK_KEY_F11:       return 151;
    case GDK_KEY_F12:       return 152;
    }
    return keyval;
}

emulator::emulator()
{
    /* toolbar buttons */
    button_load    = create_button("document-open",        "Load...", G_CALLBACK(c_load_clicked));
    button_run     = create_button("media-playback-start", "Run",     G_CALLBACK(c_run_clicked));
    button_pause   = create_button("media-playback-pause", "Pause",   G_CALLBACK(c_pause_clicked));

    GtkToolItem *separator1 = gtk_separator_tool_item_new();

    gtk_widget_set_sensitive(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), FALSE);

    /* toolbar itself */
    GtkWidget *toolbar = gtk_toolbar_new();
    gtk_widget_set_hexpand(toolbar, TRUE);
    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_load,  -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), separator1,   -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_run,   -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_pause, -1);

    /* keyboard */
    GtkWidget *keyboard = gtk_toggle_button_new_with_label("Grab keyboard focus");
    gtk_widget_add_events(keyboard, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
    g_signal_connect(keyboard, "key-press-event",   G_CALLBACK(c_keyboard_callback), this);
    g_signal_connect(keyboard, "key-release-event", G_CALLBACK(c_keyboard_callback), this);

    /* main layout */
    GtkWidget *grid = gtk_grid_new();
    gtk_grid_attach(GTK_GRID(grid), toolbar, 0, 0, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), ram.getScreenWidget(), 0, 1, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), keyboard, 0, 2, 1, 1);

    /* main window */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "HACK emulator");
    gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
    gtk_window_set_focus(GTK_WINDOW(window), NULL);
    g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
    gtk_container_add(GTK_CONTAINER(window), grid);
    gtk_widget_show_all(window);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), FALSE);

    load_dialog = gtk_file_chooser_dialog_new(
        "Load ROM", GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_OPEN,
        "gtk-cancel", GTK_RESPONSE_CANCEL,
        "gtk-open", GTK_RESPONSE_ACCEPT,
        NULL);

    error_dialog = gtk_message_dialog_new(GTK_WINDOW(window),
        GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
        "Error loading program");
}

GtkToolItem* emulator::create_button(const gchar* stock_id, const gchar* text, GCallback callback)
{
    GtkToolItem *button = gtk_tool_button_new(NULL, text);
    gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(button), stock_id);
    gtk_tool_item_set_tooltip_text(button, text);
    g_signal_connect(button, "clicked", callback, this);
    return button;
}

void emulator::load_clicked()
{
    const gint result = gtk_dialog_run(GTK_DIALOG(load_dialog));
    gtk_widget_hide(load_dialog);

    if (result != GTK_RESPONSE_ACCEPT) {
        return;
    }

    char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(load_dialog));
    const bool loaded = rom.load(filename);
    g_free(filename);

    if (!loaded) {
        gtk_dialog_run(GTK_DIALOG(error_dialog));
        gtk_widget_hide(error_dialog);
        return;
    }

    cpu.reset();
    gtk_widget_set_sensitive(GTK_WIDGET(button_run), TRUE);
}

void emulator::run_clicked()
{
    assert(!running);

    running = true;
    gtk_widget_set_sensitive(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_visible(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), TRUE);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), TRUE);

    g_thread_create(c_run_thread, this, FALSE, NULL);
}

void emulator::pause_clicked()
{
    assert(running);

    running = false;
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), FALSE);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_run), TRUE);
    gtk_widget_set_visible(GTK_WIDGET(button_run), TRUE);
}

gboolean emulator::keyboard_callback(GdkEventKey* event)
{
    if (event->type == GDK_KEY_RELEASE) {
        ram.keyboard(0);
    } else {
        ram.keyboard(translate(event->keyval));
    }
    return TRUE;
}

void emulator::run_thread()
{
    int steps = 0;
    while (running) {
        cpu.step(&rom, &ram);
        if (steps>100) {
            g_usleep(10);
            steps = 0;
        }
        ++steps;
    }
}

int main(int argc, char *argv[])
{
    gtk_init(&argc, &argv);
    emulator e;
    gtk_main();
    return 0;
}
<|endoftext|>


<commit_before>// The libMesh Finite Element Library.
// Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

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

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

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// Local includes
#include "libmesh/edge.h"
#include "libmesh/node_elem.h"

namespace libMesh
{

AutoPtr<Elem> Edge::side (const unsigned int i) const
{
  libmesh_assert_less (i, 2);
  const Elem* parent = this;
  Elem *nodeelem = new NodeElem(const_cast<Elem*>(parent));
  nodeelem->set_node(0) = this->get_node(i);
  return AutoPtr<Elem>(nodeelem);
}

AutoPtr<Elem> Edge::build_side (const unsigned int i, bool) const
{
  libmesh_assert_less (i, 2);
  const Elem* parent = this;
  Elem *nodeelem = new NodeElem(const_cast<Elem*>(parent));
  nodeelem->set_node(0) = this->get_node(i);
  return AutoPtr<Elem>(nodeelem);
}

bool Edge::is_child_on_side(const unsigned int c,
                            const unsigned int s) const
{
  libmesh_assert_less (c, this->n_children());
  libmesh_assert_less (s, this->n_sides());

  return (c == s);
}

unsigned int Edge::opposite_side(const unsigned int side) const
{
  libmesh_assert_less (side, 2);
  return 1 - side;
}

unsigned int Edge::opposite_node(const unsigned int node,
                                 const unsigned int libmesh_dbg_var(side)) const
{
  libmesh_assert_less (node, 2);
  libmesh_assert_less (side, this->n_sides());
  libmesh_assert(this->is_node_on_side(node, side));

  return 1 - node;
}

} // namespace libMesh
<commit_msg>Changes in edge.C for -Wshadow.<commit_after>// The libMesh Finite Element Library.
// Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

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

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

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// Local includes
#include "libmesh/edge.h"
#include "libmesh/node_elem.h"

namespace libMesh
{

AutoPtr<Elem> Edge::side (const unsigned int i) const
{
  libmesh_assert_less (i, 2);
  const Elem* the_parent = this;
  Elem *nodeelem = new NodeElem(const_cast<Elem*>(the_parent));
  nodeelem->set_node(0) = this->get_node(i);
  return AutoPtr<Elem>(nodeelem);
}

AutoPtr<Elem> Edge::build_side (const unsigned int i, bool) const
{
  libmesh_assert_less (i, 2);
  const Elem* the_parent = this;
  Elem *nodeelem = new NodeElem(const_cast<Elem*>(the_parent));
  nodeelem->set_node(0) = this->get_node(i);
  return AutoPtr<Elem>(nodeelem);
}

bool Edge::is_child_on_side(const unsigned int c,
                            const unsigned int s) const
{
  libmesh_assert_less (c, this->n_children());
  libmesh_assert_less (s, this->n_sides());

  return (c == s);
}

unsigned int Edge::opposite_side(const unsigned int side_in) const
{
  libmesh_assert_less (side_in, 2);
  return 1 - side_in;
}

unsigned int Edge::opposite_node(const unsigned int node_in,
                                 const unsigned int libmesh_dbg_var(side_in)) const
{
  libmesh_assert_less (node_in, 2);
  libmesh_assert_less (side_in, this->n_sides());
  libmesh_assert(this->is_node_on_side(node_in, side_in));

  return 1 - node_in;
}

} // namespace libMesh
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <kernel/syscalls.hpp>
#include <hw/acpi.hpp>
#include <hw/ioport.hpp>
#include <debug>
#include <info>

extern "C" void reboot();

namespace hw {

  struct RSDPDescriptor {
    char Signature[8];
    uint8_t Checksum;
    char OEMID[6];
    uint8_t Revision;
    uint32_t RsdtAddress;
  } __attribute__ ((packed));

  struct RSDPDescriptor20 {
    RSDPDescriptor rdsp10;

    uint32_t Length;
    uint64_t XsdtAddress;
    uint8_t  ExtendedChecksum;
    uint8_t  reserved[3];
  } __attribute__ ((packed));

  struct SDTHeader {
    char Signature[4];
    uint32_t Length;
    uint8_t Revision;
    uint8_t Checksum;
    char OEMID[6];
    char OEMTableID[8];
    uint32_t OEMRevision;
    uint32_t CreatorID;
    uint32_t CreatorRevision;

    uint32_t sigint() const {
      return *(uint32_t*) Signature;
    }
  };

  struct MADTRecord {
    uint8_t type;
    uint8_t length;
    uint8_t data[0];
  };

  struct MADTHeader {

    SDTHeader  hdr;
    uintptr_t  lapic_addr;
    uint32_t   flags; // 1 = dual 8259 PICs
    MADTRecord record[0];
  };

  struct FACPHeader
  {
    SDTHeader sdt;
    uint32_t  unneded1;
    uint32_t* DSDT;
    uint8_t unneded2[48 - 44];
    uint32_t* SMI_CMD;
    uint8_t ACPI_ENABLE;
    uint8_t ACPI_DISABLE;
    uint8_t unneded3[64 - 54];
    uint32_t* PM1a_CNT_BLK;
    uint32_t* PM1b_CNT_BLK;
    uint8_t unneded4[89 - 72];
    uint8_t PM1_CNT_LEN;
  };

  struct AddressStructure
  {
    uint8_t  address_space_id; // 0 - system memory, 1 - system I/O
    uint8_t  register_bit_width;
    uint8_t  register_bit_offset;
    uint8_t  reserved;
    uint64_t address;
  };

  struct pci_vendor_t
  {
    uint16_t    ven_id;
    const char* ven_name;
  };

  struct HPET
  {
    uint8_t hardware_rev_id;
    uint8_t comparator_count :5;
    uint8_t counter_size     :1;
    uint8_t reserved         :1;
    uint8_t legacy_replacem  :1;
    pci_vendor_t pci_vendor_id;
    AddressStructure address;
    uint8_t hpet_number;
    uint16_t minimum_tick;
    uint8_t page_protection;
  } __attribute__((packed));

  uint64_t ACPI::time() {
    return 0;
  }

  void ACPI::begin(const void* addr) {

    auto* rdsp = (RSDPDescriptor20*) addr;
    INFO("ACPI", "Reading headers");
    INFO2("OEM: %.*s Rev. %u",
        6, rdsp->rdsp10.OEMID, rdsp->rdsp10.Revision);

    auto* rsdt = (SDTHeader*) rdsp->rdsp10.RsdtAddress;
    // verify Root SDT
    if (!checksum((char*) rsdt, rsdt->Length)) {
      printf("ACPI: SDT failed checksum!");
      panic("SDT checksum failed");
    }

    // walk through system description table headers
    // remember the interesting ones, and count CPUs
    walk_sdts((char*) rsdt);
  }

  constexpr uint32_t bake(char a, char b , char c, char d) {
    return a | (b << 8) | (c << 16) | (d << 24);
  }

  void ACPI::walk_sdts(const char* addr)
  {
    // find total number of SDTs
    auto* rsdt = (SDTHeader*) addr;
    int  total = (rsdt->Length - sizeof(SDTHeader)) / 4;
    // go past rsdt
    addr += sizeof(SDTHeader);

    // parse all tables
    constexpr uint32_t APIC_t = bake('A', 'P', 'I', 'C');
    constexpr uint32_t HPET_t = bake('H', 'P', 'E', 'T');
    constexpr uint32_t FACP_t = bake('F', 'A', 'C', 'P');

    while (total) {
      // convert *addr to SDT-address
      auto sdt_ptr = *(intptr_t*) addr;
      // create SDT pointer
      auto* sdt = (SDTHeader*) sdt_ptr;
      // find out which SDT it is
      switch (sdt->sigint()) {
      case APIC_t:
        debug("APIC found: P=%p L=%u\n", sdt, sdt->Length);
        walk_madt((char*) sdt);
        break;
      case HPET_t:
        debug("HPET found: P=%p L=%u\n", sdt, sdt->Length);
        this->hpet_base = sdt_ptr + sizeof(SDTHeader);
        break;
      case FACP_t:
        printf("FACP found: P=%p L=%u\n", sdt, sdt->Length);
        walk_facp((char*) sdt);
        break;
      default:
        debug("Signature: %.*s (u=%u)\n", 4, sdt->Signature, sdt->sigint());
      }

      addr += 4; total--;
    }
    debug("Finished walking SDTs\n");
  }

  void ACPI::walk_madt(const char* addr)
  {
    auto* hdr = (MADTHeader*) addr;
    INFO("ACPI", "Reading APIC information");
    // the base address for APIC registers
    INFO2("LAPIC base: 0x%x  (flags: 0x%x)",
        hdr->lapic_addr, hdr->flags);
    this->apic_base = hdr->lapic_addr;

    // the length remaining after MADT header
    int len = hdr->hdr.Length - sizeof(MADTHeader);
    // start walking
    const char* ptr = (char*) hdr->record;

    while (len) {
      auto* rec = (MADTRecord*) ptr;
      switch (rec->type) {
      case 0:
        {
          auto& lapic = *(LAPIC*) rec;
          lapics.push_back(lapic);
          INFO2("-> CPU %u ID %u  (flags=0x%x)",
              lapic.cpu, lapic.id, lapic.flags);
        }
        break;
      case 1:
        {
          auto& ioapic = *(IOAPIC*) rec;
          ioapics.push_back(ioapic);
          INFO2("I/O APIC %u   ADDR 0x%x  INTR 0x%x",
              ioapic.id, ioapic.addr_base, ioapic.intr_base);
        }
        break;
      case 2:
        {
          auto& redirect = *(override_t*) rec;
          overrides.push_back(redirect);
          INFO2("IRQ redirect for bus %u from IRQ %u to VEC %u",
              redirect.bus_source, redirect.irq_source, redirect.global_intr);
        }
        break;
      default:
        debug("Unrecognized ACPI MADT type: %u\n", rec->type);
      }
      // decrease length as we go
      len -= rec->length;
      // go to next entry
      ptr += rec->length;
    }
  }

  void ACPI::walk_facp(const char* addr)
  {
    auto* facp = (FACPHeader*) addr;
    // verify DSDT
    constexpr uint32_t DSDT_t = bake('D', 'S', 'D', 'T');
    assert(*facp->DSDT == DSDT_t);

    /// big thanks to kaworu from OSdev.org forums for algo
    /// http://forum.osdev.org/viewtopic.php?t=16990
    char* S5Addr = (char*) facp->DSDT + 36; // skip header
    int dsdtLength = ((SDTHeader*) facp->DSDT)->Length;
    // some ting wong
    dsdtLength *= 2;
    while (dsdtLength-- > 0)
    {
      if (memcmp(S5Addr, "_S5_", 4) == 0)
           break;
      S5Addr++;
    }
    // check if \_S5 was found
    if (dsdtLength <= 0) {
      printf("WARNING: _S5 not present in ACPI\n");
      return;
    }
    // check for valid AML structure
    if ( ( *(S5Addr-1) == 0x08 || ( *(S5Addr-2) == 0x08 && *(S5Addr-1) == '\\') ) && *(S5Addr+4) == 0x12 )
    {
       S5Addr += 5;
       S5Addr += ((*S5Addr &0xC0)>>6) + 2;   // calculate PkgLength size

       if (*S5Addr == 0x0A)
          S5Addr++;   // skip byteprefix
       SLP_TYPa = *(S5Addr) << 10;
       S5Addr++;

       if (*S5Addr == 0x0A)
          S5Addr++;   // skip byteprefix
       SLP_TYPb = *(S5Addr)<<10;

       SMI_CMD = facp->SMI_CMD;

       ACPI_ENABLE = facp->ACPI_ENABLE;
       ACPI_DISABLE = facp->ACPI_DISABLE;

       PM1a_CNT = facp->PM1a_CNT_BLK;
       PM1b_CNT = facp->PM1b_CNT_BLK;

       PM1_CNT_LEN = facp->PM1_CNT_LEN;

       SLP_EN = 1<<13;
       SCI_EN = 1;

       printf("ACPI: Found shutdown information\n");
       return;

    } else {
       printf("WARNING: Failed to parse _S5 in ACPI\n");
    }
    // disable ACPI shutdown
    SCI_EN = 0;
  }

  bool ACPI::checksum(const char* addr, size_t size) const {

    const char* end = addr + size;
    uint8_t sum = 0;
    while (addr < end) {
      sum += *addr; addr++;
    }
    return sum == 0;
  }

  void ACPI::discover() {
    // "RSD PTR "
    const uint64_t sign = 0x2052545020445352;

    // guess at QEMU location of RDSP
    const auto* guess = (char*) 0xf6450;
    if (*(uint64_t*) guess == sign) {
      if (checksum(guess, sizeof(RSDPDescriptor))) {
        debug("Found ACPI located at QEMU-guess (%p)\n", guess);
        begin(guess);
        return;
      }
    }

    // search in BIOS area (below 1mb)
    const auto* addr = (char*) 0x000e0000;
    const auto* end  = (char*) 0x000fffff;
    debug("Looking for ACPI at %p\n", addr);

    while (addr < end) {

      if (*(uint64_t*) addr == sign) {
        // verify checksum of potential RDSP
        if (checksum(addr, sizeof(RSDPDescriptor))) {
          debug("Found ACPI located at %p\n", addr);
          begin(addr);
          return;
        }
      }
      addr++;
    }

    panic("ACPI RDST-search failed\n");
  }

  void ACPI::reboot()
  {
    ::reboot();
  }

  void ACPI::acpi_shutdown() {
    // check if shutdown enabled
    if (SCI_EN == 1) {
      // write shutdown commands
      hw::outw((uint32_t) PM1a_CNT, SLP_TYPa | SLP_EN);
      if (PM1b_CNT != 0)
        hw::outw((uint32_t) PM1b_CNT, SLP_TYPb | SLP_EN);
      printf("*** ACPI shutdown failed\n");
    }
  }

  __attribute__((noreturn))
  void ACPI::shutdown()
  {
    asm volatile("cli");

    // ACPI shutdown
    get().acpi_shutdown();

    // http://forum.osdev.org/viewtopic.php?t=16990
    hw::outw (0xB004, 0x2000);

    const char s[] = "Shutdown";
    const char *p;
    for (p = s; *p; p++)
      // magic code for bochs and qemu
      hw::outb (0x8900, *s);

    // VMWare poweroff when "gui.exitOnCLIHLT" is true
    printf("Shutdown failed :(\n");
    while (true) {
      asm volatile("cli; hlt" : : : "memory");
    }
  }
}
<commit_msg>acpi: Silence some output<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <kernel/syscalls.hpp>
#include <hw/acpi.hpp>
#include <hw/ioport.hpp>
#include <debug>
#include <info>

extern "C" void reboot();

namespace hw {

  struct RSDPDescriptor {
    char Signature[8];
    uint8_t Checksum;
    char OEMID[6];
    uint8_t Revision;
    uint32_t RsdtAddress;
  } __attribute__ ((packed));

  struct RSDPDescriptor20 {
    RSDPDescriptor rdsp10;

    uint32_t Length;
    uint64_t XsdtAddress;
    uint8_t  ExtendedChecksum;
    uint8_t  reserved[3];
  } __attribute__ ((packed));

  struct SDTHeader {
    char Signature[4];
    uint32_t Length;
    uint8_t Revision;
    uint8_t Checksum;
    char OEMID[6];
    char OEMTableID[8];
    uint32_t OEMRevision;
    uint32_t CreatorID;
    uint32_t CreatorRevision;

    uint32_t sigint() const {
      return *(uint32_t*) Signature;
    }
  };

  struct MADTRecord {
    uint8_t type;
    uint8_t length;
    uint8_t data[0];
  };

  struct MADTHeader {

    SDTHeader  hdr;
    uintptr_t  lapic_addr;
    uint32_t   flags; // 1 = dual 8259 PICs
    MADTRecord record[0];
  };

  struct FACPHeader
  {
    SDTHeader sdt;
    uint32_t  unneded1;
    uint32_t* DSDT;
    uint8_t unneded2[48 - 44];
    uint32_t* SMI_CMD;
    uint8_t ACPI_ENABLE;
    uint8_t ACPI_DISABLE;
    uint8_t unneded3[64 - 54];
    uint32_t* PM1a_CNT_BLK;
    uint32_t* PM1b_CNT_BLK;
    uint8_t unneded4[89 - 72];
    uint8_t PM1_CNT_LEN;
  };

  struct AddressStructure
  {
    uint8_t  address_space_id; // 0 - system memory, 1 - system I/O
    uint8_t  register_bit_width;
    uint8_t  register_bit_offset;
    uint8_t  reserved;
    uint64_t address;
  };

  struct pci_vendor_t
  {
    uint16_t    ven_id;
    const char* ven_name;
  };

  struct HPET
  {
    uint8_t hardware_rev_id;
    uint8_t comparator_count :5;
    uint8_t counter_size     :1;
    uint8_t reserved         :1;
    uint8_t legacy_replacem  :1;
    pci_vendor_t pci_vendor_id;
    AddressStructure address;
    uint8_t hpet_number;
    uint16_t minimum_tick;
    uint8_t page_protection;
  } __attribute__((packed));

  uint64_t ACPI::time() {
    return 0;
  }

  void ACPI::begin(const void* addr) {

    auto* rdsp = (RSDPDescriptor20*) addr;
    INFO("ACPI", "Reading headers");
    INFO2("OEM: %.*s Rev. %u",
        6, rdsp->rdsp10.OEMID, rdsp->rdsp10.Revision);

    auto* rsdt = (SDTHeader*) rdsp->rdsp10.RsdtAddress;
    // verify Root SDT
    if (!checksum((char*) rsdt, rsdt->Length)) {
      printf("ACPI: SDT failed checksum!");
      panic("SDT checksum failed");
    }

    // walk through system description table headers
    // remember the interesting ones, and count CPUs
    walk_sdts((char*) rsdt);
  }

  constexpr uint32_t bake(char a, char b , char c, char d) {
    return a | (b << 8) | (c << 16) | (d << 24);
  }

  void ACPI::walk_sdts(const char* addr)
  {
    // find total number of SDTs
    auto* rsdt = (SDTHeader*) addr;
    int  total = (rsdt->Length - sizeof(SDTHeader)) / 4;
    // go past rsdt
    addr += sizeof(SDTHeader);

    // parse all tables
    constexpr uint32_t APIC_t = bake('A', 'P', 'I', 'C');
    constexpr uint32_t HPET_t = bake('H', 'P', 'E', 'T');
    constexpr uint32_t FACP_t = bake('F', 'A', 'C', 'P');

    while (total) {
      // convert *addr to SDT-address
      auto sdt_ptr = *(intptr_t*) addr;
      // create SDT pointer
      auto* sdt = (SDTHeader*) sdt_ptr;
      // find out which SDT it is
      switch (sdt->sigint()) {
      case APIC_t:
        debug("APIC found: P=%p L=%u\n", sdt, sdt->Length);
        walk_madt((char*) sdt);
        break;
      case HPET_t:
        debug("HPET found: P=%p L=%u\n", sdt, sdt->Length);
        this->hpet_base = sdt_ptr + sizeof(SDTHeader);
        break;
      case FACP_t:
        debug("FACP found: P=%p L=%u\n", sdt, sdt->Length);
        walk_facp((char*) sdt);
        break;
      default:
        debug("Signature: %.*s (u=%u)\n", 4, sdt->Signature, sdt->sigint());
      }

      addr += 4; total--;
    }
    debug("Finished walking SDTs\n");
  }

  void ACPI::walk_madt(const char* addr)
  {
    auto* hdr = (MADTHeader*) addr;
    INFO("ACPI", "Reading APIC information");
    // the base address for APIC registers
    INFO2("LAPIC base: 0x%x  (flags: 0x%x)",
        hdr->lapic_addr, hdr->flags);
    this->apic_base = hdr->lapic_addr;

    // the length remaining after MADT header
    int len = hdr->hdr.Length - sizeof(MADTHeader);
    // start walking
    const char* ptr = (char*) hdr->record;

    while (len) {
      auto* rec = (MADTRecord*) ptr;
      switch (rec->type) {
      case 0:
        {
          auto& lapic = *(LAPIC*) rec;
          lapics.push_back(lapic);
          INFO2("-> CPU %u ID %u  (flags=0x%x)",
              lapic.cpu, lapic.id, lapic.flags);
        }
        break;
      case 1:
        {
          auto& ioapic = *(IOAPIC*) rec;
          ioapics.push_back(ioapic);
          INFO2("I/O APIC %u   ADDR 0x%x  INTR 0x%x",
              ioapic.id, ioapic.addr_base, ioapic.intr_base);
        }
        break;
      case 2:
        {
          auto& redirect = *(override_t*) rec;
          overrides.push_back(redirect);
          INFO2("IRQ redirect for bus %u from IRQ %u to VEC %u",
              redirect.bus_source, redirect.irq_source, redirect.global_intr);
        }
        break;
      default:
        debug("Unrecognized ACPI MADT type: %u\n", rec->type);
      }
      // decrease length as we go
      len -= rec->length;
      // go to next entry
      ptr += rec->length;
    }
  }

  void ACPI::walk_facp(const char* addr)
  {
    auto* facp = (FACPHeader*) addr;
    // verify DSDT
    constexpr uint32_t DSDT_t = bake('D', 'S', 'D', 'T');
    assert(*facp->DSDT == DSDT_t);

    /// big thanks to kaworu from OSdev.org forums for algo
    /// http://forum.osdev.org/viewtopic.php?t=16990
    char* S5Addr = (char*) facp->DSDT + 36; // skip header
    int dsdtLength = ((SDTHeader*) facp->DSDT)->Length;
    // some ting wong
    dsdtLength *= 2;
    while (dsdtLength-- > 0)
    {
      if (memcmp(S5Addr, "_S5_", 4) == 0)
           break;
      S5Addr++;
    }
    // check if \_S5 was found
    if (dsdtLength <= 0) {
      printf("WARNING: _S5 not present in ACPI\n");
      return;
    }
    // check for valid AML structure
    if ( ( *(S5Addr-1) == 0x08 || ( *(S5Addr-2) == 0x08 && *(S5Addr-1) == '\\') ) && *(S5Addr+4) == 0x12 )
    {
       S5Addr += 5;
       S5Addr += ((*S5Addr &0xC0)>>6) + 2;   // calculate PkgLength size

       if (*S5Addr == 0x0A)
          S5Addr++;   // skip byteprefix
       SLP_TYPa = *(S5Addr) << 10;
       S5Addr++;

       if (*S5Addr == 0x0A)
          S5Addr++;   // skip byteprefix
       SLP_TYPb = *(S5Addr)<<10;

       SMI_CMD = facp->SMI_CMD;

       ACPI_ENABLE = facp->ACPI_ENABLE;
       ACPI_DISABLE = facp->ACPI_DISABLE;

       PM1a_CNT = facp->PM1a_CNT_BLK;
       PM1b_CNT = facp->PM1b_CNT_BLK;

       PM1_CNT_LEN = facp->PM1_CNT_LEN;

       SLP_EN = 1<<13;
       SCI_EN = 1;

       debug("ACPI: Found shutdown information\n");
       return;

    } else {
       printf("WARNING: Failed to parse _S5 in ACPI\n");
    }
    // disable ACPI shutdown
    SCI_EN = 0;
  }

  bool ACPI::checksum(const char* addr, size_t size) const {

    const char* end = addr + size;
    uint8_t sum = 0;
    while (addr < end) {
      sum += *addr; addr++;
    }
    return sum == 0;
  }

  void ACPI::discover() {
    // "RSD PTR "
    const uint64_t sign = 0x2052545020445352;

    // guess at QEMU location of RDSP
    const auto* guess = (char*) 0xf6450;
    if (*(uint64_t*) guess == sign) {
      if (checksum(guess, sizeof(RSDPDescriptor))) {
        debug("Found ACPI located at QEMU-guess (%p)\n", guess);
        begin(guess);
        return;
      }
    }

    // search in BIOS area (below 1mb)
    const auto* addr = (char*) 0x000e0000;
    const auto* end  = (char*) 0x000fffff;
    debug("Looking for ACPI at %p\n", addr);

    while (addr < end) {

      if (*(uint64_t*) addr == sign) {
        // verify checksum of potential RDSP
        if (checksum(addr, sizeof(RSDPDescriptor))) {
          debug("Found ACPI located at %p\n", addr);
          begin(addr);
          return;
        }
      }
      addr++;
    }

    panic("ACPI RDST-search failed\n");
  }

  void ACPI::reboot()
  {
    ::reboot();
  }

  void ACPI::acpi_shutdown() {
    // check if shutdown enabled
    if (SCI_EN == 1) {
      // write shutdown commands
      hw::outw((uint32_t) PM1a_CNT, SLP_TYPa | SLP_EN);
      if (PM1b_CNT != 0)
        hw::outw((uint32_t) PM1b_CNT, SLP_TYPb | SLP_EN);
      printf("*** ACPI shutdown failed\n");
    }
  }

  __attribute__((noreturn))
  void ACPI::shutdown()
  {
    asm volatile("cli");

    // ACPI shutdown
    get().acpi_shutdown();

    // http://forum.osdev.org/viewtopic.php?t=16990
    hw::outw (0xB004, 0x2000);

    const char s[] = "Shutdown";
    const char *p;
    for (p = s; *p; p++)
      // magic code for bochs and qemu
      hw::outb (0x8900, *s);

    // VMWare poweroff when "gui.exitOnCLIHLT" is true
    printf("Shutdown failed :(\n");
    while (true) {
      asm volatile("cli; hlt" : : : "memory");
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Handler for HTTP requests.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_http.hxx"
#include "lb_instance.hxx"
#include "lb_connection.hxx"
#include "lb_config.hxx"
#include "lb_session.hxx"
#include "lb_cookie.hxx"
#include "lb_jvm_route.hxx"
#include "lb_headers.hxx"
#include "lb_log.hxx"
#include "ssl/ssl_filter.hxx"
#include "address_sticky.hxx"
#include "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_client.hxx"
#include "tcp_stock.hxx"
#include "tcp_balancer.hxx"
#include "header_writer.hxx"
#include "http_response.hxx"
#include "http_headers.hxx"
#include "stock/GetHandler.hxx"
#include "stock/Item.hxx"
#include "stock/Lease.hxx"
#include "access_log.hxx"
#include "strmap.hxx"
#include "failure.hxx"
#include "bulldog.h"
#include "pool.hxx"
#include "net/SocketAddress.hxx"
#include "istream/istream.hxx"
#include "istream/UnusedHoldPtr.hxx"
#include "gerrno.h"
#include "util/Cancellable.hxx"

#include <http/status.h>
#include <daemon/log.h>

struct LbRequest final
    : Cancellable, StockGetHandler, HttpResponseHandler {

    LbConnection &connection;
    const LbClusterConfig *cluster;

    TcpBalancer &balancer;

    HttpServerRequest &request;

    /**
     * The request body.
     */
    UnusedHoldIstreamPtr body;

    CancellablePointer cancel_ptr;

    StockItem *stock_item;

    unsigned new_cookie = 0;

    LbRequest(LbConnection &_connection, TcpBalancer &_balancer,
              HttpServerRequest &_request,
              CancellablePointer &_cancel_ptr)
        :connection(_connection),
         balancer(_balancer),
         request(_request),
         body(request.pool, request.body) {
        _cancel_ptr = *this;
    }

    void Destroy() {
        DeleteFromPool(request.pool, this);
    }

    /* virtual methods from class Cancellable */
    void Cancel() override {
        body.Clear();
        CancellablePointer c(std::move(cancel_ptr));
        Destroy();
        c.Cancel();
    }

    /* virtual methods from class StockGetHandler */
    void OnStockItemReady(StockItem &item) override;
    void OnStockItemError(GError *error) override;

    /* virtual methods from class HttpResponseHandler */
    void OnHttpResponse(http_status_t status, StringMap &&headers,
                        Istream *body) override;
    void OnHttpError(GError *error) override;
};

static bool
send_fallback(HttpServerRequest *request,
              const LbFallbackConfig *fallback)
{
    if (!fallback->location.empty()) {
        http_server_send_redirect(request, HTTP_STATUS_FOUND,
                                  fallback->location.c_str(), "Found");
        return true;
    } else if (!fallback->message.empty()) {
        /* custom status + error message */
        http_server_send_message(request, fallback->status,
                                 fallback->message.c_str());
        return true;
    } else
        return false;
}

/**
 * Generate a cookie for sticky worker selection.  Return only worker
 * numbers that are not known to be failing.  Returns 0 on total
 * failure.
 */
static unsigned
generate_cookie(const AddressList *list)
{
    assert(list->GetSize() >= 2);

    const unsigned first = lb_cookie_generate(list->GetSize());

    unsigned i = first;
    do {
        assert(i >= 1 && i <= list->GetSize());
        const SocketAddress address = list->addresses[i % list->GetSize()];
        if (failure_get_status(address) == FAILURE_OK &&
            bulldog_check(address.GetAddress(), address.GetSize()) &&
            !bulldog_is_fading(address.GetAddress(), address.GetSize()))
            return i;

        i = lb_cookie_next(list->GetSize(), i);
    } while (i != first);

    /* all nodes have failed */
    return first;
}

/**
 * Is the specified error a server failure, that justifies
 * blacklisting the server for a while?
 */
static bool
is_server_failure(GError *error)
{
    return error->domain == http_client_quark() &&
        error->code != HTTP_CLIENT_UNSPECIFIED;
}

/*
 * HTTP response handler
 *
 */

void
LbRequest::OnHttpResponse(http_status_t status, StringMap &&_headers,
                          Istream *response_body)
{
    HttpHeaders headers(std::move(_headers));

    if (request.method == HTTP_METHOD_HEAD)
        /* pass Content-Length, even though there is no response body
           (RFC 2616 14.13) */
        headers.MoveToBuffer("content-length");

    if (new_cookie != 0) {
        char buffer[64];
        /* "Discard" must be last, to work around an Android bug*/
        snprintf(buffer, sizeof(buffer),
                 "beng_lb_node=0-%x; HttpOnly; Path=/; Version=1; Discard",
                 new_cookie);

        headers.Write("cookie2", "$Version=\"1\"");
        headers.Write("set-cookie", buffer);
    }

    http_server_response(&request, status, std::move(headers), response_body);
    Destroy();
}

void
LbRequest::OnHttpError(GError *error)
{
    if (is_server_failure(error))
        failure_add(tcp_stock_item_get_address(*stock_item));

    lb_connection_log_gerror(2, &connection, "Error", error);

    if (!send_fallback(&request, &cluster->fallback)) {
        const char *msg = connection.listener.verbose_response
            ? error->message
            : "Server failure";

        http_server_send_message(&request, HTTP_STATUS_BAD_GATEWAY, msg);
    }

    g_error_free(error);
    Destroy();
}

/*
 * stock callback
 *
 */

void
LbRequest::OnStockItemReady(StockItem &item)
{
    stock_item = &item;

    const char *peer_subject = connection.ssl_filter != nullptr
        ? ssl_filter_get_peer_subject(connection.ssl_filter)
        : nullptr;
    const char *peer_issuer_subject = connection.ssl_filter != nullptr
        ? ssl_filter_get_peer_issuer_subject(connection.ssl_filter)
        : nullptr;

    auto &headers = request.headers;
    lb_forward_request_headers(request.pool, headers,
                               request.local_host_and_port,
                               request.remote_host,
                               peer_subject, peer_issuer_subject,
                               cluster->mangle_via);

    auto *lease = NewFromPool<StockItemLease>(request.pool, item);

    http_client_request(request.pool,
                        connection.instance.event_loop,
                        tcp_stock_item_get(item),
                        tcp_stock_item_get_domain(item) == AF_LOCAL
                        ? FdType::FD_SOCKET : FdType::FD_TCP,
                        *lease,
                        item.GetStockName(),
                        NULL, NULL,
                        request.method, request.uri,
                        HttpHeaders(std::move(headers)),
                        body.Steal(), true,
                        *this, cancel_ptr);
}

void
LbRequest::OnStockItemError(GError *error)
{
    lb_connection_log_gerror(2, &connection, "Connect error", error);

    body.Clear();

    if (!send_fallback(&request, &cluster->fallback)) {
        const char *msg = connection.listener.verbose_response
            ? error->message
            : "Connection failure";

        http_server_send_message(&request, HTTP_STATUS_BAD_GATEWAY,
                                 msg);
    }

    g_error_free(error);
}

/*
 * http connection handler
 *
 */

void
LbConnection::HandleHttpRequest(HttpServerRequest &request,
                                CancellablePointer &cancel_ptr)
{
    ++instance.http_request_counter;

    request_start_time = std::chrono::steady_clock::now();

    const auto &goto_ = listener.destination.FindRequestLeaf(request);
    if (goto_.status != http_status_t(0)) {
        http_server_simple_response(request, goto_.status, nullptr, nullptr);
        return;
    }

    const auto request2 =
        NewFromPool<LbRequest>(request.pool,
                               *this, *instance.tcp_balancer,
                               request, cancel_ptr);
    const auto *cluster = request2->cluster = goto_.cluster;

    SocketAddress bind_address = SocketAddress::Null();
    const bool transparent_source = cluster->transparent_source;
    if (transparent_source) {
        bind_address = request.remote_address;

        /* reset the port to 0 to allow the kernel to choose one */
        if (bind_address.GetFamily() == AF_INET) {
            struct sockaddr_in *s_in = (struct sockaddr_in *)
                p_memdup(&request.pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        } else if (bind_address.GetFamily() == AF_INET6) {
            struct sockaddr_in6 *s_in = (struct sockaddr_in6 *)
                p_memdup(&request.pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin6_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        }
    }

    if (cluster->HasZeroConf()) {
        /* TODO: generalize the Zeroconf code, implement sticky */

        auto *cluster2 = instance.clusters.Find(cluster->name);
        if (cluster2 == nullptr) {
            http_server_send_message(&request,
                                     HTTP_STATUS_INTERNAL_SERVER_ERROR,
                                     "Zeroconf cluster not found");
            return;
        }

        const auto member = cluster2->Pick();
        if (member.first == nullptr) {
            http_server_send_message(&request,
                                     HTTP_STATUS_INTERNAL_SERVER_ERROR,
                                     "Zeroconf cluster is empty");
            return;
        }

        assert(member.second.IsDefined());

        tcp_stock_get(instance.tcp_stock, &request.pool, member.first,
                      transparent_source, bind_address,
                      member.second,
                      20,
                      *request2, request2->cancel_ptr);

        return;
    }

    /* prepare for the balancer */

    unsigned session_sticky = 0;
    switch (cluster->address_list.sticky_mode) {
    case StickyMode::NONE:
    case StickyMode::FAILOVER:
        /* these modes require no preparation; they are handled
           completely by balancer_get() */
        break;

    case StickyMode::SOURCE_IP:
        /* calculate session_sticky from remote address */
        session_sticky = socket_address_sticky(request.remote_address);
        break;

    case StickyMode::SESSION_MODULO:
        /* calculate session_sticky from beng-proxy session id */
        session_sticky = lb_session_get(request.headers,
                                        cluster->session_cookie.c_str());
        break;

    case StickyMode::COOKIE:
        /* calculate session_sticky from beng-lb cookie */
        session_sticky = lb_cookie_get(request.headers);
        if (session_sticky == 0)
            request2->new_cookie = session_sticky =
                generate_cookie(&cluster->address_list);

        break;

    case StickyMode::JVM_ROUTE:
        /* calculate session_sticky from JSESSIONID cookie suffix */
        session_sticky = lb_jvm_route_get(request.headers, *cluster);
        break;
    }

    tcp_balancer_get(request2->balancer, request.pool,
                     transparent_source,
                     bind_address,
                     session_sticky,
                     cluster->address_list,
                     20,
                     *request2, request2->cancel_ptr);
}

void
LbConnection::LogHttpRequest(HttpServerRequest &request,
                             http_status_t status, int64_t length,
                             uint64_t bytes_received, uint64_t bytes_sent)
{
    access_log(&request, nullptr,
               request.headers.Get("referer"),
               request.headers.Get("user-agent"),
               status, length,
               bytes_received, bytes_sent,
               std::chrono::steady_clock::now() - request_start_time);
}

void
LbConnection::HttpConnectionError(GError *error)
{
    int level = 2;

    if (error->domain == errno_quark() && error->code == ECONNRESET)
        level = 4;

    lb_connection_log_gerror(level, this, "Error", error);
    g_error_free(error);

    assert(http != nullptr);
    http = nullptr;

    lb_connection_remove(this);
}

void
LbConnection::HttpConnectionClosed()
{
    assert(http != nullptr);
    http = nullptr;

    lb_connection_remove(this);
}
<commit_msg>lb_http: use http_server_simple_response() in send_fallback()<commit_after>/*
 * Handler for HTTP requests.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_http.hxx"
#include "lb_instance.hxx"
#include "lb_connection.hxx"
#include "lb_config.hxx"
#include "lb_session.hxx"
#include "lb_cookie.hxx"
#include "lb_jvm_route.hxx"
#include "lb_headers.hxx"
#include "lb_log.hxx"
#include "ssl/ssl_filter.hxx"
#include "address_sticky.hxx"
#include "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_client.hxx"
#include "tcp_stock.hxx"
#include "tcp_balancer.hxx"
#include "header_writer.hxx"
#include "http_response.hxx"
#include "http_headers.hxx"
#include "stock/GetHandler.hxx"
#include "stock/Item.hxx"
#include "stock/Lease.hxx"
#include "access_log.hxx"
#include "strmap.hxx"
#include "failure.hxx"
#include "bulldog.h"
#include "pool.hxx"
#include "net/SocketAddress.hxx"
#include "istream/istream.hxx"
#include "istream/UnusedHoldPtr.hxx"
#include "gerrno.h"
#include "util/Cancellable.hxx"

#include <http/status.h>
#include <daemon/log.h>

struct LbRequest final
    : Cancellable, StockGetHandler, HttpResponseHandler {

    LbConnection &connection;
    const LbClusterConfig *cluster;

    TcpBalancer &balancer;

    HttpServerRequest &request;

    /**
     * The request body.
     */
    UnusedHoldIstreamPtr body;

    CancellablePointer cancel_ptr;

    StockItem *stock_item;

    unsigned new_cookie = 0;

    LbRequest(LbConnection &_connection, TcpBalancer &_balancer,
              HttpServerRequest &_request,
              CancellablePointer &_cancel_ptr)
        :connection(_connection),
         balancer(_balancer),
         request(_request),
         body(request.pool, request.body) {
        _cancel_ptr = *this;
    }

    void Destroy() {
        DeleteFromPool(request.pool, this);
    }

    /* virtual methods from class Cancellable */
    void Cancel() override {
        body.Clear();
        CancellablePointer c(std::move(cancel_ptr));
        Destroy();
        c.Cancel();
    }

    /* virtual methods from class StockGetHandler */
    void OnStockItemReady(StockItem &item) override;
    void OnStockItemError(GError *error) override;

    /* virtual methods from class HttpResponseHandler */
    void OnHttpResponse(http_status_t status, StringMap &&headers,
                        Istream *body) override;
    void OnHttpError(GError *error) override;
};

static bool
send_fallback(HttpServerRequest *request,
              const LbFallbackConfig *fallback)
{
    if (!fallback->location.empty()) {
        http_server_simple_response(*request, HTTP_STATUS_FOUND,
                                    fallback->location.c_str(), nullptr);
        return true;
    } else if (!fallback->message.empty()) {
        /* custom status + error message */
        http_server_simple_response(*request, fallback->status, nullptr,
                                    fallback->message.c_str());
        return true;
    } else
        return false;
}

/**
 * Generate a cookie for sticky worker selection.  Return only worker
 * numbers that are not known to be failing.  Returns 0 on total
 * failure.
 */
static unsigned
generate_cookie(const AddressList *list)
{
    assert(list->GetSize() >= 2);

    const unsigned first = lb_cookie_generate(list->GetSize());

    unsigned i = first;
    do {
        assert(i >= 1 && i <= list->GetSize());
        const SocketAddress address = list->addresses[i % list->GetSize()];
        if (failure_get_status(address) == FAILURE_OK &&
            bulldog_check(address.GetAddress(), address.GetSize()) &&
            !bulldog_is_fading(address.GetAddress(), address.GetSize()))
            return i;

        i = lb_cookie_next(list->GetSize(), i);
    } while (i != first);

    /* all nodes have failed */
    return first;
}

/**
 * Is the specified error a server failure, that justifies
 * blacklisting the server for a while?
 */
static bool
is_server_failure(GError *error)
{
    return error->domain == http_client_quark() &&
        error->code != HTTP_CLIENT_UNSPECIFIED;
}

/*
 * HTTP response handler
 *
 */

void
LbRequest::OnHttpResponse(http_status_t status, StringMap &&_headers,
                          Istream *response_body)
{
    HttpHeaders headers(std::move(_headers));

    if (request.method == HTTP_METHOD_HEAD)
        /* pass Content-Length, even though there is no response body
           (RFC 2616 14.13) */
        headers.MoveToBuffer("content-length");

    if (new_cookie != 0) {
        char buffer[64];
        /* "Discard" must be last, to work around an Android bug*/
        snprintf(buffer, sizeof(buffer),
                 "beng_lb_node=0-%x; HttpOnly; Path=/; Version=1; Discard",
                 new_cookie);

        headers.Write("cookie2", "$Version=\"1\"");
        headers.Write("set-cookie", buffer);
    }

    http_server_response(&request, status, std::move(headers), response_body);
    Destroy();
}

void
LbRequest::OnHttpError(GError *error)
{
    if (is_server_failure(error))
        failure_add(tcp_stock_item_get_address(*stock_item));

    lb_connection_log_gerror(2, &connection, "Error", error);

    if (!send_fallback(&request, &cluster->fallback)) {
        const char *msg = connection.listener.verbose_response
            ? error->message
            : "Server failure";

        http_server_send_message(&request, HTTP_STATUS_BAD_GATEWAY, msg);
    }

    g_error_free(error);
    Destroy();
}

/*
 * stock callback
 *
 */

void
LbRequest::OnStockItemReady(StockItem &item)
{
    stock_item = &item;

    const char *peer_subject = connection.ssl_filter != nullptr
        ? ssl_filter_get_peer_subject(connection.ssl_filter)
        : nullptr;
    const char *peer_issuer_subject = connection.ssl_filter != nullptr
        ? ssl_filter_get_peer_issuer_subject(connection.ssl_filter)
        : nullptr;

    auto &headers = request.headers;
    lb_forward_request_headers(request.pool, headers,
                               request.local_host_and_port,
                               request.remote_host,
                               peer_subject, peer_issuer_subject,
                               cluster->mangle_via);

    auto *lease = NewFromPool<StockItemLease>(request.pool, item);

    http_client_request(request.pool,
                        connection.instance.event_loop,
                        tcp_stock_item_get(item),
                        tcp_stock_item_get_domain(item) == AF_LOCAL
                        ? FdType::FD_SOCKET : FdType::FD_TCP,
                        *lease,
                        item.GetStockName(),
                        NULL, NULL,
                        request.method, request.uri,
                        HttpHeaders(std::move(headers)),
                        body.Steal(), true,
                        *this, cancel_ptr);
}

void
LbRequest::OnStockItemError(GError *error)
{
    lb_connection_log_gerror(2, &connection, "Connect error", error);

    body.Clear();

    if (!send_fallback(&request, &cluster->fallback)) {
        const char *msg = connection.listener.verbose_response
            ? error->message
            : "Connection failure";

        http_server_send_message(&request, HTTP_STATUS_BAD_GATEWAY,
                                 msg);
    }

    g_error_free(error);
}

/*
 * http connection handler
 *
 */

void
LbConnection::HandleHttpRequest(HttpServerRequest &request,
                                CancellablePointer &cancel_ptr)
{
    ++instance.http_request_counter;

    request_start_time = std::chrono::steady_clock::now();

    const auto &goto_ = listener.destination.FindRequestLeaf(request);
    if (goto_.status != http_status_t(0)) {
        http_server_simple_response(request, goto_.status, nullptr, nullptr);
        return;
    }

    const auto request2 =
        NewFromPool<LbRequest>(request.pool,
                               *this, *instance.tcp_balancer,
                               request, cancel_ptr);
    const auto *cluster = request2->cluster = goto_.cluster;

    SocketAddress bind_address = SocketAddress::Null();
    const bool transparent_source = cluster->transparent_source;
    if (transparent_source) {
        bind_address = request.remote_address;

        /* reset the port to 0 to allow the kernel to choose one */
        if (bind_address.GetFamily() == AF_INET) {
            struct sockaddr_in *s_in = (struct sockaddr_in *)
                p_memdup(&request.pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        } else if (bind_address.GetFamily() == AF_INET6) {
            struct sockaddr_in6 *s_in = (struct sockaddr_in6 *)
                p_memdup(&request.pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin6_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        }
    }

    if (cluster->HasZeroConf()) {
        /* TODO: generalize the Zeroconf code, implement sticky */

        auto *cluster2 = instance.clusters.Find(cluster->name);
        if (cluster2 == nullptr) {
            http_server_send_message(&request,
                                     HTTP_STATUS_INTERNAL_SERVER_ERROR,
                                     "Zeroconf cluster not found");
            return;
        }

        const auto member = cluster2->Pick();
        if (member.first == nullptr) {
            http_server_send_message(&request,
                                     HTTP_STATUS_INTERNAL_SERVER_ERROR,
                                     "Zeroconf cluster is empty");
            return;
        }

        assert(member.second.IsDefined());

        tcp_stock_get(instance.tcp_stock, &request.pool, member.first,
                      transparent_source, bind_address,
                      member.second,
                      20,
                      *request2, request2->cancel_ptr);

        return;
    }

    /* prepare for the balancer */

    unsigned session_sticky = 0;
    switch (cluster->address_list.sticky_mode) {
    case StickyMode::NONE:
    case StickyMode::FAILOVER:
        /* these modes require no preparation; they are handled
           completely by balancer_get() */
        break;

    case StickyMode::SOURCE_IP:
        /* calculate session_sticky from remote address */
        session_sticky = socket_address_sticky(request.remote_address);
        break;

    case StickyMode::SESSION_MODULO:
        /* calculate session_sticky from beng-proxy session id */
        session_sticky = lb_session_get(request.headers,
                                        cluster->session_cookie.c_str());
        break;

    case StickyMode::COOKIE:
        /* calculate session_sticky from beng-lb cookie */
        session_sticky = lb_cookie_get(request.headers);
        if (session_sticky == 0)
            request2->new_cookie = session_sticky =
                generate_cookie(&cluster->address_list);

        break;

    case StickyMode::JVM_ROUTE:
        /* calculate session_sticky from JSESSIONID cookie suffix */
        session_sticky = lb_jvm_route_get(request.headers, *cluster);
        break;
    }

    tcp_balancer_get(request2->balancer, request.pool,
                     transparent_source,
                     bind_address,
                     session_sticky,
                     cluster->address_list,
                     20,
                     *request2, request2->cancel_ptr);
}

void
LbConnection::LogHttpRequest(HttpServerRequest &request,
                             http_status_t status, int64_t length,
                             uint64_t bytes_received, uint64_t bytes_sent)
{
    access_log(&request, nullptr,
               request.headers.Get("referer"),
               request.headers.Get("user-agent"),
               status, length,
               bytes_received, bytes_sent,
               std::chrono::steady_clock::now() - request_start_time);
}

void
LbConnection::HttpConnectionError(GError *error)
{
    int level = 2;

    if (error->domain == errno_quark() && error->code == ECONNRESET)
        level = 4;

    lb_connection_log_gerror(level, this, "Error", error);
    g_error_free(error);

    assert(http != nullptr);
    http = nullptr;

    lb_connection_remove(this);
}

void
LbConnection::HttpConnectionClosed()
{
    assert(http != nullptr);
    http = nullptr;

    lb_connection_remove(this);
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include "demoloop.h"
#include "graphics/2d_primitives.h"
#include "hsl.h"
using namespace std;
using namespace demoloop;

const uint32_t NUM_VERTS = 3;

float t = 0;
const float CYCLE_LENGTH = 6;

template <
  typename T,
  typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type
> constexpr T mix(T const &a, T const &b, const float &ratio) {
  return a * (1.0f - ratio) + b * ratio;
}

class Loop021 : public Demoloop {
public:
  Loop021() : Demoloop(150, 150, 150), RADIUS(height / 6) {
    glDisable(GL_DEPTH_TEST);

    float phi = 0.0f;
    const float interval = DEMOLOOP_M_PI * 2 / NUM_VERTS;

    for (uint32_t i = 0; i < (NUM_VERTS * 2) - 1; i+=2, phi += interval) {
      vertices[i].x = RADIUS * cosf(phi);
      vertices[i].y = RADIUS * sinf(phi);
      vertices[i].z = 0;

      vertices[i + 1].x = RADIUS * cosf(phi + interval);
      vertices[i + 1].y = RADIUS * sinf(phi + interval);
      vertices[i + 1].z = 0;
    }

    gl.getTransform() = glm::translate(gl.getTransform(), glm::vec3(width / 2, height / 2, 0));
  }

  void Update(float dt) {
    t += dt;

    float cycle = fmod(t, CYCLE_LENGTH);
    float cycle_ratio = cycle / CYCLE_LENGTH;

    setColor(255, 255, 255);
    gl.lines(vertices, (NUM_VERTS * 2));

    const float interval = DEMOLOOP_M_PI * 2 / NUM_VERTS;

    const glm::vec3 twoDAxis = {0, 0, 1};

    for (uint32_t i = 0; i < NUM_VERTS; ++i) {
      float internal_cycle_ratio = fmod(cycle_ratio * NUM_VERTS, 1);
      int current_vertex = fmod(floor(i + cycle_ratio * NUM_VERTS), NUM_VERTS);
      float x1 = cosf(current_vertex * interval) * RADIUS;
      float y1 = sinf(current_vertex * interval) * RADIUS;

      float x2 = cosf((current_vertex + 1) * interval) * RADIUS;
      float y2 = sinf((current_vertex + 1) * interval) * RADIUS;

      float x = mix(x1, x2, internal_cycle_ratio);
      float y = mix(y1, y2, internal_cycle_ratio);

      glm::mat4 m;
      m = glm::translate(m, {x, y, 0});
      m = glm::rotate(m, (float)DEMOLOOP_M_PI * cycle_ratio * 2, twoDAxis);
      m = glm::rotate(m, i * (float)DEMOLOOP_M_PI * 2 / NUM_VERTS, twoDAxis);
      m = glm::translate(m, {RADIUS, 0, 0});
      m = glm::rotate(m, (float)DEMOLOOP_M_PI, twoDAxis);

      setColor(hsl2rgb(static_cast<float>(i) / NUM_VERTS, 1, 0.5));
      gl.lines(vertices, (NUM_VERTS * 2), m);
    }
  }
private:
  const float RADIUS;
  Vertex vertices[NUM_VERTS * 2];
};

int main(int, char**){
  Loop021 loop;
  loop.Run();

  return 0;
}
<commit_msg>baseline functional tiling<commit_after>#include <array>
#include <vector>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include "demoloop.h"
#include "hsl.h"
#include "math_helpers.h"
using namespace std;
using namespace demoloop;

const glm::vec3 twoDAxis = {0, 0, 1};
float t = 0;
const float CYCLE_LENGTH = 5;
const uint32_t MAX_VERTS = 12;

float randFloat() {
  return static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
}

template<uint N>
typename std::enable_if<N >= 3, array<Vertex, N>>::type
polygonVertices(const float &radius) {
  array<Vertex, N> r;

  const float interval = DEMOLOOP_M_PI * 2 / N;
  float phi = 0.0f;
  // float phi = rotationOffset(N);
  for (uint i = 0; i < N; ++i, phi += interval) {
    r[i].x = cosf(phi) * radius;
    r[i].y = sinf(phi) * radius;
    r[i].z = 0;

    RGB c = hsl2rgb(phi / (DEMOLOOP_M_PI * 2), 1, 0.5);
    r[i].r = c.r;
    r[i].g = c.g;
    r[i].b = c.b;
  }

  return r;
}

template<uint32_t N, uint32_t I = 3>
typename std::enable_if<I == N, void>::type
buffer(float, GLuint*) {}

template<uint32_t N, uint32_t I = 3>
typename std::enable_if<I < N, void>::type
buffer(float RADIUS, GLuint *vbos) {
  const array<Vertex, I> vertex_data = polygonVertices<I>(RADIUS);
  glBindBuffer(GL_ARRAY_BUFFER, vbos[I - 3]);
  glBufferData(GL_ARRAY_BUFFER, I * sizeof(Vertex), vertex_data.data(), GL_DYNAMIC_DRAW);
  buffer<N, I + 1>(RADIUS, vbos);
}

struct Polygon {
  uint32_t num_verts;
  glm::mat4 transform;
};

bool doShapesIntersect(const float radius, const Polygon &a, const Polygon &b) {
  static glm::vec3 scaleA;
  static glm::vec3 translationA;
  static glm::vec3 scaleB;
  static glm::vec3 translationB;

  static glm::quat rotation;
  static glm::vec3 skew;
  static glm::vec4 perspective;
  glm::decompose(a.transform, scaleA, rotation, translationA, skew, perspective);
  glm::decompose(b.transform, scaleB, rotation, translationB, skew, perspective);

  float x1 = translationA.x, y1 = translationA.y;
  float x2 = translationB.x, y2 = translationB.y;

  uint32_t v1 = a.num_verts, v2 = b.num_verts;
  float r1 = radius * scaleA.x * cosf(DEMOLOOP_M_PI / v1), r2 = radius * scaleB.x * cosf(DEMOLOOP_M_PI / v2);

  float dx = x1 - x2, dy = y1 - y2;
  float d = sqrt(dx * dx + dy * dy);

  bool inscriptions_overlap = d < (r1 + r2 - 0.1);
  return inscriptions_overlap;
}

bool intersectsAny(const float radius, const Polygon &shape, const vector<Polygon> &shapes) {
  for (const Polygon &other : shapes) {
    if (doShapesIntersect(radius, shape, other)) {
      return true;
    }
  }
  return false;
}

vector<vector<Polygon>> build(const float radius, const uint32_t num_layers) {
  vector<vector<Polygon>> tree(num_layers);
  vector<Polygon> all_shapes;

  {
    Polygon p = {3, glm::mat4()};
    tree[0].push_back(p);
    all_shapes.push_back(p);
  }

  for (uint32_t layer_index = 1; layer_index < num_layers; ++layer_index) {
    // printf("layer_index: %u\n", layer_index);
    vector<Polygon> &previous_layer = tree[layer_index - 1];
    vector<Polygon> &current_layer = tree[layer_index];

    Polygon previous_shape = previous_layer[0];
    uint32_t previous_vertex_count = previous_shape.num_verts;
    float previous_side_length = sinf(DEMOLOOP_M_PI / previous_vertex_count) * 2 * radius;
    // printf("previous_vertex_count: %u\n", previous_vertex_count);

    uint32_t current_vertex_count = layer_index + 3;
    current_vertex_count = randFloat() * (MAX_VERTS - 3) + 3;
    float current_side_length = sinf(DEMOLOOP_M_PI / current_vertex_count) * 2 * radius;
    // printf("current_vertex_count: %u\n", current_vertex_count);

    glm::vec3 scale;
    glm::quat rotation;
    glm::vec3 translation;
    glm::vec3 skew;
    glm::vec4 perspective;
    glm::decompose(previous_shape.transform, scale, rotation, translation, skew, perspective);

    float t = (DEMOLOOP_M_PI * 2) / previous_vertex_count;
    float inner_outer_ratio = previous_side_length / current_side_length;
    // printf("inner_outer_ratio: %f\n", inner_outer_ratio);

    float previous_distance = radius * cosf(DEMOLOOP_M_PI / previous_vertex_count);
    float current_distance = radius * cosf(DEMOLOOP_M_PI / current_vertex_count) * inner_outer_ratio;
    float d = previous_distance + current_distance;
    // printf("%f\n", d);

    float PI = DEMOLOOP_M_PI;

    for (uint32_t i = 0; i < previous_vertex_count; ++i) {
      for (const Polygon &previous_shape : previous_layer) {
        glm::mat4 transform = previous_shape.transform;
        transform = glm::rotate(transform, i * t, twoDAxis);
        transform = glm::rotate(transform, PI / previous_vertex_count, twoDAxis);
        transform = glm::translate(transform, {d, 0, 0});
        transform = glm::rotate(transform, PI / current_vertex_count + PI, twoDAxis);
        transform = glm::scale(transform, {inner_outer_ratio, inner_outer_ratio, 1});

        Polygon p = {current_vertex_count, transform};
        if (intersectsAny(radius, p, all_shapes) == false) {
          current_layer.push_back(p);
          all_shapes.push_back(p);
        }
      }
    }
    // printf("\n");
  }

  return tree;
}

class Loop021 : public Demoloop {
public:
  Loop021() : Demoloop(1280, 720, 150, 150, 150), RADIUS(height / 20) {
    glDisable(GL_DEPTH_TEST);

    glGenBuffers(MAX_VERTS - 3, vbos);
    buffer<MAX_VERTS>(RADIUS, vbos);

    auto start = std::chrono::high_resolution_clock::now();
    tree = build(RADIUS, 10);
    auto delta = std::chrono::high_resolution_clock::now() - start;
    printf("built in %f\n", std::chrono::duration_cast<std::chrono::duration<float>>(delta).count());
  }

  ~Loop021() {
    glDeleteBuffers(MAX_VERTS - 3, vbos);
  }

  void Update(float dt) {
    t += dt;

    float cycle = fmod(t, CYCLE_LENGTH);
    float cycle_ratio = cycle / CYCLE_LENGTH;

    float scale = 1-pow(sin(cycle_ratio * DEMOLOOP_M_PI), 2)*0.99;
    GL::TempTransform t1(gl);
    t1.get() = glm::scale(glm::translate(t1.get(),
      glm::vec3(width / 2, height / 2, 0)), glm::vec3(scale, scale, 1)
    );

    for(const vector<Polygon> &layer : tree) {
      for(const Polygon &p : layer) {
        gl.prepareDraw(p.transform);
        glBindBuffer(GL_ARRAY_BUFFER, vbos[p.num_verts - 3]);
        gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_COLOR);
        glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) offsetof(Vertex, x));
        glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (GLvoid*) offsetof(Vertex, r));
        gl.drawArrays(GL_TRIANGLE_FAN, 0, p.num_verts);
      }
    }

    // const uint32_t N = jmap(cycle_ratio, 0, 1, 3, MAX_VERTS);

    // gl.prepareDraw();
    // glBindBuffer(GL_ARRAY_BUFFER, vbos[N - 3]);
    // gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_COLOR);
    // glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) offsetof(Vertex, x));
    // glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (GLvoid*) offsetof(Vertex, r));
    // gl.drawArrays(GL_TRIANGLE_FAN, 0, N);
  }
private:
  const float RADIUS;
  GLuint vbos[MAX_VERTS - 3];
  vector<vector<Polygon>> tree;
};

int main(int, char**){
  Loop021 loop;
  loop.Run();

  return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <algorithm>
#include <iterator>
#include <map>

#include <fcntl.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <string.h>

#include "net_util.h"

namespace net {

#ifndef INET6_ADDRSTRLEN  /* for non IPv6 machines */
#define INET6_ADDRSTRLEN 46
#endif

bool ParseIPLiteralToNumber(const std::string& ip_literal,
                            IPAddressNumber* ip_number) {
  char buf[sizeof(struct in6_addr)];
  int size = sizeof(struct in_addr);
  int mode = AF_INET;
  if (ip_literal.find(':') != std::string::npos) {
    mode = AF_INET6;
    size = sizeof(struct in6_addr);
  }
  inet_pton(mode, ip_literal.c_str(), buf);
  for (int i = 0; i < size; i++) {
    (*ip_number)[i] = buf[i];
  }
  return true;
}

IPAddressNumber ConvertIPv4NumberToIPv6Number(
    const IPAddressNumber& ipv4_number) {
  // IPv4-mapped addresses are formed by:
  // <80 bits of zeros>  + <16 bits of ones> + <32-bit IPv4 address>.
  IPAddressNumber ipv6_number;
  ipv6_number.reserve(16);
  ipv6_number.insert(ipv6_number.end(), 10, 0);
  ipv6_number.push_back(0xFF);
  ipv6_number.push_back(0xFF);
  ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end());
  return ipv6_number;
}

bool ParseCIDRBlock(const std::string& cidr_literal,
                    IPAddressNumber* ip_number,
                    size_t* prefix_length_in_bits) {
  // We expect CIDR notation to match one of these two templates:
  //   <IPv4-literal> "/" <number of bits>
  //   <IPv6-literal> "/" <number of bits>

  std::vector<std::string> parts;
  unsigned int split = cidr_literal.find('/');
  if (split == std::string::npos)
    return false;
  parts.push_back(cidr_literal.substr(0, split));
  parts.push_back(cidr_literal.substr(split + 1));
  if (parts[1].find('/') != std::string::npos)
    return false;

  // Parse the IP address.
  if (!ParseIPLiteralToNumber(parts[0], ip_number))
    return false;

  // Parse the prefix length.
  int number_of_bits = atoi(parts[1].c_str());

  // Make sure the prefix length is in a valid range.
  if (number_of_bits < 0 ||
      number_of_bits > static_cast<int>(ip_number->size() * 8))
    return false;

  *prefix_length_in_bits = static_cast<size_t>(number_of_bits);
  return true;
}

bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number,
                           const IPAddressNumber& ip_prefix,
                           size_t prefix_length_in_bits) {
  // Both the input IP address and the prefix IP address should be
  // either IPv4 or IPv6.

  // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to
  // IPv6 addresses in order to do the comparison.
  if (ip_number.size() != ip_prefix.size()) {
    if (ip_number.size() == 4) {
      return IPNumberMatchesPrefix(ConvertIPv4NumberToIPv6Number(ip_number),
                                   ip_prefix, prefix_length_in_bits);
    }
    return IPNumberMatchesPrefix(ip_number,
                                 ConvertIPv4NumberToIPv6Number(ip_prefix),
                                 96 + prefix_length_in_bits);
  }

  // Otherwise we are comparing two IPv4 addresses, or two IPv6 addresses.
  // Compare all the bytes that fall entirely within the prefix.
  int num_entire_bytes_in_prefix = prefix_length_in_bits / 8;
  for (int i = 0; i < num_entire_bytes_in_prefix; ++i) {
    if (ip_number[i] != ip_prefix[i])
      return false;
  }

  // In case the prefix was not a multiple of 8, there will be 1 byte
  // which is only partially masked.
  int remaining_bits = prefix_length_in_bits % 8;
  if (remaining_bits != 0) {
    unsigned char mask = 0xFF << (8 - remaining_bits);
    int i = num_entire_bytes_in_prefix;
    if ((ip_number[i] & mask) != (ip_prefix[i] & mask))
      return false;
  }

  return true;
}

}  // namespace net
<commit_msg>Fix crash in ParseIpLiteralToNumber<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <algorithm>
#include <iterator>
#include <map>

#include <fcntl.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <string.h>

#include "net_util.h"

namespace net {

#ifndef INET6_ADDRSTRLEN  /* for non IPv6 machines */
#define INET6_ADDRSTRLEN 46
#endif

bool ParseIPLiteralToNumber(const std::string& ip_literal,
                            IPAddressNumber* ip_number) {
  char buf[sizeof(struct in6_addr)];
  int size = sizeof(struct in_addr);
  int mode = AF_INET;
  if (ip_literal.find(':') != std::string::npos) {
    mode = AF_INET6;
    size = sizeof(struct in6_addr);
  }
  if (inet_pton(mode, ip_literal.c_str(), buf) != 1) {
    return false;
  }
  ip_number->resize(size);
  for (int i = 0; i < size; i++) {
    (*ip_number)[i] = buf[i];
  }
  return true;
}

IPAddressNumber ConvertIPv4NumberToIPv6Number(
    const IPAddressNumber& ipv4_number) {
  // IPv4-mapped addresses are formed by:
  // <80 bits of zeros>  + <16 bits of ones> + <32-bit IPv4 address>.
  IPAddressNumber ipv6_number;
  ipv6_number.reserve(16);
  ipv6_number.insert(ipv6_number.end(), 10, 0);
  ipv6_number.push_back(0xFF);
  ipv6_number.push_back(0xFF);
  ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end());
  return ipv6_number;
}

bool ParseCIDRBlock(const std::string& cidr_literal,
                    IPAddressNumber* ip_number,
                    size_t* prefix_length_in_bits) {
  // We expect CIDR notation to match one of these two templates:
  //   <IPv4-literal> "/" <number of bits>
  //   <IPv6-literal> "/" <number of bits>

  std::vector<std::string> parts;
  unsigned int split = cidr_literal.find('/');
  if (split == std::string::npos)
    return false;
  parts.push_back(cidr_literal.substr(0, split));
  parts.push_back(cidr_literal.substr(split + 1));
  if (parts[1].find('/') != std::string::npos)
    return false;

  // Parse the IP address.
  if (!ParseIPLiteralToNumber(parts[0], ip_number))
    return false;

  // Parse the prefix length.
  int number_of_bits = atoi(parts[1].c_str());

  // Make sure the prefix length is in a valid range.
  if (number_of_bits < 0 ||
      number_of_bits > static_cast<int>(ip_number->size() * 8))
    return false;

  *prefix_length_in_bits = static_cast<size_t>(number_of_bits);
  return true;
}

bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number,
                           const IPAddressNumber& ip_prefix,
                           size_t prefix_length_in_bits) {
  // Both the input IP address and the prefix IP address should be
  // either IPv4 or IPv6.

  // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to
  // IPv6 addresses in order to do the comparison.
  if (ip_number.size() != ip_prefix.size()) {
    if (ip_number.size() == 4) {
      return IPNumberMatchesPrefix(ConvertIPv4NumberToIPv6Number(ip_number),
                                   ip_prefix, prefix_length_in_bits);
    }
    return IPNumberMatchesPrefix(ip_number,
                                 ConvertIPv4NumberToIPv6Number(ip_prefix),
                                 96 + prefix_length_in_bits);
  }

  // Otherwise we are comparing two IPv4 addresses, or two IPv6 addresses.
  // Compare all the bytes that fall entirely within the prefix.
  int num_entire_bytes_in_prefix = prefix_length_in_bits / 8;
  for (int i = 0; i < num_entire_bytes_in_prefix; ++i) {
    if (ip_number[i] != ip_prefix[i])
      return false;
  }

  // In case the prefix was not a multiple of 8, there will be 1 byte
  // which is only partially masked.
  int remaining_bits = prefix_length_in_bits % 8;
  if (remaining_bits != 0) {
    unsigned char mask = 0xFF << (8 - remaining_bits);
    int i = num_entire_bytes_in_prefix;
    if ((ip_number[i] & mask) != (ip_prefix[i] & mask))
      return false;
  }

  return true;
}

}  // namespace net
<|endoftext|>