commsfwutils/commsbufs/src/commsbufchain.cpp
changeset 0 dfb7c4ff071f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commsfwutils/commsbufs/src/commsbufchain.cpp	Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,593 @@
+// Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include "es_commsbuf_internal.h"
+#include <comms-infras/mbufpanic.h>
+#include <comms-infras/commsbufpondop.h>
+#include "commsbufpondintf.h"
+#include "commsbufpool.h"
+
+RCommsBuf* RCommsBufChain::Last()
+/**
+Returns the pointer to the last RCommsBuf in the chain
+*/
+	{
+	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+	
+	TCommsBufIter it(iNext);
+	
+	RCommsBuf* buf = it.Current();
+	while(!(++it).AtEnd())
+		buf = it.Current();		
+	return buf;
+	}
+
+EXPORT_C RCommsBuf* RCommsBufChain::Last() const
+/**
+Returns the const pointer to the last RCommsBuf in the chain
+*/
+	{
+	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+		
+	TCommsBufIter it(iNext);
+	RCommsBuf* buf = it.Current();
+	while(!(++it).AtEnd())
+		buf = it.Current();		
+	return buf;
+	}
+
+EXPORT_C TInt RCommsBufChain::Length() const
+/**
+Returns the actual number of bytes present in the chain
+*/
+	{
+	TInt len = 0;
+	TCommsBufIter it(iNext);
+	while(!it.AtEnd())
+		{
+		len += it.Current()->Length();
+		++it;
+		}
+
+	return len;
+	}
+
+EXPORT_C void RCommsBufChain::Assign(RCommsBufChain& aChain)	
+/**
+Assign the supplied RCommsBufChain to "this" chain. After assign the supplied 
+RCommsBufChain will be reset and becomes empty
+
+@param aChain The chain to be assigned
+
+*/
+	{
+	__ASSERT_ALWAYS(iNext==NULL, CommsBuf::Panic(EMBuf_NotEmptyChain));
+	
+	iNext = aChain.First();
+	aChain.iNext = NULL;			
+	}
+
+EXPORT_C void RCommsBufChain::Append(RCommsBufChain& aChain)
+/**
+Append the supplied RCommsBufChain to "this" chain. After append the supplied
+RCommsBufChain will be reset and becomes empty
+
+@param aChain The chain to be appended
+*/
+	{
+	 __ASSERT_ALWAYS(iNext!=aChain.iNext, CommsBuf::Panic(EMBuf_CircularRef));
+	
+	if(iNext != NULL)
+		{
+		RCommsBuf* last = Last();
+		last->SetNext(aChain.iNext);			
+		}
+	else
+		iNext = aChain.First();
+	aChain.iNext = NULL;
+	}
+	
+EXPORT_C void RCommsBufChain::Append(RCommsBuf* aBuf)
+/**
+Append the supplied RCommsBuf to "this" chain. 
+
+@param aBuf The RCommsBuf to be appended
+*/
+	{
+	 __ASSERT_ALWAYS(iNext!=aBuf, CommsBuf::Panic(EMBuf_CircularRef));
+	
+	if(iNext == NULL)
+		{
+		iNext = aBuf;
+		return;			
+		}	
+	Last()->SetNext(aBuf);	
+	}
+	
+EXPORT_C TInt RCommsBufChain::Append(TInt aLen)
+/**
+Append space to the end of "this" chain. More RCommsBuf will be allocated if
+available append length is exceeding the supplied length
+
+@aLen Length to appended
+@return TInt KErrNoMemory on failure otherwise KErrNone
+*/
+	{
+	__ASSERT_ALWAYS(iNext, CommsBuf::Panic(EMBuf_EmptyChain));
+	TInt lenToAllocate = aLen;
+	RCommsBuf* last = NULL;
+	if (iNext != NULL)
+		{
+		last = Last();
+		// Amount of space available on end of last Mbuf
+		TInt n = Min(last->RawSize()-(last->Offset()+last->Length()), aLen);
+		// Amount of space that needs to be Allocated in new buffers
+		lenToAllocate = aLen-n;
+		last->AdjustDataEnd(n);
+		}
+
+	if (lenToAllocate > 0)
+		{
+		RCommsBuf* buf = last->Pool()->Pond().Alloc(lenToAllocate, last->RawSize(), KMaxTInt);;
+
+		if (buf == NULL)
+			{
+			return KErrNoMBufs;
+			}
+		else
+			{
+			Append(buf);
+			}
+		}
+	return KErrNone;
+	}
+
+EXPORT_C void RCommsBufChain::Prepend(RCommsBufChain& aChain)
+/**
+Prepend the supplied RCommsBufChain to "this" chain. After prepend the supplied
+RCommsBufChain will be reset and becomes empty
+
+@param aChain The chain to be prepended
+*/
+	{
+	if (iNext !=NULL)
+		{
+		RCommsBuf* last = aChain.Last();
+		last->SetNext(iNext);
+		}
+	iNext = aChain.iNext;
+	aChain.iNext = NULL;		
+	}
+	
+EXPORT_C void RCommsBufChain::Prepend(RCommsBuf* aBuf)
+/**
+Prepend the supplied RCommsBuf to "this" chain. 
+
+@param aBuf The RCommsBuf to be prepended
+*/
+	{
+	aBuf->SetNext(iNext);
+	iNext = aBuf;
+	}
+
+EXPORT_C TInt RCommsBufChain::Prepend(TInt aLen)
+/**
+Prepend space to the end of "this" chain. More RCommsBuf will be allocated if
+available prepend length is exceeding the supplied length
+
+@aLen Length to prepended
+@return TInt KErrNoMBufs on failure otherwise KErrNone
+*/
+	{
+	__ASSERT_ALWAYS(iNext, CommsBuf::Panic(EMBuf_EmptyChain));
+	// See if the length can be fit in the first comms buf
+	if(iNext->Offset() >= aLen)
+		{
+		iNext->AdjustDataStart(-aLen);			
+		return KErrNone;
+		}
+	
+	RCommsBuf* buf = iNext->Pool()->Pond().Alloc(aLen, 0, KMaxTInt);	 
+		
+	if (!buf)
+		{
+		return KErrNoMBufs;			
+		}
+	
+	RCommsBufChain chain(buf);
+	
+	aLen -= chain.Length();	
+   	// Performance enhancement - most of these prepends are 20 byes or so
+   	// This allows the next one to use this MBuf
+   	if (aLen<0)
+   		{
+  		buf->SetDataRange(-aLen, buf->Length()+aLen);
+   		}
+	
+	Prepend(chain);
+	return KErrNone;	
+	}
+		
+EXPORT_C TInt RCommsBufChain::Alloc(TInt aSize, TCommsBufAllocator& aAllocator)
+/**
+Allocate a chain of RCommsBuf
+
+@param 	aSize 		The requested total size
+@param	aAllocator	A handle to the allocator
+
+@return KErrNoMBufs if allocation is failed otherwise KErrNone
+*/
+	{	
+	return Alloc(aSize, 0, KMaxTInt, aAllocator);	
+	}
+	
+EXPORT_C TInt RCommsBufChain::Alloc(TInt aSize, TInt aMinBufSize, TCommsBufAllocator& aAllocator)
+/**
+Allocate a chain of RCommsBuf
+
+@param 	aSize 		The requested total size
+@param 	aMinBufSize The size that the allocated buffer "must" atleast have
+
+@param	aAllocator	A handle to the allocator
+
+@return KErrNoMBufs if allocation is failed otherwise KErrNone
+*/
+	{
+	return Alloc(aSize, aMinBufSize, KMaxTInt, aAllocator);	
+	}
+	
+EXPORT_C TInt RCommsBufChain::Alloc(TInt aSize, TInt aMinBufSize, TInt aMaxBufSize, TCommsBufAllocator& aAllocator)
+/**
+Allocate a chain of RCommsBuf
+
+@param 	aSize 		The requested total size
+@param 	aMinBufSize The size that the allocated buffer must atleast have
+@param 	aMinBufSize The size that the allocated buffer can have
+
+@param	aAllocator	A handle to the allocator
+
+@return KErrNoMBufs if allocation is failed otherwise KErrNone
+*/
+	{
+	 __ASSERT_ALWAYS(iNext==NULL, CommsBuf::Panic(EMBuf_NotEmptyChain));
+	
+	iNext = aAllocator.iPond.Alloc(aSize, aMinBufSize, aMaxBufSize);
+	return iNext ? KErrNone : KErrNoMBufs;
+	}
+	
+EXPORT_C TInt RCommsBufChain::Split(TInt aOffset, RCommsBufChain& aNewChain)
+	{
+  	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+    __ASSERT_ALWAYS(aOffset>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
+
+	
+	if (aOffset == 0)
+		{
+		aNewChain.iNext = NULL;
+		return KErrNone;	
+		}
+
+	TInt o, n;
+	RCommsBuf* m, *p;
+	
+	if ((m = Goto(aOffset, o, n, p)) == NULL)
+		return KErrNone;
+
+	if (o!=m->Offset()) // Not on an mbuf boundary
+		{
+		// get another mbuf and copy extra data
+		RCommsBuf* m2;
+		m2 = m->Pool()->Pond().Alloc(n, n, KMaxTInt);
+		if(!m2)
+			{
+			return KErrNoMBufs;
+			}
+
+		__ASSERT_DEBUG(m2->RawSize() >= n, CommsBuf::Panic(EMBuf_TooSmall));
+		Mem::Copy((TUint8*)m2->RawBase() + m2->Offset(), m->RawBase()+o, n);		// trs; possible future enhancement to optionally not split the mbuf unless requested, thus avoiding the copy, but kept as is to avoid a functional break
+		m2->SetDataRange(m2->Offset(), n);
+		m2->SetNext(m->Next());
+		m->AdjustDataEnd(-n);
+		m->SetNext(NULL);
+		aNewChain = m2;
+		}
+	else
+		{
+		p->SetNext(NULL);
+		aNewChain = m;
+		}
+	return KErrNone;
+	}
+
+EXPORT_C void RCommsBufChain::Write(const TDesC8& aDes, TInt aOffset /* =0 */)
+/**
+Writes the supplied descriptor at the specified offset within the chain
+
+@param aDes		Descriptor to write
+@param aOffset 	Offset within the chain
+*/
+	{
+ 	 __ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+     __ASSERT_ALWAYS(aOffset>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
+ 
+	
+	TUint8* ptr = (TUint8*)aDes.Ptr();
+	TInt len = aDes.Length();
+
+	TInt n;
+	RCommsBuf* m;
+
+	if (aOffset>0)
+		{
+		TInt o;
+
+		if ((m = Goto(aOffset, o, n)) == NULL)
+			return;
+
+		if (n>len)
+			n = len;
+
+		Mem::Copy((TUint8*)m->RawBase()+o, ptr, n);
+		ptr += n;
+		len -= n;
+		m = m->Next();
+		}
+	else
+		m = iNext;
+
+	while (len>0 && m!=NULL)
+		{
+		n = len > m->Length() ? m->Length() : len;
+		Mem::Copy((TUint8*)m->RawBase() + m->Offset(), ptr, n);
+		ptr += n;
+		len -= n;
+		m = m->Next();
+		}
+		
+	}
+
+EXPORT_C void RCommsBufChain::Read(TDes8& aDes, TInt aOffset /* =0 */) const
+/**
+Read to the supplied descriptor at the specified offset within the chain
+
+@param aDes		Descriptor to read
+@param aOffset 	Offset within the chain
+*/
+	{
+//removed after internal discussion; generally unhelpful as empty == zero length is ok concept
+ //	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+	__ASSERT_ALWAYS(aOffset>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
+
+	TUint8* ptr = (TUint8*)aDes.Ptr();
+	TInt len = aDes.Length();
+
+	TInt n;
+	RCommsBuf* m;
+
+	if (aOffset>0)
+		{
+		TInt o;
+
+		if ((m = RCommsBufChain::Goto(aOffset, o, n) ) == NULL)
+			{
+			aDes.SetLength(0);
+			return;
+			}
+
+		if (n>len)
+			n = len;
+
+		Mem::Copy(ptr, m->RawBase()+o, n);
+		ptr += n;
+		len -= n;
+		m = m->Next();
+		}
+	else
+		m = static_cast<RCommsBuf*>(iNext);
+
+	while (len>0 && m!=NULL)
+		{
+		n = len > m->Length() ? m->Length() : len;
+		Mem::Copy(ptr, m->Ptr(), n);
+		ptr += n;
+		len -= n;
+		m = m->Next();
+		}
+	aDes.SetLength(ptr-aDes.Ptr());					
+	}
+
+EXPORT_C RCommsBuf* RCommsBufChain::Goto(TInt aDataOffset, TInt& aBufDataOffset, TInt& aBufDataLen, RCommsBuf* &resPrevBuf) const
+/**
+Goto specified byte offset into an CommsBuf chain. Used as part of Read/Write, split etc to position
+CommsBuf pointer and offset from start of iBuffer.
+
+@param anOffset The offset
+@param resBuf result buffer
+@param resOffset result offset
+@param resLength result length
+@param resPrevBuf result previous Buf in the chain
+@return ETrue if successful
+*/
+
+	{
+   	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+    __ASSERT_ALWAYS(aDataOffset>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
+
+	
+	TInt o = 0;
+	RCommsBuf* p = NULL, *m = iNext;
+
+	while (m!=NULL)
+		{
+		if (o + m->Length() > aDataOffset)
+			{
+			aBufDataOffset = (aDataOffset - o) + m->Offset();
+			aBufDataLen = m->Length() - (aDataOffset - o);
+			resPrevBuf = p;
+			return m;
+			}
+		o += m->Length();
+		p = m;
+		m = m->Next();
+		}
+
+	// Attempt to goto beyond end of chain
+	__ASSERT_ALWAYS(o==aDataOffset, CommsBuf::Panic(EMBuf_BadOffset));
+	return NULL;	
+	}
+	
+EXPORT_C void RCommsBufChain::Free()
+/**
+Frees the RCommsBuf's in the chain. Upon completion the chain will become empty
+*/
+	{
+	if (iNext)
+		{
+		iNext->Pool()->Pond().Free(iNext);			
+		}
+	iNext = NULL;
+	}
+	
+EXPORT_C void RCommsBufChain::TrimStart(TInt aBytes)
+/**
+Trim the leftmost part of the data in the chain with the no. of bytes that is supplied
+
+@param aBytes The no. of bytes to be trimmed
+*/
+	{
+	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+	__ASSERT_ALWAYS(aBytes>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
+
+	if (aBytes==0)
+		return;
+
+	RCommsBuf* m, *p;
+	TInt o, n;
+	if ((m = Goto(aBytes, o, n, p)) == NULL)
+		{
+		Free();
+		return;
+		}
+
+	if (p!=NULL) // m not first mbuf?
+		{
+		p->SetNext(NULL);
+		iNext->Free();
+
+		iNext = m;
+		}
+
+	if (o!=m->Offset()) // not at mbuf boundary?
+		m->SetDataRange(o, n);		
+	}
+	
+EXPORT_C void RCommsBufChain::TrimEnd(TInt aBytes)
+/**
+Trim the rightmost part of the data in the chain with the no. of bytes that is supplied
+
+@param aBytes The no. of bytes to be trimmed
+*/
+	{
+	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+	__ASSERT_ALWAYS(aBytes>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
+
+	if(aBytes==0)
+		{
+		Free();
+		return;
+		}
+
+	RCommsBuf* m, *p;
+	TInt o, n;
+	if ((m = Goto(aBytes, o, n, p)) == NULL )
+		return;
+
+	if (o!=m->Offset()) // not at mbuf boundary?
+		{
+		m->AdjustDataEnd(-n);
+		if (p = m->Next(), p!=NULL)
+			{
+			m->SetNext(NULL);
+			p->Free();
+			}
+		}
+	else
+		{
+		if (p!=NULL)
+			p->SetNext(NULL);
+		m->Free();
+		}		
+	}
+
+EXPORT_C TInt RCommsBufChain::Align(TInt aSize)
+ /**
+Ensure that the first aSize bytes can be safely cast to a structure of size aSize.
+
+@param aSize A size
+@return the number of bytes actually aligned. This will be the min of aSize and chain length.
+*/
+	{
+	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
+	__ASSERT_ALWAYS(aSize>=0, CommsBuf::Panic(EMBuf_NegativeLength));
+
+	// update length to the largest sized mbuf possible
+	if (aSize == KMaxTInt)	// trs; does the KMBufAll concept (ie. 'largest sized' mbuf) really make sense? code kept as is to avoid a functional break
+		{
+		aSize = iNext->Pool()->Pond().LargestBufSize();
+		}
+
+	// All data required is already in the first MBuf
+	if (aSize <= iNext->Length())
+		{
+		// Case 1 - allready aligned
+		// Case 2 - it needs to be aligned
+		if (!IS_ALIGNED(iNext->Offset()))
+			{
+			Mem::Copy(iNext->RawBase(), iNext->Ptr(), iNext->Length());
+			iNext->SetOffset(0);
+			}
+		return aSize; // already as required
+		}
+
+	// Get existing data at start
+	if (iNext->Offset() != 0)
+		{
+		Mem::Copy(iNext->RawBase(), iNext->Ptr(), iNext->Length());
+		iNext->SetOffset(0);
+		}
+
+	RCommsBuf* m = iNext->Next();
+	TInt len = iNext->Length();
+	while (len<aSize && m!=NULL)
+		{
+		TInt n = aSize-len;
+		if (n>m->Length())
+			n = m->Length();
+		Mem::Copy((TUint8*)iNext->RawBase() + (iNext->Offset() + iNext->Length()), (TUint8*)m->RawBase() + m->Offset(), n);
+		iNext->AdjustDataEnd(n);
+		m->AdjustDataStart(n);
+		len += n;
+
+		// MBuf might now be empty so free it
+		if (m->Length()==0)
+			{
+			iNext->SetNext(m->Next());
+			m->SetNext(NULL);
+			m->Free();
+			m = iNext->Next();
+			}
+		}
+	return len;		
+	}