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: