sl@0: // Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32\euser\cbase\ub_buf.cpp sl@0: // sl@0: // sl@0: sl@0: #include "ub_std.h" sl@0: sl@0: class TBufSegLink : public TDblQueLink sl@0: { sl@0: public: sl@0: inline TBufSegLink() : iLen(0) {} sl@0: inline TBufSegLink *Next() const {return((TBufSegLink *)iNext);} sl@0: inline TBufSegLink *Prev() const {return((TBufSegLink *)iPrev);} sl@0: public: sl@0: TInt iLen; sl@0: }; sl@0: sl@0: EXPORT_C CBufBase::CBufBase(TInt anExpandSize) sl@0: // sl@0: // Constructor sl@0: // sl@0: /** sl@0: @internalComponent sl@0: */ sl@0: { sl@0: sl@0: __ASSERT_ALWAYS(anExpandSize>=0,Panic(EBufExpandSizeNegative)); sl@0: // iSize=0; sl@0: iExpandSize=anExpandSize; sl@0: } sl@0: sl@0: EXPORT_C CBufBase::~CBufBase() sl@0: /** sl@0: Destructor sl@0: */ sl@0: { sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::Reset() sl@0: /** sl@0: Deletes all data in the buffer. sl@0: sl@0: Its behaviour is the same as calling Delete(0,Size()). sl@0: The buffer is compressed before the function returns. sl@0: */ sl@0: { sl@0: sl@0: if (iSize) sl@0: Delete(0,iSize); sl@0: Compress(); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::Read(TInt aPos,TDes8 &aDes) const sl@0: // sl@0: // Read up to aDes.MaxLength() bytes. sl@0: // sl@0: /** sl@0: Reads data from the buffer into a descriptor. sl@0: sl@0: Data, starting at the specified buffer position is written to the descriptor, sl@0: filling the descriptor. sl@0: sl@0: @param aPos Buffer position from which data is read: must be in range zero sl@0: to Size(). sl@0: @param aDes On return, contains the data read from the buffer; its MaxLength() sl@0: specifies the amount of data to be read. sl@0: */ sl@0: { sl@0: sl@0: Read(aPos,aDes,aDes.MaxLength()); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::Read(TInt aPos,TDes8 &aDes,TInt aLength) const sl@0: /** sl@0: Reads the specified number of bytes of data from the buffer into a descriptor. sl@0: sl@0: @param aPos Buffer position from which data is read: must be in range zero sl@0: to (Size() minus the length of the data to be read). sl@0: @param aDes On return, contains data read from the buffer. sl@0: @param aLength The length of the data to be read. sl@0: */ sl@0: { sl@0: sl@0: aDes.SetLength(aLength); sl@0: Read(aPos,(TAny *)aDes.Ptr(),aLength); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::Read(TInt aPos,TAny *aPtr,TInt aLength) const sl@0: /** sl@0: Reads the specified number of bytes of data from the buffer into a specified sl@0: address. sl@0: sl@0: @param aPos Buffer position from which data is read: must be in range zero sl@0: to (Size() minus the length of the data to be read). sl@0: @param aPtr The address into which the data should be read. sl@0: @param aLength The length of the data to be read. sl@0: */ sl@0: { sl@0: sl@0: if (aLength==0) sl@0: return; sl@0: __ASSERT_ALWAYS(aLength>0,Panic(EBufReadLengthNegative)); sl@0: __ASSERT_ALWAYS((aPos+aLength)<=iSize,Panic(EBufReadBeyondEnd)); sl@0: TUint8 *pT=(TUint8 *)aPtr; sl@0: while (aLength) sl@0: { sl@0: TPtr8 p=((CBufBase *)this)->Ptr(aPos); sl@0: TInt s=Min(p.Length(),aLength); sl@0: pT=Mem::Copy(pT,p.Ptr(),s); sl@0: aLength-=s; sl@0: aPos+=s; sl@0: } sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::Write(TInt aPos,const TDesC8 &aDes) sl@0: // sl@0: // Write aDes.Length() characters to the buffer. Does not cause any expansion. sl@0: // sl@0: /** sl@0: Writes data from a descriptor to the buffer. sl@0: sl@0: The data in the descriptor overwrites the data in the buffer from the insertion sl@0: point onwards. sl@0: sl@0: No new space is allocated; this function cannot fail (provided the parameters sl@0: are specified within the bounds of the buffer and descriptor). sl@0: sl@0: No shuffling occurs; new data is written to the memory locations occupied sl@0: by the data it overwrites. sl@0: sl@0: @param aPos Buffer position at which data will begin to be written; must be sl@0: in range zero to (Size() minus the length of the data sl@0: to be written). sl@0: @param aDes Contains the data to be written. The length of data to be written sl@0: is the descriptor length. sl@0: */ sl@0: { sl@0: sl@0: Write(aPos,aDes.Ptr(),aDes.Length()); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::Write(TInt aPos,const TDesC8 &aDes,TInt aLength) sl@0: // sl@0: // Write aDes.Length() characters to the buffer. Does not cause any expansion. sl@0: // sl@0: /** sl@0: Writes the specified number of bytes of data from a descriptor to the buffer. sl@0: sl@0: The data in the descriptor overwrites the data in the buffer from the insertion sl@0: point onwards. sl@0: sl@0: No new space is allocated; this function cannot fail (provided the parameters sl@0: are specified within the bounds of the buffer and descriptor). sl@0: sl@0: No shuffling occurs; new data is written to the memory locations occupied sl@0: by the data it overwrites. sl@0: sl@0: @param aPos Buffer position at which data will begin to be written; must be sl@0: in range zero to (Size() minus the length of the data to sl@0: be written). sl@0: @param aDes Contains the data to be written. sl@0: @param aLength The length of the data to be written. sl@0: */ sl@0: { sl@0: sl@0: Write(aPos,aDes.Ptr(),aLength); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::Write(TInt aPos,const TAny *aPtr,TInt aLength) sl@0: /** sl@0: Writes the specified number of bytes of data from the specified address to the sl@0: buffer. sl@0: sl@0: The data in the buffer is overwritten from the insertion point onwards. sl@0: sl@0: No new space is allocated; this function cannot fail (provided the parameters sl@0: are specified within the bounds of the buffer and descriptor). sl@0: sl@0: No shuffling occurs: new data is written to the memory locations occupied sl@0: by the data it overwrites. sl@0: sl@0: @param aPos Buffer position at which data will begin to be written; must be sl@0: in range zero to (Size() minus the length of the data to sl@0: be written). sl@0: @param aPtr The address of the data to be written. sl@0: @param aLength The length of the data to be written. sl@0: sl@0: @panic E32USER-CBase 7, if aLength is not positive sl@0: @panic E32USER-CBase 5, if aPos + aLength is greater than the number of sl@0: data bytes in the buffer, i.e. if the target appears sl@0: to be outside the buffer. sl@0: */ sl@0: { sl@0: sl@0: if (aLength==0) sl@0: return; sl@0: __ASSERT_ALWAYS(aLength>0,Panic(EBufWriteLengthNegative)); sl@0: __ASSERT_ALWAYS((aPos+aLength)<=iSize,Panic(EBufWriteBeyondEnd)); sl@0: const TUint8 *pS=(const TUint8 *)aPtr; sl@0: while (aLength) sl@0: { sl@0: TPtr8 p=Ptr(aPos); sl@0: TInt s=Min(p.Length(),aLength); sl@0: Mem::Copy((TAny *)p.Ptr(),pS,s); sl@0: pS+=s; sl@0: aLength-=s; sl@0: aPos+=s; sl@0: } sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::InsertL(TInt aPos,const TDesC8 &aDes) sl@0: // sl@0: // Insert aDes.Length() bytes into the buffer. sl@0: // sl@0: /** sl@0: Inserts data into the buffer. sl@0: sl@0: Data at and beyond the insertion position is moved to make way for the inserted sl@0: data. Data before the insertion position remains in place. sl@0: sl@0: Notes: sl@0: sl@0: 1. Insertion may require more buffer space to be allocated. sl@0: sl@0: 2. In the case of flat buffers, the buffer is extended by a ReAllocL() of the sl@0: buffer's heap cell, to the smallest multiple of the granularity that will sl@0: contain the data required. If this reallocation fails, the insertion is sl@0: impossible and a leave occurs. sl@0: sl@0: 3. In the case of segmented buffers, a reallocation is performed if the segment sl@0: containing the insertion position has insufficient space, and sl@0: immediately-neighbouring segments cannot be used to contain the new data. sl@0: As many new segments as are necessary to contain the inserted data are sl@0: allocated. Each new segment's length is the buffer's granularity. sl@0: If extension or new allocation fails, a leave occurs. sl@0: sl@0: 4. Insertion may also require data to be shuffled. In the case of flat buffers, sl@0: data beyond the insertion point is shuffled up to create a gap; the new data sl@0: is then inserted into this gap. In the case of segmented buffers, shuffling sl@0: is minimised by inserting the new data into newly-allocated buffers, and sl@0: shuffling only immediately-neighbouring buffers if possible. This may result sl@0: in some wastage of space, but is much more time-efficient for large amounts sl@0: of data. sl@0: sl@0: @param aPos Buffer position before which the data will be inserted; must be sl@0: in range zero to Size(). sl@0: @param aDes The data to be inserted; the length of the data is the descriptor sl@0: length. sl@0: sl@0: @leave KErrNoMemory If the insertion requires a bigger buffer, and the sl@0: necessary allocation or re-allocation fails. sl@0: */ sl@0: { sl@0: sl@0: InsertL(aPos,aDes.Ptr(),aDes.Length()); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::InsertL(TInt aPos,const TDesC8 &aDes,TInt aLength) sl@0: // sl@0: // Insert aLength bytes into the buffer. sl@0: // sl@0: /** sl@0: Inserts the specified number of bytes of data from a descriptor into sl@0: the buffer. sl@0: sl@0: aLength bytes of data from aDes are inserted into the buffer at aPos. Data at sl@0: and beyond the insertion position is moved to make way for the inserted data. sl@0: Data before the insertion position remains in place. sl@0: sl@0: Notes: sl@0: sl@0: 1. Insertion may require more buffer space to be allocated. sl@0: sl@0: 2. In the case of flat buffers, the buffer is extended by a ReAllocL() of the sl@0: buffer's heap cell, to the smallest multiple of the granularity that will sl@0: contain the data required. If this reallocation fails, the insertion is sl@0: impossible and a leave occurs. sl@0: sl@0: 3. In the case of segmented buffers, a reallocation is performed if the segment sl@0: containing the insertion position has insufficient space, and sl@0: immediately-neighbouring segments cannot be used to contain the new data. sl@0: As many new segments as are necessary to contain the inserted data are sl@0: allocated. Each new segment's length is the buffer's granularity. sl@0: If extension or new allocation fails, a leave occurs. sl@0: sl@0: 4. Insertion may also require data to be shuffled. In the case of flat buffers, sl@0: data beyond the insertion point is shuffled up to create a gap: the new data sl@0: is then inserted into this gap. In the case of segmented buffers, shuffling sl@0: is minimised by inserting the new data into newly-allocated buffers, sl@0: and shuffling only immediately-neighbouring buffers if possible. sl@0: This may result in some wastage of space, but is much more time-efficient sl@0: for large amounts of data. sl@0: sl@0: @param aPos Buffer position before which the data will be inserted; must be sl@0: in range zero to Size(). sl@0: @param aDes The data to be inserted. sl@0: @param aLength The length of data to be inserted. sl@0: sl@0: @leave KErrNoMemory If the insertion requires a bigger buffer, and the sl@0: necessary allocation or re-allocation fails. sl@0: */ sl@0: { sl@0: sl@0: InsertL(aPos,aDes.Ptr(),aLength); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::InsertL(TInt aPos,const TAny *aPtr,TInt aLength) sl@0: /** sl@0: Inserts bytes of data from the specified address into the buffer. sl@0: sl@0: Inserts aLength bytes of data found at address aPtr into the buffer at aPos. sl@0: Data at and beyond the insertion position is moved to make way for the inserted sl@0: data. Data before the insertion position remains in place. sl@0: sl@0: Notes: sl@0: sl@0: 1. Insertion may require more buffer space to be allocated. sl@0: sl@0: 2. In the case of flat buffers, the buffer is extended by a ReAllocL() of the sl@0: buffer's heap cell, to the smallest multiple of the granularity that will sl@0: contain the data required. If this reallocation fails, the insertion is sl@0: impossible and a leave occurs. sl@0: sl@0: 2. In the case of segmented buffers, a reallocation is performed if the segment sl@0: containing the insertion position has insufficient space, and sl@0: immediately-neighbouring segments cannot be used to contain the new data. sl@0: As many new segments as are necessary to contain the inserted data are sl@0: allocated. Each new segment's length is the buffer's granularity. sl@0: If extension or new allocation fails, a leave occurs. sl@0: sl@0: 4. Insertion may also require data to be shuffled. In the case of flat buffers, sl@0: data beyond the insertion point is shuffled up to create a gap: the new data sl@0: is then inserted into this gap. In the case of segmented buffers, shuffling sl@0: is minimised by inserting the new data into newly-allocated buffers, and sl@0: shuffling only immediately-neighbouring buffers if possible. This may result sl@0: in some wastage of space, but is much more time-efficient for large amounts sl@0: of data. sl@0: sl@0: @param aPos Buffer position before which the data will be inserted: must be sl@0: in range zero to Size(). sl@0: @param aPtr The address of the data to be inserted. sl@0: @param aLength The length of the data to be inserted. sl@0: sl@0: @leave KErrNoMemory If the insertion requires a bigger buffer, and the sl@0: necessary allocation or re-allocation fails. sl@0: */ sl@0: { sl@0: sl@0: if (aLength==0) sl@0: return; sl@0: __ASSERT_ALWAYS(aLength>0,Panic(EBufInsertLengthNegative)); sl@0: __ASSERT_ALWAYS(aPtr,Panic(EBufInsertBadPtr)); sl@0: DoInsertL(aPos,aPtr,aLength); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::ExpandL(TInt aPos,TInt aLength) sl@0: /** sl@0: Inserts an uninitialised region into the buffer. sl@0: sl@0: Data at and beyond the insertion position is moved to make way for the inserted sl@0: region. Data before the insertion position remains in place. sl@0: sl@0: Note: sl@0: sl@0: 1. The inserted region is not initialised. After using ExpandL(), you should sl@0: then use a series of Write()s to fill this region with data. sl@0: sl@0: 2. Use ExpandL() followed by a series of Write()s when you know the amount of sl@0: data to be inserted, in advance. It is more efficient than a series of sl@0: InsertL()s. In addition, once the result of the ExpandL() has been checked, sl@0: it is guaranteed that the Write()s do not leave, which can sometimes be sl@0: useful. sl@0: sl@0: @param aPos Buffer position before which the region will be inserted; must sl@0: be in range zero to Size(). sl@0: @param aLength The length of the region to be inserted. sl@0: */ sl@0: { sl@0: sl@0: if (aLength==0) sl@0: return; sl@0: __ASSERT_ALWAYS(aLength>0,Panic(EBufInsertLengthNegative)); sl@0: DoInsertL(aPos,NULL,aLength); sl@0: } sl@0: sl@0: EXPORT_C void CBufBase::ResizeL(TInt aSize) sl@0: /** sl@0: Re-sizes the buffer to the specified size. sl@0: sl@0: The new size can be larger or smaller than the existing size. sl@0: sl@0: If the new size is larger than the existing size, the buffer is expanded by sl@0: adding uninitialised data to the end of it. sl@0: sl@0: If the new size is smaller than the existing size, the buffer is reduced; sl@0: any data at the end of the buffer is lost. sl@0: sl@0: Notes: sl@0: sl@0: 1. If the new size is larger than the existing size, the function is equivalent sl@0: to Delete(aSize,Size()-aSize). sl@0: sl@0: 2. If the new size is smaller than the existing size, the function is equivalent sl@0: to ExpandL((Size(),aSize-Size()). sl@0: sl@0: 3. The motivations for using ResizeL() are the same as those for using Delete() sl@0: and ExpandL(). sl@0: sl@0: @param aSize The new size of the buffer; this value must be greater than or sl@0: equal to zero. sl@0: */ sl@0: { sl@0: sl@0: TInt excess=iSize-aSize; sl@0: if (excess>0) sl@0: Delete(aSize,excess); sl@0: else sl@0: ExpandL(iSize,-excess); sl@0: } sl@0: sl@0: EXPORT_C CBufFlat *CBufFlat::NewL(TInt anExpandSize) sl@0: /** sl@0: Allocates and constructs a flat buffer. sl@0: sl@0: If there is insufficient memory available to allocate the flat buffer, the sl@0: function leaves. sl@0: sl@0: @param anExpandSize The granularity of buffer expansion. Additional space, sl@0: when required, is always allocated in multiples of sl@0: this number. Note: although a value of zero is permitted sl@0: by this interface, it has no meaning, and risks raising sl@0: panics later during execution. We suggest that you pass sl@0: a positive value. sl@0: sl@0: @return A pointer to the flat buffer object. sl@0: sl@0: @panic E32USER-CBase 3 if the granularity is negative. sl@0: */ sl@0: { sl@0: sl@0: return(new(ELeave) CBufFlat(anExpandSize)); sl@0: } sl@0: sl@0: EXPORT_C CBufFlat::CBufFlat(TInt anExpandSize) sl@0: // sl@0: // Constructor sl@0: // sl@0: /** sl@0: @internalComponent sl@0: */ sl@0: : CBufBase(anExpandSize) sl@0: { sl@0: sl@0: // iMaxSize=0; sl@0: // iPtr=NULL; sl@0: } sl@0: sl@0: EXPORT_C CBufFlat::~CBufFlat() sl@0: /** sl@0: Destructor. sl@0: sl@0: Frees all resources owned by the object, prior to its destruction. sl@0: Specifically, it frees the allocated cell used as a buffer. sl@0: */ sl@0: { sl@0: sl@0: User::Free(iPtr); sl@0: } sl@0: sl@0: EXPORT_C void CBufFlat::Compress() sl@0: /** sl@0: Compresses the buffer so as to occupy minimal space. sl@0: sl@0: This frees any unused memory at the end of the buffer. sl@0: sl@0: @see CBufBase::Compress sl@0: */ sl@0: { sl@0: sl@0: SetReserveL(iSize); sl@0: } sl@0: sl@0: EXPORT_C void CBufFlat::SetReserveL(TInt aSize) sl@0: /** sl@0: Specifies a minimum amount of space which the flat buffer should occupy. sl@0: sl@0: If the required size is zero, the heap cell is deleted. If it is different sl@0: from the current size, the heap cell is rellocated accordingly. sl@0: sl@0: @param aSize The size of the buffer required. If there is no data in the sl@0: buffer, i.e. Size() returns zero, then this value sl@0: can be zero, which causes the buffer's allocated heap cell sl@0: to be deleted. sl@0: sl@0: @panic E32USER-CBase 10, if aSize is negative. sl@0: @panic E32USER-CBase 11, if there is data in the buffer, and aSize is less than sl@0: the value returned by Size(). sl@0: */ sl@0: { sl@0: sl@0: __ASSERT_ALWAYS(aSize>=0,Panic(EBufFlatReserveNegative)); sl@0: __ASSERT_ALWAYS(aSize>=iSize,Panic(EBufFlatReserveSetTooSmall)); sl@0: if (!aSize) sl@0: { sl@0: User::Free(iPtr); sl@0: iPtr=NULL; sl@0: } sl@0: else sl@0: iPtr=(TUint8 *)User::ReAllocL(iPtr,aSize); sl@0: iMaxSize=aSize; sl@0: } sl@0: sl@0: EXPORT_C void CBufFlat::DoInsertL(TInt aPos,const TAny *aPtr,TInt aLength) sl@0: // sl@0: // Insert into the buffer. Can cause expansion. sl@0: // sl@0: { sl@0: sl@0: __ASSERT_ALWAYS(aPos>=0 && aPos<=iSize,Panic(EBufFlatPosOutOfRange)); sl@0: TInt len=iSize+aLength; sl@0: if (len>iMaxSize) sl@0: { sl@0: TInt r=len-iMaxSize; sl@0: r=((r/iExpandSize)+1)*iExpandSize; sl@0: SetReserveL(iMaxSize+r); sl@0: } sl@0: Mem::Copy(iPtr+aPos+aLength,iPtr+aPos,iSize-aPos); sl@0: if (aPtr) sl@0: Mem::Copy(iPtr+aPos,aPtr,aLength); sl@0: iSize+=aLength; sl@0: } sl@0: sl@0: EXPORT_C void CBufFlat::Delete(TInt aPos,TInt aLength) sl@0: /** sl@0: Deletes data from the buffer. sl@0: sl@0: During deletion, any data beyond the deleted data is shuffled up so that sl@0: the buffer contents are contiguous. No memory is freed. sl@0: sl@0: @param aPos Buffer position where the deletion will begin; must be in the sl@0: range zero to (Size() minus the length of the data sl@0: to be deleted). sl@0: @param aLength The number of bytes to be deleted. sl@0: sl@0: @panic E32USER-CBase 12, if aPos is negative or is greater than the sl@0: current size of the buffer. sl@0: @panic E32USER-CBase 13, if aPos + aLength is greater than the sl@0: current size of the buffer. sl@0: sl@0: @see CBufBase::Delete sl@0: */ sl@0: { sl@0: sl@0: __ASSERT_ALWAYS(aPos>=0 && aPos<=iSize,Panic(EBufFlatPosOutOfRange)); sl@0: __ASSERT_ALWAYS((aPos+aLength)<=iSize,Panic(EBufFlatDeleteBeyondEnd)); sl@0: Mem::Copy(iPtr+aPos,iPtr+aPos+aLength,iSize-aLength-aPos); sl@0: iSize-=aLength; sl@0: } sl@0: sl@0: EXPORT_C TPtr8 CBufFlat::Ptr(TInt aPos) sl@0: /** sl@0: Gets a pointer descriptor to represent the data starting at the specified sl@0: data byte through to the end of the contiguous region containing that byte. sl@0: sl@0: Calculation of the pointer and length involves only a few machine instructions sl@0: and is independent of the data contained in the buffer. sl@0: sl@0: @param aPos Buffer position: must be in range zero to Size(). sl@0: sl@0: @return Descriptor representing the data starting at aPos to the end of sl@0: the buffer. sl@0: */ sl@0: { sl@0: sl@0: __ASSERT_ALWAYS(aPos>=0 && aPos<=iSize,Panic(EBufFlatPosOutOfRange)); sl@0: TInt len=iSize-aPos; sl@0: return(TPtr8(iPtr+aPos,len,len)); sl@0: } sl@0: sl@0: EXPORT_C TPtr8 CBufFlat::BackPtr(TInt aPos) sl@0: // sl@0: // Return a pointer to the buffer which has the maximum amount of data sl@0: // before aPos, and the amount of data remaining. sl@0: // sl@0: /** sl@0: Gets a pointer descriptor to represent the data starting at the beginning sl@0: of the contiguous region containing that byte through to the byte immediately sl@0: preceding the specified byte. sl@0: sl@0: The descriptor always points to the beginning of the buffer containing sl@0: the specified byte. Calculation of the pointer and length involves only a few sl@0: machine instructions and is independent of the data contained in the buffer. sl@0: sl@0: @param aPos Buffer position: must be in range zero to Size(). sl@0: sl@0: @return Descriptor representing the back contiguous region. sl@0: sl@0: @see CBufBase::BackPtr sl@0: */ sl@0: { sl@0: sl@0: __ASSERT_ALWAYS(aPos>=0 && aPos<=iSize,Panic(EBufFlatPosOutOfRange)); sl@0: return(TPtr8(iPtr,aPos,aPos)); sl@0: } sl@0: sl@0: void CBufSeg::InsertIntoSegment(TBufSegLink *aSeg,TInt anOffset,const TAny *aPtr,TInt aLength) sl@0: // sl@0: // Insert into the segment. sl@0: // sl@0: { sl@0: sl@0: if (aLength) sl@0: { sl@0: TUint8 *pS=((TUint8 *)(aSeg+1))+anOffset; sl@0: Mem::Copy(pS+aLength,pS,aSeg->iLen-anOffset); sl@0: if (aPtr) sl@0: Mem::Copy(pS,aPtr,aLength); sl@0: aSeg->iLen+=aLength; sl@0: } sl@0: } sl@0: sl@0: void CBufSeg::DeleteFromSegment(TBufSegLink *aSeg,TInt anOffset,TInt aLength) sl@0: // sl@0: // Delete from the segment. sl@0: // sl@0: { sl@0: sl@0: if (aLength) sl@0: { sl@0: TUint8 *pS=((TUint8 *)(aSeg+1))+anOffset; sl@0: Mem::Copy(pS,pS+aLength,aSeg->iLen-anOffset-aLength); sl@0: aSeg->iLen-=aLength; sl@0: } sl@0: } sl@0: sl@0: void CBufSeg::FreeSegment(TBufSegLink *aSeg) sl@0: // sl@0: // Free an entire segment. sl@0: // sl@0: { sl@0: sl@0: aSeg->Deque(); sl@0: User::Free(aSeg); sl@0: } sl@0: sl@0: void CBufSeg::SetSBO(TInt aPos) sl@0: // sl@0: // Set a segment-base-offset struct (SBO) to a new pos. sl@0: // If the initial psbo->seg is not NULL, it assumes that it is a valid sl@0: // SBO for a different position and counts relative to the initial SBO sl@0: // to set the desired position. If the initial psbo->seg is NULL, it starts sl@0: // scanning from the beginning ie pos=0. sl@0: // When the position is between segments A and B, there are two equivalent sl@0: // positions: (1) at the beginning of B and (2) at the end of A. sl@0: // Option (1) is suitable for referencing the data and deleting. sl@0: // Option (2) is best for insertion when A is not full. sl@0: // This function uses option (1) and will always set the SBO to the sl@0: // beginning of the next segment. It does however set to the end of the sl@0: // last segment when pos is equal to the number of bytes in the buffer. sl@0: // sl@0: { sl@0: sl@0: __ASSERT_ALWAYS(aPos>=0 && aPos<=iSize,Panic(EBufSegPosOutOfRange)); sl@0: if (aPos==iSize) sl@0: { // Positioning to end is treated as a special case sl@0: iSeg=0; sl@0: if (iSize) sl@0: iBase=aPos-(iOffset=(iSeg=iQue.Last())->iLen); sl@0: return; sl@0: } sl@0: TInt base=iBase; sl@0: TBufSegLink *next; sl@0: if ((next=iSeg)==NULL) sl@0: { // anSbo is not valid - set to pos=0 sl@0: next=iQue.First(); sl@0: base=0; sl@0: } sl@0: if (aPosPrev(); sl@0: base-=next->iLen; sl@0: } while (aPos=(base+next->iLen) && !iQue.IsHead(nn=next->Next())) sl@0: { sl@0: base+=next->iLen; sl@0: next=nn; sl@0: } sl@0: } sl@0: iSeg=next; sl@0: iBase=base; sl@0: iOffset=aPos-base; sl@0: __ASSERT_DEBUG(iOffset<=iExpandSize,Panic(EBufSegSetSBO)); sl@0: } sl@0: sl@0: void CBufSeg::AllocSegL(TBufSegLink *aSeg,TInt aNumber) sl@0: // sl@0: // Allocate a number of segments. sl@0: // sl@0: { sl@0: sl@0: for (TInt i=0;iNext()); sl@0: User::Leave(KErrNoMemory); sl@0: } sl@0: new(pL) TBufSegLink; sl@0: pL->Enque(aSeg); sl@0: } sl@0: } sl@0: sl@0: EXPORT_C CBufSeg *CBufSeg::NewL(TInt anExpandSize) sl@0: /** sl@0: Allocates and constructs a segmented buffer. sl@0: sl@0: If there is insufficient memory available to allocate the segmented buffer, sl@0: the function leaves. sl@0: sl@0: @param anExpandSize The granularity of the buffer. Each segment contains (in sl@0: addition to 16 bytes of overhead) this number of bytes for sl@0: data. Note: although a value of zero is permitted by this sl@0: interface, it has no meaning, and risks raising panics later sl@0: during execution. We suggest that you pass a positive value. sl@0: sl@0: @return If successful, a pointer to the segmented buffer object. sl@0: sl@0: @panic E32USER-CBase 3 if the granularity is negative. sl@0: */ sl@0: { sl@0: sl@0: return(new(ELeave) CBufSeg(anExpandSize)); sl@0: } sl@0: sl@0: EXPORT_C CBufSeg::CBufSeg(TInt anExpandSize) sl@0: // sl@0: // Constructor sl@0: // sl@0: : CBufBase(anExpandSize) sl@0: { sl@0: sl@0: // iSeg=NULL; sl@0: } sl@0: sl@0: EXPORT_C CBufSeg::~CBufSeg() sl@0: /** sl@0: Destructor. sl@0: sl@0: Frees all resources owned by the object, prior to its destruction. sl@0: sl@0: Specifically, it frees all segments allocated to the buffer. sl@0: */ sl@0: { sl@0: sl@0: Delete(0,iSize); sl@0: } sl@0: sl@0: EXPORT_C void CBufSeg::Compress() sl@0: /** sl@0: Compresses the buffer so as to occupy minimal space. sl@0: sl@0: Fills any space in each segment of the buffer by moving contents from the next sl@0: segment to the current one. Where this activity results in empty segments, sl@0: it frees the memory associated with these segments. sl@0: sl@0: @see CBufBase::Compress sl@0: */ sl@0: { sl@0: sl@0: if (!iSize) sl@0: return; sl@0: iSeg=NULL; // Invalidate current position sl@0: TBufSegLink *p1=iQue.First(); sl@0: TBufSegLink *p2; sl@0: while (!iQue.IsHead(p2=p1->Next())) sl@0: { sl@0: TInt rem=iExpandSize-p1->iLen; sl@0: if (rem==0) sl@0: { sl@0: p1=p2; sl@0: continue; // Full sl@0: } sl@0: if (rem>=p2->iLen) sl@0: { // Zap the next segment sl@0: InsertIntoSegment(p1,p1->iLen,p2+1,p2->iLen); sl@0: FreeSegment(p2); sl@0: continue; sl@0: } sl@0: InsertIntoSegment(p1,p1->iLen,p2+1,rem); // Make full sl@0: DeleteFromSegment(p2,0,rem); sl@0: p1=p2; sl@0: } sl@0: } sl@0: sl@0: EXPORT_C void CBufSeg::DoInsertL(TInt aPos,const TAny *aPtr,TInt aLength) sl@0: // sl@0: // Insert data at the specified position. This is quite tricky. sl@0: // In general, the data to be copied may be broken down into the sl@0: // following elements: sl@0: // s1 bytes into the current segment (p1) sl@0: // nseg-1 segments of self->sgbuf.hd.len (ie full segments) sl@0: // s2 bytes into segment nseg sl@0: // s3 bytes into the next segment (p2) sl@0: // where p2 is the next segment before the insertion of nseg new segments. sl@0: // In addition, any remaining data to the right of the insertion point must sl@0: // be moved appropriately. In general, r1 bytes must be moved into segment sl@0: // nseg (r2 bytes) and segment p2 (r3 bytes) where r1=r2+r3. sl@0: // sl@0: { sl@0: sl@0: SetSBO(aPos); sl@0: TInt slen=iExpandSize; sl@0: TInt ofs=iOffset; sl@0: TInt ll=0; sl@0: TInt s1=0; sl@0: TInt r1=0; sl@0: TBufSegLink *p1=(TBufSegLink *)(&iQue); sl@0: TBufSegLink *p2=p1->Next(); sl@0: TUint8 *pR=NULL; sl@0: if (iSize) sl@0: { sl@0: p1=iSeg; sl@0: if (!iQue.IsHead(p2=p1->Prev()) && ofs==0 && p2->iLeniLen; sl@0: iBase-=ofs; sl@0: } sl@0: s1=slen-ofs; sl@0: if (s1>aLength) sl@0: s1=aLength; sl@0: TInt r2=slen-p1->iLen; sl@0: if (aLength>r2) sl@0: { sl@0: pR=((TUint8 *)(p1+1))+ofs; sl@0: r1=aLength-r2; sl@0: r2=p1->iLen-ofs; sl@0: if (r1>r2) sl@0: r1=r2; sl@0: else sl@0: pR+=(r2-r1); sl@0: } sl@0: p2=p1->Next(); sl@0: ll=slen-p1->iLen; sl@0: if (!iQue.IsHead(p2)) sl@0: ll+=slen-p2->iLen; sl@0: } sl@0: TUint8 *pB=((TUint8 *)aPtr)+s1; sl@0: TInt lrem=aLength-s1; sl@0: TBufSegLink *pP=p1; sl@0: if (aLength>ll) sl@0: {// Need some more segments sl@0: TInt nseg=(slen-1+aLength-ll)/slen; sl@0: AllocSegL(p1,nseg); // Could leave sl@0: while (nseg--) sl@0: { // Copy into allocated segments sl@0: pP=pP->Next(); sl@0: TInt gap=slen; sl@0: if (lremiLen; sl@0: if (r2>r1) sl@0: r2=r1; sl@0: } sl@0: InsertIntoSegment(pP,pP->iLen,pR,r2); // Moved from p1 sl@0: InsertIntoSegment(p2,0,pR+r2,r1-r2); // Also moved from p1 sl@0: } sl@0: p1->iLen-=r1; sl@0: InsertIntoSegment(p1,ofs,aPtr,s1); sl@0: iSize+=aLength; sl@0: } sl@0: sl@0: EXPORT_C void CBufSeg::Delete(TInt aPos,TInt aLength) sl@0: /** sl@0: Deletes data from the buffer. sl@0: sl@0: During deletion, shuffling is minimised by deleting intermediate segments sl@0: and allowing segments to contain less data than the buffer granularity. sl@0: sl@0: @param aPos Buffer position where the deletion will begin; must be in the sl@0: range zero to (Size() minus the length of the data sl@0: to be deleted). sl@0: @param aLength The number of bytes to be deleted. sl@0: sl@0: @see CBufBase::Delete sl@0: */ sl@0: { sl@0: sl@0: if (aLength==0) sl@0: return; sl@0: SetSBO(aPos); sl@0: TInt ofs=iOffset; sl@0: __ASSERT_ALWAYS((iBase+ofs+aLength)<=iSize,Panic(EBufSegDeleteBeyondEnd)); sl@0: iSize-=aLength; sl@0: TBufSegLink *p1=iSeg; sl@0: TBufSegLink *p2; sl@0: TInt rem=p1->iLen-ofs; sl@0: FOREVER sl@0: { sl@0: p2=p1->Next(); sl@0: TInt gap=aLength; sl@0: if (gap>rem) sl@0: gap=rem; sl@0: DeleteFromSegment(p1,ofs,gap); sl@0: if (p1->iLen==0) sl@0: { sl@0: iSeg=NULL; sl@0: FreeSegment(p1); sl@0: } sl@0: p1=p2; sl@0: if ((aLength-=gap)==0) sl@0: break; sl@0: rem=p1->iLen; sl@0: ofs=0; sl@0: } sl@0: if (iSize) sl@0: { sl@0: p1=p2->Prev(); sl@0: if (!iQue.IsHead(p1) && !iQue.IsHead(p2)) sl@0: { sl@0: if ((p1->iLen+p2->iLen)<=iExpandSize) sl@0: { // Join to the right sl@0: InsertIntoSegment(p1,p1->iLen,p2+1,p2->iLen); sl@0: FreeSegment(p2); sl@0: } sl@0: } sl@0: } sl@0: SetSBO(aPos); sl@0: } sl@0: sl@0: EXPORT_C TPtr8 CBufSeg::Ptr(TInt aPos) sl@0: /** sl@0: Gets a pointer descriptor to represent the data starting at the specified sl@0: data byte through to the end of the contiguous region containing that byte. sl@0: sl@0: The time needed for calculation of the pointer depends on how many segments sl@0: there are in the buffer, and how near the target segment is to the segment sl@0: which was last used in the buffer. sl@0: sl@0: @param aPos Buffer position: must be in range zero to Size(). sl@0: sl@0: @return Descriptor representing the data starting at aPos to the end of sl@0: the contiguous region containing that byte. sl@0: */ sl@0: { sl@0: sl@0: if (iSize==0) sl@0: return(TPtr8(NULL,0,0)); sl@0: SetSBO(aPos); sl@0: TInt len=iSeg->iLen-iOffset; sl@0: return(TPtr8(((TUint8 *)(iSeg+1))+iOffset,len,len)); sl@0: } sl@0: sl@0: EXPORT_C TPtr8 CBufSeg::BackPtr(TInt aPos) sl@0: // sl@0: // Return a pointer to the buffer which has the maximum amount of data sl@0: // before aPos, and the amount of data remaining. sl@0: // sl@0: /** sl@0: Gets a pointer descriptor to represent the data starting at the beginning sl@0: of the contiguous region containing that byte through to the byte immediately sl@0: preceding the specified byte. sl@0: sl@0: The descriptor always points to the beginning of the segment containing the sl@0: specified byte. The time needed for calculation of the pointer depends on how sl@0: many segments there are in the buffer, and how near the target segment is to sl@0: the segment which was last used in the buffer. sl@0: sl@0: @param aPos Buffer position: must be in range zero to Size(). sl@0: sl@0: @return Descriptor representing the back contiguous region. sl@0: sl@0: @see CBufBase::BackPtr sl@0: */ sl@0: sl@0: sl@0: { sl@0: sl@0: if (aPos==0) sl@0: return(TPtr8(NULL,0,0)); sl@0: SetSBO(aPos); sl@0: if (iOffset) sl@0: return(TPtr8((TUint8 *)(iSeg+1),iOffset,iOffset)); sl@0: TBufSegLink *pL=iSeg->Prev(); sl@0: TInt len=pL->iLen; sl@0: return(TPtr8((TUint8 *)(pL+1),len,len)); sl@0: } sl@0: