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 */
27 #include <cstdlib>
28 #include "graphite2/Segment.h"
29 #include "inc/debug.h"
30 #include "inc/Endian.h"
31 #include "inc/Silf.h"
32 #include "inc/Segment.h"
33 #include "inc/Rule.h"
34 #include "inc/Error.h"
37 using namespace graphite2;
39 namespace { static const uint32 ERROROFFSET = 0xFFFFFFFF; }
41 Silf::Silf() throw()
42 : m_passes(0),
43 m_pseudos(0),
44 m_classOffsets(0),
45 m_classData(0),
46 m_justs(0),
47 m_numPasses(0),
48 m_numJusts(0),
49 m_sPass(0),
50 m_pPass(0),
51 m_jPass(0),
52 m_bPass(0),
53 m_flags(0),
54 m_aPseudo(0),
55 m_aBreak(0),
56 m_aUser(0),
57 m_aBidi(0),
58 m_aMirror(0),
59 m_aPassBits(0),
60 m_iMaxComp(0),
61 m_aLig(0),
62 m_numPseudo(0),
63 m_nClass(0),
64 m_nLinear(0),
65 m_gEndLine(0)
66 {
67 memset(&m_silfinfo, 0, sizeof m_silfinfo);
68 }
70 Silf::~Silf() throw()
71 {
72 releaseBuffers();
73 }
75 void Silf::releaseBuffers() throw()
76 {
77 delete [] m_passes;
78 delete [] m_pseudos;
79 free(m_classOffsets);
80 free(m_classData);
81 free(m_justs);
82 m_passes= 0;
83 m_pseudos = 0;
84 m_classOffsets = 0;
85 m_classData = 0;
86 m_justs = 0;
87 }
90 bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, uint32 version)
91 {
92 const byte * p = silf_start,
93 * const silf_end = p + lSilf;
94 Error e;
96 if (version >= 0x00030000)
97 {
98 if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
99 be::skip<int32>(p); // ruleVersion
100 be::skip<uint16>(p,2); // passOffset & pseudosOffset
101 }
102 else if (e.test(lSilf < 20, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
103 const uint16 maxGlyph = be::read<uint16>(p);
104 m_silfinfo.extra_ascent = be::read<uint16>(p);
105 m_silfinfo.extra_descent = be::read<uint16>(p);
106 m_numPasses = be::read<uint8>(p);
107 m_sPass = be::read<uint8>(p);
108 m_pPass = be::read<uint8>(p);
109 m_jPass = be::read<uint8>(p);
110 m_bPass = be::read<uint8>(p);
111 m_flags = be::read<uint8>(p);
112 be::skip<uint8>(p,2); // max{Pre,Post}Context.
113 m_aPseudo = be::read<uint8>(p);
114 m_aBreak = be::read<uint8>(p);
115 m_aBidi = be::read<uint8>(p);
116 m_aMirror = be::read<uint8>(p);
117 m_aPassBits = be::read<uint8>(p);
119 // Read Justification levels.
120 m_numJusts = be::read<uint8>(p);
121 if (e.test(maxGlyph >= face.glyphs().numGlyphs(), E_BADMAXGLYPH)
122 || e.test(p + m_numJusts * 8 >= silf_end, E_BADNUMJUSTS))
123 {
124 releaseBuffers(); return face.error(e);
125 }
127 if (m_numJusts)
128 {
129 m_justs = gralloc<Justinfo>(m_numJusts);
130 if (e.test(!m_justs, E_OUTOFMEM)) return face.error(e);
132 for (uint8 i = 0; i < m_numJusts; i++)
133 {
134 ::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]);
135 be::skip<byte>(p,8);
136 }
137 }
139 if (e.test(p + sizeof(uint16) + sizeof(uint8)*8 >= silf_end, E_BADENDJUSTS)) { releaseBuffers(); return face.error(e); }
140 m_aLig = be::read<uint16>(p);
141 m_aUser = be::read<uint8>(p);
142 m_iMaxComp = be::read<uint8>(p);
143 be::skip<byte>(p,5); // direction and 4 reserved bytes
144 be::skip<uint16>(p, be::read<uint8>(p)); // don't need critical features yet
145 be::skip<byte>(p); // reserved
146 if (e.test(p >= silf_end, E_BADCRITFEATURES)) { releaseBuffers(); return face.error(e); }
147 be::skip<uint32>(p, be::read<uint8>(p)); // don't use scriptTag array.
148 if (e.test(p + sizeof(uint16) + sizeof(uint32) >= silf_end, E_BADSCRIPTTAGS)) { releaseBuffers(); return face.error(e); }
149 m_gEndLine = be::read<uint16>(p); // lbGID
150 const byte * o_passes = p,
151 * const passes_start = silf_start + be::read<uint32>(p);
153 const size_t num_attrs = face.glyphs().numAttrs();
154 if (e.test(m_aPseudo >= num_attrs, E_BADAPSEUDO)
155 || e.test(m_aBreak >= num_attrs, E_BADABREAK)
156 || e.test(m_aBidi >= num_attrs, E_BADABIDI)
157 || e.test(m_aMirror>= num_attrs, E_BADAMIRROR)
158 || e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= silf_end, E_BADPASSESSTART)
159 || e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS)
160 || e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS)
161 || e.test((m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)), E_BADBPASS)
162 || e.test(m_aLig > 127, E_BADALIG))
163 {
164 releaseBuffers();
165 return face.error(e);
166 }
167 be::skip<uint32>(p, m_numPasses);
168 if (e.test(p + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
169 m_numPseudo = be::read<uint16>(p);
170 be::skip<uint16>(p, 3); // searchPseudo, pseudoSelector, pseudoShift
171 if (e.test(p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO))
172 {
173 releaseBuffers(); return face.error(e);
174 }
175 m_pseudos = new Pseudo[m_numPseudo];
176 for (int i = 0; i < m_numPseudo; i++)
177 {
178 m_pseudos[i].uid = be::read<uint32>(p);
179 m_pseudos[i].gid = be::read<uint16>(p);
180 }
182 const size_t clen = readClassMap(p, passes_start - p, version, e);
183 if (e || e.test(p + clen > passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
185 m_passes = new Pass[m_numPasses];
186 for (size_t i = 0; i < m_numPasses; ++i)
187 {
188 const byte * const pass_start = silf_start + be::read<uint32>(o_passes),
189 * const pass_end = silf_start + be::peek<uint32>(o_passes);
190 face.error_context((face.error_context() & 0xFF00) + EC_ASILF + (i << 16));
191 if (e.test(pass_start > pass_end, E_BADPASSSTART) || e.test(pass_end > silf_end, E_BADPASSEND)) {
192 releaseBuffers(); return face.error(e);
193 }
195 m_passes[i].init(this);
196 if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face, e))
197 {
198 releaseBuffers();
199 return false;
200 }
201 }
203 // fill in gr_faceinfo
204 m_silfinfo.upem = face.glyphs().unitsPerEm();
205 m_silfinfo.has_bidi_pass = (m_bPass != 0xFF);
206 m_silfinfo.justifies = (m_numJusts != 0) || (m_jPass < m_pPass);
207 m_silfinfo.line_ends = (m_flags & 1);
208 m_silfinfo.space_contextuals = gr_faceinfo::gr_space_contextuals((m_flags >> 2) & 0x7);
209 return true;
210 }
212 template<typename T> inline uint32 Silf::readClassOffsets(const byte *&p, size_t data_len, Error &e)
213 {
214 const T cls_off = 2*sizeof(uint16) + sizeof(T)*(m_nClass+1);
215 const size_t max_off = (be::peek<T>(p + sizeof(T)*m_nClass) - cls_off)/sizeof(uint16);
216 // Check that the last+1 offset is less than or equal to the class map length.
217 if (e.test(be::peek<T>(p) != cls_off, E_MISALIGNEDCLASSES)
218 || e.test(max_off > (data_len - cls_off)/sizeof(uint16), E_HIGHCLASSOFFSET))
219 return ERROROFFSET;
221 // Read in all the offsets.
222 m_classOffsets = gralloc<uint32>(m_nClass+1);
223 if (e.test(!m_classOffsets, E_OUTOFMEM)) return ERROROFFSET;
224 for (uint32 * o = m_classOffsets, * const o_end = o + m_nClass + 1; o != o_end; ++o)
225 {
226 *o = (be::read<T>(p) - cls_off)/sizeof(uint16);
227 if (e.test(*o > max_off, E_HIGHCLASSOFFSET))
228 return ERROROFFSET;
229 }
230 return max_off;
231 }
233 size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version, Error &e)
234 {
235 if (e.test(data_len < sizeof(uint16)*2, E_BADCLASSSIZE)) return ERROROFFSET;
237 m_nClass = be::read<uint16>(p);
238 m_nLinear = be::read<uint16>(p);
240 // Check that numLinear < numClass,
241 // that there is at least enough data for numClasses offsets.
242 if (e.test(m_nLinear > m_nClass, E_TOOMANYLINEAR)
243 || e.test((m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32) : sizeof(uint16)) > (data_len - 4), E_CLASSESTOOBIG))
244 return ERROROFFSET;
246 uint32 max_off;
247 if (version >= 0x00040000)
248 max_off = readClassOffsets<uint32>(p, data_len, e);
249 else
250 max_off = readClassOffsets<uint16>(p, data_len, e);
252 if (max_off == ERROROFFSET) return ERROROFFSET;
254 // Check the linear offsets are sane, these must be monotonically increasing.
255 for (const uint32 *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o)
256 if (e.test(o[0] > o[1], E_BADCLASSOFFSET))
257 return ERROROFFSET;
259 // Fortunately the class data is all uint16s so we can decode these now
260 m_classData = gralloc<uint16>(max_off);
261 if (e.test(!m_classData, E_OUTOFMEM)) return ERROROFFSET;
262 for (uint16 *d = m_classData, * const d_end = d + max_off; d != d_end; ++d)
263 *d = be::read<uint16>(p);
265 // Check the lookup class invariants for each non-linear class
266 for (const uint32 *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o)
267 {
268 const uint16 * lookup = m_classData + *o;
269 if (e.test(*o > max_off - 4, E_HIGHCLASSOFFSET) // LookupClass doesn't stretch over max_off
270 || e.test(lookup[0] == 0 // A LookupClass with no looks is a suspicious thing ...
271 || lookup[0] > (max_off - *o - 4)/2 // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
272 || lookup[3] != lookup[0] - lookup[1], E_BADCLASSLOOKUPINFO)) // rangeShift: numIDs - searchRange
273 return ERROROFFSET;
274 }
276 return max_off;
277 }
279 uint16 Silf::findPseudo(uint32 uid) const
280 {
281 for (int i = 0; i < m_numPseudo; i++)
282 if (m_pseudos[i].uid == uid) return m_pseudos[i].gid;
283 return 0;
284 }
286 uint16 Silf::findClassIndex(uint16 cid, uint16 gid) const
287 {
288 if (cid > m_nClass) return -1;
290 const uint16 * cls = m_classData + m_classOffsets[cid];
291 if (cid < m_nLinear) // output class being used for input, shouldn't happen
292 {
293 for (unsigned int i = 0, n = m_classOffsets[cid + 1]; i < n; ++i, ++cls)
294 if (*cls == gid) return i;
295 return -1;
296 }
297 else
298 {
299 const uint16 * min = cls + 4, // lookups array
300 * max = min + cls[0]*2; // lookups aray is numIDs (cls[0]) uint16 pairs long
301 do
302 {
303 const uint16 * p = min + (-2 & ((max-min)/2));
304 if (p[0] > gid) max = p;
305 else min = p;
306 }
307 while (max - min > 2);
308 return min[0] == gid ? min[1] : -1;
309 }
310 }
312 uint16 Silf::getClassGlyph(uint16 cid, unsigned int index) const
313 {
314 if (cid > m_nClass) return 0;
316 uint32 loc = m_classOffsets[cid];
317 if (cid < m_nLinear)
318 {
319 if (index < m_classOffsets[cid + 1] - loc)
320 return m_classData[index + loc];
321 }
322 else // input class being used for output. Shouldn't happen
323 {
324 for (unsigned int i = loc + 4; i < m_classOffsets[cid + 1]; i += 2)
325 if (m_classData[i + 1] == index) return m_classData[i];
326 }
327 return 0;
328 }
331 bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const
332 {
333 assert(seg != 0);
334 SlotMap map(*seg);
335 FiniteStateMachine fsm(map, seg->getFace()->logger());
336 vm::Machine m(map);
337 unsigned int initSize = seg->slotCount();
338 uint8 lbidi = m_bPass;
339 #if !defined GRAPHITE2_NTRACING
340 json * const dbgout = seg->getFace()->logger();
341 #endif
343 if (lastPass == 0)
344 {
345 if (firstPass == lastPass && lbidi == 0xFF)
346 return true;
347 lastPass = m_numPasses;
348 }
349 if (firstPass <= lbidi && lastPass >= lbidi && dobidi)
350 lastPass++;
351 else
352 lbidi = 0xFF;
354 for (size_t i = firstPass; i < lastPass; ++i)
355 {
356 // bidi and mirroring
357 if (i == lbidi)
358 {
359 #if !defined GRAPHITE2_NTRACING
360 if (dbgout)
361 {
362 *dbgout << json::item << json::object
363 << "id" << -1
364 << "slots" << json::array;
365 seg->positionSlots(0);
366 for(Slot * s = seg->first(); s; s = s->next())
367 *dbgout << dslot(seg, s);
368 *dbgout << json::close
369 << "rules" << json::array << json::close
370 << json::close;
371 }
372 #endif
374 if (!(seg->dir() & 2))
375 seg->bidiPass(m_aBidi, seg->dir() & 1, m_aMirror);
376 else if (m_aMirror)
377 {
378 Slot * s;
379 for (s = seg->first(); s; s = s->next())
380 {
381 unsigned short g = seg->glyphAttr(s->gid(), m_aMirror);
382 if (g && (!(seg->dir() & 4) || !seg->glyphAttr(s->gid(), m_aMirror + 1)))
383 s->setGlyph(seg, g);
384 }
385 }
386 --i;
387 --lastPass;
388 lbidi = 0xFF;
389 continue;
390 }
392 #if !defined GRAPHITE2_NTRACING
393 if (dbgout)
394 {
395 *dbgout << json::item << json::object
396 << "id" << i+1
397 << "slots" << json::array;
398 seg->positionSlots(0);
399 for(Slot * s = seg->first(); s; s = s->next())
400 *dbgout << dslot(seg, s);
401 *dbgout << json::close;
402 }
403 #endif
405 // test whether to reorder, prepare for positioning
406 if (i >= 32 || (seg->passBits() & (1 << i)) == 0)
407 m_passes[i].runGraphite(m, fsm);
408 // only subsitution passes can change segment length, cached subsegments are short for their text
409 if (m.status() != vm::Machine::finished
410 || (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR
411 || (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize))))
412 return false;
413 }
414 return true;
415 }