editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx |    8 -
 editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx |    2 
 include/oox/core/fasttokenhandler.hxx                |   10 -
 include/oox/helper/attributelist.hxx                 |   15 +-
 include/oox/token/tokenmap.hxx                       |   75 ++----------
 include/sax/fastattribs.hxx                          |    3 
 oox/qa/token/tokenmap-test.cxx                       |   12 --
 oox/source/core/fasttokenhandler.cxx                 |   20 +--
 oox/source/crypto/AgileEngine.cxx                    |    2 
 oox/source/drawingml/customshapeproperties.cxx       |    2 
 oox/source/drawingml/table/tablecell.cxx             |    2 
 oox/source/helper/attributelist.cxx                  |  110 ++++++++-----------
 oox/source/mathml/importutils.cxx                    |    2 
 oox/source/token/tokenmap.cxx                        |   86 ++++++++------
 sax/qa/cppunit/parser.cxx                            |    2 
 sax/qa/cppunit/xmlimport.cxx                         |    9 -
 sax/source/tools/fastattribs.cxx                     |   16 +-
 sw/source/core/inc/SwXMLBlockImport.hxx              |    4 
 sw/source/core/swg/SwXMLBlockImport.cxx              |   16 +-
 unoxml/qa/unit/domtest.cxx                           |    2 
 writerfilter/source/ooxml/OOXMLStreamImpl.cxx        |    2 
 xmloff/inc/fasttokenhandler.hxx                      |   59 +---------
 xmloff/qa/unit/tokenmap-test.cxx                     |   18 ---
 xmloff/source/core/fasttokenhandler.cxx              |   81 ++++++-------
 xmloff/source/core/xmlimp.cxx                        |    6 -
 25 files changed, 223 insertions(+), 341 deletions(-)

New commits:
commit 804644a5666407fb4096fbcea97eb2ece97f7bfa
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Thu Sep 12 14:34:26 2024 +0500
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Thu Feb 20 09:38:49 2025 +0100

    Optimize TokenMap and AttributeList in oox and xo
    
    Shaves lots of string allocations, and uses optimized code paths
    
    Change-Id: I8e33e2aecdc7e0d2f2c31b774daa36304b3973ac
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173179
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    (cherry picked from commit 3a37d8320c0b8a7bced8e67f7ed2581d4013e38b)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/181923
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx 
b/editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx
index 4bdadcdcde61..ae1744615073 100644
--- a/editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx
+++ b/editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx
@@ -35,7 +35,7 @@ SvXMLAutoCorrectTokenHandler::~SvXMLAutoCorrectTokenHandler()
 
 sal_Int32 SAL_CALL SvXMLAutoCorrectTokenHandler::getTokenFromUTF8( const 
Sequence< sal_Int8 >& Identifier )
 {
-    return getTokenDirect( reinterpret_cast< const char* >( 
Identifier.getConstArray() ), Identifier.getLength() );
+    return getTokenDirect( std::string_view(reinterpret_cast< const char* >( 
Identifier.getConstArray() ), Identifier.getLength()) );
 }
 
 Sequence< sal_Int8 > SAL_CALL SvXMLAutoCorrectTokenHandler::getUTF8Identifier( 
sal_Int32 )
@@ -43,11 +43,9 @@ Sequence< sal_Int8 > SAL_CALL 
SvXMLAutoCorrectTokenHandler::getUTF8Identifier( s
     return Sequence< sal_Int8 >();
 }
 
-sal_Int32 SvXMLAutoCorrectTokenHandler::getTokenDirect( const char *pTag, 
sal_Int32 nLength ) const
+sal_Int32 SvXMLAutoCorrectTokenHandler::getTokenDirect(std::string_view token) 
const
 {
-    if( !nLength )
-        nLength = strlen( pTag );
-    const struct xmltoken* pToken = Perfect_Hash::in_word_set( pTag, nLength );
+    const struct xmltoken* pToken = Perfect_Hash::in_word_set(token.data(), 
token.size());
     return pToken ? pToken->nToken : XML_TOKEN_INVALID;
 }
 
diff --git a/editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx 
b/editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx
index df913dbe6b01..0c86077fd4b5 100644
--- a/editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx
+++ b/editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx
@@ -39,7 +39,7 @@ public:
     virtual css::uno::Sequence< sal_Int8 > SAL_CALL getUTF8Identifier( 
sal_Int32 Token ) override;
 
     // Much faster direct C++ shortcut to the method that matters
-    virtual sal_Int32 getTokenDirect( const char *pToken, sal_Int32 nLength ) 
const override;
+    virtual sal_Int32 getTokenDirect(std::string_view token) const override;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/oox/core/fasttokenhandler.hxx 
b/include/oox/core/fasttokenhandler.hxx
index 396474b988a1..d47c81dd0130 100644
--- a/include/oox/core/fasttokenhandler.hxx
+++ b/include/oox/core/fasttokenhandler.hxx
@@ -29,8 +29,6 @@
 #include <sal/types.h>
 #include <sax/fastattribs.hxx>
 
-namespace oox { class TokenMap; }
-
 namespace oox::core {
 
 
@@ -41,8 +39,7 @@ class OOX_DLLPUBLIC FastTokenHandler final :
     public cppu::ImplInheritanceHelper< sax_fastparser::FastTokenHandlerBase, 
css::lang::XServiceInfo >
 {
 public:
-    explicit            FastTokenHandler();
-    virtual             ~FastTokenHandler() override;
+    explicit            FastTokenHandler() = default;
 
     // XServiceInfo
     virtual OUString SAL_CALL getImplementationName() override;
@@ -54,10 +51,7 @@ public:
     virtual sal_Int32 SAL_CALL getTokenFromUTF8( const css::uno::Sequence< 
sal_Int8 >& Identifier ) override;
 
     // Much faster direct C++ shortcut to the method that matters
-    virtual sal_Int32 getTokenDirect( const char *pToken, sal_Int32 nLength ) 
const override;
-
-private:
-    const TokenMap&     mrTokenMap;     ///< Reference to global token map 
singleton.
+    virtual sal_Int32 getTokenDirect(std::string_view token) const override;
 };
 
 
diff --git a/include/oox/helper/attributelist.hxx 
b/include/oox/helper/attributelist.hxx
index 25f2ebe4f823..7e3c773a5781 100644
--- a/include/oox/helper/attributelist.hxx
+++ b/include/oox/helper/attributelist.hxx
@@ -29,6 +29,7 @@
 #include <com/sun/star/util/DateTime.hpp>
 #include <oox/dllapi.h>
 #include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
 #include <sal/types.h>
 #include <oox/drawingml/color.hxx>
 
@@ -83,10 +84,10 @@ class OOX_DLLPUBLIC AttributeList
 public:
     explicit            AttributeList(
                             const css::uno::Reference< 
css::xml::sax::XFastAttributeList >& rxAttribs );
+    ~AttributeList();
 
     /** Returns the wrapped com.sun.star.xml.sax.XFastAttributeList object. */
-    const css::uno::Reference< css::xml::sax::XFastAttributeList >&
-                        getFastAttributeList() const { return mxAttribs; }
+    css::uno::Reference<css::xml::sax::XFastAttributeList> 
getFastAttributeList() const;
 
     /** Returns true, if the specified attribute is present. */
     bool                hasAttribute( sal_Int32 nAttrToken ) const;
@@ -180,10 +181,12 @@ public:
     std::vector<sal_Int32> getTokenList(sal_Int32 nAttrToken) const;
 
 private:
-    css::uno::Reference< css::xml::sax::XFastAttributeList >
-                        mxAttribs;
-    mutable sax_fastparser::FastAttributeList *mpAttribList;
-    sax_fastparser::FastAttributeList *getAttribList() const;
+    AttributeList(const AttributeList&) = delete;
+    AttributeList(AttributeList&&) = delete;
+    void operator=(const AttributeList&) = delete;
+    void operator=(AttributeList&&) = delete;
+
+    rtl::Reference<sax_fastparser::FastAttributeList> mxAttribs;
 };
 
 
diff --git a/include/oox/token/tokenmap.hxx b/include/oox/token/tokenmap.hxx
index 4358822c360f..3c0c50d5a013 100644
--- a/include/oox/token/tokenmap.hxx
+++ b/include/oox/token/tokenmap.hxx
@@ -35,70 +35,29 @@
 namespace oox {
 
 
-class TokenMap
+namespace TokenMap
 {
-public:
-    explicit            TokenMap();
-                        ~TokenMap();
+/** Returns the token identifier for a UTF8 string passed in pToken */
+sal_Int32 getTokenFromUtf8(std::string_view token);
 
-    /** Returns the token identifier for the passed Unicode token name. */
-    static sal_Int32    getTokenFromUnicode( std::u16string_view rUnicodeName 
);
-
-    /** Returns the UTF8 name of the passed token identifier as byte sequence. 
*/
-    css::uno::Sequence< sal_Int8 > const &
-                        getUtf8TokenName( sal_Int32 nToken ) const
-    {
-        SAL_WARN_IF(nToken < 0 || nToken >= XML_TOKEN_COUNT, "oox", "Wrong 
nToken parameter");
-        if (0 <= nToken && nToken < XML_TOKEN_COUNT)
-            return maTokenNames[ nToken ];
-        return EMPTY_BYTE_SEQ;
-    }
-
-    /** Returns the token identifier for the passed UTF8 token name. */
-    sal_Int32           getTokenFromUtf8(
-                            const css::uno::Sequence< sal_Int8 >& rUtf8Name ) 
const
-    {
-        return getTokenFromUTF8( reinterpret_cast< const char * >(
-                                rUtf8Name.getConstArray() ),
-                             rUtf8Name.getLength() );
-    }
-
-    /** Returns the token identifier for a UTF8 string passed in pToken */
-    sal_Int32 getTokenFromUTF8( const char *pToken, sal_Int32 nLength ) const
-    {
-        // 50% of OOXML tokens are primarily 1 lower-case character, a-z
-        if( nLength == 1)
-        {
-            char c = pToken[0];
-            if (c >= 'a' && c <= 'z')
-                return mnAlphaTokens[ c - 'a' ];
-        }
-        return getTokenPerfectHash( pToken, nLength );
-    }
-
-    /** Returns the name of the passed token identifier as OUString. */
-    OUString getUnicodeTokenName(sal_Int32 nToken) const
-    {
-        SAL_WARN_IF(nToken < 0 || nToken >= XML_TOKEN_COUNT, "oox", "Wrong 
nToken parameter");
-        OUString const ret((0 <= nToken && nToken < XML_TOKEN_COUNT)
-            ? rtl::OUString(reinterpret_cast<const 
char*>(maTokenNames[nToken].getConstArray()),
-                            maTokenNames[nToken].getLength(), 
RTL_TEXTENCODING_UTF8)
-            : OUString());
-        return ret;
-    }
+/** Returns the token identifier for the passed Unicode token name. */
+inline sal_Int32 getTokenFromUnicode(std::u16string_view rUnicodeName)
+{
+    return getTokenFromUtf8(OUStringToOString(rUnicodeName, 
RTL_TEXTENCODING_UTF8));
+}
 
-private:
-    static sal_Int32 getTokenPerfectHash( const char *pToken, sal_Int32 
nLength );
-    static const css::uno::Sequence< sal_Int8 > EMPTY_BYTE_SEQ;
+/** Returns the UTF8 name of the passed token identifier as byte sequence. */
+css::uno::Sequence<sal_Int8> const& getUtf8TokenName(sal_Int32 nToken);
 
-    std::vector< css::uno::Sequence< sal_Int8 > >
-                        maTokenNames;
-    sal_Int32           mnAlphaTokens[26];
+/** Returns the name of the passed token identifier as OUString. */
+inline OUString getUnicodeTokenName(sal_Int32 nToken)
+{
+    auto name = getUtf8TokenName(nToken);
+    return OUString(reinterpret_cast<const char*>(name.getConstArray()), 
name.getLength(),
+                    RTL_TEXTENCODING_UTF8);
+}
 };
 
-
-TokenMap& StaticTokenMap();
-
 } // namespace oox
 
 #endif
diff --git a/include/sax/fastattribs.hxx b/include/sax/fastattribs.hxx
index fb6f47eb7f9d..72eb9cf890d3 100644
--- a/include/sax/fastattribs.hxx
+++ b/include/sax/fastattribs.hxx
@@ -56,7 +56,7 @@ class SAX_DLLPUBLIC FastTokenHandlerBase :
 {
  public:
     virtual ~FastTokenHandlerBase();
-    virtual sal_Int32 getTokenDirect( const char *pToken, sal_Int32 nLength ) 
const = 0;
+    virtual sal_Int32 getTokenDirect(std::string_view token) const = 0;
 
     /**
      * Client method to attempt the use of this interface if possible.
@@ -122,6 +122,7 @@ public:
     {
         return OStringToOUString(getAsViewByIndex(nTokenIndex), 
RTL_TEXTENCODING_UTF8);
     }
+    sal_Int32 getValueTokenByIndex(sal_Int32 nTokenIndex) const;
 
     // XFastAttributeList
     virtual sal_Bool SAL_CALL hasAttribute( ::sal_Int32 Token ) override;
diff --git a/oox/qa/token/tokenmap-test.cxx b/oox/qa/token/tokenmap-test.cxx
index e30b6aef806b..b8b29911c30f 100644
--- a/oox/qa/token/tokenmap-test.cxx
+++ b/oox/qa/token/tokenmap-test.cxx
@@ -30,9 +30,6 @@ public:
     CPPUNIT_TEST(test_roundTrip);
     CPPUNIT_TEST(test_roundTripUnicode);
     CPPUNIT_TEST_SUITE_END();
-
-private:
-    TokenMap tokenMap;
 };
 
 void TokenmapTest::test_roundTrip()
@@ -40,10 +37,9 @@ void TokenmapTest::test_roundTrip()
     for ( sal_Int32 nToken = 0; nToken < XML_TOKEN_COUNT; ++nToken )
     {
         // check that the getIdentifier <-> getToken roundtrip works
-        Sequence< sal_Int8 > rUtf8Name = tokenMap.getUtf8TokenName(nToken);
-        sal_Int32 ret = tokenMap.getTokenFromUTF8(
-            reinterpret_cast< const char * >(rUtf8Name.getConstArray()),
-            rUtf8Name.getLength() );
+        Sequence< sal_Int8 > rUtf8Name = TokenMap::getUtf8TokenName(nToken);
+        sal_Int32 ret = TokenMap::getTokenFromUtf8(std::string_view(
+            reinterpret_cast<const char*>(rUtf8Name.getConstArray()), 
rUtf8Name.getLength()));
         CPPUNIT_ASSERT_EQUAL(ret, nToken);
     }
 }
@@ -53,7 +49,7 @@ void TokenmapTest::test_roundTripUnicode()
     for (sal_Int32 nToken = 0; nToken < XML_TOKEN_COUNT; ++nToken)
     {
         // check that the getIdentifier <-> getToken roundtrip works for 
OUString
-        OUString sName = tokenMap.getUnicodeTokenName(nToken);
+        OUString sName = TokenMap::getUnicodeTokenName(nToken);
         sal_Int32 ret = oox::TokenMap::getTokenFromUnicode(sName);
         CPPUNIT_ASSERT_EQUAL(ret, nToken);
     }
diff --git a/oox/source/core/fasttokenhandler.cxx 
b/oox/source/core/fasttokenhandler.cxx
index 398772df058f..327073de07fe 100644
--- a/oox/source/core/fasttokenhandler.cxx
+++ b/oox/source/core/fasttokenhandler.cxx
@@ -29,15 +29,6 @@ namespace oox::core {
 
 using namespace ::com::sun::star::uno;
 
-FastTokenHandler::FastTokenHandler() :
-    mrTokenMap( StaticTokenMap() )
-{
-}
-
-FastTokenHandler::~FastTokenHandler()
-{
-}
-
 // XServiceInfo
 OUString SAL_CALL FastTokenHandler::getImplementationName()
 {
@@ -57,17 +48,18 @@ Sequence< OUString > SAL_CALL 
FastTokenHandler::getSupportedServiceNames()
 
 Sequence< sal_Int8 > FastTokenHandler::getUTF8Identifier( sal_Int32 nToken )
 {
-    return mrTokenMap.getUtf8TokenName( nToken );
+    return TokenMap::getUtf8TokenName(nToken);
 }
 
 sal_Int32 FastTokenHandler::getTokenFromUTF8( const Sequence< sal_Int8 >& 
rIdentifier )
 {
-    return mrTokenMap.getTokenFromUtf8( rIdentifier );
+    return TokenMap::getTokenFromUtf8(std::string_view(
+        reinterpret_cast<const char*>(rIdentifier.getConstArray()), 
rIdentifier.getLength()));
 }
 
-sal_Int32 FastTokenHandler::getTokenDirect( const char *pToken, sal_Int32 
nLength ) const
+sal_Int32 FastTokenHandler::getTokenDirect(std::string_view token) const
 {
-    return mrTokenMap.getTokenFromUTF8( pToken, nLength );
+    return TokenMap::getTokenFromUtf8(token);
 }
 
 } // namespace oox::core
@@ -76,7 +68,7 @@ extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
 com_sun_star_comp_oox_core_FastTokenHandler_get_implementation(
     uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
 {
-    return cppu::acquire(new oox::core::FastTokenHandler());
+    return cppu::acquire(new oox::core::FastTokenHandler);
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/crypto/AgileEngine.cxx 
b/oox/source/crypto/AgileEngine.cxx
index f52376c15e45..13928abf9c51 100644
--- a/oox/source/crypto/AgileEngine.cxx
+++ b/oox/source/crypto/AgileEngine.cxx
@@ -65,7 +65,7 @@ public:
         return Sequence<sal_Int8>();
     }
 
-    virtual sal_Int32 getTokenDirect( const char * /* pToken */, sal_Int32 /* 
nLength */ ) const override
+    virtual sal_Int32 getTokenDirect(std::string_view /* token */) const 
override
     {
         return -1;
     }
diff --git a/oox/source/drawingml/customshapeproperties.cxx 
b/oox/source/drawingml/customshapeproperties.cxx
index 321bc9bf537a..d596ddf15547 100644
--- a/oox/source/drawingml/customshapeproperties.cxx
+++ b/oox/source/drawingml/customshapeproperties.cxx
@@ -56,7 +56,7 @@ CustomShapeProperties::CustomShapeProperties()
 
 OUString CustomShapeProperties::getShapePresetTypeName() const
 {
-    return StaticTokenMap().getUnicodeTokenName(mnShapePresetType);
+    return TokenMap::getUnicodeTokenName(mnShapePresetType);
 }
 
 sal_Int32 CustomShapeProperties::SetCustomShapeGuideValue( std::vector< 
CustomShapeGuide >& rGuideList, const CustomShapeGuide& rGuide )
diff --git a/oox/source/drawingml/table/tablecell.cxx 
b/oox/source/drawingml/table/tablecell.cxx
index 78ec4f61feeb..412d8ee55ce1 100644
--- a/oox/source/drawingml/table/tablecell.cxx
+++ b/oox/source/drawingml/table/tablecell.cxx
@@ -605,7 +605,7 @@ void TableCell::pushToXCell( const 
::oox::core::XmlFilterBase& rFilterBase, cons
     else if ( getVertToken() != XML_horz && getVertToken() != XML_eaVert )
     {
         // put the vert value in the grab bag for roundtrip
-        const OUString 
aTokenName(StaticTokenMap().getUnicodeTokenName(getVertToken()));
+        const OUString 
aTokenName(TokenMap::getUnicodeTokenName(getVertToken()));
         Sequence<PropertyValue> aGrabBag;
         xPropSet->getPropertyValue("CellInteropGrabBag") >>= aGrabBag;
         PropertyValue aPropertyValue = 
comphelper::makePropertyValue("mso-tcPr-vert-value", aTokenName);
diff --git a/oox/source/helper/attributelist.cxx 
b/oox/source/helper/attributelist.cxx
index 7a973975f3d2..dc5c54c14d3d 100644
--- a/oox/source/helper/attributelist.cxx
+++ b/oox/source/helper/attributelist.cxx
@@ -62,6 +62,19 @@ sal_Unicode lclGetXChar( const sal_Unicode*& rpcStr, const 
sal_Unicode* pcEnd )
     return *rpcStr++;
 }
 
+template<typename C> sal_uInt32 decodeUnsigned_impl(std::basic_string_view<C> 
rValue)
+{
+    return getLimitedValue<sal_uInt32, sal_Int64>(o3tl::toInt64(rValue), 0, 
SAL_MAX_UINT32);
+}
+
+template <typename C> sal_Int32 
decodeIntegerHex_impl(std::basic_string_view<C> rValue)
+{
+    // It looks like all Office Open XML attributes containing hexadecimal
+    // values are based on xsd:hexBinary and so use an unsigned representation:
+    return static_cast<sal_Int32>(o3tl::toUInt32(rValue, 16));
+        //TODO: Change this function to return sal_uInt32 and get rid of the
+        // cast, but that will have a ripple effect
+}
 } // namespace
 
 #define STRING_TO_TOKEN(color) if (sColorName == u"" #color) return XML_##color
@@ -98,12 +111,13 @@ OUString AttributeConversion::decodeXString( const 
OUString& rValue )
     // string shorter than one encoded character - no need to decode
     if( rValue.getLength() < XSTRING_ENCCHAR_LEN )
         return rValue;
-    if (rValue.indexOf(u"_x") == -1)
+    sal_Int32 index = rValue.indexOf(u"_x");
+    if (index == -1)
         return rValue;
 
-    OUStringBuffer aBuffer;
-    const sal_Unicode* pcStr = rValue.getStr();
-    const sal_Unicode* pcEnd = pcStr + rValue.getLength();
+    OUStringBuffer aBuffer(rValue.subView(0, index));
+    const sal_Unicode* pcStr = rValue.getStr() + index;
+    const sal_Unicode* pcEnd = rValue.getStr() + rValue.getLength();
     while( pcStr < pcEnd )
         aBuffer.append( lclGetXChar( pcStr, pcEnd ) );
     return 
comphelper::string::sanitizeStringSurrogates(aBuffer.makeStringAndClear());
@@ -116,7 +130,7 @@ sal_Int32 AttributeConversion::decodeInteger( 
std::u16string_view rValue )
 
 sal_uInt32 AttributeConversion::decodeUnsigned( std::u16string_view rValue )
 {
-    return getLimitedValue< sal_uInt32, sal_Int64 >( o3tl::toInt64(rValue), 0, 
SAL_MAX_UINT32 );
+    return decodeUnsigned_impl(rValue);
 }
 
 sal_Int64 AttributeConversion::decodeHyper( std::u16string_view rValue )
@@ -126,27 +140,19 @@ sal_Int64 AttributeConversion::decodeHyper( 
std::u16string_view rValue )
 
 sal_Int32 AttributeConversion::decodeIntegerHex( std::u16string_view rValue )
 {
-    // It looks like all Office Open XML attributes containing hexadecimal
-    // values are based on xsd:hexBinary and so use an unsigned representation:
-    return static_cast< sal_Int32 >(o3tl::toUInt32(rValue, 16));
-        //TODO: Change this function to return sal_uInt32 and get rid of the
-        // cast, but that will have a ripple effect
+    return decodeIntegerHex_impl(rValue);
 }
 
 AttributeList::AttributeList( const Reference< XFastAttributeList >& rxAttribs 
) :
-    mxAttribs( rxAttribs ),
-    mpAttribList( nullptr )
+    mxAttribs(&sax_fastparser::castToFastAttributeList(rxAttribs))
 {
-    OSL_ENSURE( mxAttribs.is(), "AttributeList::AttributeList - missing 
attribute list interface" );
 }
 
-sax_fastparser::FastAttributeList *AttributeList::getAttribList() const
+AttributeList::~AttributeList() = default;
+
+css::uno::Reference<css::xml::sax::XFastAttributeList> 
AttributeList::getFastAttributeList() const
 {
-    if( mpAttribList == nullptr )
-    {
-        mpAttribList = &sax_fastparser::castToFastAttributeList( mxAttribs );
-    }
-    return mpAttribList;
+    return mxAttribs;
 }
 
 bool AttributeList::hasAttribute( sal_Int32 nAttrToken ) const
@@ -173,46 +179,43 @@ std::optional< sal_Int32 > AttributeList::getToken( 
sal_Int32 nAttrToken ) const
 std::optional< OUString > AttributeList::getString( sal_Int32 nAttrToken ) 
const
 {
     // check if the attribute exists (empty string may be different to missing 
attribute)
-    if( mxAttribs->hasAttribute( nAttrToken ) )
-        return std::optional< OUString >( mxAttribs->getOptionalValue( 
nAttrToken ) );
+    if (sal_Int32 index = mxAttribs->getAttributeIndex(nAttrToken); index >= 0)
+        return mxAttribs->getValueByIndex(index);
     return std::optional< OUString >();
 }
 
 OUString AttributeList::getStringDefaulted( sal_Int32 nAttrToken ) const
 {
-    // check if the attribute exists (empty string may be different to missing 
attribute)
-    if( mxAttribs->hasAttribute( nAttrToken ) )
-        return mxAttribs->getOptionalValue( nAttrToken );
-    return OUString();
+    return mxAttribs->getOptionalValue(nAttrToken);
 }
 
 std::optional< OUString > AttributeList::getXString( sal_Int32 nAttrToken ) 
const
 {
     // check if the attribute exists (empty string may be different to missing 
attribute)
-    if( mxAttribs->hasAttribute( nAttrToken ) )
-        return std::optional< OUString >( AttributeConversion::decodeXString( 
mxAttribs->getOptionalValue( nAttrToken ) ) );
+    if (sal_Int32 index = mxAttribs->getAttributeIndex(nAttrToken); index >= 0)
+        return 
AttributeConversion::decodeXString(mxAttribs->getValueByIndex(index));
     return std::optional< OUString >();
 }
 
 std::optional< double > AttributeList::getDouble( sal_Int32 nAttrToken ) const
 {
     double nValue;
-    bool bValid = getAttribList()->getAsDouble( nAttrToken, nValue );
+    bool bValid = mxAttribs->getAsDouble(nAttrToken, nValue);
     return bValid ? std::optional< double >( nValue ) : std::optional< double 
>();
 }
 
 std::optional< sal_Int32 > AttributeList::getInteger( sal_Int32 nAttrToken ) 
const
 {
     sal_Int32 nValue;
-    bool bValid = getAttribList()->getAsInteger( nAttrToken, nValue );
+    bool bValid = mxAttribs->getAsInteger(nAttrToken, nValue);
     return bValid ? std::optional< sal_Int32 >( nValue ) : std::optional< 
sal_Int32 >();
 }
 
 std::optional< sal_uInt32 > AttributeList::getUnsigned( sal_Int32 nAttrToken ) 
const
 {
-    OUString aValue = mxAttribs->getOptionalValue( nAttrToken );
-    bool bValid = !aValue.isEmpty();
-    return bValid ? std::optional< sal_uInt32 >( 
AttributeConversion::decodeUnsigned( aValue ) ) : std::optional< sal_uInt32 >();
+    std::string_view aValue = getView(nAttrToken);
+    bool bValid = !aValue.empty();
+    return bValid ? std::optional< sal_uInt32 >( decodeUnsigned_impl( aValue ) 
) : std::optional< sal_uInt32 >();
 }
 
 std::optional< sal_Int64 > AttributeList::getHyper( sal_Int32 nAttrToken ) 
const
@@ -224,19 +227,19 @@ std::optional< sal_Int64 > AttributeList::getHyper( 
sal_Int32 nAttrToken ) const
 
 std::optional< sal_Int32 > AttributeList::getIntegerHex( sal_Int32 nAttrToken 
) const
 {
-    OUString aValue = mxAttribs->getOptionalValue( nAttrToken );
-    bool bValid = !aValue.isEmpty();
-    return bValid ? std::optional< sal_Int32 >( 
AttributeConversion::decodeIntegerHex( aValue ) ) : std::optional< sal_Int32 
>();
+    std::string_view aValue = getView(nAttrToken);
+    bool bValid = !aValue.empty();
+    return bValid ? std::optional< sal_Int32 >( decodeIntegerHex_impl( aValue 
) ) : std::optional< sal_Int32 >();
 }
 
 std::optional< bool > AttributeList::getBool( sal_Int32 nAttrToken ) const
 {
-    std::string_view pAttr;
+    sal_Int32 index = mxAttribs->getAttributeIndex(nAttrToken);
+    if (index == -1)
+        return std::optional< bool >();
+    std::string_view pAttr = mxAttribs->getAsViewByIndex(index);
 
     // catch the common cases as quickly as possible first
-    bool bHasAttr = getAttribList()->getAsView( nAttrToken, pAttr );
-    if( !bHasAttr )
-        return std::optional< bool >();
     if( pAttr == "false" )
         return std::optional< bool >( false );
     if( pAttr == "true" )
@@ -245,7 +248,7 @@ std::optional< bool > AttributeList::getBool( sal_Int32 
nAttrToken ) const
     // now for all the crazy stuff
 
     // boolean attributes may be "t", "f", "true", "false", "on", "off", "1", 
or "0"
-    switch( getToken( nAttrToken, XML_TOKEN_INVALID ) )
+    switch (mxAttribs->getValueTokenByIndex(index))
     {
         case XML_t:     return std::optional< bool >( true );  // used in VML
         case XML_true:  return std::optional< bool >( true );
@@ -254,8 +257,7 @@ std::optional< bool > AttributeList::getBool( sal_Int32 
nAttrToken ) const
         case XML_false: return std::optional< bool >( false );
         case XML_off:   return std::optional< bool >( false );
     }
-    std::optional< sal_Int32 > onValue = getInteger( nAttrToken );
-    return onValue.has_value() ? std::optional< bool >( onValue.value() != 0 ) 
: std::optional< bool >();
+    return std::optional<bool>(o3tl::toInt32(pAttr) != 0);
 }
 
 std::optional< util::DateTime > AttributeList::getDateTime( sal_Int32 
nAttrToken ) const
@@ -288,17 +290,8 @@ sal_Int32 AttributeList::getToken( sal_Int32 nAttrToken, 
sal_Int32 nDefault ) co
 
 OUString AttributeList::getString( sal_Int32 nAttrToken, const OUString& 
rDefault ) const
 {
-    // try to avoid slow exception throw/catch if we can
-    if (rDefault.isEmpty())
-        return mxAttribs->getOptionalValue( nAttrToken );
-
-    try
-    {
-        return mxAttribs->getValue( nAttrToken );
-    }
-    catch( Exception& )
-    {
-    }
+    if (sal_Int32 index = mxAttribs->getAttributeIndex(nAttrToken); index >= 0)
+        return mxAttribs->getValueByIndex(index);
     return rDefault;
 }
 
@@ -310,7 +303,7 @@ OUString AttributeList::getXString( sal_Int32 nAttrToken, 
const OUString& rDefau
 std::string_view AttributeList::getView( sal_Int32 nAttrToken ) const
 {
     std::string_view p;
-    getAttribList()->getAsView(nAttrToken, p);
+    mxAttribs->getAsView(nAttrToken, p);
     return p;
 }
 
@@ -357,12 +350,9 @@ util::DateTime AttributeList::getDateTime( sal_Int32 
nAttrToken, const util::Dat
 std::vector<sal_Int32> AttributeList::getTokenList(sal_Int32 nAttrToken) const
 {
     std::vector<sal_Int32> aValues;
-    OUString sValue = getString(nAttrToken, "");
-    sal_Int32 nIndex = 0;
-    do
-    {
-        
aValues.push_back(AttributeConversion::decodeToken(o3tl::getToken(sValue, 0, ' 
', nIndex)));
-    } while (nIndex >= 0);
+    if (std::string_view sValue = getView(nAttrToken); !sValue.empty())
+        for (size_t index = 0; index != std::string_view::npos;)
+            
aValues.push_back(TokenMap::getTokenFromUtf8(o3tl::getToken(sValue, ' ', 
index)));
 
     return aValues;
 }
diff --git a/oox/source/mathml/importutils.cxx 
b/oox/source/mathml/importutils.cxx
index 7aa8a1a924e5..4f6c243b9970 100644
--- a/oox/source/mathml/importutils.cxx
+++ b/oox/source/mathml/importutils.cxx
@@ -52,7 +52,7 @@ AttributeListBuilder::AttributeListBuilder( const 
uno::Reference< xml::sax::XFas
 
 OString tokenToString( int token )
 {
-    uno::Sequence< sal_Int8 > const & aTokenNameSeq = 
StaticTokenMap().getUtf8TokenName( token & TOKEN_MASK );
+    uno::Sequence<sal_Int8> const& aTokenNameSeq = 
TokenMap::getUtf8TokenName(token & TOKEN_MASK);
     OString tokenname( reinterpret_cast< const char* >( 
aTokenNameSeq.getConstArray() ), aTokenNameSeq.getLength() );
     if( tokenname.isEmpty())
         tokenname = "???"_ostr;
diff --git a/oox/source/token/tokenmap.cxx b/oox/source/token/tokenmap.cxx
index 1e51116192c6..dcd1481cbe15 100644
--- a/oox/source/token/tokenmap.cxx
+++ b/oox/source/token/tokenmap.cxx
@@ -17,6 +17,10 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <sal/config.h>
+
+#include <array>
+
 #include <oox/token/tokenmap.hxx>
 
 #include <string.h>
@@ -25,8 +29,6 @@
 
 namespace oox {
 
-using ::com::sun::star::uno::Sequence;
-
 namespace {
 // include auto-generated Perfect_Hash
 #if defined __clang__
@@ -42,55 +44,61 @@ namespace {
 #endif
 } // namespace
 
-const css::uno::Sequence< sal_Int8 > TokenMap::EMPTY_BYTE_SEQ;
+static sal_Int32 getTokenPerfectHash(const char* pStr, sal_Int32 nLength)
+{
+    const struct xmltoken* pToken = Perfect_Hash::in_word_set( pStr, nLength );
+    return pToken ? pToken->nToken : XML_TOKEN_INVALID;
+}
 
-TokenMap::TokenMap() :
-    maTokenNames( static_cast< size_t >( XML_TOKEN_COUNT ) )
+css::uno::Sequence<sal_Int8> const& TokenMap::getUtf8TokenName(sal_Int32 
nToken)
 {
-    static const char* sppcTokenNames[] =
+    static const auto saTokenNames = []()
     {
+        static constexpr std::string_view sppcTokenNames[] = {
 // include auto-generated C array with token names as C strings
 #include <tokennames.inc>
-        ""
-    };
-
-    const char* const* ppcTokenName = sppcTokenNames;
-    for (auto & tokenName : maTokenNames)
-    {
-        OString aUtf8Token( *ppcTokenName );
-        tokenName = Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( 
aUtf8Token.getStr() ), aUtf8Token.getLength() );
-        ++ppcTokenName;
-    }
+        };
+        static_assert(std::size(sppcTokenNames) == XML_TOKEN_COUNT);
 
-    for (unsigned char c = 'a'; c <= 'z'; c++)
-    {
-        const struct xmltoken* pToken = Perfect_Hash::in_word_set(
-                reinterpret_cast< const char* >( &c ), 1 );
-        mnAlphaTokens[ c - 'a' ] = pToken ? pToken->nToken : XML_TOKEN_INVALID;
-    }
-}
+        std::vector<css::uno::Sequence<sal_Int8>> aTokenNames;
+        aTokenNames.reserve(std::size(sppcTokenNames));
+        std::transform(
+            std::begin(sppcTokenNames), std::end(sppcTokenNames), 
std::back_inserter(aTokenNames),
+            [](auto aUtf8Token)
+            {
+                return css::uno::Sequence<sal_Int8>(
+                    reinterpret_cast<const sal_Int8*>(aUtf8Token.data()), 
aUtf8Token.size());
+            });
+        return aTokenNames;
+    }();
 
-TokenMap::~TokenMap()
-{
+    SAL_WARN_IF(nToken < 0 || nToken >= XML_TOKEN_COUNT, "oox", "Wrong nToken 
parameter");
+    if (0 <= nToken && nToken < XML_TOKEN_COUNT)
+        return saTokenNames[nToken];
+    static const css::uno::Sequence<sal_Int8> EMPTY_BYTE_SEQ;
+    return EMPTY_BYTE_SEQ;
 }
 
-sal_Int32 TokenMap::getTokenFromUnicode( std::u16string_view rUnicodeName )
-{
-    OString aUtf8Name = OUStringToOString( rUnicodeName, RTL_TEXTENCODING_UTF8 
);
-    const struct xmltoken* pToken = Perfect_Hash::in_word_set( 
aUtf8Name.getStr(), aUtf8Name.getLength() );
-    return pToken ? pToken->nToken : XML_TOKEN_INVALID;
-}
 
-sal_Int32 TokenMap::getTokenPerfectHash( const char *pStr, sal_Int32 nLength )
+/** Returns the token identifier for a UTF8 string passed in pToken */
+sal_Int32 TokenMap::getTokenFromUtf8(std::string_view token)
 {
-    const struct xmltoken* pToken = Perfect_Hash::in_word_set( pStr, nLength );
-    return pToken ? pToken->nToken : XML_TOKEN_INVALID;
-}
+    static const auto snAlphaTokens = []()
+    {
+        std::array<sal_Int32, 26> nAlphaTokens{};
+        for (char c = 'a'; c <= 'z'; c++)
+            nAlphaTokens[c - 'a'] = getTokenPerfectHash(&c, 1);
+        return nAlphaTokens;
+    }();
 
-TokenMap& StaticTokenMap()
-{
-    static TokenMap SINGLETON;
-    return SINGLETON;
+    // 50% of OOXML tokens are primarily 1 lower-case character, a-z
+    if (token.size() == 1)
+    {
+        char c = token[0];
+        if (c >= 'a' && c <= 'z')
+            return snAlphaTokens[c - 'a'];
+    }
+    return getTokenPerfectHash(token.data(), token.size());
 }
 
 } // namespace oox
diff --git a/sax/qa/cppunit/parser.cxx b/sax/qa/cppunit/parser.cxx
index 670c1afa9277..288a91eefda0 100644
--- a/sax/qa/cppunit/parser.cxx
+++ b/sax/qa/cppunit/parser.cxx
@@ -37,7 +37,7 @@ public:
         CPPUNIT_ASSERT_MESSAGE( "getUTF8Identifier: unexpected call", false );
         return uno::Sequence<sal_Int8>();
     }
-    virtual sal_Int32 getTokenDirect( const char * /* pToken */, sal_Int32 /* 
nLength */ ) const override
+    virtual sal_Int32 getTokenDirect(std::string_view /* token */) const 
override
     {
         return -1;
     }
diff --git a/sax/qa/cppunit/xmlimport.cxx b/sax/qa/cppunit/xmlimport.cxx
index 1eb872d50585..befe13d2902c 100644
--- a/sax/qa/cppunit/xmlimport.cxx
+++ b/sax/qa/cppunit/xmlimport.cxx
@@ -262,7 +262,7 @@ public:
     virtual Sequence< sal_Int8 > SAL_CALL getUTF8Identifier( sal_Int32 nToken 
) override;
     virtual sal_Int32 SAL_CALL getTokenFromUTF8( const css::uno::Sequence< 
sal_Int8 >& Identifier ) override;
     //FastTokenHandlerBase
-    virtual sal_Int32 getTokenDirect( const char *pToken, sal_Int32 nLength ) 
const override;
+    virtual sal_Int32 getTokenDirect(std::string_view sToken) const override;
 };
 
 const std::string_view DummyTokenHandler::tokens[] = {
@@ -304,13 +304,12 @@ Sequence< sal_Int8 > 
DummyTokenHandler::getUTF8Identifier( sal_Int32 nToken )
 
 sal_Int32 DummyTokenHandler::getTokenFromUTF8( const uno::Sequence< sal_Int8 
>& rIdentifier )
 {
-    return getTokenDirect( reinterpret_cast< const char* >(
-                    rIdentifier.getConstArray() ), rIdentifier.getLength() );
+    return getTokenDirect(std::string_view(
+        reinterpret_cast<const char*>(rIdentifier.getConstArray()), 
rIdentifier.getLength()));
 }
 
-sal_Int32 DummyTokenHandler::getTokenDirect( const char* pToken, sal_Int32 
nLength ) const
+sal_Int32 DummyTokenHandler::getTokenDirect(std::string_view sToken) const
 {
-    std::string_view sToken( pToken, nLength );
     for( size_t  i = 0; i < std::size(tokens); i++ )
     {
         if ( tokens[i] == sToken )
diff --git a/sax/source/tools/fastattribs.cxx b/sax/source/tools/fastattribs.cxx
index 45e2e9c5ebba..ceda451bafc5 100644
--- a/sax/source/tools/fastattribs.cxx
+++ b/sax/source/tools/fastattribs.cxx
@@ -172,9 +172,7 @@ sal_Int32 FastAttributeList::getValueToken( ::sal_Int32 
Token )
 {
     for (size_t i = 0, n = maAttributeTokens.size(); i < n; ++i)
         if (maAttributeTokens[i] == Token)
-            return FastTokenHandlerBase::getTokenFromChars(
-                       mpTokenHandler,
-                       getAsViewByIndex(i) );
+            return getValueTokenByIndex(i);
 
     throw SAXException("FastAttributeList::getValueToken: unknown token " + 
OUString::number(Token), nullptr, Any());
 }
@@ -183,9 +181,7 @@ sal_Int32 FastAttributeList::getOptionalValueToken( 
::sal_Int32 Token, ::sal_Int
 {
     for (size_t i = 0, n = maAttributeTokens.size(); i < n; ++i)
         if (maAttributeTokens[i] == Token)
-            return FastTokenHandlerBase::getTokenFromChars(
-                       mpTokenHandler,
-                       getAsViewByIndex(i) );
+            return getValueTokenByIndex(i);
 
     return Default;
 }
@@ -246,6 +242,12 @@ OUString FastAttributeList::getOptionalValue( ::sal_Int32 
Token )
 
     return OUString();
 }
+
+sal_Int32 FastAttributeList::getValueTokenByIndex(sal_Int32 nTokenIndex) const
+{
+    return FastTokenHandlerBase::getTokenFromChars(mpTokenHandler, 
getAsViewByIndex(nTokenIndex));
+}
+
 Sequence< Attribute > FastAttributeList::getUnknownAttributes(  )
 {
     auto nSize = maUnknownAttributes.size();
@@ -282,7 +284,7 @@ sal_Int32 FastTokenHandlerBase::getTokenFromChars(
         const FastTokenHandlerBase *pTokenHandler,
         std::string_view token )
 {
-    return pTokenHandler->getTokenDirect(token.data(), token.size());
+    return pTokenHandler->getTokenDirect(token);
 }
 
 }
diff --git a/sw/source/core/inc/SwXMLBlockImport.hxx 
b/sw/source/core/inc/SwXMLBlockImport.hxx
index 821033c1e1e5..8932236e327d 100644
--- a/sw/source/core/inc/SwXMLBlockImport.hxx
+++ b/sw/source/core/inc/SwXMLBlockImport.hxx
@@ -94,7 +94,7 @@ public:
     css::uno::Sequence< sal_Int8 > SAL_CALL getUTF8Identifier( sal_Int32 Token 
) override;
 
     //Much fast direct C++ shortcut to the method that matters
-    virtual sal_Int32 getTokenDirect( const char *pTag, sal_Int32 nLength ) 
const override;
+    virtual sal_Int32 getTokenDirect(std::string_view token) const override;
 };
 
 enum SwXMLBlockListToken : sal_Int32
@@ -120,7 +120,7 @@ public:
     css::uno::Sequence< sal_Int8 > SAL_CALL getUTF8Identifier( sal_Int32 Token 
) override;
 
     //Much fast direct C++ shortcut to the method that matters
-    virtual sal_Int32 getTokenDirect( const char *pTag, sal_Int32 nLength ) 
const override;
+    virtual sal_Int32 getTokenDirect(std::string_view token) const override;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/swg/SwXMLBlockImport.cxx 
b/sw/source/core/swg/SwXMLBlockImport.cxx
index b0453a8a5a65..cf740a42bb05 100644
--- a/sw/source/core/swg/SwXMLBlockImport.cxx
+++ b/sw/source/core/swg/SwXMLBlockImport.cxx
@@ -127,7 +127,7 @@ SwXMLTextBlockTokenHandler::~SwXMLTextBlockTokenHandler()
 
 sal_Int32 SAL_CALL SwXMLTextBlockTokenHandler::getTokenFromUTF8( const 
Sequence< sal_Int8 >& Identifier )
 {
-    return getTokenDirect( reinterpret_cast< const char* >( 
Identifier.getConstArray() ), Identifier.getLength() );
+    return getTokenDirect( std::string_view(reinterpret_cast< const char* >( 
Identifier.getConstArray() ), Identifier.getLength()) );
 }
 
 Sequence< sal_Int8 > SAL_CALL SwXMLTextBlockTokenHandler::getUTF8Identifier( 
sal_Int32 )
@@ -135,11 +135,9 @@ Sequence< sal_Int8 > SAL_CALL 
SwXMLTextBlockTokenHandler::getUTF8Identifier( sal
     return Sequence< sal_Int8 >();
 }
 
-sal_Int32 SwXMLTextBlockTokenHandler::getTokenDirect( const char *pTag, 
sal_Int32 nLength ) const
+sal_Int32 SwXMLTextBlockTokenHandler::getTokenDirect(std::string_view token) 
const
 {
-    if( !nLength )
-        nLength = strlen( pTag );
-    const struct xmltoken* pToken = TextBlockTokens::in_word_set( pTag, 
nLength );
+    const struct xmltoken* pToken = TextBlockTokens::in_word_set(token.data(), 
token.size());
     return pToken ? pToken->nToken : XML_TOKEN_INVALID;
 }
 
@@ -153,7 +151,7 @@ SwXMLBlockListTokenHandler::~SwXMLBlockListTokenHandler()
 
 sal_Int32 SAL_CALL SwXMLBlockListTokenHandler::getTokenFromUTF8( const 
Sequence< sal_Int8 >& Identifier )
 {
-    return getTokenDirect( reinterpret_cast< const char* >( 
Identifier.getConstArray() ), Identifier.getLength() );
+    return getTokenDirect( std::string_view(reinterpret_cast< const char* >( 
Identifier.getConstArray() ), Identifier.getLength()) );
 }
 
 Sequence< sal_Int8 > SAL_CALL SwXMLBlockListTokenHandler::getUTF8Identifier( 
sal_Int32 )
@@ -161,11 +159,9 @@ Sequence< sal_Int8 > SAL_CALL 
SwXMLBlockListTokenHandler::getUTF8Identifier( sal
     return Sequence< sal_Int8 >();
 }
 
-sal_Int32 SwXMLBlockListTokenHandler::getTokenDirect( const char *pTag, 
sal_Int32 nLength ) const
+sal_Int32 SwXMLBlockListTokenHandler::getTokenDirect(std::string_view token) 
const
 {
-    if( !nLength )
-        nLength = strlen( pTag );
-    const struct xmltoken* pToken = BlockListTokens::in_word_set( pTag, 
nLength );
+    const struct xmltoken* pToken = BlockListTokens::in_word_set(token.data(), 
token.size());
     return pToken ? pToken->nToken : XML_TOKEN_INVALID;
 }
 
diff --git a/unoxml/qa/unit/domtest.cxx b/unoxml/qa/unit/domtest.cxx
index a97248167f60..53ff3099b66e 100644
--- a/unoxml/qa/unit/domtest.cxx
+++ b/unoxml/qa/unit/domtest.cxx
@@ -183,7 +183,7 @@ struct TokenHandler : public 
sax_fastparser::FastTokenHandlerBase
         return uno::Sequence<sal_Int8>();
     }
 
-    virtual sal_Int32 getTokenDirect( const char * /* pToken */, sal_Int32 /* 
nLength */ ) const override
+    virtual sal_Int32 getTokenDirect( std::string_view /* token */ ) const 
override
     {
         return -1;
     }
diff --git a/writerfilter/source/ooxml/OOXMLStreamImpl.cxx 
b/writerfilter/source/ooxml/OOXMLStreamImpl.cxx
index aa2567ad53b2..f9808a4e4b0e 100644
--- a/writerfilter/source/ooxml/OOXMLStreamImpl.cxx
+++ b/writerfilter/source/ooxml/OOXMLStreamImpl.cxx
@@ -394,7 +394,7 @@ uno::Reference<uno::XComponentContext> 
OOXMLStreamImpl::getContext()
 uno::Reference <xml::sax::XFastTokenHandler> 
OOXMLStreamImpl::getFastTokenHandler()
 {
     if (! mxFastTokenHandler.is())
-        mxFastTokenHandler.set(new oox::core::FastTokenHandler());
+        mxFastTokenHandler.set(new oox::core::FastTokenHandler);
 
     return mxFastTokenHandler;
 }
diff --git a/xmloff/inc/fasttokenhandler.hxx b/xmloff/inc/fasttokenhandler.hxx
index 528ecde17546..963d2410016e 100644
--- a/xmloff/inc/fasttokenhandler.hxx
+++ b/xmloff/inc/fasttokenhandler.hxx
@@ -18,72 +18,29 @@
 
 namespace xmloff::token {
 
-class TokenMap
+namespace TokenMap
 {
-public:
-    explicit TokenMap();
-            ~TokenMap();
-
-    /** Returns the UTF-8 name of the passed token identifier as byte 
sequence. */
-    css::uno::Sequence< sal_Int8 > const & getUtf8TokenName( sal_Int32 nToken 
) const
-    {
-        SAL_WARN_IF(nToken < 0 || nToken >= XML_TOKEN_COUNT, "xmloff", "Wrong 
nToken parameter");
-        if( 0 <= nToken && nToken < XML_TOKEN_COUNT )
-            return maTokenNamesUtf8[ nToken ];
-        return EMPTY_BYTE_SEQ;
-    }
-
-    const OUString& getTokenName( sal_Int32 nToken ) const
-    {
-        SAL_WARN_IF(nToken < 0 || nToken >= XML_TOKEN_COUNT, "xmloff", "Wrong 
nToken parameter");
-        if( 0 <= nToken && nToken < XML_TOKEN_COUNT )
-            return maTokenNames[ nToken ];
-        return EMPTY_STRING;
-    }
-
-    /** Returns the token identifier for the passed UTF-8 token name. */
-    static sal_Int32 getTokenFromUtf8( const css::uno::Sequence< sal_Int8 >& 
rUtf8Name )
-    {
-        return getTokenFromUTF8( reinterpret_cast< const char* >(
-                    rUtf8Name.getConstArray() ), rUtf8Name.getLength() );
-    }
-
-    /** Returns the token identifier for a UTF-8 string */
-    static sal_Int32 getTokenFromUTF8( const char *pToken, sal_Int32 nLength )
-    {
-        return getTokenPerfectHash( pToken, nLength );
-    }
+/** Returns the UTF-8 name of the passed token identifier as byte sequence. */
+css::uno::Sequence<sal_Int8> const& getUtf8TokenName(sal_Int32 nToken);
 
-private:
-    static sal_Int32 getTokenPerfectHash( const char *pToken, sal_Int32 
nLength );
-
-    std::vector< css::uno::Sequence< sal_Int8 > > maTokenNamesUtf8;
-    std::vector< OUString > maTokenNames;
-
-    static const css::uno::Sequence< sal_Int8 > EMPTY_BYTE_SEQ;
-    static const OUString EMPTY_STRING;
+/** Returns the token identifier for a UTF-8 string */
+sal_Int32 getTokenFromUtf8(std::string_view token);
 };
 
-TokenMap& StaticTokenMap();
-
 class FastTokenHandler final :
     public sax_fastparser::FastTokenHandlerBase
 {
 public:
-    explicit FastTokenHandler();
-    virtual ~FastTokenHandler() override;
+    explicit FastTokenHandler() = default;
 
     // XFastTokenHandler
     virtual css::uno::Sequence< sal_Int8 > SAL_CALL getUTF8Identifier( 
sal_Int32 nToken ) override;
     virtual sal_Int32 SAL_CALL getTokenFromUTF8( const css::uno::Sequence< 
sal_Int8 >& Identifier ) override;
 
-    const OUString & getIdentifier( sal_Int32 nToken ) const;
+    static const OUString& getIdentifier(sal_Int32 nToken);
 
     // Much faster direct C++ shortcut to the method that matters
-    virtual sal_Int32 getTokenDirect( const char *pToken, sal_Int32 nLength ) 
const override;
-
-private:
-    TokenMap& mrTokenMap;
+    virtual sal_Int32 getTokenDirect(std::string_view token) const override;
 };
 
 } // namespace xmloff::token
diff --git a/xmloff/qa/unit/tokenmap-test.cxx b/xmloff/qa/unit/tokenmap-test.cxx
index 395237b8adb1..a3ef3f5da9eb 100644
--- a/xmloff/qa/unit/tokenmap-test.cxx
+++ b/xmloff/qa/unit/tokenmap-test.cxx
@@ -23,9 +23,6 @@ namespace xmloff {
 class TokenmapTest: public CppUnit::TestFixture
 {
 public:
-
-    TokenmapTest();
-
     void test_roundTrip();
     void test_listEquality();
 
@@ -35,25 +32,18 @@ public:
     CPPUNIT_TEST(test_listEquality);
 
     CPPUNIT_TEST_SUITE_END();
-
-private:
-    std::unique_ptr<token::TokenMap> pTokenMap;
 };
 
-TokenmapTest::TokenmapTest() : pTokenMap(new token::TokenMap)
-{
-}
-
 void TokenmapTest::test_roundTrip()
 {
     for ( sal_Int32 nToken = 0; nToken < XML_TOKEN_COUNT; ++nToken )
     {
         // check that the getIdentifier <-> getToken roundtrip works
-        Sequence< sal_Int8 > rUtf8Name = pTokenMap->getUtf8TokenName(nToken);
+        Sequence< sal_Int8 > rUtf8Name = 
token::TokenMap::getUtf8TokenName(nToken);
         CPPUNIT_ASSERT_MESSAGE("Token name sequence should not be empty", 
rUtf8Name.getLength());
         const char* pChar = reinterpret_cast< const char * 
>(rUtf8Name.getConstArray());
         CPPUNIT_ASSERT_MESSAGE("Token name sequence array pointer failed", 
pChar);
-        sal_Int32 ret = token::TokenMap::getTokenFromUTF8( pChar, 
rUtf8Name.getLength() );
+        sal_Int32 ret = token::TokenMap::getTokenFromUtf8( 
std::string_view(pChar, rUtf8Name.getLength()) );
         CPPUNIT_ASSERT_EQUAL_MESSAGE("No roundtrip for token", ret, nToken);
     }
 }
@@ -65,7 +55,7 @@ void TokenmapTest::test_listEquality()
     // aTokenList in xmloff/source/core/xmltoken.cxx, and 
xmloff/source/token/tokens.txt
     for ( sal_Int32 nToken = 0; nToken < XML_TOKEN_COUNT; ++nToken )
     {
-        Sequence< sal_Int8 > rUtf8Name = pTokenMap->getUtf8TokenName(nToken);
+        Sequence<sal_Int8> rUtf8Name = 
token::TokenMap::getUtf8TokenName(nToken);
         const OUString& rName = OUString( reinterpret_cast< const char* >(
                         rUtf8Name.getConstArray() ), rUtf8Name.getLength(), 
RTL_TEXTENCODING_UTF8 );
         if ( rName.endsWith("_DUMMY") )
@@ -78,7 +68,7 @@ void TokenmapTest::test_listEquality()
             nToken < xmloff::token::XMLTokenEnum::XML_TOKEN_END; ++nToken )
     {
         const OUString& rTokenName = GetXMLToken( 
static_cast<xmloff::token::XMLTokenEnum>(nToken) );
-        Sequence< sal_Int8 > rUtf8Name = pTokenMap->getUtf8TokenName(nToken);
+        Sequence<sal_Int8> rUtf8Name = 
token::TokenMap::getUtf8TokenName(nToken);
         const OUString& rName = OUString( reinterpret_cast< const char* >(
                         rUtf8Name.getConstArray() ), rUtf8Name.getLength(), 
RTL_TEXTENCODING_UTF8 );
         if ( !rName.endsWith("_DUMMY") )
diff --git a/xmloff/source/core/fasttokenhandler.cxx 
b/xmloff/source/core/fasttokenhandler.cxx
index b82ee67bfe54..e7b2dc7e017e 100644
--- a/xmloff/source/core/fasttokenhandler.cxx
+++ b/xmloff/source/core/fasttokenhandler.cxx
@@ -32,76 +32,73 @@ namespace token {
 
 using namespace css;
 
-TokenMap& StaticTokenMap()
+static sal_Int32 getTokenPerfectHash(const char* pStr, sal_Int32 nLength)
 {
-    static TokenMap SINGLETON;
-    return SINGLETON;
+    const struct xmltoken* pToken = Perfect_Hash::in_word_set(pStr, nLength);
+    return pToken ? pToken->nToken : xmloff::XML_TOKEN_INVALID;
 }
 
-const css::uno::Sequence< sal_Int8 > TokenMap::EMPTY_BYTE_SEQ;
-const OUString TokenMap::EMPTY_STRING;
-
-TokenMap::TokenMap() :
-    maTokenNamesUtf8( static_cast< size_t >( XML_TOKEN_COUNT ) ),
-    maTokenNames( static_cast< size_t >( XML_TOKEN_COUNT ) )
+static const std::pair<css::uno::Sequence<sal_Int8>, OUString>& 
getNames(sal_Int32 nToken)
 {
-    static const char* sppcTokenNames[] =
+    static const auto saTokenNames = []()
     {
+        static constexpr std::string_view sppcTokenNames[] = {
 #include <tokennames.inc>
-        ""
-    };
-
-    const char* const* ppcTokenName = sppcTokenNames;
-    int i = 0;
-    for( auto& rTokenName : maTokenNamesUtf8 )
-    {
-        std::string_view pStr = *ppcTokenName;
-        rTokenName = uno::Sequence< sal_Int8 >(
-                    reinterpret_cast< const sal_Int8* >( pStr.data() ), 
pStr.length() );
-        maTokenNames[i++] = OStringToOUString( pStr, RTL_TEXTENCODING_UTF8 );
-        ++ppcTokenName;
-    }
-}
-
-TokenMap::~TokenMap()
-{
-}
-
-sal_Int32 TokenMap::getTokenPerfectHash( const char *pStr, sal_Int32 nLength )
-{
-    const struct xmltoken *pToken = Perfect_Hash::in_word_set( pStr, nLength );
-    return pToken ? pToken->nToken : xmloff::XML_TOKEN_INVALID;
+        };
+        static_assert(std::size(sppcTokenNames) == XML_TOKEN_COUNT);
+
+        std::vector<std::pair<css::uno::Sequence<sal_Int8>, OUString>> names;
+        names.reserve(std::size(sppcTokenNames));
+        std::transform(std::begin(sppcTokenNames), std::end(sppcTokenNames),
+                       std::back_inserter(names),
+                       [](auto token)
+                       {
+                           return std::make_pair(
+                               css::uno::Sequence<sal_Int8>(
+                                   reinterpret_cast<const 
sal_Int8*>(token.data()), token.size()),
+                               OStringToOUString(token, 
RTL_TEXTENCODING_UTF8));
+                       });
+        return names;
+    }();
+
+    SAL_WARN_IF(nToken < 0 || nToken >= XML_TOKEN_COUNT, "xmloff", "Wrong 
nToken parameter");
+    if (0 <= nToken && nToken < XML_TOKEN_COUNT)
+        return saTokenNames[nToken];
+    static const std::pair<css::uno::Sequence<sal_Int8>, OUString> EMPTY;
+    return EMPTY;
 }
 
-FastTokenHandler::FastTokenHandler() :
-    mrTokenMap( StaticTokenMap() )
+css::uno::Sequence<sal_Int8> const& TokenMap::getUtf8TokenName(sal_Int32 
nToken)
 {
+    return getNames(nToken).first;
 }
 
-FastTokenHandler::~FastTokenHandler()
+sal_Int32 TokenMap::getTokenFromUtf8(std::string_view token)
 {
+    return getTokenPerfectHash(token.data(), token.size());
 }
 
 // XFastTokenHandler
 uno::Sequence< sal_Int8 > FastTokenHandler::getUTF8Identifier( sal_Int32 
nToken )
 {
-    return mrTokenMap.getUtf8TokenName( nToken );
+    return TokenMap::getUtf8TokenName( nToken );
 }
 
-const OUString& FastTokenHandler::getIdentifier( sal_Int32 nToken ) const
+const OUString& FastTokenHandler::getIdentifier(sal_Int32 nToken)
 {
-    return mrTokenMap.getTokenName( nToken );
+    return getNames(nToken).second;
 }
 
 sal_Int32 FastTokenHandler::getTokenFromUTF8( const uno::Sequence< sal_Int8 >& 
rIdentifier )
 {
-    return TokenMap::getTokenFromUtf8( rIdentifier );
+    return TokenMap::getTokenFromUtf8(std::string_view(
+        reinterpret_cast<const char*>(rIdentifier.getConstArray()), 
rIdentifier.getLength()));
 }
 
 // Much faster direct C++ shortcut
-sal_Int32 FastTokenHandler::getTokenDirect( const char* pToken, sal_Int32 
nLength ) const
+sal_Int32 FastTokenHandler::getTokenDirect(std::string_view token) const
 {
-    return TokenMap::getTokenFromUTF8( pToken, nLength );
+    return TokenMap::getTokenFromUtf8(token);
 }
 
 } // namespace token
diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx
index cd89ce6cd303..c8c81bd72823 100644
--- a/xmloff/source/core/xmlimp.cxx
+++ b/xmloff/source/core/xmlimp.cxx
@@ -91,7 +91,7 @@ using namespace ::com::sun::star::container;
 using namespace ::com::sun::star::document;
 using namespace ::xmloff::token;
 
-rtl::Reference< FastTokenHandler > SvXMLImport::xTokenHandler( new 
FastTokenHandler() );
+rtl::Reference<FastTokenHandler> SvXMLImport::xTokenHandler(new 
FastTokenHandler);
 std::unordered_map< sal_Int32, std::pair< OUString, OUString > > 
SvXMLImport::aNamespaceMap;
 std::unordered_map< OUString, OUString > SvXMLImport::aNamespaceURIPrefixMap;
 bool SvXMLImport::bIsNSMapsInitialized = false;
@@ -1989,7 +1989,7 @@ bool SvXMLImport::embeddedFontAlreadyProcessed( const 
OUString& url )
 
 const OUString & SvXMLImport::getNameFromToken( sal_Int32 nToken )
 {
-    return xTokenHandler->getIdentifier( nToken & TOKEN_MASK );
+    return FastTokenHandler::getIdentifier( nToken & TOKEN_MASK );
 }
 
 OUString SvXMLImport::getPrefixAndNameFromToken( sal_Int32 nToken )
@@ -1999,7 +1999,7 @@ OUString SvXMLImport::getPrefixAndNameFromToken( 
sal_Int32 nToken )
     auto aIter( aNamespaceMap.find( nNamespaceToken ) );
     if( aIter != aNamespaceMap.end() )
         rv = (*aIter).second.second + " " + aIter->second.first + ":";
-    return rv + xTokenHandler->getIdentifier( nToken & TOKEN_MASK );
+    return rv + FastTokenHandler::getIdentifier(nToken & TOKEN_MASK);
 }
 
 OUString SvXMLImport::getNamespacePrefixFromToken(sal_Int32 nToken, const 
SvXMLNamespaceMap* pMap)

Reply via email to