<commit_before>////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2005 Peter Kmmel
// Copyright (c) 2005 Richard Sposato
// Permission to use, copy, modify, distribute and sell this software for any
//     purpose is hereby granted without fee, provided that the above copyright
//     notice appear in all copies and that both that copyright notice and this
//     permission notice appear in supporting documentation.
// The authors make no representations about the
//     suitability of this software for any purpose. It is provided "as is"
//     without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////

// $Header$

//#define LOKI_CLASS_LEVEL_THREADING
//#define LOKI_OBJECT_LEVEL_THREADING

// Uncomment this to test new [] and delete [].
//#define LOKI_SMALL_OBJECT_USE_NEW_ARRAY

#include "SmallObj.h"
#include "timer.h"

#include <iostream>
#include <string>

#ifdef COMPARE_BOOST_POOL
  #include <boost\pool\object_pool.hpp>
#endif

using namespace std;

class ThisIsASmallObject
{
	int i;
	double d;
	//std::string s;
};

template<class T>
struct Base : public ThisIsASmallObject, public T {};
template<>
struct Base<void> : public ThisIsASmallObject {};

typedef Base<void>
A;
typedef Base< Loki::SmallObject< Loki::SingleThreaded > >
B;
typedef Base< Loki::SmallValueObject< Loki::SingleThreaded > >
C;

#ifdef COMPARE_BOOST_POOL

class BoostPoolNew
{
private:
    static boost::object_pool< BoostPoolNew > BoostPool;

public:
    /// Throwing single-object new throws bad_alloc when allocation fails.
#ifdef _MSC_VER
    /// @note MSVC complains about non-empty exception specification lists.
    static void * operator new ( std::size_t )
#else
    static void * operator new ( std::size_t ) throw ( std::bad_alloc )
#endif
    {
        return BoostPool.malloc();
    }

    /// Non-throwing single-object new returns NULL if allocation fails.
    static void * operator new ( std::size_t, const std::nothrow_t & ) throw ()
    {
        return BoostPool.malloc();
    }

    /// Placement single-object new merely calls global placement new.
    inline static void * operator new ( std::size_t size, void * place )
    {
        return ::operator new( size, place );
    }

    /// Single-object delete.
    static void operator delete ( void * p ) throw ()
    {
        BoostPool.free( reinterpret_cast< BoostPoolNew * >( p ) );
    }

    /** Non-throwing single-object delete is only called when nothrow
        new operator is used, and the constructor throws an exception.
        */
    static void operator delete ( void * p, const std::nothrow_t & ) throw()
    {
        BoostPool.free( reinterpret_cast< BoostPoolNew * >( p ) );
    }

    /// Placement single-object delete merely calls global placement delete.
    inline static void operator delete ( void * p, void * place )
    {
        ::operator delete ( p, place );
    }

};

boost::object_pool< BoostPoolNew > BoostPoolNew::BoostPool;

typedef Base< BoostPoolNew > D;
#endif

/*
class A
{ int i; int* p;};
class B : public Loki::SmallObject<>
{ int i; int* p;};
class C : public Loki::SmallValueObject<>
{ int i; int* p;};
*/

template<class T>
int run_new_delete(int loop, Timer& t, const char* s)
{
	t.start();
	/****************************************************************/
	for (int i=0; i<loop; ++i)
	{
		T* p = new T;
		delete p;
	}
	/****************************************************************/
	t.stop();
	t.print(t.t(),s);
	return t.t();
}

template<class T>
int run_new_delete(T** array, int N, int loop, Timer& t, const char* s)
{
	t.start();
	/****************************************************************/
	for (int i=0; i<loop; ++i)
		for (int n=0; n<N; n++)
		{
			array[n] = new T;
			delete array[n];
		}
	/****************************************************************/
	t.stop();
	t.print(t.t(),s);
	return t.t();
}

template<class T>
int run_new(T** array, int loop, Timer& t, const char* s)
{
	t.start();
	/****************************************************************/
	for (int i=0; i<loop; ++i)
		array[i] = new T;
	/****************************************************************/
	t.stop();
	t.print(t.t(),s);
	return t.t();
}
template<class T>
int run_delete(T** array, int loop, Timer& t, const char* s)
{
	t.start();
	/****************************************************************/
	for (int i=0; i<loop; ++i)
		delete array[i];
	/****************************************************************/
	t.stop();
	t.print(t.t(),s);
	return t.t();
}

template<class T>
int run_new_delete_array(int N, int loop, Timer& t, const char* s)
{
	t.start();
	/****************************************************************/
	for (int i=0; i<loop; ++i)
	{
		T* p = new T[N];
		delete [] p;
	}
	/****************************************************************/
	t.stop();
	t.print(t.t(),s);
	return t.t();
}

template<class T>
int run_new_array( int N, T** array, int loop, Timer& t, const char* s)
{
	t.start();
	/****************************************************************/
	for (int i=0; i<loop; ++i)
		array[i] = new T[N];
	/****************************************************************/
	t.stop();
	t.print(t.t(),s);
	return t.t();
}
template<class T>
int run_delete_array( T** array, int loop, Timer& t, const char* s)
{
	t.start();
	/****************************************************************/
	for (int i=0; i<loop; ++i)
		delete [] array[i];
	/****************************************************************/
	t.stop();
	t.print(t.t(),s);
	return t.t();
}

int main()
{
	int loop = 1000000;

	cout << "Small-Object Benchmark Tests" << endl;
    cout << "A = global new and delete \tsizeof(A) =" << sizeof(A) << endl;
    cout << "B = Loki::SmallObject     \tsizeof(B) =" << sizeof(B) << endl;
    cout << "C = Loki::SmallValueObject\tsizeof(C) =" << sizeof(C) << endl;
#ifdef COMPARE_BOOST_POOL
    cout << "D = boost::object_pool    \tsizeof(D) =" << sizeof(D) << endl;
#endif
	cout << endl << endl;

	Timer t;

	t.t100 = 0;
	t.t100 = run_new_delete<A>(loop,t,"new & delete A : ");
	run_new_delete<B>(loop,t,"new & delete B : ");
	run_new_delete<C>(loop,t,"new & delete C : ");
#ifdef COMPARE_BOOST_POOL
	run_new_delete<D>(loop,t,"new & delete D : ");
#endif

	cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

	int N = 100000;
	int loop2 = loop/N*10;

	A** a = new A*[N];
	B** b = new B*[N];
	C** c = new C*[N];
#ifdef COMPARE_BOOST_POOL
	D** d = new D*[N];
#endif

	for(int i=0; i<N; i++)
	{
		a[i]=0;
		b[i]=0;
		c[i]=0;
#ifdef COMPARE_BOOST_POOL
        d[i]=0;
#endif
	}

	t.t100 = 0;
	t.t100 = run_new_delete(a,N,loop2,t,"new & del. A on array: ");
	run_new_delete(b,N,loop2,t,"new & del. B on array: ");
	run_new_delete(c,N,loop2,t,"new & del. C on array: ");
#ifdef COMPARE_BOOST_POOL
	run_new_delete(d,N,loop2,t,"new & del. D on array: ");
#endif

	cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

	t.t100 = 0;
	t.t100 = run_new(a,N,t,"new A on array : ");
	run_new(b,N,t,"new B on array : ");
	run_new(c,N,t,"new C on array : ");
#ifdef COMPARE_BOOST_POOL
    run_new(d,N,t,"new D on array : ");
#endif

	cout << endl;
////////////////////////////////////////////////////////////////////////////////

	t.t100 = 0;
	t.t100 = run_delete(a,N,t,"delete A on array : ");
	run_delete(b,N,t,"delete B on array : ");
	run_delete(c,N,t,"delete C on array : ");
#ifdef COMPARE_BOOST_POOL
    run_delete(d,N,t,"delete D on array : ");
#endif

	cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

	N = 5;

	t.t100 = 0;
	t.t100 = run_new_delete_array<A>(N,loop,t,"new & delete [] A : ");
	run_new_delete_array<B>(N,loop,t,"new & delete [] B : ");
	run_new_delete_array<C>(N,loop,t,"new & delete [] C : ");
#ifdef COMPARE_BOOST_POOL
    run_new_delete_array<D>(N,loop,t,"new & delete [] D : ");
#endif

	cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

    int count = 1000;
	t.t100 = 0;
	t.t100 = run_new_array(N,a,count,t,"new [] A on array : ");
	run_new_array(N,b,count,t,"new [] B on array : ");
	run_new_array(N,c,count,t,"new [] C on array : ");
#ifdef COMPARE_BOOST_POOL
    run_new_array(N,d,count,t,"new [] D on array : ");
#endif

	cout << endl;
////////////////////////////////////////////////////////////////////////////////

	t.t100 = 0;
	t.t100 = run_delete_array(a,count,t,"delete [] A on array : ");
	run_delete_array(b,count,t,"delete [] B on array : ");
	run_delete_array(c,count,t,"delete [] C on array : ");
#ifdef COMPARE_BOOST_POOL
    run_delete_array(d,count,t,"delete [] D on array : ");
#endif

	delete [] a;
	delete [] b;
	delete [] c;
#ifdef COMPARE_BOOST_POOL
    delete [] d;
#endif

	cout << endl << endl;
    Loki::AllocatorSingleton<>::ClearExtraMemory();
////////////////////////////////////////////////////////////////////////////////

	cout << endl;
	system("PAUSE");
	return 0;
}

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

// $Log$
// Revision 1.8  2005/10/26 00:41:00  rich_sposato
// Added comparison to boost::pool memory allocator.
//
// Revision 1.7  2005/10/14 18:35:06  rich_sposato
// Added cvs keywords.
//
<commit_msg>make object size more flexible<commit_after>////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2005 Peter Kmmel
// Copyright (c) 2005 Richard Sposato
// Permission to use, copy, modify, distribute and sell this software for any
//     purpose is hereby granted without fee, provided that the above copyright
//     notice appear in all copies and that both that copyright notice and this
//     permission notice appear in supporting documentation.
// The authors make no representations about the
//     suitability of this software for any purpose. It is provided "as is"
//     without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////

// $Header$

//#define LOKI_CLASS_LEVEL_THREADING
#define LOKI_OBJECT_LEVEL_THREADING

// Uncomment this to test new [] and delete [].
#define LOKI_SMALL_OBJECT_USE_NEW_ARRAY

#include "SmallObj.h"
#include "timer.h"

#include <iostream>
#include <string>

#define COMPARE_BOOST_POOL
#ifdef COMPARE_BOOST_POOL
  #include <boost\pool\object_pool.hpp>
#endif

using namespace std;

template<unsigned int N>
class ThisIsASmallObject
{
  char data[N];
};

template<unsigned int N, class T>
struct Base : public ThisIsASmallObject<N>, public T {};

template<unsigned int N>
struct Base<N, void> : public ThisIsASmallObject<N> {};

#ifdef COMPARE_BOOST_POOL

template<unsigned int N>
class BoostPoolNew : public Base<N,void>
{
private:
    static boost::object_pool< BoostPoolNew<N> > BoostPool;

public:
    /// Throwing single-object new throws bad_alloc when allocation fails.
#ifdef _MSC_VER
    /// @note MSVC complains about non-empty exception specification lists.
    static void * operator new ( std::size_t )
#else
    static void * operator new ( std::size_t ) throw ( std::bad_alloc )
#endif
    {
        return BoostPool.malloc();
    }

    /// Non-throwing single-object new returns NULL if allocation fails.
    static void * operator new ( std::size_t, const std::nothrow_t & ) throw ()
    {
        return BoostPool.malloc();
    }

    /// Placement single-object new merely calls global placement new.
    inline static void * operator new ( std::size_t size, void * place )
    {
        return ::operator new( size, place );
    }

    /// Single-object delete.
    static void operator delete ( void * p ) throw ()
    {
        BoostPool.free( reinterpret_cast< BoostPoolNew * >( p ) );
    }

    /** Non-throwing single-object delete is only called when nothrow
        new operator is used, and the constructor throws an exception.
        */
    static void operator delete ( void * p, const std::nothrow_t & ) throw()
    {
        BoostPool.free( reinterpret_cast< BoostPoolNew * >( p ) );
    }

    /// Placement single-object delete merely calls global placement delete.
    inline static void operator delete ( void * p, void * place )
    {
        ::operator delete ( p, place );
    }

};

template<unsigned int N>
boost::object_pool< BoostPoolNew<N> > BoostPoolNew<N>::BoostPool;

#endif

template<class T>
int run_oneline_new_delete(int loop, Timer& t, const char* s)
{
    t.start();
    /****************************************************************/
    for (int i=0; i<loop; ++i)
    {
        delete new T;
    }
    /****************************************************************/
    t.stop();
    t.print(t.t(),s);
    return t.t();
}

template<class T>
int run_new_delete(int loop, Timer& t, const char* s)
{
    t.start();
    /****************************************************************/
    for (int i=0; i<loop; ++i)
    {
        T* p = new T;
        delete p;
    }
    /****************************************************************/
    t.stop();
    t.print(t.t(),s);
    return t.t();
}

template<class T>
int run_new_delete(T** array, int N, int loop, Timer& t, const char* s)
{
    t.start();
    /****************************************************************/
    for (int i=0; i<loop; ++i)
        for (int n=0; n<N; n++)
        {
            array[n] = new T;
            delete array[n];
        }
    /****************************************************************/
    t.stop();
    t.print(t.t(),s);
    return t.t();
}

template<class T>
int run_new(T** array, int loop, Timer& t, const char* s)
{
    t.start();
    /****************************************************************/
    for (int i=0; i<loop; ++i)
        array[i] = new T;
    /****************************************************************/
    t.stop();
    t.print(t.t(),s);
    return t.t();
}
template<class T>
int run_delete(T** array, int loop, Timer& t, const char* s)
{
    t.start();
    /****************************************************************/
    for (int i=0; i<loop; ++i)
        delete array[i];
    /****************************************************************/
    t.stop();
    t.print(t.t(),s);
    return t.t();
}

template<class T>
int run_new_delete_array(int N, int loop, Timer& t, const char* s)
{
    t.start();
    /****************************************************************/
    for (int i=0; i<loop; ++i)
    {
        T* p = new T[N];
        delete [] p;
    }
    /****************************************************************/
    t.stop();
    t.print(t.t(),s);
    return t.t();
}

template<class T>
int run_new_array( int N, T** array, int loop, Timer& t, const char* s)
{
    t.start();
    /****************************************************************/
    for (int i=0; i<loop; ++i)
        array[i] = new T[N];
    /****************************************************************/
    t.stop();
    t.print(t.t(),s);
    return t.t();
}
template<class T>
int run_delete_array( T** array, int loop, Timer& t, const char* s)
{
    t.start();
    /****************************************************************/
    for (int i=0; i<loop; ++i)
        delete [] array[i];
    /****************************************************************/
    t.stop();
    t.print(t.t(),s);
    return t.t();
}

template<unsigned int N, int loop>
void testSize()
{
    typedef Base<N, void>
    A;
    typedef Base<N, Loki::SmallObject< Loki::SingleThreaded > >
    B;
    typedef Base<N, Loki::SmallValueObject< Loki::SingleThreaded > >
    C;
#ifdef COMPARE_BOOST_POOL
    typedef BoostPoolNew<N>
    D;
#endif

    cout << "Small-Object Benchmark Tests \n" << endl;
    cout << "A = global new and delete \tsizeof(A) =" << sizeof(A) << endl;
    cout << "B = Loki::SmallObject     \tsizeof(B) =" << sizeof(B) << endl;
    cout << "C = Loki::SmallValueObject\tsizeof(C) =" << sizeof(C) << endl;
#ifdef COMPARE_BOOST_POOL
    cout << "D = boost::object_pool    \tsizeof(D) =" << sizeof(D) << endl;
#endif
    cout << endl << endl;

    Timer t;

    t.t100 = 0;
    t.t100 = run_oneline_new_delete<A>(loop,t,"'delete new A' : ");
    run_new_delete<B>(loop,t,"'delete new B' : ");
    run_new_delete<C>(loop,t,"'delete new C' : ");
#ifdef COMPARE_BOOST_POOL
    run_new_delete<D>(loop,t,"'delete new D' : ");
#endif

    cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

    t.t100 = 0;
    t.t100 = run_new_delete<A>(loop,t,"new & delete A : ");
    run_new_delete<B>(loop,t,"new & delete B : ");
    run_new_delete<C>(loop,t,"new & delete C : ");
#ifdef COMPARE_BOOST_POOL
    run_new_delete<D>(loop,t,"new & delete D : ");
#endif

    cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

    int N = 10000;
    int loop2 = loop/N*10;

    A** a = new A*[N];
    B** b = new B*[N];
    C** c = new C*[N];
#ifdef COMPARE_BOOST_POOL
    D** d = new D*[N];
#endif

    for(int i=0; i<N; i++)
    {
        a[i]=0;
        b[i]=0;
        c[i]=0;
#ifdef COMPARE_BOOST_POOL
        d[i]=0;
#endif
    }

    t.t100 = 0;
    t.t100 = run_new_delete(a,N,loop2,t,"new & del. A on array: ");
    run_new_delete(b,N,loop2,t,"new & del. B on array: ");
    run_new_delete(c,N,loop2,t,"new & del. C on array: ");
#ifdef COMPARE_BOOST_POOL
    run_new_delete(d,N,loop2,t,"new & del. D on array: ");
#endif

    cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

    t.t100 = 0;
    t.t100 = run_new(a,N,t,"new A on array : ");
    run_new(b,N,t,"new B on array : ");
    run_new(c,N,t,"new C on array : ");
#ifdef COMPARE_BOOST_POOL
    run_new(d,N,t,"new D on array : ");
#endif

    cout << endl;
////////////////////////////////////////////////////////////////////////////////

    t.t100 = 0;
    t.t100 = run_delete(a,N,t,"delete A on array : ");
    run_delete(b,N,t,"delete B on array : ");
    run_delete(c,N,t,"delete C on array : ");
#ifdef COMPARE_BOOST_POOL
    run_delete(d,N,t,"delete D on array : ");
#endif

    cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

    N = 5;

    t.t100 = 0;
    t.t100 = run_new_delete_array<A>(N,loop,t,"new & delete [] A : ");
    run_new_delete_array<B>(N,loop,t,"new & delete [] B : ");
    run_new_delete_array<C>(N,loop,t,"new & delete [] C : ");
#ifdef COMPARE_BOOST_POOL
    run_new_delete_array<D>(N,loop,t,"new & delete [] D : ");
#endif

    cout << endl << endl;
////////////////////////////////////////////////////////////////////////////////

    int count = 100;
    t.t100 = 0;
    t.t100 = run_new_array(N,a,count,t,"new [] A on array : ");
    run_new_array(N,b,count,t,"new [] B on array : ");
    run_new_array(N,c,count,t,"new [] C on array : ");
#ifdef COMPARE_BOOST_POOL
    run_new_array(N,d,count,t,"new [] D on array : ");
#endif

    cout << endl;
////////////////////////////////////////////////////////////////////////////////

    t.t100 = 0;
    t.t100 = run_delete_array(a,count,t,"delete [] A on array : ");
    run_delete_array(b,count,t,"delete [] B on array : ");
    run_delete_array(c,count,t,"delete [] C on array : ");
#ifdef COMPARE_BOOST_POOL
    run_delete_array(d,count,t,"delete [] D on array : ");
#endif

    delete [] a;
    delete [] b;
    delete [] c;
#ifdef COMPARE_BOOST_POOL
    delete [] d;
#endif

    cout << endl << endl;
    Loki::AllocatorSingleton<>::ClearExtraMemory();
////////////////////////////////////////////////////////////////////////////////
}

int main()
{
    cout << endl;

    const int loop = 700000;

    testSize<8,loop>();
    testSize<64,loop>();
    //testSize<256,loop>();
    //testSize<1024,loop>();

    system("PAUSE");

    return 0;
}

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

// $Log$
// Revision 1.9  2005/10/26 23:30:06  syntheticpp
// make object size more flexible
//
// Revision 1.8  2005/10/26 00:41:00  rich_sposato
// Added comparison to boost::pool memory allocator.
//
// Revision 1.7  2005/10/14 18:35:06  rich_sposato
// Added cvs keywords.
//

<|endoftext|>


<commit_before>/*
    Title:      Object size

    Copyright (c) 2000
        Cambridge University Technical Services Limited

    Further development David C.J. Matthews 2016

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.

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

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_WIN32)
#include "winconfig.h"
#else
#error "No configuration file"
#endif

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#ifdef HAVE_ASSERT_H
#include <assert.h>
#define ASSERT(x) assert(x)
#else
#define ASSERT(x)
#endif

#include "globals.h"
#include "arb.h"
#include "run_time.h"
#include "machine_dep.h"
#include "objsize.h"
#include "scanaddrs.h"
#include "polystring.h"
#include "save_vec.h"
#include "bitmap.h"
#include "memmgr.h"
#include "mpoly.h"
#include "processes.h"

extern "C" {
    POLYEXTERNALSYMBOL POLYUNSIGNED PolyObjSize(PolyObject *threadId, PolyWord obj);
    POLYEXTERNALSYMBOL POLYUNSIGNED PolyShowSize(PolyObject *threadId, PolyWord obj);
    POLYEXTERNALSYMBOL POLYUNSIGNED PolyObjProfile(PolyObject *threadId, PolyWord obj);
}

extern FILE *polyStdout;

#define MAX_PROF_LEN 100 // Profile lengths between 1 and this

class ProcessVisitAddresses: public ScanAddress
{
public:
    virtual POLYUNSIGNED ScanAddressAt(PolyWord *pt) { return ShowWord(*pt); }
    virtual PolyObject *ScanObjectAddress(PolyObject *base);

    POLYUNSIGNED ShowWord(PolyWord w);
    ProcessVisitAddresses(bool show);
    ~ProcessVisitAddresses();

    VisitBitmap *FindBitmap(PolyWord p);
    void ShowBytes(PolyObject *start);
    void ShowCode(PolyObject *start);
    void ShowWords(PolyObject *start);

    POLYUNSIGNED total_length;
    bool     show_size;
    void     *io_bottom;
    void     *io_top;
    VisitBitmap  **bitmaps;
    unsigned   nBitmaps;
    // Counts of objects of each size for mutable and immutable data.
    unsigned   iprofile[MAX_PROF_LEN+1];
    unsigned   mprofile[MAX_PROF_LEN+1];
};

ProcessVisitAddresses::ProcessVisitAddresses(bool show)
{
    // Need to get the allocation lock here.  Another thread
    // could allocate new local areas resulting in gMem.nlSpaces
    // and gMem.lSpaces changing under our feet.
    PLocker lock(&gMem.allocLock);

    MemSpace *ioSpace = gMem.IoSpace();
    io_bottom    = ioSpace->bottom;
    io_top       = ioSpace->top;
    total_length = 0;
    show_size    = show;

    // Create a bitmap for each of the areas apart from the IO area
    nBitmaps = gMem.nlSpaces+gMem.npSpaces; //
    bitmaps = new VisitBitmap*[nBitmaps];
    unsigned bm = 0;
    unsigned j;
    for (j = 0; j < gMem.npSpaces; j++)
    {
        MemSpace *space = gMem.pSpaces[j];
        // Permanent areas are filled with objects from the bottom.
        bitmaps[bm++] = new VisitBitmap(space->bottom, space->top);
    }
    for (j = 0; j < gMem.nlSpaces; j++)
    {
        LocalMemSpace *space = gMem.lSpaces[j];
        bitmaps[bm++] = new VisitBitmap(space->bottom, space->top);
    }
    ASSERT(bm == nBitmaps);

    // Clear the profile counts.
    for (unsigned i = 0; i < MAX_PROF_LEN+1; i++)
    {
        iprofile[i] = mprofile[i] = 0;
    }
}

ProcessVisitAddresses::~ProcessVisitAddresses()
{
    if (bitmaps)
    {
        for (unsigned i = 0; i < nBitmaps; i++)
            delete(bitmaps[i]);
        delete[](bitmaps);
    }
}

// Return the bitmap corresponding to the address or NULL if it isn't there.
VisitBitmap *ProcessVisitAddresses::FindBitmap(PolyWord p)
{
    for (unsigned i = 0; i < nBitmaps; i++)
    {
        VisitBitmap *bm = bitmaps[i];
        if (bm->InRange(p.AsStackAddr())) return bm;
    }
    return 0;
}

void ProcessVisitAddresses::ShowBytes(PolyObject *start)
{
    POLYUNSIGNED bytes = start->Length() * sizeof(PolyWord);
    char *array = (char *) start;

    putc('\n', polyStdout);

    if (start->IsMutable()) fprintf(polyStdout, "MUTABLE ");

    fprintf(polyStdout, "BYTES:%p:%" POLYUFMT "\n", array, bytes);

    POLYUNSIGNED i, n;
    for (i = 0, n = 0; n < bytes; n++)
    {
        fprintf(polyStdout, "%02x ",array[n] & 0xff);
        i++;
        if (i == 16)
        {
            putc('\n', polyStdout);
            i = 0;
        }
    }

    if (i != 0) putc('\n', polyStdout);
}

#define MAXNAME 500

void ProcessVisitAddresses::ShowCode(PolyObject *start)
{
    POLYUNSIGNED length = start->Length();

    putc('\n', polyStdout);
    if (start->IsMutable()) fprintf(polyStdout, "MUTABLE ");

    char buffer[MAXNAME+1];
    PolyWord *consts = start->ConstPtrForCode();
    PolyWord string = consts[0];

    if (string == TAGGED(0))
        strcpy(buffer, "<not-named>");
    else
        (void) Poly_string_to_C(string, buffer, sizeof(buffer));

    fprintf(polyStdout, "CODE:%p:%" POLYUFMT " %s\n", start, length, buffer);

    POLYUNSIGNED i, n;
    for (i = 0, n = 0; n < length; n++)
    {
        if (i != 0) putc('\t', polyStdout);

        fprintf(polyStdout, "%8p ", start->Get(n).AsObjPtr());
        i++;
        if (i == 4)
        {
            putc('\n', polyStdout);
            i = 0;
        }
    }

    if (i != 0) putc('\n', polyStdout);
}

void ProcessVisitAddresses::ShowWords(PolyObject *start)
{
    POLYUNSIGNED length = start->Length();

    putc('\n', polyStdout);
    if (start->IsMutable()) fprintf(polyStdout, "MUTABLE ");

    fprintf(polyStdout, "WORDS:%p:%" POLYUFMT "\n", start, length);

    POLYUNSIGNED i, n;
    for (i = 0, n = 0; n < length; n++)
    {
        if (i != 0)
            putc('\t', polyStdout);

        fprintf(polyStdout, "%8p ", start->Get(n).AsObjPtr());
        i++;
        if (i == 4)
        {
            putc('\n', polyStdout);
            i = 0;
        }
    }

    if (i != 0)
        putc('\n', polyStdout);
}

// This is called initially to print the top-level object.
// Since we don't process stacks it probably doesn't get called elsewhere.
PolyObject *ProcessVisitAddresses::ScanObjectAddress(PolyObject *base)
{
    POLYUNSIGNED lengthWord = ShowWord(base);
    if (lengthWord)
        ScanAddressesInObject(base, lengthWord);
    return base;
}

// Handle the normal case.  Print the object at this word and
// return true is it must be handled recursively.
POLYUNSIGNED ProcessVisitAddresses::ShowWord(PolyWord w)
{

    if (IS_INT(w))
        return 0; /* not a pointer */

    if (w.AsAddress() >= io_bottom && w.AsAddress() < io_top)
        return 0; /* IO segment */

    if (w == PolyWord::FromUnsigned(0))
        return 0;

    VisitBitmap *bm    = FindBitmap(w);

    if (bm == 0)
    {
        fprintf(polyStdout, "Bad address " ZERO_X "%p found\n", w.AsObjPtr());
        return 0;
    }

    PolyObject *p;

    if (OBJ_IS_CODEPTR(w))
        p = ObjCodePtrToPtr(w.AsCodePtr()); /* find beginning of the code object */
    else p = w.AsObjPtr();

    /* Have we already visited this object? */
    if (bm->AlreadyVisited(p))
        return 0;

    bm->SetVisited(p);

    POLYUNSIGNED L = p->LengthWord();
    POLYUNSIGNED obj_length = OBJ_OBJECT_LENGTH(L);

    // Increment the appropriate size profile count.
    if (p->IsMutable())
    {
        if (obj_length > MAX_PROF_LEN)
            mprofile[MAX_PROF_LEN]++;
        else
            mprofile[obj_length]++;
    }
    else
    {
        if (obj_length > MAX_PROF_LEN)
            iprofile[MAX_PROF_LEN]++;
        else
            iprofile[obj_length]++;
    }

    total_length += obj_length + 1; /* total space needed for object */

    if (OBJ_IS_BYTE_OBJECT(L))
    {
        if (show_size)
            ShowBytes(p);
        return 0;
    }
    else if (OBJ_IS_CODE_OBJECT(L))
    {
        PolyWord *cp;
        POLYUNSIGNED const_count;
        p->GetConstSegmentForCode(cp, const_count);

        if (show_size)
            ShowCode(p);

        return L; // Process addresses in it.
     }
    else /* Word object */
    {
        if (show_size)
            ShowWords(p);
        return L; // Process addresses in it.
    }
}

Handle ObjSize(TaskData *taskData, Handle obj)
{
    ProcessVisitAddresses process(false);
    process.ScanObjectAddress(obj->WordP());
    return Make_arbitrary_precision(taskData, process.total_length);
}

Handle ShowSize(TaskData *taskData, Handle obj)
{
    ProcessVisitAddresses process(true);
    process.ScanObjectAddress(obj->WordP());
    fflush(polyStdout); /* We need this for Windows at least. */
    return Make_arbitrary_precision(taskData, process.total_length);
}

static void printfprof(unsigned *counts)
{
    for(unsigned i = 0; i < MAX_PROF_LEN+1; i++)
    {
        if (counts[i] != 0)
        {
            if (i == MAX_PROF_LEN)
                fprintf(polyStdout, ">%d\t%u\n", MAX_PROF_LEN, counts[i]);
            else
                fprintf(polyStdout, "%d\t%u\n", i, counts[i]);
        }
    }
}

Handle ObjProfile(TaskData *taskData, Handle obj)
{
    ProcessVisitAddresses process(false);
    process.ScanObjectAddress(obj->WordP());
    fprintf(polyStdout, "\nImmutable object sizes and counts\n");
    printfprof(process.iprofile);
    fprintf(polyStdout, "\nMutable object sizes and counts\n");
    printfprof(process.mprofile);
    fflush(polyStdout); /* We need this for Windows at least. */
    return Make_arbitrary_precision(taskData, process.total_length);
}

POLYUNSIGNED PolyObjSize(PolyObject *threadId, PolyWord obj)
{
    TaskData *taskData = TaskData::FindTaskForId(threadId);
    ASSERT(taskData != 0);
    taskData->PreRTSCall();

    ProcessVisitAddresses process(false);
    process.ScanObjectAddress(obj.AsObjPtr());
    Handle result = Make_arbitrary_precision(taskData, process.total_length);

    taskData->PostRTSCall();
    return result->Word().AsUnsigned();
}

POLYUNSIGNED PolyShowSize(PolyObject *threadId, PolyWord obj)
{
    TaskData *taskData = TaskData::FindTaskForId(threadId);
    ASSERT(taskData != 0);
    taskData->PreRTSCall();

    ProcessVisitAddresses process(true);
    process.ScanObjectAddress(obj.AsObjPtr());
    fflush(polyStdout); /* We need this for Windows at least. */
    Handle result = Make_arbitrary_precision(taskData, process.total_length);

    taskData->PostRTSCall();
    return result->Word().AsUnsigned();
}

POLYUNSIGNED PolyObjProfile(PolyObject *threadId, PolyWord obj)
{
    TaskData *taskData = TaskData::FindTaskForId(threadId);
    ASSERT(taskData != 0);
    taskData->PreRTSCall();

    ProcessVisitAddresses process(false);
    process.ScanObjectAddress(obj.AsObjPtr());
    fprintf(polyStdout, "\nImmutable object sizes and counts\n");
    printfprof(process.iprofile);
    fprintf(polyStdout, "\nMutable object sizes and counts\n");
    printfprof(process.mprofile);
    fflush(polyStdout); /* We need this for Windows at least. */
    Handle result = Make_arbitrary_precision(taskData, process.total_length);

    taskData->PostRTSCall();
    return result->Word().AsUnsigned();
}

static struct _entrypts entryPtTable[] =
{
    { "PolyObjSize",                    (polyRTSFunction)&PolyObjSize},
    { "PolyShowSize",                   (polyRTSFunction)&PolyShowSize},
    { "PolyObjProfile",                 (polyRTSFunction)&PolyObjProfile},

    { NULL, NULL} // End of list.
};

class ObjSizeModule: public RtsModule
{
public:
    virtual entrypts GetRTSCalls(void) { return entryPtTable; }
};

// Declare this.  It will be automatically added to the table.
static ObjSizeModule objSizeModule;
<commit_msg>Fix objSize for code segments.<commit_after>/*
    Title:      Object size

    Copyright (c) 2000
        Cambridge University Technical Services Limited

    Further development David C.J. Matthews 2016

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.

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

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_WIN32)
#include "winconfig.h"
#else
#error "No configuration file"
#endif

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#ifdef HAVE_ASSERT_H
#include <assert.h>
#define ASSERT(x) assert(x)
#else
#define ASSERT(x)
#endif

#include "globals.h"
#include "arb.h"
#include "run_time.h"
#include "machine_dep.h"
#include "objsize.h"
#include "scanaddrs.h"
#include "polystring.h"
#include "save_vec.h"
#include "bitmap.h"
#include "memmgr.h"
#include "mpoly.h"
#include "processes.h"

extern "C" {
    POLYEXTERNALSYMBOL POLYUNSIGNED PolyObjSize(PolyObject *threadId, PolyWord obj);
    POLYEXTERNALSYMBOL POLYUNSIGNED PolyShowSize(PolyObject *threadId, PolyWord obj);
    POLYEXTERNALSYMBOL POLYUNSIGNED PolyObjProfile(PolyObject *threadId, PolyWord obj);
}

extern FILE *polyStdout;

#define MAX_PROF_LEN 100 // Profile lengths between 1 and this

class ProcessVisitAddresses: public ScanAddress
{
public:
    virtual POLYUNSIGNED ScanAddressAt(PolyWord *pt) { return ShowWord(*pt); }
    virtual PolyObject *ScanObjectAddress(PolyObject *base);

    POLYUNSIGNED ShowWord(PolyWord w);
    ProcessVisitAddresses(bool show);
    ~ProcessVisitAddresses();

    VisitBitmap *FindBitmap(PolyWord p);
    void ShowBytes(PolyObject *start);
    void ShowCode(PolyObject *start);
    void ShowWords(PolyObject *start);

    POLYUNSIGNED total_length;
    bool     show_size;
    void     *io_bottom;
    void     *io_top;
    VisitBitmap  **bitmaps;
    unsigned   nBitmaps;
    // Counts of objects of each size for mutable and immutable data.
    unsigned   iprofile[MAX_PROF_LEN+1];
    unsigned   mprofile[MAX_PROF_LEN+1];
};

ProcessVisitAddresses::ProcessVisitAddresses(bool show)
{
    // Need to get the allocation lock here.  Another thread
    // could allocate new local areas resulting in gMem.nlSpaces
    // and gMem.lSpaces changing under our feet.
    PLocker lock(&gMem.allocLock);

    MemSpace *ioSpace = gMem.IoSpace();
    io_bottom    = ioSpace->bottom;
    io_top       = ioSpace->top;
    total_length = 0;
    show_size    = show;

    // Create a bitmap for each of the areas apart from the IO area
    nBitmaps = gMem.nlSpaces+gMem.npSpaces+gMem.ncSpaces; //
    bitmaps = new VisitBitmap*[nBitmaps];
    unsigned bm = 0;
    unsigned j;
    for (j = 0; j < gMem.npSpaces; j++)
    {
        MemSpace *space = gMem.pSpaces[j];
        // Permanent areas are filled with objects from the bottom.
        bitmaps[bm++] = new VisitBitmap(space->bottom, space->top);
    }
    for (j = 0; j < gMem.nlSpaces; j++)
    {
        LocalMemSpace *space = gMem.lSpaces[j];
        bitmaps[bm++] = new VisitBitmap(space->bottom, space->top);
    }
    for (j = 0; j < gMem.ncSpaces; j++)
    {
        CodeSpace *space = gMem.cSpaces[j];
        bitmaps[bm++] = new VisitBitmap(space->bottom, space->top);
    }
    ASSERT(bm == nBitmaps);

    // Clear the profile counts.
    for (unsigned i = 0; i < MAX_PROF_LEN+1; i++)
    {
        iprofile[i] = mprofile[i] = 0;
    }
}

ProcessVisitAddresses::~ProcessVisitAddresses()
{
    if (bitmaps)
    {
        for (unsigned i = 0; i < nBitmaps; i++)
            delete(bitmaps[i]);
        delete[](bitmaps);
    }
}

// Return the bitmap corresponding to the address or NULL if it isn't there.
VisitBitmap *ProcessVisitAddresses::FindBitmap(PolyWord p)
{
    for (unsigned i = 0; i < nBitmaps; i++)
    {
        VisitBitmap *bm = bitmaps[i];
        if (bm->InRange(p.AsStackAddr())) return bm;
    }
    return 0;
}

void ProcessVisitAddresses::ShowBytes(PolyObject *start)
{
    POLYUNSIGNED bytes = start->Length() * sizeof(PolyWord);
    char *array = (char *) start;

    putc('\n', polyStdout);

    if (start->IsMutable()) fprintf(polyStdout, "MUTABLE ");

    fprintf(polyStdout, "BYTES:%p:%" POLYUFMT "\n", array, bytes);

    POLYUNSIGNED i, n;
    for (i = 0, n = 0; n < bytes; n++)
    {
        fprintf(polyStdout, "%02x ",array[n] & 0xff);
        i++;
        if (i == 16)
        {
            putc('\n', polyStdout);
            i = 0;
        }
    }

    if (i != 0) putc('\n', polyStdout);
}

#define MAXNAME 500

void ProcessVisitAddresses::ShowCode(PolyObject *start)
{
    POLYUNSIGNED length = start->Length();

    putc('\n', polyStdout);
    if (start->IsMutable()) fprintf(polyStdout, "MUTABLE ");

    char buffer[MAXNAME+1];
    PolyWord *consts = start->ConstPtrForCode();
    PolyWord string = consts[0];

    if (string == TAGGED(0))
        strcpy(buffer, "<not-named>");
    else
        (void) Poly_string_to_C(string, buffer, sizeof(buffer));

    fprintf(polyStdout, "CODE:%p:%" POLYUFMT " %s\n", start, length, buffer);

    POLYUNSIGNED i, n;
    for (i = 0, n = 0; n < length; n++)
    {
        if (i != 0) putc('\t', polyStdout);

        fprintf(polyStdout, "%8p ", start->Get(n).AsObjPtr());
        i++;
        if (i == 4)
        {
            putc('\n', polyStdout);
            i = 0;
        }
    }

    if (i != 0) putc('\n', polyStdout);
}

void ProcessVisitAddresses::ShowWords(PolyObject *start)
{
    POLYUNSIGNED length = start->Length();

    putc('\n', polyStdout);
    if (start->IsMutable()) fprintf(polyStdout, "MUTABLE ");

    fprintf(polyStdout, "WORDS:%p:%" POLYUFMT "\n", start, length);

    POLYUNSIGNED i, n;
    for (i = 0, n = 0; n < length; n++)
    {
        if (i != 0)
            putc('\t', polyStdout);

        fprintf(polyStdout, "%8p ", start->Get(n).AsObjPtr());
        i++;
        if (i == 4)
        {
            putc('\n', polyStdout);
            i = 0;
        }
    }

    if (i != 0)
        putc('\n', polyStdout);
}

// This is called initially to print the top-level object.
// Since we don't process stacks it probably doesn't get called elsewhere.
PolyObject *ProcessVisitAddresses::ScanObjectAddress(PolyObject *base)
{
    POLYUNSIGNED lengthWord = ShowWord(base);
    if (lengthWord)
        ScanAddressesInObject(base, lengthWord);
    return base;
}

// Handle the normal case.  Print the object at this word and
// return true is it must be handled recursively.
POLYUNSIGNED ProcessVisitAddresses::ShowWord(PolyWord w)
{

    if (IS_INT(w))
        return 0; /* not a pointer */

    if (w.AsAddress() >= io_bottom && w.AsAddress() < io_top)
        return 0; /* IO segment */

    if (w == PolyWord::FromUnsigned(0))
        return 0;

    VisitBitmap *bm    = FindBitmap(w);

    if (bm == 0)
    {
        fprintf(polyStdout, "Bad address " ZERO_X "%p found\n", w.AsObjPtr());
        return 0;
    }

    PolyObject *p;

    if (OBJ_IS_CODEPTR(w))
        p = ObjCodePtrToPtr(w.AsCodePtr()); /* find beginning of the code object */
    else p = w.AsObjPtr();

    /* Have we already visited this object? */
    if (bm->AlreadyVisited(p))
        return 0;

    bm->SetVisited(p);

    POLYUNSIGNED L = p->LengthWord();
    POLYUNSIGNED obj_length = OBJ_OBJECT_LENGTH(L);

    // Increment the appropriate size profile count.
    if (p->IsMutable())
    {
        if (obj_length > MAX_PROF_LEN)
            mprofile[MAX_PROF_LEN]++;
        else
            mprofile[obj_length]++;
    }
    else
    {
        if (obj_length > MAX_PROF_LEN)
            iprofile[MAX_PROF_LEN]++;
        else
            iprofile[obj_length]++;
    }

    total_length += obj_length + 1; /* total space needed for object */

    if (OBJ_IS_BYTE_OBJECT(L))
    {
        if (show_size)
            ShowBytes(p);
        return 0;
    }
    else if (OBJ_IS_CODE_OBJECT(L))
    {
        PolyWord *cp;
        POLYUNSIGNED const_count;
        p->GetConstSegmentForCode(cp, const_count);

        if (show_size)
            ShowCode(p);

        return L; // Process addresses in it.
     }
    else /* Word object */
    {
        if (show_size)
            ShowWords(p);
        return L; // Process addresses in it.
    }
}

Handle ObjSize(TaskData *taskData, Handle obj)
{
    ProcessVisitAddresses process(false);
    process.ScanObjectAddress(obj->WordP());
    return Make_arbitrary_precision(taskData, process.total_length);
}

Handle ShowSize(TaskData *taskData, Handle obj)
{
    ProcessVisitAddresses process(true);
    process.ScanObjectAddress(obj->WordP());
    fflush(polyStdout); /* We need this for Windows at least. */
    return Make_arbitrary_precision(taskData, process.total_length);
}

static void printfprof(unsigned *counts)
{
    for(unsigned i = 0; i < MAX_PROF_LEN+1; i++)
    {
        if (counts[i] != 0)
        {
            if (i == MAX_PROF_LEN)
                fprintf(polyStdout, ">%d\t%u\n", MAX_PROF_LEN, counts[i]);
            else
                fprintf(polyStdout, "%d\t%u\n", i, counts[i]);
        }
    }
}

Handle ObjProfile(TaskData *taskData, Handle obj)
{
    ProcessVisitAddresses process(false);
    process.ScanObjectAddress(obj->WordP());
    fprintf(polyStdout, "\nImmutable object sizes and counts\n");
    printfprof(process.iprofile);
    fprintf(polyStdout, "\nMutable object sizes and counts\n");
    printfprof(process.mprofile);
    fflush(polyStdout); /* We need this for Windows at least. */
    return Make_arbitrary_precision(taskData, process.total_length);
}

POLYUNSIGNED PolyObjSize(PolyObject *threadId, PolyWord obj)
{
    TaskData *taskData = TaskData::FindTaskForId(threadId);
    ASSERT(taskData != 0);
    taskData->PreRTSCall();

    ProcessVisitAddresses process(false);
    process.ScanObjectAddress(obj.AsObjPtr());
    Handle result = Make_arbitrary_precision(taskData, process.total_length);

    taskData->PostRTSCall();
    return result->Word().AsUnsigned();
}

POLYUNSIGNED PolyShowSize(PolyObject *threadId, PolyWord obj)
{
    TaskData *taskData = TaskData::FindTaskForId(threadId);
    ASSERT(taskData != 0);
    taskData->PreRTSCall();

    ProcessVisitAddresses process(true);
    process.ScanObjectAddress(obj.AsObjPtr());
    fflush(polyStdout); /* We need this for Windows at least. */
    Handle result = Make_arbitrary_precision(taskData, process.total_length);

    taskData->PostRTSCall();
    return result->Word().AsUnsigned();
}

POLYUNSIGNED PolyObjProfile(PolyObject *threadId, PolyWord obj)
{
    TaskData *taskData = TaskData::FindTaskForId(threadId);
    ASSERT(taskData != 0);
    taskData->PreRTSCall();

    ProcessVisitAddresses process(false);
    process.ScanObjectAddress(obj.AsObjPtr());
    fprintf(polyStdout, "\nImmutable object sizes and counts\n");
    printfprof(process.iprofile);
    fprintf(polyStdout, "\nMutable object sizes and counts\n");
    printfprof(process.mprofile);
    fflush(polyStdout); /* We need this for Windows at least. */
    Handle result = Make_arbitrary_precision(taskData, process.total_length);

    taskData->PostRTSCall();
    return result->Word().AsUnsigned();
}

static struct _entrypts entryPtTable[] =
{
    { "PolyObjSize",                    (polyRTSFunction)&PolyObjSize},
    { "PolyShowSize",                   (polyRTSFunction)&PolyShowSize},
    { "PolyObjProfile",                 (polyRTSFunction)&PolyObjProfile},

    { NULL, NULL} // End of list.
};

class ObjSizeModule: public RtsModule
{
public:
    virtual entrypts GetRTSCalls(void) { return entryPtTable; }
};

// Declare this.  It will be automatically added to the table.
static ObjSizeModule objSizeModule;
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkColorSpaceXform.h"
#include "SkColorSpaceXformCanvas.h"
#include "SkMakeUnique.h"
#include "SkNoDrawCanvas.h"
#include "SkSurface.h"
#include "SkTLazy.h"

class SkColorSpaceXformCanvas : public SkNoDrawCanvas {
public:
    SkColorSpaceXformCanvas(SkCanvas* target,
                            sk_sp<SkColorSpace> targetCS)
        : SkNoDrawCanvas(SkIRect::MakeSize(target->getBaseLayerSize()))
        , fTarget(target)
        , fTargetCS(std::move(targetCS)) {

        fFromSRGB = SkColorSpaceXform::New(SkColorSpace::MakeSRGB().get(), fTargetCS.get());
    }

    SkColor xform(SkColor srgb) const {
        SkColor xformed;
        SkAssertResult(fFromSRGB->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &xformed,
                                        SkColorSpaceXform::kBGRA_8888_ColorFormat, &srgb,
                                        1, kUnpremul_SkAlphaType));
        return xformed;
    }

    const SkPaint& xform(const SkPaint& paint, SkTLazy<SkPaint>* lazy) const {
        const SkPaint* result = &paint;
        auto get_lazy = [&] {
            if (!lazy->isValid()) {
                lazy->init(paint);
                result = lazy->get();
            }
            return lazy->get();
        };

        // All SkColorSpaces have the same black point.
        if (paint.getColor() & 0xffffff) {
            get_lazy()->setColor(this->xform(paint.getColor()));
        }

        // TODO:
        //    - shaders
        //    - color filters
        //    - image filters?

        return *result;
    }

    sk_sp<const SkImage> xform(const SkImage* img) const {
        // TODO: for real
        return sk_ref_sp(img);
    }

    const SkPaint* xform(const SkPaint* paint, SkTLazy<SkPaint>* lazy) const {
        return paint ? &this->xform(*paint, lazy) : nullptr;
    }

    void onDrawPaint(const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPaint(this->xform(paint, &lazy));
    }

    void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawRect(rect, this->xform(paint, &lazy));
    }
    void onDrawOval(const SkRect& oval, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawOval(oval, this->xform(paint, &lazy));
    }
    void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawRRect(rrect, this->xform(paint, &lazy));
    }
    void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawDRRect(outer, inner, this->xform(paint, &lazy));
    }
    void onDrawPath(const SkPath& path, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPath(path, this->xform(paint, &lazy));
    }
    void onDrawArc(const SkRect& oval, SkScalar start, SkScalar sweep, bool useCenter,
                   const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawArc(oval, start, sweep, useCenter, this->xform(paint, &lazy));
    }
    void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawRegion(region, this->xform(paint, &lazy));
    }
    void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texs[4],
                     SkBlendMode mode, const SkPaint& paint) override {
        // TODO: colors
        SkTLazy<SkPaint> lazy;
        fTarget->drawPatch(cubics, colors, texs, mode, this->xform(paint, &lazy));
    }
    void onDrawPoints(PointMode mode, size_t count, const SkPoint* pts,
                      const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPoints(mode, count, pts, this->xform(paint, &lazy));
    }
    void onDrawVertices(VertexMode vmode, int count,
                        const SkPoint* verts, const SkPoint* texs, const SkColor* colors,
                        SkBlendMode mode,
                        const uint16_t* indices, int indexCount, const SkPaint& paint) override {
        // TODO: colors
        SkTLazy<SkPaint> lazy;
        fTarget->drawVertices(vmode, count, verts, texs, colors, mode, indices, indexCount,
                              this->xform(paint, &lazy));
    }

    void onDrawText(const void* ptr, size_t len,
                    SkScalar x, SkScalar y,
                    const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawText(ptr, len, x, y, this->xform(paint, &lazy));
    }
    void onDrawPosText(const void* ptr, size_t len,
                       const SkPoint* xys,
                       const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPosText(ptr, len, xys, this->xform(paint, &lazy));
    }
    void onDrawPosTextH(const void* ptr, size_t len,
                        const SkScalar* xs, SkScalar y,
                        const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPosTextH(ptr, len, xs, y, this->xform(paint, &lazy));
    }
    void onDrawTextOnPath(const void* ptr, size_t len,
                          const SkPath& path, const SkMatrix* matrix,
                          const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawTextOnPath(ptr, len, path, matrix, this->xform(paint, &lazy));
    }
    void onDrawTextRSXform(const void* ptr, size_t len,
                           const SkRSXform* xforms, const SkRect* cull,
                           const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawTextRSXform(ptr, len, xforms, cull, this->xform(paint, &lazy));
    }
    void onDrawTextBlob(const SkTextBlob* blob,
                        SkScalar x, SkScalar y,
                        const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawTextBlob(blob, x, y, this->xform(paint, &lazy));
    }

    void onDrawImage(const SkImage* img,
                     SkScalar l, SkScalar t,
                     const SkPaint* paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawImage(this->xform(img).get(),
                           l, t,
                           this->xform(paint, &lazy));
    }
    void onDrawImageRect(const SkImage* img,
                         const SkRect* src, const SkRect& dst,
                         const SkPaint* paint, SrcRectConstraint constraint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawImageRect(this->xform(img).get(),
                               src ? *src : dst, dst,
                               this->xform(paint, &lazy), constraint);
    }
    void onDrawImageNine(const SkImage* img,
                         const SkIRect& center, const SkRect& dst,
                         const SkPaint* paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawImageNine(this->xform(img).get(),
                               center, dst,
                               this->xform(paint, &lazy));
    }
    void onDrawImageLattice(const SkImage* img,
                            const Lattice& lattice, const SkRect& dst,
                            const SkPaint* paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawImageLattice(this->xform(img).get(),
                                  lattice, dst,
                                  this->xform(paint, &lazy));
    }
    void onDrawAtlas(const SkImage* atlas, const SkRSXform* xforms, const SkRect* tex,
                     const SkColor* colors, int count, SkBlendMode mode,
                     const SkRect* cull, const SkPaint* paint) override {
        // TODO: colors
        SkTLazy<SkPaint> lazy;
        fTarget->drawAtlas(this->xform(atlas).get(), xforms, tex, colors, count, mode, cull,
                           this->xform(paint, &lazy));
    }

    void onDrawBitmap(const SkBitmap& bitmap,
                      SkScalar l, SkScalar t,
                      const SkPaint* paint) override {
        if (auto image = SkImage::MakeFromBitmap(bitmap)) {
            this->onDrawImage(image.get(), l, t, paint);
        }
    }
    void onDrawBitmapRect(const SkBitmap& bitmap,
                          const SkRect* src, const SkRect& dst,
                          const SkPaint* paint, SrcRectConstraint constraint) override {
        if (auto image = SkImage::MakeFromBitmap(bitmap)) {
            this->onDrawImageRect(image.get(), src, dst, paint, constraint);
        }
    }
    void onDrawBitmapNine(const SkBitmap& bitmap,
                          const SkIRect& center, const SkRect& dst,
                          const SkPaint* paint) override {
        if (auto image = SkImage::MakeFromBitmap(bitmap)) {
            this->onDrawImageNine(image.get(), center, dst, paint);
        }
    }
    void onDrawBitmapLattice(const SkBitmap& bitmap,
                             const Lattice& lattice, const SkRect& dst,
                             const SkPaint* paint) override {
        if (auto image = SkImage::MakeFromBitmap(bitmap)) {
            this->onDrawImageLattice(image.get(), lattice, dst, paint);
        }
    }

    // TODO: May not be ideal to unfurl pictures.
    void onDrawPicture(const SkPicture* pic,
                       const SkMatrix* matrix,
                       const SkPaint* paint) override {
        SkTLazy<SkPaint> lazy;
        SkCanvas::onDrawPicture(pic, matrix, this->xform(paint, &lazy));
    }
    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
        SkTLazy<SkPaint> lazy;
        SkCanvas::onDrawDrawable(drawable, matrix);
    }

    SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
        SkTLazy<SkPaint> lazy;
        fTarget->saveLayer({
            rec.fBounds,
            this->xform(rec.fPaint, &lazy),
            rec.fBackdrop,  // TODO: this is an image filter
            rec.fSaveLayerFlags,
        });
        return kNoLayer_SaveLayerStrategy;
    }

    // Everything from here on should be uninteresting strictly proxied state-change calls.
    void willSave()    override { fTarget->save(); }
    void willRestore() override { fTarget->restore(); }

    void didConcat   (const SkMatrix& m) override { fTarget->concat   (m); }
    void didSetMatrix(const SkMatrix& m) override { fTarget->setMatrix(m); }

    void onClipRect(const SkRect& clip, SkClipOp op, ClipEdgeStyle style) override {
        fTarget->clipRect(clip, op, style);
    }
    void onClipRRect(const SkRRect& clip, SkClipOp op, ClipEdgeStyle style) override {
        fTarget->clipRRect(clip, op, style);
    }
    void onClipPath(const SkPath& clip, SkClipOp op, ClipEdgeStyle style) override {
        fTarget->clipPath(clip, op, style);
    }
    void onClipRegion(const SkRegion& clip, SkClipOp op) override {
        fTarget->clipRegion(clip, op);
    }

    void onDrawAnnotation(const SkRect& rect, const char* key, SkData* val) override {
        fTarget->drawAnnotation(rect, key, val);
    }

    sk_sp<SkSurface> onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) override {
        return fTarget->makeSurface(info, &props);
    }

private:
    SkCanvas*                          fTarget;
    sk_sp<SkColorSpace>                fTargetCS;
    std::unique_ptr<SkColorSpaceXform> fFromSRGB;
};

std::unique_ptr<SkCanvas> SkCreateColorSpaceXformCanvas(SkCanvas* target,
                                                        sk_sp<SkColorSpace> targetCS) {
    return skstd::make_unique<SkColorSpaceXformCanvas>(target, std::move(targetCS));
}
<commit_msg>More SkCSXformCanvas work.<commit_after>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkColorFilter.h"
#include "SkColorSpaceXform.h"
#include "SkColorSpaceXformCanvas.h"
#include "SkMakeUnique.h"
#include "SkNoDrawCanvas.h"
#include "SkSurface.h"
#include "SkTLazy.h"

class SkColorSpaceXformCanvas : public SkNoDrawCanvas {
public:
    SkColorSpaceXformCanvas(SkCanvas* target,
                            sk_sp<SkColorSpace> targetCS)
        : SkNoDrawCanvas(SkIRect::MakeSize(target->getBaseLayerSize()))
        , fTarget(target)
        , fTargetCS(std::move(targetCS)) {

        fFromSRGB = SkColorSpaceXform::New(SkColorSpace::MakeSRGB().get(), fTargetCS.get());
    }

    void xform(SkColor* xformed, const SkColor* srgb, int n) const {
        SkAssertResult(fFromSRGB->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, xformed,
                                        SkColorSpaceXform::kBGRA_8888_ColorFormat, srgb,
                                        n, kUnpremul_SkAlphaType));
    }

    SkColor xform(SkColor srgb) const {
        SkColor xformed;
        this->xform(&xformed, &srgb, 1);
        return xformed;
    }

    const SkPaint& xform(const SkPaint& paint, SkTLazy<SkPaint>* lazy) const {
        const SkPaint* result = &paint;
        auto get_lazy = [&] {
            if (!lazy->isValid()) {
                lazy->init(paint);
                result = lazy->get();
            }
            return lazy->get();
        };

        // All SkColorSpaces have the same black point.
        if (paint.getColor() & 0xffffff) {
            get_lazy()->setColor(this->xform(paint.getColor()));
        }

        // As far as I know, SkModeColorFilter is the only color filter that holds a color.
        if (auto cf = paint.getColorFilter()) {
            SkColor color;
            SkBlendMode mode;
            if (cf->asColorMode(&color, &mode)) {
                get_lazy()->setColorFilter(SkColorFilter::MakeModeFilter(this->xform(color), mode));
            }
        }

        // TODO:
        //    - shaders
        //    - image filters?

        return *result;
    }

    const SkPaint* xform(const SkPaint* paint, SkTLazy<SkPaint>* lazy) const {
        return paint ? &this->xform(*paint, lazy) : nullptr;
    }

    sk_sp<const SkImage> xform(const SkImage* img) const {
        // TODO: for real
        return sk_ref_sp(img);
    }

    void onDrawPaint(const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPaint(this->xform(paint, &lazy));
    }

    void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawRect(rect, this->xform(paint, &lazy));
    }
    void onDrawOval(const SkRect& oval, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawOval(oval, this->xform(paint, &lazy));
    }
    void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawRRect(rrect, this->xform(paint, &lazy));
    }
    void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawDRRect(outer, inner, this->xform(paint, &lazy));
    }
    void onDrawPath(const SkPath& path, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPath(path, this->xform(paint, &lazy));
    }
    void onDrawArc(const SkRect& oval, SkScalar start, SkScalar sweep, bool useCenter,
                   const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawArc(oval, start, sweep, useCenter, this->xform(paint, &lazy));
    }
    void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawRegion(region, this->xform(paint, &lazy));
    }
    void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texs[4],
                     SkBlendMode mode, const SkPaint& paint) override {
        SkColor xformed[4];
        if (colors) {
            this->xform(xformed, colors, 4);
            colors = xformed;
        }

        SkTLazy<SkPaint> lazy;
        fTarget->drawPatch(cubics, colors, texs, mode, this->xform(paint, &lazy));
    }
    void onDrawPoints(PointMode mode, size_t count, const SkPoint* pts,
                      const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPoints(mode, count, pts, this->xform(paint, &lazy));
    }
    void onDrawVertices(VertexMode vmode, int count,
                        const SkPoint* verts, const SkPoint* texs, const SkColor* colors,
                        SkBlendMode mode,
                        const uint16_t* indices, int indexCount, const SkPaint& paint) override {
        SkTArray<SkColor> xformed;
        if (colors) {
            xformed.reset(count);
            this->xform(xformed.begin(), colors, count);
            colors = xformed.begin();
        }

        SkTLazy<SkPaint> lazy;
        fTarget->drawVertices(vmode, count, verts, texs, colors, mode, indices, indexCount,
                              this->xform(paint, &lazy));
    }

    void onDrawText(const void* ptr, size_t len,
                    SkScalar x, SkScalar y,
                    const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawText(ptr, len, x, y, this->xform(paint, &lazy));
    }
    void onDrawPosText(const void* ptr, size_t len,
                       const SkPoint* xys,
                       const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPosText(ptr, len, xys, this->xform(paint, &lazy));
    }
    void onDrawPosTextH(const void* ptr, size_t len,
                        const SkScalar* xs, SkScalar y,
                        const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawPosTextH(ptr, len, xs, y, this->xform(paint, &lazy));
    }
    void onDrawTextOnPath(const void* ptr, size_t len,
                          const SkPath& path, const SkMatrix* matrix,
                          const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawTextOnPath(ptr, len, path, matrix, this->xform(paint, &lazy));
    }
    void onDrawTextRSXform(const void* ptr, size_t len,
                           const SkRSXform* xforms, const SkRect* cull,
                           const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawTextRSXform(ptr, len, xforms, cull, this->xform(paint, &lazy));
    }
    void onDrawTextBlob(const SkTextBlob* blob,
                        SkScalar x, SkScalar y,
                        const SkPaint& paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawTextBlob(blob, x, y, this->xform(paint, &lazy));
    }

    void onDrawImage(const SkImage* img,
                     SkScalar l, SkScalar t,
                     const SkPaint* paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawImage(this->xform(img).get(),
                           l, t,
                           this->xform(paint, &lazy));
    }
    void onDrawImageRect(const SkImage* img,
                         const SkRect* src, const SkRect& dst,
                         const SkPaint* paint, SrcRectConstraint constraint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawImageRect(this->xform(img).get(),
                               src ? *src : dst, dst,
                               this->xform(paint, &lazy), constraint);
    }
    void onDrawImageNine(const SkImage* img,
                         const SkIRect& center, const SkRect& dst,
                         const SkPaint* paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawImageNine(this->xform(img).get(),
                               center, dst,
                               this->xform(paint, &lazy));
    }
    void onDrawImageLattice(const SkImage* img,
                            const Lattice& lattice, const SkRect& dst,
                            const SkPaint* paint) override {
        SkTLazy<SkPaint> lazy;
        fTarget->drawImageLattice(this->xform(img).get(),
                                  lattice, dst,
                                  this->xform(paint, &lazy));
    }
    void onDrawAtlas(const SkImage* atlas, const SkRSXform* xforms, const SkRect* tex,
                     const SkColor* colors, int count, SkBlendMode mode,
                     const SkRect* cull, const SkPaint* paint) override {
        SkTArray<SkColor> xformed;
        if (colors) {
            xformed.reset(count);
            this->xform(xformed.begin(), colors, count);
            colors = xformed.begin();
        }

        SkTLazy<SkPaint> lazy;
        fTarget->drawAtlas(this->xform(atlas).get(), xforms, tex, colors, count, mode, cull,
                           this->xform(paint, &lazy));
    }

    void onDrawBitmap(const SkBitmap& bitmap,
                      SkScalar l, SkScalar t,
                      const SkPaint* paint) override {
        if (auto image = SkImage::MakeFromBitmap(bitmap)) {
            this->onDrawImage(image.get(), l, t, paint);
        }
    }
    void onDrawBitmapRect(const SkBitmap& bitmap,
                          const SkRect* src, const SkRect& dst,
                          const SkPaint* paint, SrcRectConstraint constraint) override {
        if (auto image = SkImage::MakeFromBitmap(bitmap)) {
            this->onDrawImageRect(image.get(), src, dst, paint, constraint);
        }
    }
    void onDrawBitmapNine(const SkBitmap& bitmap,
                          const SkIRect& center, const SkRect& dst,
                          const SkPaint* paint) override {
        if (auto image = SkImage::MakeFromBitmap(bitmap)) {
            this->onDrawImageNine(image.get(), center, dst, paint);
        }
    }
    void onDrawBitmapLattice(const SkBitmap& bitmap,
                             const Lattice& lattice, const SkRect& dst,
                             const SkPaint* paint) override {
        if (auto image = SkImage::MakeFromBitmap(bitmap)) {
            this->onDrawImageLattice(image.get(), lattice, dst, paint);
        }
    }

    // TODO: May not be ideal to unfurl pictures.
    void onDrawPicture(const SkPicture* pic,
                       const SkMatrix* matrix,
                       const SkPaint* paint) override {
        SkTLazy<SkPaint> lazy;
        SkCanvas::onDrawPicture(pic, matrix, this->xform(paint, &lazy));
    }
    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
        SkTLazy<SkPaint> lazy;
        SkCanvas::onDrawDrawable(drawable, matrix);
    }

    SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
        SkTLazy<SkPaint> lazy;
        fTarget->saveLayer({
            rec.fBounds,
            this->xform(rec.fPaint, &lazy),
            rec.fBackdrop,  // TODO: this is an image filter
            rec.fSaveLayerFlags,
        });
        return kNoLayer_SaveLayerStrategy;
    }

    // Everything from here on should be uninteresting strictly proxied state-change calls.
    void willSave()    override { fTarget->save(); }
    void willRestore() override { fTarget->restore(); }

    void didConcat   (const SkMatrix& m) override { fTarget->concat   (m); }
    void didSetMatrix(const SkMatrix& m) override { fTarget->setMatrix(m); }

    void onClipRect(const SkRect& clip, SkClipOp op, ClipEdgeStyle style) override {
        fTarget->clipRect(clip, op, style);
    }
    void onClipRRect(const SkRRect& clip, SkClipOp op, ClipEdgeStyle style) override {
        fTarget->clipRRect(clip, op, style);
    }
    void onClipPath(const SkPath& clip, SkClipOp op, ClipEdgeStyle style) override {
        fTarget->clipPath(clip, op, style);
    }
    void onClipRegion(const SkRegion& clip, SkClipOp op) override {
        fTarget->clipRegion(clip, op);
    }

    void onDrawAnnotation(const SkRect& rect, const char* key, SkData* val) override {
        fTarget->drawAnnotation(rect, key, val);
    }

    sk_sp<SkSurface> onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) override {
        return fTarget->makeSurface(info, &props);
    }

private:
    SkCanvas*                          fTarget;
    sk_sp<SkColorSpace>                fTargetCS;
    std::unique_ptr<SkColorSpaceXform> fFromSRGB;
};

std::unique_ptr<SkCanvas> SkCreateColorSpaceXformCanvas(SkCanvas* target,
                                                        sk_sp<SkColorSpace> targetCS) {
    return skstd::make_unique<SkColorSpaceXformCanvas>(target, std::move(targetCS));
}
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include "keycode.hpp"

using namespace org_pqrs_KeyRemap4MacBook;

TEST(ModifierFlag, stripFN) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_EQ(ModifierFlag::stripFN(flags), flags);
  EXPECT_EQ(ModifierFlag::stripFN(flags | ModifierFlag::FN), flags);
}

TEST(ModifierFlag, stripCURSOR) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_EQ(ModifierFlag::stripCURSOR(flags), flags);
  EXPECT_EQ(ModifierFlag::stripCURSOR(flags | ModifierFlag::CURSOR), flags);
}

TEST(ModifierFlag, stripNONE) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_EQ(ModifierFlag::stripNONE(flags), flags);
  EXPECT_EQ(ModifierFlag::stripNONE(flags | ModifierFlag::NONE), flags);
}

TEST(ModifierFlag, isOn) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_TRUE(ModifierFlag::isOn(flags, ModifierFlag::SHIFT_L));
  EXPECT_FALSE(ModifierFlag::isOn(flags, ModifierFlag::SHIFT_R));

  flags = ModifierFlag::NONE;
  EXPECT_TRUE(ModifierFlag::isOn(flags, ModifierFlag::NONE));
}

namespace {
  unsigned int keypads[][2] = {
    { KeyCode::KEYPAD_0, KeyCode::M },
    { KeyCode::KEYPAD_1, KeyCode::J },
    { KeyCode::KEYPAD_2, KeyCode::K },
    { KeyCode::KEYPAD_3, KeyCode::L },
    { KeyCode::KEYPAD_4, KeyCode::U },
    { KeyCode::KEYPAD_5, KeyCode::I },
    { KeyCode::KEYPAD_6, KeyCode::O },
    { KeyCode::KEYPAD_7, KeyCode::KEY_7 },
    { KeyCode::KEYPAD_8, KeyCode::KEY_8 },
    { KeyCode::KEYPAD_9, KeyCode::KEY_9 },
    { KeyCode::KEYPAD_CLEAR, KeyCode::KEY_6 },
    { KeyCode::KEYPAD_PLUS, KeyCode::SLASH },
    { KeyCode::KEYPAD_MINUS, KeyCode::SEMICOLON },
    { KeyCode::KEYPAD_MULTIPLY, KeyCode::P },
    { KeyCode::KEYPAD_SLASH, KeyCode::KEY_0 },
    { KeyCode::KEYPAD_EQUAL, KeyCode::MINUS },
    { KeyCode::KEYPAD_DOT, KeyCode::DOT },
  };

  unsigned int cursors[][2] = {
    { KeyCode::PAGEUP, KeyCode::CURSOR_UP },
    { KeyCode::PAGEDOWN, KeyCode::CURSOR_DOWN },
    { KeyCode::HOME, KeyCode::CURSOR_LEFT },
    { KeyCode::END, KeyCode::CURSOR_RIGHT },
  };
}

TEST(KeyCode, normalizeKey) {
  unsigned int key = 0;
  unsigned int flags = 0;
  unsigned int keyboardType = 0;

  // ENTER_POWERBOOK -> ENTER
  key = KeyCode::ENTER_POWERBOOK; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::POWERBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // ENTER_POWERBOOK(+FN) -> ENTER(+FN) -> RETURN
  key = KeyCode::ENTER_POWERBOOK; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::POWERBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::RETURN)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // normal key
  key = KeyCode::A; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::A)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // KEYPAD
  for (size_t i = 0; i < sizeof(keypads) / sizeof(keypads[0]); ++i) {
    key = keypads[i][0]; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, keypads[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
  }

  // PAGEUP
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][0]; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, cursors[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
  }

  // ENTER
  key = KeyCode::ENTER; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // FORWARD_DELETE
  key = KeyCode::FORWARD_DELETE; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::FORWARD_DELETE)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // normal key(+FN)
  key = KeyCode::A; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::A)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));

  // KEYPAD(+FN)
  for (size_t i = 0; i < sizeof(keypads) / sizeof(keypads[0]); ++i) {
    key = keypads[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, static_cast<unsigned int>(keypads[i][1])); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
  }

  // PAGEUP(+FN)
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, cursors[i][1]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::CURSOR));
  }

  // ENTER(+FN)
  key = KeyCode::ENTER; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::RETURN)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // FORWARD_DELETE(+FN)
  key = KeyCode::FORWARD_DELETE; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::DELETE)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
}

TEST(KeyCode, reverseNormalizeKey) {
  unsigned int key = 0;
  unsigned int flags = 0;
  unsigned int keyboardType = 0;

  // ENTER_POWERBOOK -> ENTER
  key = KeyCode::ENTER_POWERBOOK; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::POWERBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER_POWERBOOK)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // ENTER_POWERBOOK(+FN) -> ENTER(+FN) -> RETURN
  key = KeyCode::ENTER_POWERBOOK; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::POWERBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  flags |= ModifierFlag::FN;
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER_POWERBOOK)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));

  // normal key
  key = KeyCode::A; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::A)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // KEYPAD
  for (size_t i = 0; i < sizeof(keypads) / sizeof(keypads[0]); ++i) {
    key = keypads[i][0]; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, keypads[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
  }

  // PAGEUP
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][0]; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, cursors[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
  }

  // ENTER
  key = KeyCode::ENTER; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // FORWARD_DELETE
  key = KeyCode::FORWARD_DELETE; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::FORWARD_DELETE)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // normal key(+FN)
  key = KeyCode::A; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  flags |= ModifierFlag::FN;
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::A)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));

  // KEYPAD(+FN)
  for (size_t i = 0; i < sizeof(keypads) / sizeof(keypads[0]); ++i) {
    key = keypads[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    flags |= ModifierFlag::FN;
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, static_cast<unsigned int>(keypads[i][0])); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));
  }

  // PAGEUP(+FN)
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    flags |= ModifierFlag::FN;
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, cursors[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));
  }

  // ENTER(+FN)
  key = KeyCode::ENTER; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  flags |= ModifierFlag::FN;
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));

  // FORWARD_DELETE(+FN)
  key = KeyCode::FORWARD_DELETE; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  flags |= ModifierFlag::FN;
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::FORWARD_DELETE)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));
}
<commit_msg>update kext/Tests<commit_after>#include <gtest/gtest.h>
#include "keycode.hpp"

using namespace org_pqrs_KeyRemap4MacBook;

TEST(ModifierFlag, stripFN) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_EQ(ModifierFlag::stripFN(flags), flags);
  EXPECT_EQ(ModifierFlag::stripFN(flags | ModifierFlag::FN), flags);
}

TEST(ModifierFlag, stripCURSOR) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_EQ(ModifierFlag::stripCURSOR(flags), flags);
  EXPECT_EQ(ModifierFlag::stripCURSOR(flags | ModifierFlag::CURSOR), flags);
}

TEST(ModifierFlag, stripKEYPAD) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_EQ(ModifierFlag::stripKEYPAD(flags), flags);
  EXPECT_EQ(ModifierFlag::stripKEYPAD(flags | ModifierFlag::KEYPAD), flags);
}

TEST(ModifierFlag, stripNONE) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_EQ(ModifierFlag::stripNONE(flags), flags);
  EXPECT_EQ(ModifierFlag::stripNONE(flags | ModifierFlag::NONE), flags);
}

TEST(ModifierFlag, isOn) {
  unsigned int flags = ModifierFlag::SHIFT_L | ModifierFlag::CONTROL_R | ModifierFlag::COMMAND_R;
  EXPECT_TRUE(ModifierFlag::isOn(flags, ModifierFlag::SHIFT_L));
  EXPECT_FALSE(ModifierFlag::isOn(flags, ModifierFlag::SHIFT_R));

  flags = ModifierFlag::NONE;
  EXPECT_TRUE(ModifierFlag::isOn(flags, ModifierFlag::NONE));
}

namespace {
  unsigned int keypads[][2] = {
    { KeyCode::KEYPAD_0, KeyCode::M },
    { KeyCode::KEYPAD_1, KeyCode::J },
    { KeyCode::KEYPAD_2, KeyCode::K },
    { KeyCode::KEYPAD_3, KeyCode::L },
    { KeyCode::KEYPAD_4, KeyCode::U },
    { KeyCode::KEYPAD_5, KeyCode::I },
    { KeyCode::KEYPAD_6, KeyCode::O },
    { KeyCode::KEYPAD_7, KeyCode::KEY_7 },
    { KeyCode::KEYPAD_8, KeyCode::KEY_8 },
    { KeyCode::KEYPAD_9, KeyCode::KEY_9 },
    { KeyCode::KEYPAD_CLEAR, KeyCode::KEY_6 },
    { KeyCode::KEYPAD_PLUS, KeyCode::SLASH },
    { KeyCode::KEYPAD_MINUS, KeyCode::SEMICOLON },
    { KeyCode::KEYPAD_MULTIPLY, KeyCode::P },
    { KeyCode::KEYPAD_SLASH, KeyCode::KEY_0 },
    { KeyCode::KEYPAD_EQUAL, KeyCode::MINUS },
    { KeyCode::KEYPAD_DOT, KeyCode::DOT },
  };

  unsigned int cursors[][2] = {
    { KeyCode::PAGEUP, KeyCode::CURSOR_UP },
    { KeyCode::PAGEDOWN, KeyCode::CURSOR_DOWN },
    { KeyCode::HOME, KeyCode::CURSOR_LEFT },
    { KeyCode::END, KeyCode::CURSOR_RIGHT },
  };
}

TEST(KeyCode, normalizeKey) {
  unsigned int key = 0;
  unsigned int flags = 0;
  unsigned int keyboardType = 0;

  // ENTER_POWERBOOK -> ENTER
  key = KeyCode::ENTER_POWERBOOK; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::POWERBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // ENTER_POWERBOOK(+FN) -> ENTER(+FN) -> RETURN
  key = KeyCode::ENTER_POWERBOOK; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::POWERBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::RETURN)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // normal key
  key = KeyCode::A; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::A)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // KEYPAD
  for (size_t i = 0; i < sizeof(keypads) / sizeof(keypads[0]); ++i) {
    key = keypads[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::KEYPAD; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, keypads[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::KEYPAD));
  }

  // PAGEUP
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][0]; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, cursors[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
  }

  // ENTER
  key = KeyCode::ENTER; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // FORWARD_DELETE
  key = KeyCode::FORWARD_DELETE; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::FORWARD_DELETE)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // normal key(+FN)
  key = KeyCode::A; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::A)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));

  // KEYPAD(+FN)
  for (size_t i = 0; i < sizeof(keypads) / sizeof(keypads[0]); ++i) {
    key = keypads[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN | ModifierFlag::KEYPAD; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, static_cast<unsigned int>(keypads[i][1])); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::KEYPAD));
  }

  // PAGEUP(+FN)
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, cursors[i][1]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::CURSOR));
  }

  // ENTER(+FN)
  key = KeyCode::ENTER; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::RETURN)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // FORWARD_DELETE(+FN)
  key = KeyCode::FORWARD_DELETE; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::DELETE)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
}

TEST(KeyCode, reverseNormalizeKey) {
  unsigned int key = 0;
  unsigned int flags = 0;
  unsigned int keyboardType = 0;

  // ENTER_POWERBOOK -> ENTER
  key = KeyCode::ENTER_POWERBOOK; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::POWERBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER_POWERBOOK)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // ENTER_POWERBOOK(+FN) -> ENTER(+FN) -> RETURN
  key = KeyCode::ENTER_POWERBOOK; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::POWERBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  flags |= ModifierFlag::FN;
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER_POWERBOOK)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));

  // normal key
  key = KeyCode::A; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::A)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // KEYPAD
  for (size_t i = 0; i < sizeof(keypads) / sizeof(keypads[0]); ++i) {
    key = keypads[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::KEYPAD; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, keypads[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::KEYPAD));
  }

  // PAGEUP
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][0]; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, cursors[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));
  }

  // ENTER
  key = KeyCode::ENTER; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // FORWARD_DELETE
  key = KeyCode::FORWARD_DELETE; flags = ModifierFlag::SHIFT_L; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::FORWARD_DELETE)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L));

  // CURSOR
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][1]; flags = ModifierFlag::CURSOR; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, static_cast<unsigned int>(cursors[i][1])); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::CURSOR));
  }

  // normal key(+FN)
  key = KeyCode::A; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  flags |= ModifierFlag::FN;
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::A)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));

  // KEYPAD(+FN)
  for (size_t i = 0; i < sizeof(keypads) / sizeof(keypads[0]); ++i) {
    key = keypads[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN | ModifierFlag::KEYPAD; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    flags |= ModifierFlag::FN;
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, static_cast<unsigned int>(keypads[i][0])); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN | ModifierFlag::KEYPAD));
  }

  // PAGEUP(+FN)
  for (size_t i = 0; i < sizeof(cursors) / sizeof(cursors[0]); ++i) {
    key = cursors[i][0]; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
    KeyCode::normalizeKey(key, flags, keyboardType);
    flags |= ModifierFlag::FN;
    KeyCode::reverseNormalizeKey(key, flags, keyboardType);
    EXPECT_EQ(key, cursors[i][0]); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));
  }

  // ENTER(+FN)
  key = KeyCode::ENTER; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  flags |= ModifierFlag::FN;
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::ENTER)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));

  // FORWARD_DELETE(+FN)
  key = KeyCode::FORWARD_DELETE; flags = ModifierFlag::SHIFT_L | ModifierFlag::FN; keyboardType = KeyboardType::MACBOOK;
  KeyCode::normalizeKey(key, flags, keyboardType);
  flags |= ModifierFlag::FN;
  KeyCode::reverseNormalizeKey(key, flags, keyboardType);
  EXPECT_EQ(key, static_cast<unsigned int>(KeyCode::FORWARD_DELETE)); EXPECT_EQ(flags, static_cast<unsigned int>(ModifierFlag::SHIFT_L | ModifierFlag::FN));
}
<|endoftext|>


<commit_before>/*

 SD - a slightly more friendly wrapper for sdfatlib

 This library aims to expose a subset of SD card functionality
 in the form of a higher level "wrapper" object.

 License: GNU General Public License V3
          (Because sdfatlib is licensed with this.)

 (C) Copyright 2010 SparkFun Electronics

 */

#include <SD.h>

/* for debugging file open/close leaks
   uint8_t nfilecount=0;
*/

File::File(SdFile f, char *n) {
  // oh man you are kidding me, new() doesnt exist? Ok we do it by hand!
  _file = (SdFile *)malloc(sizeof(SdFile));
  if (_file) {
    memcpy(_file, &f, sizeof(SdFile));

    strncpy(_name, n, 12);
    _name[12] = 0;

    /* for debugging file open/close leaks
       nfilecount++;
       Serial.print("Created \"");
       Serial.print(n);
       Serial.print("\": ");
       Serial.println(nfilecount, DEC);
    */
  }
}

File::File(void) {
  _file = 0;
  _name[0] = 0;
  //Serial.print("Created empty file object");
}

File::~File(void) {
  //  Serial.print("Deleted file object");
}

// returns a pointer to the file name
char *File::name(void) {
  return _name;
}

// a directory is a special type of file
boolean File::isDirectory(void) {
  return (_file && _file->isDir());
}

size_t File::write(uint8_t val) {
  return write(&val, 1);
}

size_t File::write(const char *str) {
  return write((const uint8_t *) str, strlen(str));
}

size_t File::write(const uint8_t *buf, size_t size) {
  size_t t;
  if (!_file) {
    setWriteError();
    return 0;
  }
  _file->clearWriteError();
  t = _file->write(buf, size);
  if (_file->writeError()) {
    setWriteError();
    return 0;
  }
  return t;
}

int File::peek() {
  if (! _file)
    return 0;

  int c = _file->read();
  if (c != -1) _file->seekCur(-1);
  return c;
}

int File::read() {
  if (_file)
    return _file->read();
  return -1;
}

// buffered read for more efficient, high speed reading
int File::read(void *buf, uint16_t nbyte) {
  if (_file)
    return _file->read(buf, nbyte);
  return 0;
}

int File::available() {
  if (! _file) return 0;
  return size() - position();
}

void File::flush() {
  if (_file)
    _file->sync();
}

boolean File::seek(uint32_t pos) {
  if (! _file) return false;

  return _file->seekSet(pos);
}

uint32_t File::position() {
  if (! _file) return -1;
  return _file->curPosition();
}

uint32_t File::size() {
  if (! _file) return 0;
  return _file->fileSize();
}

void File::close() {
  if (_file) {
    _file->close();
    free(_file);
    _file = 0;

    /* for debugging file open/close leaks
    nfilecount--;
    Serial.print("Deleted ");
    Serial.println(nfilecount, DEC);
    */
  }
}

File::operator bool() {
  if (_file)
    return  _file->isOpen();
  return false;
}

<commit_msg>Fixing SD library compilation error (writeError() -> getWriteError()).<commit_after>/*

 SD - a slightly more friendly wrapper for sdfatlib

 This library aims to expose a subset of SD card functionality
 in the form of a higher level "wrapper" object.

 License: GNU General Public License V3
          (Because sdfatlib is licensed with this.)

 (C) Copyright 2010 SparkFun Electronics

 */

#include <SD.h>

/* for debugging file open/close leaks
   uint8_t nfilecount=0;
*/

File::File(SdFile f, char *n) {
  // oh man you are kidding me, new() doesnt exist? Ok we do it by hand!
  _file = (SdFile *)malloc(sizeof(SdFile));
  if (_file) {
    memcpy(_file, &f, sizeof(SdFile));

    strncpy(_name, n, 12);
    _name[12] = 0;

    /* for debugging file open/close leaks
       nfilecount++;
       Serial.print("Created \"");
       Serial.print(n);
       Serial.print("\": ");
       Serial.println(nfilecount, DEC);
    */
  }
}

File::File(void) {
  _file = 0;
  _name[0] = 0;
  //Serial.print("Created empty file object");
}

File::~File(void) {
  //  Serial.print("Deleted file object");
}

// returns a pointer to the file name
char *File::name(void) {
  return _name;
}

// a directory is a special type of file
boolean File::isDirectory(void) {
  return (_file && _file->isDir());
}

size_t File::write(uint8_t val) {
  return write(&val, 1);
}

size_t File::write(const char *str) {
  return write((const uint8_t *) str, strlen(str));
}

size_t File::write(const uint8_t *buf, size_t size) {
  size_t t;
  if (!_file) {
    setWriteError();
    return 0;
  }
  _file->clearWriteError();
  t = _file->write(buf, size);
  if (_file->getWriteError()) {
    setWriteError();
    return 0;
  }
  return t;
}

int File::peek() {
  if (! _file)
    return 0;

  int c = _file->read();
  if (c != -1) _file->seekCur(-1);
  return c;
}

int File::read() {
  if (_file)
    return _file->read();
  return -1;
}

// buffered read for more efficient, high speed reading
int File::read(void *buf, uint16_t nbyte) {
  if (_file)
    return _file->read(buf, nbyte);
  return 0;
}

int File::available() {
  if (! _file) return 0;
  return size() - position();
}

void File::flush() {
  if (_file)
    _file->sync();
}

boolean File::seek(uint32_t pos) {
  if (! _file) return false;

  return _file->seekSet(pos);
}

uint32_t File::position() {
  if (! _file) return -1;
  return _file->curPosition();
}

uint32_t File::size() {
  if (! _file) return 0;
  return _file->fileSize();
}

void File::close() {
  if (_file) {
    _file->close();
    free(_file);
    _file = 0;

    /* for debugging file open/close leaks
    nfilecount--;
    Serial.print("Deleted ");
    Serial.println(nfilecount, DEC);
    */
  }
}

File::operator bool() {
  if (_file)
    return  _file->isOpen();
  return false;
}

<|endoftext|>