<commit_before>#include "gridmap.hpp"
#include "gui.hpp"
#include "oddlib/lvlarchive.hpp"
#include "renderer.hpp"
#include "oddlib/path.hpp"
#include "oddlib/bits_factory.hpp"
#include "logger.hpp"
#include <cassert>
#include "sdl_raii.hpp"

Level::Level(GameData& gameData, IAudioController& /*audioController*/, FileSystem& fs)
    : mGameData(gameData), mFs(fs)
{

}

void Level::Update()
{
    if (mMap)
    {
        mMap->Update();
    }
}

void Level::Render(Renderer& rend, GuiContext& gui, int screenW, int screenH)
{
    RenderDebugPathSelection(rend, gui);
    if (mMap)
    {
        mMap->Render(rend, gui, screenW, screenH);
    }
}

void Level::RenderDebugPathSelection(Renderer& rend, GuiContext& gui)
{
    gui_begin_window(&gui, "Paths", v2i(300, 400));

    static std::vector<std::pair<std::string, const GameData::PathEntry*>> items;
    if (items.empty())
    {

        for (const auto& pathGroup : mGameData.Paths())
        {
            for (const auto& path : pathGroup.second)
            {
                items.push_back(std::make_pair(pathGroup.first + " " + std::to_string(path.mPathChunkId), &path));
            }
        }
    }

    for (const auto& item : items)
    {
        if (gui_button(&gui, item.first.c_str()))
        {
            const GameData::PathEntry* entry = item.second;
            const std::string baseLvlName = item.first.substr(0, 2); // R1, MI etc

            // Open the LVL
            const std::string lvlName = baseLvlName + ".LVL";

            auto chunkStream = mFs.ResourcePaths().OpenLvlFileChunkById(lvlName, baseLvlName + "PATH.BND", entry->mPathChunkId);
            if (chunkStream)
            {
                Oddlib::Path path(*chunkStream,
                    entry->mNumberOfCollisionItems,
                    entry->mObjectIndexTableOffset,
                    entry->mObjectDataOffset,
                    entry->mMapXSize,
                    entry->mMapYSize);
                mMap = std::make_unique<GridMap>(lvlName, path, mFs, rend);
            }
            else
            {
                LOG_ERROR("LVL or file in LVL not found");
            }
        }
    }

    gui_end_window(&gui);
}

GridScreen::GridScreen(const std::string& lvlName, const std::string& fileName, Renderer& rend)
    : mLvlName(lvlName),
      mFileName(fileName)
      , mTexHandle(0)
      , mRend(rend)
{

}

GridScreen::~GridScreen()
{
    mRend.destroyTexture(mTexHandle);
}

int GridScreen::getTexHandle(FileSystem& fs)
{
    if (!mTexHandle)
    {
        auto stream = fs.ResourcePaths().OpenLvlFileChunkByType(mLvlName, mFileName, Oddlib::MakeType('B', 'i', 't', 's'));
        if (stream)
        {
            auto bits = Oddlib::MakeBits(*stream);

            SDL_Surface* surf = bits->GetSurface();
            SDL_SurfacePtr converted;
            if (surf->format->format != SDL_PIXELFORMAT_RGB24)
            {
                converted.reset(SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_RGB24, 0));
                surf = converted.get();
            }

#if 0
            SDL_SurfacePtr scaled;
            { // Scale the surface
                assert(surf->w == 640);
                assert(surf->h == 240);

                scaled.reset(SDL_CreateRGBSurface(0, 640, 480, 24, 0xFF, 0x00FF, 0x0000FF, 0));
                uint8_t *src = static_cast<uint8_t*>(surf->pixels);
                uint8_t *dst = static_cast<uint8_t*>(scaled->pixels);
                for (int y = 0; y < scaled->h; ++y) {
                    const int src_y = min(y, surf->h);
                    for (int x = 0; x < scaled->w; ++x) {
                        const int src_x = min(x, surf->w);

                        const int dst_ix = x*3 + scaled->pitch*y;
                        const int src_ix = src_x*3 + surf->pitch*src_y;

                        dst[dst_ix + 0] = src[src_ix + 0];
                        dst[dst_ix + 1] = src[src_ix + 1];
                        dst[dst_ix + 2] = src[src_ix + 2];
                    }
                }
            }
            mTexHandle = mRend.createTexture(GL_RGB, scaled->w, scaled->h, GL_RGB, GL_UNSIGNED_BYTE, scaled->pixels, false);
#else
            mTexHandle = mRend.createTexture(GL_RGB, surf->w, surf->h, GL_RGB, GL_UNSIGNED_BYTE, surf->pixels, false);
#endif
        }
    }
    return mTexHandle;
}

GridMap::GridMap(const std::string& lvlName, Oddlib::Path& path, FileSystem& fs, Renderer& rend)
    : mFs(fs), mLvlName(lvlName)
{
    mScreens.resize(path.XSize());
    for (auto& col : mScreens)
    {
        col.resize(path.YSize());
    }

    for (Uint32 x = 0; x < path.XSize(); x++)
    {
        for (Uint32 y = 0; y < path.YSize(); y++)
        {
            mScreens[x][y] = std::make_unique<GridScreen>(mLvlName, path.CameraFileName(x,y), rend);
        }
    }
}

void GridMap::Update()
{

}

void GridMap::Render(Renderer& rend, GuiContext& gui, int /*screenW*/, int /*screenH*/)
{
    gui.next_window_pos = v2i(950, 50);
    gui_begin_window(&gui, "GridMap", v2i(200, 500));
    for (auto x = 0u; x < mScreens.size(); x++)
    {
        for (auto y = 0u; y < mScreens[0].size(); y++)
        {
            if (gui_button(&gui, gui_str(&gui, "cam_%i_%i|%s", (int)x, (int)y, mScreens[x][y]->FileName().c_str())))
            {
                mEditorScreenX = (int)x;
                mEditorScreenY = (int)y;
            }
        }
    }
    gui_end_window(&gui);

    if (mEditorScreenX >= static_cast<int>(mScreens.size()) || // Invalid x check
        (mEditorScreenX >= 0 && mEditorScreenY >= static_cast<int>(mScreens[mEditorScreenX].size()))) // Invalid y check
    {
        mEditorScreenX = mEditorScreenY = -1;
    }

    if (mEditorScreenX >= 0 && mEditorScreenY >= 0)
    {
        GridScreen *screen = mScreens[mEditorScreenX][mEditorScreenY].get();

        gui.next_window_pos = v2i(100, 100);

        V2i size = v2i(640, 480);
        gui_begin_window(&gui, "CAM", size);
        size = gui_window_client_size(&gui);

        rend.beginLayer(gui_layer(&gui));
        V2i pos = gui_turtle_pos(&gui);
        rend.drawQuad(screen->getTexHandle(mFs), 1.0f*pos.x, 1.0f*pos.y, 1.0f*size.x, 1.0f*size.y);
        rend.endLayer();

        gui_end_window(&gui);
    }
}

<commit_msg>Fix another lvl file case bug on Linux<commit_after>#include "gridmap.hpp"
#include "gui.hpp"
#include "oddlib/lvlarchive.hpp"
#include "renderer.hpp"
#include "oddlib/path.hpp"
#include "oddlib/bits_factory.hpp"
#include "logger.hpp"
#include <cassert>
#include "sdl_raii.hpp"

Level::Level(GameData& gameData, IAudioController& /*audioController*/, FileSystem& fs)
    : mGameData(gameData), mFs(fs)
{

}

void Level::Update()
{
    if (mMap)
    {
        mMap->Update();
    }
}

void Level::Render(Renderer& rend, GuiContext& gui, int screenW, int screenH)
{
    RenderDebugPathSelection(rend, gui);
    if (mMap)
    {
        mMap->Render(rend, gui, screenW, screenH);
    }
}

void Level::RenderDebugPathSelection(Renderer& rend, GuiContext& gui)
{
    gui_begin_window(&gui, "Paths", v2i(300, 400));

    static std::vector<std::pair<std::string, const GameData::PathEntry*>> items;
    if (items.empty())
    {

        for (const auto& pathGroup : mGameData.Paths())
        {
            for (const auto& path : pathGroup.second)
            {
                items.push_back(std::make_pair(pathGroup.first + " " + std::to_string(path.mPathChunkId), &path));
            }
        }
    }

    for (const auto& item : items)
    {
        if (gui_button(&gui, item.first.c_str()))
        {
            const GameData::PathEntry* entry = item.second;
            const std::string baseLvlNameUpper = item.first.substr(0, 2); // R1, MI etc
            std::string baseLvlNameLower = baseLvlNameUpper;

            // Open the LVL
            std::transform(baseLvlNameLower.begin(), baseLvlNameLower.end(), baseLvlNameLower.begin(), string_util::c_tolower);
            const std::string lvlName = baseLvlNameLower + ".lvl";

            LOG_INFO("Looking for LVL " << lvlName);

            auto chunkStream = mFs.ResourcePaths().OpenLvlFileChunkById(lvlName, baseLvlNameUpper + "PATH.BND", entry->mPathChunkId);
            if (chunkStream)
            {
                Oddlib::Path path(*chunkStream,
                    entry->mNumberOfCollisionItems,
                    entry->mObjectIndexTableOffset,
                    entry->mObjectDataOffset,
                    entry->mMapXSize,
                    entry->mMapYSize);
                mMap = std::make_unique<GridMap>(lvlName, path, mFs, rend);
            }
            else
            {
                LOG_ERROR("LVL or file in LVL not found");
            }
        }
    }

    gui_end_window(&gui);
}

GridScreen::GridScreen(const std::string& lvlName, const std::string& fileName, Renderer& rend)
    : mLvlName(lvlName),
      mFileName(fileName)
      , mTexHandle(0)
      , mRend(rend)
{

}

GridScreen::~GridScreen()
{
    mRend.destroyTexture(mTexHandle);
}

int GridScreen::getTexHandle(FileSystem& fs)
{
    if (!mTexHandle)
    {
        auto stream = fs.ResourcePaths().OpenLvlFileChunkByType(mLvlName, mFileName, Oddlib::MakeType('B', 'i', 't', 's'));
        if (stream)
        {
            auto bits = Oddlib::MakeBits(*stream);

            SDL_Surface* surf = bits->GetSurface();
            SDL_SurfacePtr converted;
            if (surf->format->format != SDL_PIXELFORMAT_RGB24)
            {
                converted.reset(SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_RGB24, 0));
                surf = converted.get();
            }

#if 0
            SDL_SurfacePtr scaled;
            { // Scale the surface
                assert(surf->w == 640);
                assert(surf->h == 240);

                scaled.reset(SDL_CreateRGBSurface(0, 640, 480, 24, 0xFF, 0x00FF, 0x0000FF, 0));
                uint8_t *src = static_cast<uint8_t*>(surf->pixels);
                uint8_t *dst = static_cast<uint8_t*>(scaled->pixels);
                for (int y = 0; y < scaled->h; ++y) {
                    const int src_y = min(y, surf->h);
                    for (int x = 0; x < scaled->w; ++x) {
                        const int src_x = min(x, surf->w);

                        const int dst_ix = x*3 + scaled->pitch*y;
                        const int src_ix = src_x*3 + surf->pitch*src_y;

                        dst[dst_ix + 0] = src[src_ix + 0];
                        dst[dst_ix + 1] = src[src_ix + 1];
                        dst[dst_ix + 2] = src[src_ix + 2];
                    }
                }
            }
            mTexHandle = mRend.createTexture(GL_RGB, scaled->w, scaled->h, GL_RGB, GL_UNSIGNED_BYTE, scaled->pixels, false);
#else
            mTexHandle = mRend.createTexture(GL_RGB, surf->w, surf->h, GL_RGB, GL_UNSIGNED_BYTE, surf->pixels, false);
#endif
        }
    }
    return mTexHandle;
}

GridMap::GridMap(const std::string& lvlName, Oddlib::Path& path, FileSystem& fs, Renderer& rend)
    : mFs(fs), mLvlName(lvlName)
{
    mScreens.resize(path.XSize());
    for (auto& col : mScreens)
    {
        col.resize(path.YSize());
    }

    for (Uint32 x = 0; x < path.XSize(); x++)
    {
        for (Uint32 y = 0; y < path.YSize(); y++)
        {
            mScreens[x][y] = std::make_unique<GridScreen>(mLvlName, path.CameraFileName(x,y), rend);
        }
    }
}

void GridMap::Update()
{

}

void GridMap::Render(Renderer& rend, GuiContext& gui, int /*screenW*/, int /*screenH*/)
{
    gui.next_window_pos = v2i(950, 50);
    gui_begin_window(&gui, "GridMap", v2i(200, 500));
    for (auto x = 0u; x < mScreens.size(); x++)
    {
        for (auto y = 0u; y < mScreens[0].size(); y++)
        {
            if (gui_button(&gui, gui_str(&gui, "cam_%i_%i|%s", (int)x, (int)y, mScreens[x][y]->FileName().c_str())))
            {
                mEditorScreenX = (int)x;
                mEditorScreenY = (int)y;
            }
        }
    }
    gui_end_window(&gui);

    if (mEditorScreenX >= static_cast<int>(mScreens.size()) || // Invalid x check
        (mEditorScreenX >= 0 && mEditorScreenY >= static_cast<int>(mScreens[mEditorScreenX].size()))) // Invalid y check
    {
        mEditorScreenX = mEditorScreenY = -1;
    }

    if (mEditorScreenX >= 0 && mEditorScreenY >= 0)
    {
        GridScreen *screen = mScreens[mEditorScreenX][mEditorScreenY].get();

        gui.next_window_pos = v2i(100, 100);

        V2i size = v2i(640, 480);
        gui_begin_window(&gui, "CAM", size);
        size = gui_window_client_size(&gui);

        rend.beginLayer(gui_layer(&gui));
        V2i pos = gui_turtle_pos(&gui);
        rend.drawQuad(screen->getTexHandle(mFs), 1.0f*pos.x, 1.0f*pos.y, 1.0f*size.x, 1.0f*size.y);
        rend.endLayer();

        gui_end_window(&gui);
    }
}

<|endoftext|>


<commit_before>/* FMINST -- simple FM instrument

   p0 = start time
   p1 = duration
   p2 = amp *
   p3 = frequency of carrier (Hz or oct.pc **)
   p4 = frequency of modulator (Hz or oct.pct)
   p5 = FM index low point
   p6 = FM index high point
   p7 = pan (in percent-to-left form: 0-1) [optional; default is 0]
   p8 = reference to wavetable [optional; if missing, must use gen 2 ***]
   p9 = index guide [optional; if missing, must use gen 3 ****]

   p2 (amplitude), p3 (carrier freq), p4 (modulator freq), p5 (index low),
   p6 (index high), p7 (pan) and p9 (index guide) can receive dynamic updates
   from a table or real-time control source.

   * If an old-style gen table 1 is present, its values will be multiplied
   by p2 (amp), even if the latter is dynamic.

   ** oct.pc format generally will not work as you expect for p3 and p4
   (osc freq) if the pfield changes dynamically.  Use Hz instead in that case.

   *** If p8 is missing, you must use an old-style gen table 2 for the
   oscillator waveform.

   **** If p9 is missing, you must use an old-style gen table 3 for the
   index guide function.

                                                rev for v4, JGG, 7/12/04
*/
#include <stdlib.h>
#include <stdio.h>
#include <ugens.h>
#include <Instrument.h>
#include <PField.h>
#include <Option.h>		// for fastUpdate
#include "FMINST.h"
#include <rt.h>
#include <rtdefs.h>

#define AMP_GEN_SLOT     1
#define WAVET_GEN_SLOT   2
#define INDEX_GEN_SLOT   3

FMINST::FMINST() : Instrument()
{
	indexenv = NULL;
	branch = 0;
}

FMINST::~FMINST()
{
	delete carosc;
	delete modosc;
}

// In fastUpdate mode, we skip doupdate() entirely, instead updating only amp,
// and only from a table.  The table can be a makegen or a PField table.  PField
// tables must be "flattened" using copytable if they are compound (e.g. passed
// through a PField filter or multiplied by a constant).  We use p[ampindex] as
// an amp multiplier, unless using a PField table, in which case there is no amp
// multiplier -- the p[ampindex] value is the first table value.   -JGG

void FMINST::initamp(float dur, double p[], int ampindex, int ampgenslot)
{
	fastUpdate = Option::fastUpdate();
	if (fastUpdate) {
		// Prefer PField table, otherwise makegen
		int tablen = 0;
		amptable = (double *) getPFieldTable(ampindex, &tablen);
		if (amptable)
			ampmult = 1.0f;
		else {
			ampmult = p[ampindex];
			amptable = floc(ampgenslot);
			if (amptable)
				tablen = fsize(ampgenslot);
		}
		if (amptable)
			tableset(SR, dur, tablen, amptabs);
		else
			amp = ampmult;
	}
	else {
		// NB: ampmult never used, first amp set in doupdate
		amptable = floc(ampgenslot);
		if (amptable) {
			int tablen = fsize(ampgenslot);
			tableset(SR, dur, tablen, amptabs);
		}
	}
}

int FMINST::init(double p[], int n_args)
{
	nargs = n_args;
	float outskip = p[0];
	float dur = p[1];

	if (rtsetoutput(outskip, dur, this) == -1)
		return DONT_SCHEDULE;
	if (outputChannels() > 2)
		return die("FMINST", "Can't handle more than 2 output channels.");

	carfreqraw = p[3];
	if (carfreqraw < 15.0)
		carfreq = cpspch(carfreqraw);
	else
		carfreq = carfreqraw;

	modfreqraw = p[4];
	if (modfreqraw < 15.0)
		modfreq = cpspch(modfreqraw);
	else
		modfreq = modfreqraw;

	double *wavetable = NULL;
	int tablelen = 0;
	if (n_args > 8) {      // handle table coming in as optional p8 TablePField
		wavetable = (double *) getPFieldTable(8, &tablelen);
	}
	if (wavetable == NULL) {
		wavetable = floc(WAVET_GEN_SLOT);
		if (wavetable == NULL)
			return die("FMINST", "Either use the wavetable pfield (p8) or make "
                    "an old-style gen function in slot %d.", WAVET_GEN_SLOT);
		tablelen = fsize(WAVET_GEN_SLOT);
	}

	carosc = new Ooscili(SR, carfreq, wavetable, tablelen);
	modosc = new Ooscili(SR, modfreq, wavetable, tablelen);

	if (n_args < 10) {		// no p9 guide PField, must use gen table
		indexenv = floc(INDEX_GEN_SLOT);
		if (indexenv == NULL)
			return die("FMINST", "Either use the index guide pfield (p9) or make "
                    "an old-style gen function in slot %d.", INDEX_GEN_SLOT);
		int len = fsize(INDEX_GEN_SLOT);
		tableset(SR, dur, len, indtabs);
	}

	initamp(dur, p, 2, 1);
	if (fastUpdate) {
		minindex = p[5];
		indexdiff = p[6] - minindex;
		pan = p[7];
	}

	return nSamps();
}

void FMINST::doupdate()
{
   double p[10];
   update(p, 10);

	amp = p[2];
	if (amptable)
		amp *= tablei(currentFrame(), amptable, amptabs);

	if (p[3] != carfreqraw) {
		carfreqraw = p[3];
		if (carfreqraw < 15.0)
			carfreq = cpspch(carfreqraw);
		else
			carfreq = carfreqraw;
	}
	if (p[4] != modfreqraw) {
		modfreqraw = p[4];
		if (modfreqraw < 15.0)
			modfreq = cpspch(modfreqraw);
		else
			modfreq = modfreqraw;
		modosc->setfreq(modfreq);
	}

	minindex = p[5];
	float maxindex = p[6];
	if (minindex > maxindex) {		// swap if wrong order
		float tmp = minindex;
		minindex = maxindex;
		maxindex = tmp;
	}
	float guide;
	if (nargs > 9)			// guide pfield is present
		guide = p[9];
	else                 // backward-compatible gen table
		guide = tablei(currentFrame(), indexenv, indtabs);
	float index = minindex + ((maxindex - minindex) * guide);
	peakdev = index * modfreq;

	pan = p[7];
}

int FMINST::run()
{
	const int nframes = framesToRun();
	for (int i = 0; i < nframes; i++) {
		if (--branch <= 0) {
			if (fastUpdate) {
				if (amptable)
					amp = ampmult * tablei(currentFrame(), amptable, amptabs);
				float guide = tablei(currentFrame(), indexenv, indtabs);
				peakdev = modfreq * (minindex + (indexdiff * guide));
			}
			else
				doupdate();
			branch = getSkip();
		}

		float out[2];

		float modsig = modosc->next() * peakdev;
		carosc->setfreq(carfreq + modsig);
		out[0] = carosc->next() * amp;

		if (outputChannels() == 2) {
			out[1] = (1.0 - pan) * out[0];
			out[0] *= pan;
		}

		rtaddout(out);
		increment();
	}
	return framesToRun();
}

Instrument *makeFMINST()
{
	FMINST *inst;

	inst = new FMINST();
	inst->set_bus_config("FMINST");

	return inst;
}

void rtprofile()
{
	RT_INTRO("FMINST",makeFMINST);
}

<commit_msg>Catch wavetable that's too large for Ooscili.<commit_after>/* FMINST -- simple FM instrument

   p0 = start time
   p1 = duration
   p2 = amp *
   p3 = frequency of carrier (Hz or oct.pc **)
   p4 = frequency of modulator (Hz or oct.pct)
   p5 = FM index low point
   p6 = FM index high point
   p7 = pan (in percent-to-left form: 0-1) [optional; default is 0]
   p8 = reference to wavetable [optional; if missing, must use gen 2 ***]
   p9 = index guide [optional; if missing, must use gen 3 ****]

   p2 (amplitude), p3 (carrier freq), p4 (modulator freq), p5 (index low),
   p6 (index high), p7 (pan) and p9 (index guide) can receive dynamic updates
   from a table or real-time control source.

   * If an old-style gen table 1 is present, its values will be multiplied
   by p2 (amp), even if the latter is dynamic.

   ** oct.pc format generally will not work as you expect for p3 and p4
   (osc freq) if the pfield changes dynamically.  Use Hz instead in that case.

   *** If p8 is missing, you must use an old-style gen table 2 for the
   oscillator waveform.

   **** If p9 is missing, you must use an old-style gen table 3 for the
   index guide function.

                                                rev for v4, JGG, 7/12/04
*/
#include <stdlib.h>
#include <stdio.h>
#include <ugens.h>
#include <Instrument.h>
#include <PField.h>
#include <Option.h>		// for fastUpdate
#include "FMINST.h"
#include <rt.h>
#include <rtdefs.h>

#define AMP_GEN_SLOT     1
#define WAVET_GEN_SLOT   2
#define INDEX_GEN_SLOT   3

FMINST::FMINST() : Instrument()
{
	indexenv = NULL;
	branch = 0;
}

FMINST::~FMINST()
{
	delete carosc;
	delete modosc;
}

// In fastUpdate mode, we skip doupdate() entirely, instead updating only amp,
// and only from a table.  The table can be a makegen or a PField table.  PField
// tables must be "flattened" using copytable if they are compound (e.g. passed
// through a PField filter or multiplied by a constant).  We use p[ampindex] as
// an amp multiplier, unless using a PField table, in which case there is no amp
// multiplier -- the p[ampindex] value is the first table value.   -JGG

void FMINST::initamp(float dur, double p[], int ampindex, int ampgenslot)
{
	fastUpdate = Option::fastUpdate();
	if (fastUpdate) {
		// Prefer PField table, otherwise makegen
		int tablen = 0;
		amptable = (double *) getPFieldTable(ampindex, &tablen);
		if (amptable)
			ampmult = 1.0f;
		else {
			ampmult = p[ampindex];
			amptable = floc(ampgenslot);
			if (amptable)
				tablen = fsize(ampgenslot);
		}
		if (amptable)
			tableset(SR, dur, tablen, amptabs);
		else
			amp = ampmult;
	}
	else {
		// NB: ampmult never used, first amp set in doupdate
		amptable = floc(ampgenslot);
		if (amptable) {
			int tablen = fsize(ampgenslot);
			tableset(SR, dur, tablen, amptabs);
		}
	}
}

int FMINST::init(double p[], int n_args)
{
	nargs = n_args;
	float outskip = p[0];
	float dur = p[1];

	if (rtsetoutput(outskip, dur, this) == -1)
		return DONT_SCHEDULE;
	if (outputChannels() > 2)
		return die("FMINST", "Can't handle more than 2 output channels.");

	carfreqraw = p[3];
	if (carfreqraw < 15.0)
		carfreq = cpspch(carfreqraw);
	else
		carfreq = carfreqraw;

	modfreqraw = p[4];
	if (modfreqraw < 15.0)
		modfreq = cpspch(modfreqraw);
	else
		modfreq = modfreqraw;

	double *wavetable = NULL;
	int tablelen = 0;
	if (n_args > 8) {      // handle table coming in as optional p8 TablePField
		wavetable = (double *) getPFieldTable(8, &tablelen);
	}
	if (wavetable == NULL) {
		wavetable = floc(WAVET_GEN_SLOT);
		if (wavetable == NULL)
			return die("FMINST", "Either use the wavetable pfield (p8) or make "
                    "an old-style gen function in slot %d.", WAVET_GEN_SLOT);
		tablelen = fsize(WAVET_GEN_SLOT);
	}
	if (tablelen > 32767)
		return die("FMINST", "wavetable must have fewer than 32768 samples.");

	carosc = new Ooscili(SR, carfreq, wavetable, tablelen);
	modosc = new Ooscili(SR, modfreq, wavetable, tablelen);

	if (n_args < 10) {		// no p9 guide PField, must use gen table
		indexenv = floc(INDEX_GEN_SLOT);
		if (indexenv == NULL)
			return die("FMINST", "Either use the index guide pfield (p9) or make "
                    "an old-style gen function in slot %d.", INDEX_GEN_SLOT);
		int len = fsize(INDEX_GEN_SLOT);
		tableset(SR, dur, len, indtabs);
	}

	initamp(dur, p, 2, 1);
	if (fastUpdate) {
		minindex = p[5];
		indexdiff = p[6] - minindex;
		pan = p[7];
	}

	return nSamps();
}

void FMINST::doupdate()
{
   double p[10];
   update(p, 10);

	amp = p[2];
	if (amptable)
		amp *= tablei(currentFrame(), amptable, amptabs);

	if (p[3] != carfreqraw) {
		carfreqraw = p[3];
		if (carfreqraw < 15.0)
			carfreq = cpspch(carfreqraw);
		else
			carfreq = carfreqraw;
	}
	if (p[4] != modfreqraw) {
		modfreqraw = p[4];
		if (modfreqraw < 15.0)
			modfreq = cpspch(modfreqraw);
		else
			modfreq = modfreqraw;
		modosc->setfreq(modfreq);
	}

	minindex = p[5];
	float maxindex = p[6];
	if (minindex > maxindex) {		// swap if wrong order
		float tmp = minindex;
		minindex = maxindex;
		maxindex = tmp;
	}
	float guide;
	if (nargs > 9)			// guide pfield is present
		guide = p[9];
	else                 // backward-compatible gen table
		guide = tablei(currentFrame(), indexenv, indtabs);
	float index = minindex + ((maxindex - minindex) * guide);
	peakdev = index * modfreq;

	pan = p[7];
}

int FMINST::run()
{
	const int nframes = framesToRun();
	for (int i = 0; i < nframes; i++) {
		if (--branch <= 0) {
			if (fastUpdate) {
				if (amptable)
					amp = ampmult * tablei(currentFrame(), amptable, amptabs);
				float guide = tablei(currentFrame(), indexenv, indtabs);
				peakdev = modfreq * (minindex + (indexdiff * guide));
			}
			else
				doupdate();
			branch = getSkip();
		}

		float out[2];

		float modsig = modosc->next() * peakdev;
		carosc->setfreq(carfreq + modsig);
		out[0] = carosc->next() * amp;

		if (outputChannels() == 2) {
			out[1] = (1.0 - pan) * out[0];
			out[0] *= pan;
		}

		rtaddout(out);
		increment();
	}
	return framesToRun();
}

Instrument *makeFMINST()
{
	FMINST *inst;

	inst = new FMINST();
	inst->set_bus_config("FMINST");

	return inst;
}

void rtprofile()
{
	RT_INTRO("FMINST",makeFMINST);
}

<|endoftext|>


<commit_before>#include "dsa_common.h"

#include "socket.h"

#include <boost/asio/connect.hpp>
#include "module/logger.h"

namespace dsa {

Websocket::Websocket(tcp::socket&& socket)
    : _is_secure_stream(false),
      _socket(std::move(socket)),
      _ws_stream(std::make_unique<websocket_stream>(std::move(_socket))) {
  _ws_stream->binary(true);
}

Websocket::Websocket(tcp::socket&& socket, ssl::context& ssl_context)
    : _is_secure_stream(true),
      _socket(std::move(socket)),
      _wss_stream(
          std::make_unique<websocket_ssl_stream>(_socket, ssl_context)) {
  _wss_stream->binary(true);
}

Websocket::~Websocket() = default;

void Websocket::async_connect(const string_& hostname,
                              tcp::resolver::results_type& resolved,
                              ConnectCallback&& handler) {
  _is_websocket = true;
  if (!is_secure_stream()) {
    // TODO: timeout

    boost::asio::async_connect(
        _ws_stream->next_layer(), resolved.begin(), resolved.end(),
        // capture shared_ptr to keep the instance
        // capture this to access protected member
        [ this, hostname, handler = std::move(handler) ](
            const boost::system::error_code& error,
            tcp::resolver::iterator) mutable {
          if (is_destroyed()) return;
          if (error != boost::system::errc::success) {
            handler(error);
            return;
          }
          // websocket handshake
          _ws_stream->async_handshake(hostname, "/", std::move(handler));
        });
  } else {
    std::lock_guard<std::mutex> lock(_mutex);
    boost::asio::async_connect(
        _wss_stream->next_layer().next_layer(), resolved.begin(),
        resolved.end(),
        // capture shared_ptr to keep the instance
        // capture this to access protected member
        [ this, hostname, handler = std::move(handler) ](
            const boost::system::error_code& error,
            tcp::resolver::iterator) mutable {
          if (is_destroyed()) return;
          if (error != boost::system::errc::success) {
            handler(error);
            return;
          }
          std::lock_guard<std::mutex> lock(_mutex);
          // ssl handshake
          _wss_stream->next_layer().async_handshake(ssl::stream_base::client, [
            this, hostname, handler = std::move(handler)
          ](boost::system::error_code ec) {
            if (is_destroyed()) return;
            if (ec != boost::system::errc::success) {
              handler(ec);
              return;
            }

            std::lock_guard<std::mutex> lock(_mutex);
            // websocket handshake
            _wss_stream->async_handshake(
                hostname, "/", std::move(handler));  // websocket handshake

          });  // ssl handshake

        });  // async_connect
  }
}

void Websocket::destroy() {
  if (_destroyed) return;

  _destroyed = true;

  if (_is_websocket) {
    auto on_close = [](const boost::system::error_code ec) {
      if (ec) {
        LOG_DEBUG(__FILENAME__,
                  LOG << "websocket close error: " << ec.message());
      }
    };
    // destroy websocket
    if (is_secure_stream()) {
      std::lock_guard<std::mutex> lock(_mutex);
      _wss_stream->async_close(websocket::close_code::normal, on_close);
    } else {
      _ws_stream->async_close(websocket::close_code::normal, on_close);
    }
  } else {
    // destroy http
    if (_is_secure_stream) {
      _wss_stream->next_layer().shutdown();
    } else if (_socket.is_open()) {
      _socket.shutdown(tcp::socket::shutdown_send);
    }
  }
}
}  // namespace dsa
<commit_msg>don't close a closed websocket<commit_after>#include "dsa_common.h"

#include "socket.h"

#include <boost/asio/connect.hpp>
#include "module/logger.h"

namespace dsa {

Websocket::Websocket(tcp::socket&& socket)
    : _is_secure_stream(false),
      _socket(std::move(socket)),
      _ws_stream(std::make_unique<websocket_stream>(std::move(_socket))) {
  _ws_stream->binary(true);
}

Websocket::Websocket(tcp::socket&& socket, ssl::context& ssl_context)
    : _is_secure_stream(true),
      _socket(std::move(socket)),
      _wss_stream(
          std::make_unique<websocket_ssl_stream>(_socket, ssl_context)) {
  _wss_stream->binary(true);
}

Websocket::~Websocket() = default;

void Websocket::async_connect(const string_& hostname,
                              tcp::resolver::results_type& resolved,
                              ConnectCallback&& handler) {
  _is_websocket = true;
  if (!is_secure_stream()) {
    // TODO: timeout

    boost::asio::async_connect(
        _ws_stream->next_layer(), resolved.begin(), resolved.end(),
        // capture shared_ptr to keep the instance
        // capture this to access protected member
        [ this, hostname, handler = std::move(handler) ](
            const boost::system::error_code& error,
            tcp::resolver::iterator) mutable {
          if (is_destroyed()) return;
          if (error != boost::system::errc::success) {
            handler(error);
            return;
          }
          // websocket handshake
          _ws_stream->async_handshake(hostname, "/", std::move(handler));
        });
  } else {
    std::lock_guard<std::mutex> lock(_mutex);
    boost::asio::async_connect(
        _wss_stream->next_layer().next_layer(), resolved.begin(),
        resolved.end(),
        // capture shared_ptr to keep the instance
        // capture this to access protected member
        [ this, hostname, handler = std::move(handler) ](
            const boost::system::error_code& error,
            tcp::resolver::iterator) mutable {
          if (is_destroyed()) return;
          if (error != boost::system::errc::success) {
            handler(error);
            return;
          }
          std::lock_guard<std::mutex> lock(_mutex);
          // ssl handshake
          _wss_stream->next_layer().async_handshake(ssl::stream_base::client, [
            this, hostname, handler = std::move(handler)
          ](boost::system::error_code ec) {
            if (is_destroyed()) return;
            if (ec != boost::system::errc::success) {
              handler(ec);
              return;
            }

            std::lock_guard<std::mutex> lock(_mutex);
            // websocket handshake
            _wss_stream->async_handshake(
                hostname, "/", std::move(handler));  // websocket handshake

          });  // ssl handshake

        });  // async_connect
  }
}

void Websocket::destroy() {
  if (_destroyed) return;

  _destroyed = true;

  if (_is_websocket) {
    auto on_close = [](const boost::system::error_code ec) {
      if (ec) {
        LOG_DEBUG(__FILENAME__,
                  LOG << "websocket close error: " << ec.message());
      }
    };
    // destroy websocket
    if (is_secure_stream()) {
      std::lock_guard<std::mutex> lock(_mutex);
      if (_wss_stream->is_open()) {
        _wss_stream->async_close(websocket::close_code::normal, on_close);
      }
    } else {
      if (_ws_stream->is_open()) {
        _ws_stream->async_close(websocket::close_code::normal, on_close);
      }
    }
  } else {
    // destroy http
    if (_is_secure_stream) {
      _wss_stream->next_layer().shutdown();
    } else if (_socket.is_open()) {
      _socket.shutdown(tcp::socket::shutdown_send);
    }
  }
}
}  // namespace dsa
<|endoftext|>


<commit_before>using namespace System;
using namespace System::Reflection;
using namespace System::Runtime::CompilerServices;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Permissions;

//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly:AssemblyTitleAttribute("TeseCpp.Tests")];
[assembly:AssemblyDescriptionAttribute("")];
[assembly:AssemblyConfigurationAttribute("")];
[assembly:AssemblyCompanyAttribute("")];
[assembly:AssemblyProductAttribute("TeseCpp.Tests")];
[assembly:AssemblyCopyrightAttribute("Copyright 2016")];
[assembly:AssemblyTrademarkAttribute("")];
[assembly:AssemblyCultureAttribute("")];

//
// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the value or you can default the Revision and Build Numbers
// by using the '*' as shown below:

[assembly:AssemblyVersionAttribute("1.0.*")];

[assembly:ComVisible(false)];

[assembly:CLSCompliantAttribute(true)];

[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)];<commit_msg>Reworked test assembly info<commit_after>#include "stdafx.h"

using namespace System;
using namespace System::Reflection;
using namespace System::Runtime::CompilerServices;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Permissions;

//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly:AssemblyTitleAttribute("TeseCpp.Tests")];
[assembly:AssemblyDescriptionAttribute("")];
[assembly:AssemblyConfigurationAttribute("")];
[assembly:AssemblyCompanyAttribute("")];
[assembly:AssemblyProductAttribute("TeseCpp.Tests")];
[assembly:AssemblyCopyrightAttribute("Copyright 2016")];
[assembly:AssemblyTrademarkAttribute("")];
[assembly:AssemblyCultureAttribute("")];

//
// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the value or you can default the Revision and Build Numbers
// by using the '*' as shown below:

[assembly:AssemblyVersionAttribute("1.0.*")];

[assembly:ComVisible(false)];

[assembly:CLSCompliantAttribute(true)];

[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)];
<|endoftext|>


<commit_before>#include "Environ.hpp"

#include "error.hpp"
#include "log.hpp"
#include "utils/path.hpp"
#include "Node.hpp"

#include <boost/serialization/vector.hpp>
#include <boost/serialization/variant.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <map>
#include <fstream>

namespace fs = boost::filesystem;

namespace std {

	// This overload has to be in std namespace
	static ostream& operator <<(ostream& out,
	                            vector<configure::Environ::Value> const& values)
	{
		out << "{";
		for (auto const& el: values)
			out << el << ", ";
		out << "}";
		return out;
	}

}

namespace configure {

	Environ::Environ()
	{}

	Environ::Environ(Environ const& other)
		: _values(other._values)
	{}

	Environ::~Environ()
	{}

	bool Environ::has(std::string const& key) const
	{ return _values.find(key) != _values.end(); }

	Environ::Kind Environ::kind(std::string const& key) const
	{
		auto it = _values.find(key);
		if (it == _values.end())
			return Kind::none;
		switch (it->second.which())
		{
			case 0: return Kind::string;
			case 1: return Kind::path;
			case 2: return Kind::boolean;
			case 3: return Kind::integer;
			case 4: return Kind::list;
			default: std::abort();
		}
	}

	std::string Environ::as_string(std::string const& key) const
	{
		std::stringstream ss;
		ss << _values.at(key);
		return ss.str();
	}

	void Environ::value_changed(std::string const&) { /* Nothing to do */ }

	void Environ::new_key(std::string const&) { /* Nothing to do */ }

	void Environ::load(boost::filesystem::path const& path)
	{
		std::ifstream in(path.string(), std::ios::binary);
		boost::archive::binary_iarchive ar(in);
		ar & *this;
	}

	void Environ::save(boost::filesystem::path const& path) const
	{
		std::ofstream out(path.string(), std::ios::binary);
		boost::archive::binary_oarchive ar(out);
		ar & *this;
	}

	template<class Archive>
	void Environ::save(Archive& ar, unsigned int) const
	{
		Values::size_type s = _values.size();
		ar << s;
		for (auto& el: _values)
			ar << el;
	}

	template<class Archive>
	void Environ::load(Archive& ar, unsigned int)
	{
		Values::size_type size;
		ar >> size;
		_values.reserve(size);
		for (Values::size_type i = 0; i < size; ++i)
		{
			Values::value_type el;
			ar >> el;
			_values.insert(std::move(el));
		}
	}

	template<typename Archive>
	void Environ::serialize(Archive& ar, unsigned int version)
	{
		boost::serialization::split_member(ar, *this, version);
	}

#define INSTANCIATE(T) \
	template \
	void Environ::serialize<T>(T&, unsigned int const); \

	INSTANCIATE(boost::archive::binary_iarchive);
	INSTANCIATE(boost::archive::binary_oarchive);
#undef INSTANCIATE

	std::vector<std::string> Environ::keys() const
	{
		std::vector<std::string> res;
		res.reserve(_values.size());
		for (auto& p: _values)
			res.push_back(p.first);
		return res;
	}

	std::string Environ::normalize(std::string key)
	{
		if (key.empty())
			CONFIGURE_THROW(error::InvalidKey("Empty key"));
		if (key[0] >= '0' && key[0] <= '9')
			CONFIGURE_THROW(error::InvalidKey("Key '" + key + "' cannot start with a digit"));
		if (key[0] == '-')
			CONFIGURE_THROW(error::InvalidKey("Key '" + key + "' cannot start with a dash"));
		if (key[0] == '_')
			CONFIGURE_THROW(error::InvalidKey("Key '" + key + "' cannot start with a dash"));
		for (size_t i = 0, len = key.size(); i < len; ++i)
		{
			char& c = key[i];
			if (c == '-') c = '_';
			else if (c >= 'a' && c <= 'z') c = c - 'a' + 'A';
			else if (!(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9') && c != '_')
				CONFIGURE_THROW(error::InvalidKey(
					"Key '" + key + "' contains an invalid character '" +
					std::string(&c, 1) + "' at index " + std::to_string(i)
				));
		}
		if (key[key.size() - 1] == '_') // Cannot ends with '-' since they are converted in the for loop
			CONFIGURE_THROW(error::InvalidKey("Key '" + key + "' cannot end with a dash"));
		return key;
	}

	std::ostream& operator <<(std::ostream& out, Environ::Kind kind)
	{
		switch (kind)
		{
			case Environ::Kind::integer: return out << "Kind::integer";
			case Environ::Kind::boolean: return out << "Kind::boolean";
			case Environ::Kind::path: return out << "Kind::path";
			case Environ::Kind::string: return out << "Kind::string";
			case Environ::Kind::list: return out << "Kind::list";
			case Environ::Kind::none: return out << "Kind::none";
		}
		std::abort();
	}

	boost::filesystem::path const& Environ::_node_path(Node const& node)
	{
		if (node.is_virtual())
			CONFIGURE_THROW(
				error::InvalidNode("Cannot store a virtual node in the environ")
			);
		return node.path();
	}

}
<commit_msg>Normalize environ keys.<commit_after>#include "Environ.hpp"

#include "error.hpp"
#include "log.hpp"
#include "utils/path.hpp"
#include "Node.hpp"

#include <boost/serialization/vector.hpp>
#include <boost/serialization/variant.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <map>
#include <fstream>

namespace fs = boost::filesystem;

namespace std {

	// This overload has to be in std namespace
	static ostream& operator <<(ostream& out,
	                            vector<configure::Environ::Value> const& values)
	{
		out << "{";
		for (auto const& el: values)
			out << el << ", ";
		out << "}";
		return out;
	}

}

namespace configure {

	Environ::Environ()
	{}

	Environ::Environ(Environ const& other)
		: _values(other._values)
	{}

	Environ::~Environ()
	{}

	bool Environ::has(std::string const& key) const
	{ return _values.find(key) != _values.end(); }

	Environ::Kind Environ::kind(std::string const& key) const
	{
		auto it = _values.find(key);
		if (it == _values.end())
			return Kind::none;
		switch (it->second.which())
		{
			case 0: return Kind::string;
			case 1: return Kind::path;
			case 2: return Kind::boolean;
			case 3: return Kind::integer;
			case 4: return Kind::list;
			default: std::abort();
		}
	}

	std::string Environ::as_string(std::string const& key) const
	{
		std::stringstream ss;
		ss << _values.at(key);
		return ss.str();
	}

	void Environ::value_changed(std::string const&) { /* Nothing to do */ }

	void Environ::new_key(std::string const&) { /* Nothing to do */ }

	void Environ::load(boost::filesystem::path const& path)
	{
		std::ifstream in(path.string(), std::ios::binary);
		boost::archive::binary_iarchive ar(in);
		ar & *this;
	}

	void Environ::save(boost::filesystem::path const& path) const
	{
		std::ofstream out(path.string(), std::ios::binary);
		boost::archive::binary_oarchive ar(out);
		ar & *this;
	}

	template<class Archive>
	void Environ::save(Archive& ar, unsigned int) const
	{
		Values::size_type s = _values.size();
		ar << s;
		for (auto& el: _values)
			ar << el;
	}

	template<class Archive>
	void Environ::load(Archive& ar, unsigned int)
	{
		Values::size_type size;
		ar >> size;
		_values.reserve(size);
		for (Values::size_type i = 0; i < size; ++i)
		{
			Values::value_type el;
			ar >> el;
			_values.insert(std::move(el));
		}
	}

	template<typename Archive>
	void Environ::serialize(Archive& ar, unsigned int version)
	{
		boost::serialization::split_member(ar, *this, version);
	}

#define INSTANCIATE(T) \
	template \
	void Environ::serialize<T>(T&, unsigned int const); \

	INSTANCIATE(boost::archive::binary_iarchive);
	INSTANCIATE(boost::archive::binary_oarchive);
#undef INSTANCIATE

	std::vector<std::string> Environ::keys() const
	{
		std::vector<std::string> res;
		res.reserve(_values.size());
		for (auto& p: _values)
			res.push_back(Environ::normalize(p.first));
		return res;
	}

	std::string Environ::normalize(std::string key)
	{
		if (key.empty())
			CONFIGURE_THROW(error::InvalidKey("Empty key"));
		if (key[0] >= '0' && key[0] <= '9')
			CONFIGURE_THROW(error::InvalidKey("Key '" + key + "' cannot start with a digit"));
		if (key[0] == '-')
			CONFIGURE_THROW(error::InvalidKey("Key '" + key + "' cannot start with a dash"));
		if (key[0] == '_')
			CONFIGURE_THROW(error::InvalidKey("Key '" + key + "' cannot start with a dash"));
		for (size_t i = 0, len = key.size(); i < len; ++i)
		{
			char& c = key[i];
			if (c == '-') c = '_';
			else if (c >= 'a' && c <= 'z') c = c - 'a' + 'A';
			else if (!(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9') && c != '_')
				CONFIGURE_THROW(error::InvalidKey(
					"Key '" + key + "' contains an invalid character '" +
					std::string(&c, 1) + "' at index " + std::to_string(i)
				));
		}
		if (key[key.size() - 1] == '_') // Cannot ends with '-' since they are converted in the for loop
			CONFIGURE_THROW(error::InvalidKey("Key '" + key + "' cannot end with a dash"));
		return key;
	}

	std::ostream& operator <<(std::ostream& out, Environ::Kind kind)
	{
		switch (kind)
		{
			case Environ::Kind::integer: return out << "Kind::integer";
			case Environ::Kind::boolean: return out << "Kind::boolean";
			case Environ::Kind::path: return out << "Kind::path";
			case Environ::Kind::string: return out << "Kind::string";
			case Environ::Kind::list: return out << "Kind::list";
			case Environ::Kind::none: return out << "Kind::none";
		}
		std::abort();
	}

	boost::filesystem::path const& Environ::_node_path(Node const& node)
	{
		if (node.is_virtual())
			CONFIGURE_THROW(
				error::InvalidNode("Cannot store a virtual node in the environ")
			);
		return node.path();
	}

}
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
* Copyright (C) 2010-2011 SmokeX <smokexjc@gmail.com>
*
* 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 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.
*/

/*
  Parts of this code come from Konversation and are copyrighted to:
  Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
  Copyright (C) 2004 Peter Simonsson <psn@linux.se>
  Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
  Copyright (C) 2004-2009 Eli Mackenzie <argonel@gmail.com>
*/

#include "ircutil.h"
#include <QString>
#include <QRegExp>

/*!
    \file ircutil.h
    \brief #include <IrcUtil>
 */

/*!
    \class IrcUtil ircutil.h <IrcUtil>
    \ingroup utility
    \brief The IrcUtil class provides miscellaneous utility functions.
 */

static QRegExp URL_PATTERN(QLatin1String("((www\\.(?!\\.)|(ssh|fish|irc|amarok|(f|sf|ht)tp(|s))://)(\\.?[\\d\\w/,\\':~\\^\\?=;#@\\-\\+\\%\\*\\{\\}\\!\\(\\)\\[\\]]|&)+)|""([-.\\d\\w]+@[-.\\d\\w]{2,}\\.[\\w]{2,})"), Qt::CaseInsensitive);

/*!
    Converts \a message to HTML. This function parses the
    message and replaces IRC-style formatting like colors,
    bold and underline to the corresponding HTML formatting.
    Furthermore, this function detects URLs and replaces
    them with appropriate HTML hyperlinks.
*/
QString IrcUtil::messageToHtml(const QString& message)
{
    QString processed = message;
    processed.replace(QLatin1Char('&'), QLatin1String("&"));
    processed.replace(QLatin1Char('<'), QLatin1String("<"));
    processed.replace(QLatin1Char('>'), QLatin1String(">"));
    processed.replace(QLatin1Char('"'), QLatin1String("""));
    processed.replace(QLatin1Char('\''), QLatin1String("'"));
    processed.replace(QLatin1Char('\t'), QLatin1String(" "));

    enum
    {
        None            = 0x0,
        Bold            = 0x1,
        Color           = 0x2,
        Italic          = 0x4,
        StrikeThrough   = 0x8,
        Underline       = 0x10,
        Inverse         = 0x20
    };
    int state = None;

    int pos = 0;
    bool parseColor = false;
    while (pos < processed.size())
    {
        if (parseColor)
        {
            int len = 2;
            bool ok = false;
            int code = processed.mid(pos, len).toInt(&ok);
            if (ok)
            {
                processed.remove(pos, len);
                QString color = colorCodeToName(code);
                processed = processed.arg(color);
                len = color.length();
            }
            pos += len;
            parseColor = false;
        }

        QString replacement;
        switch (processed.at(pos).unicode())
        {
        case '\x02': // bold
            if (state & Bold)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Bold;
            break;

        case '\x03': // color
            if (state & Color)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Color;
            parseColor = state & Color;
            break;

        //case '\x09': // italic
        case '\x1d': // italic
            if (state & Italic)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Italic;
            break;

        case '\x13': // strike-through
            if (state & StrikeThrough)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= StrikeThrough;
            break;

        case '\x15': // underline
        case '\x1f': // underline
            if (state & Underline)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Underline;
            break;

        case '\x16': // inverse
            if (state & Inverse)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Inverse;
            break;

        case '\x0f': // none
            replacement = QLatin1String("");
            state = None;
            break;

        default:
            break;
        }

        if (!replacement.isNull())
        {
            processed.replace(pos, 1, replacement);
            pos += replacement.length();
        }
        else
        {
            ++pos;
        }
    }

    pos = 0;
    while ((pos = URL_PATTERN.indexIn(processed, pos)) >= 0)
    {
        int len = URL_PATTERN.matchedLength();
        QString href = processed.mid(pos, len);

        // Don't consider trailing > as part of the link.
        QString append;
        if (href.endsWith(QLatin1String(">")))
        {
            append.append(href.right(4));
            href.chop(4);
        }

        // Don't consider trailing comma or semi-colon as part of the link.
        if (href.endsWith(QLatin1Char(',')) || href.endsWith(QLatin1Char(';')))
        {
            append.append(href.right(1));
            href.chop(1);
        }

        // Don't consider trailing closing parenthesis part of the link when
        // there's an opening parenthesis preceding in the beginning of the
        // URL or there is no opening parenthesis in the URL at all.
        if (pos > 0 && href.endsWith(QLatin1Char(')'))
            && (processed.at(pos-1) == QLatin1Char('(')
            || !href.contains(QLatin1Char('('))))
        {
            append.prepend(href.right(1));
            href.chop(1);
        }

        // Qt doesn't support (?<=pattern) so we do it here
        if (pos > 0 && processed.at(pos-1).isLetterOrNumber())
        {
            pos++;
            continue;
        }

        QString protocol;
        if (URL_PATTERN.cap(1).startsWith(QLatin1String("www."), Qt::CaseInsensitive))
            protocol = QLatin1String("http://");
        else if (URL_PATTERN.cap(1).isEmpty())
            protocol = QLatin1String("mailto:");

        QString source = href;
        source.replace(QLatin1String("&"), QLatin1String("&"));

        QString link = QString(QLatin1String("%3")).arg(protocol, source, href) + append;
        processed.replace(pos, len, link);
        pos += link.length();
    }

    return processed;
}

/*!
    Converts a color \a code to a color name.
*/
QString IrcUtil::colorCodeToName(int code)
{
    switch (code)
    {
    case 0:  return QLatin1String("white");
    case 1:  return QLatin1String("black");
    case 2:  return QLatin1String("navy");
    case 3:  return QLatin1String("green");
    case 4:  return QLatin1String("red");
    case 5:  return QLatin1String("maroon");
    case 6:  return QLatin1String("purple");
    case 7:  return QLatin1String("orange");
    case 8:  return QLatin1String("yellow");
    case 9:  return QLatin1String("lime");
    case 10: return QLatin1String("darkcyan");
    case 11: return QLatin1String("cyan");
    case 12: return QLatin1String("blue");
    case 13: return QLatin1String("magenta");
    case 14: return QLatin1String("gray");
    case 15: return QLatin1String("lightgray");
    default: return QLatin1String("black");
    }
}

<commit_msg>Fixed a crash bug in IrcUtil::messageToHtml()<commit_after>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
* Copyright (C) 2010-2011 SmokeX <smokexjc@gmail.com>
*
* 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 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.
*/

/*
  Parts of this code come from Konversation and are copyrighted to:
  Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
  Copyright (C) 2004 Peter Simonsson <psn@linux.se>
  Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
  Copyright (C) 2004-2009 Eli Mackenzie <argonel@gmail.com>
*/

#include "ircutil.h"
#include <QString>
#include <QRegExp>

/*!
    \file ircutil.h
    \brief #include <IrcUtil>
 */

/*!
    \class IrcUtil ircutil.h <IrcUtil>
    \ingroup utility
    \brief The IrcUtil class provides miscellaneous utility functions.
 */

static QRegExp URL_PATTERN(QLatin1String("((www\\.(?!\\.)|(ssh|fish|irc|amarok|(f|sf|ht)tp(|s))://)(\\.?[\\d\\w/,\\':~\\^\\?=;#@\\-\\+\\%\\*\\{\\}\\!\\(\\)\\[\\]]|&)+)|""([-.\\d\\w]+@[-.\\d\\w]{2,}\\.[\\w]{2,})"), Qt::CaseInsensitive);

/*!
    Converts \a message to HTML. This function parses the
    message and replaces IRC-style formatting like colors,
    bold and underline to the corresponding HTML formatting.
    Furthermore, this function detects URLs and replaces
    them with appropriate HTML hyperlinks.
*/
QString IrcUtil::messageToHtml(const QString& message)
{
    QString processed = message;
    processed.replace(QLatin1Char('&'), QLatin1String("&"));
    processed.replace(QLatin1Char('<'), QLatin1String("<"));
    processed.replace(QLatin1Char('>'), QLatin1String(">"));
    processed.replace(QLatin1Char('"'), QLatin1String("""));
    processed.replace(QLatin1Char('\''), QLatin1String("'"));
    processed.replace(QLatin1Char('\t'), QLatin1String(" "));

    enum
    {
        None            = 0x0,
        Bold            = 0x1,
        Color           = 0x2,
        Italic          = 0x4,
        StrikeThrough   = 0x8,
        Underline       = 0x10,
        Inverse         = 0x20
    };
    int state = None;

    int pos = 0;
    bool parseColor = false;
    while (pos < processed.size())
    {
        if (parseColor)
        {
            int len = 0;
            if (processed.at(pos).isDigit()) ++len;
            if (pos+1 < processed.size() && processed.at(pos+1).isDigit()) ++len;

            bool ok = false;
            int code = processed.mid(pos, len).toInt(&ok);
            if (ok)
            {
                processed.remove(pos, len);
                QString color = colorCodeToName(code);
                processed = processed.arg(color);
                len = color.length();
            }
            pos += len;
            if (pos >= processed.size())
                break;
            parseColor = false;
        }

        QString replacement;
        switch (processed.at(pos).unicode())
        {
        case '\x02': // bold
            if (state & Bold)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Bold;
            break;

        case '\x03': // color
            if (state & Color)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Color;
            parseColor = state & Color;
            break;

        //case '\x09': // italic
        case '\x1d': // italic
            if (state & Italic)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Italic;
            break;

        case '\x13': // strike-through
            if (state & StrikeThrough)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= StrikeThrough;
            break;

        case '\x15': // underline
        case '\x1f': // underline
            if (state & Underline)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Underline;
            break;

        case '\x16': // inverse
            if (state & Inverse)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Inverse;
            break;

        case '\x0f': // none
            replacement = QLatin1String("");
            state = None;
            break;

        default:
            break;
        }

        if (!replacement.isNull())
        {
            processed.replace(pos, 1, replacement);
            pos += replacement.length();
        }
        else
        {
            ++pos;
        }
    }

    pos = 0;
    while ((pos = URL_PATTERN.indexIn(processed, pos)) >= 0)
    {
        int len = URL_PATTERN.matchedLength();
        QString href = processed.mid(pos, len);

        // Don't consider trailing > as part of the link.
        QString append;
        if (href.endsWith(QLatin1String(">")))
        {
            append.append(href.right(4));
            href.chop(4);
        }

        // Don't consider trailing comma or semi-colon as part of the link.
        if (href.endsWith(QLatin1Char(',')) || href.endsWith(QLatin1Char(';')))
        {
            append.append(href.right(1));
            href.chop(1);
        }

        // Don't consider trailing closing parenthesis part of the link when
        // there's an opening parenthesis preceding in the beginning of the
        // URL or there is no opening parenthesis in the URL at all.
        if (pos > 0 && href.endsWith(QLatin1Char(')'))
            && (processed.at(pos-1) == QLatin1Char('(')
            || !href.contains(QLatin1Char('('))))
        {
            append.prepend(href.right(1));
            href.chop(1);
        }

        // Qt doesn't support (?<=pattern) so we do it here
        if (pos > 0 && processed.at(pos-1).isLetterOrNumber())
        {
            pos++;
            continue;
        }

        QString protocol;
        if (URL_PATTERN.cap(1).startsWith(QLatin1String("www."), Qt::CaseInsensitive))
            protocol = QLatin1String("http://");
        else if (URL_PATTERN.cap(1).isEmpty())
            protocol = QLatin1String("mailto:");

        QString source = href;
        source.replace(QLatin1String("&"), QLatin1String("&"));

        QString link = QString(QLatin1String("%3")).arg(protocol, source, href) + append;
        processed.replace(pos, len, link);
        pos += link.length();
    }

    return processed;
}

/*!
    Converts a color \a code to a color name.
*/
QString IrcUtil::colorCodeToName(int code)
{
    switch (code)
    {
    case 0:  return QLatin1String("white");
    case 1:  return QLatin1String("black");
    case 2:  return QLatin1String("navy");
    case 3:  return QLatin1String("green");
    case 4:  return QLatin1String("red");
    case 5:  return QLatin1String("maroon");
    case 6:  return QLatin1String("purple");
    case 7:  return QLatin1String("orange");
    case 8:  return QLatin1String("yellow");
    case 9:  return QLatin1String("lime");
    case 10: return QLatin1String("darkcyan");
    case 11: return QLatin1String("cyan");
    case 12: return QLatin1String("blue");
    case 13: return QLatin1String("magenta");
    case 14: return QLatin1String("gray");
    case 15: return QLatin1String("lightgray");
    default: return QLatin1String("black");
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2009 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 "chrome/browser/net/websocket_experiment/websocket_experiment_task.h"

#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/net/url_request_context_getter.h"
#include "chrome/browser/profile.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/websockets/websocket.h"

namespace chrome_browser_net_websocket_experiment {

URLFetcher* WebSocketExperimentTask::Context::CreateURLFetcher(
    const Config& config, URLFetcher::Delegate* delegate) {
  URLRequestContextGetter* getter =
      Profile::GetDefaultRequestContext();
  DCHECK(getter);
  DLOG(INFO) << "url=" << config.http_url;
  URLFetcher* fetcher =
      new URLFetcher(config.http_url, URLFetcher::GET, delegate);
  fetcher->set_request_context(getter);
  fetcher->set_load_flags(
      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA);
  return fetcher;
}

net::WebSocket* WebSocketExperimentTask::Context::CreateWebSocket(
    const Config& config, net::WebSocketDelegate* delegate) {
  URLRequestContextGetter* getter =
      Profile::GetDefaultRequestContext();
  DCHECK(getter);
  net::WebSocket::Request* request(
      new net::WebSocket::Request(config.url,
                                  config.ws_protocol,
                                  config.ws_origin,
                                  config.ws_location,
                                  getter->GetURLRequestContext()));
  return new net::WebSocket(request, delegate);
}

WebSocketExperimentTask::WebSocketExperimentTask(
    const Config& config,
    net::CompletionCallback* callback)
    : config_(config),
      context_(ALLOW_THIS_IN_INITIALIZER_LIST(new Context())),
      method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
      callback_(callback),
      next_state_(STATE_NONE),
      last_websocket_error_(net::OK) {
}

WebSocketExperimentTask::~WebSocketExperimentTask() {
  DLOG(INFO) << "WebSocketExperimentTask finished";
  DCHECK(!websocket_);
}

void WebSocketExperimentTask::Run() {
  next_state_ = STATE_URL_FETCH;
  DoLoop(net::OK);
}

void WebSocketExperimentTask::Cancel() {
  next_state_ = STATE_NONE;
  DoLoop(net::OK);
}

// URLFetcher::Delegate method.
void WebSocketExperimentTask::OnURLFetchComplete(
    const URLFetcher* source,
    const GURL& url,
    const URLRequestStatus& status,
    int response_code,
    const ResponseCookies& cookies,
    const std::string& data) {
  result_.url_fetch = base::TimeTicks::Now() - url_fetch_start_time_;
  RevokeTimeoutTimer();
  DLOG(INFO) << "OnURLFetchCompleted";
  int result = net::ERR_FAILED;
  if (next_state_ != STATE_URL_FETCH_COMPLETE) {
    DLOG(INFO) << "unexpected state=" << next_state_;
    result = net::ERR_UNEXPECTED;
  } else if (response_code == 200 || response_code == 304)
    result = net::OK;
  DoLoop(result);
}

// net::WebSocketDelegate
void WebSocketExperimentTask::OnOpen(net::WebSocket* websocket) {
  result_.websocket_connect =
      base::TimeTicks::Now() - websocket_connect_start_time_;
  RevokeTimeoutTimer();
  int result = net::ERR_UNEXPECTED;
  if (next_state_ == STATE_WEBSOCKET_CONNECT_COMPLETE)
    result = net::OK;
  else
    DLOG(INFO) << "unexpected state=" << next_state_;
  DoLoop(result);
}

void WebSocketExperimentTask::OnMessage(
    net::WebSocket* websocket, const std::string& msg) {
  if (!result_.websocket_echo.ToInternalValue())
    result_.websocket_echo =
        base::TimeTicks::Now() - websocket_echo_start_time_;
  if (!websocket_idle_start_time_.is_null() &&
      !result_.websocket_idle.ToInternalValue())
    result_.websocket_idle =
        base::TimeTicks::Now() - websocket_idle_start_time_;
  RevokeTimeoutTimer();
  DLOG(INFO) << "OnMessage msg=" << msg;
  received_messages_.push_back(msg);
  int result = net::ERR_UNEXPECTED;
  switch (next_state_) {
    case STATE_WEBSOCKET_RECV_HELLO:
    case STATE_WEBSOCKET_RECV_PUSH_MESSAGE:
    case STATE_WEBSOCKET_RECV_BYE:
      result = net::OK;
      break;
    default:
      DLOG(INFO) << "unexpected state=" << next_state_;
      break;
  }
  DoLoop(result);
}

void WebSocketExperimentTask::OnClose(net::WebSocket* websocket) {
  RevokeTimeoutTimer();
  websocket_ = NULL;
  result_.websocket_total =
      base::TimeTicks::Now() - websocket_connect_start_time_;
  int result = net::ERR_CONNECTION_CLOSED;
  if (last_websocket_error_ != net::OK)
    result = last_websocket_error_;
  if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE)
    result = net::OK;
  DoLoop(result);
}

void WebSocketExperimentTask::OnError(
    const net::WebSocket* websocket, int error) {
  DLOG(INFO) << "WebSocket error=" << net::ErrorToString(error);
  last_websocket_error_ = error;
}

void WebSocketExperimentTask::SetContext(Context* context) {
  context_.reset(context);
}

void WebSocketExperimentTask::OnTimedOut() {
  DLOG(INFO) << "OnTimedOut";
  RevokeTimeoutTimer();
  DoLoop(net::ERR_TIMED_OUT);
}

void WebSocketExperimentTask::DoLoop(int result) {
  if (next_state_ == STATE_NONE) {
    Finish(net::ERR_ABORTED);
    return;
  }
  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    DLOG(INFO) << "WebSocketExperimentTask state=" << state;
    switch (state) {
      case STATE_URL_FETCH:
        result = DoURLFetch();
        break;
      case STATE_URL_FETCH_COMPLETE:
        result = DoURLFetchComplete(result);
        break;
      case STATE_WEBSOCKET_CONNECT:
        result = DoWebSocketConnect();
        break;
      case STATE_WEBSOCKET_CONNECT_COMPLETE:
        result = DoWebSocketConnectComplete(result);
        break;
      case STATE_WEBSOCKET_SEND_HELLO:
        result = DoWebSocketSendHello();
        break;
      case STATE_WEBSOCKET_RECV_HELLO:
        result = DoWebSocketReceiveHello(result);
        break;
      case STATE_WEBSOCKET_KEEP_IDLE:
        result = DoWebSocketKeepIdle();
        break;
      case STATE_WEBSOCKET_KEEP_IDLE_COMPLETE:
        result = DoWebSocketKeepIdleComplete(result);
        break;
      case STATE_WEBSOCKET_RECV_PUSH_MESSAGE:
        result = DoWebSocketReceivePushMessage(result);
        break;
      case STATE_WEBSOCKET_ECHO_BACK_MESSAGE:
        result = DoWebSocketEchoBackMessage();
        break;
      case STATE_WEBSOCKET_RECV_BYE:
        result = DoWebSocketReceiveBye(result);
        break;
      case STATE_WEBSOCKET_CLOSE:
        result = DoWebSocketClose();
        break;
      case STATE_WEBSOCKET_CLOSE_COMPLETE:
        result = DoWebSocketCloseComplete(result);
        break;
      default:
        NOTREACHED();
        break;
    }
    result_.last_state = state;
  } while (result != net::ERR_IO_PENDING && next_state_ != STATE_NONE);

  DLOG(INFO) << "WebSocketExperiemntTask Loop done next_state=" << next_state_
             << " result=" << net::ErrorToString(result);
  if (result != net::ERR_IO_PENDING)
    Finish(result);
}

int WebSocketExperimentTask::DoURLFetch() {
  next_state_ = STATE_URL_FETCH_COMPLETE;
  DCHECK(!url_fetcher_.get());

  url_fetcher_.reset(context_->CreateURLFetcher(config_, this));
  SetTimeout(config_.url_fetch_deadline_ms);
  DLOG(INFO) << "URLFetch url=" << url_fetcher_->url()
             << " timeout=" << config_.url_fetch_deadline_ms;
  url_fetch_start_time_ = base::TimeTicks::Now();
  url_fetcher_->Start();
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoURLFetchComplete(int result) {
  DLOG(INFO) << "DoURLFetchComplete result=" << result;
  url_fetcher_.reset();

  if (result < 0)
    return result;

  next_state_ = STATE_WEBSOCKET_CONNECT;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketConnect() {
  DCHECK(!websocket_);

  next_state_ = STATE_WEBSOCKET_CONNECT_COMPLETE;
  websocket_ = context_->CreateWebSocket(config_, this);
  websocket_connect_start_time_ = base::TimeTicks::Now();
  websocket_->Connect();

  SetTimeout(config_.websocket_onopen_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketConnectComplete(int result) {
  if (result < 0)
    return result;
  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_SEND_HELLO;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketSendHello() {
  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_RECV_HELLO;

  websocket_echo_start_time_ = base::TimeTicks::Now();
  websocket_->Send(config_.websocket_hello_message);
  SetTimeout(config_.websocket_hello_echoback_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketReceiveHello(int result) {
  if (result < 0)
    return result;

  DCHECK(websocket_);

  if (received_messages_.size() != 1)
    return net::ERR_INVALID_RESPONSE;

  std::string msg = received_messages_.front();
  received_messages_.pop_front();
  if (msg != config_.websocket_hello_message)
    return net::ERR_INVALID_RESPONSE;

  next_state_ = STATE_WEBSOCKET_KEEP_IDLE;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketKeepIdle() {
  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_KEEP_IDLE_COMPLETE;
  websocket_idle_start_time_ = base::TimeTicks::Now();
  SetTimeout(config_.websocket_idle_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketKeepIdleComplete(int result) {
  if (result != net::ERR_TIMED_OUT) {
    // Server sends back too early, or unexpected close?
    if (result == net::OK)
      result = net::ERR_UNEXPECTED;
    return result;
  }

  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_RECV_PUSH_MESSAGE;
  SetTimeout(config_.websocket_receive_push_message_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketReceivePushMessage(int result) {
  if (result < 0)
    return result;

  DCHECK(websocket_);
  if (received_messages_.size() != 1)
    return net::ERR_INVALID_RESPONSE;

  push_message_ = received_messages_.front();
  received_messages_.pop_front();

  next_state_ = STATE_WEBSOCKET_ECHO_BACK_MESSAGE;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketEchoBackMessage() {
  DCHECK(websocket_);
  DCHECK(!push_message_.empty());

  next_state_ = STATE_WEBSOCKET_RECV_BYE;
  websocket_->Send(push_message_);
  SetTimeout(config_.websocket_bye_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketReceiveBye(int result) {
  if (result < 0)
    return result;

  DCHECK(websocket_);

  if (received_messages_.size() != 1)
    return net::ERR_INVALID_RESPONSE;

  std::string bye = received_messages_.front();
  received_messages_.pop_front();

  if (bye != config_.websocket_bye_message)
    return net::ERR_INVALID_RESPONSE;

  next_state_ = STATE_WEBSOCKET_CLOSE;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketClose() {
  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_CLOSE_COMPLETE;
  websocket_->Close();
  SetTimeout(config_.websocket_close_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketCloseComplete(int result) {
  websocket_ = NULL;
  return result;
}

void WebSocketExperimentTask::SetTimeout(int64 deadline_ms) {
  bool r = ChromeThread::PostDelayedTask(
      ChromeThread::IO,
      FROM_HERE,
      method_factory_.NewRunnableMethod(&WebSocketExperimentTask::OnTimedOut),
      deadline_ms);
  DCHECK(r) << "No IO thread running?";
}

void WebSocketExperimentTask::RevokeTimeoutTimer() {
  method_factory_.RevokeAll();
}

void WebSocketExperimentTask::Finish(int result) {
  DLOG(INFO) << "Finish Task for " << config_.url
             << " next_state=" << next_state_
             << " result=" << net::ErrorToString(result);
  url_fetcher_.reset();
  scoped_refptr<net::WebSocket> websocket = websocket_;
  websocket_ = NULL;
  callback_->Run(result);
  if (websocket)
    websocket->DetachDelegate();
}

}  // namespace chrome_browser_net
<commit_msg>Fix DCHECK in WebSocketExperimentTask.<commit_after>// Copyright (c) 2009 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 "chrome/browser/net/websocket_experiment/websocket_experiment_task.h"

#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/net/url_request_context_getter.h"
#include "chrome/browser/profile.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/websockets/websocket.h"

namespace chrome_browser_net_websocket_experiment {

URLFetcher* WebSocketExperimentTask::Context::CreateURLFetcher(
    const Config& config, URLFetcher::Delegate* delegate) {
  URLRequestContextGetter* getter =
      Profile::GetDefaultRequestContext();
  // Profile::GetDefaultRequestContext() is initialized lazily, on the UI
  // thread. So here, where we access it from the IO thread, if the task runs
  // before it has gotten lazily initialized yet.
  if (!getter)
    return NULL;
  DLOG(INFO) << "url=" << config.http_url;
  URLFetcher* fetcher =
      new URLFetcher(config.http_url, URLFetcher::GET, delegate);
  fetcher->set_request_context(getter);
  fetcher->set_load_flags(
      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA);
  return fetcher;
}

net::WebSocket* WebSocketExperimentTask::Context::CreateWebSocket(
    const Config& config, net::WebSocketDelegate* delegate) {
  URLRequestContextGetter* getter =
      Profile::GetDefaultRequestContext();
  // Profile::GetDefaultRequestContext() is initialized lazily, on the UI
  // thread. So here, where we access it from the IO thread, if the task runs
  // before it has gotten lazily initialized yet.
  if (!getter)
    return NULL;
  net::WebSocket::Request* request(
      new net::WebSocket::Request(config.url,
                                  config.ws_protocol,
                                  config.ws_origin,
                                  config.ws_location,
                                  getter->GetURLRequestContext()));
  return new net::WebSocket(request, delegate);
}

WebSocketExperimentTask::WebSocketExperimentTask(
    const Config& config,
    net::CompletionCallback* callback)
    : config_(config),
      context_(ALLOW_THIS_IN_INITIALIZER_LIST(new Context())),
      method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
      callback_(callback),
      next_state_(STATE_NONE),
      last_websocket_error_(net::OK) {
}

WebSocketExperimentTask::~WebSocketExperimentTask() {
  DLOG(INFO) << "WebSocketExperimentTask finished";
  DCHECK(!websocket_);
}

void WebSocketExperimentTask::Run() {
  next_state_ = STATE_URL_FETCH;
  DoLoop(net::OK);
}

void WebSocketExperimentTask::Cancel() {
  next_state_ = STATE_NONE;
  DoLoop(net::OK);
}

// URLFetcher::Delegate method.
void WebSocketExperimentTask::OnURLFetchComplete(
    const URLFetcher* source,
    const GURL& url,
    const URLRequestStatus& status,
    int response_code,
    const ResponseCookies& cookies,
    const std::string& data) {
  result_.url_fetch = base::TimeTicks::Now() - url_fetch_start_time_;
  RevokeTimeoutTimer();
  DLOG(INFO) << "OnURLFetchCompleted";
  int result = net::ERR_FAILED;
  if (next_state_ != STATE_URL_FETCH_COMPLETE) {
    DLOG(INFO) << "unexpected state=" << next_state_;
    result = net::ERR_UNEXPECTED;
  } else if (response_code == 200 || response_code == 304)
    result = net::OK;
  DoLoop(result);
}

// net::WebSocketDelegate
void WebSocketExperimentTask::OnOpen(net::WebSocket* websocket) {
  result_.websocket_connect =
      base::TimeTicks::Now() - websocket_connect_start_time_;
  RevokeTimeoutTimer();
  int result = net::ERR_UNEXPECTED;
  if (next_state_ == STATE_WEBSOCKET_CONNECT_COMPLETE)
    result = net::OK;
  else
    DLOG(INFO) << "unexpected state=" << next_state_;
  DoLoop(result);
}

void WebSocketExperimentTask::OnMessage(
    net::WebSocket* websocket, const std::string& msg) {
  if (!result_.websocket_echo.ToInternalValue())
    result_.websocket_echo =
        base::TimeTicks::Now() - websocket_echo_start_time_;
  if (!websocket_idle_start_time_.is_null() &&
      !result_.websocket_idle.ToInternalValue())
    result_.websocket_idle =
        base::TimeTicks::Now() - websocket_idle_start_time_;
  RevokeTimeoutTimer();
  DLOG(INFO) << "OnMessage msg=" << msg;
  received_messages_.push_back(msg);
  int result = net::ERR_UNEXPECTED;
  switch (next_state_) {
    case STATE_WEBSOCKET_RECV_HELLO:
    case STATE_WEBSOCKET_RECV_PUSH_MESSAGE:
    case STATE_WEBSOCKET_RECV_BYE:
      result = net::OK;
      break;
    default:
      DLOG(INFO) << "unexpected state=" << next_state_;
      break;
  }
  DoLoop(result);
}

void WebSocketExperimentTask::OnClose(net::WebSocket* websocket) {
  RevokeTimeoutTimer();
  websocket_ = NULL;
  result_.websocket_total =
      base::TimeTicks::Now() - websocket_connect_start_time_;
  int result = net::ERR_CONNECTION_CLOSED;
  if (last_websocket_error_ != net::OK)
    result = last_websocket_error_;
  if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE)
    result = net::OK;
  DoLoop(result);
}

void WebSocketExperimentTask::OnError(
    const net::WebSocket* websocket, int error) {
  DLOG(INFO) << "WebSocket error=" << net::ErrorToString(error);
  last_websocket_error_ = error;
}

void WebSocketExperimentTask::SetContext(Context* context) {
  context_.reset(context);
}

void WebSocketExperimentTask::OnTimedOut() {
  DLOG(INFO) << "OnTimedOut";
  RevokeTimeoutTimer();
  DoLoop(net::ERR_TIMED_OUT);
}

void WebSocketExperimentTask::DoLoop(int result) {
  if (next_state_ == STATE_NONE) {
    Finish(net::ERR_ABORTED);
    return;
  }
  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    DLOG(INFO) << "WebSocketExperimentTask state=" << state;
    switch (state) {
      case STATE_URL_FETCH:
        result = DoURLFetch();
        break;
      case STATE_URL_FETCH_COMPLETE:
        result = DoURLFetchComplete(result);
        break;
      case STATE_WEBSOCKET_CONNECT:
        result = DoWebSocketConnect();
        break;
      case STATE_WEBSOCKET_CONNECT_COMPLETE:
        result = DoWebSocketConnectComplete(result);
        break;
      case STATE_WEBSOCKET_SEND_HELLO:
        result = DoWebSocketSendHello();
        break;
      case STATE_WEBSOCKET_RECV_HELLO:
        result = DoWebSocketReceiveHello(result);
        break;
      case STATE_WEBSOCKET_KEEP_IDLE:
        result = DoWebSocketKeepIdle();
        break;
      case STATE_WEBSOCKET_KEEP_IDLE_COMPLETE:
        result = DoWebSocketKeepIdleComplete(result);
        break;
      case STATE_WEBSOCKET_RECV_PUSH_MESSAGE:
        result = DoWebSocketReceivePushMessage(result);
        break;
      case STATE_WEBSOCKET_ECHO_BACK_MESSAGE:
        result = DoWebSocketEchoBackMessage();
        break;
      case STATE_WEBSOCKET_RECV_BYE:
        result = DoWebSocketReceiveBye(result);
        break;
      case STATE_WEBSOCKET_CLOSE:
        result = DoWebSocketClose();
        break;
      case STATE_WEBSOCKET_CLOSE_COMPLETE:
        result = DoWebSocketCloseComplete(result);
        break;
      default:
        NOTREACHED();
        break;
    }
    result_.last_state = state;
  } while (result != net::ERR_IO_PENDING && next_state_ != STATE_NONE);

  DLOG(INFO) << "WebSocketExperiemntTask Loop done next_state=" << next_state_
             << " result=" << net::ErrorToString(result);
  if (result != net::ERR_IO_PENDING)
    Finish(result);
}

int WebSocketExperimentTask::DoURLFetch() {
  DCHECK(!url_fetcher_.get());

  url_fetcher_.reset(context_->CreateURLFetcher(config_, this));
  if (!url_fetcher_.get()) {
    // Request context is not ready.
    next_state_ = STATE_NONE;
    return net::ERR_UNEXPECTED;
  }

  next_state_ = STATE_URL_FETCH_COMPLETE;
  SetTimeout(config_.url_fetch_deadline_ms);
  DLOG(INFO) << "URLFetch url=" << url_fetcher_->url()
             << " timeout=" << config_.url_fetch_deadline_ms;
  url_fetch_start_time_ = base::TimeTicks::Now();
  url_fetcher_->Start();
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoURLFetchComplete(int result) {
  DLOG(INFO) << "DoURLFetchComplete result=" << result;
  url_fetcher_.reset();

  if (result < 0)
    return result;

  next_state_ = STATE_WEBSOCKET_CONNECT;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketConnect() {
  DCHECK(!websocket_);

  websocket_ = context_->CreateWebSocket(config_, this);
  if (!websocket_) {
    // Request context is not ready.
    next_state_ = STATE_NONE;
    return net::ERR_UNEXPECTED;
  }
  next_state_ = STATE_WEBSOCKET_CONNECT_COMPLETE;
  websocket_connect_start_time_ = base::TimeTicks::Now();
  websocket_->Connect();

  SetTimeout(config_.websocket_onopen_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketConnectComplete(int result) {
  if (result < 0)
    return result;
  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_SEND_HELLO;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketSendHello() {
  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_RECV_HELLO;

  websocket_echo_start_time_ = base::TimeTicks::Now();
  websocket_->Send(config_.websocket_hello_message);
  SetTimeout(config_.websocket_hello_echoback_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketReceiveHello(int result) {
  if (result < 0)
    return result;

  DCHECK(websocket_);

  if (received_messages_.size() != 1)
    return net::ERR_INVALID_RESPONSE;

  std::string msg = received_messages_.front();
  received_messages_.pop_front();
  if (msg != config_.websocket_hello_message)
    return net::ERR_INVALID_RESPONSE;

  next_state_ = STATE_WEBSOCKET_KEEP_IDLE;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketKeepIdle() {
  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_KEEP_IDLE_COMPLETE;
  websocket_idle_start_time_ = base::TimeTicks::Now();
  SetTimeout(config_.websocket_idle_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketKeepIdleComplete(int result) {
  if (result != net::ERR_TIMED_OUT) {
    // Server sends back too early, or unexpected close?
    if (result == net::OK)
      result = net::ERR_UNEXPECTED;
    return result;
  }

  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_RECV_PUSH_MESSAGE;
  SetTimeout(config_.websocket_receive_push_message_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketReceivePushMessage(int result) {
  if (result < 0)
    return result;

  DCHECK(websocket_);
  if (received_messages_.size() != 1)
    return net::ERR_INVALID_RESPONSE;

  push_message_ = received_messages_.front();
  received_messages_.pop_front();

  next_state_ = STATE_WEBSOCKET_ECHO_BACK_MESSAGE;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketEchoBackMessage() {
  DCHECK(websocket_);
  DCHECK(!push_message_.empty());

  next_state_ = STATE_WEBSOCKET_RECV_BYE;
  websocket_->Send(push_message_);
  SetTimeout(config_.websocket_bye_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketReceiveBye(int result) {
  if (result < 0)
    return result;

  DCHECK(websocket_);

  if (received_messages_.size() != 1)
    return net::ERR_INVALID_RESPONSE;

  std::string bye = received_messages_.front();
  received_messages_.pop_front();

  if (bye != config_.websocket_bye_message)
    return net::ERR_INVALID_RESPONSE;

  next_state_ = STATE_WEBSOCKET_CLOSE;
  return net::OK;
}

int WebSocketExperimentTask::DoWebSocketClose() {
  DCHECK(websocket_);

  next_state_ = STATE_WEBSOCKET_CLOSE_COMPLETE;
  websocket_->Close();
  SetTimeout(config_.websocket_close_deadline_ms);
  return net::ERR_IO_PENDING;
}

int WebSocketExperimentTask::DoWebSocketCloseComplete(int result) {
  websocket_ = NULL;
  return result;
}

void WebSocketExperimentTask::SetTimeout(int64 deadline_ms) {
  bool r = ChromeThread::PostDelayedTask(
      ChromeThread::IO,
      FROM_HERE,
      method_factory_.NewRunnableMethod(&WebSocketExperimentTask::OnTimedOut),
      deadline_ms);
  DCHECK(r) << "No IO thread running?";
}

void WebSocketExperimentTask::RevokeTimeoutTimer() {
  method_factory_.RevokeAll();
}

void WebSocketExperimentTask::Finish(int result) {
  DLOG(INFO) << "Finish Task for " << config_.url
             << " next_state=" << next_state_
             << " result=" << net::ErrorToString(result);
  url_fetcher_.reset();
  scoped_refptr<net::WebSocket> websocket = websocket_;
  websocket_ = NULL;
  callback_->Run(result);
  if (websocket)
    websocket->DetachDelegate();
}

}  // namespace chrome_browser_net
<|endoftext|>