File: | home/maarten/src/libreoffice/core/include/o3tl/cow_wrapper.hxx |
Warning: | line 40, column 61 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||||||||||||||
2 | /* | ||||||||||||||||
3 | * This file is part of the LibreOffice project. | ||||||||||||||||
4 | * | ||||||||||||||||
5 | * This Source Code Form is subject to the terms of the Mozilla Public | ||||||||||||||||
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||||||||||||
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||||||||||||
8 | * | ||||||||||||||||
9 | * This file incorporates work covered by the following license notice: | ||||||||||||||||
10 | * | ||||||||||||||||
11 | * Licensed to the Apache Software Foundation (ASF) under one or more | ||||||||||||||||
12 | * contributor license agreements. See the NOTICE file distributed | ||||||||||||||||
13 | * with this work for additional information regarding copyright | ||||||||||||||||
14 | * ownership. The ASF licenses this file to you under the Apache | ||||||||||||||||
15 | * License, Version 2.0 (the "License"); you may not use this file | ||||||||||||||||
16 | * except in compliance with the License. You may obtain a copy of | ||||||||||||||||
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . | ||||||||||||||||
18 | */ | ||||||||||||||||
19 | |||||||||||||||||
20 | #include <sal/log.hxx> | ||||||||||||||||
21 | #include <osl/diagnose.h> | ||||||||||||||||
22 | #include <poly.h> | ||||||||||||||||
23 | #include <tools/poly.hxx> | ||||||||||||||||
24 | #include <tools/debug.hxx> | ||||||||||||||||
25 | #include <tools/stream.hxx> | ||||||||||||||||
26 | #include <tools/vcompat.hxx> | ||||||||||||||||
27 | #include <tools/gen.hxx> | ||||||||||||||||
28 | #include <basegfx/polygon/b2dpolypolygon.hxx> | ||||||||||||||||
29 | #include <basegfx/polygon/b2dpolypolygoncutter.hxx> | ||||||||||||||||
30 | |||||||||||||||||
31 | namespace tools { | ||||||||||||||||
32 | |||||||||||||||||
33 | PolyPolygon::PolyPolygon( sal_uInt16 nInitSize ) | ||||||||||||||||
34 | : mpImplPolyPolygon( ImplPolyPolygon( nInitSize ) ) | ||||||||||||||||
35 | { | ||||||||||||||||
36 | } | ||||||||||||||||
37 | |||||||||||||||||
38 | PolyPolygon::PolyPolygon( const tools::Polygon& rPoly ) | ||||||||||||||||
39 | : mpImplPolyPolygon( rPoly ) | ||||||||||||||||
40 | { | ||||||||||||||||
41 | } | ||||||||||||||||
42 | |||||||||||||||||
43 | PolyPolygon::PolyPolygon( const tools::PolyPolygon& rPolyPoly ) | ||||||||||||||||
44 | : mpImplPolyPolygon( rPolyPoly.mpImplPolyPolygon ) | ||||||||||||||||
45 | { | ||||||||||||||||
46 | } | ||||||||||||||||
47 | |||||||||||||||||
48 | PolyPolygon::PolyPolygon( tools::PolyPolygon&& rPolyPoly ) noexcept | ||||||||||||||||
49 | : mpImplPolyPolygon( std::move(rPolyPoly.mpImplPolyPolygon) ) | ||||||||||||||||
50 | { | ||||||||||||||||
51 | } | ||||||||||||||||
52 | |||||||||||||||||
53 | PolyPolygon::~PolyPolygon() | ||||||||||||||||
54 | { | ||||||||||||||||
55 | } | ||||||||||||||||
56 | |||||||||||||||||
57 | void PolyPolygon::Insert( const tools::Polygon& rPoly, sal_uInt16 nPos ) | ||||||||||||||||
58 | { | ||||||||||||||||
59 | assert ( mpImplPolyPolygon->mvPolyAry.size() < MAX_POLYGONS )(static_cast <bool> (mpImplPolyPolygon->mvPolyAry.size () < ((sal_uInt16) 0xFFFF)) ? void (0) : __assert_fail ("mpImplPolyPolygon->mvPolyAry.size() < MAX_POLYGONS" , "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" , 59, __extension__ __PRETTY_FUNCTION__)); | ||||||||||||||||
60 | |||||||||||||||||
61 | if ( nPos > mpImplPolyPolygon->mvPolyAry.size() ) | ||||||||||||||||
62 | nPos = mpImplPolyPolygon->mvPolyAry.size(); | ||||||||||||||||
63 | |||||||||||||||||
64 | mpImplPolyPolygon->mvPolyAry.insert(mpImplPolyPolygon->mvPolyAry.begin() + nPos, rPoly); | ||||||||||||||||
65 | } | ||||||||||||||||
66 | |||||||||||||||||
67 | void PolyPolygon::Remove( sal_uInt16 nPos ) | ||||||||||||||||
68 | { | ||||||||||||||||
69 | assert(nPos < Count() && "PolyPolygon::Remove(): nPos >= nSize")(static_cast <bool> (nPos < Count() && "PolyPolygon::Remove(): nPos >= nSize" ) ? void (0) : __assert_fail ("nPos < Count() && \"PolyPolygon::Remove(): nPos >= nSize\"" , "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" , 69, __extension__ __PRETTY_FUNCTION__)); | ||||||||||||||||
70 | |||||||||||||||||
71 | mpImplPolyPolygon->mvPolyAry.erase(mpImplPolyPolygon->mvPolyAry.begin() + nPos); | ||||||||||||||||
72 | } | ||||||||||||||||
73 | |||||||||||||||||
74 | void PolyPolygon::Replace( const tools::Polygon& rPoly, sal_uInt16 nPos ) | ||||||||||||||||
75 | { | ||||||||||||||||
76 | assert(nPos < Count() && "PolyPolygon::Replace(): nPos >= nSize")(static_cast <bool> (nPos < Count() && "PolyPolygon::Replace(): nPos >= nSize" ) ? void (0) : __assert_fail ("nPos < Count() && \"PolyPolygon::Replace(): nPos >= nSize\"" , "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" , 76, __extension__ __PRETTY_FUNCTION__)); | ||||||||||||||||
77 | |||||||||||||||||
78 | mpImplPolyPolygon->mvPolyAry[nPos] = rPoly; | ||||||||||||||||
79 | } | ||||||||||||||||
80 | |||||||||||||||||
81 | const tools::Polygon& PolyPolygon::GetObject( sal_uInt16 nPos ) const | ||||||||||||||||
82 | { | ||||||||||||||||
83 | assert(nPos < Count() && "PolyPolygon::GetObject(): nPos >= nSize")(static_cast <bool> (nPos < Count() && "PolyPolygon::GetObject(): nPos >= nSize" ) ? void (0) : __assert_fail ("nPos < Count() && \"PolyPolygon::GetObject(): nPos >= nSize\"" , "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" , 83, __extension__ __PRETTY_FUNCTION__)); | ||||||||||||||||
84 | |||||||||||||||||
85 | return mpImplPolyPolygon->mvPolyAry[nPos]; | ||||||||||||||||
86 | } | ||||||||||||||||
87 | |||||||||||||||||
88 | bool PolyPolygon::IsRect() const | ||||||||||||||||
89 | { | ||||||||||||||||
90 | bool bIsRect = false; | ||||||||||||||||
91 | if ( Count() == 1 ) | ||||||||||||||||
92 | bIsRect = mpImplPolyPolygon->mvPolyAry[ 0 ].IsRect(); | ||||||||||||||||
93 | return bIsRect; | ||||||||||||||||
94 | } | ||||||||||||||||
95 | |||||||||||||||||
96 | void PolyPolygon::Clear() | ||||||||||||||||
97 | { | ||||||||||||||||
98 | mpImplPolyPolygon->mvPolyAry.clear(); | ||||||||||||||||
99 | } | ||||||||||||||||
100 | |||||||||||||||||
101 | void PolyPolygon::Optimize( PolyOptimizeFlags nOptimizeFlags ) | ||||||||||||||||
102 | { | ||||||||||||||||
103 | if(!(bool(nOptimizeFlags
| ||||||||||||||||
| |||||||||||||||||
104 | return; | ||||||||||||||||
105 | |||||||||||||||||
106 | // #115630# ImplDrawHatch does not work with beziers included in the polypolygon, take care of that | ||||||||||||||||
107 | bool bIsCurve(false); | ||||||||||||||||
108 | |||||||||||||||||
109 | for(sal_uInt16 a(0); !bIsCurve
| ||||||||||||||||
110 | { | ||||||||||||||||
111 | if((*this)[a].HasFlags()) | ||||||||||||||||
112 | { | ||||||||||||||||
113 | bIsCurve = true; | ||||||||||||||||
114 | } | ||||||||||||||||
115 | } | ||||||||||||||||
116 | |||||||||||||||||
117 | if(bIsCurve
| ||||||||||||||||
118 | { | ||||||||||||||||
119 | OSL_ENSURE(false, "Optimize does *not* support curves, falling back to AdaptiveSubdivide()...")do { if (true && (!(false))) { sal_detail_logFormat(( SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "119" ": "), "%s", "Optimize does *not* support curves, falling back to AdaptiveSubdivide()..." ); } } while (false); | ||||||||||||||||
120 | tools::PolyPolygon aPolyPoly; | ||||||||||||||||
121 | |||||||||||||||||
122 | AdaptiveSubdivide(aPolyPoly); | ||||||||||||||||
123 | aPolyPoly.Optimize(nOptimizeFlags); | ||||||||||||||||
124 | *this = aPolyPoly; | ||||||||||||||||
125 | } | ||||||||||||||||
126 | else | ||||||||||||||||
127 | { | ||||||||||||||||
128 | double fArea; | ||||||||||||||||
129 | const bool bEdges = ( nOptimizeFlags & PolyOptimizeFlags::EDGES ) == PolyOptimizeFlags::EDGES; | ||||||||||||||||
130 | sal_uInt16 nPercent = 0; | ||||||||||||||||
131 | |||||||||||||||||
132 | if( bEdges ) | ||||||||||||||||
133 | { | ||||||||||||||||
134 | const tools::Rectangle aBound( GetBoundRect() ); | ||||||||||||||||
135 | |||||||||||||||||
136 | fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5; | ||||||||||||||||
137 | nPercent = 50; | ||||||||||||||||
138 | nOptimizeFlags &= ~PolyOptimizeFlags::EDGES; | ||||||||||||||||
139 | } | ||||||||||||||||
140 | |||||||||||||||||
141 | // Optimize polygons | ||||||||||||||||
142 | for( sal_uInt16 i = 0, nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); i < nPolyCount; i++ ) | ||||||||||||||||
143 | { | ||||||||||||||||
144 | if( bEdges ) | ||||||||||||||||
145 | { | ||||||||||||||||
146 | mpImplPolyPolygon->mvPolyAry[ i ].Optimize( PolyOptimizeFlags::NO_SAME ); | ||||||||||||||||
147 | tools::Polygon::ImplReduceEdges( mpImplPolyPolygon->mvPolyAry[ i ], fArea, nPercent ); | ||||||||||||||||
148 | } | ||||||||||||||||
149 | |||||||||||||||||
150 | if( bool(nOptimizeFlags) ) | ||||||||||||||||
151 | mpImplPolyPolygon->mvPolyAry[ i ].Optimize( nOptimizeFlags ); | ||||||||||||||||
152 | } | ||||||||||||||||
153 | } | ||||||||||||||||
154 | } | ||||||||||||||||
155 | |||||||||||||||||
156 | void PolyPolygon::AdaptiveSubdivide( tools::PolyPolygon& rResult ) const | ||||||||||||||||
157 | { | ||||||||||||||||
158 | rResult.Clear(); | ||||||||||||||||
159 | |||||||||||||||||
160 | tools::Polygon aPolygon; | ||||||||||||||||
161 | |||||||||||||||||
162 | for( size_t i = 0; i < mpImplPolyPolygon->mvPolyAry.size(); i++ ) | ||||||||||||||||
163 | { | ||||||||||||||||
164 | mpImplPolyPolygon->mvPolyAry[ i ].AdaptiveSubdivide( aPolygon, 1.0 ); | ||||||||||||||||
165 | rResult.Insert( aPolygon ); | ||||||||||||||||
166 | } | ||||||||||||||||
167 | } | ||||||||||||||||
168 | |||||||||||||||||
169 | tools::PolyPolygon PolyPolygon::SubdivideBezier( const tools::PolyPolygon& rPolyPoly ) | ||||||||||||||||
170 | { | ||||||||||||||||
171 | sal_uInt16 i, nPolys = rPolyPoly.Count(); | ||||||||||||||||
172 | tools::PolyPolygon aPolyPoly( nPolys ); | ||||||||||||||||
173 | for( i=0; i<nPolys; ++i ) | ||||||||||||||||
174 | aPolyPoly.Insert( tools::Polygon::SubdivideBezier( rPolyPoly.GetObject(i) ) ); | ||||||||||||||||
175 | |||||||||||||||||
176 | return aPolyPoly; | ||||||||||||||||
177 | } | ||||||||||||||||
178 | |||||||||||||||||
179 | |||||||||||||||||
180 | void PolyPolygon::GetIntersection( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult ) const | ||||||||||||||||
181 | { | ||||||||||||||||
182 | ImplDoOperation( rPolyPoly, rResult, PolyClipOp::INTERSECT ); | ||||||||||||||||
183 | } | ||||||||||||||||
184 | |||||||||||||||||
185 | void PolyPolygon::GetUnion( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult ) const | ||||||||||||||||
186 | { | ||||||||||||||||
187 | ImplDoOperation( rPolyPoly, rResult, PolyClipOp::UNION ); | ||||||||||||||||
188 | } | ||||||||||||||||
189 | |||||||||||||||||
190 | void PolyPolygon::ImplDoOperation( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult, PolyClipOp nOperation ) const | ||||||||||||||||
191 | { | ||||||||||||||||
192 | // Convert to B2DPolyPolygon, temporarily. It might be | ||||||||||||||||
193 | // advantageous in the future, to have a tools::PolyPolygon adaptor that | ||||||||||||||||
194 | // just simulates a B2DPolyPolygon here... | ||||||||||||||||
195 | basegfx::B2DPolyPolygon aMergePolyPolygonA( getB2DPolyPolygon() ); | ||||||||||||||||
196 | basegfx::B2DPolyPolygon aMergePolyPolygonB( rPolyPoly.getB2DPolyPolygon() ); | ||||||||||||||||
197 | |||||||||||||||||
198 | // normalize the two polypolygons before. Force properly oriented | ||||||||||||||||
199 | // polygons. | ||||||||||||||||
200 | aMergePolyPolygonA = basegfx::utils::prepareForPolygonOperation( aMergePolyPolygonA ); | ||||||||||||||||
201 | aMergePolyPolygonB = basegfx::utils::prepareForPolygonOperation( aMergePolyPolygonB ); | ||||||||||||||||
202 | |||||||||||||||||
203 | switch( nOperation ) | ||||||||||||||||
204 | { | ||||||||||||||||
205 | // All code extracted from svx/source/svdraw/svedtv2.cxx | ||||||||||||||||
206 | |||||||||||||||||
207 | case PolyClipOp::UNION: | ||||||||||||||||
208 | { | ||||||||||||||||
209 | // merge A and B (OR) | ||||||||||||||||
210 | aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB); | ||||||||||||||||
211 | break; | ||||||||||||||||
212 | } | ||||||||||||||||
213 | |||||||||||||||||
214 | default: | ||||||||||||||||
215 | case PolyClipOp::INTERSECT: | ||||||||||||||||
216 | { | ||||||||||||||||
217 | // cut poly 1 against polys 2..n (AND) | ||||||||||||||||
218 | aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB); | ||||||||||||||||
219 | break; | ||||||||||||||||
220 | } | ||||||||||||||||
221 | } | ||||||||||||||||
222 | |||||||||||||||||
223 | rResult = tools::PolyPolygon( aMergePolyPolygonA ); | ||||||||||||||||
224 | } | ||||||||||||||||
225 | |||||||||||||||||
226 | sal_uInt16 PolyPolygon::Count() const | ||||||||||||||||
227 | { | ||||||||||||||||
228 | return mpImplPolyPolygon->mvPolyAry.size(); | ||||||||||||||||
229 | } | ||||||||||||||||
230 | |||||||||||||||||
231 | void PolyPolygon::Move( long nHorzMove, long nVertMove ) | ||||||||||||||||
232 | { | ||||||||||||||||
233 | // Required for DrawEngine | ||||||||||||||||
234 | if( nHorzMove || nVertMove ) | ||||||||||||||||
235 | { | ||||||||||||||||
236 | // move points | ||||||||||||||||
237 | sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); | ||||||||||||||||
238 | for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) | ||||||||||||||||
239 | mpImplPolyPolygon->mvPolyAry[i].Move( nHorzMove, nVertMove ); | ||||||||||||||||
240 | } | ||||||||||||||||
241 | } | ||||||||||||||||
242 | |||||||||||||||||
243 | void PolyPolygon::Translate( const Point& rTrans ) | ||||||||||||||||
244 | { | ||||||||||||||||
245 | // move points | ||||||||||||||||
246 | for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ ) | ||||||||||||||||
247 | mpImplPolyPolygon->mvPolyAry[ i ].Translate( rTrans ); | ||||||||||||||||
248 | } | ||||||||||||||||
249 | |||||||||||||||||
250 | void PolyPolygon::Scale( double fScaleX, double fScaleY ) | ||||||||||||||||
251 | { | ||||||||||||||||
252 | // Move points | ||||||||||||||||
253 | for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ ) | ||||||||||||||||
254 | mpImplPolyPolygon->mvPolyAry[ i ].Scale( fScaleX, fScaleY ); | ||||||||||||||||
255 | } | ||||||||||||||||
256 | |||||||||||||||||
257 | void PolyPolygon::Rotate( const Point& rCenter, sal_uInt16 nAngle10 ) | ||||||||||||||||
258 | { | ||||||||||||||||
259 | nAngle10 %= 3600; | ||||||||||||||||
260 | |||||||||||||||||
261 | if( nAngle10 ) | ||||||||||||||||
262 | { | ||||||||||||||||
263 | const double fAngle = F_PI1800(3.14159265358979323846/1800.0) * nAngle10; | ||||||||||||||||
264 | Rotate( rCenter, sin( fAngle ), cos( fAngle ) ); | ||||||||||||||||
265 | } | ||||||||||||||||
266 | } | ||||||||||||||||
267 | |||||||||||||||||
268 | void PolyPolygon::Rotate( const Point& rCenter, double fSin, double fCos ) | ||||||||||||||||
269 | { | ||||||||||||||||
270 | // move points | ||||||||||||||||
271 | for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ ) | ||||||||||||||||
272 | mpImplPolyPolygon->mvPolyAry[ i ].Rotate( rCenter, fSin, fCos ); | ||||||||||||||||
273 | } | ||||||||||||||||
274 | |||||||||||||||||
275 | void PolyPolygon::Clip( const tools::Rectangle& rRect ) | ||||||||||||||||
276 | { | ||||||||||||||||
277 | sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); | ||||||||||||||||
278 | sal_uInt16 i; | ||||||||||||||||
279 | |||||||||||||||||
280 | if ( !nPolyCount ) | ||||||||||||||||
281 | return; | ||||||||||||||||
282 | |||||||||||||||||
283 | // Clip every polygon, deleting the empty ones | ||||||||||||||||
284 | for ( i = 0; i < nPolyCount; i++ ) | ||||||||||||||||
285 | mpImplPolyPolygon->mvPolyAry[i].Clip( rRect ); | ||||||||||||||||
286 | while ( nPolyCount ) | ||||||||||||||||
287 | { | ||||||||||||||||
288 | if ( GetObject( nPolyCount-1 ).GetSize() <= 2 ) | ||||||||||||||||
289 | Remove( nPolyCount-1 ); | ||||||||||||||||
290 | nPolyCount--; | ||||||||||||||||
291 | } | ||||||||||||||||
292 | } | ||||||||||||||||
293 | |||||||||||||||||
294 | tools::Rectangle PolyPolygon::GetBoundRect() const | ||||||||||||||||
295 | { | ||||||||||||||||
296 | long nXMin=0, nXMax=0, nYMin=0, nYMax=0; | ||||||||||||||||
297 | bool bFirst = true; | ||||||||||||||||
298 | sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); | ||||||||||||||||
299 | |||||||||||||||||
300 | for ( sal_uInt16 n = 0; n < nPolyCount; n++ ) | ||||||||||||||||
301 | { | ||||||||||||||||
302 | const tools::Polygon* pPoly = &mpImplPolyPolygon->mvPolyAry[n]; | ||||||||||||||||
303 | const Point* pAry = pPoly->GetConstPointAry(); | ||||||||||||||||
304 | sal_uInt16 nPointCount = pPoly->GetSize(); | ||||||||||||||||
305 | |||||||||||||||||
306 | for ( sal_uInt16 i = 0; i < nPointCount; i++ ) | ||||||||||||||||
307 | { | ||||||||||||||||
308 | const Point* pPt = &pAry[ i ]; | ||||||||||||||||
309 | |||||||||||||||||
310 | if ( bFirst ) | ||||||||||||||||
311 | { | ||||||||||||||||
312 | nXMin = nXMax = pPt->X(); | ||||||||||||||||
313 | nYMin = nYMax = pPt->Y(); | ||||||||||||||||
314 | bFirst = false; | ||||||||||||||||
315 | } | ||||||||||||||||
316 | else | ||||||||||||||||
317 | { | ||||||||||||||||
318 | if ( pPt->X() < nXMin ) | ||||||||||||||||
319 | nXMin = pPt->X(); | ||||||||||||||||
320 | if ( pPt->X() > nXMax ) | ||||||||||||||||
321 | nXMax = pPt->X(); | ||||||||||||||||
322 | if ( pPt->Y() < nYMin ) | ||||||||||||||||
323 | nYMin = pPt->Y(); | ||||||||||||||||
324 | if ( pPt->Y() > nYMax ) | ||||||||||||||||
325 | nYMax = pPt->Y(); | ||||||||||||||||
326 | } | ||||||||||||||||
327 | } | ||||||||||||||||
328 | } | ||||||||||||||||
329 | |||||||||||||||||
330 | if ( !bFirst ) | ||||||||||||||||
331 | return tools::Rectangle( nXMin, nYMin, nXMax, nYMax ); | ||||||||||||||||
332 | else | ||||||||||||||||
333 | return tools::Rectangle(); | ||||||||||||||||
334 | } | ||||||||||||||||
335 | |||||||||||||||||
336 | Polygon& PolyPolygon::operator[]( sal_uInt16 nPos ) | ||||||||||||||||
337 | { | ||||||||||||||||
338 | assert(nPos < Count() && "PolyPolygon::[](): nPos >= nSize")(static_cast <bool> (nPos < Count() && "PolyPolygon::[](): nPos >= nSize" ) ? void (0) : __assert_fail ("nPos < Count() && \"PolyPolygon::[](): nPos >= nSize\"" , "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" , 338, __extension__ __PRETTY_FUNCTION__)); | ||||||||||||||||
339 | |||||||||||||||||
340 | return mpImplPolyPolygon->mvPolyAry[nPos]; | ||||||||||||||||
341 | } | ||||||||||||||||
342 | |||||||||||||||||
343 | PolyPolygon& PolyPolygon::operator=( const tools::PolyPolygon& rPolyPoly ) | ||||||||||||||||
344 | { | ||||||||||||||||
345 | mpImplPolyPolygon = rPolyPoly.mpImplPolyPolygon; | ||||||||||||||||
346 | return *this; | ||||||||||||||||
347 | } | ||||||||||||||||
348 | |||||||||||||||||
349 | PolyPolygon& PolyPolygon::operator=( tools::PolyPolygon&& rPolyPoly ) noexcept | ||||||||||||||||
350 | { | ||||||||||||||||
351 | mpImplPolyPolygon = std::move(rPolyPoly.mpImplPolyPolygon); | ||||||||||||||||
352 | return *this; | ||||||||||||||||
353 | } | ||||||||||||||||
354 | |||||||||||||||||
355 | bool PolyPolygon::operator==( const tools::PolyPolygon& rPolyPoly ) const | ||||||||||||||||
356 | { | ||||||||||||||||
357 | return rPolyPoly.mpImplPolyPolygon == mpImplPolyPolygon; | ||||||||||||||||
358 | } | ||||||||||||||||
359 | |||||||||||||||||
360 | SvStream& ReadPolyPolygon( SvStream& rIStream, tools::PolyPolygon& rPolyPoly ) | ||||||||||||||||
361 | { | ||||||||||||||||
362 | sal_uInt16 nPolyCount(0); | ||||||||||||||||
363 | |||||||||||||||||
364 | // Read number of polygons | ||||||||||||||||
365 | rIStream.ReadUInt16( nPolyCount ); | ||||||||||||||||
366 | |||||||||||||||||
367 | const size_t nMinRecordSize = sizeof(sal_uInt16); | ||||||||||||||||
368 | const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize; | ||||||||||||||||
369 | if (nPolyCount > nMaxRecords) | ||||||||||||||||
370 | { | ||||||||||||||||
371 | SAL_WARN("tools", "Parsing error: " << nMaxRecords <<do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "tools")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ( "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "372" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "372" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ( "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "372" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "372" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false) | ||||||||||||||||
372 | " max possible entries, but " << nPolyCount << " claimed, truncating")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "tools")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ( "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "372" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "372" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ( "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "372" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "372" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | ||||||||||||||||
373 | nPolyCount = nMaxRecords; | ||||||||||||||||
374 | } | ||||||||||||||||
375 | |||||||||||||||||
376 | if( nPolyCount ) | ||||||||||||||||
377 | { | ||||||||||||||||
378 | rPolyPoly.mpImplPolyPolygon->mvPolyAry.resize(nPolyCount); | ||||||||||||||||
379 | |||||||||||||||||
380 | tools::Polygon aTempPoly; | ||||||||||||||||
381 | for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) | ||||||||||||||||
382 | { | ||||||||||||||||
383 | ReadPolygon( rIStream, aTempPoly ); | ||||||||||||||||
384 | rPolyPoly.mpImplPolyPolygon->mvPolyAry[i] = aTempPoly; | ||||||||||||||||
385 | } | ||||||||||||||||
386 | } | ||||||||||||||||
387 | else | ||||||||||||||||
388 | rPolyPoly = tools::PolyPolygon(); | ||||||||||||||||
389 | |||||||||||||||||
390 | return rIStream; | ||||||||||||||||
391 | } | ||||||||||||||||
392 | |||||||||||||||||
393 | SvStream& WritePolyPolygon( SvStream& rOStream, const tools::PolyPolygon& rPolyPoly ) | ||||||||||||||||
394 | { | ||||||||||||||||
395 | // Write number of polygons | ||||||||||||||||
396 | sal_uInt16 nPolyCount = rPolyPoly.mpImplPolyPolygon->mvPolyAry.size(); | ||||||||||||||||
397 | rOStream.WriteUInt16( nPolyCount ); | ||||||||||||||||
398 | |||||||||||||||||
399 | // output polygons | ||||||||||||||||
400 | for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) | ||||||||||||||||
401 | WritePolygon( rOStream, rPolyPoly.mpImplPolyPolygon->mvPolyAry[i] ); | ||||||||||||||||
402 | |||||||||||||||||
403 | return rOStream; | ||||||||||||||||
404 | } | ||||||||||||||||
405 | |||||||||||||||||
406 | void PolyPolygon::Read( SvStream& rIStream ) | ||||||||||||||||
407 | { | ||||||||||||||||
408 | VersionCompat aCompat( rIStream, StreamMode::READ ); | ||||||||||||||||
409 | |||||||||||||||||
410 | sal_uInt16 nPolyCount(0); | ||||||||||||||||
411 | |||||||||||||||||
412 | // Read number of polygons | ||||||||||||||||
413 | rIStream.ReadUInt16( nPolyCount ); | ||||||||||||||||
414 | |||||||||||||||||
415 | const size_t nMinRecordSize = sizeof(sal_uInt16); | ||||||||||||||||
416 | const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize; | ||||||||||||||||
417 | if (nPolyCount > nMaxRecords) | ||||||||||||||||
418 | { | ||||||||||||||||
419 | SAL_WARN("tools", "Parsing error: " << nMaxRecords <<do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "tools")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ( "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "420" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "420" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ( "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "420" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "420" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false) | ||||||||||||||||
420 | " max possible entries, but " << nPolyCount << " claimed, truncating")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "tools")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ( "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "420" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "420" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ( "/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "420" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "420" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | ||||||||||||||||
421 | nPolyCount = nMaxRecords; | ||||||||||||||||
422 | } | ||||||||||||||||
423 | |||||||||||||||||
424 | if( nPolyCount ) | ||||||||||||||||
425 | { | ||||||||||||||||
426 | mpImplPolyPolygon->mvPolyAry.clear(); | ||||||||||||||||
427 | |||||||||||||||||
428 | for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) | ||||||||||||||||
429 | { | ||||||||||||||||
430 | tools::Polygon aTempPoly; | ||||||||||||||||
431 | aTempPoly.ImplRead( rIStream ); | ||||||||||||||||
432 | mpImplPolyPolygon->mvPolyAry.emplace_back( aTempPoly ); | ||||||||||||||||
433 | } | ||||||||||||||||
434 | } | ||||||||||||||||
435 | else | ||||||||||||||||
436 | *this = tools::PolyPolygon(); | ||||||||||||||||
437 | } | ||||||||||||||||
438 | |||||||||||||||||
439 | void PolyPolygon::Write( SvStream& rOStream ) const | ||||||||||||||||
440 | { | ||||||||||||||||
441 | VersionCompat aCompat( rOStream, StreamMode::WRITE, 1 ); | ||||||||||||||||
442 | |||||||||||||||||
443 | // Write number of polygons | ||||||||||||||||
444 | sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); | ||||||||||||||||
445 | rOStream.WriteUInt16( nPolyCount ); | ||||||||||||||||
446 | |||||||||||||||||
447 | // Output polygons | ||||||||||||||||
448 | for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) | ||||||||||||||||
449 | mpImplPolyPolygon->mvPolyAry[i].ImplWrite( rOStream ); | ||||||||||||||||
450 | } | ||||||||||||||||
451 | |||||||||||||||||
452 | // convert to basegfx::B2DPolyPolygon and return | ||||||||||||||||
453 | basegfx::B2DPolyPolygon PolyPolygon::getB2DPolyPolygon() const | ||||||||||||||||
454 | { | ||||||||||||||||
455 | basegfx::B2DPolyPolygon aRetval; | ||||||||||||||||
456 | |||||||||||||||||
457 | for(size_t a(0); a < mpImplPolyPolygon->mvPolyAry.size(); a++) | ||||||||||||||||
458 | { | ||||||||||||||||
459 | tools::Polygon const & rCandidate = mpImplPolyPolygon->mvPolyAry[a]; | ||||||||||||||||
460 | aRetval.append(rCandidate.getB2DPolygon()); | ||||||||||||||||
461 | } | ||||||||||||||||
462 | |||||||||||||||||
463 | return aRetval; | ||||||||||||||||
464 | } | ||||||||||||||||
465 | |||||||||||||||||
466 | // constructor to convert from basegfx::B2DPolyPolygon | ||||||||||||||||
467 | PolyPolygon::PolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon) | ||||||||||||||||
468 | : mpImplPolyPolygon(rPolyPolygon) | ||||||||||||||||
469 | { | ||||||||||||||||
470 | } | ||||||||||||||||
471 | |||||||||||||||||
472 | } /* namespace tools */ | ||||||||||||||||
473 | |||||||||||||||||
474 | ImplPolyPolygon::ImplPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon) | ||||||||||||||||
475 | { | ||||||||||||||||
476 | const sal_uInt16 nCount(sal_uInt16(rPolyPolygon.count())); | ||||||||||||||||
477 | DBG_ASSERT(sal_uInt32(nCount) == rPolyPolygon.count(),do { if (true && (!(sal_uInt32(nCount) == rPolyPolygon .count()))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN ), ("legacy.tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "478" ": "), "%s", "PolyPolygon::PolyPolygon: Too many sub-polygons in given basegfx::B2DPolyPolygon (!)" ); } } while (false) | ||||||||||||||||
478 | "PolyPolygon::PolyPolygon: Too many sub-polygons in given basegfx::B2DPolyPolygon (!)")do { if (true && (!(sal_uInt32(nCount) == rPolyPolygon .count()))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN ), ("legacy.tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx" ":" "478" ": "), "%s", "PolyPolygon::PolyPolygon: Too many sub-polygons in given basegfx::B2DPolyPolygon (!)" ); } } while (false); | ||||||||||||||||
479 | |||||||||||||||||
480 | if ( nCount ) | ||||||||||||||||
481 | { | ||||||||||||||||
482 | mvPolyAry.resize( nCount ); | ||||||||||||||||
483 | |||||||||||||||||
484 | for(sal_uInt16 a(0); a < nCount; a++) | ||||||||||||||||
485 | { | ||||||||||||||||
486 | const basegfx::B2DPolygon& aCandidate(rPolyPolygon.getB2DPolygon(sal_uInt32(a))); | ||||||||||||||||
487 | mvPolyAry[a] = tools::Polygon( aCandidate ); | ||||||||||||||||
488 | } | ||||||||||||||||
489 | } | ||||||||||||||||
490 | else | ||||||||||||||||
491 | mvPolyAry.reserve(16); | ||||||||||||||||
492 | } | ||||||||||||||||
493 | |||||||||||||||||
494 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
2 | /* | ||||
3 | * This file is part of the LibreOffice project. | ||||
4 | * | ||||
5 | * This Source Code Form is subject to the terms of the Mozilla Public | ||||
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
8 | * | ||||
9 | * This file incorporates work covered by the following license notice: | ||||
10 | * | ||||
11 | * Licensed to the Apache Software Foundation (ASF) under one or more | ||||
12 | * contributor license agreements. See the NOTICE file distributed | ||||
13 | * with this work for additional information regarding copyright | ||||
14 | * ownership. The ASF licenses this file to you under the Apache | ||||
15 | * License, Version 2.0 (the "License"); you may not use this file | ||||
16 | * except in compliance with the License. You may obtain a copy of | ||||
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . | ||||
18 | */ | ||||
19 | |||||
20 | #ifndef INCLUDED_O3TL_COW_WRAPPER_HXX | ||||
21 | #define INCLUDED_O3TL_COW_WRAPPER_HXX | ||||
22 | |||||
23 | #include <memory> | ||||
24 | #include <osl/interlck.h> | ||||
25 | |||||
26 | #include <utility> | ||||
27 | #include <cstddef> | ||||
28 | |||||
29 | namespace o3tl | ||||
30 | { | ||||
31 | /** Thread-unsafe refcounting | ||||
32 | |||||
33 | This is the default locking policy for cow_wrapper. No | ||||
34 | locking/guarding against concurrent access is performed | ||||
35 | whatsoever. | ||||
36 | */ | ||||
37 | struct UnsafeRefCountingPolicy | ||||
38 | { | ||||
39 | typedef std::size_t ref_count_t; | ||||
40 | static void incrementCount( ref_count_t& rCount ) { ++rCount; } | ||||
| |||||
41 | static bool decrementCount( ref_count_t& rCount ) { return --rCount != 0; } | ||||
42 | }; | ||||
43 | |||||
44 | /** Thread-safe refcounting | ||||
45 | |||||
46 | Use this to have the cow_wrapper refcounting mechanisms employ | ||||
47 | the thread-safe oslInterlockedCount . | ||||
48 | */ | ||||
49 | struct ThreadSafeRefCountingPolicy | ||||
50 | { | ||||
51 | typedef oslInterlockedCount ref_count_t; | ||||
52 | static void incrementCount( ref_count_t& rCount ) { osl_atomic_increment(&rCount)__sync_add_and_fetch((&rCount), 1); } | ||||
53 | static bool decrementCount( ref_count_t& rCount ) | ||||
54 | { | ||||
55 | if( rCount == 1 ) // caller is already the only/last reference | ||||
56 | return false; | ||||
57 | else | ||||
58 | return osl_atomic_decrement(&rCount)__sync_sub_and_fetch((&rCount), 1) != 0; | ||||
59 | } | ||||
60 | }; | ||||
61 | |||||
62 | /** Copy-on-write wrapper. | ||||
63 | |||||
64 | This template provides copy-on-write semantics for the wrapped | ||||
65 | type: when copying, the operation is performed shallow, | ||||
66 | i.e. different cow_wrapper objects share the same underlying | ||||
67 | instance. Only when accessing the underlying object via | ||||
68 | non-const methods, a unique copy is provided. | ||||
69 | |||||
70 | The type parameter <code>T</code> must satisfy the following | ||||
71 | requirements: it must be default-constructible, copyable (it | ||||
72 | need not be assignable), and be of non-reference type. Note | ||||
73 | that, despite the fact that this template provides access to | ||||
74 | the wrapped type via pointer-like methods | ||||
75 | (<code>operator->()</code> and <code>operator*()</code>), it does | ||||
76 | <em>not</em> work like e.g. the std smart pointer wrappers | ||||
77 | (shared_ptr, unique_ptr, etc.). Internally, the cow_wrapper | ||||
78 | holds a by-value instance of the wrapped object. This is to | ||||
79 | avoid one additional heap allocation, and providing access via | ||||
80 | <code>operator->()</code>/<code>operator*()</code> is because | ||||
81 | <code>operator.()</code> cannot be overridden. | ||||
82 | |||||
83 | Regarding thread safety: this wrapper is <em>not</em> | ||||
84 | thread-safe per se, because cow_wrapper has no way of | ||||
85 | synchronizing the potentially many different cow_wrapper | ||||
86 | instances, that reference a single shared value_type | ||||
87 | instance. That said, when passing | ||||
88 | <code>ThreadSafeRefCountingPolicy</code> as the | ||||
89 | <code>MTPolicy</code> parameter, accessing a thread-safe | ||||
90 | pointee through multiple cow_wrapper instances might be | ||||
91 | thread-safe, if the individual pointee methods are | ||||
92 | thread-safe, <em>including</em> pointee's copy | ||||
93 | constructor. Any wrapped object that needs external | ||||
94 | synchronisation (e.g. via an external mutex, which arbitrates | ||||
95 | access to object methods, and can be held across multiple | ||||
96 | object method calls) cannot easily be dealt with in a | ||||
97 | thread-safe way, because, as noted, objects are shared behind | ||||
98 | the client's back. | ||||
99 | |||||
100 | @attention if one wants to use the pimpl idiom together with | ||||
101 | cow_wrapper (i.e. put an opaque type into the cow_wrapper), | ||||
102 | then <em>all<em> methods in the surrounding class needs to be | ||||
103 | non-inline (<em>including</em> destructor, copy constructor | ||||
104 | and assignment operator). | ||||
105 | |||||
106 | @example | ||||
107 | <pre> | ||||
108 | class cow_wrapper_client_impl; | ||||
109 | |||||
110 | class cow_wrapper_client | ||||
111 | { | ||||
112 | public: | ||||
113 | cow_wrapper_client(); | ||||
114 | cow_wrapper_client( const cow_wrapper_client& ); | ||||
115 | cow_wrapper_client( cow_wrapper_client&& ); | ||||
116 | ~cow_wrapper_client(); | ||||
117 | |||||
118 | cow_wrapper_client& operator=( const cow_wrapper_client& ); | ||||
119 | cow_wrapper_client& operator=( cow_wrapper_client&& ); | ||||
120 | |||||
121 | void modify( int nVal ); | ||||
122 | int queryUnmodified() const; | ||||
123 | |||||
124 | private: | ||||
125 | o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl; | ||||
126 | }; | ||||
127 | </pre> | ||||
128 | and the implementation file would look like this: | ||||
129 | <pre> | ||||
130 | class cow_wrapper_client_impl | ||||
131 | { | ||||
132 | public: | ||||
133 | void setValue( int nVal ) { mnValue = nVal; } | ||||
134 | int getValue() const { return mnValue; } | ||||
135 | |||||
136 | private: | ||||
137 | int mnValue; | ||||
138 | } | ||||
139 | |||||
140 | cow_wrapper_client::cow_wrapper_client() : | ||||
141 | maImpl() | ||||
142 | { | ||||
143 | } | ||||
144 | cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) : | ||||
145 | maImpl( rSrc.maImpl ) | ||||
146 | { | ||||
147 | } | ||||
148 | cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) : | ||||
149 | maImpl( std::move( rSrc.maImpl ) ) | ||||
150 | { | ||||
151 | } | ||||
152 | cow_wrapper_client::~cow_wrapper_client() | ||||
153 | { | ||||
154 | } | ||||
155 | cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc ) | ||||
156 | { | ||||
157 | maImpl = rSrc.maImpl; | ||||
158 | return *this; | ||||
159 | } | ||||
160 | cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc ) | ||||
161 | { | ||||
162 | maImpl = std::move( rSrc.maImpl ); | ||||
163 | return *this; | ||||
164 | } | ||||
165 | void cow_wrapper_client::modify( int nVal ) | ||||
166 | { | ||||
167 | maImpl->setValue( nVal ); | ||||
168 | } | ||||
169 | int cow_wrapper_client::queryUnmodified() const | ||||
170 | { | ||||
171 | return maImpl->getValue(); | ||||
172 | } | ||||
173 | </pre> | ||||
174 | */ | ||||
175 | template<typename T, class MTPolicy=UnsafeRefCountingPolicy> class cow_wrapper | ||||
176 | { | ||||
177 | /** shared value object - gets cloned before cow_wrapper hands | ||||
178 | out a non-const reference to it | ||||
179 | */ | ||||
180 | struct impl_t | ||||
181 | { | ||||
182 | impl_t(const impl_t&) = delete; | ||||
183 | impl_t& operator=(const impl_t&) = delete; | ||||
184 | |||||
185 | impl_t() : | ||||
186 | m_value(), | ||||
187 | m_ref_count(1) | ||||
188 | { | ||||
189 | } | ||||
190 | |||||
191 | explicit impl_t( const T& v ) : | ||||
192 | m_value(v), | ||||
193 | m_ref_count(1) | ||||
194 | { | ||||
195 | } | ||||
196 | |||||
197 | explicit impl_t( T&& v ) : | ||||
198 | m_value(std::move(v)), | ||||
199 | m_ref_count(1) | ||||
200 | { | ||||
201 | } | ||||
202 | |||||
203 | T m_value; | ||||
204 | typename MTPolicy::ref_count_t m_ref_count; | ||||
205 | }; | ||||
206 | |||||
207 | void release() | ||||
208 | { | ||||
209 | if( m_pimpl
| ||||
210 | { | ||||
211 | delete m_pimpl; | ||||
212 | m_pimpl = nullptr; | ||||
213 | } | ||||
214 | } | ||||
215 | |||||
216 | public: | ||||
217 | typedef T value_type; | ||||
218 | typedef T* pointer; | ||||
219 | typedef const T* const_pointer; | ||||
220 | typedef MTPolicy mt_policy; | ||||
221 | |||||
222 | /** Default-construct wrapped type instance | ||||
223 | */ | ||||
224 | cow_wrapper() : | ||||
225 | m_pimpl( new impl_t() ) | ||||
226 | { | ||||
227 | } | ||||
228 | |||||
229 | /** Copy-construct wrapped type instance from given object | ||||
230 | */ | ||||
231 | explicit cow_wrapper( const value_type& r ) : | ||||
232 | m_pimpl( new impl_t(r) ) | ||||
233 | { | ||||
234 | } | ||||
235 | |||||
236 | /** Move-construct wrapped type instance from given object | ||||
237 | */ | ||||
238 | explicit cow_wrapper( value_type&& r ) : | ||||
239 | m_pimpl( new impl_t(std::move(r)) ) | ||||
240 | { | ||||
241 | } | ||||
242 | |||||
243 | /** Shallow-copy given cow_wrapper | ||||
244 | */ | ||||
245 | explicit cow_wrapper( const cow_wrapper& rSrc ) : // nothrow | ||||
246 | m_pimpl( rSrc.m_pimpl ) | ||||
247 | { | ||||
248 | MTPolicy::incrementCount( m_pimpl->m_ref_count ); | ||||
249 | } | ||||
250 | |||||
251 | /** Move-construct and steal rSrc shared resource | ||||
252 | */ | ||||
253 | explicit cow_wrapper( cow_wrapper&& rSrc ) noexcept : | ||||
254 | m_pimpl( rSrc.m_pimpl ) | ||||
255 | { | ||||
256 | rSrc.m_pimpl = nullptr; | ||||
257 | } | ||||
258 | |||||
259 | ~cow_wrapper() // nothrow, if ~T does not throw | ||||
260 | { | ||||
261 | release(); | ||||
262 | } | ||||
263 | |||||
264 | /// now sharing rSrc cow_wrapper instance with us | ||||
265 | cow_wrapper& operator=( const cow_wrapper& rSrc ) // nothrow | ||||
266 | { | ||||
267 | // this already guards against self-assignment | ||||
268 | MTPolicy::incrementCount( rSrc.m_pimpl->m_ref_count ); | ||||
269 | |||||
270 | release(); | ||||
271 | m_pimpl = rSrc.m_pimpl; | ||||
272 | |||||
273 | return *this; | ||||
274 | } | ||||
275 | |||||
276 | /// stealing rSrc's resource | ||||
277 | cow_wrapper& operator=(cow_wrapper&& rSrc) noexcept | ||||
278 | { | ||||
279 | // self-movement guts ourself, see also 17.6.4.9 | ||||
280 | release(); | ||||
281 | m_pimpl = rSrc.m_pimpl; | ||||
282 | |||||
283 | rSrc.m_pimpl = nullptr; | ||||
284 | |||||
285 | return *this; | ||||
286 | } | ||||
287 | |||||
288 | /// unshare with any other cow_wrapper instance | ||||
289 | value_type& make_unique() | ||||
290 | { | ||||
291 | if( m_pimpl->m_ref_count > 1 ) | ||||
292 | { | ||||
293 | impl_t* pimpl = new impl_t(m_pimpl->m_value); | ||||
294 | release(); | ||||
295 | m_pimpl = pimpl; | ||||
296 | } | ||||
297 | |||||
298 | return m_pimpl->m_value; | ||||
299 | } | ||||
300 | |||||
301 | /// true, if not shared with any other cow_wrapper instance | ||||
302 | bool is_unique() const // nothrow | ||||
303 | { | ||||
304 | return !m_pimpl || m_pimpl->m_ref_count == 1; | ||||
305 | } | ||||
306 | |||||
307 | /// return number of shared instances (1 for unique object) | ||||
308 | typename MTPolicy::ref_count_t use_count() const // nothrow | ||||
309 | { | ||||
310 | return m_pimpl ? m_pimpl->m_ref_count : 0; | ||||
311 | } | ||||
312 | |||||
313 | void swap(cow_wrapper& r) // never throws | ||||
314 | { | ||||
315 | std::swap(m_pimpl, r.m_pimpl); | ||||
316 | } | ||||
317 | |||||
318 | pointer operator->() { return &make_unique(); } | ||||
319 | value_type& operator*() { return make_unique(); } | ||||
320 | const_pointer operator->() const { return &m_pimpl->m_value; } | ||||
321 | const value_type& operator*() const { return m_pimpl->m_value; } | ||||
322 | |||||
323 | pointer get() { return &make_unique(); } | ||||
324 | const_pointer get() const { return &m_pimpl->m_value; } | ||||
325 | |||||
326 | /// true, if both cow_wrapper internally share the same object | ||||
327 | bool same_object( const cow_wrapper& rOther ) const | ||||
328 | { | ||||
329 | return rOther.m_pimpl == m_pimpl; | ||||
330 | } | ||||
331 | |||||
332 | private: | ||||
333 | impl_t* m_pimpl; | ||||
334 | }; | ||||
335 | |||||
336 | |||||
337 | template<class T, class P> inline bool operator==( const cow_wrapper<T,P>& a, | ||||
338 | const cow_wrapper<T,P>& b ) | ||||
339 | { | ||||
340 | return a.same_object(b) || *a == *b; | ||||
341 | } | ||||
342 | |||||
343 | template<class T, class P> inline bool operator!=( const cow_wrapper<T,P>& a, | ||||
344 | const cow_wrapper<T,P>& b ) | ||||
345 | { | ||||
346 | return !a.same_object(b) && *a != *b; | ||||
347 | } | ||||
348 | |||||
349 | template<class A, class B, class P> inline bool operator<( const cow_wrapper<A,P>& a, | ||||
350 | const cow_wrapper<B,P>& b ) | ||||
351 | { | ||||
352 | return *a < *b; | ||||
353 | } | ||||
354 | |||||
355 | template<class T, class P> inline void swap( cow_wrapper<T,P>& a, | ||||
356 | cow_wrapper<T,P>& b ) | ||||
357 | { | ||||
358 | a.swap(b); | ||||
359 | } | ||||
360 | |||||
361 | } | ||||
362 | |||||
363 | #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */ | ||||
364 | |||||
365 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |