#ifndef WidthCache_h
#define WidthCache_h
#include "platform/text/TextRun.h"
#include "wtf/Forward.h"
#include "wtf/HashFunctions.h"
#include "wtf/HashSet.h"
#include "wtf/HashTableDeletedValueType.h"
#include "wtf/StringHasher.h"
namespace WebCore {
struct GlyphOverflow;
class WidthCache {
private:
class SmallStringKey {
public:
static unsigned capacity() { return s_capacity; }
SmallStringKey()
: m_length(s_emptyValueLength)
{
}
SmallStringKey(WTF::HashTableDeletedValueType)
: m_length(s_deletedValueLength)
{
}
template<typename CharacterType> SmallStringKey(CharacterType* characters, unsigned short length)
: m_length(length)
{
ASSERT(length <= s_capacity);
StringHasher hasher;
bool remainder = length & 1;
length >>= 1;
unsigned i = 0;
while (length--) {
m_characters[i] = characters[i];
m_characters[i + 1] = characters[i + 1];
hasher.addCharactersAssumingAligned(characters[i], characters[i + 1]);
i += 2;
}
if (remainder) {
m_characters[i] = characters[i];
hasher.addCharacter(characters[i]);
}
m_hash = hasher.hash();
}
const UChar* characters() const { return m_characters; }
unsigned short length() const { return m_length; }
unsigned hash() const { return m_hash; }
bool isHashTableDeletedValue() const { return m_length == s_deletedValueLength; }
bool isHashTableEmptyValue() const { return m_length == s_emptyValueLength; }
private:
static const unsigned s_capacity = 15;
static const unsigned s_emptyValueLength = s_capacity + 1;
static const unsigned s_deletedValueLength = s_capacity + 2;
unsigned m_hash;
unsigned short m_length;
UChar m_characters[s_capacity];
};
struct SmallStringKeyHash {
static unsigned hash(const SmallStringKey& key) { return key.hash(); }
static bool equal(const SmallStringKey& a, const SmallStringKey& b) { return a == b; }
static const bool safeToCompareToEmptyOrDeleted = true;
};
struct SmallStringKeyHashTraits : WTF::SimpleClassHashTraits<SmallStringKey> {
static const bool hasIsEmptyValueFunction = true;
static bool isEmptyValue(const SmallStringKey& key) { return key.isHashTableEmptyValue(); }
static const bool needsDestruction = false;
static const unsigned minimumTableSize = 16;
};
friend bool operator==(const SmallStringKey&, const SmallStringKey&);
public:
WidthCache()
: m_interval(s_maxInterval)
, m_countdown(m_interval)
{
}
float* add(const TextRun& run, float entry)
{
if (static_cast<unsigned>(run.length()) > SmallStringKey::capacity())
return 0;
if (m_countdown > 0) {
--m_countdown;
return 0;
}
return addSlowCase(run, entry);
}
void clear()
{
m_singleCharMap.clear();
m_map.clear();
}
private:
float* addSlowCase(const TextRun& run, float entry)
{
int length = run.length();
bool isNewEntry;
float *value;
if (length == 1) {
SingleCharMap::AddResult addResult = m_singleCharMap.add(run[0], entry);
isNewEntry = addResult.isNewEntry;
value = &addResult.storedValue->value;
} else {
SmallStringKey smallStringKey;
if (run.is8Bit())
smallStringKey = SmallStringKey(run.characters8(), length);
else
smallStringKey = SmallStringKey(run.characters16(), length);
Map::AddResult addResult = m_map.add(smallStringKey, entry);
isNewEntry = addResult.isNewEntry;
value = &addResult.storedValue->value;
}
if (!isNewEntry) {
m_interval = s_minInterval;
return value;
}
if (m_interval < s_maxInterval)
++m_interval;
m_countdown = m_interval;
if ((m_singleCharMap.size() + m_map.size()) < s_maxSize)
return value;
m_singleCharMap.clear();
m_map.clear();
return 0;
}
typedef HashMap<SmallStringKey, float, SmallStringKeyHash, SmallStringKeyHashTraits> Map;
typedef HashMap<uint32_t, float, DefaultHash<uint32_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint32_t> > SingleCharMap;
static const int s_minInterval = -3;
static const int s_maxInterval = 20;
static const unsigned s_maxSize = 500000;
int m_interval;
int m_countdown;
SingleCharMap m_singleCharMap;
Map m_map;
};
inline bool operator==(const WidthCache::SmallStringKey& a, const WidthCache::SmallStringKey& b)
{
if (a.length() != b.length())
return false;
return WTF::equal(a.characters(), b.characters(), a.length());
}
}
#endif