diff -r 000000000000 -r dfb7c4ff071f commsfwutils/commsbufs/src/commsbufchain.cpp --- /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 +#include +#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(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 (lenm->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; + }