xref: /MusicPlayer2/scintilla/src/PositionCache.cxx (revision 8af74909132ed5e696cb05b6689ae4baf14c1c96)
1*8af74909SZhong Yang // Scintilla source code edit control
2*8af74909SZhong Yang /** @file PositionCache.cxx
3*8af74909SZhong Yang  ** Classes for caching layout information.
4*8af74909SZhong Yang  **/
5*8af74909SZhong Yang // Copyright 1998-2007 by Neil Hodgson <[email protected]>
6*8af74909SZhong Yang // The License.txt file describes the conditions under which this software may be distributed.
7*8af74909SZhong Yang 
8*8af74909SZhong Yang #include <cstddef>
9*8af74909SZhong Yang #include <cstdlib>
10*8af74909SZhong Yang #include <cstring>
11*8af74909SZhong Yang #include <cmath>
12*8af74909SZhong Yang 
13*8af74909SZhong Yang #include <stdexcept>
14*8af74909SZhong Yang #include <string>
15*8af74909SZhong Yang #include <string_view>
16*8af74909SZhong Yang #include <vector>
17*8af74909SZhong Yang #include <map>
18*8af74909SZhong Yang #include <algorithm>
19*8af74909SZhong Yang #include <iterator>
20*8af74909SZhong Yang #include <memory>
21*8af74909SZhong Yang 
22*8af74909SZhong Yang #include "Platform.h"
23*8af74909SZhong Yang 
24*8af74909SZhong Yang #include "ILoader.h"
25*8af74909SZhong Yang #include "ILexer.h"
26*8af74909SZhong Yang #include "Scintilla.h"
27*8af74909SZhong Yang 
28*8af74909SZhong Yang #include "CharacterCategory.h"
29*8af74909SZhong Yang #include "Position.h"
30*8af74909SZhong Yang #include "UniqueString.h"
31*8af74909SZhong Yang #include "SplitVector.h"
32*8af74909SZhong Yang #include "Partitioning.h"
33*8af74909SZhong Yang #include "RunStyles.h"
34*8af74909SZhong Yang #include "ContractionState.h"
35*8af74909SZhong Yang #include "CellBuffer.h"
36*8af74909SZhong Yang #include "KeyMap.h"
37*8af74909SZhong Yang #include "Indicator.h"
38*8af74909SZhong Yang #include "LineMarker.h"
39*8af74909SZhong Yang #include "Style.h"
40*8af74909SZhong Yang #include "ViewStyle.h"
41*8af74909SZhong Yang #include "CharClassify.h"
42*8af74909SZhong Yang #include "Decoration.h"
43*8af74909SZhong Yang #include "CaseFolder.h"
44*8af74909SZhong Yang #include "Document.h"
45*8af74909SZhong Yang #include "UniConversion.h"
46*8af74909SZhong Yang #include "Selection.h"
47*8af74909SZhong Yang #include "PositionCache.h"
48*8af74909SZhong Yang 
49*8af74909SZhong Yang using namespace Scintilla;
50*8af74909SZhong Yang 
Resize(size_t maxLineLength_)51*8af74909SZhong Yang void BidiData::Resize(size_t maxLineLength_) {
52*8af74909SZhong Yang 	stylesFonts.resize(maxLineLength_ + 1);
53*8af74909SZhong Yang 	widthReprs.resize(maxLineLength_ + 1);
54*8af74909SZhong Yang }
55*8af74909SZhong Yang 
LineLayout(int maxLineLength_)56*8af74909SZhong Yang LineLayout::LineLayout(int maxLineLength_) :
57*8af74909SZhong Yang 	lenLineStarts(0),
58*8af74909SZhong Yang 	lineNumber(-1),
59*8af74909SZhong Yang 	inCache(false),
60*8af74909SZhong Yang 	maxLineLength(-1),
61*8af74909SZhong Yang 	numCharsInLine(0),
62*8af74909SZhong Yang 	numCharsBeforeEOL(0),
63*8af74909SZhong Yang 	validity(ValidLevel::invalid),
64*8af74909SZhong Yang 	xHighlightGuide(0),
65*8af74909SZhong Yang 	highlightColumn(false),
66*8af74909SZhong Yang 	containsCaret(false),
67*8af74909SZhong Yang 	edgeColumn(0),
68*8af74909SZhong Yang 	bracePreviousStyles{},
69*8af74909SZhong Yang 	hotspot(0,0),
70*8af74909SZhong Yang 	widthLine(wrapWidthInfinite),
71*8af74909SZhong Yang 	lines(1),
72*8af74909SZhong Yang 	wrapIndent(0) {
73*8af74909SZhong Yang 	Resize(maxLineLength_);
74*8af74909SZhong Yang }
75*8af74909SZhong Yang 
~LineLayout()76*8af74909SZhong Yang LineLayout::~LineLayout() {
77*8af74909SZhong Yang 	Free();
78*8af74909SZhong Yang }
79*8af74909SZhong Yang 
Resize(int maxLineLength_)80*8af74909SZhong Yang void LineLayout::Resize(int maxLineLength_) {
81*8af74909SZhong Yang 	if (maxLineLength_ > maxLineLength) {
82*8af74909SZhong Yang 		Free();
83*8af74909SZhong Yang 		chars = std::make_unique<char[]>(maxLineLength_ + 1);
84*8af74909SZhong Yang 		styles = std::make_unique<unsigned char []>(maxLineLength_ + 1);
85*8af74909SZhong Yang 		// Extra position allocated as sometimes the Windows
86*8af74909SZhong Yang 		// GetTextExtentExPoint API writes an extra element.
87*8af74909SZhong Yang 		positions = std::make_unique<XYPOSITION []>(maxLineLength_ + 1 + 1);
88*8af74909SZhong Yang 		if (bidiData) {
89*8af74909SZhong Yang 			bidiData->Resize(maxLineLength_);
90*8af74909SZhong Yang 		}
91*8af74909SZhong Yang 
92*8af74909SZhong Yang 		maxLineLength = maxLineLength_;
93*8af74909SZhong Yang 	}
94*8af74909SZhong Yang }
95*8af74909SZhong Yang 
EnsureBidiData()96*8af74909SZhong Yang void LineLayout::EnsureBidiData() {
97*8af74909SZhong Yang 	if (!bidiData) {
98*8af74909SZhong Yang 		bidiData = std::make_unique<BidiData>();
99*8af74909SZhong Yang 		bidiData->Resize(maxLineLength);
100*8af74909SZhong Yang 	}
101*8af74909SZhong Yang }
102*8af74909SZhong Yang 
Free()103*8af74909SZhong Yang void LineLayout::Free() noexcept {
104*8af74909SZhong Yang 	chars.reset();
105*8af74909SZhong Yang 	styles.reset();
106*8af74909SZhong Yang 	positions.reset();
107*8af74909SZhong Yang 	lineStarts.reset();
108*8af74909SZhong Yang 	bidiData.reset();
109*8af74909SZhong Yang }
110*8af74909SZhong Yang 
Invalidate(ValidLevel validity_)111*8af74909SZhong Yang void LineLayout::Invalidate(ValidLevel validity_) noexcept {
112*8af74909SZhong Yang 	if (validity > validity_)
113*8af74909SZhong Yang 		validity = validity_;
114*8af74909SZhong Yang }
115*8af74909SZhong Yang 
LineStart(int line) const116*8af74909SZhong Yang int LineLayout::LineStart(int line) const noexcept {
117*8af74909SZhong Yang 	if (line <= 0) {
118*8af74909SZhong Yang 		return 0;
119*8af74909SZhong Yang 	} else if ((line >= lines) || !lineStarts) {
120*8af74909SZhong Yang 		return numCharsInLine;
121*8af74909SZhong Yang 	} else {
122*8af74909SZhong Yang 		return lineStarts[line];
123*8af74909SZhong Yang 	}
124*8af74909SZhong Yang }
125*8af74909SZhong Yang 
LineLength(int line) const126*8af74909SZhong Yang int Scintilla::LineLayout::LineLength(int line) const noexcept {
127*8af74909SZhong Yang 	if (!lineStarts) {
128*8af74909SZhong Yang 		return numCharsInLine;
129*8af74909SZhong Yang 	} if (line >= lines - 1) {
130*8af74909SZhong Yang 		return numCharsInLine - lineStarts[line];
131*8af74909SZhong Yang 	} else {
132*8af74909SZhong Yang 		return lineStarts[line + 1] - lineStarts[line];
133*8af74909SZhong Yang 	}
134*8af74909SZhong Yang }
135*8af74909SZhong Yang 
LineLastVisible(int line,Scope scope) const136*8af74909SZhong Yang int LineLayout::LineLastVisible(int line, Scope scope) const noexcept {
137*8af74909SZhong Yang 	if (line < 0) {
138*8af74909SZhong Yang 		return 0;
139*8af74909SZhong Yang 	} else if ((line >= lines-1) || !lineStarts) {
140*8af74909SZhong Yang 		return scope == Scope::visibleOnly ? numCharsBeforeEOL : numCharsInLine;
141*8af74909SZhong Yang 	} else {
142*8af74909SZhong Yang 		return lineStarts[line+1];
143*8af74909SZhong Yang 	}
144*8af74909SZhong Yang }
145*8af74909SZhong Yang 
SubLineRange(int subLine,Scope scope) const146*8af74909SZhong Yang Range LineLayout::SubLineRange(int subLine, Scope scope) const noexcept {
147*8af74909SZhong Yang 	return Range(LineStart(subLine), LineLastVisible(subLine, scope));
148*8af74909SZhong Yang }
149*8af74909SZhong Yang 
InLine(int offset,int line) const150*8af74909SZhong Yang bool LineLayout::InLine(int offset, int line) const noexcept {
151*8af74909SZhong Yang 	return ((offset >= LineStart(line)) && (offset < LineStart(line + 1))) ||
152*8af74909SZhong Yang 		((offset == numCharsInLine) && (line == (lines-1)));
153*8af74909SZhong Yang }
154*8af74909SZhong Yang 
SubLineFromPosition(int posInLine,PointEnd pe) const155*8af74909SZhong Yang int LineLayout::SubLineFromPosition(int posInLine, PointEnd pe) const noexcept {
156*8af74909SZhong Yang 	if (!lineStarts || (posInLine > maxLineLength)) {
157*8af74909SZhong Yang 		return lines - 1;
158*8af74909SZhong Yang 	}
159*8af74909SZhong Yang 
160*8af74909SZhong Yang 	for (int line = 0; line < lines; line++) {
161*8af74909SZhong Yang 		if (pe & peSubLineEnd) {
162*8af74909SZhong Yang 			// Return subline not start of next
163*8af74909SZhong Yang 			if (lineStarts[line + 1] <= posInLine + 1)
164*8af74909SZhong Yang 				return line;
165*8af74909SZhong Yang 		} else {
166*8af74909SZhong Yang 			if (lineStarts[line + 1] <= posInLine)
167*8af74909SZhong Yang 				return line;
168*8af74909SZhong Yang 		}
169*8af74909SZhong Yang 	}
170*8af74909SZhong Yang 
171*8af74909SZhong Yang 	return lines - 1;
172*8af74909SZhong Yang }
173*8af74909SZhong Yang 
SetLineStart(int line,int start)174*8af74909SZhong Yang void LineLayout::SetLineStart(int line, int start) {
175*8af74909SZhong Yang 	if ((line >= lenLineStarts) && (line != 0)) {
176*8af74909SZhong Yang 		const int newMaxLines = line + 20;
177*8af74909SZhong Yang 		std::unique_ptr<int[]> newLineStarts = std::make_unique<int[]>(newMaxLines);
178*8af74909SZhong Yang 		for (int i = 0; i < newMaxLines; i++) {
179*8af74909SZhong Yang 			if (i < lenLineStarts)
180*8af74909SZhong Yang 				newLineStarts[i] = lineStarts[i];
181*8af74909SZhong Yang 			else
182*8af74909SZhong Yang 				newLineStarts[i] = 0;
183*8af74909SZhong Yang 		}
184*8af74909SZhong Yang 		lineStarts = std::move(newLineStarts);
185*8af74909SZhong Yang 		lenLineStarts = newMaxLines;
186*8af74909SZhong Yang 	}
187*8af74909SZhong Yang 	lineStarts[line] = start;
188*8af74909SZhong Yang }
189*8af74909SZhong Yang 
SetBracesHighlight(Range rangeLine,const Sci::Position braces[],char bracesMatchStyle,int xHighlight,bool ignoreStyle)190*8af74909SZhong Yang void LineLayout::SetBracesHighlight(Range rangeLine, const Sci::Position braces[],
191*8af74909SZhong Yang                                     char bracesMatchStyle, int xHighlight, bool ignoreStyle) {
192*8af74909SZhong Yang 	if (!ignoreStyle && rangeLine.ContainsCharacter(braces[0])) {
193*8af74909SZhong Yang 		const Sci::Position braceOffset = braces[0] - rangeLine.start;
194*8af74909SZhong Yang 		if (braceOffset < numCharsInLine) {
195*8af74909SZhong Yang 			bracePreviousStyles[0] = styles[braceOffset];
196*8af74909SZhong Yang 			styles[braceOffset] = bracesMatchStyle;
197*8af74909SZhong Yang 		}
198*8af74909SZhong Yang 	}
199*8af74909SZhong Yang 	if (!ignoreStyle && rangeLine.ContainsCharacter(braces[1])) {
200*8af74909SZhong Yang 		const Sci::Position braceOffset = braces[1] - rangeLine.start;
201*8af74909SZhong Yang 		if (braceOffset < numCharsInLine) {
202*8af74909SZhong Yang 			bracePreviousStyles[1] = styles[braceOffset];
203*8af74909SZhong Yang 			styles[braceOffset] = bracesMatchStyle;
204*8af74909SZhong Yang 		}
205*8af74909SZhong Yang 	}
206*8af74909SZhong Yang 	if ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) ||
207*8af74909SZhong Yang 	        (braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) {
208*8af74909SZhong Yang 		xHighlightGuide = xHighlight;
209*8af74909SZhong Yang 	}
210*8af74909SZhong Yang }
211*8af74909SZhong Yang 
RestoreBracesHighlight(Range rangeLine,const Sci::Position braces[],bool ignoreStyle)212*8af74909SZhong Yang void LineLayout::RestoreBracesHighlight(Range rangeLine, const Sci::Position braces[], bool ignoreStyle) {
213*8af74909SZhong Yang 	if (!ignoreStyle && rangeLine.ContainsCharacter(braces[0])) {
214*8af74909SZhong Yang 		const Sci::Position braceOffset = braces[0] - rangeLine.start;
215*8af74909SZhong Yang 		if (braceOffset < numCharsInLine) {
216*8af74909SZhong Yang 			styles[braceOffset] = bracePreviousStyles[0];
217*8af74909SZhong Yang 		}
218*8af74909SZhong Yang 	}
219*8af74909SZhong Yang 	if (!ignoreStyle && rangeLine.ContainsCharacter(braces[1])) {
220*8af74909SZhong Yang 		const Sci::Position braceOffset = braces[1] - rangeLine.start;
221*8af74909SZhong Yang 		if (braceOffset < numCharsInLine) {
222*8af74909SZhong Yang 			styles[braceOffset] = bracePreviousStyles[1];
223*8af74909SZhong Yang 		}
224*8af74909SZhong Yang 	}
225*8af74909SZhong Yang 	xHighlightGuide = 0;
226*8af74909SZhong Yang }
227*8af74909SZhong Yang 
FindBefore(XYPOSITION x,Range range) const228*8af74909SZhong Yang int LineLayout::FindBefore(XYPOSITION x, Range range) const noexcept {
229*8af74909SZhong Yang 	Sci::Position lower = range.start;
230*8af74909SZhong Yang 	Sci::Position upper = range.end;
231*8af74909SZhong Yang 	do {
232*8af74909SZhong Yang 		const Sci::Position middle = (upper + lower + 1) / 2; 	// Round high
233*8af74909SZhong Yang 		const XYPOSITION posMiddle = positions[middle];
234*8af74909SZhong Yang 		if (x < posMiddle) {
235*8af74909SZhong Yang 			upper = middle - 1;
236*8af74909SZhong Yang 		} else {
237*8af74909SZhong Yang 			lower = middle;
238*8af74909SZhong Yang 		}
239*8af74909SZhong Yang 	} while (lower < upper);
240*8af74909SZhong Yang 	return static_cast<int>(lower);
241*8af74909SZhong Yang }
242*8af74909SZhong Yang 
243*8af74909SZhong Yang 
FindPositionFromX(XYPOSITION x,Range range,bool charPosition) const244*8af74909SZhong Yang int LineLayout::FindPositionFromX(XYPOSITION x, Range range, bool charPosition) const noexcept {
245*8af74909SZhong Yang 	int pos = FindBefore(x, range);
246*8af74909SZhong Yang 	while (pos < range.end) {
247*8af74909SZhong Yang 		if (charPosition) {
248*8af74909SZhong Yang 			if (x < (positions[pos + 1])) {
249*8af74909SZhong Yang 				return pos;
250*8af74909SZhong Yang 			}
251*8af74909SZhong Yang 		} else {
252*8af74909SZhong Yang 			if (x < ((positions[pos] + positions[pos + 1]) / 2)) {
253*8af74909SZhong Yang 				return pos;
254*8af74909SZhong Yang 			}
255*8af74909SZhong Yang 		}
256*8af74909SZhong Yang 		pos++;
257*8af74909SZhong Yang 	}
258*8af74909SZhong Yang 	return static_cast<int>(range.end);
259*8af74909SZhong Yang }
260*8af74909SZhong Yang 
PointFromPosition(int posInLine,int lineHeight,PointEnd pe) const261*8af74909SZhong Yang Point LineLayout::PointFromPosition(int posInLine, int lineHeight, PointEnd pe) const noexcept {
262*8af74909SZhong Yang 	Point pt;
263*8af74909SZhong Yang 	// In case of very long line put x at arbitrary large position
264*8af74909SZhong Yang 	if (posInLine > maxLineLength) {
265*8af74909SZhong Yang 		pt.x = positions[maxLineLength] - positions[LineStart(lines)];
266*8af74909SZhong Yang 	}
267*8af74909SZhong Yang 
268*8af74909SZhong Yang 	for (int subLine = 0; subLine < lines; subLine++) {
269*8af74909SZhong Yang 		const Range rangeSubLine = SubLineRange(subLine, Scope::visibleOnly);
270*8af74909SZhong Yang 		if (posInLine >= rangeSubLine.start) {
271*8af74909SZhong Yang 			pt.y = static_cast<XYPOSITION>(subLine*lineHeight);
272*8af74909SZhong Yang 			if (posInLine <= rangeSubLine.end) {
273*8af74909SZhong Yang 				pt.x = positions[posInLine] - positions[rangeSubLine.start];
274*8af74909SZhong Yang 				if (rangeSubLine.start != 0)	// Wrapped lines may be indented
275*8af74909SZhong Yang 					pt.x += wrapIndent;
276*8af74909SZhong Yang 				if (pe & peSubLineEnd)	// Return end of first subline not start of next
277*8af74909SZhong Yang 					break;
278*8af74909SZhong Yang 			} else if ((pe & peLineEnd) && (subLine == (lines-1))) {
279*8af74909SZhong Yang 				pt.x = positions[numCharsInLine] - positions[rangeSubLine.start];
280*8af74909SZhong Yang 				if (rangeSubLine.start != 0)	// Wrapped lines may be indented
281*8af74909SZhong Yang 					pt.x += wrapIndent;
282*8af74909SZhong Yang 			}
283*8af74909SZhong Yang 		} else {
284*8af74909SZhong Yang 			break;
285*8af74909SZhong Yang 		}
286*8af74909SZhong Yang 	}
287*8af74909SZhong Yang 	return pt;
288*8af74909SZhong Yang }
289*8af74909SZhong Yang 
EndLineStyle() const290*8af74909SZhong Yang int LineLayout::EndLineStyle() const noexcept {
291*8af74909SZhong Yang 	return styles[numCharsBeforeEOL > 0 ? numCharsBeforeEOL-1 : 0];
292*8af74909SZhong Yang }
293*8af74909SZhong Yang 
ScreenLine(const LineLayout * ll_,int subLine,const ViewStyle & vs,XYPOSITION width_,int tabWidthMinimumPixels_)294*8af74909SZhong Yang ScreenLine::ScreenLine(
295*8af74909SZhong Yang 	const LineLayout *ll_,
296*8af74909SZhong Yang 	int subLine,
297*8af74909SZhong Yang 	const ViewStyle &vs,
298*8af74909SZhong Yang 	XYPOSITION width_,
299*8af74909SZhong Yang 	int tabWidthMinimumPixels_) :
300*8af74909SZhong Yang 	ll(ll_),
301*8af74909SZhong Yang 	start(ll->LineStart(subLine)),
302*8af74909SZhong Yang 	len(ll->LineLength(subLine)),
303*8af74909SZhong Yang 	width(width_),
304*8af74909SZhong Yang 	height(static_cast<float>(vs.lineHeight)),
305*8af74909SZhong Yang 	ctrlCharPadding(vs.ctrlCharPadding),
306*8af74909SZhong Yang 	tabWidth(vs.tabWidth),
307*8af74909SZhong Yang 	tabWidthMinimumPixels(tabWidthMinimumPixels_) {
308*8af74909SZhong Yang }
309*8af74909SZhong Yang 
~ScreenLine()310*8af74909SZhong Yang ScreenLine::~ScreenLine() {
311*8af74909SZhong Yang }
312*8af74909SZhong Yang 
Text() const313*8af74909SZhong Yang std::string_view ScreenLine::Text() const {
314*8af74909SZhong Yang 	return std::string_view(&ll->chars[start], len);
315*8af74909SZhong Yang }
316*8af74909SZhong Yang 
Length() const317*8af74909SZhong Yang size_t ScreenLine::Length() const {
318*8af74909SZhong Yang 	return len;
319*8af74909SZhong Yang }
320*8af74909SZhong Yang 
RepresentationCount() const321*8af74909SZhong Yang size_t ScreenLine::RepresentationCount() const {
322*8af74909SZhong Yang 	return std::count_if(&ll->bidiData->widthReprs[start],
323*8af74909SZhong Yang 		&ll->bidiData->widthReprs[start + len],
324*8af74909SZhong Yang 		[](XYPOSITION w) noexcept { return w > 0.0f; });
325*8af74909SZhong Yang }
326*8af74909SZhong Yang 
Width() const327*8af74909SZhong Yang XYPOSITION ScreenLine::Width() const {
328*8af74909SZhong Yang 	return width;
329*8af74909SZhong Yang }
330*8af74909SZhong Yang 
Height() const331*8af74909SZhong Yang XYPOSITION ScreenLine::Height() const {
332*8af74909SZhong Yang 	return height;
333*8af74909SZhong Yang }
334*8af74909SZhong Yang 
TabWidth() const335*8af74909SZhong Yang XYPOSITION ScreenLine::TabWidth() const {
336*8af74909SZhong Yang 	return tabWidth;
337*8af74909SZhong Yang }
338*8af74909SZhong Yang 
TabWidthMinimumPixels() const339*8af74909SZhong Yang XYPOSITION ScreenLine::TabWidthMinimumPixels() const {
340*8af74909SZhong Yang 	return static_cast<XYPOSITION>(tabWidthMinimumPixels);
341*8af74909SZhong Yang }
342*8af74909SZhong Yang 
FontOfPosition(size_t position) const343*8af74909SZhong Yang const Font *ScreenLine::FontOfPosition(size_t position) const {
344*8af74909SZhong Yang 	return &ll->bidiData->stylesFonts[start + position];
345*8af74909SZhong Yang }
346*8af74909SZhong Yang 
RepresentationWidth(size_t position) const347*8af74909SZhong Yang XYPOSITION ScreenLine::RepresentationWidth(size_t position) const {
348*8af74909SZhong Yang 	return ll->bidiData->widthReprs[start + position];
349*8af74909SZhong Yang }
350*8af74909SZhong Yang 
TabPositionAfter(XYPOSITION xPosition) const351*8af74909SZhong Yang XYPOSITION ScreenLine::TabPositionAfter(XYPOSITION xPosition) const {
352*8af74909SZhong Yang 	return (std::floor((xPosition + TabWidthMinimumPixels()) / TabWidth()) + 1) * TabWidth();
353*8af74909SZhong Yang }
354*8af74909SZhong Yang 
LineLayoutCache()355*8af74909SZhong Yang LineLayoutCache::LineLayoutCache() :
356*8af74909SZhong Yang 	level(0),
357*8af74909SZhong Yang 	allInvalidated(false), styleClock(-1), useCount(0) {
358*8af74909SZhong Yang 	Allocate(0);
359*8af74909SZhong Yang }
360*8af74909SZhong Yang 
~LineLayoutCache()361*8af74909SZhong Yang LineLayoutCache::~LineLayoutCache() {
362*8af74909SZhong Yang 	Deallocate();
363*8af74909SZhong Yang }
364*8af74909SZhong Yang 
Allocate(size_t length_)365*8af74909SZhong Yang void LineLayoutCache::Allocate(size_t length_) {
366*8af74909SZhong Yang 	PLATFORM_ASSERT(cache.empty());
367*8af74909SZhong Yang 	allInvalidated = false;
368*8af74909SZhong Yang 	cache.resize(length_);
369*8af74909SZhong Yang }
370*8af74909SZhong Yang 
AllocateForLevel(Sci::Line linesOnScreen,Sci::Line linesInDoc)371*8af74909SZhong Yang void LineLayoutCache::AllocateForLevel(Sci::Line linesOnScreen, Sci::Line linesInDoc) {
372*8af74909SZhong Yang 	PLATFORM_ASSERT(useCount == 0);
373*8af74909SZhong Yang 	size_t lengthForLevel = 0;
374*8af74909SZhong Yang 	if (level == llcCaret) {
375*8af74909SZhong Yang 		lengthForLevel = 1;
376*8af74909SZhong Yang 	} else if (level == llcPage) {
377*8af74909SZhong Yang 		lengthForLevel = linesOnScreen + 1;
378*8af74909SZhong Yang 	} else if (level == llcDocument) {
379*8af74909SZhong Yang 		lengthForLevel = linesInDoc;
380*8af74909SZhong Yang 	}
381*8af74909SZhong Yang 	if (lengthForLevel > cache.size()) {
382*8af74909SZhong Yang 		Deallocate();
383*8af74909SZhong Yang 		Allocate(lengthForLevel);
384*8af74909SZhong Yang 	} else {
385*8af74909SZhong Yang 		if (lengthForLevel < cache.size()) {
386*8af74909SZhong Yang 			for (size_t i = lengthForLevel; i < cache.size(); i++) {
387*8af74909SZhong Yang 				cache[i].reset();
388*8af74909SZhong Yang 			}
389*8af74909SZhong Yang 		}
390*8af74909SZhong Yang 		cache.resize(lengthForLevel);
391*8af74909SZhong Yang 	}
392*8af74909SZhong Yang 	PLATFORM_ASSERT(cache.size() == lengthForLevel);
393*8af74909SZhong Yang }
394*8af74909SZhong Yang 
Deallocate()395*8af74909SZhong Yang void LineLayoutCache::Deallocate() noexcept {
396*8af74909SZhong Yang 	PLATFORM_ASSERT(useCount == 0);
397*8af74909SZhong Yang 	cache.clear();
398*8af74909SZhong Yang }
399*8af74909SZhong Yang 
Invalidate(LineLayout::ValidLevel validity_)400*8af74909SZhong Yang void LineLayoutCache::Invalidate(LineLayout::ValidLevel validity_) noexcept {
401*8af74909SZhong Yang 	if (!cache.empty() && !allInvalidated) {
402*8af74909SZhong Yang 		for (const std::unique_ptr<LineLayout> &ll : cache) {
403*8af74909SZhong Yang 			if (ll) {
404*8af74909SZhong Yang 				ll->Invalidate(validity_);
405*8af74909SZhong Yang 			}
406*8af74909SZhong Yang 		}
407*8af74909SZhong Yang 		if (validity_ == LineLayout::ValidLevel::invalid) {
408*8af74909SZhong Yang 			allInvalidated = true;
409*8af74909SZhong Yang 		}
410*8af74909SZhong Yang 	}
411*8af74909SZhong Yang }
412*8af74909SZhong Yang 
SetLevel(int level_)413*8af74909SZhong Yang void LineLayoutCache::SetLevel(int level_) noexcept {
414*8af74909SZhong Yang 	allInvalidated = false;
415*8af74909SZhong Yang 	if ((level_ != -1) && (level != level_)) {
416*8af74909SZhong Yang 		level = level_;
417*8af74909SZhong Yang 		Deallocate();
418*8af74909SZhong Yang 	}
419*8af74909SZhong Yang }
420*8af74909SZhong Yang 
Retrieve(Sci::Line lineNumber,Sci::Line lineCaret,int maxChars,int styleClock_,Sci::Line linesOnScreen,Sci::Line linesInDoc)421*8af74909SZhong Yang LineLayout *LineLayoutCache::Retrieve(Sci::Line lineNumber, Sci::Line lineCaret, int maxChars, int styleClock_,
422*8af74909SZhong Yang                                       Sci::Line linesOnScreen, Sci::Line linesInDoc) {
423*8af74909SZhong Yang 	AllocateForLevel(linesOnScreen, linesInDoc);
424*8af74909SZhong Yang 	if (styleClock != styleClock_) {
425*8af74909SZhong Yang 		Invalidate(LineLayout::ValidLevel::checkTextAndStyle);
426*8af74909SZhong Yang 		styleClock = styleClock_;
427*8af74909SZhong Yang 	}
428*8af74909SZhong Yang 	allInvalidated = false;
429*8af74909SZhong Yang 	Sci::Position pos = -1;
430*8af74909SZhong Yang 	LineLayout *ret = nullptr;
431*8af74909SZhong Yang 	if (level == llcCaret) {
432*8af74909SZhong Yang 		pos = 0;
433*8af74909SZhong Yang 	} else if (level == llcPage) {
434*8af74909SZhong Yang 		if (lineNumber == lineCaret) {
435*8af74909SZhong Yang 			pos = 0;
436*8af74909SZhong Yang 		} else if (cache.size() > 1) {
437*8af74909SZhong Yang 			pos = 1 + (lineNumber % (cache.size() - 1));
438*8af74909SZhong Yang 		}
439*8af74909SZhong Yang 	} else if (level == llcDocument) {
440*8af74909SZhong Yang 		pos = lineNumber;
441*8af74909SZhong Yang 	}
442*8af74909SZhong Yang 	if (pos >= 0) {
443*8af74909SZhong Yang 		PLATFORM_ASSERT(useCount == 0);
444*8af74909SZhong Yang 		if (!cache.empty() && (pos < static_cast<int>(cache.size()))) {
445*8af74909SZhong Yang 			if (cache[pos]) {
446*8af74909SZhong Yang 				if ((cache[pos]->lineNumber != lineNumber) ||
447*8af74909SZhong Yang 				        (cache[pos]->maxLineLength < maxChars)) {
448*8af74909SZhong Yang 					cache[pos].reset();
449*8af74909SZhong Yang 				}
450*8af74909SZhong Yang 			}
451*8af74909SZhong Yang 			if (!cache[pos]) {
452*8af74909SZhong Yang 				cache[pos] = std::make_unique<LineLayout>(maxChars);
453*8af74909SZhong Yang 			}
454*8af74909SZhong Yang 			cache[pos]->lineNumber = lineNumber;
455*8af74909SZhong Yang 			cache[pos]->inCache = true;
456*8af74909SZhong Yang 			ret = cache[pos].get();
457*8af74909SZhong Yang 			useCount++;
458*8af74909SZhong Yang 		}
459*8af74909SZhong Yang 	}
460*8af74909SZhong Yang 
461*8af74909SZhong Yang 	if (!ret) {
462*8af74909SZhong Yang 		ret = new LineLayout(maxChars);
463*8af74909SZhong Yang 		ret->lineNumber = lineNumber;
464*8af74909SZhong Yang 	}
465*8af74909SZhong Yang 
466*8af74909SZhong Yang 	return ret;
467*8af74909SZhong Yang }
468*8af74909SZhong Yang 
Dispose(LineLayout * ll)469*8af74909SZhong Yang void LineLayoutCache::Dispose(LineLayout *ll) noexcept {
470*8af74909SZhong Yang 	allInvalidated = false;
471*8af74909SZhong Yang 	if (ll) {
472*8af74909SZhong Yang 		if (!ll->inCache) {
473*8af74909SZhong Yang 			delete ll;
474*8af74909SZhong Yang 		} else {
475*8af74909SZhong Yang 			useCount--;
476*8af74909SZhong Yang 		}
477*8af74909SZhong Yang 	}
478*8af74909SZhong Yang }
479*8af74909SZhong Yang 
480*8af74909SZhong Yang // Simply pack the (maximum 4) character bytes into an int
KeyFromString(const char * charBytes,size_t len)481*8af74909SZhong Yang static unsigned int KeyFromString(const char *charBytes, size_t len) noexcept {
482*8af74909SZhong Yang 	PLATFORM_ASSERT(len <= 4);
483*8af74909SZhong Yang 	unsigned int k=0;
484*8af74909SZhong Yang 	for (size_t i=0; i<len && charBytes[i]; i++) {
485*8af74909SZhong Yang 		k = k * 0x100;
486*8af74909SZhong Yang 		const unsigned char uc = charBytes[i];
487*8af74909SZhong Yang 		k += uc;
488*8af74909SZhong Yang 	}
489*8af74909SZhong Yang 	return k;
490*8af74909SZhong Yang }
491*8af74909SZhong Yang 
SpecialRepresentations()492*8af74909SZhong Yang SpecialRepresentations::SpecialRepresentations() {
493*8af74909SZhong Yang 	const short none = 0;
494*8af74909SZhong Yang 	std::fill(startByteHasReprs, std::end(startByteHasReprs), none);
495*8af74909SZhong Yang }
496*8af74909SZhong Yang 
SetRepresentation(const char * charBytes,const char * value)497*8af74909SZhong Yang void SpecialRepresentations::SetRepresentation(const char *charBytes, const char *value) {
498*8af74909SZhong Yang 	const unsigned int key = KeyFromString(charBytes, UTF8MaxBytes);
499*8af74909SZhong Yang 	MapRepresentation::iterator it = mapReprs.find(key);
500*8af74909SZhong Yang 	if (it == mapReprs.end()) {
501*8af74909SZhong Yang 		// New entry so increment for first byte
502*8af74909SZhong Yang 		const unsigned char ucStart = charBytes[0];
503*8af74909SZhong Yang 		startByteHasReprs[ucStart]++;
504*8af74909SZhong Yang 	}
505*8af74909SZhong Yang 	mapReprs[key] = Representation(value);
506*8af74909SZhong Yang }
507*8af74909SZhong Yang 
ClearRepresentation(const char * charBytes)508*8af74909SZhong Yang void SpecialRepresentations::ClearRepresentation(const char *charBytes) {
509*8af74909SZhong Yang 	MapRepresentation::iterator it = mapReprs.find(KeyFromString(charBytes, UTF8MaxBytes));
510*8af74909SZhong Yang 	if (it != mapReprs.end()) {
511*8af74909SZhong Yang 		mapReprs.erase(it);
512*8af74909SZhong Yang 		const unsigned char ucStart = charBytes[0];
513*8af74909SZhong Yang 		startByteHasReprs[ucStart]--;
514*8af74909SZhong Yang 	}
515*8af74909SZhong Yang }
516*8af74909SZhong Yang 
RepresentationFromCharacter(const char * charBytes,size_t len) const517*8af74909SZhong Yang const Representation *SpecialRepresentations::RepresentationFromCharacter(const char *charBytes, size_t len) const {
518*8af74909SZhong Yang 	PLATFORM_ASSERT(len <= 4);
519*8af74909SZhong Yang 	const unsigned char ucStart = charBytes[0];
520*8af74909SZhong Yang 	if (!startByteHasReprs[ucStart])
521*8af74909SZhong Yang 		return nullptr;
522*8af74909SZhong Yang 	MapRepresentation::const_iterator it = mapReprs.find(KeyFromString(charBytes, len));
523*8af74909SZhong Yang 	if (it != mapReprs.end()) {
524*8af74909SZhong Yang 		return &(it->second);
525*8af74909SZhong Yang 	}
526*8af74909SZhong Yang 	return nullptr;
527*8af74909SZhong Yang }
528*8af74909SZhong Yang 
Contains(const char * charBytes,size_t len) const529*8af74909SZhong Yang bool SpecialRepresentations::Contains(const char *charBytes, size_t len) const {
530*8af74909SZhong Yang 	PLATFORM_ASSERT(len <= 4);
531*8af74909SZhong Yang 	const unsigned char ucStart = charBytes[0];
532*8af74909SZhong Yang 	if (!startByteHasReprs[ucStart])
533*8af74909SZhong Yang 		return false;
534*8af74909SZhong Yang 	MapRepresentation::const_iterator it = mapReprs.find(KeyFromString(charBytes, len));
535*8af74909SZhong Yang 	return it != mapReprs.end();
536*8af74909SZhong Yang }
537*8af74909SZhong Yang 
Clear()538*8af74909SZhong Yang void SpecialRepresentations::Clear() {
539*8af74909SZhong Yang 	mapReprs.clear();
540*8af74909SZhong Yang 	const short none = 0;
541*8af74909SZhong Yang 	std::fill(startByteHasReprs, std::end(startByteHasReprs), none);
542*8af74909SZhong Yang }
543*8af74909SZhong Yang 
Insert(Sci::Position val)544*8af74909SZhong Yang void BreakFinder::Insert(Sci::Position val) {
545*8af74909SZhong Yang 	const int posInLine = static_cast<int>(val);
546*8af74909SZhong Yang 	if (posInLine > nextBreak) {
547*8af74909SZhong Yang 		const std::vector<int>::iterator it = std::lower_bound(selAndEdge.begin(), selAndEdge.end(), posInLine);
548*8af74909SZhong Yang 		if (it == selAndEdge.end()) {
549*8af74909SZhong Yang 			selAndEdge.push_back(posInLine);
550*8af74909SZhong Yang 		} else if (*it != posInLine) {
551*8af74909SZhong Yang 			selAndEdge.insert(it, 1, posInLine);
552*8af74909SZhong Yang 		}
553*8af74909SZhong Yang 	}
554*8af74909SZhong Yang }
555*8af74909SZhong Yang 
BreakFinder(const LineLayout * ll_,const Selection * psel,Range lineRange_,Sci::Position posLineStart_,int xStart,bool breakForSelection,const Document * pdoc_,const SpecialRepresentations * preprs_,const ViewStyle * pvsDraw)556*8af74909SZhong Yang BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart_,
557*8af74909SZhong Yang 	int xStart, bool breakForSelection, const Document *pdoc_, const SpecialRepresentations *preprs_, const ViewStyle *pvsDraw) :
558*8af74909SZhong Yang 	ll(ll_),
559*8af74909SZhong Yang 	lineRange(lineRange_),
560*8af74909SZhong Yang 	posLineStart(posLineStart_),
561*8af74909SZhong Yang 	nextBreak(static_cast<int>(lineRange_.start)),
562*8af74909SZhong Yang 	saeCurrentPos(0),
563*8af74909SZhong Yang 	saeNext(0),
564*8af74909SZhong Yang 	subBreak(-1),
565*8af74909SZhong Yang 	pdoc(pdoc_),
566*8af74909SZhong Yang 	encodingFamily(pdoc_->CodePageFamily()),
567*8af74909SZhong Yang 	preprs(preprs_) {
568*8af74909SZhong Yang 
569*8af74909SZhong Yang 	// Search for first visible break
570*8af74909SZhong Yang 	// First find the first visible character
571*8af74909SZhong Yang 	if (xStart > 0.0f)
572*8af74909SZhong Yang 		nextBreak = ll->FindBefore(static_cast<XYPOSITION>(xStart), lineRange);
573*8af74909SZhong Yang 	// Now back to a style break
574*8af74909SZhong Yang 	while ((nextBreak > lineRange.start) && (ll->styles[nextBreak] == ll->styles[nextBreak - 1])) {
575*8af74909SZhong Yang 		nextBreak--;
576*8af74909SZhong Yang 	}
577*8af74909SZhong Yang 
578*8af74909SZhong Yang 	if (breakForSelection) {
579*8af74909SZhong Yang 		const SelectionPosition posStart(posLineStart);
580*8af74909SZhong Yang 		const SelectionPosition posEnd(posLineStart + lineRange.end);
581*8af74909SZhong Yang 		const SelectionSegment segmentLine(posStart, posEnd);
582*8af74909SZhong Yang 		for (size_t r=0; r<psel->Count(); r++) {
583*8af74909SZhong Yang 			const SelectionSegment portion = psel->Range(r).Intersect(segmentLine);
584*8af74909SZhong Yang 			if (!(portion.start == portion.end)) {
585*8af74909SZhong Yang 				if (portion.start.IsValid())
586*8af74909SZhong Yang 					Insert(portion.start.Position() - posLineStart);
587*8af74909SZhong Yang 				if (portion.end.IsValid())
588*8af74909SZhong Yang 					Insert(portion.end.Position() - posLineStart);
589*8af74909SZhong Yang 			}
590*8af74909SZhong Yang 		}
591*8af74909SZhong Yang 	}
592*8af74909SZhong Yang 	if (pvsDraw && pvsDraw->indicatorsSetFore) {
593*8af74909SZhong Yang 		for (const IDecoration *deco : pdoc->decorations->View()) {
594*8af74909SZhong Yang 			if (pvsDraw->indicators[deco->Indicator()].OverridesTextFore()) {
595*8af74909SZhong Yang 				Sci::Position startPos = deco->EndRun(posLineStart);
596*8af74909SZhong Yang 				while (startPos < (posLineStart + lineRange.end)) {
597*8af74909SZhong Yang 					Insert(startPos - posLineStart);
598*8af74909SZhong Yang 					startPos = deco->EndRun(startPos);
599*8af74909SZhong Yang 				}
600*8af74909SZhong Yang 			}
601*8af74909SZhong Yang 		}
602*8af74909SZhong Yang 	}
603*8af74909SZhong Yang 	Insert(ll->edgeColumn);
604*8af74909SZhong Yang 	Insert(lineRange.end);
605*8af74909SZhong Yang 	saeNext = (!selAndEdge.empty()) ? selAndEdge[0] : -1;
606*8af74909SZhong Yang }
607*8af74909SZhong Yang 
~BreakFinder()608*8af74909SZhong Yang BreakFinder::~BreakFinder() {
609*8af74909SZhong Yang }
610*8af74909SZhong Yang 
Next()611*8af74909SZhong Yang TextSegment BreakFinder::Next() {
612*8af74909SZhong Yang 	if (subBreak == -1) {
613*8af74909SZhong Yang 		const int prev = nextBreak;
614*8af74909SZhong Yang 		while (nextBreak < lineRange.end) {
615*8af74909SZhong Yang 			int charWidth = 1;
616*8af74909SZhong Yang 			if (encodingFamily == EncodingFamily::unicode)
617*8af74909SZhong Yang 				charWidth = UTF8DrawBytes(reinterpret_cast<unsigned char *>(&ll->chars[nextBreak]),
618*8af74909SZhong Yang 					static_cast<int>(lineRange.end - nextBreak));
619*8af74909SZhong Yang 			else if (encodingFamily == EncodingFamily::dbcs)
620*8af74909SZhong Yang 				charWidth = pdoc->DBCSDrawBytes(
621*8af74909SZhong Yang 					std::string_view(&ll->chars[nextBreak], lineRange.end - nextBreak));
622*8af74909SZhong Yang 			const Representation *repr = preprs->RepresentationFromCharacter(&ll->chars[nextBreak], charWidth);
623*8af74909SZhong Yang 			if (((nextBreak > 0) && (ll->styles[nextBreak] != ll->styles[nextBreak - 1])) ||
624*8af74909SZhong Yang 					repr ||
625*8af74909SZhong Yang 					(nextBreak == saeNext)) {
626*8af74909SZhong Yang 				while ((nextBreak >= saeNext) && (saeNext < lineRange.end)) {
627*8af74909SZhong Yang 					saeCurrentPos++;
628*8af74909SZhong Yang 					saeNext = static_cast<int>((saeCurrentPos < selAndEdge.size()) ? selAndEdge[saeCurrentPos] : lineRange.end);
629*8af74909SZhong Yang 				}
630*8af74909SZhong Yang 				if ((nextBreak > prev) || repr) {
631*8af74909SZhong Yang 					// Have a segment to report
632*8af74909SZhong Yang 					if (nextBreak == prev) {
633*8af74909SZhong Yang 						nextBreak += charWidth;
634*8af74909SZhong Yang 					} else {
635*8af74909SZhong Yang 						repr = nullptr;	// Optimize -> should remember repr
636*8af74909SZhong Yang 					}
637*8af74909SZhong Yang 					if ((nextBreak - prev) < lengthStartSubdivision) {
638*8af74909SZhong Yang 						return TextSegment(prev, nextBreak - prev, repr);
639*8af74909SZhong Yang 					} else {
640*8af74909SZhong Yang 						break;
641*8af74909SZhong Yang 					}
642*8af74909SZhong Yang 				}
643*8af74909SZhong Yang 			}
644*8af74909SZhong Yang 			nextBreak += charWidth;
645*8af74909SZhong Yang 		}
646*8af74909SZhong Yang 		if ((nextBreak - prev) < lengthStartSubdivision) {
647*8af74909SZhong Yang 			return TextSegment(prev, nextBreak - prev);
648*8af74909SZhong Yang 		}
649*8af74909SZhong Yang 		subBreak = prev;
650*8af74909SZhong Yang 	}
651*8af74909SZhong Yang 	// Splitting up a long run from prev to nextBreak in lots of approximately lengthEachSubdivision.
652*8af74909SZhong Yang 	// For very long runs add extra breaks after spaces or if no spaces before low punctuation.
653*8af74909SZhong Yang 	const int startSegment = subBreak;
654*8af74909SZhong Yang 	if ((nextBreak - subBreak) <= lengthEachSubdivision) {
655*8af74909SZhong Yang 		subBreak = -1;
656*8af74909SZhong Yang 		return TextSegment(startSegment, nextBreak - startSegment);
657*8af74909SZhong Yang 	} else {
658*8af74909SZhong Yang 		subBreak += pdoc->SafeSegment(&ll->chars[subBreak], nextBreak-subBreak, lengthEachSubdivision);
659*8af74909SZhong Yang 		if (subBreak >= nextBreak) {
660*8af74909SZhong Yang 			subBreak = -1;
661*8af74909SZhong Yang 			return TextSegment(startSegment, nextBreak - startSegment);
662*8af74909SZhong Yang 		} else {
663*8af74909SZhong Yang 			return TextSegment(startSegment, subBreak - startSegment);
664*8af74909SZhong Yang 		}
665*8af74909SZhong Yang 	}
666*8af74909SZhong Yang }
667*8af74909SZhong Yang 
More() const668*8af74909SZhong Yang bool BreakFinder::More() const noexcept {
669*8af74909SZhong Yang 	return (subBreak >= 0) || (nextBreak < lineRange.end);
670*8af74909SZhong Yang }
671*8af74909SZhong Yang 
PositionCacheEntry()672*8af74909SZhong Yang PositionCacheEntry::PositionCacheEntry() noexcept :
673*8af74909SZhong Yang 	styleNumber(0), len(0), clock(0), positions(nullptr) {
674*8af74909SZhong Yang }
675*8af74909SZhong Yang 
676*8af74909SZhong Yang // Copy constructor not currently used, but needed for being element in std::vector.
PositionCacheEntry(const PositionCacheEntry & other)677*8af74909SZhong Yang PositionCacheEntry::PositionCacheEntry(const PositionCacheEntry &other) :
678*8af74909SZhong Yang 	styleNumber(other.styleNumber), len(other.styleNumber), clock(other.styleNumber), positions(nullptr) {
679*8af74909SZhong Yang 	if (other.positions) {
680*8af74909SZhong Yang 		const size_t lenData = len + (len / sizeof(XYPOSITION)) + 1;
681*8af74909SZhong Yang 		positions = std::make_unique<XYPOSITION[]>(lenData);
682*8af74909SZhong Yang 		memcpy(positions.get(), other.positions.get(), lenData * sizeof(XYPOSITION));
683*8af74909SZhong Yang 	}
684*8af74909SZhong Yang }
685*8af74909SZhong Yang 
Set(unsigned int styleNumber_,const char * s_,unsigned int len_,const XYPOSITION * positions_,unsigned int clock_)686*8af74909SZhong Yang void PositionCacheEntry::Set(unsigned int styleNumber_, const char *s_,
687*8af74909SZhong Yang 	unsigned int len_, const XYPOSITION *positions_, unsigned int clock_) {
688*8af74909SZhong Yang 	Clear();
689*8af74909SZhong Yang 	styleNumber = styleNumber_;
690*8af74909SZhong Yang 	len = len_;
691*8af74909SZhong Yang 	clock = clock_;
692*8af74909SZhong Yang 	if (s_ && positions_) {
693*8af74909SZhong Yang 		positions = std::make_unique<XYPOSITION[]>(len + (len / sizeof(XYPOSITION)) + 1);
694*8af74909SZhong Yang 		for (unsigned int i=0; i<len; i++) {
695*8af74909SZhong Yang 			positions[i] = positions_[i];
696*8af74909SZhong Yang 		}
697*8af74909SZhong Yang 		memcpy(&positions[len], s_, len);
698*8af74909SZhong Yang 	}
699*8af74909SZhong Yang }
700*8af74909SZhong Yang 
~PositionCacheEntry()701*8af74909SZhong Yang PositionCacheEntry::~PositionCacheEntry() {
702*8af74909SZhong Yang 	Clear();
703*8af74909SZhong Yang }
704*8af74909SZhong Yang 
Clear()705*8af74909SZhong Yang void PositionCacheEntry::Clear() noexcept {
706*8af74909SZhong Yang 	positions.reset();
707*8af74909SZhong Yang 	styleNumber = 0;
708*8af74909SZhong Yang 	len = 0;
709*8af74909SZhong Yang 	clock = 0;
710*8af74909SZhong Yang }
711*8af74909SZhong Yang 
Retrieve(unsigned int styleNumber_,const char * s_,unsigned int len_,XYPOSITION * positions_) const712*8af74909SZhong Yang bool PositionCacheEntry::Retrieve(unsigned int styleNumber_, const char *s_,
713*8af74909SZhong Yang 	unsigned int len_, XYPOSITION *positions_) const noexcept {
714*8af74909SZhong Yang 	if ((styleNumber == styleNumber_) && (len == len_) &&
715*8af74909SZhong Yang 		(memcmp(&positions[len], s_, len)== 0)) {
716*8af74909SZhong Yang 		for (unsigned int i=0; i<len; i++) {
717*8af74909SZhong Yang 			positions_[i] = positions[i];
718*8af74909SZhong Yang 		}
719*8af74909SZhong Yang 		return true;
720*8af74909SZhong Yang 	} else {
721*8af74909SZhong Yang 		return false;
722*8af74909SZhong Yang 	}
723*8af74909SZhong Yang }
724*8af74909SZhong Yang 
Hash(unsigned int styleNumber_,const char * s,unsigned int len_)725*8af74909SZhong Yang unsigned int PositionCacheEntry::Hash(unsigned int styleNumber_, const char *s, unsigned int len_) noexcept {
726*8af74909SZhong Yang 	unsigned int ret = s[0] << 7;
727*8af74909SZhong Yang 	for (unsigned int i=0; i<len_; i++) {
728*8af74909SZhong Yang 		ret *= 1000003;
729*8af74909SZhong Yang 		ret ^= s[i];
730*8af74909SZhong Yang 	}
731*8af74909SZhong Yang 	ret *= 1000003;
732*8af74909SZhong Yang 	ret ^= len_;
733*8af74909SZhong Yang 	ret *= 1000003;
734*8af74909SZhong Yang 	ret ^= styleNumber_;
735*8af74909SZhong Yang 	return ret;
736*8af74909SZhong Yang }
737*8af74909SZhong Yang 
NewerThan(const PositionCacheEntry & other) const738*8af74909SZhong Yang bool PositionCacheEntry::NewerThan(const PositionCacheEntry &other) const noexcept {
739*8af74909SZhong Yang 	return clock > other.clock;
740*8af74909SZhong Yang }
741*8af74909SZhong Yang 
ResetClock()742*8af74909SZhong Yang void PositionCacheEntry::ResetClock() noexcept {
743*8af74909SZhong Yang 	if (clock > 0) {
744*8af74909SZhong Yang 		clock = 1;
745*8af74909SZhong Yang 	}
746*8af74909SZhong Yang }
747*8af74909SZhong Yang 
PositionCache()748*8af74909SZhong Yang PositionCache::PositionCache() {
749*8af74909SZhong Yang 	clock = 1;
750*8af74909SZhong Yang 	pces.resize(0x400);
751*8af74909SZhong Yang 	allClear = true;
752*8af74909SZhong Yang }
753*8af74909SZhong Yang 
~PositionCache()754*8af74909SZhong Yang PositionCache::~PositionCache() {
755*8af74909SZhong Yang 	Clear();
756*8af74909SZhong Yang }
757*8af74909SZhong Yang 
Clear()758*8af74909SZhong Yang void PositionCache::Clear() noexcept {
759*8af74909SZhong Yang 	if (!allClear) {
760*8af74909SZhong Yang 		for (PositionCacheEntry &pce : pces) {
761*8af74909SZhong Yang 			pce.Clear();
762*8af74909SZhong Yang 		}
763*8af74909SZhong Yang 	}
764*8af74909SZhong Yang 	clock = 1;
765*8af74909SZhong Yang 	allClear = true;
766*8af74909SZhong Yang }
767*8af74909SZhong Yang 
SetSize(size_t size_)768*8af74909SZhong Yang void PositionCache::SetSize(size_t size_) {
769*8af74909SZhong Yang 	Clear();
770*8af74909SZhong Yang 	pces.resize(size_);
771*8af74909SZhong Yang }
772*8af74909SZhong Yang 
MeasureWidths(Surface * surface,const ViewStyle & vstyle,unsigned int styleNumber,const char * s,unsigned int len,XYPOSITION * positions,const Document * pdoc)773*8af74909SZhong Yang void PositionCache::MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber,
774*8af74909SZhong Yang 	const char *s, unsigned int len, XYPOSITION *positions, const Document *pdoc) {
775*8af74909SZhong Yang 
776*8af74909SZhong Yang 	allClear = false;
777*8af74909SZhong Yang 	size_t probe = pces.size();	// Out of bounds
778*8af74909SZhong Yang 	if ((!pces.empty()) && (len < 30)) {
779*8af74909SZhong Yang 		// Only store short strings in the cache so it doesn't churn with
780*8af74909SZhong Yang 		// long comments with only a single comment.
781*8af74909SZhong Yang 
782*8af74909SZhong Yang 		// Two way associative: try two probe positions.
783*8af74909SZhong Yang 		const unsigned int hashValue = PositionCacheEntry::Hash(styleNumber, s, len);
784*8af74909SZhong Yang 		probe = hashValue % pces.size();
785*8af74909SZhong Yang 		if (pces[probe].Retrieve(styleNumber, s, len, positions)) {
786*8af74909SZhong Yang 			return;
787*8af74909SZhong Yang 		}
788*8af74909SZhong Yang 		const unsigned int probe2 = (hashValue * 37) % pces.size();
789*8af74909SZhong Yang 		if (pces[probe2].Retrieve(styleNumber, s, len, positions)) {
790*8af74909SZhong Yang 			return;
791*8af74909SZhong Yang 		}
792*8af74909SZhong Yang 		// Not found. Choose the oldest of the two slots to replace
793*8af74909SZhong Yang 		if (pces[probe].NewerThan(pces[probe2])) {
794*8af74909SZhong Yang 			probe = probe2;
795*8af74909SZhong Yang 		}
796*8af74909SZhong Yang 	}
797*8af74909SZhong Yang 	FontAlias fontStyle = vstyle.styles[styleNumber].font;
798*8af74909SZhong Yang 	if (len > BreakFinder::lengthStartSubdivision) {
799*8af74909SZhong Yang 		// Break up into segments
800*8af74909SZhong Yang 		unsigned int startSegment = 0;
801*8af74909SZhong Yang 		XYPOSITION xStartSegment = 0;
802*8af74909SZhong Yang 		while (startSegment < len) {
803*8af74909SZhong Yang 			const unsigned int lenSegment = pdoc->SafeSegment(s + startSegment, len - startSegment, BreakFinder::lengthEachSubdivision);
804*8af74909SZhong Yang 			surface->MeasureWidths(fontStyle, std::string_view(s + startSegment, lenSegment), positions + startSegment);
805*8af74909SZhong Yang 			for (unsigned int inSeg = 0; inSeg < lenSegment; inSeg++) {
806*8af74909SZhong Yang 				positions[startSegment + inSeg] += xStartSegment;
807*8af74909SZhong Yang 			}
808*8af74909SZhong Yang 			xStartSegment = positions[startSegment + lenSegment - 1];
809*8af74909SZhong Yang 			startSegment += lenSegment;
810*8af74909SZhong Yang 		}
811*8af74909SZhong Yang 	} else {
812*8af74909SZhong Yang 		surface->MeasureWidths(fontStyle, std::string_view(s, len), positions);
813*8af74909SZhong Yang 	}
814*8af74909SZhong Yang 	if (probe < pces.size()) {
815*8af74909SZhong Yang 		// Store into cache
816*8af74909SZhong Yang 		clock++;
817*8af74909SZhong Yang 		if (clock > 60000) {
818*8af74909SZhong Yang 			// Since there are only 16 bits for the clock, wrap it round and
819*8af74909SZhong Yang 			// reset all cache entries so none get stuck with a high clock.
820*8af74909SZhong Yang 			for (PositionCacheEntry &pce : pces) {
821*8af74909SZhong Yang 				pce.ResetClock();
822*8af74909SZhong Yang 			}
823*8af74909SZhong Yang 			clock = 2;
824*8af74909SZhong Yang 		}
825*8af74909SZhong Yang 		pces[probe].Set(styleNumber, s, len, positions, clock);
826*8af74909SZhong Yang 	}
827*8af74909SZhong Yang }
828