summaryrefslogtreecommitdiff
path: root/Build/source/libs/graphite/src/segment/GrSlotStream.h
blob: 006191252dcec3992f9e1bcb74a3754623f9102f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
/*--------------------------------------------------------------------*//*:Ignore this sentence.
Copyright (C) 1999, 2001 SIL International. All rights reserved.

Distributable under the terms of either the Common Public License or the
GNU Lesser General Public License, as specified in the LICENSING.txt file.

File: GrSlotStream.h
Responsibility: Sharon Correll
Last reviewed: Not yet.

Description:
    Classes GrSlotStream and GrSlotState.
----------------------------------------------------------------------------------------------*/
#ifdef _MSC_VER
#pragma once
#endif
#ifndef SLOTSTREAM_INCLUDED
#define SLOTSTREAM_INCLUDED

//:End Ignore

namespace gr
{

class EngineState;
class Font;

/*----------------------------------------------------------------------------------------------
	Each GrSlotStream represents the output of one pass, which also serves as the input
	of the following pass. Hence it has two positions, a write position for when it is
	serving as an output stream, and a read position for when it is serving as an input
	stream. The TableManager holds a list of the streams.

	The reading of slot streams is complicated by two factors. First, it may be necessary
	to access the "pre-context" items of a rule, which are the slots before the current
	stream position at the point when the rule is matched. These items will not be modified 
	by the rule, but may have been modified by a previous rule in the same pass, Therefore
	we need to read these slots from the output stream, rather than the input stream, 
	because the output stream has the current slot attribute values that we want to work with.
	In order make this happen, we keep track of the read and write positions of 
	the streams at the time the current rule was matched (m_islotRuleStartRead and 
	m_islotRuleStartWrite) and whenever we try to read a slot before m_islotRuleStartRead, 
	we read the corresponding slot from the output stream instead.

	Second, a rule may explicitly set the advance position so that some items are 
	actually reprocessed (and potentially modified) by the same pass. When this happens, 
	we actually copy the slots to reprocess from the output stream back 
	into a temporary buffer (m_vpslotReproc) in the input stream, and set up the stream so 
	that the next read operations will read from the buffer.

	It gets tricky because these two mechanisms can overlap with each other, so care 
	must be taken to read from the right place: the input stream in the normal way,
	the reprocess buffer, or the output stream. See the RuleInputSlot method.

	Another messy bit of code has to do with maintaining chunks. These are used for 
	backtracking and unwinding after inserting a line-break. Adjacent streams have maps 
	showing how many slots were processed by a single rule; the maps must be consisent
	between adjacent rules. The following is an example of a pair of valid chunk maps:

				Input stream		Output stream
		Pos		  next-map			  prev-map
		0			0					0
		1			-1					-1
		2			2					2
		3			4					-1
		4			-1					3
		5			-1					-1
		6			7					-1
		7			-1					6
		8			-1					-1

	A -1 means that slot is not at the beginning of a chunk. Otherwise, there must be a
	corresponding value in the parallel map of the adjacent stream--eg, the input stream's 
	next-map(3) == 4 and the output stream's prev-map(4) == 3. Assertions are run 
	regularly to ensure that the chunk maps stay valid.
	
	So above, the first chunk corresponds to slots 0-1 in the input and 0-1 in the output;
	two slots were processed by the rule. The second chunk corresponds to slot 2 
	in the input and slots 2-3 in the output (a slot was inserted). 
	The third corresponds to slots 3-5 in the input and 4-6 in the output (three slots
	processed). The fourth chunk covers slots 6-8 in the input and 7-8 in the output
	(one slot was deleted).

	ENHANCE SharonC: explain the effect of skipped slots on the chunk maps.

	Hungarian: sstrm
----------------------------------------------------------------------------------------------*/
class GrSlotStream {

	friend class GrSlotState;
	friend class FontMemoryUsage;

public:
	//	Constructor:
	GrSlotStream(int ipass)
	{
		m_ipass = ipass;
		m_vpslot.clear();
		m_vislotPrevChunkMap.clear();
		m_vislotNextChunkMap.clear();
		m_vpslotReproc.clear();
	}

	//	Destructor:
	~GrSlotStream()
	{
		ReleaseSlots(0, m_vpslot.size());
	}

	void ReleaseSlots(int islotMin, int islotLim)
	{
		//	A slot stream is responsible for deleting the slot states that it created,
		//	that is, the ones whose modified tag equals this stream's pass index.
		//	NO LONGER TRUE - there is a master list of them managed by the GrTableManager.
//		for (int islot = islotMin; islot < islotLim; ++islot)
//		{
//			if (m_vpslot[islot]->m_ipassModified == m_ipass)
//				delete m_vpslot[islot];
//		}
	}

	//	Getters:
	int WritePos()		{ return m_islotWritePos; }
	int ReadPos()		{ return m_islotReadPos; }
	bool FullyWritten()	{ return m_fFullyWritten; }

	int SlotsPresent()	{ return m_vpslot.size(); }

	//	Setters:
	void SetWritePos(int islot)
	{
		m_islotWritePos = islot;
	}
	void SetReadPos(int islot)
	{
		m_islotReadPos = islot;
	}

	void Initialize(int ipassPos1, bool fAnythingPrevious)
	{
		m_islotWritePos = 0;
		m_islotReadPos = 0;
		m_islotReprocPos = -1;
		m_islotReprocLim = -1;
		m_islotRuleStartRead = 0;
		m_islotRuleStartWrite = 0;
		m_islotReadPosMax = 0;
		m_cslotSkippedForResync = 0;
		m_cslotPreSeg = (fAnythingPrevious) ? -1 : 0;
		m_fFullyWritten = false;
		m_islotSegMin = -1;
		m_islotSegLim = -1;
		m_fUsedByPosPass = (m_ipass + 1 >= ipassPos1);
		m_fInputToPosPass1 = (m_ipass + 1 == ipassPos1);
		m_vpslot.clear();	// not responsible for destroying the slots
		m_vislotPrevChunkMap.clear();
		m_vislotNextChunkMap.clear();
	}

public:
	bool AtEnd();
	bool AtEndOfContext();
	void NextPut(GrSlotState* pslot);
	GrSlotState * NextGet();
	GrSlotState * Peek(int dislot = 0);
	GrSlotState * PeekBack(int dislot, bool fNullOkay = false);
	void Skip(int = 1);
	GrSlotState * SlotAt(int islot)
	{
		return m_vpslot[islot];
	}

	int OutputOfPass()
	{
		return m_ipass;
	}

	//	This function is intended to be used within positioning passes where there is a
	//	one-to-one correspondence beween the slots in consecutive streams.
	GrSlotState * OutputSlotAt(int islot)
	{
		return Peek(islot - ReadPosForNextGet());
	}

	int ReadPosForNextGet()
	{
		return ReadPos() - SlotsToReprocess();
	}

	//	Only valid for streams that are the input or output to a positioning pass.
	GrSlotState * SlotAtPosPassIndex(int islot)
	{
		Assert(GotIndexOffset());
		Assert(m_fUsedByPosPass);
		return m_vpslot[islot + m_cslotPreSeg];
	}

	//	Only valid for streams that are the input or output to a positioning pass.
	bool HasSlotAtPosPassIndex(int islot)
	{
		Assert(GotIndexOffset());
		Assert(m_fUsedByPosPass);
		return (signed(m_vpslot.size()) > (islot + m_cslotPreSeg));
	}

	bool AssertValid();
	void AssertChunkMapsValid(GrSlotStream * psstrmOut);
	void AssertStreamIndicesValid(GrSlotStream * psstrmIn);
	void AssertAttachmentsInOutput(int islotMin, int islotLim);
	bool NoReproc()
	{
		return (m_islotReprocPos == -1 || (m_islotReprocPos >= signed(m_vpslotReproc.size())));
	}

	int SlotsPending();
	int SlotsPendingInContext();
	int TotalSlotsPending();
	bool PastEndOfPositioning(bool fOutput);

	void ClearReprocBuffer()
	{
		m_islotReprocPos = -1;
		m_islotReprocLim = -1;
		m_vpslotReproc.clear();
	}

	int ReprocLim()
	{
		return m_islotReprocLim;
	}

	int ReprocMin()
	{
		Assert(m_islotReprocPos > -1);
		return m_islotReprocLim - m_vpslot.size();
	}

	int RuleStartReadPos()
	{
		return m_islotRuleStartRead;
	}

	void SetRuleStartReadPos()
	{
		if (m_islotReprocPos > -1)
			m_islotRuleStartRead = m_islotReprocLim - SlotsToReprocess();
		else
			m_islotRuleStartRead = m_islotReadPos;
	}

	int RuleStartWritePos()
	{
		return m_islotRuleStartWrite;
	}

	void SetRuleStartWritePos()
	{
		m_islotRuleStartWrite = m_islotWritePos;
	}

	void MarkFullyWritten();

	void SetSegMin(int islot, bool fAdjusting = false)
	{
		Assert(fAdjusting || m_islotSegMin == -1 || m_islotSegMin == islot);
		m_islotSegMin = islot;
	}
	void SetSegMinToWritePos(bool fMod = true)
	{
		if (m_islotSegMin == -1)
			m_islotSegMin = m_islotWritePos;
		else
			Assert(m_islotSegMin <= m_islotWritePos); // eg, already set to before the initial LB
	}
	int SegMin()
	{
		return m_islotSegMin;
	}
	void SetSegLim(int islot)
	{
		m_islotSegLim = islot;
	}
	void SetSegLimToWritePos(bool fMod = true)
	{
		if (m_islotSegLim > -1 && !fMod)
		{
			Assert(m_islotSegLim <= m_islotWritePos);
			return;
		}
		m_islotSegLim = m_islotWritePos;
	}
	int SegLimIfKnown()
	{
		return m_islotSegLim;
	}
	int FinalSegLim()
	{
		if (m_islotSegLim > -1)
			return m_islotSegLim;
		else
			return m_islotWritePos;
	}

	// Return true if we are exactly at the segment min.
	bool AtSegMin()
	{
		if (m_islotSegMin == -1)
			return false;
		return (m_islotSegMin == ReadPosForNextGet());
	}

	// Return true if we are exactly at the segment lim.
	bool AtSegLim()
	{
		if (m_islotSegLim == -1)
			return false;
		return (m_islotSegLim == ReadPosForNextGet());
	}

	int ReadPosMax()
	{
		return m_islotReadPosMax;
	}
	void SetReadPosMax(int islot)
	{
		m_islotReadPosMax = std::max(m_islotReadPosMax, islot);
	}

	int OldDirLevelRange(EngineState * pengst, int islotStart, int nTopDirection);
	int DirLevelRange(EngineState * pengst, int islotStart, int nTopDirection,
		std::vector<int> & vislotStarts, std::vector<int> & vislotStops);
	int GetSlotDirLevel(EngineState * pengst, int islot,
		int nOuterLevel, int nCurrLevel, DirCode dircMarker, DirCode dircOverride);
	DirCode AdjacentStrongCode(EngineState * pengst, int islot, int nInc,
		DirCode dircPDF, bool fNumbersAreStrong = false);
	DirCode TerminatorSequence(EngineState * pengst, int islot, int nInc, DirCode dircPDF);
	DirCode AdjacentNonBndNeutralCode(EngineState * pengst, int islot, int nInc,
		DirCode dircPDF);

	void CopyOneSlotFrom(GrSlotStream * psstrmPrev);
	void SimpleCopyFrom(GrSlotStream * psstrmI, int islotInput, int islotOutput);

	bool MoreSpace(GrTableManager * ptman,
		float xsSpaceAllotted, bool fWidthIsCharCount,
		bool fIgnoreTrailingWS, TrWsHandling twsh,
		float * pxsWidth);

	int InsertLineBreak(GrTableManager * ptman,
		int islotPrevBreak, bool fInsertedLB, int islotStartTry,
		LineBrk lb, TrWsHandling twsh, int islotMin, LineBrk * plbNextToTry);
	int MakeSegmentBreak(GrTableManager * ptman,
		int islotPrevBreak, bool fInsertedLB, int islotStartTry,
		LineBrk lb, TrWsHandling twsh, int islotMin, LineBrk * plbNextToTry);
	bool FindSegmentEnd(GrTableManager * ptman,
		int islotStartTry, LineBrk lb, TrWsHandling twsh, int islotMin,
		int * pislot, int * pichwSegOffset,
		LineBrk * plbFound, LineBrk * plbNextToTry);
	bool HasEarlierBetterBreak(int islotBreak, LineBrk lbFound, gid16 chwLB);
	LineBrk BreakWeightAt(gid16 chwLB, int islot);
	void AppendLineBreak(GrTableManager *, GrCharStream * pchstrm,
		LineBrk, DirCode dirc, int islot, bool fInitial, int ichwSegOffset);
	int FindFinalLineBreak(gid16 chwLB, int islotMin, int islotLim);
	void AdjustPrevStreamNextChunkMap(GrTableManager * ptman, int islotMod, int nInc);
	void AdjustNextChunkMap(int islotMin, int islotMod, int nInc);

	void EnsureLocalCopy(GrTableManager * ptman, GrSlotState * pslot, GrSlotStream * psstrmIn);
	void ReplaceSlotInReprocessBuffer(GrSlotState * pslotOld, GrSlotState * pslotNew);

	void ZapCalculatedDirLevels(int islotLB);

	int MaxClusterSlot(int islotChunkLim)
	{
		return MaxClusterSlot(islotChunkLim - 1, islotChunkLim);
	}
	int MaxClusterSlot(int islotChunkMin, int islotChunkLim);

	void UnwindInput( int islot, bool fPreBidiPass);
	void UnwindOutput(int islot, bool fOutputOfPosPass);

	void MapInputChunk( int islotIn, int islotOut, int islotInLim,
		bool fReprocessing, bool fBackingUp);
	void MapOutputChunk(int islotOut, int islotIn, int islotOutLim,
		bool fReprocessing, int cslotReprocess, bool fBackingUp);

	int LastNextChunkLength();

	int ChunkInPrev(int i)
	{
		Assert(i >= 0);
		Assert(i < signed(m_vislotPrevChunkMap.size()));
		Assert(i < m_islotWritePos);
		return m_vislotPrevChunkMap[i];
	}
	int ChunkInNext(int i)
	{
		Assert(i >= 0);
		Assert(i < signed(m_vislotNextChunkMap.size()));
		Assert(i < m_islotReadPos);
		return m_vislotNextChunkMap[i];
	}

	int ChunkInNextMin(int islot);
	int ChunkInNextLim(int islot);

	void ResyncSkip(int nslot);
	int SlotsSkippedToResync()
	{
		return m_cslotSkippedForResync;
	}
	void ClearSlotsSkippedToResync()
	{
		m_cslotSkippedForResync = 0;
	}

	int IndexOffset()
	{
		Assert(GotIndexOffset());	// should have been calculated
		return m_cslotPreSeg;
	}
	void SetIndexOffset(int c)
	{
		m_cslotPreSeg = c;
	}
	bool GotIndexOffset()
	{
		return (m_cslotPreSeg >= 0);
	}
	void CalcIndexOffset(GrTableManager * ptman);

	void SetPosForNextRule(int nslot, GrSlotStream * psstrmInput, bool fOutputOfPosPass);
	void SetLBContextFlag(GrTableManager * ptman, int islotStart);
	int SlotsToReprocess();

	void SetNeutralAssociations(gid16 chwLB);
protected:
	GrSlotState * FindAssociatedSlot(int islot, int nInc, gid16 chwLB);

	GrSlotState * GetSlotAt(int islot);

public:
	//	Used by the action code:
	void BackupReadPos(int cslot = 1)
	{
		for (int islot = 0; islot < cslot; islot++)
		{
			if (m_islotReprocLim > -1 && m_islotReprocLim == m_islotReadPos)
			{
				if (m_islotReprocPos == -1)
					m_islotReprocPos = m_vpslotReproc.size() - 1;
				else
					--m_islotReprocPos;
				Assert(m_islotReprocPos >= 0);
			}
			else
				--m_islotReadPos;
		}
	}

	GrSlotState * RuleInputSlot(int dislot = 0, GrSlotStream * psstrmOut = NULL,
		bool fNullOkay = false);
	GrSlotState * RuleOutputSlot(int dislot = 0);

	void PutSlotAt(GrSlotState * pslot, int islot)
	{
		//	Currently only used for the final pass, when applying justify.width to
		//	the advance width.
		Assert(signed(m_vpslot.size()) > islot);
		Assert(WritePos() > islot);
		Assert(ReadPos() <= islot);
		Assert(SlotsToReprocess() == 0);
		m_vpslot[islot] = pslot;
	}

protected:
	//	Instance variables:
	int		m_ipass;	// which pass this stream is serving as OUTPUT of

	std::vector<GrSlotState*> m_vpslot;			// the slots being processed

	//	Chunking information:
	std::vector<int> m_vislotPrevChunkMap;	// chunk mapping into previous stream;
											// -1 if not at a chunk boundary
											// (this information is needed for
											// reinitialization after inserting a line break)

	std::vector<int> m_vislotNextChunkMap;	// chunk mapping into next stream, or -1
											// (this information is needed for backtracking)

	//	Since most streams serve as both input and output, there are two positions.
	//	Note that readPos <= writePos always.
	int m_islotWritePos;	// where previous pass should put next output
	int m_islotReadPos;		// where following pass should start processing from

	//	When we are reprocessing some of the output from the next stream:
	std::vector<GrSlotState*> m_vpslotReproc;	// temporary buffer
	int m_islotReprocLim;	// the read position corresponding to the end of the reprocess
							// buffer; -1 if buffer is invalid
	int m_islotReprocPos;	// index into m_vpslotReproc from where we should read next;
							// -1 if we are not reprocessing

	int m_islotRuleStartRead;	// read position when the rule was started; any requests before
								// this point should access the output stream, because it has 
								// the most up-to-date values.
	int m_islotRuleStartWrite;	// write position when the rule started

	bool m_fFullyWritten;	// set to true when we can't get any more from the
							// previous pass; initially false.

	int m_islotSegMin;	// the official min of the segment; -1 if not yet set
	int m_islotSegLim;	// the official lim of the segment; -1 if not yet set
						// (only set for the output of the final line-break pass
						// and those beyond)

	int m_cslotSkippedForResync;	// number of slots skipped to resync;
									// specifically, the number of slots' worth of the
									// next-chunk-map that are invalid, not mapped
									// to any slots in the following stream

	int m_cslotPreSeg;	// number of slots previous to the official beginning of the segment;
						// -1 if we haven't figured it out yet. Used for keeping streams
						// in the positioning pass in sync; not particularly useful for
						// substitution passes, and will possibly be left uncalculated.

	bool m_fUsedByPosPass;		// true if this is input to or output from a positioning pass
	bool m_fInputToPosPass1;	// true if this is the input to the first positioning pass

	//	Max value of m_islotReadPos; used for recognizing infinite loops and forcibly
	//	advancing (see GrPass::CheckInputProgress).
	int m_islotReadPosMax;

public:
	//	For transduction logging:
#ifdef TRACING
//	void LogSlotGlyphs(std::ofstream & strmOut, gid16 chwLB);
#endif // TRACING

	//	For test procedures:
	int NumberOfSlots() { return m_vpslot.size(); }
	GrSlotState * LastSlotWritten()
	{
		if (m_islotWritePos == 0)
			return NULL;
		else
			return m_vpslot[m_islotWritePos-1];
	}
	GrSlotState * LastSlotRead()
	{
		if (m_islotReadPos == 0)
			return NULL;
		else
			return m_vpslot[m_islotReadPos-1];
	}

	gid16 GlyphIDAt(int islot)
	{
		return m_vpslot[islot]->m_chwGlyphID;
	}

	int PreChunk()	// answer the number of prepended items that are not in any chunk;
					// should be obsolete now
	{
		int islot;
		for (islot = 0; islot < m_islotReadPos; ++islot)
		{
			if (m_vislotNextChunkMap[islot] != -1)
				return islot;
		}
		Assert(false);
		return islot;
	}

};	// end of class GrSlotStream

} // namespace gr


#endif // !SLOTSTREAM_INCLUDED