/**
 * Copyright (C) 2023-2026 Linaro Limited (or its affiliates). All rights reserved.
 * Copyright (C) 2016-2023 Arm Limited (or its affiliates).
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */

#ifndef IMPLICITLYSHAREDSTRINGHELPER_H
#define IMPLICITLYSHAREDSTRINGHELPER_H

#include <QString>
#include <QSet>
#include <QStack>

#include "lgpl_core_export.h"

//! Class that maintains a cache of 'canonical' strings that may be reused to reduce memory overhead of duplicate strings
/*! QStrings use implicit sharing to avoid duplication of data when strings
    are copied, assigned, or passed by value. When duplicate strings are
    created directly (from literals, or read in from external source such
    as an XML file) implicit sharing is not used as Qt does not maintain
    an internal cache of all QStrings that have been seen by a program.

    This class allow strings to be 'interned' : a single canonical string
    will be maintained in memory that can be reused, rather than maintaining
    multiple copies of that same string in memory. The first time a string is
    requested from ::intern that string is added to the InternStringCache
    (assuming an instance exists). When a duplicate string is requested
    an copy of the cached QString is returned.
    In terms of program behaviour nothing is changed (as the input and
    output strings to ::intern will be identical wrt string comparisons),
    but internally the implicit sharing of QStrings will be reduce memory
    usage.

    To use this class wrap any source for QStrings that will likely return
    duplicates with \c InternStringCache::intern(...), and create
    a single InternStringCache with a lifetime that spans the
    period in which duplicate strings could be seen.
\code
    {
        InternStringCache internStringCache;

        QString strA = InternStringCache::intern("string1")); // "string1" is added to the cache
        QString strB = InternStringCache::intern("string1");  // strB now implicitly shares strA's data
    }
\endcode
    The InternStringCache does not need to be in scope to be used
    (internally it sets a thread-local global variable), but the same
    InternStringCache instance does need to exist for the whole
    duration where duplicate strings could be created. For best results avoid
    creating multiple instance of InternStringCache. Since QStrings will
    internally use reference counting you do not need to be concerned about
    strings being invalidated when the cache goes out of scope - you only
    lose the ability for new strings to use the existing string representation
    when they are interned.

    Note that any modification of a QString will break the implicit sharing
    and cause a hard copy to take place. InternStringCache is therefore
    not appropriate when you expect strings to be altered or modified.
    */
class LGPL_CORE_EXPORT InternStringCache
{
public:
    InternStringCache();
    ~InternStringCache();

    static QString intern(const QString& str);
    static bool hasCache();

    bool isCached(const QString& str) const;

private:
    QSet<QString> mStrings;     //!< Set of all cached strings
    int mNumAdded;              //!< The number of strings that were added to the cache
    int mNumFetchedFromCache;   //!< The number of strings that were fetched from the cache (thus implicitly shared)
    static int sNumNoHelper;    //!< The number of ::get calls that were made without a InternStringCache instance present to provide a cache.
};

#endif // IMPLICITLYSHAREDSTRINGHELPER_H
