source: vendor/trolltech/current/src/tools/qgarray.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 19.2 KB
Line 
1/****************************************************************************
2** $Id: qgarray.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of QGArray class
5**
6** Created : 930906
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the tools module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qglobal.h"
39#if defined(Q_CC_BOR)
40 // needed for qsort() because of a std namespace problem on Borland
41# include "qplatformdefs.h"
42#elif defined(Q_WS_WIN)
43 // needed for bsearch on some platforms
44# include "qt_windows.h"
45#endif
46
47#define QGARRAY_CPP
48#include "qgarray.h"
49#include <stdlib.h>
50#include <string.h>
51
52#ifdef QT_THREAD_SUPPORT
53# include <private/qmutexpool_p.h>
54#endif // QT_THREAD_SUPPORT
55
56/*
57 If USE_MALLOC isn't defined, we use new[] and delete[] to allocate
58 memory. The documentation for QMemArray<T>::assign() explicitly
59 mentions that the array is freed using free(), so don't mess around
60 with USE_MALLOC unless you know what you're doing.
61*/
62#define USE_MALLOC
63
64#undef NEW
65#undef DELETE
66
67#if defined(USE_MALLOC)
68#define NEW(type,size) ((type*)malloc(size*sizeof(type)))
69#define DELETE(array) (free((char*)array))
70#else
71#define NEW(type,size) (new type[size])
72#define DELETE(array) (delete[] array)
73#define DONT_USE_REALLOC // comment to use realloc()
74#endif
75
76/*!
77 \class QShared qshared.h
78 \reentrant
79 \ingroup shared
80 \brief The QShared class is used internally for implementing shared classes.
81
82 \internal
83
84 It only contains a reference count and member functions to increment and
85 decrement it.
86
87 Shared classes normally have internal classes that inherit QShared and
88 add the shared data.
89
90 \sa \link shclass.html Shared Classes\endlink
91*/
92
93/*!
94 \class QGArray qgarray.h
95 \reentrant
96 \ingroup shared
97 \ingroup collection
98 \brief The QGArray class is an internal class for implementing the QMemArray class.
99
100 \internal
101
102 QGArray is a strictly internal class that acts as base class for the
103 QMemArray template array.
104
105 It contains an array of bytes and has no notion of an array element.
106*/
107
108
109/*!
110 Constructs a null array.
111*/
112
113QGArray::QGArray()
114{
115 shd = newData();
116 Q_CHECK_PTR( shd );
117}
118
119/*!
120 Dummy constructor; does not allocate any data.
121
122 This constructor does not initialize any array data so subclasses
123 must do it. The intention is to make the code more efficient.
124*/
125
126QGArray::QGArray( int, int )
127{
128}
129
130/*!
131 Constructs an array with room for \a size bytes.
132*/
133
134QGArray::QGArray( int size )
135{
136 if ( size < 0 ) {
137#if defined(QT_CHECK_RANGE)
138 qWarning( "QGArray: Cannot allocate array with negative length" );
139#endif
140 size = 0;
141 }
142 shd = newData();
143 Q_CHECK_PTR( shd );
144 if ( size == 0 ) // zero length
145 return;
146 shd->data = NEW(char,size);
147 Q_CHECK_PTR( shd->data );
148 shd->len =
149#ifdef QT_QGARRAY_SPEED_OPTIM
150 shd->maxl =
151#endif
152 size;
153}
154
155/*!
156 Constructs a shallow copy of \a a.
157*/
158
159QGArray::QGArray( const QGArray &a )
160{
161 shd = a.shd;
162 shd->ref();
163}
164
165/*!
166 Dereferences the array data and deletes it if this was the last
167 reference.
168*/
169
170QGArray::~QGArray()
171{
172 if ( shd && shd->deref() ) { // delete when last reference
173 if ( shd->data ) // is lost
174 DELETE(shd->data);
175 deleteData( shd );
176 shd = 0;
177 }
178}
179
180
181/*!
182 \fn QGArray &QGArray::operator=( const QGArray &a )
183
184 Assigns a shallow copy of \a a to this array and returns a reference to
185 this array. Equivalent to assign().
186*/
187
188/*!
189 \fn void QGArray::detach()
190
191 Detaches this array from shared array data.
192*/
193
194/*!
195 \fn char *QGArray::data() const
196
197 Returns a pointer to the actual array data.
198*/
199
200/*!
201 \fn uint QGArray::nrefs() const
202
203 Returns the reference count.
204*/
205
206/*!
207 \fn uint QGArray::size() const
208
209 Returns the size of the array, in bytes.
210*/
211
212
213/*!
214 Returns TRUE if this array is equal to \a a, otherwise FALSE.
215 The comparison is bitwise, of course.
216*/
217
218bool QGArray::isEqual( const QGArray &a ) const
219{
220 if ( size() != a.size() ) // different size
221 return FALSE;
222 if ( data() == a.data() ) // has same data
223 return TRUE;
224 return (size() ? memcmp( data(), a.data(), size() ) : 0) == 0;
225}
226
227
228/*!
229 Resizes the array to \a newsize bytes. \a optim is either
230 MemOptim (the default) or SpeedOptim.
231*/
232bool QGArray::resize( uint newsize, Optimization optim )
233{
234#ifndef QT_QGARRAY_SPEED_OPTIM
235 Q_UNUSED(optim);
236#endif
237
238 if ( newsize == shd->len
239#ifdef QT_QGARRAY_SPEED_OPTIM
240 && newsize == shd->maxl
241#endif
242 ) // nothing to do
243 return TRUE;
244 if ( newsize == 0 ) { // remove array
245 if ( shd->data )
246 DELETE(shd->data);
247 shd->data = 0;
248 shd->len = 0;
249#ifdef QT_QGARRAY_SPEED_OPTIM
250 shd->maxl = 0;
251#endif
252 return TRUE;
253 }
254
255 uint newmaxl = newsize;
256#ifdef QT_QGARRAY_SPEED_OPTIM
257 if ( optim == SpeedOptim ) {
258 if ( newsize <= shd->maxl &&
259 ( newsize * 4 > shd->maxl || shd->maxl <= 4 ) ) {
260 shd->len = newsize;
261 return TRUE;
262 }
263 newmaxl = 4;
264 while ( newmaxl < newsize )
265 newmaxl *= 2;
266 // try to spare some memory
267 if ( newmaxl >= 1024 * 1024 && newsize <= newmaxl - (newmaxl >> 2) )
268 newmaxl -= newmaxl >> 2;
269 }
270 shd->maxl = newmaxl;
271#endif
272
273 if ( shd->data ) { // existing data
274#if defined(DONT_USE_REALLOC)
275 char *newdata = NEW(char,newsize); // manual realloc
276 memcpy( newdata, shd->data, QMIN(shd->len,newmaxl) );
277 DELETE(shd->data);
278 shd->data = newdata;
279#else
280 shd->data = (char *)realloc( shd->data, newmaxl );
281#endif
282 } else {
283 shd->data = NEW(char,newmaxl);
284 }
285 if ( !shd->data ) // no memory
286 return FALSE;
287 shd->len = newsize;
288 return TRUE;
289}
290
291/*!\overload
292*/
293bool QGArray::resize( uint newsize )
294{
295 return resize( newsize, MemOptim );
296}
297
298
299/*!
300 Fills the array with the repeated occurrences of \a d, which is
301 \a sz bytes long.
302 If \a len is specified as different from -1, then the array will be
303 resized to \a len*sz before it is filled.
304
305 Returns TRUE if successful, or FALSE if the memory cannot be allocated
306 (only when \a len != -1).
307
308 \sa resize()
309*/
310
311bool QGArray::fill( const char *d, int len, uint sz )
312{
313 if ( len < 0 )
314 len = shd->len/sz; // default: use array length
315 else if ( !resize( len*sz ) )
316 return FALSE;
317 if ( sz == 1 ) // 8 bit elements
318 memset( data(), *d, len );
319 else if ( sz == 4 ) { // 32 bit elements
320 register Q_INT32 *x = (Q_INT32*)data();
321 Q_INT32 v = *((Q_INT32*)d);
322 while ( len-- )
323 *x++ = v;
324 } else if ( sz == 2 ) { // 16 bit elements
325 register Q_INT16 *x = (Q_INT16*)data();
326 Q_INT16 v = *((Q_INT16*)d);
327 while ( len-- )
328 *x++ = v;
329 } else { // any other size elements
330 register char *x = data();
331 while ( len-- ) { // more complicated
332 memcpy( x, d, sz );
333 x += sz;
334 }
335 }
336 return TRUE;
337}
338
339/*!
340 \overload
341 Shallow copy. Dereference the current array and references the data
342 contained in \a a instead. Returns a reference to this array.
343 \sa operator=()
344*/
345
346QGArray &QGArray::assign( const QGArray &a )
347{
348 a.shd->ref(); // avoid 'a = a'
349 if ( shd->deref() ) { // delete when last reference
350 if ( shd->data ) // is lost
351 DELETE(shd->data);
352 deleteData( shd );
353 }
354 shd = a.shd;
355 return *this;
356}
357
358/*!
359 Shallow copy. Dereference the current array and references the
360 array data \a d, which contains \a len bytes.
361 Returns a reference to this array.
362
363 Do not delete \a d later, because QGArray takes care of that.
364*/
365
366QGArray &QGArray::assign( const char *d, uint len )
367{
368 if ( shd->count > 1 ) { // disconnect this
369 shd->count--;
370 shd = newData();
371 Q_CHECK_PTR( shd );
372 } else {
373 if ( shd->data )
374 DELETE(shd->data);
375 }
376 shd->data = (char *)d;
377 shd->len =
378#ifdef QT_QGARRAY_SPEED_OPTIM
379 shd->maxl =
380#endif
381 len;
382 return *this;
383}
384
385/*!
386 Deep copy. Dereference the current array and obtains a copy of the data
387 contained in \a a instead. Returns a reference to this array.
388 \sa assign(), operator=()
389*/
390
391QGArray &QGArray::duplicate( const QGArray &a )
392{
393 if ( a.shd == shd ) { // a.duplicate(a) !
394 if ( shd->count > 1 ) {
395 shd->count--;
396 register array_data *n = newData();
397 Q_CHECK_PTR( n );
398 if ( (n->len=shd->len) ) {
399 n->data = NEW(char,n->len);
400 Q_CHECK_PTR( n->data );
401 if ( n->data )
402 memcpy( n->data, shd->data, n->len );
403 } else {
404 n->data = 0;
405 }
406 shd = n;
407 }
408 return *this;
409 }
410 char *oldptr = 0;
411 if ( shd->count > 1 ) { // disconnect this
412 shd->count--;
413 shd = newData();
414 Q_CHECK_PTR( shd );
415 } else { // delete after copy was made
416 oldptr = shd->data;
417 }
418 if ( a.shd->len ) { // duplicate data
419 shd->data = NEW(char,a.shd->len);
420 Q_CHECK_PTR( shd->data );
421 if ( shd->data )
422 memcpy( shd->data, a.shd->data, a.shd->len );
423 } else {
424 shd->data = 0;
425 }
426 shd->len =
427#ifdef QT_QGARRAY_SPEED_OPTIM
428 shd->maxl =
429#endif
430 a.shd->len;
431 if ( oldptr )
432 DELETE(oldptr);
433 return *this;
434}
435
436/*!
437 \overload
438 Deep copy. Dereferences the current array and obtains a copy of
439 \a len characters from array data \a d instead. Returns a reference
440 to this array.
441 \sa assign(), operator=()
442*/
443
444QGArray &QGArray::duplicate( const char *d, uint len )
445{
446 char *data;
447 if ( d == 0 || len == 0 ) {
448 data = 0;
449 len = 0;
450 } else {
451 if ( shd->count == 1 && shd->len == len ) {
452 if ( shd->data != d ) // avoid self-assignment
453 memcpy( shd->data, d, len ); // use same buffer
454 return *this;
455 }
456 data = NEW(char,len);
457 Q_CHECK_PTR( data );
458 memcpy( data, d, len );
459 }
460 if ( shd->count > 1 ) { // detach
461 shd->count--;
462 shd = newData();
463 Q_CHECK_PTR( shd );
464 } else { // just a single reference
465 if ( shd->data )
466 DELETE(shd->data);
467 }
468 shd->data = data;
469 shd->len =
470#ifdef QT_QGARRAY_SPEED_OPTIM
471 shd->maxl =
472#endif
473 len;
474 return *this;
475}
476
477/*!
478 Resizes this array to \a len bytes and copies the \a len bytes at
479 address \a d into it.
480
481 \warning This function disregards the reference count mechanism. If
482 other QGArrays reference the same data as this, all will be updated.
483*/
484
485void QGArray::store( const char *d, uint len )
486{ // store, but not deref
487 resize( len );
488 memcpy( shd->data, d, len );
489}
490
491
492/*!
493 \fn array_data *QGArray::sharedBlock() const
494
495 Returns a pointer to the shared array block.
496
497 \warning
498
499 Do not use this function. Using it is begging for trouble. We dare
500 not remove it, for fear of breaking code, but we \e strongly
501 discourage new use of it.
502*/
503
504/*!
505 \fn void QGArray::setSharedBlock( array_data *p )
506
507 Sets the shared array block to \a p.
508
509 \warning
510
511 Do not use this function. Using it is begging for trouble. We dare
512 not remove it, for fear of breaking code, but we \e strongly
513 discourage new use of it.
514*/
515
516
517/*!
518 Sets raw data and returns a reference to the array.
519
520 Dereferences the current array and sets the new array data to \a d and
521 the new array size to \a len. Do not attempt to resize or re-assign the
522 array data when raw data has been set.
523 Call resetRawData(d,len) to reset the array.
524
525 Setting raw data is useful because it sets QMemArray data without
526 allocating memory or copying data.
527
528 Example of intended use:
529 \code
530 static uchar bindata[] = { 231, 1, 44, ... };
531 QByteArray a;
532 a.setRawData( bindata, sizeof(bindata) ); // a points to bindata
533 QDataStream s( a, IO_ReadOnly ); // open on a's data
534 s >> <something>; // read raw bindata
535 s.close();
536 a.resetRawData( bindata, sizeof(bindata) ); // finished
537 \endcode
538
539 Example of misuse (do not do this):
540 \code
541 static uchar bindata[] = { 231, 1, 44, ... };
542 QByteArray a, b;
543 a.setRawData( bindata, sizeof(bindata) ); // a points to bindata
544 a.resize( 8 ); // will crash
545 b = a; // will crash
546 a[2] = 123; // might crash
547 // forget to resetRawData - will crash
548 \endcode
549
550 \warning If you do not call resetRawData(), QGArray will attempt to
551 deallocate or reallocate the raw data, which might not be too good.
552 Be careful.
553*/
554
555QGArray &QGArray::setRawData( const char *d, uint len )
556{
557 duplicate( 0, 0 ); // set null data
558 shd->data = (char *)d;
559 shd->len = len;
560 return *this;
561}
562
563/*!
564 Resets raw data.
565
566 The arguments must be the data, \a d, and length \a len, that were
567 passed to setRawData(). This is for consistency checking.
568*/
569
570void QGArray::resetRawData( const char *d, uint len )
571{
572 if ( d != shd->data || len != shd->len ) {
573#if defined(QT_CHECK_STATE)
574 qWarning( "QGArray::resetRawData: Inconsistent arguments" );
575#endif
576 return;
577 }
578 shd->data = 0;
579 shd->len = 0;
580}
581
582
583/*!
584 Finds the first occurrence of \a d in the array from position \a index,
585 where \a sz is the size of the \a d element.
586
587 Note that \a index is given in units of \a sz, not bytes.
588
589 This function only compares whole cells, not bytes.
590*/
591
592int QGArray::find( const char *d, uint index, uint sz ) const
593{
594 index *= sz;
595 if ( index >= shd->len ) {
596#if defined(QT_CHECK_RANGE)
597 qWarning( "QGArray::find: Index %d out of range", index/sz );
598#endif
599 return -1;
600 }
601 register uint i;
602 uint ii;
603 switch ( sz ) {
604 case 1: { // 8 bit elements
605 register char *x = data() + index;
606 char v = *d;
607 for ( i=index; i<shd->len; i++ ) {
608 if ( *x++ == v )
609 break;
610 }
611 ii = i;
612 }
613 break;
614 case 2: { // 16 bit elements
615 register Q_INT16 *x = (Q_INT16*)(data() + index);
616 Q_INT16 v = *((Q_INT16*)d);
617 for ( i=index; i<shd->len; i+=2 ) {
618 if ( *x++ == v )
619 break;
620 }
621 ii = i/2;
622 }
623 break;
624 case 4: { // 32 bit elements
625 register Q_INT32 *x = (Q_INT32*)(data() + index);
626 Q_INT32 v = *((Q_INT32*)d);
627 for ( i=index; i<shd->len; i+=4 ) {
628 if ( *x++ == v )
629 break;
630 }
631 ii = i/4;
632 }
633 break;
634 default: { // any size elements
635 for ( i=index; i<shd->len; i+=sz ) {
636 if ( memcmp( d, &shd->data[i], sz ) == 0 )
637 break;
638 }
639 ii = i/sz;
640 }
641 break;
642 }
643 return i<shd->len ? (int)ii : -1;
644}
645
646/*!
647 Returns the number of occurrences of \a d in the array, where \a sz is
648 the size of the \a d element.
649
650 This function only compares whole cells, not bytes.
651*/
652
653int QGArray::contains( const char *d, uint sz ) const
654{
655 register uint i = shd->len;
656 int count = 0;
657 switch ( sz ) {
658 case 1: { // 8 bit elements
659 register char *x = data();
660 char v = *d;
661 while ( i-- ) {
662 if ( *x++ == v )
663 count++;
664 }
665 }
666 break;
667 case 2: { // 16 bit elements
668 register Q_INT16 *x = (Q_INT16*)data();
669 Q_INT16 v = *((Q_INT16*)d);
670 i /= 2;
671 while ( i-- ) {
672 if ( *x++ == v )
673 count++;
674 }
675 }
676 break;
677 case 4: { // 32 bit elements
678 register Q_INT32 *x = (Q_INT32*)data();
679 Q_INT32 v = *((Q_INT32*)d);
680 i /= 4;
681 while ( i-- ) {
682 if ( *x++ == v )
683 count++;
684 }
685 }
686 break;
687 default: { // any size elements
688 for ( i=0; i<shd->len; i+=sz ) {
689 if ( memcmp(d, &shd->data[i], sz) == 0 )
690 count++;
691 }
692 }
693 break;
694 }
695 return count;
696}
697
698static int cmp_item_size = 0;
699
700#if defined(Q_C_CALLBACKS)
701extern "C" {
702#endif
703
704#ifdef Q_OS_TEMP
705static int __cdecl cmp_arr( const void *n1, const void *n2 )
706#else
707static int cmp_arr( const void *n1, const void *n2 )
708#endif
709{
710 return ( n1 && n2 ) ? memcmp( n1, n2, cmp_item_size )
711 : ( n1 ? 1 : ( n2 ? -1 : 0 ) );
712 // ### Qt 3.0: Add a virtual compareItems() method and call that instead
713}
714
715#if defined(Q_C_CALLBACKS)
716}
717#endif
718
719/*!
720 Sorts the first \a sz items of the array.
721*/
722
723void QGArray::sort( uint sz )
724{
725 int numItems = size() / sz;
726 if ( numItems < 2 )
727 return;
728
729#ifdef QT_THREAD_SUPPORT
730 QMutexLocker locker( qt_global_mutexpool ?
731 qt_global_mutexpool->get( &cmp_item_size ) : 0 );
732#endif // QT_THREAD_SUPPORT
733
734 cmp_item_size = sz;
735 qsort( shd->data, numItems, sz, cmp_arr );
736}
737
738/*!
739 Binary search; assumes that \a d is a sorted array of size \a sz.
740*/
741
742int QGArray::bsearch( const char *d, uint sz ) const
743{
744 int numItems = size() / sz;
745 if ( !numItems )
746 return -1;
747
748#ifdef QT_THREAD_SUPPORT
749 QMutexLocker locker( qt_global_mutexpool ?
750 qt_global_mutexpool->get( &cmp_item_size ) : 0 );
751#endif // QT_THREAD_SUPPORT
752
753 cmp_item_size = sz;
754 char* r = (char*)::bsearch( d, shd->data, numItems, sz, cmp_arr );
755 if ( !r )
756 return -1;
757 while( (r >= shd->data + sz) && (cmp_arr( r - sz, d ) == 0) )
758 r -= sz; // search to first of equal elements; bsearch is undef
759 return (int)(( r - shd->data ) / sz);
760}
761
762
763/*!
764 \fn char *QGArray::at( uint index ) const
765
766 Returns a pointer to the byte at offset \a index in the array.
767*/
768
769/*!
770 Expand the array if necessary, and copies (the first part of) its
771 contents from the \a index * \a sz bytes at \a d.
772
773 Returns TRUE if the operation succeeds, FALSE if it runs out of
774 memory.
775
776 \warning This function disregards the reference count mechanism. If
777 other QGArrays reference the same data as this, all will be changed.
778*/
779
780bool QGArray::setExpand( uint index, const char *d, uint sz )
781{
782 index *= sz;
783 if ( index >= shd->len ) {
784 if ( !resize( index+sz ) ) // no memory
785 return FALSE;
786 }
787 memcpy( data() + index, d, sz );
788 return TRUE;
789}
790
791
792/*!
793 Prints a warning message if at() or [] is given a bad index.
794*/
795
796void QGArray::msg_index( uint index )
797{
798#if defined(QT_CHECK_RANGE)
799 qWarning( "QGArray::at: Absolute index %d out of range", index );
800#else
801 Q_UNUSED( index )
802#endif
803}
804
805
806/*!
807 Returns a new shared array block.
808*/
809
810QGArray::array_data * QGArray::newData()
811{
812 return new array_data;
813}
814
815
816/*!
817 Deletes the shared array block \a p.
818*/
819
820void QGArray::deleteData( array_data *p )
821{
822 delete p;
823}
Note: See TracBrowser for help on using the repository browser.