--- /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;
+ }