<commit_before>#include "condor_common.h"
#include "condor_config.h"
#include "condor_debug.h"

#include "sysapi.h"
#include "sysapi_externs.h"

/*
 * See GitTrac #3544 for an explanation of why we care about processor flags
 * (short answer: because glibc does, and breaks standard universe as a
 * result).  To determine which flags glibc cares about, examine the glibc
 * sources; in 2.12-2, the file sysdeps/x86_64/multiarch/init-arch.h #defines
 * [bit|index]_[SSE2|SSSE3|SSE4_1|SSE4_2].  I confirmed (by checking with
 * Wikipedia's CPUID article) that glibc was using the same names for those
 * processor flags as everyone else (using the value of the shift in the bit_*
 * macros, combined with CPUID_E[C|D]X_OFFSET).  Thus, SSSE3 really does have
 * three 'S's.
 *
 * We ignore what glibc is doing in init-arch.c because it's strictly for
 * performance -- see the ticket for details.
 *
 * We're ignoring SSE2 because every processor built in the last 10 years
 * supports it.
 *
 * We're also (implicitly) asserting homogeneous cores; we simply don't have
 * a way to represent anything else at this level of the code.  Instead, we
 * complain to the log if the processor flag strings aren't all the same.
 */

static struct sysapi_cpuinfo theInfo;

const struct sysapi_cpuinfo *sysapi_processor_flags_raw( void ) {
    sysapi_internal_reconfig();

    if( _sysapi_processor_flags_raw != NULL ) {
        return &theInfo;
    }

    /* Set the default to the empty string so that if something goes wrong
       during parsing (or if /proc/cpuinfo doesn't exist), we stop trying. */
    _sysapi_processor_flags_raw = "";

    /* If we check for the null string, we could leak memory for processors
       without flags.  (We shouldn't see any, but...) */
    int foundProcessorFlags = 0;

    /* You can adapt this to ncpus.cpp's _SysapiProcCpuinfo for debugging. */
    FILE * fp = safe_fopen_wrapper_follow( "/proc/cpuinfo", "r", 0644 );
    dprintf( D_LOAD, "Reading from /proc/cpuinfo\n" );
    if( fp ) {
        int size = 128;
        char * buffer = (char *)malloc( size );
        if( buffer == NULL ) {
            EXCEPT( "Failed to allocate buffer for parsing /proc/cpuinfo.\n" );
        }

        while( fgets( buffer, size, fp ) ) {
            while( strchr( buffer, '\n' ) == NULL ) {
                char * newBuffer = (char *)realloc( buffer, size + size );
                if( newBuffer == NULL ) {
                    EXCEPT( "Failed to allocate memory for a long line in /proc/cpuinfo.\n" );
                }
                buffer = newBuffer;

                newBuffer = buffer + strlen( buffer );
                if( ! fgets( newBuffer, size, fp ) ) {
                    /* If we fail a read before finding the end of the line,
                       something has probably gone terribly, terribly wrong. */
                    EXCEPT( "Failed to find end of line ('%s') before end of file.\n", buffer );
                    // If /proc/cpuinfo regularly terminates without a newline,
                    // we could do this instead.
                    // free( buffer );
                    // fclose( fp );
                    // return _sysapi_processor_flags_raw;
                }
                size += size;
            }

            char * colon = strchr( buffer, ':' );

            const char * value = "";
            const char * attribute = NULL;
            if( colon != NULL ) {
                for( unsigned int v = 1; colon[v] != '\0' && isspace( colon[v] ); ++v ) {
                    value = colon + v;
                }

                char * tmp = colon;
                while( isspace( *tmp ) || (*tmp == ':' ) ) {
                    *tmp = '\0';
                    --tmp;
                }
                attribute = buffer;

                if( strcmp( attribute, "flags" ) == 0 ) {
                    if( foundProcessorFlags == 0 ) {
                        /* This is where we assume flags fits into buffer. */
                        _sysapi_processor_flags_raw = strdup( value );
                        if( _sysapi_processor_flags_raw == NULL ) {
                            EXCEPT( "Failed to allocate memory for the raw processor flags.\n" );
                        }
                    } else {
                        if( strcmp( _sysapi_processor_flags_raw, value ) != 0 ) {
                            dprintf( D_ALWAYS, "WARNING: Processor flags '%s' and '%s' are not the same; using the former.\n", _sysapi_processor_flags_raw, value );
                        }
                    }

                    foundProcessorFlags += 1;
                } else if (strcmp(attribute, "model") == 0) {
			sscanf(value, "%d", &theInfo.model_no);
		} else if (strcmp(attribute,"cpu family") == 0) {
			sscanf(value, "%d", &theInfo.family);
		} else if (strcmp(attribute,"cache size") == 0) {
			sscanf(value, "%d", &theInfo.cache);
		}
            }
        }

        free( buffer );
        fclose( fp );
    }

    theInfo.processor_flags = _sysapi_processor_flags;
    return &theInfo;
}

const struct sysapi_cpuinfo *sysapi_processor_flags( void ) {
    sysapi_internal_reconfig();

    if( _sysapi_processor_flags != NULL ) {
        return &theInfo;
    }

    if( _sysapi_processor_flags_raw == NULL ) {
        sysapi_processor_flags_raw();
        ASSERT(_sysapi_processor_flags_raw != NULL);
    }

    /* Which flags do we care about?  You MUST terminate this list with NULL. */
    static const char * const flagNames[] = { "avx", "avx2", "avx512", "ssse3", "sse4_1", "sse4_2", NULL };

    /* Do some memory-allocation math. */
    int numFlags = 0;
    int maxFlagLength = 0;
    for( int i = 0; flagNames[i] != NULL; ++i ) {
        ++numFlags;
        int curFlagLength = (int)strlen( flagNames[i] );
        if( curFlagLength > maxFlagLength ) { maxFlagLength = curFlagLength; }
    }

    char * currentFlag = (char *)malloc( (1 + maxFlagLength) * sizeof( char ) );
    if( currentFlag == NULL ) {
        EXCEPT( "Failed to allocate memory for current processor flag." );
    }
    currentFlag[0] = '\0';

    /* If we track which flags we have, we can make sure the order we
       print them is the same regardless of the raw flags order. */
    const char ** flags = (const char **)malloc( sizeof( const  char * ) * numFlags );
    if( flags == NULL ) {
        EXCEPT( "Failed to allocate memory for processor flags." );
    }
    for( int i = 0; i < numFlags; ++i ) { flags[i] = ""; }

    const char * flagStart = _sysapi_processor_flags_raw;
    const char * flagEnd = _sysapi_processor_flags_raw;
    while( * flagStart != '\0' ) {
        if( * flagStart == ' ' ) { ++flagStart; continue; }

        for( flagEnd = flagStart; (* flagEnd != '\0') && (* flagEnd != ' '); ++flagEnd ) { ; }

        int flagSize = (flagEnd - flagStart) / (int)sizeof( char );
        if( flagSize > maxFlagLength ) {
            flagStart = flagEnd;
            continue;
        }

        /* We know that flagStart is neither ' ' nor '\0', so we must have
           at least one character.  Because we only care about flags of a
           certain length or smaller, we know we won't overflow our buffer. */
        strncpy( currentFlag, flagStart, flagSize );
        currentFlag[flagSize] = '\0';

        for( int i = 0; flagNames[i] != NULL; ++i ) {
            if( strcmp( currentFlag, flagNames[i] ) == 0 ) {
                /* Add to the flags. */
                flags[i] = flagNames[i];
                break;
            }
        }

        flagStart = flagEnd;
    }
    free( currentFlag );

    /* How much space do we need? */
    int flagsLength = 1;
    for( int i = 0; i < numFlags; ++i ) {
        int length = (int)strlen( flags[i] );
        if( length ) { flagsLength += length + 1; }
    }

    if( flagsLength == 1 ) {
        _sysapi_processor_flags = "none";
    } else {
        char * processor_flags = (char *)malloc( sizeof( char ) * flagsLength );
        if( processor_flags == NULL ) {
            EXCEPT( "Failed to allocate memory for processor flag list." );
        }
        processor_flags[0] = '\0';

        /* This way, the flags will always print out in the same order. */
        for( int i = 0; i < numFlags; ++i ) {
            if( strlen( flags[i] ) ) {
                strcat( processor_flags, flags[i] );
                strcat( processor_flags, " " );
            }
        }

        /* Remove the trailing space. */
        processor_flags[ flagsLength - 2 ] = '\0';
        _sysapi_processor_flags = processor_flags;
    }

    free( flags );
    theInfo.processor_flags = _sysapi_processor_flags;
    return &theInfo;
}

// ----------------------------------------------------------------------------

#if defined( PROCESSOR_FLAGS_TESTING )

/*
 * To compile:

g++ -DGLIBC=GLIBC -DHAVE_CONFIG_H -DLINUX -DPROCESSOR_FLAGS_TESTING \
    -I../condor_includes -I../condor_utils -I../safefile \
    -I<configured build directory>/src/condor_includes \
    processor_flags.cpp

 *
 */

// Required to link.
char * _sysapi_processor_flags = NULL;
char * _sysapi_processor_flags_raw = NULL;
void sysapi_internal_reconfig( void ) { ; }
int _EXCEPT_Line;
int _EXCEPT_Errno;
const char * _EXCEPT_File;
void _EXCEPT_ ( const char * fmt, ... ) { exit( 1 ); }

int main( int argc, char ** argv ) {
    const char * expected;

    expected = "sse4_1 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"test1 sse4_1 sse3 ssse3 test2 testFour";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "none";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"test1 test2 testFour";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "none";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_1 sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"sse4_1 sse4_2 ssse3";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_1 sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"sse4_1 sse4_2 ssse3 ssse3";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_1 sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"sse4_1 sse4_2 ssse3 ssse3";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_1 sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"ssse3 sse4_1 sse4_2 ssse3 ssse3";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"test1 sse4_2 sse3 ssse3 test2 testFour";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    return 0;
}

#endif
<commit_msg>Check return value from scanf #6992<commit_after>#include "condor_common.h"
#include "condor_config.h"
#include "condor_debug.h"

#include "sysapi.h"
#include "sysapi_externs.h"

/*
 * See GitTrac #3544 for an explanation of why we care about processor flags
 * (short answer: because glibc does, and breaks standard universe as a
 * result).  To determine which flags glibc cares about, examine the glibc
 * sources; in 2.12-2, the file sysdeps/x86_64/multiarch/init-arch.h #defines
 * [bit|index]_[SSE2|SSSE3|SSE4_1|SSE4_2].  I confirmed (by checking with
 * Wikipedia's CPUID article) that glibc was using the same names for those
 * processor flags as everyone else (using the value of the shift in the bit_*
 * macros, combined with CPUID_E[C|D]X_OFFSET).  Thus, SSSE3 really does have
 * three 'S's.
 *
 * We ignore what glibc is doing in init-arch.c because it's strictly for
 * performance -- see the ticket for details.
 *
 * We're ignoring SSE2 because every processor built in the last 10 years
 * supports it.
 *
 * We're also (implicitly) asserting homogeneous cores; we simply don't have
 * a way to represent anything else at this level of the code.  Instead, we
 * complain to the log if the processor flag strings aren't all the same.
 */

static struct sysapi_cpuinfo theInfo;

const struct sysapi_cpuinfo *sysapi_processor_flags_raw( void ) {
    sysapi_internal_reconfig();

    if( _sysapi_processor_flags_raw != NULL ) {
        return &theInfo;
    }

    /* Set the default to the empty string so that if something goes wrong
       during parsing (or if /proc/cpuinfo doesn't exist), we stop trying. */
    _sysapi_processor_flags_raw = "";

    /* If we check for the null string, we could leak memory for processors
       without flags.  (We shouldn't see any, but...) */
    int foundProcessorFlags = 0;

    /* You can adapt this to ncpus.cpp's _SysapiProcCpuinfo for debugging. */
    FILE * fp = safe_fopen_wrapper_follow( "/proc/cpuinfo", "r", 0644 );
    dprintf( D_LOAD, "Reading from /proc/cpuinfo\n" );
    if( fp ) {
        int size = 128;
        char * buffer = (char *)malloc( size );
        if( buffer == NULL ) {
            EXCEPT( "Failed to allocate buffer for parsing /proc/cpuinfo.\n" );
        }

        while( fgets( buffer, size, fp ) ) {
            while( strchr( buffer, '\n' ) == NULL ) {
                char * newBuffer = (char *)realloc( buffer, size + size );
                if( newBuffer == NULL ) {
                    EXCEPT( "Failed to allocate memory for a long line in /proc/cpuinfo.\n" );
                }
                buffer = newBuffer;

                newBuffer = buffer + strlen( buffer );
                if( ! fgets( newBuffer, size, fp ) ) {
                    /* If we fail a read before finding the end of the line,
                       something has probably gone terribly, terribly wrong. */
                    EXCEPT( "Failed to find end of line ('%s') before end of file.\n", buffer );
                    // If /proc/cpuinfo regularly terminates without a newline,
                    // we could do this instead.
                    // free( buffer );
                    // fclose( fp );
                    // return _sysapi_processor_flags_raw;
                }
                size += size;
            }

            char * colon = strchr( buffer, ':' );

            const char * value = "";
            const char * attribute = NULL;
            if( colon != NULL ) {
                for( unsigned int v = 1; colon[v] != '\0' && isspace( colon[v] ); ++v ) {
                    value = colon + v;
                }

                char * tmp = colon;
                while( isspace( *tmp ) || (*tmp == ':' ) ) {
                    *tmp = '\0';
                    --tmp;
                }
                attribute = buffer;

                if( strcmp( attribute, "flags" ) == 0 ) {
                    if( foundProcessorFlags == 0 ) {
                        /* This is where we assume flags fits into buffer. */
                        _sysapi_processor_flags_raw = strdup( value );
                        if( _sysapi_processor_flags_raw == NULL ) {
                            EXCEPT( "Failed to allocate memory for the raw processor flags.\n" );
                        }
                    } else {
                        if( strcmp( _sysapi_processor_flags_raw, value ) != 0 ) {
                            dprintf( D_ALWAYS, "WARNING: Processor flags '%s' and '%s' are not the same; using the former.\n", _sysapi_processor_flags_raw, value );
                        }
                    }

                    foundProcessorFlags += 1;
                } else if (strcmp(attribute, "model") == 0) {
			int tmp = 0;
			int r = sscanf(value, "%d", &tmp);
			if (r > 0) theInfo.model_no = tmp;
		} else if (strcmp(attribute,"cpu family") == 0) {
			int tmp = 0;
			int r = sscanf(value, "%d", &tmp);
			if (r > 0) theInfo.family = tmp;
		} else if (strcmp(attribute,"cache size") == 0) {
			int tmp = 0;
			int r = sscanf(value, "%d", &tmp);
			if (r > 0) theInfo.cache = tmp;
		}
            }
        }

        free( buffer );
        fclose( fp );
    }

    theInfo.processor_flags = _sysapi_processor_flags;
    return &theInfo;
}

const struct sysapi_cpuinfo *sysapi_processor_flags( void ) {
    sysapi_internal_reconfig();

    if( _sysapi_processor_flags != NULL ) {
        return &theInfo;
    }

    if( _sysapi_processor_flags_raw == NULL ) {
        sysapi_processor_flags_raw();
        ASSERT(_sysapi_processor_flags_raw != NULL);
    }

    /* Which flags do we care about?  You MUST terminate this list with NULL. */
    static const char * const flagNames[] = { "avx", "avx2", "avx512", "ssse3", "sse4_1", "sse4_2", NULL };

    /* Do some memory-allocation math. */
    int numFlags = 0;
    int maxFlagLength = 0;
    for( int i = 0; flagNames[i] != NULL; ++i ) {
        ++numFlags;
        int curFlagLength = (int)strlen( flagNames[i] );
        if( curFlagLength > maxFlagLength ) { maxFlagLength = curFlagLength; }
    }

    char * currentFlag = (char *)malloc( (1 + maxFlagLength) * sizeof( char ) );
    if( currentFlag == NULL ) {
        EXCEPT( "Failed to allocate memory for current processor flag." );
    }
    currentFlag[0] = '\0';

    /* If we track which flags we have, we can make sure the order we
       print them is the same regardless of the raw flags order. */
    const char ** flags = (const char **)malloc( sizeof( const  char * ) * numFlags );
    if( flags == NULL ) {
        EXCEPT( "Failed to allocate memory for processor flags." );
    }
    for( int i = 0; i < numFlags; ++i ) { flags[i] = ""; }

    const char * flagStart = _sysapi_processor_flags_raw;
    const char * flagEnd = _sysapi_processor_flags_raw;
    while( * flagStart != '\0' ) {
        if( * flagStart == ' ' ) { ++flagStart; continue; }

        for( flagEnd = flagStart; (* flagEnd != '\0') && (* flagEnd != ' '); ++flagEnd ) { ; }

        int flagSize = (flagEnd - flagStart) / (int)sizeof( char );
        if( flagSize > maxFlagLength ) {
            flagStart = flagEnd;
            continue;
        }

        /* We know that flagStart is neither ' ' nor '\0', so we must have
           at least one character.  Because we only care about flags of a
           certain length or smaller, we know we won't overflow our buffer. */
        strncpy( currentFlag, flagStart, flagSize );
        currentFlag[flagSize] = '\0';

        for( int i = 0; flagNames[i] != NULL; ++i ) {
            if( strcmp( currentFlag, flagNames[i] ) == 0 ) {
                /* Add to the flags. */
                flags[i] = flagNames[i];
                break;
            }
        }

        flagStart = flagEnd;
    }
    free( currentFlag );

    /* How much space do we need? */
    int flagsLength = 1;
    for( int i = 0; i < numFlags; ++i ) {
        int length = (int)strlen( flags[i] );
        if( length ) { flagsLength += length + 1; }
    }

    if( flagsLength == 1 ) {
        _sysapi_processor_flags = "none";
    } else {
        char * processor_flags = (char *)malloc( sizeof( char ) * flagsLength );
        if( processor_flags == NULL ) {
            EXCEPT( "Failed to allocate memory for processor flag list." );
        }
        processor_flags[0] = '\0';

        /* This way, the flags will always print out in the same order. */
        for( int i = 0; i < numFlags; ++i ) {
            if( strlen( flags[i] ) ) {
                strcat( processor_flags, flags[i] );
                strcat( processor_flags, " " );
            }
        }

        /* Remove the trailing space. */
        processor_flags[ flagsLength - 2 ] = '\0';
        _sysapi_processor_flags = processor_flags;
    }

    free( flags );
    theInfo.processor_flags = _sysapi_processor_flags;
    return &theInfo;
}

// ----------------------------------------------------------------------------

#if defined( PROCESSOR_FLAGS_TESTING )

/*
 * To compile:

g++ -DGLIBC=GLIBC -DHAVE_CONFIG_H -DLINUX -DPROCESSOR_FLAGS_TESTING \
    -I../condor_includes -I../condor_utils -I../safefile \
    -I<configured build directory>/src/condor_includes \
    processor_flags.cpp

 *
 */

// Required to link.
char * _sysapi_processor_flags = NULL;
char * _sysapi_processor_flags_raw = NULL;
void sysapi_internal_reconfig( void ) { ; }
int _EXCEPT_Line;
int _EXCEPT_Errno;
const char * _EXCEPT_File;
void _EXCEPT_ ( const char * fmt, ... ) { exit( 1 ); }

int main( int argc, char ** argv ) {
    const char * expected;

    expected = "sse4_1 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"test1 sse4_1 sse3 ssse3 test2 testFour";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "none";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"test1 test2 testFour";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "none";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_1 sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"sse4_1 sse4_2 ssse3";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_1 sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"sse4_1 sse4_2 ssse3 ssse3";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_1 sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"sse4_1 sse4_2 ssse3 ssse3";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_1 sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"ssse3 sse4_1 sse4_2 ssse3 ssse3";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    expected = "sse4_2 ssse3";
    _sysapi_processor_flags = NULL;
    _sysapi_processor_flags_raw = (char *)"test1 sse4_2 sse3 ssse3 test2 testFour";
    fprintf( stdout, "Expected '%s', got '%s'.\n", expected, sysapi_processor_flags() );

    return 0;
}

#endif
<|endoftext|>


<commit_before>#include "scraper.hpp"

#include <iostream>
#include <deque>
#include <fstream>
#include <boost/locale.hpp>

#include "parsers/category_parser.hpp"
#include "parsers/subcategory_parser.hpp"
#include "parsers/product_parser.hpp"

namespace supermarx
{
	scraper::scraper(callback_t callback_)
	: callback(callback_)
	, dl("supermarx albert/1.0")
	{}

	void scraper::scrape()
	{
		static const std::string domain_uri = "http://www.ah.nl";
		std::deque<std::string> todo;

		category_parser c_p(
			[&](const category_parser::category_uri_t c)
		{
			subcategory_parser sc_p(
				[&](const subcategory_parser::subcategory_uri_t sc)
			{
				std::cout << sc << std::endl;
				todo.push_back(sc);
			});

			std::cout << c << std::endl;

			sc_p.parse(boost::locale::conv::to_utf<char>(dl.fetch(domain_uri + c), "iso88591"));
		});

		c_p.parse(boost::locale::conv::to_utf<char>(dl.fetch(domain_uri + "/appie/producten"), "iso88591"));

		while(!todo.empty())
		{
			std::string current_uri = todo.front();
			todo.pop_front();

			std::cout << current_uri << std::endl;

			product_parser p_p(
			[&](const std::string uri)
			{
				todo.push_front(uri);
			},
			callback);

			p_p.parse(boost::locale::conv::to_utf<char>(dl.fetch(domain_uri + current_uri), "iso88591"));
		}
	}
}
<commit_msg>Fixed rename of product page<commit_after>#include "scraper.hpp"

#include <iostream>
#include <deque>
#include <fstream>
#include <boost/locale.hpp>

#include "parsers/category_parser.hpp"
#include "parsers/subcategory_parser.hpp"
#include "parsers/product_parser.hpp"

namespace supermarx
{
	scraper::scraper(callback_t callback_)
	: callback(callback_)
	, dl("supermarx albert/1.0")
	{}

	void scraper::scrape()
	{
		static const std::string domain_uri = "http://www.ah.nl";
		std::deque<std::string> todo;

		category_parser c_p(
			[&](const category_parser::category_uri_t c)
		{
			subcategory_parser sc_p(
				[&](const subcategory_parser::subcategory_uri_t sc)
			{
				std::cout << sc << std::endl;
				todo.push_back(sc);
			});

			std::cout << c << std::endl;

			sc_p.parse(boost::locale::conv::to_utf<char>(dl.fetch(domain_uri + c), "iso88591"));
		});

		c_p.parse(boost::locale::conv::to_utf<char>(dl.fetch(domain_uri + "/producten"), "iso88591"));

		while(!todo.empty())
		{
			std::string current_uri = todo.front();
			todo.pop_front();

			std::cout << current_uri << std::endl;

			product_parser p_p(
			[&](const std::string uri)
			{
				todo.push_front(uri);
			},
			callback);

			p_p.parse(boost::locale::conv::to_utf<char>(dl.fetch(domain_uri + current_uri), "iso88591"));
		}
	}
}
<|endoftext|>


<commit_before>// -*- coding: us-ascii-unix -*-
// Copyright 2012 Lukas Kemmer
//
// 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 <cassert>
#include <functional>
#include "commands/text-entry-cmd.hh"
#include "editors/text-entry-util.hh"
#include "geo/rect.hh"
#include "objects/objtext.hh"
#include "rendering/overlay.hh"
#include "tasks/task.hh"
#include "tasks/text-edit.hh"
#include "tasks/text-select.hh"
#include "tools/tool-actions.hh"
#include "text/auto-complete.hh"
#include "text/char-constants.hh"
#include "text/formatting.hh"
#include "tools/tool-contexts.hh"
#include "util-wx/key-codes.hh"
#include "util/command-util.hh"
#include "util/pos-info.hh"
#include "util/undo-redo.hh"

namespace faint{

class TextCommand{
public:
  TextCommand(const std::function<void()>& doFunc,
    const std::function<void()>& undoFunc,
    const utf8_string& name)
    : m_do(doFunc),
      m_undo(undoFunc),
      m_name(name)
  {}

  void Do(){
    m_do();
  }

  const utf8_string& GetName() const{
    return m_name;
  }

  void Undo(){
    m_undo();
  }
private:
  std::function<void()> m_do;
  std::function<void()> m_undo;
  utf8_string m_name;
};

inline bool is_exit_key(const KeyPress& key){
  return key.Is(Ctrl, key::enter) ||
    key.Is(key::esc);
}

inline bool outside(Object* obj, const PosInfo& info){
  return !bounding_rect(obj->GetTri()).Contains(info.pos);
}

inline bool right_click(const PosInfo& info){
  return info.modifiers.RightMouse();
}

static Optional<TextCommand> handle_command_key(const KeyPress& key,
  ObjText* obj)
{
  auto toggle = [obj](const auto& setting){
    return [obj, setting](){
      return obj->Set(setting, obj->GetSettings().Not(setting));
    };
  };

  if (key.Is(Ctrl, key::B)){
    auto toggleBold = toggle(ts_FontBold);
    return option(TextCommand(toggleBold, toggleBold, "Toggle bold"));
  }
  else if (key.Is(Ctrl, key::I)){
    auto toggleItalic = toggle(ts_FontItalic);
    return option(TextCommand(toggleItalic, toggleItalic, "Toggle italic"));
  }
  return no_option();
}

static bool handle_completion(AutoCompleteState& autoComplete, TextBuffer& text){
  // Find the preceeding backslash or character halting auto-completion
  const size_t bs = text.prev_any_of(utf8_string(chars::backslash) +
    utf8_string(chars::space) +
    utf8_string(chars::comma) +
    utf8_string(chars::eol));

  if (bs == utf8_string::npos || text.at(bs) != chars::backslash){
    return false;
  }

  auto completion = autoComplete.Empty() ?
    autoComplete.Complete(text.get().substr(bs, text.caret() - bs)) :
    autoComplete.Next();
  text.select(CaretRange(bs, text.caret()));
  text.insert(completion);
  return true;
}

static void select_word_at_pos(ObjText* textObject, const Point& pos){
  size_t caret = textObject->CaretPos(pos);
  TextBuffer& textBuffer = textObject->GetTextBuffer();
  textBuffer.select(word_boundaries(caret, textBuffer));
}

static AutoComplete& text_auto_complete(){
  static AutoComplete ac(expression_names());
  return ac;
}

class EditText : public Task,
                 public TextContext,
                 public SelectionContext,
                 public HistoryContext{
public:
  EditText(const Rect& r,
    const utf8_string& str,
    Settings& settings,
    ToolActions& actions)
    : m_actions(actions),
      m_active(false),
      m_autoComplete(text_auto_complete()),
      m_newTextObject(true),
      m_settings(settings),
      m_textObject(new ObjText(tri_from_rect(r), str, settings))
  {}

  EditText(ObjText* obj, Settings& s, ToolActions& actions)
    : m_actions(actions),
      m_active(false),
      m_autoComplete(text_auto_complete()),
      m_newTextObject(false),
      m_oldText(obj->GetTextBuffer().get()),
      m_settings(s),
      m_textObject(obj)
  {
    m_settings = m_textObject->GetSettings();
  }

  ~EditText(){
    if (m_active){
      EndEntry();
    }
  }

  void Activate() override{
    assert(!m_active);
    m_textObject->SetActive(true);
    m_textObject->SetEdited(true);
    TextBuffer& buf(m_textObject->GetTextBuffer());
    buf.caret(buf.size());
    m_actions.BeginTextEntry();
    m_active = true;
  }

  bool AcceptsPastedText() const override{
    return true;
  }

  bool AllowsGlobalRedo() const override{
    return !m_active;
  }

  bool CanUndo() const override{
    return m_active && m_states.CanUndo();
  }

  bool CanRedo() const override{
    return m_active && m_states.CanRedo();
  }

  utf8_string GetRedoName() const override{
    return m_states.PeekRedo().GetName();
  }

  utf8_string GetUndoName() const override{
    return m_states.PeekUndo().GetName();
  }

  bool RefreshOnMouseOut() const override{
    return false;
  }

  void Redo() override{
    m_states.Redo().Do();
  }

  void Undo() override{
    m_states.Undo().Undo();
  }

  TaskResult Char(const KeyInfo& info) override{
    auto handleKeyPress = [&](){
      m_autoComplete.Forget();
      return handle_key_press(info.key, m_textObject->GetTextBuffer()) ?
        TaskResult::DRAW : TaskResult::NONE;
    };

    if (is_exit_key(info.key)){
      return Commit(info.layerType);
    }
    else if (info.key.Is(key::tab) && m_settings.Get(ts_ParseExpressions)){
      return handle_completion(m_autoComplete, m_textObject->GetTextBuffer()) ?
        TaskResult::DRAW :
        handleKeyPress();
    }

    return handle_command_key(info.key, m_textObject).Visit(
      [&](TextCommand& cmd){
        m_autoComplete.Forget();
        // Fixme: Need to support undo
        cmd.Do();
        m_states.Did(cmd);
        return TaskResult::DRAW;
      },
      handleKeyPress);
  }

  Optional<utf8_string> CopyText() const override{
    TextBuffer& buffer(m_textObject->GetTextBuffer());
    return option(buffer.get_selection());
  }

  Optional<utf8_string> CutText() override{
    TextBuffer& buffer(m_textObject->GetTextBuffer());
    Optional<utf8_string> s = buffer.get_selection();
    buffer.del();
    return s;
  }

  bool Delete() override{
    m_textObject->GetTextBuffer().del();
    return true;
  }

  bool Deselect() override{
    m_textObject->GetTextBuffer().select_none();
    return true;
  }

  TaskResult DoubleClick(const PosInfo& info) override{
    select_word_at_pos(m_textObject, info.pos);
    return TaskResult::DRAW;
  }

  void Draw(FaintDC& dc, Overlays& overlays, const PosInfo& info) override{
    overlays.Corners(m_textObject->GetTri());

    if (m_newTextObject){
      m_textObject->Draw(dc, get_expression_context(info));
    }
    if (!m_textObject->HasSelectedRange()){
      overlays.Caret(m_textObject->GetCaret());
    }
  }

  bool DrawBeforeZoom(Layer layer) const override{
    // This is only relevant when first editing raster text or creating
    // a new text object - when editing an existing object, the text
    // object draws itself, independently of the task.
    return layer == Layer::RASTER;
  }

  bool EatsSettings() const override{
    return false; // Fixme: Actually... dunno
  }

  Command* GetCommand() override{
    return m_command.Take();
  }

  Cursor GetCursor(const PosInfo& info) const override{
    if (m_textObject->GetTri().Contains(info.pos)){
      return Cursor::CARET;
    }
    return Cursor::ARROW;
  }

  Task* GetNewTask() override{
    return m_newTask.Take();
  }

  IntRect GetRefreshRect(const RefreshInfo&) const override{
    return m_textObject->GetRefreshRect();
  }

  TaskResult MouseDown(const PosInfo& info) override{
    assert(m_textObject != nullptr);
    if (right_click(info) || outside(m_textObject, info)){
      return Commit(info.layerType);
    }
    m_newTask.Set(select_text_task(m_textObject, m_newTextObject, info.pos));
    return TaskResult::PUSH;
  }

  TaskResult MouseUp(const PosInfo&) override{
    return TaskResult::NONE;
  }

  TaskResult MouseMove(const PosInfo& info) override{
    info.status.SetMainText("Use Ctrl+Enter to stop editing");
    info.status.SetText(str_floor(info.pos));
    return TaskResult::NONE;
  }

  Optional<const faint::HistoryContext&> HistoryContext() const override{
    return Optional<const faint::HistoryContext&>(*this);
  }

  void Paste(const utf8_string& str) override{
    m_textObject->GetTextBuffer().insert(str);
  }

  TaskResult Preempt(const PosInfo& info) override{
    return Commit(info.layerType);
  }

  bool SelectAll() override{
    TextBuffer& text = m_textObject->GetTextBuffer();
    text.select(text.all());
    return true;
  }

  void SelectionChange() override{
  }

  Optional<const faint::SelectionContext&> SelectionContext() const override{
    return Optional<const faint::SelectionContext&>(*this);
  }

  Optional<const faint::TextContext&> TextContext() const override{
    return Optional<const faint::TextContext&>(*this);
  }

  void UpdateSettings() override{
    m_textObject->UpdateSettings(m_settings);
  }

  void SetLayer(Layer) override{
  }

  EditText& operator=(const EditText&) = delete;

private:
  void EndEntry(){
    assert(m_active);

    m_actions.EndTextEntry();
    if (m_textObject != nullptr){
      m_textObject->SetActive(false);
      m_textObject->SetEdited(false);
    }
    m_active = false;
  }

  TaskResult Commit(Layer layerType){
    if (m_active){
      EndEntry();
    }
    if (m_newTextObject){
      m_command.Set(add_or_draw(m_textObject, layerType));
      return TaskResult::COMMIT_AND_CHANGE;
    }

    const utf8_string& newText(m_textObject->GetTextBuffer().get());
    if (newText == m_oldText){
      // Text unchanged, create no no command.
      return TaskResult::CHANGE;
    }
    m_command.Set(text_entry_command(m_textObject, New(newText), Old(m_oldText)));
    return TaskResult::COMMIT_AND_CHANGE;
  }

  ToolActions& m_actions;
  bool m_active;
  AutoCompleteState m_autoComplete;
  PendingCommand m_command;
  PendingTask m_newTask;
  bool m_newTextObject;
  utf8_string m_oldText;
  Settings& m_settings;
  UndoRedo<TextCommand> m_states;
  ObjText* m_textObject;
};

Task* edit_text_task(const Rect& r,
  const utf8_string& str,
  Settings& settings,
  ToolActions& actions)
{
  return new EditText(r, str, settings, actions);
}

Task* edit_text_task(ObjText* obj, Settings& settings, ToolActions& actions){
  return new EditText(obj, settings, actions);
}

} // namespace
<commit_msg>Undo for text object style changes.<commit_after>// -*- coding: us-ascii-unix -*-
// Copyright 2012 Lukas Kemmer
//
// 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 <cassert>
#include <functional>
#include "commands/command-bunch.hh"
#include "commands/text-entry-cmd.hh"
#include "editors/text-entry-util.hh"
#include "geo/rect.hh"
#include "objects/objtext.hh"
#include "rendering/overlay.hh"
#include "tasks/task.hh"
#include "tasks/text-edit.hh"
#include "tasks/text-select.hh"
#include "tools/tool-actions.hh"
#include "text/auto-complete.hh"
#include "text/char-constants.hh"
#include "text/formatting.hh"
#include "tools/tool-contexts.hh"
#include "util-wx/key-codes.hh"
#include "util/command-util.hh"
#include "util/pos-info.hh"
#include "util/undo-redo.hh"

namespace faint{

class TextChange{
public:
  TextChange(const std::function<void()>& doFunc,
    const std::function<void()>& undoFunc,
    const utf8_string& name)
    : m_do(doFunc),
      m_undo(undoFunc),
      m_name(name)
  {}

  void Do(){
    m_do();
  }

  const utf8_string& GetName() const{
    return m_name;
  }

  void Undo(){
    m_undo();
  }
private:
  std::function<void()> m_do;
  std::function<void()> m_undo;
  utf8_string m_name;
};

class TextCommand : public Command{
public:
  TextCommand(const TextChange& change)
    : Command(CommandType::OBJECT),
      m_change(change)
  {}

  void Do(CommandContext&) override{
    m_change.Do();
  }

  void Undo(CommandContext&) override{
    m_change.Undo();
  }

  utf8_string Name() const override{
    return m_change.GetName();
  }

  TextCommand& operator=(const TextCommand&) = delete;

private:
  TextChange m_change;
};

inline bool is_exit_key(const KeyPress& key){
  return key.Is(Ctrl, key::enter) ||
    key.Is(key::esc);
}

inline bool outside(Object* obj, const PosInfo& info){
  return !bounding_rect(obj->GetTri()).Contains(info.pos);
}

inline bool right_click(const PosInfo& info){
  return info.modifiers.RightMouse();
}

static Optional<TextChange> handle_command_key(const KeyPress& key,
  ObjText* obj)
{
  auto setter = [obj](const auto& setting, const auto& value){
    return [obj, setting, value](){
      return obj->Set(setting, value);
    };
  };

  if (key.Is(Ctrl, key::B)){
    auto bold = obj->GetSettings().Get(ts_FontBold);
    return option(TextChange(setter(ts_FontBold, !bold),
      setter(ts_FontBold, bold),
      bold ? "Clear bold" : "Set bold"));
  }
  else if (key.Is(Ctrl, key::I)){
    auto italic = obj->GetSettings().Get(ts_FontItalic);
    return option(TextChange(setter(ts_FontItalic, !italic),
      setter(ts_FontItalic, italic),
      italic ? "Clear italic" : "Set italic"));
  }
  return no_option();
}

static bool handle_completion(AutoCompleteState& autoComplete, TextBuffer& text){
  // Find the preceeding backslash or character halting auto-completion
  const size_t bs = text.prev_any_of(utf8_string(chars::backslash) +
    utf8_string(chars::space) +
    utf8_string(chars::comma) +
    utf8_string(chars::eol));

  if (bs == utf8_string::npos || text.at(bs) != chars::backslash){
    return false;
  }

  auto completion = autoComplete.Empty() ?
    autoComplete.Complete(text.get().substr(bs, text.caret() - bs)) :
    autoComplete.Next();
  text.select(CaretRange(bs, text.caret()));
  text.insert(completion);
  return true;
}

static void select_word_at_pos(ObjText* textObject, const Point& pos){
  size_t caret = textObject->CaretPos(pos);
  TextBuffer& textBuffer = textObject->GetTextBuffer();
  textBuffer.select(word_boundaries(caret, textBuffer));
}

static AutoComplete& text_auto_complete(){
  static AutoComplete ac(expression_names());
  return ac;
}

class EditText : public Task,
                 public TextContext,
                 public SelectionContext,
                 public HistoryContext{
public:
  EditText(const Rect& r,
    const utf8_string& str,
    Settings& settings,
    ToolActions& actions)
    : m_actions(actions),
      m_active(false),
      m_autoComplete(text_auto_complete()),
      m_newTextObject(true),
      m_settings(settings),
      m_textObject(new ObjText(tri_from_rect(r), str, settings))
  {}

  EditText(ObjText* obj, Settings& s, ToolActions& actions)
    : m_actions(actions),
      m_active(false),
      m_autoComplete(text_auto_complete()),
      m_newTextObject(false),
      m_oldText(obj->GetTextBuffer().get()),
      m_settings(s),
      m_textObject(obj)
  {
    m_settings = m_textObject->GetSettings();
  }

  ~EditText(){
    if (m_active){
      EndEntry();
    }
  }

  void Activate() override{
    assert(!m_active);
    m_textObject->SetActive(true);
    m_textObject->SetEdited(true);
    TextBuffer& buf(m_textObject->GetTextBuffer());
    buf.caret(buf.size());
    m_actions.BeginTextEntry();
    m_active = true;
  }

  bool AcceptsPastedText() const override{
    return true;
  }

  bool AllowsGlobalRedo() const override{
    return !m_active;
  }

  bool CanUndo() const override{
    return m_active && m_states.CanUndo();
  }

  bool CanRedo() const override{
    return m_active && m_states.CanRedo();
  }

  utf8_string GetRedoName() const override{
    return m_states.PeekRedo().GetName();
  }

  utf8_string GetUndoName() const override{
    return m_states.PeekUndo().GetName();
  }

  bool RefreshOnMouseOut() const override{
    return false;
  }

  void Redo() override{
    m_states.Redo().Do();
  }

  void Undo() override{
    m_states.Undo().Undo(); // Fixme: Bizarre: Undo just moves between lists.
  }

  TaskResult Char(const KeyInfo& info) override{
    auto handleKeyPress = [&](){
      m_autoComplete.Forget();
      return handle_key_press(info.key, m_textObject->GetTextBuffer()) ?
        TaskResult::DRAW : TaskResult::NONE;
    };

    if (is_exit_key(info.key)){
      return Commit(info.layerType);
    }
    else if (info.key.Is(key::tab) && m_settings.Get(ts_ParseExpressions)){
      return handle_completion(m_autoComplete, m_textObject->GetTextBuffer()) ?
        TaskResult::DRAW :
        handleKeyPress();
    }

    return handle_command_key(info.key, m_textObject).Visit(
      [&](TextChange& cmd){
        m_autoComplete.Forget();
        // Fixme: Need to support undo
        cmd.Do();
        m_states.Did(cmd);
        return TaskResult::DRAW;
      },
      handleKeyPress);
  }

  Optional<utf8_string> CopyText() const override{
    TextBuffer& buffer(m_textObject->GetTextBuffer());
    return option(buffer.get_selection());
  }

  Optional<utf8_string> CutText() override{
    TextBuffer& buffer(m_textObject->GetTextBuffer());
    Optional<utf8_string> s = buffer.get_selection();
    buffer.del();
    return s;
  }

  bool Delete() override{
    m_textObject->GetTextBuffer().del();
    return true;
  }

  bool Deselect() override{
    m_textObject->GetTextBuffer().select_none();
    return true;
  }

  TaskResult DoubleClick(const PosInfo& info) override{
    select_word_at_pos(m_textObject, info.pos);
    return TaskResult::DRAW;
  }

  void Draw(FaintDC& dc, Overlays& overlays, const PosInfo& info) override{
    overlays.Corners(m_textObject->GetTri());

    if (m_newTextObject){
      m_textObject->Draw(dc, get_expression_context(info));
    }
    if (!m_textObject->HasSelectedRange()){
      overlays.Caret(m_textObject->GetCaret());
    }
  }

  bool DrawBeforeZoom(Layer layer) const override{
    // This is only relevant when first editing raster text or creating
    // a new text object - when editing an existing object, the text
    // object draws itself, independently of the task.
    return layer == Layer::RASTER;
  }

  bool EatsSettings() const override{
    return false; // Fixme: Actually... dunno
  }

  Command* GetCommand() override{
    return m_command.Take();
  }

  Cursor GetCursor(const PosInfo& info) const override{
    if (m_textObject->GetTri().Contains(info.pos)){
      return Cursor::CARET;
    }
    return Cursor::ARROW;
  }

  Task* GetNewTask() override{
    return m_newTask.Take();
  }

  IntRect GetRefreshRect(const RefreshInfo&) const override{
    return m_textObject->GetRefreshRect();
  }

  TaskResult MouseDown(const PosInfo& info) override{
    assert(m_textObject != nullptr);
    if (right_click(info) || outside(m_textObject, info)){
      return Commit(info.layerType);
    }
    m_newTask.Set(select_text_task(m_textObject, m_newTextObject, info.pos));
    return TaskResult::PUSH;
  }

  TaskResult MouseUp(const PosInfo&) override{
    return TaskResult::NONE;
  }

  TaskResult MouseMove(const PosInfo& info) override{
    info.status.SetMainText("Use Ctrl+Enter to stop editing");
    info.status.SetText(str_floor(info.pos));
    return TaskResult::NONE;
  }

  Optional<const faint::HistoryContext&> HistoryContext() const override{
    return Optional<const faint::HistoryContext&>(*this);
  }

  void Paste(const utf8_string& str) override{
    m_textObject->GetTextBuffer().insert(str);
  }

  TaskResult Preempt(const PosInfo& info) override{
    return Commit(info.layerType);
  }

  bool SelectAll() override{
    TextBuffer& text = m_textObject->GetTextBuffer();
    text.select(text.all());
    return true;
  }

  void SelectionChange() override{
  }

  Optional<const faint::SelectionContext&> SelectionContext() const override{
    return Optional<const faint::SelectionContext&>(*this);
  }

  Optional<const faint::TextContext&> TextContext() const override{
    return Optional<const faint::TextContext&>(*this);
  }

  void UpdateSettings() override{
    m_textObject->UpdateSettings(m_settings);
  }

  void SetLayer(Layer) override{
  }

  EditText& operator=(const EditText&) = delete;

private:
  void EndEntry(){
    assert(m_active);

    m_actions.EndTextEntry();
    if (m_textObject != nullptr){
      m_textObject->SetActive(false);
      m_textObject->SetEdited(false);
    }
    m_active = false;
  }

  TaskResult Commit(Layer layerType){
    if (m_active){
      EndEntry();
    }
    if (m_newTextObject){
      m_command.Set(add_or_draw(m_textObject, layerType));
      return TaskResult::COMMIT_AND_CHANGE;
    }

    const utf8_string& newText(m_textObject->GetTextBuffer().get());
    std::deque<Command*> cmds;
    while (m_states.CanUndo()){
      // Fixme: Dificult to understand (first undo just moves between lists)
      TextChange& change = m_states.Undo();
      change.Undo();
      cmds.push_front(new TextCommand(change));
    }

    if (newText != m_oldText){
      // Fixme: Do all changes, also text edits, via TextChange instead
      cmds.push_back(text_entry_command(m_textObject,
        New(newText),
        Old(m_oldText)));
    }

    if (cmds.empty()){
      // No changes - create no command.
      return TaskResult::CHANGE;
    }
    else {
      m_command.Set(cmds.size() == 1 ?
        cmds.back() :
        command_bunch(CommandType::OBJECT, bunch_name("Modify Text"), cmds));
      return TaskResult::COMMIT_AND_CHANGE;
    }
  }

  ToolActions& m_actions;
  bool m_active;
  AutoCompleteState m_autoComplete;
  PendingCommand m_command;
  PendingTask m_newTask;
  bool m_newTextObject;
  utf8_string m_oldText;
  Settings& m_settings;
  UndoRedo<TextChange> m_states;
  ObjText* m_textObject;
};

Task* edit_text_task(const Rect& r,
  const utf8_string& str,
  Settings& settings,
  ToolActions& actions)
{
  return new EditText(r, str, settings, actions);
}

Task* edit_text_task(ObjText* obj, Settings& settings, ToolActions& actions){
  return new EditText(obj, settings, actions);
}

} // namespace
<|endoftext|>


<commit_before>// Filename: pfmBba.cxx
// Created by:  drose (02Mar11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "pfmBba.h"
#include "config_pfm.h"
#include "pfmFile.h"
#include "pystub.h"

////////////////////////////////////////////////////////////////////
//     Function: PfmBba::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
PfmBba::
PfmBba() {
  set_program_description
    ("pfm-bba generates a .bba file from a .pfm file that lists the "
     "planar bounding volume of the pfm's internal data.");

  add_option
    ("z", "", 0,
     "Treats (0,0,0) in the pfm file as a special don't-touch value.",
     &PfmBba::dispatch_none, &_got_zero_special);

  add_option
    ("o", "filename", 50,
     "Specify the filename to which the resulting bba file will be written.",
     &PfmBba::dispatch_filename, &_got_output_filename, &_output_filename);
}

////////////////////////////////////////////////////////////////////
//     Function: PfmBba::run
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
void PfmBba::
run() {
  Filenames::const_iterator fi;
  for (fi = _input_filenames.begin(); fi != _input_filenames.end(); ++fi) {
    PfmFile file;
    if (!file.read(*fi)) {
      nout << "Cannot read " << *fi << "\n";
      exit(1);
    }
    if (!process_pfm(*fi, file)) {
      exit(1);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PfmBba::process_pfm
//       Access: Public
//  Description: Handles a single pfm file.
////////////////////////////////////////////////////////////////////
bool PfmBba::
process_pfm(const Filename &input_filename, PfmFile &file) {
  file.set_zero_special(_got_zero_special);

  Filename bba_filename;
  if (_got_output_filename) {
    bba_filename = _output_filename;
  } else {
    bba_filename = input_filename;
    bba_filename.set_extension("bba");
  }

  if (!bba_filename.empty()) {
    bba_filename.set_text();
    PT(BoundingHexahedron) bounds = file.compute_planar_bounds(LPoint2f(0.5, 0.5), pfm_bba_dist[0], pfm_bba_dist[1], false);
    nassertr(bounds != (BoundingHexahedron *)NULL, false);

    pofstream out;
    if (!bba_filename.open_write(out)) {
      cerr << "Unable to open " << bba_filename << "\n";
      return false;
    }

    LPoint3 points[8];
    for (int i = 0; i < 8; ++i) {
      points[i] = bounds->get_point(i);
    }
    LPlane plane(points[0], points[1], points[2]);
    LVector3 normal = plane.get_normal();

    static const PN_stdfloat scale = 20.0f;
    normal *= scale;
    points[0] += normal;
    points[1] += normal;
    points[2] += normal;
    points[3] += normal;

    for (int i = 0; i < 8; ++i) {
      const LPoint3 &p = points[i];
      out << p[0] << "," << p[1] << "," << p[2] << "\n";
    }
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: PfmBba::handle_args
//       Access: Protected, Virtual
//  Description: Does something with the additional arguments on the
//               command line (after all the -options have been
//               parsed).  Returns true if the arguments are good,
//               false otherwise.
////////////////////////////////////////////////////////////////////
bool PfmBba::
handle_args(ProgramBase::Args &args) {
  if (args.empty()) {
    nout << "You must specify the pfm file(s) to read on the command line.\n";
    return false;
  }

  if (args.size() > 1 && _got_output_filename) {
    nout << "Cannot use -o when multiple pfm files are specified.\n";
    return false;
  }

  Args::const_iterator ai;
  for (ai = args.begin(); ai != args.end(); ++ai) {
    _input_filenames.push_back(Filename::from_os_specific(*ai));
  }

  return true;
}

int main(int argc, char *argv[]) {
  // A call to pystub() to force libpystub.so to be linked in.
  pystub();

  PfmBba prog;
  prog.parse_command_line(argc, argv);
  prog.run();
  return 0;
}
<commit_msg>comment out bbox expansion<commit_after>// Filename: pfmBba.cxx
// Created by:  drose (02Mar11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "pfmBba.h"
#include "config_pfm.h"
#include "pfmFile.h"
#include "pystub.h"

////////////////////////////////////////////////////////////////////
//     Function: PfmBba::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
PfmBba::
PfmBba() {
  set_program_description
    ("pfm-bba generates a .bba file from a .pfm file that lists the "
     "planar bounding volume of the pfm's internal data.");

  add_option
    ("z", "", 0,
     "Treats (0,0,0) in the pfm file as a special don't-touch value.",
     &PfmBba::dispatch_none, &_got_zero_special);

  add_option
    ("o", "filename", 50,
     "Specify the filename to which the resulting bba file will be written.",
     &PfmBba::dispatch_filename, &_got_output_filename, &_output_filename);
}

////////////////////////////////////////////////////////////////////
//     Function: PfmBba::run
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
void PfmBba::
run() {
  Filenames::const_iterator fi;
  for (fi = _input_filenames.begin(); fi != _input_filenames.end(); ++fi) {
    PfmFile file;
    if (!file.read(*fi)) {
      nout << "Cannot read " << *fi << "\n";
      exit(1);
    }
    if (!process_pfm(*fi, file)) {
      exit(1);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PfmBba::process_pfm
//       Access: Public
//  Description: Handles a single pfm file.
////////////////////////////////////////////////////////////////////
bool PfmBba::
process_pfm(const Filename &input_filename, PfmFile &file) {
  file.set_zero_special(_got_zero_special);

  Filename bba_filename;
  if (_got_output_filename) {
    bba_filename = _output_filename;
  } else {
    bba_filename = input_filename;
    bba_filename.set_extension("bba");
  }

  if (!bba_filename.empty()) {
    bba_filename.set_text();
    PT(BoundingHexahedron) bounds = file.compute_planar_bounds(LPoint2f(0.5, 0.5), pfm_bba_dist[0], pfm_bba_dist[1], false);
    nassertr(bounds != (BoundingHexahedron *)NULL, false);

    pofstream out;
    if (!bba_filename.open_write(out)) {
      cerr << "Unable to open " << bba_filename << "\n";
      return false;
    }

    LPoint3 points[8];
    for (int i = 0; i < 8; ++i) {
      points[i] = bounds->get_point(i);
    }

    // Experiment with expanding the back wall backwards.
    /*
    LPlane plane(points[0], points[1], points[2]);
    LVector3 normal = plane.get_normal();

    static const PN_stdfloat scale = 20.0f;
    normal *= scale;
    points[0] += normal;
    points[1] += normal;
    points[2] += normal;
    points[3] += normal;
    */

    for (int i = 0; i < 8; ++i) {
      const LPoint3 &p = points[i];
      out << p[0] << "," << p[1] << "," << p[2] << "\n";
    }
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: PfmBba::handle_args
//       Access: Protected, Virtual
//  Description: Does something with the additional arguments on the
//               command line (after all the -options have been
//               parsed).  Returns true if the arguments are good,
//               false otherwise.
////////////////////////////////////////////////////////////////////
bool PfmBba::
handle_args(ProgramBase::Args &args) {
  if (args.empty()) {
    nout << "You must specify the pfm file(s) to read on the command line.\n";
    return false;
  }

  if (args.size() > 1 && _got_output_filename) {
    nout << "Cannot use -o when multiple pfm files are specified.\n";
    return false;
  }

  Args::const_iterator ai;
  for (ai = args.begin(); ai != args.end(); ++ai) {
    _input_filenames.push_back(Filename::from_os_specific(*ai));
  }

  return true;
}

int main(int argc, char *argv[]) {
  // A call to pystub() to force libpystub.so to be linked in.
  pystub();

  PfmBba prog;
  prog.parse_command_line(argc, argv);
  prog.run();
  return 0;
}
<|endoftext|>


<commit_before>/*
        ##########    Copyright (C) 2015 Vincenzo Pacella
        ##      ##    Distributed under MIT license, see file LICENSE
        ##      ##    or <http://opensource.org/licenses/MIT>
        ##      ##
##########      ############################################################# shaduzlabs.com #####*/

#include "catch.hpp"

#include <gfx/Canvas.h>

namespace sl
{
namespace cabl
{
namespace test
{

//--------------------------------------------------------------------------------------------------

//  ------------------
// | 0000000000000000 |
// | 0000000000000000 |
// | 0000000000000000 |
// | 0000000000000000 |
// | 0000000000000000 |
//  ------------------

TEST_CASE("Constructors, reset, bool operator", "[gfx/Canvas]")
{
  Canvas c(16, 5, Canvas::Allocation::OneBytePacksOneRowOfEightPixels);
  CHECK(c.width() == 16);
  CHECK(c.height() == 5);
}

//--------------------------------------------------------------------------------------------------

} // namespace test
} // namespace cabl
} // namespace sl
<commit_msg>enabled Canvas unit tests for real-real :)<commit_after>/*
        ##########    Copyright (C) 2015 Vincenzo Pacella
        ##      ##    Distributed under MIT license, see file LICENSE
        ##      ##    or <http://opensource.org/licenses/MIT>
        ##      ##
##########      ############################################################# shaduzlabs.com #####*/

#include "catch.hpp"

#include <gfx/Canvas.h>

namespace sl
{
namespace cabl
{
namespace test
{

//--------------------------------------------------------------------------------------------------

//  ------------------
// | 0000000000000000 |
// | 0000000000000000 |
// | 0000000000000000 |
// | 0000000000000000 |
// | 0000000000000000 |
//  ------------------

TEST_CASE("Canvas Constructor", "[gfx/Canvas]")
{
  Canvas c(16, 5, Canvas::Allocation::OneBytePacksOneRowOfEightPixels);
  CHECK(c.width() == 16);
  CHECK(c.height() == 5);
}

//--------------------------------------------------------------------------------------------------

} // namespace test
} // namespace cabl
} // namespace sl
<|endoftext|>


<commit_before>/*
 * Copyright 2009-2011 The VOTCA Development Team (http://www.votca.org)
 *
 * 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 "bondedstatistics.h"

void BondedStatistics::BeginCG(Topology *top, Topology *top_atom)
{
    InteractionContainer &ic = top->BondedInteractions();
    InteractionContainer::iterator ia;

    _bonded_values.clear();
    for(ia=ic.begin(); ia!=ic.end(); ++ia) {
        _bonded_values.CreateArray((*ia)->getName());
    }
}

void BondedStatistics::EndCG()
{
}

void BondedStatistics::EvalConfiguration(Topology *conf, Topology *conv_atom)
{
    InteractionContainer &ic = conf->BondedInteractions();
    InteractionContainer::iterator ia;

    DataCollection<double>::container::iterator is;

    for(ia=ic.begin(), is = _bonded_values.begin(); ia != ic.end(); ++ia, ++is) {
//        const string &name = (*ia)->getName();
        (*is)->push_back((*ia)->EvaluateVar(*conf));
    }
}
<commit_msg>Removed commented out code<commit_after>/*
 * Copyright 2009-2011 The VOTCA Development Team (http://www.votca.org)
 *
 * 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 "bondedstatistics.h"

void BondedStatistics::BeginCG(Topology *top, Topology *top_atom)
{
    InteractionContainer &ic = top->BondedInteractions();
    InteractionContainer::iterator ia;

    _bonded_values.clear();
    for(ia=ic.begin(); ia!=ic.end(); ++ia) {
        _bonded_values.CreateArray((*ia)->getName());
    }
}

void BondedStatistics::EndCG()
{
}

void BondedStatistics::EvalConfiguration(Topology *conf, Topology *conv_atom)
{
    InteractionContainer &ic = conf->BondedInteractions();
    InteractionContainer::iterator ia;

    DataCollection<double>::container::iterator is;
    for(ia=ic.begin(), is = _bonded_values.begin(); ia != ic.end(); ++ia, ++is) {
        (*is)->push_back((*ia)->EvaluateVar(*conf));
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2012 Intel Corporation
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Benjamin Segovia <benjamin.segovia@intel.com>
 */

/**
 * \file alloc.cpp
 * \author Benjamin Segovia <benjamin.segovia@intel.com>
 *
 *  Provides facilities to track allocations and pre-initialize memory at
 *  memory allocation and memory free time
 */
#include "sys/alloc.hpp"
#include "sys/atomic.hpp"
#include "sys/mutex.hpp"

#if GBE_DEBUG_MEMORY
#ifdef __MSVC__
#include <unordered_map>
#else
#include <tr1/unordered_map>
#endif /* __MSVC__ */
#include <cstring>
#endif /* GBE_DEBUG_MEMORY */

#if defined(__ICC__)
#include <stdint.h>
#endif /* __ICC__ */
#include <map>
#include <vector>

////////////////////////////////////////////////////////////////////////////////
/// Memory debugger
////////////////////////////////////////////////////////////////////////////////
#if GBE_DEBUG_MEMORY
namespace gbe
{

  /*! Store each allocation data */
  struct AllocData {
    INLINE AllocData(void) {}
    INLINE AllocData(int fileName_, int functionName_, int line_, intptr_t alloc_) :
      fileName(fileName_), functionName(functionName_), line(line_), alloc(alloc_) {}
    int fileName, functionName, line;
    intptr_t alloc;
  };

  /*! Store allocation information */
  struct MemDebugger {
    MemDebugger(void) : unfreedNum(0), allocNum(0) {}
    void* insertAlloc(void *ptr, const char *file, const char *function, int line);
    void removeAlloc(void *ptr);
    void dumpAlloc(void);
    void dumpData(const AllocData &data);
    /*! Count the still unfreed allocations */
    volatile intptr_t unfreedNum;
    /*! Total number of allocations done */
    volatile intptr_t allocNum;
    /*! Sorts the file name and function name strings */
    std::tr1::unordered_map<const char*, int> staticStringMap;
    /*! Each element contains the actual string */
    std::vector<const char*> staticStringVector;
    std::map<uintptr_t, AllocData> allocMap;
    /*! Protect the memory debugger accesses */
    MutexSys mutex;
  };

  void* MemDebugger::insertAlloc(void *ptr, const char *file, const char *function, int line)
  {
    if (ptr == NULL) return ptr;
    Lock<MutexSys> lock(mutex);
    const uintptr_t iptr = (uintptr_t) ptr;
    if (UNLIKELY(allocMap.find(iptr) != allocMap.end())) {
      this->dumpData(allocMap.find(iptr)->second);
      FATAL("Pointer already in map");
    }
    const auto fileIt = staticStringMap.find(file);
    const auto functionIt = staticStringMap.find(function);
    int fileName, functionName;
    if (fileIt == staticStringMap.end()) {
      staticStringVector.push_back(file);
      staticStringMap[file] = fileName = int(staticStringVector.size()) - 1;
    } else
      fileName = staticStringMap[file];
    if (functionIt == staticStringMap.end()) {
      staticStringVector.push_back(function);
      staticStringMap[function] = functionName = int(staticStringVector.size()) - 1;
    } else
      functionName = staticStringMap[function];
    allocMap[iptr] = AllocData(fileName, functionName, line, allocNum);
    unfreedNum++;
    allocNum++;
    return ptr;
  }

  void MemDebugger::removeAlloc(void *ptr)
  {
    if (ptr == NULL) return;
    Lock<MutexSys> lock(mutex);
    const uintptr_t iptr = (uintptr_t) ptr;
    FATAL_IF(allocMap.find(iptr) == allocMap.end(), "Pointer not referenced");
    allocMap.erase(iptr);
    unfreedNum--;
  }

  void MemDebugger::dumpData(const AllocData &data) {
    std::cerr << "ALLOC " << data.alloc << ": " <<
                 "file " << staticStringVector[data.fileName] << ", " <<
                 "function " << staticStringVector[data.functionName] << ", " <<
                 "line " << data.line << std::endl;
  }

  void MemDebugger::dumpAlloc(void) {
    std::cerr << "MemDebugger: Unfreed number: " << unfreedNum << std::endl;
    for (auto it = allocMap.begin(); it != allocMap.end(); ++it)
      this->dumpData(it->second);
    std::cerr << "MemDebugger: " << staticStringVector.size()
              << " allocated static strings" << std::endl;
  }

  /*! The user can deactivate the memory initialization */
  static bool memoryInitializationEnabled = true;

  /*! Declare C like interface functions here */
  static MemDebugger *memDebugger = NULL;

  /*! Stop the memory debugger */
  static void MemDebuggerEnd(void) {
    MemDebugger *_debug = memDebugger;
    memDebugger = NULL;
    delete _debug;
  }

  /*! Use this to serialize multiple starts of the debugger */
  static MutexSys startMemDebuggerMutex;

  /*! Start the memory debugger */
  static void MemDebuggerStart(void) {
    Lock<MutexSys> lock(startMemDebuggerMutex);
    if (memDebugger == NULL) {
      atexit(MemDebuggerEnd);
      memDebugger = new MemDebugger;
    }
  }

  void* MemDebuggerInsertAlloc(void *ptr, const char *file, const char *function, int line) {
    if (memDebugger == NULL) MemDebuggerStart();
    return memDebugger->insertAlloc(ptr, file, function, line);
  }
  void MemDebuggerRemoveAlloc(void *ptr) {
    if (memDebugger == NULL) MemDebuggerStart();
    memDebugger->removeAlloc(ptr);
  }
  void MemDebuggerDumpAlloc(void) {
    if (memDebugger == NULL) MemDebuggerStart();
    memDebugger->dumpAlloc();
  }
  void MemDebuggerEnableMemoryInitialization(bool enabled) {
    memoryInitializationEnabled = enabled;
  }
  void MemDebuggerInitializeMem(void *mem, size_t sz) {
    if (memoryInitializationEnabled) std::memset(mem, 0xcd, sz);
  }
} /* namespace gbe */

#endif /* GBE_DEBUG_MEMORY */

namespace gbe
{
#if GBE_DEBUG_MEMORY
  void* memAlloc(size_t size) {
    void *ptr = std::malloc(size + sizeof(size_t));
    *(size_t *) ptr = size;
    MemDebuggerInitializeMem((char*) ptr + sizeof(size_t), size);
    return (char *) ptr + sizeof(size_t);
  }
  void memFree(void *ptr) {
    if (ptr != NULL) {
      char *toFree = (char*) ptr - sizeof(size_t);
      const size_t size = *(size_t *) toFree;
      MemDebuggerInitializeMem(ptr, size);
      std::free(toFree);
    }
  }
#else
  void* memAlloc(size_t size) { return  std::malloc(size); }
  void memFree(void *ptr) { if (ptr != NULL) std::free(ptr); }
#endif /* GBE_DEBUG_MEMORY */

} /* namespace gbe */

#if GBE_DEBUG_MEMORY

namespace gbe
{
  void* alignedMalloc(size_t size, size_t align) {
    void* mem = malloc(size+(align-1)+sizeof(uintptr_t) + sizeof(void*));
    FATAL_IF (!mem && size, "memory allocation failed");
    char* aligned = (char*) mem + sizeof(uintptr_t) + sizeof(void*);
    aligned += align - ((uintptr_t)aligned & (align - 1));
    ((void**)aligned)[-1] = mem;
    ((uintptr_t*)aligned)[-2] = uintptr_t(size);
    MemDebuggerInitializeMem(aligned, size);
    return aligned;
  }

  void alignedFree(void* ptr) {
    if (ptr) {
      const size_t size = ((uintptr_t*)ptr)[-2];
      MemDebuggerInitializeMem(ptr, size);
      free(((void**)ptr)[-2]);
    }
  }
} /* namespace gbe */

#else

////////////////////////////////////////////////////////////////////////////////
/// Windows Platform
////////////////////////////////////////////////////////////////////////////////

#if defined(__WIN32__)

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

namespace gbe
{
  void* alignedMalloc(size_t size, size_t align) {
    void* ptr = _mm_malloc(size,align);
    FATAL_IF (!ptr && size, "memory allocation failed");
    MemDebuggerInitializeMem(ptr, size);
    return ptr;
  }

  void alignedFree(void *ptr) { if (ptr) _mm_free(ptr); }
} /* namespace gbe */

#endif /* __WIN32__ */

////////////////////////////////////////////////////////////////////////////////
/// Linux Platform
////////////////////////////////////////////////////////////////////////////////

#if defined(__LINUX__)

#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <malloc.h>
#include <iostream>

namespace gbe
{
  void* alignedMalloc(size_t size, size_t align) {
    void* ptr = memalign(align,size);
    FATAL_IF (!ptr && size, "memory allocation failed");
    MemDebuggerInitializeMem(ptr, size);
    return ptr;
  }

  void alignedFree(void *ptr) { if (ptr) std::free(ptr); }
} /* namespace gbe */

#endif /* __LINUX__ */

////////////////////////////////////////////////////////////////////////////////
/// MacOS Platform
////////////////////////////////////////////////////////////////////////////////

#if defined(__MACOSX__)

#include <cstdlib>

namespace gbe
{
  void* alignedMalloc(size_t size, size_t align) {
    void* mem = malloc(size+(align-1)+sizeof(void*));
    FATAL_IF (!mem && size, "memory allocation failed");
    char* aligned = ((char*)mem) + sizeof(void*);
    aligned += align - ((uintptr_t)aligned & (align - 1));
    ((void**)aligned)[-1] = mem;
    MemDebuggerInitializeMem(aligned, size);
    return aligned;
  }

  void alignedFree(void* ptr) { if (ptr) free(((void**)ptr)[-1]); }
} /* namespace gbe */

#endif /* __MACOSX__ */

#endif /* GBE_DEBUG_MEMORY */

<commit_msg>Made the memory debugger properly output the unfreed allocations<commit_after>/*
 * Copyright © 2012 Intel Corporation
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Benjamin Segovia <benjamin.segovia@intel.com>
 */

/**
 * \file alloc.cpp
 * \author Benjamin Segovia <benjamin.segovia@intel.com>
 *
 *  Provides facilities to track allocations and pre-initialize memory at
 *  memory allocation and memory free time
 */
#include "sys/alloc.hpp"
#include "sys/atomic.hpp"
#include "sys/mutex.hpp"

#if GBE_DEBUG_MEMORY
#ifdef __MSVC__
#include <unordered_map>
#else
#include <tr1/unordered_map>
#endif /* __MSVC__ */
#include <cstring>
#endif /* GBE_DEBUG_MEMORY */

#if defined(__ICC__)
#include <stdint.h>
#endif /* __ICC__ */
#include <map>
#include <vector>

////////////////////////////////////////////////////////////////////////////////
/// Memory debugger
////////////////////////////////////////////////////////////////////////////////
#if GBE_DEBUG_MEMORY
namespace gbe
{

  /*! Store each allocation data */
  struct AllocData {
    INLINE AllocData(void) {}
    INLINE AllocData(int fileName_, int functionName_, int line_, intptr_t alloc_) :
      fileName(fileName_), functionName(functionName_), line(line_), alloc(alloc_) {}
    int fileName, functionName, line;
    intptr_t alloc;
  };

  /*! Store allocation information */
  struct MemDebugger {
    MemDebugger(void) : unfreedNum(0), allocNum(0) {}
    ~MemDebugger(void) { this->dumpAlloc(); }
    void* insertAlloc(void *ptr, const char *file, const char *function, int line);
    void removeAlloc(void *ptr);
    void dumpAlloc(void);
    void dumpData(const AllocData &data);
    /*! Count the still unfreed allocations */
    volatile intptr_t unfreedNum;
    /*! Total number of allocations done */
    volatile intptr_t allocNum;
    /*! Sorts the file name and function name strings */
    std::tr1::unordered_map<const char*, int> staticStringMap;
    /*! Each element contains the actual string */
    std::vector<const char*> staticStringVector;
    std::map<uintptr_t, AllocData> allocMap;
    /*! Protect the memory debugger accesses */
    MutexSys mutex;
  };

  void* MemDebugger::insertAlloc(void *ptr, const char *file, const char *function, int line)
  {
    if (ptr == NULL) return ptr;
    Lock<MutexSys> lock(mutex);
    const uintptr_t iptr = (uintptr_t) ptr;
    if (UNLIKELY(allocMap.find(iptr) != allocMap.end())) {
      this->dumpData(allocMap.find(iptr)->second);
      FATAL("Pointer already in map");
    }
    const auto fileIt = staticStringMap.find(file);
    const auto functionIt = staticStringMap.find(function);
    int fileName, functionName;
    if (fileIt == staticStringMap.end()) {
      staticStringVector.push_back(file);
      staticStringMap[file] = fileName = int(staticStringVector.size()) - 1;
    } else
      fileName = staticStringMap[file];
    if (functionIt == staticStringMap.end()) {
      staticStringVector.push_back(function);
      staticStringMap[function] = functionName = int(staticStringVector.size()) - 1;
    } else
      functionName = staticStringMap[function];
    allocMap[iptr] = AllocData(fileName, functionName, line, allocNum);
    unfreedNum++;
    allocNum++;
    return ptr;
  }

  void MemDebugger::removeAlloc(void *ptr)
  {
    if (ptr == NULL) return;
    Lock<MutexSys> lock(mutex);
    const uintptr_t iptr = (uintptr_t) ptr;
    FATAL_IF(allocMap.find(iptr) == allocMap.end(), "Pointer not referenced");
    allocMap.erase(iptr);
    unfreedNum--;
  }

  void MemDebugger::dumpData(const AllocData &data) {
    std::cerr << "ALLOC " << data.alloc << ": " <<
                 "file " << staticStringVector[data.fileName] << ", " <<
                 "function " << staticStringVector[data.functionName] << ", " <<
                 "line " << data.line << std::endl;
  }

  void MemDebugger::dumpAlloc(void) {
    std::cerr << "MemDebugger: Unfreed number: " << unfreedNum << std::endl;
    for (auto it = allocMap.begin(); it != allocMap.end(); ++it)
      this->dumpData(it->second);
    std::cerr << "MemDebugger: " << staticStringVector.size()
              << " allocated static strings" << std::endl;
  }

  /*! The user can deactivate the memory initialization */
  static bool memoryInitializationEnabled = true;

  /*! Declare C like interface functions here */
  static MemDebugger *memDebugger = NULL;

  /*! Stop the memory debugger */
  static void MemDebuggerEnd(void) {
    MemDebugger *_debug = memDebugger;
    memDebugger = NULL;
    delete _debug;
  }

  /*! Use this to serialize multiple starts of the debugger */
  static MutexSys startMemDebuggerMutex;

  /*! Start the memory debugger */
  static void MemDebuggerStart(void) {
    Lock<MutexSys> lock(startMemDebuggerMutex);
    if (memDebugger == NULL) {
      atexit(MemDebuggerEnd);
      memDebugger = new MemDebugger;
    }
  }

  void* MemDebuggerInsertAlloc(void *ptr, const char *file, const char *function, int line) {
    if (memDebugger == NULL) MemDebuggerStart();
    return memDebugger->insertAlloc(ptr, file, function, line);
  }
  void MemDebuggerRemoveAlloc(void *ptr) {
    if (memDebugger == NULL) MemDebuggerStart();
    memDebugger->removeAlloc(ptr);
  }
  void MemDebuggerDumpAlloc(void) {
    if (memDebugger == NULL) MemDebuggerStart();
    memDebugger->dumpAlloc();
  }
  void MemDebuggerEnableMemoryInitialization(bool enabled) {
    memoryInitializationEnabled = enabled;
  }
  void MemDebuggerInitializeMem(void *mem, size_t sz) {
    if (memoryInitializationEnabled) std::memset(mem, 0xcd, sz);
  }
} /* namespace gbe */

#endif /* GBE_DEBUG_MEMORY */

namespace gbe
{
#if GBE_DEBUG_MEMORY
  void* memAlloc(size_t size) {
    void *ptr = std::malloc(size + sizeof(size_t));
    *(size_t *) ptr = size;
    MemDebuggerInitializeMem((char*) ptr + sizeof(size_t), size);
    return (char *) ptr + sizeof(size_t);
  }
  void memFree(void *ptr) {
    if (ptr != NULL) {
      char *toFree = (char*) ptr - sizeof(size_t);
      const size_t size = *(size_t *) toFree;
      MemDebuggerInitializeMem(ptr, size);
      std::free(toFree);
    }
  }
#else
  void* memAlloc(size_t size) { return  std::malloc(size); }
  void memFree(void *ptr) { if (ptr != NULL) std::free(ptr); }
#endif /* GBE_DEBUG_MEMORY */

} /* namespace gbe */

#if GBE_DEBUG_MEMORY

namespace gbe
{
  void* alignedMalloc(size_t size, size_t align) {
    void* mem = malloc(size+(align-1)+sizeof(uintptr_t) + sizeof(void*));
    FATAL_IF (!mem && size, "memory allocation failed");
    char* aligned = (char*) mem + sizeof(uintptr_t) + sizeof(void*);
    aligned += align - ((uintptr_t)aligned & (align - 1));
    ((void**)aligned)[-1] = mem;
    ((uintptr_t*)aligned)[-2] = uintptr_t(size);
    MemDebuggerInitializeMem(aligned, size);
    return aligned;
  }

  void alignedFree(void* ptr) {
    if (ptr) {
      const size_t size = ((uintptr_t*)ptr)[-2];
      MemDebuggerInitializeMem(ptr, size);
      free(((void**)ptr)[-2]);
    }
  }
} /* namespace gbe */

#else

////////////////////////////////////////////////////////////////////////////////
/// Windows Platform
////////////////////////////////////////////////////////////////////////////////

#if defined(__WIN32__)

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

namespace gbe
{
  void* alignedMalloc(size_t size, size_t align) {
    void* ptr = _mm_malloc(size,align);
    FATAL_IF (!ptr && size, "memory allocation failed");
    MemDebuggerInitializeMem(ptr, size);
    return ptr;
  }

  void alignedFree(void *ptr) { if (ptr) _mm_free(ptr); }
} /* namespace gbe */

#endif /* __WIN32__ */

////////////////////////////////////////////////////////////////////////////////
/// Linux Platform
////////////////////////////////////////////////////////////////////////////////

#if defined(__LINUX__)

#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <malloc.h>
#include <iostream>

namespace gbe
{
  void* alignedMalloc(size_t size, size_t align) {
    void* ptr = memalign(align,size);
    FATAL_IF (!ptr && size, "memory allocation failed");
    MemDebuggerInitializeMem(ptr, size);
    return ptr;
  }

  void alignedFree(void *ptr) { if (ptr) std::free(ptr); }
} /* namespace gbe */

#endif /* __LINUX__ */

////////////////////////////////////////////////////////////////////////////////
/// MacOS Platform
////////////////////////////////////////////////////////////////////////////////

#if defined(__MACOSX__)

#include <cstdlib>

namespace gbe
{
  void* alignedMalloc(size_t size, size_t align) {
    void* mem = malloc(size+(align-1)+sizeof(void*));
    FATAL_IF (!mem && size, "memory allocation failed");
    char* aligned = ((char*)mem) + sizeof(void*);
    aligned += align - ((uintptr_t)aligned & (align - 1));
    ((void**)aligned)[-1] = mem;
    MemDebuggerInitializeMem(aligned, size);
    return aligned;
  }

  void alignedFree(void* ptr) { if (ptr) free(((void**)ptr)[-1]); }
} /* namespace gbe */

#endif /* __MACOSX__ */

#endif /* GBE_DEBUG_MEMORY */

<|endoftext|>


<commit_before>//
//  AppleIIgs.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 20/10/2020.
//  Copyright 2020 Thomas Harte. All rights reserved.
//

#include "AppleIIgs.hpp"

#include "../../MachineTypes.hpp"
#include "../../../Processors/65816/65816.hpp"

#include "../../../Analyser/Static/AppleIIgs/Target.hpp"
#include "MemoryMap.hpp"

#include "../../../Components/8530/z8530.hpp"
#include "../../../Components/AppleClock/AppleClock.hpp"
#include "../../../Components/DiskII/IWM.hpp"

#include <cassert>
#include <array>

namespace Apple {
namespace IIgs {

class ConcreteMachine:
	public Apple::IIgs::Machine,
	public MachineTypes::TimedMachine,
	public MachineTypes::ScanProducer,
	public CPU::MOS6502Esque::BusHandler<uint32_t> {

	public:
		ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
			m65816_(*this) {

			set_clock_rate(14318180.0);

			using Target = Analyser::Static::AppleIIgs::Target;
			std::vector<ROMMachine::ROM> rom_descriptions;
			const std::string machine_name = "AppleIIgs";
			switch(target.model) {
				case Target::Model::ROM00:
					/* TODO */
				case Target::Model::ROM01:
					rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0);
				break;

				case Target::Model::ROM03:
					rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29);
				break;
			}
			const auto roms = rom_fetcher(rom_descriptions);
			if(!roms[0]) {
				throw ROMMachine::Error::MissingROMs;
			}
			rom_ = *roms[0];

			size_t ram_size = 0;
			switch(target.memory_model) {
				case Target::MemoryModel::TwoHundredAndFiftySixKB:
					ram_size = 256;
				break;

				case Target::MemoryModel::OneMB:
					ram_size = 128 + 1024;
				break;

				case Target::MemoryModel::EightMB:
					ram_size = 128 + 8 * 1024;
				break;
			}
			ram_.resize(ram_size * 1024);

			memory_.set_storage(ram_, rom_);

			// Sync up initial values.
			memory_.set_speed_register(speed_register_);
		}

		void run_for(const Cycles cycles) override {
			m65816_.run_for(cycles);
		}

		void set_scan_target(Outputs::Display::ScanTarget *) override {
		}

		Outputs::Display::ScanStatus get_scaled_scan_status() const override {
			return Outputs::Display::ScanStatus();
		}

		forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) {
			const auto &region = MemoryMapRegion(memory_, address);

			// TODO: potentially push time to clock_.

			if(region.flags & MemoryMap::Region::IsIO) {
				// Ensure classic auxiliary and language card accesses have effect.
				const bool is_read = isReadOperation(operation);
				memory_.access(uint16_t(address), is_read);

				switch(address & 0xffff) {

					// New video register.
					case 0xc029:
						if(is_read) {
							*value = 0;
						} else {
							printf("New video: %02x\n", *value);
							// TODO: this bit should affect memory bank selection, somehow?
							// Cf. Page 90.
						}
					break;

					// Shadow register.
					case 0xc035:
						if(is_read) {
							*value = memory_.get_shadow_register();
						} else {
							memory_.set_shadow_register(*value);
						}
					break;

					// Clock data.
					case 0xc033:
						if(is_read) {
							*value = clock_.get_data();
						} else {
							clock_.set_data(*value);
						}
					break;

					// Clock and border control.
					case 0xc034:
						if(is_read) {
							*value = clock_.get_control();
						} else {
							clock_.set_control(*value);
							// TODO: also set border colour.
						}
					break;

					// Speed register.
					case 0xc036:
						if(is_read) {
							*value = speed_register_;
						} else {
							memory_.set_speed_register(*value);
							speed_register_ = *value;
							printf("[Unimplemented] most of speed register: %02x\n", *value);
						}
					break;

					// [Memory] State register.
					case 0xc068:
						if(is_read) {
							*value = memory_.get_state_register();
						} else {
							memory_.set_state_register(*value);
						}
					break;

					// Various independent memory switch reads [TODO: does the IIe-style keyboard the low seven?].
#define SwitchRead(s) *value = memory_.s ? 0x80 : 0x00
#define LanguageRead(s) SwitchRead(language_card_switches().state().s)
#define AuxiliaryRead(s) SwitchRead(auxiliary_switches().switches().s)
					case 0xc011:	LanguageRead(bank1);						break;
					case 0xc012:	LanguageRead(read);							break;
					case 0xc013:	AuxiliaryRead(read_auxiliary_memory);		break;
					case 0xc014:	AuxiliaryRead(write_auxiliary_memory);		break;
					case 0xc015:	AuxiliaryRead(internal_CX_rom);				break;
					case 0xc016:	AuxiliaryRead(alternative_zero_page);		break;
					case 0xc017:	AuxiliaryRead(slot_C3_rom);					break;
#undef AuxiliaryRead
#undef LanguageRead
#undef SwitchRead

					// The SCC.
					case 0xc038: case 0xc039: case 0xc03a: case 0xc03b:
						if(isReadOperation(operation)) {
							*value = scc_.read(int(address));
						} else {
							scc_.write(int(address), *value);
						}
					break;

					// These were all dealt with by the call to memory_.access.
					// TODO: subject to read data? Does vapour lock apply?
					case 0xc000: case 0xc001: case 0xc002: case 0xc003: case 0xc004: case 0xc005:
					case 0xc006: case 0xc007: case 0xc008: case 0xc009: case 0xc00a: case 0xc00b:
					case 0xc054: case 0xc055: case 0xc056: case 0xc057:
					break;

					default:
						if((address & 0xffff) < 0xc100) {
							// TODO: all other IO accesses.
							printf("Unhandled IO: %04x\n", address & 0xffff);
							assert(false);
						} else {
							// Card IO. Not implemented!
							if(isReadOperation(operation)) {
								*value = 0xff;
							}
						}
				}
			} else {
				// For debugging purposes; if execution heads off into an unmapped page then
				// it's pretty certain that my 65816 still has issues.
				assert(operation != CPU::WDC65816::BusOperation::ReadOpcode || region.read);

				if(isReadOperation(operation)) {
					MemoryMapRead(region, address, value);
				} else {
					MemoryMapWrite(memory_, region, address, value);
				}
			}

			printf("%06x %s %02x", address, isReadOperation(operation) ? "->" : "<-", *value);
			if(operation == CPU::WDC65816::BusOperation::ReadOpcode) {
				printf(" a:%04x x:%04x y:%04x s:%04x e:%d p:%02x db:%02x pb:%02x d:%04x\n",
					m65816_.get_value_of_register(CPU::WDC65816::Register::A),
					m65816_.get_value_of_register(CPU::WDC65816::Register::X),
					m65816_.get_value_of_register(CPU::WDC65816::Register::Y),
					m65816_.get_value_of_register(CPU::WDC65816::Register::StackPointer),
					m65816_.get_value_of_register(CPU::WDC65816::Register::EmulationFlag),
					m65816_.get_value_of_register(CPU::WDC65816::Register::Flags),
					m65816_.get_value_of_register(CPU::WDC65816::Register::DataBank),
					m65816_.get_value_of_register(CPU::WDC65816::Register::ProgramBank),
					m65816_.get_value_of_register(CPU::WDC65816::Register::Direct)
				);
			} else printf("\n");

			Cycles duration = Cycles(5);

			// TODO: determine the cost of this access.
//			if((mapping.flags & BankMapping::Is1Mhz) || ((mapping.flags & BankMapping::IsShadowed) && !isReadOperation(operation))) {
//				// TODO: (i) get into phase; (ii) allow for the 1Mhz bus length being sporadically 16 rather than 14.
//				duration = Cycles(14);
//			} else {
//				// TODO: (i) get into phase; (ii) allow for collisions with the refresh cycle.
//				duration = Cycles(5);
//			}
			fast_access_phase_ = (fast_access_phase_ + duration.as<int>()) % 5;		// TODO: modulo something else, to allow for refresh.
			slow_access_phase_ = (slow_access_phase_ + duration.as<int>()) % 14;	// TODO: modulo something else, to allow for stretched cycles.
			return duration;
		}

	private:
		CPU::WDC65816::Processor<ConcreteMachine, false> m65816_;
		MemoryMap memory_;
		Apple::Clock::ParallelClock clock_;

		int fast_access_phase_ = 0;
		int slow_access_phase_ = 0;

		uint8_t speed_register_ = 0x00;

		// MARK: - Memory storage.

		std::vector<uint8_t> ram_;
		std::vector<uint8_t> rom_;

		// MARK: - Other components.
 		Zilog::SCC::z8530 scc_;
};

}
}

using namespace Apple::IIgs;

Machine *Machine::AppleIIgs(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
	return new ConcreteMachine(*dynamic_cast<const Analyser::Static::AppleIIgs::Target *>(target), rom_fetcher);
}

Machine::~Machine() {}
<commit_msg>Maps in "the interrupt ROM addresses".<commit_after>//
//  AppleIIgs.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 20/10/2020.
//  Copyright 2020 Thomas Harte. All rights reserved.
//

#include "AppleIIgs.hpp"

#include "../../MachineTypes.hpp"
#include "../../../Processors/65816/65816.hpp"

#include "../../../Analyser/Static/AppleIIgs/Target.hpp"
#include "MemoryMap.hpp"

#include "../../../Components/8530/z8530.hpp"
#include "../../../Components/AppleClock/AppleClock.hpp"
#include "../../../Components/DiskII/IWM.hpp"

#include <cassert>
#include <array>

namespace Apple {
namespace IIgs {

class ConcreteMachine:
	public Apple::IIgs::Machine,
	public MachineTypes::TimedMachine,
	public MachineTypes::ScanProducer,
	public CPU::MOS6502Esque::BusHandler<uint32_t> {

	public:
		ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
			m65816_(*this) {

			set_clock_rate(14318180.0);

			using Target = Analyser::Static::AppleIIgs::Target;
			std::vector<ROMMachine::ROM> rom_descriptions;
			const std::string machine_name = "AppleIIgs";
			switch(target.model) {
				case Target::Model::ROM00:
					/* TODO */
				case Target::Model::ROM01:
					rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0);
				break;

				case Target::Model::ROM03:
					rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29);
				break;
			}
			const auto roms = rom_fetcher(rom_descriptions);
			if(!roms[0]) {
				throw ROMMachine::Error::MissingROMs;
			}
			rom_ = *roms[0];

			size_t ram_size = 0;
			switch(target.memory_model) {
				case Target::MemoryModel::TwoHundredAndFiftySixKB:
					ram_size = 256;
				break;

				case Target::MemoryModel::OneMB:
					ram_size = 128 + 1024;
				break;

				case Target::MemoryModel::EightMB:
					ram_size = 128 + 8 * 1024;
				break;
			}
			ram_.resize(ram_size * 1024);

			memory_.set_storage(ram_, rom_);

			// Sync up initial values.
			memory_.set_speed_register(speed_register_);
		}

		void run_for(const Cycles cycles) override {
			m65816_.run_for(cycles);
		}

		void set_scan_target(Outputs::Display::ScanTarget *) override {
		}

		Outputs::Display::ScanStatus get_scaled_scan_status() const override {
			return Outputs::Display::ScanStatus();
		}

		forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) {
			const auto &region = MemoryMapRegion(memory_, address);

			// TODO: potentially push time to clock_.

			if(region.flags & MemoryMap::Region::IsIO) {
				// Ensure classic auxiliary and language card accesses have effect.
				const bool is_read = isReadOperation(operation);
				memory_.access(uint16_t(address), is_read);

				switch(address & 0xffff) {

					// New video register.
					case 0xc029:
						if(is_read) {
							*value = 0;
						} else {
							printf("New video: %02x\n", *value);
							// TODO: this bit should affect memory bank selection, somehow?
							// Cf. Page 90.
						}
					break;

					// Shadow register.
					case 0xc035:
						if(is_read) {
							*value = memory_.get_shadow_register();
						} else {
							memory_.set_shadow_register(*value);
						}
					break;

					// Clock data.
					case 0xc033:
						if(is_read) {
							*value = clock_.get_data();
						} else {
							clock_.set_data(*value);
						}
					break;

					// Clock and border control.
					case 0xc034:
						if(is_read) {
							*value = clock_.get_control();
						} else {
							clock_.set_control(*value);
							// TODO: also set border colour.
						}
					break;

					// Speed register.
					case 0xc036:
						if(is_read) {
							*value = speed_register_;
						} else {
							memory_.set_speed_register(*value);
							speed_register_ = *value;
							printf("[Unimplemented] most of speed register: %02x\n", *value);
						}
					break;

					// [Memory] State register.
					case 0xc068:
						if(is_read) {
							*value = memory_.get_state_register();
						} else {
							memory_.set_state_register(*value);
						}
					break;

					// Various independent memory switch reads [TODO: does the IIe-style keyboard the low seven?].
#define SwitchRead(s) *value = memory_.s ? 0x80 : 0x00
#define LanguageRead(s) SwitchRead(language_card_switches().state().s)
#define AuxiliaryRead(s) SwitchRead(auxiliary_switches().switches().s)
					case 0xc011:	LanguageRead(bank1);						break;
					case 0xc012:	LanguageRead(read);							break;
					case 0xc013:	AuxiliaryRead(read_auxiliary_memory);		break;
					case 0xc014:	AuxiliaryRead(write_auxiliary_memory);		break;
					case 0xc015:	AuxiliaryRead(internal_CX_rom);				break;
					case 0xc016:	AuxiliaryRead(alternative_zero_page);		break;
					case 0xc017:	AuxiliaryRead(slot_C3_rom);					break;
#undef AuxiliaryRead
#undef LanguageRead
#undef SwitchRead

					// The SCC.
					case 0xc038: case 0xc039: case 0xc03a: case 0xc03b:
						if(isReadOperation(operation)) {
							*value = scc_.read(int(address));
						} else {
							scc_.write(int(address), *value);
						}
					break;

					// These were all dealt with by the call to memory_.access.
					// TODO: subject to read data? Does vapour lock apply?
					case 0xc000: case 0xc001: case 0xc002: case 0xc003: case 0xc004: case 0xc005:
					case 0xc006: case 0xc007: case 0xc008: case 0xc009: case 0xc00a: case 0xc00b:
					case 0xc054: case 0xc055: case 0xc056: case 0xc057:
					break;

					// Interrupt ROM addresses; Cf. P25 of the Hardware Reference.
					case 0xc071: case 0xc072: case 0xc073: case 0xc074: case 0xc075: case 0xc076: case 0xc077:
					case 0xc078: case 0xc079: case 0xc07a: case 0xc07b: case 0xc07c: case 0xc07d: case 0xc07e: case 0xc07f:
						if(isReadOperation(operation)) {
							*value = rom_[rom_.size() - 65536 + (address & 0xffff)];
						}
					break;

					default:
						if((address & 0xffff) < 0xc100) {
							// TODO: all other IO accesses.
							printf("Unhandled IO: %04x\n", address & 0xffff);
							assert(false);
						} else {
							// Card IO. Not implemented!
							if(isReadOperation(operation)) {
								*value = 0xff;
							}
						}
				}
			} else {
				// For debugging purposes; if execution heads off into an unmapped page then
				// it's pretty certain that my 65816 still has issues.
				assert(operation != CPU::WDC65816::BusOperation::ReadOpcode || region.read);

				if(isReadOperation(operation)) {
					MemoryMapRead(region, address, value);
				} else {
					MemoryMapWrite(memory_, region, address, value);
				}
			}

			printf("%06x %s %02x", address, isReadOperation(operation) ? "->" : "<-", *value);
			if(operation == CPU::WDC65816::BusOperation::ReadOpcode) {
				printf(" a:%04x x:%04x y:%04x s:%04x e:%d p:%02x db:%02x pb:%02x d:%04x\n",
					m65816_.get_value_of_register(CPU::WDC65816::Register::A),
					m65816_.get_value_of_register(CPU::WDC65816::Register::X),
					m65816_.get_value_of_register(CPU::WDC65816::Register::Y),
					m65816_.get_value_of_register(CPU::WDC65816::Register::StackPointer),
					m65816_.get_value_of_register(CPU::WDC65816::Register::EmulationFlag),
					m65816_.get_value_of_register(CPU::WDC65816::Register::Flags),
					m65816_.get_value_of_register(CPU::WDC65816::Register::DataBank),
					m65816_.get_value_of_register(CPU::WDC65816::Register::ProgramBank),
					m65816_.get_value_of_register(CPU::WDC65816::Register::Direct)
				);
			} else printf("\n");

			Cycles duration = Cycles(5);

			// TODO: determine the cost of this access.
//			if((mapping.flags & BankMapping::Is1Mhz) || ((mapping.flags & BankMapping::IsShadowed) && !isReadOperation(operation))) {
//				// TODO: (i) get into phase; (ii) allow for the 1Mhz bus length being sporadically 16 rather than 14.
//				duration = Cycles(14);
//			} else {
//				// TODO: (i) get into phase; (ii) allow for collisions with the refresh cycle.
//				duration = Cycles(5);
//			}
			fast_access_phase_ = (fast_access_phase_ + duration.as<int>()) % 5;		// TODO: modulo something else, to allow for refresh.
			slow_access_phase_ = (slow_access_phase_ + duration.as<int>()) % 14;	// TODO: modulo something else, to allow for stretched cycles.
			return duration;
		}

	private:
		CPU::WDC65816::Processor<ConcreteMachine, false> m65816_;
		MemoryMap memory_;
		Apple::Clock::ParallelClock clock_;

		int fast_access_phase_ = 0;
		int slow_access_phase_ = 0;

		uint8_t speed_register_ = 0x00;

		// MARK: - Memory storage.

		std::vector<uint8_t> ram_;
		std::vector<uint8_t> rom_;

		// MARK: - Other components.
 		Zilog::SCC::z8530 scc_;
};

}
}

using namespace Apple::IIgs;

Machine *Machine::AppleIIgs(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
	return new ConcreteMachine(*dynamic_cast<const Analyser::Static::AppleIIgs::Target *>(target), rom_fetcher);
}

Machine::~Machine() {}
<|endoftext|>