Home | History | Annotate | Download | only in common
      1 /*
      2 ******************************************************************************
      3 * Copyright (C) 2014, International Business Machines Corporation and
      4 * others. All Rights Reserved.
      5 ******************************************************************************
      6 *
      7 * File UNIFIEDCACHE.H - The ICU Unified cache.
      8 ******************************************************************************
      9 */
     10 
     11 #ifndef __UNIFIED_CACHE_H__
     12 #define __UNIFIED_CACHE_H__
     13 
     14 #include "utypeinfo.h"  // for 'typeid' to work
     15 
     16 #include "unicode/uobject.h"
     17 #include "unicode/locid.h"
     18 #include "sharedobject.h"
     19 #include "unicode/unistr.h"
     20 #include "cstring.h"
     21 #include "ustr_imp.h"
     22 
     23 struct UHashtable;
     24 struct UHashElement;
     25 
     26 U_NAMESPACE_BEGIN
     27 
     28 class UnifiedCache;
     29 
     30 /**
     31  * A base class for all cache keys
     32  */
     33 class U_COMMON_API CacheKeyBase : public UObject {
     34  public:
     35    CacheKeyBase() : creationStatus(U_ZERO_ERROR) {}
     36 
     37    /**
     38     * Copy constructor. Needed to support cloning.
     39     */
     40    CacheKeyBase(const CacheKeyBase &other)
     41            : UObject(other), creationStatus(other.creationStatus) { }
     42    virtual ~CacheKeyBase();
     43 
     44    /**
     45     * Returns the hash code for this object.
     46     */
     47    virtual int32_t hashCode() const = 0;
     48 
     49    /**
     50     * Clones this object polymorphically. Caller owns returned value.
     51     */
     52    virtual CacheKeyBase *clone() const = 0;
     53 
     54    /**
     55     * Equality operator.
     56     */
     57    virtual UBool operator == (const CacheKeyBase &other) const = 0;
     58 
     59    /**
     60     * Create a new object for this key. Called by cache on cache miss.
     61     * createObject must add a reference to the object it returns. Note
     62     * that getting an object from the cache and returning it without calling
     63     * removeRef on it satisfies this requirement. It can also return NULL
     64     * and set status to an error.
     65     *
     66     * @param creationContext the context in which the object is being
     67     *                        created. May be NULL.
     68     * @param status          Implementations can return a failure here.
     69     *                        In addition, implementations may return a
     70     *                        non NULL object and set a warning status.
     71     */
     72    virtual const SharedObject *createObject(
     73            const void *creationContext, UErrorCode &status) const = 0;
     74 
     75    /**
     76     * Writes a description of this key to buffer and returns buffer. Written
     77     * description is NULL terminated.
     78     */
     79    virtual char *writeDescription(char *buffer, int32_t bufSize) const = 0;
     80 
     81    /**
     82     * Inequality operator.
     83     */
     84    UBool operator != (const CacheKeyBase &other) const {
     85        return !(*this == other);
     86    }
     87  private:
     88    mutable UErrorCode creationStatus;
     89    friend class UnifiedCache;
     90 };
     91 
     92 
     93 
     94 /**
     95  * Templated version of CacheKeyBase.
     96  * A key of type LocaleCacheKey<T> maps to a value of type T.
     97  */
     98 template<typename T>
     99 class CacheKey : public CacheKeyBase {
    100  public:
    101    virtual ~CacheKey() { }
    102    /**
    103     * The template parameter, T, determines the hash code returned.
    104     */
    105    virtual int32_t hashCode() const {
    106        const char *s = typeid(T).name();
    107        return ustr_hashCharsN(s, uprv_strlen(s));
    108    }
    109 
    110    /**
    111     * Use the value type, T,  as the description.
    112     */
    113    virtual char *writeDescription(char *buffer, int32_t bufLen) const {
    114        const char *s = typeid(T).name();
    115        uprv_strncpy(buffer, s, bufLen);
    116        buffer[bufLen - 1] = 0;
    117        return buffer;
    118    }
    119 
    120    /**
    121     * Two objects are equal if they are of the same type.
    122     */
    123    virtual UBool operator == (const CacheKeyBase &other) const {
    124        return typeid(*this) == typeid(other);
    125    }
    126 };
    127 
    128 /**
    129  * Cache key based on locale.
    130  * A key of type LocaleCacheKey<T> maps to a value of type T.
    131  */
    132 template<typename T>
    133 class LocaleCacheKey : public CacheKey<T> {
    134  protected:
    135    Locale   fLoc;
    136  public:
    137    LocaleCacheKey(const Locale &loc) : fLoc(loc) {};
    138    LocaleCacheKey(const LocaleCacheKey<T> &other)
    139            : CacheKey<T>(other), fLoc(other.fLoc) { }
    140    virtual ~LocaleCacheKey() { }
    141    virtual int32_t hashCode() const {
    142        return 37 *CacheKey<T>::hashCode() + fLoc.hashCode();
    143    }
    144    virtual UBool operator == (const CacheKeyBase &other) const {
    145        // reflexive
    146        if (this == &other) {
    147            return TRUE;
    148        }
    149        if (!CacheKey<T>::operator == (other)) {
    150            return FALSE;
    151        }
    152        // We know this and other are of same class because operator== on
    153        // CacheKey returned true.
    154        const LocaleCacheKey<T> *fOther =
    155                static_cast<const LocaleCacheKey<T> *>(&other);
    156        return fLoc == fOther->fLoc;
    157    }
    158    virtual CacheKeyBase *clone() const {
    159        return new LocaleCacheKey<T>(*this);
    160    }
    161    virtual const T *createObject(
    162            const void *creationContext, UErrorCode &status) const;
    163    /**
    164     * Use the locale id as the description.
    165     */
    166    virtual char *writeDescription(char *buffer, int32_t bufLen) const {
    167        const char *s = fLoc.getName();
    168        uprv_strncpy(buffer, s, bufLen);
    169        buffer[bufLen - 1] = 0;
    170        return buffer;
    171    }
    172 
    173 };
    174 
    175 /**
    176  * The unified cache. A singleton type.
    177  */
    178 class U_COMMON_API UnifiedCache : public UObject {
    179  public:
    180    /**
    181     * @internal
    182     */
    183    UnifiedCache(UErrorCode &status);
    184 
    185    /**
    186     * Returns the cache instance.
    187     */
    188    static const UnifiedCache *getInstance(UErrorCode &status);
    189 
    190    /**
    191     * Fetches a value from the cache by key. Equivalent to
    192     * get(key, NULL, ptr, status);
    193     */
    194    template<typename T>
    195    void get(
    196            const CacheKey<T>& key,
    197            const T *&ptr,
    198            UErrorCode &status) const {
    199        get(key, NULL, ptr, status);
    200    }
    201 
    202    /**
    203     * Fetches value from the cache by key.
    204     *
    205     * @param key             the cache key.
    206     * @param creationContext passed verbatim to createObject method of key
    207     * @param ptr             On entry, ptr must be NULL or be included if
    208     *                        the reference count of the object it points
    209     *                        to. On exit, ptr points to the fetched object
    210     *                        from the cache or is left unchanged on
    211     *                        failure. Caller must call removeRef on ptr
    212     *                        if set to a non NULL value.
    213     * @param status          Any error returned here. May be set to a
    214     *                        warning value even if ptr is set.
    215     */
    216    template<typename T>
    217    void get(
    218            const CacheKey<T>& key,
    219            const void *creationContext,
    220            const T *&ptr,
    221            UErrorCode &status) const {
    222        if (U_FAILURE(status)) {
    223            return;
    224        }
    225        UErrorCode creationStatus = U_ZERO_ERROR;
    226        const SharedObject *value = NULL;
    227        _get(key, value, creationContext, creationStatus);
    228        const T *tvalue = (const T *) value;
    229        if (U_SUCCESS(creationStatus)) {
    230            SharedObject::copyPtr(tvalue, ptr);
    231        }
    232        SharedObject::clearPtr(tvalue);
    233        // Take care not to overwrite a warning status passed in with
    234        // another warning or U_ZERO_ERROR.
    235        if (status == U_ZERO_ERROR || U_FAILURE(creationStatus)) {
    236            status = creationStatus;
    237        }
    238    }
    239 
    240 #ifdef UNIFIED_CACHE_DEBUG
    241    /**
    242     * Dumps the contents of this cache to standard error. Used for testing of
    243     * cache only.
    244     */
    245    void dumpContents() const;
    246 #endif
    247 
    248    /**
    249     * Convenience method to get a value of type T from cache for a
    250     * particular locale with creationContext == NULL.
    251     * @param loc    the locale
    252     * @param ptr    On entry, must be NULL or included in the ref count
    253     *               of the object to which it points.
    254     *               On exit, fetched value stored here or is left
    255     *               unchanged on failure. Caller must call removeRef on
    256     *               ptr if set to a non NULL value.
    257     * @param status Any error returned here. May be set to a
    258     *               warning value even if ptr is set.
    259     */
    260    template<typename T>
    261    static void getByLocale(
    262            const Locale &loc, const T *&ptr, UErrorCode &status) {
    263        const UnifiedCache *cache = getInstance(status);
    264        if (U_FAILURE(status)) {
    265            return;
    266        }
    267        cache->get(LocaleCacheKey<T>(loc), ptr, status);
    268    }
    269 
    270 #ifdef UNIFIED_CACHE_DEBUG
    271    /**
    272     * Dumps the cache contents to stderr. For testing only.
    273     */
    274    static void dump();
    275 #endif
    276 
    277    /**
    278     * Returns the number of keys in this cache. For testing only.
    279     */
    280    int32_t keyCount() const;
    281 
    282    /**
    283     * Removes any values from cache that are not referenced outside
    284     * the cache.
    285     */
    286    void flush() const;
    287 
    288    virtual ~UnifiedCache();
    289  private:
    290    UHashtable *fHashtable;
    291    UnifiedCache(const UnifiedCache &other);
    292    UnifiedCache &operator=(const UnifiedCache &other);
    293    UBool _flush(UBool all) const;
    294    void _get(
    295            const CacheKeyBase &key,
    296            const SharedObject *&value,
    297            const void *creationContext,
    298            UErrorCode &status) const;
    299    UBool _poll(
    300            const CacheKeyBase &key,
    301            const SharedObject *&value,
    302            UErrorCode &status) const;
    303    void _putNew(
    304            const CacheKeyBase &key,
    305            const SharedObject *value,
    306            const UErrorCode creationStatus,
    307            UErrorCode &status) const;
    308    void _putIfAbsentAndGet(
    309            const CacheKeyBase &key,
    310            const SharedObject *&value,
    311            UErrorCode &status) const;
    312 #ifdef UNIFIED_CACHE_DEBUG
    313    void _dumpContents() const;
    314 #endif
    315    static void _put(
    316            const UHashElement *element,
    317            const SharedObject *value,
    318            const UErrorCode status);
    319    static void _fetch(
    320            const UHashElement *element,
    321            const SharedObject *&value,
    322            UErrorCode &status);
    323    static UBool _inProgress(const UHashElement *element);
    324 };
    325 
    326 U_NAMESPACE_END
    327 
    328 #endif
    329