Bug Summary

File:home/maarten/src/libreoffice/core/include/o3tl/cow_wrapper.hxx
Warning:line 40, column 61
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name poly2.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D TOOLS_DLLIMPLEMENTATION -D SYSTEM_ZLIB -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/tools/inc -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx

/home/maarten/src/libreoffice/core/tools/source/generic/poly2.cxx

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
31namespace tools {
32
33PolyPolygon::PolyPolygon( sal_uInt16 nInitSize )
34 : mpImplPolyPolygon( ImplPolyPolygon( nInitSize ) )
35{
36}
37
38PolyPolygon::PolyPolygon( const tools::Polygon& rPoly )
39 : mpImplPolyPolygon( rPoly )
40{
41}
42
43PolyPolygon::PolyPolygon( const tools::PolyPolygon& rPolyPoly )
44 : mpImplPolyPolygon( rPolyPoly.mpImplPolyPolygon )
45{
46}
47
48PolyPolygon::PolyPolygon( tools::PolyPolygon&& rPolyPoly ) noexcept
49 : mpImplPolyPolygon( std::move(rPolyPoly.mpImplPolyPolygon) )
50{
51}
52
53PolyPolygon::~PolyPolygon()
54{
55}
22
Calling '~cow_wrapper'
27
Returning from '~cow_wrapper'
56
57void 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
67void 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
74void 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
81const 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
88bool PolyPolygon::IsRect() const
89{
90 bool bIsRect = false;
91 if ( Count() == 1 )
92 bIsRect = mpImplPolyPolygon->mvPolyAry[ 0 ].IsRect();
93 return bIsRect;
94}
95
96void PolyPolygon::Clear()
97{
98 mpImplPolyPolygon->mvPolyAry.clear();
99}
100
101void PolyPolygon::Optimize( PolyOptimizeFlags nOptimizeFlags )
102{
103 if(!(bool(nOptimizeFlags
11.1
'nOptimizeFlags' is not equal to 0
11.1
'nOptimizeFlags' is not equal to 0
)
&& Count())
)
1
Assuming 'nOptimizeFlags' is not equal to 0
2
Assuming the condition is false
3
Taking false branch
12
Assuming the condition is false
13
Taking false branch
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
3.1
'bIsCurve' is false
7.1
'bIsCurve' is true
13.1
'bIsCurve' is false
17.1
'bIsCurve' is true
3.1
'bIsCurve' is false
7.1
'bIsCurve' is true
13.1
'bIsCurve' is false
17.1
'bIsCurve' is true
&& a < Count(); a++)
4
Assuming the condition is true
5
Loop condition is true. Entering loop body
14
Assuming the condition is true
15
Loop condition is true. Entering loop body
110 {
111 if((*this)[a].HasFlags())
6
Assuming the condition is true
7
Taking true branch
16
Assuming the condition is true
17
Taking true branch
112 {
113 bIsCurve = true;
114 }
115 }
116
117 if(bIsCurve
7.2
'bIsCurve' is true
17.2
'bIsCurve' is true
7.2
'bIsCurve' is true
17.2
'bIsCurve' is true
)
8
Taking true branch
18
Taking true branch
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)
;
9
Taking true branch
10
Loop condition is false. Exiting loop
19
Taking true branch
20
Loop condition is false. Exiting loop
120 tools::PolyPolygon aPolyPoly;
121
122 AdaptiveSubdivide(aPolyPoly);
123 aPolyPoly.Optimize(nOptimizeFlags);
11
Calling 'PolyPolygon::Optimize'
29
Returning; memory was released
124 *this = aPolyPoly;
30
Calling copy assignment operator for 'PolyPolygon'
125 }
21
Calling '~PolyPolygon'
28
Returning from '~PolyPolygon'
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
156void 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
169tools::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
180void PolyPolygon::GetIntersection( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult ) const
181{
182 ImplDoOperation( rPolyPoly, rResult, PolyClipOp::INTERSECT );
183}
184
185void PolyPolygon::GetUnion( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult ) const
186{
187 ImplDoOperation( rPolyPoly, rResult, PolyClipOp::UNION );
188}
189
190void 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
226sal_uInt16 PolyPolygon::Count() const
227{
228 return mpImplPolyPolygon->mvPolyAry.size();
229}
230
231void 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
243void 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
250void 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
257void 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
268void 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
275void 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
294tools::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
336Polygon& 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
343PolyPolygon& PolyPolygon::operator=( const tools::PolyPolygon& rPolyPoly )
344{
345 mpImplPolyPolygon = rPolyPoly.mpImplPolyPolygon;
31
Calling copy assignment operator for 'cow_wrapper<ImplPolyPolygon, o3tl::UnsafeRefCountingPolicy>'
346 return *this;
347}
348
349PolyPolygon& PolyPolygon::operator=( tools::PolyPolygon&& rPolyPoly ) noexcept
350{
351 mpImplPolyPolygon = std::move(rPolyPoly.mpImplPolyPolygon);
352 return *this;
353}
354
355bool PolyPolygon::operator==( const tools::PolyPolygon& rPolyPoly ) const
356{
357 return rPolyPoly.mpImplPolyPolygon == mpImplPolyPolygon;
358}
359
360SvStream& 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
393SvStream& 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
406void 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
439void 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
453basegfx::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
467PolyPolygon::PolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
468 : mpImplPolyPolygon(rPolyPolygon)
469{
470}
471
472} /* namespace tools */
473
474ImplPolyPolygon::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: */

/home/maarten/src/libreoffice/core/include/o3tl/cow_wrapper.hxx

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
29namespace 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; }
33
Use of memory after it is freed
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>
108class cow_wrapper_client_impl;
109
110class cow_wrapper_client
111{
112public:
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
124private:
125 o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
126};
127 </pre>
128 and the implementation file would look like this:
129 <pre>
130class cow_wrapper_client_impl
131{
132public:
133 void setValue( int nVal ) { mnValue = nVal; }
134 int getValue() const { return mnValue; }
135
136private:
137 int mnValue;
138}
139
140cow_wrapper_client::cow_wrapper_client() :
141 maImpl()
142{
143}
144cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
145 maImpl( rSrc.maImpl )
146{
147}
148cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) :
149 maImpl( std::move( rSrc.maImpl ) )
150{
151}
152cow_wrapper_client::~cow_wrapper_client()
153{
154}
155cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
156{
157 maImpl = rSrc.maImpl;
158 return *this;
159}
160cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
161{
162 maImpl = std::move( rSrc.maImpl );
163 return *this;
164}
165void cow_wrapper_client::modify( int nVal )
166{
167 maImpl->setValue( nVal );
168}
169int 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
23.1
Field 'm_pimpl' is non-null
23.1
Field 'm_pimpl' is non-null
&& !MTPolicy::decrementCount(m_pimpl->m_ref_count) )
24
Taking true branch
210 {
211 delete m_pimpl;
25
Memory is released
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();
23
Calling 'cow_wrapper::release'
26
Returning; memory was released
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 );
32
Calling 'UnsafeRefCountingPolicy::incrementCount'
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: */