<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id$
 */

#if !defined(CODEWARRIORDEFS_HPP)
#define CODEWARRIORDEFS_HPP

// ---------------------------------------------------------------------------
//  Include some runtime files that will be needed product wide
// ---------------------------------------------------------------------------
//#include <sys/types.h>  	// for size_t and ssize_t
//#include <limits.h>		// for MAX of size_t and ssize_t
//#include <extras.h>

#if defined(_WIN32) || defined(WIN32)
#include <wchar.h>
#include <wctype.h>
#endif

// ---------------------------------------------------------------------------
//  A define in the build for each project is also used to control whether
//  the export keyword is from the project's viewpoint or the client's.
//  These defines provide the platform specific keywords that they need
//  to do this.
// ---------------------------------------------------------------------------
#if defined(macintosh)
#define PLATFORM_EXPORT		__declspec(export)
#define PLATFORM_IMPORT		__declspec(import)
#elif defined(_WIN32) || defined(WIN32)
#define PLATFORM_EXPORT     __declspec(dllexport)
#define PLATFORM_IMPORT     __declspec(dllimport)
#endif

// ---------------------------------------------------------------------------
//  Indicate that we do not support native bools
//  If the compiler can handle boolean itself, do not define it
// ---------------------------------------------------------------------------
// #define NO_NATIVE_BOOL

// ---------------------------------------------------------------------------
//  Each compiler might support L"" prefixed constants. There are places
//  where it is advantageous to use the L"" where it supported, to avoid
//  unnecessary transcoding.
//  If your compiler does not support it, don't define this.
// ---------------------------------------------------------------------------
// #define XML_LSTRSUPPORT

// ---------------------------------------------------------------------------
//  Indicate that we support C++ namespace
//  Do not define it if the compile cannot handle C++ namespace
// ---------------------------------------------------------------------------
// #define XERCES_HAS_CPP_NAMESPACE

// ---------------------------------------------------------------------------
//  Define our version of the XML character
// ---------------------------------------------------------------------------
#if defined(macintosh)
typedef unsigned short  XMLCh;
#elif defined(_WIN32) || defined(WIN32)
typedef wchar_t			XMLCh;
#endif

// ---------------------------------------------------------------------------
//  Define unsigned 16 and 32 bits integers
// ---------------------------------------------------------------------------
typedef unsigned short  XMLUInt16;
typedef unsigned int    XMLUInt32;

// ---------------------------------------------------------------------------
//  Define signed 32 bits integers
// ---------------------------------------------------------------------------
typedef int             XMLInt32;

// ---------------------------------------------------------------------------
//  XMLSize_t is the unsigned integral type.
// ---------------------------------------------------------------------------
#if defined(_SIZE_T) && defined(SIZE_MAX) && defined(_SSIZE_T) && defined(SSIZE_MAX)
    typedef size_t              XMLSize_t;
    #define XML_SIZE_MAX        SIZE_MAX
    typedef ssize_t             XMLSSize_t;
    #define XML_SSIZE_MAX       SSIZE_MAX
#else
    typedef unsigned long       XMLSize_t;
    #define XML_SIZE_MAX        ULONG_MAX
    typedef long                XMLSSize_t;
    #define XML_SSIZE_MAX       LONG_MAX
#endif

// ---------------------------------------------------------------------------
//  Force on the Xerces debug token if it was on in the build environment
// ---------------------------------------------------------------------------
#ifdef _DEBUG
#define XERCES_DEBUG
#endif

// ---------------------------------------------------------------------------
//  Provide some common string ops that are different/notavail for CodeWarrior.
//	(these routines are defined in CW 8 by extras.h, but there is no MachO
//	library for extras).
// ---------------------------------------------------------------------------
#if __MACH__
int stricmp(const char* const str1, const char* const  str2);
int strnicmp(const char* const str1, const char* const  str2, const unsigned int count);
#endif

#if defined(_WIN32) || defined(WIN32)
/* used in place of calling mbstowcs or wcstombs with a NULL destination */
int mbswcslen(const char * s, const unsigned int n);
int wcsmbslen(const wchar_t * pwcs, const unsigned int n);
#endif

// ---------------------------------------------------------------------------
//  The name of the DLL that is built by the Codewarrior version of the
//  system. We append a previously defined token which holds the DLL
//  versioning string. This is defined in XercesDefs.hpp which is what this
//  file is included into.
// ---------------------------------------------------------------------------
const char* const Xerces_DLLName = "xerces-c" Xerces_DLLVersionStr;

#endif //CODEWARRIORDEFS_HPP
<commit_msg>Restore support for CodeWarrior MachO framework build<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id$
 */

#if !defined(CODEWARRIORDEFS_HPP)
#define CODEWARRIORDEFS_HPP

// ---------------------------------------------------------------------------
//  Include some runtime files that will be needed product wide
// ---------------------------------------------------------------------------
//#include <sys/types.h>  	// for size_t and ssize_t
//#include <limits.h>		// for MAX of size_t and ssize_t
//#include <extras.h>

#if defined(_WIN32) || defined(WIN32)
#include <wchar.h>
#include <wctype.h>
#endif

// ---------------------------------------------------------------------------
//  A define in the build for each project is also used to control whether
//  the export keyword is from the project's viewpoint or the client's.
//  These defines provide the platform specific keywords that they need
//  to do this.
// ---------------------------------------------------------------------------
#if defined(XML_MACOS)
#define PLATFORM_EXPORT		__declspec(export)
#define PLATFORM_IMPORT		__declspec(import)
#elif defined(_WIN32) || defined(WIN32)
#define PLATFORM_EXPORT     __declspec(dllexport)
#define PLATFORM_IMPORT     __declspec(dllimport)
#endif

// ---------------------------------------------------------------------------
//  Indicate that we do not support native bools
//  If the compiler can handle boolean itself, do not define it
// ---------------------------------------------------------------------------
// #define NO_NATIVE_BOOL

// ---------------------------------------------------------------------------
//  Each compiler might support L"" prefixed constants. There are places
//  where it is advantageous to use the L"" where it supported, to avoid
//  unnecessary transcoding.
//  If your compiler does not support it, don't define this.
// ---------------------------------------------------------------------------
// #define XML_LSTRSUPPORT

// ---------------------------------------------------------------------------
//  Indicate that we support C++ namespace
//  Do not define it if the compile cannot handle C++ namespace
// ---------------------------------------------------------------------------
// #define XERCES_HAS_CPP_NAMESPACE

// ---------------------------------------------------------------------------
//  Define our version of the XML character
// ---------------------------------------------------------------------------
#if defined(XML_MACOS)
typedef unsigned short  XMLCh;
#elif defined(_WIN32) || defined(WIN32)
typedef wchar_t			XMLCh;
#endif

// ---------------------------------------------------------------------------
//  Define unsigned 16 and 32 bits integers
// ---------------------------------------------------------------------------
typedef unsigned short  XMLUInt16;
typedef unsigned int    XMLUInt32;

// ---------------------------------------------------------------------------
//  Define signed 32 bits integers
// ---------------------------------------------------------------------------
typedef int             XMLInt32;

// ---------------------------------------------------------------------------
//  XMLSize_t is the unsigned integral type.
// ---------------------------------------------------------------------------
#if defined(_SIZE_T) && defined(SIZE_MAX) && defined(_SSIZE_T) && defined(SSIZE_MAX)
    typedef size_t              XMLSize_t;
    #define XML_SIZE_MAX        SIZE_MAX
    typedef ssize_t             XMLSSize_t;
    #define XML_SSIZE_MAX       SSIZE_MAX
#else
    typedef unsigned long       XMLSize_t;
    #define XML_SIZE_MAX        ULONG_MAX
    typedef long                XMLSSize_t;
    #define XML_SSIZE_MAX       LONG_MAX
#endif

// ---------------------------------------------------------------------------
//  Force on the Xerces debug token if it was on in the build environment
// ---------------------------------------------------------------------------
#ifdef _DEBUG
#define XERCES_DEBUG
#endif

// ---------------------------------------------------------------------------
//  Provide some common string ops that are different/notavail for CodeWarrior.
//	(these routines are defined in CW 8 by extras.h, but there is no MachO
//	library for extras).
// ---------------------------------------------------------------------------
#if __MACH__
int stricmp(const char* const str1, const char* const  str2);
int strnicmp(const char* const str1, const char* const  str2, const unsigned int count);
#endif

#if defined(_WIN32) || defined(WIN32)
/* used in place of calling mbstowcs or wcstombs with a NULL destination */
int mbswcslen(const char * s, const unsigned int n);
int wcsmbslen(const wchar_t * pwcs, const unsigned int n);
#endif

// ---------------------------------------------------------------------------
//  The name of the DLL that is built by the Codewarrior version of the
//  system. We append a previously defined token which holds the DLL
//  versioning string. This is defined in XercesDefs.hpp which is what this
//  file is included into.
// ---------------------------------------------------------------------------
const char* const Xerces_DLLName = "xerces-c" Xerces_DLLVersionStr;

#endif //CODEWARRIORDEFS_HPP
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <swmodeltestbase.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/dispatchcommand.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/string.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdview.hxx>
#include <vcl/svapp.hxx>
#include <crsskip.hxx>
#include <drawdoc.hxx>
#include <ndtxt.hxx>
#include <wrtsh.hxx>

static const char* DATA_DIRECTORY = "/sw/qa/extras/tiledrendering/data/";

/// Testsuite for the SwXTextDocument methods implementing the vcl::ITiledRenderable interface.
class SwTiledRenderingTest : public SwModelTestBase
{
public:
    void testRegisterCallback();
    void testPostKeyEvent();
    void testPostMouseEvent();
    void testSetTextSelection();
    void testSetGraphicSelection();
    void testResetSelection();
    void testSearch();

    CPPUNIT_TEST_SUITE(SwTiledRenderingTest);
    CPPUNIT_TEST(testRegisterCallback);
    CPPUNIT_TEST(testPostKeyEvent);
    CPPUNIT_TEST(testPostMouseEvent);
    CPPUNIT_TEST(testSetTextSelection);
    CPPUNIT_TEST(testSetGraphicSelection);
    CPPUNIT_TEST(testResetSelection);
    CPPUNIT_TEST(testSearch);
    CPPUNIT_TEST_SUITE_END();

private:
    SwXTextDocument* createDoc(const char* pName);
    static void callback(int nType, const char* pPayload, void* pData);
    void callbackImpl(int nType, const char* pPayload);
    Rectangle m_aInvalidation;
};

SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName)
{
    load(DATA_DIRECTORY, pName);

    SwXTextDocument* pTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
    CPPUNIT_ASSERT(pTextDocument);
    pTextDocument->initializeForTiledRendering();
    return pTextDocument;
}

void SwTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
{
    static_cast<SwTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
}

void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
{
    switch (nType)
    {
    case LOK_CALLBACK_INVALIDATE_TILES:
    {
        if (m_aInvalidation.IsEmpty())
        {
            uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
            CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
            m_aInvalidation.setX(aSeq[0].toInt32());
            m_aInvalidation.setY(aSeq[1].toInt32());
            m_aInvalidation.setWidth(aSeq[2].toInt32());
            m_aInvalidation.setHeight(aSeq[3].toInt32());
        }
    }
    break;
    }
}

void SwTiledRenderingTest::testRegisterCallback()
{
#ifdef MACOSX
    // For some reason this particular test requires window system access on OS X.

    // Without window system access, we do get a number of "<<<WARNING>>>
    // AquaSalGraphics::CheckContext() FAILED!!!!" [sic] and " <Warning>: CGSConnectionByID: 0 is
    // not a valid connection ID" warnings while running the other tests, too, but they still
    // succeed.

    if (!vcl::IsWindowSystemAvailable())
        return;
#endif

    SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
    pXTextDocument->registerCallback(&SwTiledRenderingTest::callback, this);
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    // Insert a character at the beginning of the document.
    pWrtShell->Insert("x");

    // Check that the top left 256x256px tile would be invalidated.
    CPPUNIT_ASSERT(!m_aInvalidation.IsEmpty());
#if !defined(WNT) && !defined(MACOSX)
    Rectangle aTopLeft(0, 0, 256*15, 256*15); // 1 px = 15 twips, assuming 96 DPI.
    // FIXME - fails on Windows since about cbd48230bb3a90c4c485fa33123c6653234e02e9
    // [plus minus few commits maybe]
    // Also on OS X. But is tiled rendering even supposed to work on Windows and OS X?
    CPPUNIT_ASSERT(m_aInvalidation.IsOver(aTopLeft));
#endif
}

void SwTiledRenderingTest::testPostKeyEvent()
{
    SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
    SwShellCrsr* pShellCrsr = pWrtShell->getShellCrsr(false);
    // Did we manage to go after the first character?
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCrsr->GetPoint()->nContent.GetIndex());

    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
    // Did we manage to insert the character after the first one?
    CPPUNIT_ASSERT_EQUAL(OUString("Axaa bbb."), pShellCrsr->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
}

void SwTiledRenderingTest::testPostMouseEvent()
{
    SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
    SwShellCrsr* pShellCrsr = pWrtShell->getShellCrsr(false);
    // Did we manage to go after the first character?
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCrsr->GetPoint()->nContent.GetIndex());

    Point aStart = pShellCrsr->GetSttPos();
    aStart.setX(aStart.getX() - 1000);
    pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1);
    pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1);
    // The new cursor position must be before the first word.
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), pShellCrsr->GetPoint()->nContent.GetIndex());
}

void SwTiledRenderingTest::testSetTextSelection()
{
    SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    // Move the cursor into the second word.
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 5, /*bBasicCall=*/false);
    // Create a selection by on the word.
    pWrtShell->SelWrd();
    SwShellCrsr* pShellCrsr = pWrtShell->getShellCrsr(false);
    // Did we indeed manage to select the second word?
    CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCrsr->GetText());

    // Now use setTextSelection() to move the start of the selection 1000 twips left.
    Point aStart = pShellCrsr->GetSttPos();
    aStart.setX(aStart.getX() - 1000);
    pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_START, aStart.getX(), aStart.getY());
    // The new selection must include the first word, too -- but not the ending dot.
    CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb"), pShellCrsr->GetText());

    // Next: test that LOK_SETTEXTSELECTION_RESET + LOK_SETTEXTSELECTION_END can be used to create a selection.
    pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_RESET, aStart.getX(), aStart.getY());
    pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_END, aStart.getX() + 1000, aStart.getY());
    CPPUNIT_ASSERT_EQUAL(OUString("Aaa b"), pShellCrsr->GetText());
}

void SwTiledRenderingTest::testSetGraphicSelection()
{
    SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
    SdrObject* pObject = pPage->GetObj(0);
    pWrtShell->SelectObj(Point(), 0, pObject);
    // Make sure the rectangle has 8 handles: at each corner and at the center of each edge.
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(8), pObject->GetHdlCount());
    // Take the bottom center one.
    SdrHdl* pHdl = pObject->GetHdl(6);
    CPPUNIT_ASSERT_EQUAL(HDL_LOWER, pHdl->GetKind());
    Rectangle aShapeBefore = pObject->GetSnapRect();
    // Resize.
    pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, pHdl->GetPos().getX(), pHdl->GetPos().getY());
    pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, pHdl->GetPos().getX(), pHdl->GetPos().getY() + 1000);
    Rectangle aShapeAfter = pObject->GetSnapRect();
    // Check that a resize happened, but aspect ratio is not kept.
    CPPUNIT_ASSERT_EQUAL(aShapeBefore.getWidth(), aShapeAfter.getWidth());
#if !defined(MACOSX) // FIXME
    CPPUNIT_ASSERT_EQUAL(aShapeBefore.getHeight() + 1000, aShapeAfter.getHeight());
#endif
}

void SwTiledRenderingTest::testResetSelection()
{
    SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    // Select one character.
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
    SwShellCrsr* pShellCrsr = pWrtShell->getShellCrsr(false);
    // We have a text selection.
    CPPUNIT_ASSERT(pShellCrsr->HasMark());

    pXTextDocument->resetSelection();
    // We no longer have a text selection.
    CPPUNIT_ASSERT(!pShellCrsr->HasMark());

    SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
    SdrObject* pObject = pPage->GetObj(0);
    Point aPoint = pObject->GetSnapRect().Center();
    // Select the shape.
    pWrtShell->EnterSelFrmMode(&aPoint);
    // We have a graphic selection.
    CPPUNIT_ASSERT(pWrtShell->IsSelFrmMode());

    pXTextDocument->resetSelection();
    // We no longer have a graphic selection.
    CPPUNIT_ASSERT(!pWrtShell->IsSelFrmMode());
}

void lcl_search()
{
    uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
    {
        {"SearchItem.SearchString", uno::makeAny(OUString("shape"))},
        {"SearchItem.Backward", uno::makeAny(false)}
    }));
    comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
}

void SwTiledRenderingTest::testSearch()
{
#if !defined(WNT) && !defined(MACOSX)
    SwXTextDocument* pXTextDocument = createDoc("search.odt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    size_t nNode = pWrtShell->getShellCrsr(false)->Start()->nNode.GetNode().GetIndex();

    // First hit, in the second paragraph, before the shape.
    lcl_search();
    CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
    size_t nActual = pWrtShell->getShellCrsr(false)->Start()->nNode.GetNode().GetIndex();
    CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);

    // Next hit, in the shape.
    lcl_search();
    CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());

    // Next hit, in the shape, still.
    lcl_search();
    CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());

    // Last hit, in the last paragraph, after the shape.
    lcl_search();
    CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
    nActual = pWrtShell->getShellCrsr(false)->Start()->nNode.GetNode().GetIndex();
    CPPUNIT_ASSERT_EQUAL(nNode + 7, nActual);
#endif
}

CPPUNIT_TEST_SUITE_REGISTRATION(SwTiledRenderingTest);

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>loplugin:unreffun<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <swmodeltestbase.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/dispatchcommand.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/string.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdview.hxx>
#include <vcl/svapp.hxx>
#include <crsskip.hxx>
#include <drawdoc.hxx>
#include <ndtxt.hxx>
#include <wrtsh.hxx>

static const char* DATA_DIRECTORY = "/sw/qa/extras/tiledrendering/data/";

/// Testsuite for the SwXTextDocument methods implementing the vcl::ITiledRenderable interface.
class SwTiledRenderingTest : public SwModelTestBase
{
public:
    void testRegisterCallback();
    void testPostKeyEvent();
    void testPostMouseEvent();
    void testSetTextSelection();
    void testSetGraphicSelection();
    void testResetSelection();
    void testSearch();

    CPPUNIT_TEST_SUITE(SwTiledRenderingTest);
    CPPUNIT_TEST(testRegisterCallback);
    CPPUNIT_TEST(testPostKeyEvent);
    CPPUNIT_TEST(testPostMouseEvent);
    CPPUNIT_TEST(testSetTextSelection);
    CPPUNIT_TEST(testSetGraphicSelection);
    CPPUNIT_TEST(testResetSelection);
    CPPUNIT_TEST(testSearch);
    CPPUNIT_TEST_SUITE_END();

private:
    SwXTextDocument* createDoc(const char* pName);
    static void callback(int nType, const char* pPayload, void* pData);
    void callbackImpl(int nType, const char* pPayload);
    Rectangle m_aInvalidation;
};

SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName)
{
    load(DATA_DIRECTORY, pName);

    SwXTextDocument* pTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
    CPPUNIT_ASSERT(pTextDocument);
    pTextDocument->initializeForTiledRendering();
    return pTextDocument;
}

void SwTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
{
    static_cast<SwTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
}

void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
{
    switch (nType)
    {
    case LOK_CALLBACK_INVALIDATE_TILES:
    {
        if (m_aInvalidation.IsEmpty())
        {
            uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload));
            CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
            m_aInvalidation.setX(aSeq[0].toInt32());
            m_aInvalidation.setY(aSeq[1].toInt32());
            m_aInvalidation.setWidth(aSeq[2].toInt32());
            m_aInvalidation.setHeight(aSeq[3].toInt32());
        }
    }
    break;
    }
}

void SwTiledRenderingTest::testRegisterCallback()
{
#ifdef MACOSX
    // For some reason this particular test requires window system access on OS X.

    // Without window system access, we do get a number of "<<<WARNING>>>
    // AquaSalGraphics::CheckContext() FAILED!!!!" [sic] and " <Warning>: CGSConnectionByID: 0 is
    // not a valid connection ID" warnings while running the other tests, too, but they still
    // succeed.

    if (!vcl::IsWindowSystemAvailable())
        return;
#endif

    SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
    pXTextDocument->registerCallback(&SwTiledRenderingTest::callback, this);
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    // Insert a character at the beginning of the document.
    pWrtShell->Insert("x");

    // Check that the top left 256x256px tile would be invalidated.
    CPPUNIT_ASSERT(!m_aInvalidation.IsEmpty());
#if !defined(WNT) && !defined(MACOSX)
    Rectangle aTopLeft(0, 0, 256*15, 256*15); // 1 px = 15 twips, assuming 96 DPI.
    // FIXME - fails on Windows since about cbd48230bb3a90c4c485fa33123c6653234e02e9
    // [plus minus few commits maybe]
    // Also on OS X. But is tiled rendering even supposed to work on Windows and OS X?
    CPPUNIT_ASSERT(m_aInvalidation.IsOver(aTopLeft));
#endif
}

void SwTiledRenderingTest::testPostKeyEvent()
{
    SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
    SwShellCrsr* pShellCrsr = pWrtShell->getShellCrsr(false);
    // Did we manage to go after the first character?
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCrsr->GetPoint()->nContent.GetIndex());

    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
    // Did we manage to insert the character after the first one?
    CPPUNIT_ASSERT_EQUAL(OUString("Axaa bbb."), pShellCrsr->GetPoint()->nNode.GetNode().GetTextNode()->GetText());
}

void SwTiledRenderingTest::testPostMouseEvent()
{
    SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
    SwShellCrsr* pShellCrsr = pWrtShell->getShellCrsr(false);
    // Did we manage to go after the first character?
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), pShellCrsr->GetPoint()->nContent.GetIndex());

    Point aStart = pShellCrsr->GetSttPos();
    aStart.setX(aStart.getX() - 1000);
    pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, aStart.getX(), aStart.getY(), 1);
    pXTextDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, aStart.getX(), aStart.getY(), 1);
    // The new cursor position must be before the first word.
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), pShellCrsr->GetPoint()->nContent.GetIndex());
}

void SwTiledRenderingTest::testSetTextSelection()
{
    SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    // Move the cursor into the second word.
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 5, /*bBasicCall=*/false);
    // Create a selection by on the word.
    pWrtShell->SelWrd();
    SwShellCrsr* pShellCrsr = pWrtShell->getShellCrsr(false);
    // Did we indeed manage to select the second word?
    CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCrsr->GetText());

    // Now use setTextSelection() to move the start of the selection 1000 twips left.
    Point aStart = pShellCrsr->GetSttPos();
    aStart.setX(aStart.getX() - 1000);
    pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_START, aStart.getX(), aStart.getY());
    // The new selection must include the first word, too -- but not the ending dot.
    CPPUNIT_ASSERT_EQUAL(OUString("Aaa bbb"), pShellCrsr->GetText());

    // Next: test that LOK_SETTEXTSELECTION_RESET + LOK_SETTEXTSELECTION_END can be used to create a selection.
    pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_RESET, aStart.getX(), aStart.getY());
    pXTextDocument->setTextSelection(LOK_SETTEXTSELECTION_END, aStart.getX() + 1000, aStart.getY());
    CPPUNIT_ASSERT_EQUAL(OUString("Aaa b"), pShellCrsr->GetText());
}

void SwTiledRenderingTest::testSetGraphicSelection()
{
    SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
    SdrObject* pObject = pPage->GetObj(0);
    pWrtShell->SelectObj(Point(), 0, pObject);
    // Make sure the rectangle has 8 handles: at each corner and at the center of each edge.
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(8), pObject->GetHdlCount());
    // Take the bottom center one.
    SdrHdl* pHdl = pObject->GetHdl(6);
    CPPUNIT_ASSERT_EQUAL(HDL_LOWER, pHdl->GetKind());
    Rectangle aShapeBefore = pObject->GetSnapRect();
    // Resize.
    pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_START, pHdl->GetPos().getX(), pHdl->GetPos().getY());
    pXTextDocument->setGraphicSelection(LOK_SETGRAPHICSELECTION_END, pHdl->GetPos().getX(), pHdl->GetPos().getY() + 1000);
    Rectangle aShapeAfter = pObject->GetSnapRect();
    // Check that a resize happened, but aspect ratio is not kept.
    CPPUNIT_ASSERT_EQUAL(aShapeBefore.getWidth(), aShapeAfter.getWidth());
#if !defined(MACOSX) // FIXME
    CPPUNIT_ASSERT_EQUAL(aShapeBefore.getHeight() + 1000, aShapeAfter.getHeight());
#endif
}

void SwTiledRenderingTest::testResetSelection()
{
    SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    // Select one character.
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
    SwShellCrsr* pShellCrsr = pWrtShell->getShellCrsr(false);
    // We have a text selection.
    CPPUNIT_ASSERT(pShellCrsr->HasMark());

    pXTextDocument->resetSelection();
    // We no longer have a text selection.
    CPPUNIT_ASSERT(!pShellCrsr->HasMark());

    SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
    SdrObject* pObject = pPage->GetObj(0);
    Point aPoint = pObject->GetSnapRect().Center();
    // Select the shape.
    pWrtShell->EnterSelFrmMode(&aPoint);
    // We have a graphic selection.
    CPPUNIT_ASSERT(pWrtShell->IsSelFrmMode());

    pXTextDocument->resetSelection();
    // We no longer have a graphic selection.
    CPPUNIT_ASSERT(!pWrtShell->IsSelFrmMode());
}

#if !(defined WNT || defined MACOSX)
void lcl_search()
{
    uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
    {
        {"SearchItem.SearchString", uno::makeAny(OUString("shape"))},
        {"SearchItem.Backward", uno::makeAny(false)}
    }));
    comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
}
#endif

void SwTiledRenderingTest::testSearch()
{
#if !defined(WNT) && !defined(MACOSX)
    SwXTextDocument* pXTextDocument = createDoc("search.odt");
    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    size_t nNode = pWrtShell->getShellCrsr(false)->Start()->nNode.GetNode().GetIndex();

    // First hit, in the second paragraph, before the shape.
    lcl_search();
    CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
    size_t nActual = pWrtShell->getShellCrsr(false)->Start()->nNode.GetNode().GetIndex();
    CPPUNIT_ASSERT_EQUAL(nNode + 1, nActual);

    // Next hit, in the shape.
    lcl_search();
    CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());

    // Next hit, in the shape, still.
    lcl_search();
    CPPUNIT_ASSERT(pWrtShell->GetDrawView()->GetTextEditObject());

    // Last hit, in the last paragraph, after the shape.
    lcl_search();
    CPPUNIT_ASSERT(!pWrtShell->GetDrawView()->GetTextEditObject());
    nActual = pWrtShell->getShellCrsr(false)->Start()->nNode.GetNode().GetIndex();
    CPPUNIT_ASSERT_EQUAL(nNode + 7, nActual);
#endif
}

CPPUNIT_TEST_SUITE_REGISTRATION(SwTiledRenderingTest);

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#include "toy/math/NumberFormat.hpp"

namespace toy{
namespace math{

struct NumberFormatPrivate
{
	uint8_t   isGood           : 1;
	uint8_t   isInteger        : 1;
	uint8_t   isNegative       : 1;
	uint8_t   isDecimal        : 1;
	uint8_t   isHexadecimal    : 1;
	uint8_t   : 0;
};

}}

using namespace toy;
using namespace math;

auto NumberFormat::operator = ( const NumberFormat & m ) -> const NumberFormat&
{
	*_this = *(m._this);
	return *this;
}

NumberFormat::NumberFormat(const NumberFormat & m):_this(new NumberFormatPrivate)
{
	*_this = *(m._this);
}

static bool IsNumberChar(char c)
{
	if ( c<'0' || c>'9' )
	{
		return false;
	}

	return true;
}

static bool IsHexNumberChar(char c)
{
	if ( ( c<'0' || c>'9' ) && ( c<'a' || c>'f' ) )
	{
		return false;
	}

	return true;
}

NumberFormat::NumberFormat(const std::string &num):_this(new NumberFormatPrivate)
{
	*((uint8_t*)_this) = uint8_t(0);

	int size = num.size();

	if ( size==0 )
	{
		return;
	}

	int index = 0;

	if ( num[index]=='-' )
	{
		if ( size==1 ) return;
		index++;
		_this->isNegative = 1;
	}

	bool maybeIsHexadecimal = false;
	bool dotFound = false;

	bool (*func)(char) = IsNumberChar;

	if ( num[index]=='0' )
	{
		if ( size<index+2 ) return;

		if ( num[index+1]=='x' )
		{
			if ( size<index+3 ) return;

			func = IsHexNumberChar;
			index += 2;
			maybeIsHexadecimal = true;

			if ( num[index]=='0' )
			{
				if ( size<index+2 )
				{
					return;   // 0x0
				}
				else
				{
					if ( num[index+1]=='.' )
					{
						if ( size<index+3 ) return; // 0x0.

						dotFound = true;
						index += 2;
					}
					else
					{
						return;   // 0x0123
					}
				}
			}
			else if ( num[index]=='.' )
			{
				return;
			}
		}
		else if ( num[index+1]=='.' )
		{
			if ( size<index+3 ) return;  // 0.

			dotFound = true;
			index += 2;
		}
		else
		{
			return;
		}
	}
	else if ( num[index]=='.' )
	{
		return;
	}

	for (; size>index ;index++)
	{
		if ( num[index]=='.' )
		{
			if ( dotFound )
			{
				return;
			}
			else
			{
				dotFound = true;
			}
		}
		else if ( ! func(num[index]) ) return;
	}

	if ( num[size-1]=='.' )
	{
		return;   // xxx.
	}

	_this->isGood = 1;

	if ( maybeIsHexadecimal )
	{
		_this->isHexadecimal = 1;
	}
	else
	{
		_this->isDecimal = 1;
	}

	if ( dotFound==false )
	{
		_this->isInteger = 1;
	}
}

NumberFormat::~NumberFormat()
{
	delete _this;
}

bool NumberFormat::isGood()
{
	return _this->isGood;
}

bool NumberFormat::isInteger()
{
	return _this->isInteger;
}

bool NumberFormat::isNegative()
{
	return _this->isNegative;
}

bool NumberFormat::isDecimal()
{
	return _this->isDecimal;
}

bool NumberFormat::isHexadecimal()
{
	return _this->isHexadecimal;
}
<commit_msg>Fix a bug when toy::math::NumberFormat got string "0".<commit_after>#include "toy/math/NumberFormat.hpp"

namespace toy{
namespace math{

struct NumberFormatPrivate
{
	uint8_t   isGood           : 1;
	uint8_t   isInteger        : 1;
	uint8_t   isNegative       : 1;
	uint8_t   isDecimal        : 1;
	uint8_t   isHexadecimal    : 1;
	uint8_t   : 0;
};

}}

using namespace toy;
using namespace math;

auto NumberFormat::operator = ( const NumberFormat & m ) -> const NumberFormat&
{
	*_this = *(m._this);
	return *this;
}

NumberFormat::NumberFormat(const NumberFormat & m):_this(new NumberFormatPrivate)
{
	*_this = *(m._this);
}

static bool IsNumberChar(char c)
{
	if ( c<'0' || c>'9' )
	{
		return false;
	}

	return true;
}

static bool IsHexNumberChar(char c)
{
	if ( ( c<'0' || c>'9' ) && ( c<'a' || c>'f' ) )
	{
		return false;
	}

	return true;
}

NumberFormat::NumberFormat(const std::string &num):_this(new NumberFormatPrivate)
{
	*((uint8_t*)_this) = uint8_t(0);

	int size = num.size();

	if ( size==0 )
	{
		return;
	}

	int index = 0;

	if ( num[index]=='-' )
	{
		if ( size==1 ) return;
		index++;
		_this->isNegative = 1;
	}

	bool maybeIsHexadecimal = false;
	bool dotFound = false;

	bool (*func)(char) = IsNumberChar;

	if ( num[index]=='0' )
	{
		if ( size<index+2 )
		{
			if ( _this->isNegative==0 )
			{
				_this->isGood = 1;
				_this->isDecimal = 1;
				_this->isInteger = 1;
			}
			return;   // 0
		}

		if ( num[index+1]=='x' )
		{
			if ( size<index+3 ) return;

			func = IsHexNumberChar;
			index += 2;
			maybeIsHexadecimal = true;

			if ( num[index]=='0' )
			{
				if ( size<index+2 )
				{
					if ( _this->isNegative==0 )
					{
						_this->isGood = 1;
						_this->isHexadecimal = 1;
						_this->isInteger = 1;
					}
					return;   // 0x0
				}
				else
				{
					if ( num[index+1]=='.' )
					{
						if ( size<index+3 ) return; // 0x0.

						dotFound = true;
						index += 2;
					}
					else
					{
						return;   // 0x0123
					}
				}
			}
			else if ( num[index]=='.' )
			{
				return;
			}
		}
		else if ( num[index+1]=='.' )
		{
			if ( size<index+3 ) return;  // 0.

			dotFound = true;
			index += 2;
		}
		else
		{
			return;
		}
	}
	else if ( num[index]=='.' )
	{
		return;
	}

	for (; size>index ;index++)
	{
		if ( num[index]=='.' )
		{
			if ( dotFound )
			{
				return;
			}
			else
			{
				dotFound = true;
			}
		}
		else if ( ! func(num[index]) ) return;
	}

	if ( num[size-1]=='.' )
	{
		return;   // xxx.
	}

	_this->isGood = 1;

	if ( maybeIsHexadecimal )
	{
		_this->isHexadecimal = 1;
	}
	else
	{
		_this->isDecimal = 1;
	}

	if ( dotFound==false )
	{
		_this->isInteger = 1;
	}
}

NumberFormat::~NumberFormat()
{
	delete _this;
}

bool NumberFormat::isGood()
{
	return _this->isGood;
}

bool NumberFormat::isInteger()
{
	return _this->isInteger;
}

bool NumberFormat::isNegative()
{
	return _this->isNegative;
}

bool NumberFormat::isDecimal()
{
	return _this->isDecimal;
}

bool NumberFormat::isHexadecimal()
{
	return _this->isHexadecimal;
}
<|endoftext|>


<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// XLA-specific Tile Op.

#include <vector>
#include "absl/algorithm/container.h"
#include "absl/types/span.h"
#include "tensorflow/compiler/tf2xla/lib/broadcast.h"
#include "tensorflow/compiler/tf2xla/type_util.h"
#include "tensorflow/compiler/tf2xla/xla_helpers.h"
#include "tensorflow/compiler/tf2xla/xla_op_kernel.h"
#include "tensorflow/compiler/tf2xla/xla_op_registry.h"
#include "tensorflow/compiler/xla/client/xla_builder.h"
#include "tensorflow/core/framework/numeric_op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/framework/type_index.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/platform/macros.h"

namespace tensorflow {
namespace {

// --------------------------------------------------------------------------
class TileOp : public XlaOpKernel {
 public:
  explicit TileOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape("input");
    const TensorShape multiples_shape = ctx->InputShape("multiples");

    OP_REQUIRES(
        ctx, TensorShapeUtils::IsVector(multiples_shape),
        errors::InvalidArgument("Expected multiples to be 1-D, but got shape ",
                                multiples_shape.DebugString()));
    OP_REQUIRES(ctx, input_shape.dims() == multiples_shape.num_elements(),
                errors::InvalidArgument(
                    "Expected multiples argument to be a vector of length ",
                    input_shape.dims(), " but got length ",
                    multiples_shape.dim_size(0)));
    const int input_dims = input_shape.dims();
    auto input = ctx->Input(0);
    // If input is a scalar then multiples has 0 elements and this is
    // a NoOp.
    if (input_dims == 0) {
      ctx->SetOutput(0, input);
      return;
    }

    std::vector<int64> multiples;
    OP_REQUIRES_OK(ctx, ctx->ConstantInputAsIntVector("multiples", &multiples));
    std::vector<int64> output_dims(input_shape.dims());
    for (int64 i = 0; i < input_shape.dims(); ++i) {
      OP_REQUIRES(ctx, multiples[i] >= 0,
                  errors::InvalidArgument("Expected multiples[", i,
                                          "] >= 0, but got ", output_dims[i]));
      output_dims[i] = input_shape.dim_size(i) * multiples[i];
    }

    // If all multiples are 1, than the input is the same as the output.
    if (absl::c_all_of(multiples,
                       [](int64 multiple) { return multiple == 1; })) {
      ctx->SetOutput(0, input);
      return;
    }

    auto result = BroadcastTo(ctx->Input("input"), output_dims);
    OP_REQUIRES_OK(ctx, result.status());
    ctx->SetOutput(0, result.ValueOrDie());
  }

 private:
  TF_DISALLOW_COPY_AND_ASSIGN(TileOp);
};

REGISTER_XLA_OP(Name("Tile").CompileTimeConstantInput("multiples"), TileOp);

}  // namespace
}  // namespace tensorflow
<commit_msg>[TF2XLA]Tile Op: Support dynamic multipliers.<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// XLA-specific Tile Op.

#include <vector>
#include "absl/algorithm/container.h"
#include "absl/types/span.h"
#include "tensorflow/compiler/tf2xla/lib/broadcast.h"
#include "tensorflow/compiler/tf2xla/type_util.h"
#include "tensorflow/compiler/tf2xla/xla_helpers.h"
#include "tensorflow/compiler/tf2xla/xla_op_kernel.h"
#include "tensorflow/compiler/tf2xla/xla_op_registry.h"
#include "tensorflow/compiler/xla/client/xla_builder.h"
#include "tensorflow/core/framework/numeric_op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/framework/type_index.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/platform/macros.h"

namespace tensorflow {
namespace {

// --------------------------------------------------------------------------
class TileOp : public XlaOpKernel {
 public:
  explicit TileOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape("input");
    const TensorShape multiples_shape = ctx->InputShape("multiples");

    OP_REQUIRES(
        ctx, TensorShapeUtils::IsVector(multiples_shape),
        errors::InvalidArgument("Expected multiples to be 1-D, but got shape ",
                                multiples_shape.DebugString()));
    OP_REQUIRES(ctx, input_shape.dims() == multiples_shape.num_elements(),
                errors::InvalidArgument(
                    "Expected multiples argument to be a vector of length ",
                    input_shape.dims(), " but got length ",
                    multiples_shape.dim_size(0)));
    const int input_dims = input_shape.dims();
    auto input = ctx->Input(0);
    // If input is a scalar then multiples has 0 elements and this is
    // a NoOp.
    if (input_dims == 0) {
      ctx->SetOutput(0, input);
      return;
    }

    std::vector<int64> multiples;
    OP_REQUIRES_OK(ctx, ctx->ConstantInputAsIntVector("multiples", &multiples));
    std::vector<int64> output_dims(input_shape.dims());
    for (int64 i = 0; i < input_shape.dims(); ++i) {
      OP_REQUIRES(ctx, multiples[i] >= 0,
                  errors::InvalidArgument("Expected multiples[", i,
                                          "] >= 0, but got ", output_dims[i]));
      output_dims[i] = input_shape.dim_size(i) * multiples[i];
    }

    // If all multiples are 1, than the input is the same as the output.
    if (absl::c_all_of(multiples,
                       [](int64 multiple) { return multiple == 1; })) {
      ctx->SetOutput(0, input);
      return;
    }
    std::vector<int64> dynamic_multiples;
    ctx->set_dynamic_dimension_is_minus_one(true);
    // The multiplier can be a dynamic value.
    OP_REQUIRES_OK(
        ctx, ctx->ConstantInputAsIntVector("multiples", &dynamic_multiples));

    auto result_or = BroadcastTo(ctx->Input("input"), output_dims);

    OP_REQUIRES_OK(ctx, result_or.status());
    auto result = result_or.ValueOrDie();
    for (int64 i = 0; i < dynamic_multiples.size(); ++i) {
      // If a dimension is dynamic, call set-dimension-size on the output.
      if (dynamic_multiples[i] == -1) {
        auto dynamic_dim_size =
            xla::Slice(ctx->Input("multiples"), {i}, {i + 1}, {1});
        dynamic_dim_size = xla::Reshape(dynamic_dim_size, {});
        dynamic_dim_size = xla::ConvertElementType(dynamic_dim_size, xla::S32);
        result = xla::SetDimensionSize(result, dynamic_dim_size, i);
      }
    }

    ctx->SetOutput(0, result);
  }

 private:
  TF_DISALLOW_COPY_AND_ASSIGN(TileOp);
};

REGISTER_XLA_OP(Name("Tile").CompileTimeConstantInput("multiples"), TileOp);

}  // namespace
}  // namespace tensorflow
<|endoftext|>


<commit_before>/* Copyright 2015 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/common_runtime/gpu/gpu_util.h"

#include "tensorflow/stream_executor/stream.h"
#include "tensorflow/stream_executor/stream_executor.h"
#include "tensorflow/core/common_runtime/device.h"
#include "tensorflow/core/common_runtime/gpu/dma_helper.h"
#include "tensorflow/core/common_runtime/gpu/gpu_event_mgr.h"
#include "tensorflow/core/common_runtime/gpu/process_state.h"
#include "tensorflow/core/common_runtime/gpu_device_context.h"
#include "tensorflow/core/framework/tensor_reference.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/refcount.h"
#include "tensorflow/core/lib/gtl/array_slice.h"
#include "tensorflow/core/lib/gtl/stl_util.h"
#include "tensorflow/core/lib/hash/hash.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/tensor_coding.h"
#include "tensorflow/core/platform/tracing.h"
#include "tensorflow/core/public/tensor.h"
#include "tensorflow/core/util/util.h"

#include "tensorflow/core/platform/stream_executor_util.h"

// If this need to be runtime configurable, consider adding options to
// ConfigProto.
const tensorflow::int64 FLAGS_brain_gpu_util_debug_string_maxlen = 128;
extern bool FLAGS_brain_gpu_record_mem_types;

using perftools::gputools::DeviceMemoryBase;
using perftools::gputools::DeviceMemory;
using perftools::gputools::Stream;

namespace tensorflow {

namespace gpu = ::perftools::gputools;

/*static*/
void GPUUtil::SetProtoFromGPU(const Tensor& tensor, Device* dev,
                              const DeviceContext* device_context,
                              TensorProto* proto, bool is_dead,
                              StatusCallback done) {
  VLOG(1) << "SetProtoFromGPU device_context " << device_context;
  // Tensor values need to be copied from GPU to CPU ram so that
  // we can build the protobuf response for a RecvTensor RPC.
  // "device context" identifies the stream where the _Send op executed.
  CHECK(device_context);
  gpu::Stream* stream =
      static_cast<const GPUDeviceContext*>(device_context)->stream();

  if (!DMAHelper::CanUseDMA(&tensor)) {
    done(errors::Internal(strings::StrCat(
        "GPU copy from non-DMA ", DataTypeString(tensor.dtype()), "tensor")));
    return;
  }
  proto->set_dtype(tensor.dtype());
  tensor.shape().AsProto(proto->mutable_tensor_shape());
  // Prepare a Cord with the right data buf size, and DMA the
  // data over from the GPU buffer.  Note that 0-size tensors
  // do not have a backing buffer.
  const size_t num_bytes = is_dead ? 0 : tensor.TotalBytes();
  if (num_bytes > 0) {
    port::Tracing::ScopedAnnotation annotation("SetProtoFromGPU");
    Allocator* alloc = ProcessState::singleton()->GetCUDAHostAllocator(0);
    char* mb = alloc->Allocate<char>(num_bytes);
    const char* src_ptr =
        reinterpret_cast<const char*>(DMAHelper::base(&tensor));
    DeviceMemoryBase gpu_src_ptr(const_cast<char*>(src_ptr), num_bytes);
    stream->ThenMemcpy(mb, gpu_src_ptr, num_bytes);
    // Use of tensor may outlive stack scope, so keep a ref.
    TensorReference tensor_ref(tensor);
    dev->tensorflow_gpu_device_info()->event_mgr->ThenExecute(
        stream, [stream, done, proto, mb, num_bytes, alloc, tensor_ref]() {
          if (!stream->ok()) {
            done(errors::Internal("SetProtoFromGPU: GPU Memcpy failed"));
            // TODO(pbar) We currently have no way to recover the
            // worker from a GPU stream in the error state.  Until
            // there is a way to reset the CUDA driver, it is
            // preferable to crash the process and restart.  Tracked
            // under b/23717097
            LOG(FATAL) << "SetProtoFromGPU: GPU Memcpy failed";
            return;
          }
          tensor_ref.Unref();
          port::CopyFromArray(proto->mutable_tensor_content(), mb, num_bytes);
          alloc->Deallocate<char>(mb, num_bytes);
          done(Status::OK());
        });
  } else {
    done(Status::OK());
  }
}

typedef ProcessState::MemDesc PMD;

/*static*/
void GPUUtil::CopyViaDMA(const string& edge_name,
                         DeviceContext* send_dev_context,
                         DeviceContext* recv_dev_context, Device* src,
                         Device* dst, AllocatorAttributes src_alloc_attr,
                         AllocatorAttributes dst_alloc_attr,
                         const Tensor* input, Tensor* output,
                         StatusCallback done) {
  port::Tracing::ScopedAnnotation annotation(edge_name);
  VLOG(1) << "CopyViaDMA " << edge_name;
  size_t total_bytes = input->TotalBytes();
  // Note that 0-size tensors have no backing buffer.
  if (total_bytes > 0) {
    const void* src_ptr = DMAHelper::base(input);
    void* dst_ptr = DMAHelper::base(output);
    VLOG(2) << "src_ptr " << src_ptr << " dst_ptr " << dst_ptr;
    if (FLAGS_brain_gpu_record_mem_types) {
      ProcessState::MemDesc smd = ProcessState::singleton()->PtrType(src_ptr);
      ProcessState::MemDesc dmd = ProcessState::singleton()->PtrType(dst_ptr);
      VLOG(0) << "Src " << smd.DebugString() << " Dst " << dmd.DebugString();
      if (smd.loc == PMD::CPU && dmd.loc == PMD::GPU && (!smd.gpu_registered)) {
        LOG(WARNING) << "CPU -> GPU no reg for " << edge_name;
      }
      if (dmd.loc == PMD::CPU && smd.loc == PMD::GPU && (!dmd.gpu_registered)) {
        LOG(WARNING) << "GPU -> CPU no reg for " << edge_name;
      }
    }

    auto src_device_type = src->attributes().device_type();
    auto dst_device_type = dst->attributes().device_type();

    bool non_cpu_src = (!src_alloc_attr.on_host() &&
                        src_device_type != DeviceType(DEVICE_CPU).type());
    bool non_cpu_dst = (!dst_alloc_attr.on_host() &&
                        dst_device_type != DeviceType(DEVICE_CPU).type());
    if (non_cpu_src) {
      gpu::Stream* stream = send_dev_context->stream();
      if (stream == nullptr) {
        done(errors::Internal("Failed to find device stream"));
        return;
      }
      auto* src_dev_info = src->tensorflow_gpu_device_info();
      CHECK(src_dev_info);

      if (non_cpu_dst) {
        // Device to device copy
        DeviceMemoryBase gpu_dst_ptr(dst_ptr, total_bytes);
        stream->ThenMemcpy(
            &gpu_dst_ptr,
            DeviceMemoryBase{const_cast<void*>(src_ptr), total_bytes},
            total_bytes);
        if (dst_device_type == DeviceType(DEVICE_GPU).type()) {
          // Use of input may outlive stack scope, so keep a ref.
          TensorReference input_ref(*input);
          src_dev_info->event_mgr->ThenExecute(
              stream, [done, stream, input_ref]() {
                input_ref.Unref();
                if (!stream->ok()) {
                  done(errors::Internal("GPU->GPU Memcpy failed"));
                } else {
                  done(Status::OK());
                }
              });
        }
        send_dev_context->MaintainLifetimeOnStream(input, stream);
      } else {
        // Device to host copy.
        return send_dev_context->CopyDeviceTensorToCPU(input, edge_name, src,
                                                       output, done);
      }
    } else if (non_cpu_dst) {
      // Host to Device copy.
      // Note that this is already an async copy.
      recv_dev_context->CopyCPUTensorToDevice(input, dst, output, done);
    } else {
      memcpy(dst_ptr, src_ptr, total_bytes);
      done(Status::OK());
    }
  } else {
    // buffer is empty
    done(Status::OK());
  }
}

void GPUUtil::CopyGPUTensorToCPU(Device* gpu_device,
                                 const DeviceContext* device_context,
                                 const Tensor* gpu_tensor, Tensor* cpu_tensor,
                                 StatusCallback done) {
  VLOG(1) << "CopyGPUTensorToCPU";
  size_t total_bytes = gpu_tensor->TotalBytes();
  // Note that 0-size tensors have no backing buffer.
  if (total_bytes > 0) {
    const void* src_ptr = DMAHelper::base(gpu_tensor);
    void* dst_ptr = DMAHelper::base(cpu_tensor);
    CHECK(dst_ptr);
    auto* stream = gpu_device->tensorflow_gpu_device_info()->stream;
    if (device_context) {
      stream = static_cast<const GPUDeviceContext*>(device_context)->stream();
    }
    stream->ThenMemcpy(
        dst_ptr, DeviceMemoryBase{const_cast<void*>(src_ptr), total_bytes},
        total_bytes);
    stream->BlockHostUntilDone();
    if (!stream->ok()) {
      done(errors::Internal("CopyGPUTensorToCPU: GPU->CPU Memcpy failed"));
      return;
    }
  }

  done(Status::OK());
}

/*  static */
void GPUUtil::CopyCPUTensorToGPU(const Tensor* cpu_tensor,
                                 const DeviceContext* device_context,
                                 Device* gpu_device, Tensor* gpu_tensor,
                                 StatusCallback done) {
  VLOG(1) << "CopyCPUTensorToGPU";
  CHECK(DeviceType(gpu_device->attributes().device_type()) ==
        DeviceType(DEVICE_GPU));

  auto* dev_info = gpu_device->tensorflow_gpu_device_info();
  if (!dev_info) {
    done(errors::Internal("Failed to find dest device GPUDeviceInfo"));
    return;
  }
  if (cpu_tensor->TotalBytes() != gpu_tensor->TotalBytes()) {
    done(errors::Internal(
        strings::StrCat("Can't copy ", cpu_tensor->TotalBytes(),
                        " bytes of a tensor into another with ",
                        gpu_tensor->TotalBytes(), " bytes buffer.")));
    return;
  }
  const int64 total_bytes = cpu_tensor->TotalBytes();
  // Note that 0-size tensors have no backing buffer.
  if (total_bytes > 0) {
    const void* src_ptr = DMAHelper::base(cpu_tensor);
    void* dst_ptr = DMAHelper::base(gpu_tensor);
    DeviceMemoryBase gpu_dst_ptr(dst_ptr, total_bytes);

    CHECK(device_context);
    auto* stream =
        static_cast<const GPUDeviceContext*>(device_context)->stream();
    stream->ThenMemcpy(&gpu_dst_ptr, src_ptr, total_bytes);
    auto* dev_info = gpu_device->tensorflow_gpu_device_info();
    // Use of cpu_tensor may outlive stack scope, so keep a ref.
    TensorReference input_ref(*cpu_tensor);
    dev_info->event_mgr->ThenExecute(stream, [stream, done, input_ref]() {
      input_ref.Unref();
      if (!stream->ok()) {
        done(errors::Internal("CopyCPUTensorToGPU: GPU Memcpy failed"));
      } else {
        done(Status::OK());
      }
    });
  } else {
    // empty tensor case
    done(Status::OK());
  }
}

Status GPUUtil::Sync(Device* gpu_device) {
  VLOG(1) << "GPUUtil::Sync";
  auto* dev_info = gpu_device->tensorflow_gpu_device_info();
  if (!dev_info) {
    return errors::Internal("Failed to find dest device GPUDeviceInfo");
  }
  dev_info->stream->BlockHostUntilDone();
  if (!dev_info->stream->ok()) {
    LOG(FATAL) << "GPU sync failed";
  }
  return Status::OK();
}

Status GPUUtil::SyncAll(Device* gpu_device) {
  VLOG(1) << "GPUUtil::SyncAll";
  auto* dev_info = gpu_device->tensorflow_gpu_device_info();
  if (!dev_info) {
    return errors::Internal("Failed to find dest device GPUDeviceInfo");
  }
  if (!dev_info->stream->parent()->SynchronizeAllActivity() ||
      !dev_info->stream->ok()) {
    LOG(FATAL) << "GPU sync failed";
  }
  return Status::OK();
}

string GPUUtil::MemoryDebugString(const Device* device, Tensor* tensor) {
  string ret;
  CHECK(tensor);
  const int64 num_bytes = std::min<int64>(
      FLAGS_brain_gpu_util_debug_string_maxlen, tensor->TotalBytes());
  void* ptr = (num_bytes > 0) ? DMAHelper::base(tensor) : nullptr;
  strings::Appendf(&ret, "%p:", ptr);
  if (num_bytes > 0) {
    auto* dev_info = device->tensorflow_gpu_device_info();
    if (!dev_info) {
      strings::StrAppend(
          &ret, PrintMemory(reinterpret_cast<const char*>(ptr), num_bytes));
    } else {
      string buf;
      buf.resize(num_bytes);
      DeviceMemoryBase gpu_ptr(ptr, num_bytes);
      Status s = dev_info->stream->parent()->SynchronousMemcpyD2H(
          gpu_ptr, num_bytes, gtl::string_as_array(&buf));
      strings::StrAppend(&ret,
                         PrintMemory(gtl::string_as_array(&buf), num_bytes));
    }
  }
  return ret;
}

// TODO(pbar) Checksum is called from places without a valid device context.
uint64 GPUUtil::Checksum(Device* gpu_device,
                         const DeviceContext* device_context,
                         const Tensor& tensor) {
  Tensor copy(tensor.dtype(), tensor.shape());
  Status s;
  Notification n;
  CopyGPUTensorToCPU(gpu_device, device_context, &tensor, &copy,
                     [&s, &n](Status status) {
                       s.Update(status);
                       n.Notify();
                     });
  n.WaitForNotification();
  CHECK(s.ok()) << s;
  return Checksum(copy);
}

uint64 GPUUtil::Checksum(const Tensor& tensor) {
  const float* fptr = reinterpret_cast<const float*>(DMAHelper::base(&tensor));
  size_t num_bytes = tensor.TotalBytes();
  size_t num_floats = num_bytes / sizeof(float);
  for (size_t i = 0; i < num_floats; ++i) {
    CHECK(!std::isnan(fptr[i])) << " i " << i;
  }
  // TODO(tucker): consider using crc32c instead.
  return Hash64(reinterpret_cast<const char*>(DMAHelper::base(&tensor)),
                tensor.TotalBytes(), 0);
}

}  // namespace tensorflow
<commit_msg>Move GPU specific code out of generic DMA codepaths.<commit_after>/* Copyright 2015 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/common_runtime/gpu/gpu_util.h"

#include "tensorflow/stream_executor/stream.h"
#include "tensorflow/stream_executor/stream_executor.h"
#include "tensorflow/core/common_runtime/device.h"
#include "tensorflow/core/common_runtime/gpu/dma_helper.h"
#include "tensorflow/core/common_runtime/gpu/gpu_event_mgr.h"
#include "tensorflow/core/common_runtime/gpu/process_state.h"
#include "tensorflow/core/common_runtime/gpu_device_context.h"
#include "tensorflow/core/framework/tensor_reference.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/refcount.h"
#include "tensorflow/core/lib/gtl/array_slice.h"
#include "tensorflow/core/lib/gtl/stl_util.h"
#include "tensorflow/core/lib/hash/hash.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/tensor_coding.h"
#include "tensorflow/core/platform/tracing.h"
#include "tensorflow/core/public/tensor.h"
#include "tensorflow/core/util/util.h"

#include "tensorflow/core/platform/stream_executor_util.h"

// If this need to be runtime configurable, consider adding options to
// ConfigProto.
const tensorflow::int64 FLAGS_brain_gpu_util_debug_string_maxlen = 128;
extern bool FLAGS_brain_gpu_record_mem_types;

using perftools::gputools::DeviceMemoryBase;
using perftools::gputools::DeviceMemory;
using perftools::gputools::Stream;

namespace tensorflow {

namespace gpu = ::perftools::gputools;

/*static*/
void GPUUtil::SetProtoFromGPU(const Tensor& tensor, Device* dev,
                              const DeviceContext* device_context,
                              TensorProto* proto, bool is_dead,
                              StatusCallback done) {
  VLOG(1) << "SetProtoFromGPU device_context " << device_context;
  // Tensor values need to be copied from GPU to CPU ram so that
  // we can build the protobuf response for a RecvTensor RPC.
  // "device context" identifies the stream where the _Send op executed.
  CHECK(device_context);
  gpu::Stream* stream =
      static_cast<const GPUDeviceContext*>(device_context)->stream();

  if (!DMAHelper::CanUseDMA(&tensor)) {
    done(errors::Internal(strings::StrCat(
        "GPU copy from non-DMA ", DataTypeString(tensor.dtype()), "tensor")));
    return;
  }
  proto->set_dtype(tensor.dtype());
  tensor.shape().AsProto(proto->mutable_tensor_shape());
  // Prepare a Cord with the right data buf size, and DMA the
  // data over from the GPU buffer.  Note that 0-size tensors
  // do not have a backing buffer.
  const size_t num_bytes = is_dead ? 0 : tensor.TotalBytes();
  if (num_bytes > 0) {
    port::Tracing::ScopedAnnotation annotation("SetProtoFromGPU");
    Allocator* alloc = ProcessState::singleton()->GetCUDAHostAllocator(0);
    char* mb = alloc->Allocate<char>(num_bytes);
    const char* src_ptr =
        reinterpret_cast<const char*>(DMAHelper::base(&tensor));
    DeviceMemoryBase gpu_src_ptr(const_cast<char*>(src_ptr), num_bytes);
    stream->ThenMemcpy(mb, gpu_src_ptr, num_bytes);
    // Use of tensor may outlive stack scope, so keep a ref.
    TensorReference tensor_ref(tensor);
    dev->tensorflow_gpu_device_info()->event_mgr->ThenExecute(
        stream, [stream, done, proto, mb, num_bytes, alloc, tensor_ref]() {
          if (!stream->ok()) {
            done(errors::Internal("SetProtoFromGPU: GPU Memcpy failed"));
            // TODO(pbar) We currently have no way to recover the
            // worker from a GPU stream in the error state.  Until
            // there is a way to reset the CUDA driver, it is
            // preferable to crash the process and restart.  Tracked
            // under b/23717097
            LOG(FATAL) << "SetProtoFromGPU: GPU Memcpy failed";
            return;
          }
          tensor_ref.Unref();
          port::CopyFromArray(proto->mutable_tensor_content(), mb, num_bytes);
          alloc->Deallocate<char>(mb, num_bytes);
          done(Status::OK());
        });
  } else {
    done(Status::OK());
  }
}

typedef ProcessState::MemDesc PMD;

/*static*/
void GPUUtil::CopyViaDMA(const string& edge_name,
                         DeviceContext* send_dev_context,
                         DeviceContext* recv_dev_context, Device* src,
                         Device* dst, AllocatorAttributes src_alloc_attr,
                         AllocatorAttributes dst_alloc_attr,
                         const Tensor* input, Tensor* output,
                         StatusCallback done) {
  port::Tracing::ScopedAnnotation annotation(edge_name);
  VLOG(1) << "CopyViaDMA " << edge_name;
  size_t total_bytes = input->TotalBytes();
  // Note that 0-size tensors have no backing buffer.
  if (total_bytes > 0) {
    const void* src_ptr = DMAHelper::base(input);
    void* dst_ptr = DMAHelper::base(output);
    VLOG(2) << "src_ptr " << src_ptr << " dst_ptr " << dst_ptr;
    if (FLAGS_brain_gpu_record_mem_types) {
      ProcessState::MemDesc smd = ProcessState::singleton()->PtrType(src_ptr);
      ProcessState::MemDesc dmd = ProcessState::singleton()->PtrType(dst_ptr);
      VLOG(0) << "Src " << smd.DebugString() << " Dst " << dmd.DebugString();
      if (smd.loc == PMD::CPU && dmd.loc == PMD::GPU && (!smd.gpu_registered)) {
        LOG(WARNING) << "CPU -> GPU no reg for " << edge_name;
      }
      if (dmd.loc == PMD::CPU && smd.loc == PMD::GPU && (!dmd.gpu_registered)) {
        LOG(WARNING) << "GPU -> CPU no reg for " << edge_name;
      }
    }

    auto src_device_type = src->attributes().device_type();
    auto dst_device_type = dst->attributes().device_type();

    bool non_cpu_src = (!src_alloc_attr.on_host() &&
                        src_device_type != DeviceType(DEVICE_CPU).type());
    bool non_cpu_dst = (!dst_alloc_attr.on_host() &&
                        dst_device_type != DeviceType(DEVICE_CPU).type());
    if (non_cpu_src) {
      if (non_cpu_dst) {
        // Device to device copy
        gpu::Stream* stream = send_dev_context->stream();
        if (stream == nullptr) {
          done(errors::Internal("Failed to find device stream"));
          return;
        }
        auto* src_dev_info = src->tensorflow_gpu_device_info();
        CHECK(src_dev_info);

        DeviceMemoryBase gpu_dst_ptr(dst_ptr, total_bytes);
        stream->ThenMemcpy(
            &gpu_dst_ptr,
            DeviceMemoryBase{const_cast<void*>(src_ptr), total_bytes},
            total_bytes);
        if (dst_device_type == DeviceType(DEVICE_GPU).type()) {
          // Use of input may outlive stack scope, so keep a ref.
          TensorReference input_ref(*input);
          src_dev_info->event_mgr->ThenExecute(
              stream, [done, stream, input_ref]() {
                input_ref.Unref();
                if (!stream->ok()) {
                  done(errors::Internal("GPU->GPU Memcpy failed"));
                } else {
                  done(Status::OK());
                }
              });
        }
        send_dev_context->MaintainLifetimeOnStream(input, stream);
      } else {
        // Device to host copy.
        return send_dev_context->CopyDeviceTensorToCPU(input, edge_name, src,
                                                       output, done);
      }
    } else if (non_cpu_dst) {
      // Host to Device copy.
      // Note that this is already an async copy.
      recv_dev_context->CopyCPUTensorToDevice(input, dst, output, done);
    } else {
      memcpy(dst_ptr, src_ptr, total_bytes);
      done(Status::OK());
    }
  } else {
    // buffer is empty
    done(Status::OK());
  }
}

void GPUUtil::CopyGPUTensorToCPU(Device* gpu_device,
                                 const DeviceContext* device_context,
                                 const Tensor* gpu_tensor, Tensor* cpu_tensor,
                                 StatusCallback done) {
  VLOG(1) << "CopyGPUTensorToCPU";
  size_t total_bytes = gpu_tensor->TotalBytes();
  // Note that 0-size tensors have no backing buffer.
  if (total_bytes > 0) {
    const void* src_ptr = DMAHelper::base(gpu_tensor);
    void* dst_ptr = DMAHelper::base(cpu_tensor);
    CHECK(dst_ptr);
    auto* stream = gpu_device->tensorflow_gpu_device_info()->stream;
    if (device_context) {
      stream = static_cast<const GPUDeviceContext*>(device_context)->stream();
    }
    stream->ThenMemcpy(
        dst_ptr, DeviceMemoryBase{const_cast<void*>(src_ptr), total_bytes},
        total_bytes);
    stream->BlockHostUntilDone();
    if (!stream->ok()) {
      done(errors::Internal("CopyGPUTensorToCPU: GPU->CPU Memcpy failed"));
      return;
    }
  }

  done(Status::OK());
}

/*  static */
void GPUUtil::CopyCPUTensorToGPU(const Tensor* cpu_tensor,
                                 const DeviceContext* device_context,
                                 Device* gpu_device, Tensor* gpu_tensor,
                                 StatusCallback done) {
  VLOG(1) << "CopyCPUTensorToGPU";
  CHECK(DeviceType(gpu_device->attributes().device_type()) ==
        DeviceType(DEVICE_GPU));

  auto* dev_info = gpu_device->tensorflow_gpu_device_info();
  if (!dev_info) {
    done(errors::Internal("Failed to find dest device GPUDeviceInfo"));
    return;
  }
  if (cpu_tensor->TotalBytes() != gpu_tensor->TotalBytes()) {
    done(errors::Internal(
        strings::StrCat("Can't copy ", cpu_tensor->TotalBytes(),
                        " bytes of a tensor into another with ",
                        gpu_tensor->TotalBytes(), " bytes buffer.")));
    return;
  }
  const int64 total_bytes = cpu_tensor->TotalBytes();
  // Note that 0-size tensors have no backing buffer.
  if (total_bytes > 0) {
    const void* src_ptr = DMAHelper::base(cpu_tensor);
    void* dst_ptr = DMAHelper::base(gpu_tensor);
    DeviceMemoryBase gpu_dst_ptr(dst_ptr, total_bytes);

    CHECK(device_context);
    auto* stream =
        static_cast<const GPUDeviceContext*>(device_context)->stream();
    stream->ThenMemcpy(&gpu_dst_ptr, src_ptr, total_bytes);
    auto* dev_info = gpu_device->tensorflow_gpu_device_info();
    // Use of cpu_tensor may outlive stack scope, so keep a ref.
    TensorReference input_ref(*cpu_tensor);
    dev_info->event_mgr->ThenExecute(stream, [stream, done, input_ref]() {
      input_ref.Unref();
      if (!stream->ok()) {
        done(errors::Internal("CopyCPUTensorToGPU: GPU Memcpy failed"));
      } else {
        done(Status::OK());
      }
    });
  } else {
    // empty tensor case
    done(Status::OK());
  }
}

Status GPUUtil::Sync(Device* gpu_device) {
  VLOG(1) << "GPUUtil::Sync";
  auto* dev_info = gpu_device->tensorflow_gpu_device_info();
  if (!dev_info) {
    return errors::Internal("Failed to find dest device GPUDeviceInfo");
  }
  dev_info->stream->BlockHostUntilDone();
  if (!dev_info->stream->ok()) {
    LOG(FATAL) << "GPU sync failed";
  }
  return Status::OK();
}

Status GPUUtil::SyncAll(Device* gpu_device) {
  VLOG(1) << "GPUUtil::SyncAll";
  auto* dev_info = gpu_device->tensorflow_gpu_device_info();
  if (!dev_info) {
    return errors::Internal("Failed to find dest device GPUDeviceInfo");
  }
  if (!dev_info->stream->parent()->SynchronizeAllActivity() ||
      !dev_info->stream->ok()) {
    LOG(FATAL) << "GPU sync failed";
  }
  return Status::OK();
}

string GPUUtil::MemoryDebugString(const Device* device, Tensor* tensor) {
  string ret;
  CHECK(tensor);
  const int64 num_bytes = std::min<int64>(
      FLAGS_brain_gpu_util_debug_string_maxlen, tensor->TotalBytes());
  void* ptr = (num_bytes > 0) ? DMAHelper::base(tensor) : nullptr;
  strings::Appendf(&ret, "%p:", ptr);
  if (num_bytes > 0) {
    auto* dev_info = device->tensorflow_gpu_device_info();
    if (!dev_info) {
      strings::StrAppend(
          &ret, PrintMemory(reinterpret_cast<const char*>(ptr), num_bytes));
    } else {
      string buf;
      buf.resize(num_bytes);
      DeviceMemoryBase gpu_ptr(ptr, num_bytes);
      Status s = dev_info->stream->parent()->SynchronousMemcpyD2H(
          gpu_ptr, num_bytes, gtl::string_as_array(&buf));
      strings::StrAppend(&ret,
                         PrintMemory(gtl::string_as_array(&buf), num_bytes));
    }
  }
  return ret;
}

// TODO(pbar) Checksum is called from places without a valid device context.
uint64 GPUUtil::Checksum(Device* gpu_device,
                         const DeviceContext* device_context,
                         const Tensor& tensor) {
  Tensor copy(tensor.dtype(), tensor.shape());
  Status s;
  Notification n;
  CopyGPUTensorToCPU(gpu_device, device_context, &tensor, &copy,
                     [&s, &n](Status status) {
                       s.Update(status);
                       n.Notify();
                     });
  n.WaitForNotification();
  CHECK(s.ok()) << s;
  return Checksum(copy);
}

uint64 GPUUtil::Checksum(const Tensor& tensor) {
  const float* fptr = reinterpret_cast<const float*>(DMAHelper::base(&tensor));
  size_t num_bytes = tensor.TotalBytes();
  size_t num_floats = num_bytes / sizeof(float);
  for (size_t i = 0; i < num_floats; ++i) {
    CHECK(!std::isnan(fptr[i])) << " i " << i;
  }
  // TODO(tucker): consider using crc32c instead.
  return Hash64(reinterpret_cast<const char*>(DMAHelper::base(&tensor)),
                tensor.TotalBytes(), 0);
}

}  // namespace tensorflow
<|endoftext|>