Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* GRAPHITE2 LICENSING
3 Copyright 2010, SIL International
4 All rights reserved.
6 This library is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should also have received a copy of the GNU Lesser General Public
17 License along with this library in the file named "LICENSE".
18 If not, write to the Free Software Foundation, 51 Franklin Street,
19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
20 internet at http://www.fsf.org/licenses/lgpl.html.
22 Alternatively, the contents of this file may be used under the terms of the
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
24 License, as published by the Free Software Foundation, either version 2
25 of the License or (at your option) any later version.
26 */
28 #include "inc/Main.h"
29 #include "inc/TtfTypes.h"
30 #include "inc/TtfUtil.h"
31 #include "inc/SegCache.h"
32 #include "inc/SegCacheEntry.h"
33 #include "inc/SegCacheStore.h"
34 #include "inc/CmapCache.h"
37 using namespace graphite2;
39 #ifndef GRAPHITE2_NSEGCACHE
41 SegCache::SegCache(const SegCacheStore * store, const Features & feats)
42 : m_prefixLength(ePrefixLength),
43 m_maxCachedSegLength(eMaxSpliceSize),
44 m_segmentCount(0),
45 m_features(feats),
46 m_totalAccessCount(0l), m_totalMisses(0l),
47 m_purgeFactor(1.0f / (ePurgeFactor * store->maxSegmentCount()))
48 {
49 m_prefixes.raw = grzeroalloc<void*>(store->maxCmapGid() + 2);
50 m_prefixes.range[SEG_CACHE_MIN_INDEX] = SEG_CACHE_UNSET_INDEX;
51 m_prefixes.range[SEG_CACHE_MAX_INDEX] = SEG_CACHE_UNSET_INDEX;
52 }
54 void SegCache::freeLevel(SegCacheStore * store, SegCachePrefixArray prefixes, size_t level)
55 {
56 for (size_t i = 0; i < store->maxCmapGid(); i++)
57 {
58 if (prefixes.array[i].raw)
59 {
60 if (level + 1 < ePrefixLength)
61 freeLevel(store, prefixes.array[i], level + 1);
62 else
63 {
64 SegCachePrefixEntry * prefixEntry = prefixes.prefixEntries[i];
65 delete prefixEntry;
66 }
67 }
68 }
69 free(prefixes.raw);
70 }
72 void SegCache::clear(SegCacheStore * store)
73 {
74 freeLevel(store, m_prefixes, 0);
75 m_prefixes.raw = NULL;
76 }
78 SegCache::~SegCache()
79 {
80 assert(m_prefixes.raw == NULL);
81 }
83 SegCacheEntry* SegCache::cache(SegCacheStore * store, const uint16* cmapGlyphs, size_t length, Segment * seg, size_t charOffset)
84 {
85 uint16 pos = 0;
86 if (!length) return NULL;
87 assert(length < m_maxCachedSegLength);
88 SegCachePrefixArray pArray = m_prefixes;
89 while (pos + 1 < m_prefixLength)
90 {
91 uint16 gid = (pos < length)? cmapGlyphs[pos] : 0;
92 if (!pArray.array[gid].raw)
93 {
94 pArray.array[gid].raw = grzeroalloc<void*>(store->maxCmapGid() + 2);
95 if (!pArray.array[gid].raw)
96 return NULL; // malloc failed
97 if (pArray.range[SEG_CACHE_MIN_INDEX] == SEG_CACHE_UNSET_INDEX)
98 {
99 pArray.range[SEG_CACHE_MIN_INDEX] = gid;
100 pArray.range[SEG_CACHE_MAX_INDEX] = gid;
101 }
102 else
103 {
104 if (gid < pArray.range[SEG_CACHE_MIN_INDEX])
105 pArray.range[SEG_CACHE_MIN_INDEX] = gid;
106 else if (gid > pArray.range[SEG_CACHE_MAX_INDEX])
107 pArray.range[SEG_CACHE_MAX_INDEX] = gid;
108 }
109 }
110 pArray = pArray.array[gid];
111 ++pos;
112 }
113 uint16 gid = (pos < length)? cmapGlyphs[pos] : 0;
114 SegCachePrefixEntry * prefixEntry = pArray.prefixEntries[gid];
115 if (!prefixEntry)
116 {
117 prefixEntry = new SegCachePrefixEntry();
118 pArray.prefixEntries[gid] = prefixEntry;
119 if (pArray.range[SEG_CACHE_MIN_INDEX] == SEG_CACHE_UNSET_INDEX)
120 {
121 pArray.range[SEG_CACHE_MIN_INDEX] = gid;
122 pArray.range[SEG_CACHE_MAX_INDEX] = gid;
123 }
124 else
125 {
126 if (gid < pArray.range[SEG_CACHE_MIN_INDEX])
127 pArray.range[SEG_CACHE_MIN_INDEX] = gid;
128 else if (gid > pArray.range[SEG_CACHE_MAX_INDEX])
129 pArray.range[SEG_CACHE_MAX_INDEX] = gid;
130 }
131 }
132 if (!prefixEntry) return NULL;
133 // if the cache is full run a purge - this is slow, since it walks the tree
134 if (m_segmentCount + 1 > store->maxSegmentCount())
135 {
136 purge(store);
137 assert(m_segmentCount < store->maxSegmentCount());
138 }
139 SegCacheEntry * pEntry = prefixEntry->cache(cmapGlyphs, length, seg, charOffset, m_totalAccessCount);
140 if (pEntry) ++m_segmentCount;
141 return pEntry;
142 }
144 void SegCache::purge(SegCacheStore * store)
145 {
146 unsigned long long minAccessCount = static_cast<unsigned long long>(m_totalAccessCount * m_purgeFactor + 1);
147 if (minAccessCount < 2) minAccessCount = 2;
148 unsigned long long oldAccessTime = m_totalAccessCount - store->maxSegmentCount() / eAgeFactor;
149 purgeLevel(store, m_prefixes, 0, minAccessCount, oldAccessTime);
150 }
152 void SegCache::purgeLevel(SegCacheStore * store, SegCachePrefixArray prefixes, size_t level,
153 unsigned long long minAccessCount, unsigned long long oldAccessTime)
154 {
155 if (prefixes.range[SEG_CACHE_MIN_INDEX] == SEG_CACHE_UNSET_INDEX) return;
156 size_t maxGlyphCached = prefixes.range[SEG_CACHE_MAX_INDEX];
157 for (size_t i = prefixes.range[SEG_CACHE_MIN_INDEX]; i <= maxGlyphCached; i++)
158 {
159 if (prefixes.array[i].raw)
160 {
161 if (level + 1 < ePrefixLength)
162 purgeLevel(store, prefixes.array[i], level + 1, minAccessCount, oldAccessTime);
163 else
164 {
165 SegCachePrefixEntry * prefixEntry = prefixes.prefixEntries[i];
166 m_segmentCount -= prefixEntry->purge(minAccessCount,
167 oldAccessTime, m_totalAccessCount);
168 }
169 }
170 }
171 }
173 uint32 SegCachePrefixEntry::purge(unsigned long long minAccessCount,
174 unsigned long long oldAccessTime,
175 unsigned long long currentTime)
176 {
177 // ignore the purge request if another has been done recently
178 //if (m_lastPurge > oldAccessTime)
179 // return 0;
181 uint32 totalPurged = 0;
182 // real length is length + 1 in this loop
183 for (uint16 length = 0; length < eMaxSpliceSize; length++)
184 {
185 if (m_entryCounts[length] == 0)
186 continue;
187 uint16 purgeCount = 0;
188 uint16 newIndex = 0;
189 for (uint16 j = 0; j < m_entryCounts[length]; j++)
190 {
191 SegCacheEntry & tempEntry = m_entries[length][j];
192 // purge entries with a low access count which haven't been
193 // accessed recently
194 if (tempEntry.accessCount() <= minAccessCount &&
195 tempEntry.lastAccess() <= oldAccessTime)
196 {
197 tempEntry.clear();
198 ++purgeCount;
199 }
200 else
201 {
202 memcpy(m_entries[length] + newIndex++, m_entries[length] + j, sizeof(SegCacheEntry));
203 }
204 }
205 if (purgeCount == m_entryCounts[length])
206 {
207 assert(newIndex == 0);
208 m_entryCounts[length] = 0;
209 m_entryBSIndex[length] = 0;
210 free(m_entries[length]);
211 m_entries[length] = NULL;
212 }
213 else if (purgeCount > 0)
214 {
215 assert(m_entryCounts[length] == newIndex + purgeCount);
216 m_entryCounts[length] = newIndex;
217 }
218 totalPurged += purgeCount;
219 }
220 m_lastPurge = currentTime;
221 return totalPurged;
222 }
224 #endif