commsfwutils/commsbufs/src/commsbufchain.cpp
changeset 0 dfb7c4ff071f
equal deleted inserted replaced
-1:000000000000 0:dfb7c4ff071f
       
     1 // Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "es_commsbuf_internal.h"
       
    17 #include <comms-infras/mbufpanic.h>
       
    18 #include <comms-infras/commsbufpondop.h>
       
    19 #include "commsbufpondintf.h"
       
    20 #include "commsbufpool.h"
       
    21 
       
    22 RCommsBuf* RCommsBufChain::Last()
       
    23 /**
       
    24 Returns the pointer to the last RCommsBuf in the chain
       
    25 */
       
    26 	{
       
    27 	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
    28 	
       
    29 	TCommsBufIter it(iNext);
       
    30 	
       
    31 	RCommsBuf* buf = it.Current();
       
    32 	while(!(++it).AtEnd())
       
    33 		buf = it.Current();		
       
    34 	return buf;
       
    35 	}
       
    36 
       
    37 EXPORT_C RCommsBuf* RCommsBufChain::Last() const
       
    38 /**
       
    39 Returns the const pointer to the last RCommsBuf in the chain
       
    40 */
       
    41 	{
       
    42 	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
    43 		
       
    44 	TCommsBufIter it(iNext);
       
    45 	RCommsBuf* buf = it.Current();
       
    46 	while(!(++it).AtEnd())
       
    47 		buf = it.Current();		
       
    48 	return buf;
       
    49 	}
       
    50 
       
    51 EXPORT_C TInt RCommsBufChain::Length() const
       
    52 /**
       
    53 Returns the actual number of bytes present in the chain
       
    54 */
       
    55 	{
       
    56 	TInt len = 0;
       
    57 	TCommsBufIter it(iNext);
       
    58 	while(!it.AtEnd())
       
    59 		{
       
    60 		len += it.Current()->Length();
       
    61 		++it;
       
    62 		}
       
    63 
       
    64 	return len;
       
    65 	}
       
    66 
       
    67 EXPORT_C void RCommsBufChain::Assign(RCommsBufChain& aChain)	
       
    68 /**
       
    69 Assign the supplied RCommsBufChain to "this" chain. After assign the supplied 
       
    70 RCommsBufChain will be reset and becomes empty
       
    71 
       
    72 @param aChain The chain to be assigned
       
    73 
       
    74 */
       
    75 	{
       
    76 	__ASSERT_ALWAYS(iNext==NULL, CommsBuf::Panic(EMBuf_NotEmptyChain));
       
    77 	
       
    78 	iNext = aChain.First();
       
    79 	aChain.iNext = NULL;			
       
    80 	}
       
    81 
       
    82 EXPORT_C void RCommsBufChain::Append(RCommsBufChain& aChain)
       
    83 /**
       
    84 Append the supplied RCommsBufChain to "this" chain. After append the supplied
       
    85 RCommsBufChain will be reset and becomes empty
       
    86 
       
    87 @param aChain The chain to be appended
       
    88 */
       
    89 	{
       
    90 	 __ASSERT_ALWAYS(iNext!=aChain.iNext, CommsBuf::Panic(EMBuf_CircularRef));
       
    91 	
       
    92 	if(iNext != NULL)
       
    93 		{
       
    94 		RCommsBuf* last = Last();
       
    95 		last->SetNext(aChain.iNext);			
       
    96 		}
       
    97 	else
       
    98 		iNext = aChain.First();
       
    99 	aChain.iNext = NULL;
       
   100 	}
       
   101 	
       
   102 EXPORT_C void RCommsBufChain::Append(RCommsBuf* aBuf)
       
   103 /**
       
   104 Append the supplied RCommsBuf to "this" chain. 
       
   105 
       
   106 @param aBuf The RCommsBuf to be appended
       
   107 */
       
   108 	{
       
   109 	 __ASSERT_ALWAYS(iNext!=aBuf, CommsBuf::Panic(EMBuf_CircularRef));
       
   110 	
       
   111 	if(iNext == NULL)
       
   112 		{
       
   113 		iNext = aBuf;
       
   114 		return;			
       
   115 		}	
       
   116 	Last()->SetNext(aBuf);	
       
   117 	}
       
   118 	
       
   119 EXPORT_C TInt RCommsBufChain::Append(TInt aLen)
       
   120 /**
       
   121 Append space to the end of "this" chain. More RCommsBuf will be allocated if
       
   122 available append length is exceeding the supplied length
       
   123 
       
   124 @aLen Length to appended
       
   125 @return TInt KErrNoMemory on failure otherwise KErrNone
       
   126 */
       
   127 	{
       
   128 	__ASSERT_ALWAYS(iNext, CommsBuf::Panic(EMBuf_EmptyChain));
       
   129 	TInt lenToAllocate = aLen;
       
   130 	RCommsBuf* last = NULL;
       
   131 	if (iNext != NULL)
       
   132 		{
       
   133 		last = Last();
       
   134 		// Amount of space available on end of last Mbuf
       
   135 		TInt n = Min(last->RawSize()-(last->Offset()+last->Length()), aLen);
       
   136 		// Amount of space that needs to be Allocated in new buffers
       
   137 		lenToAllocate = aLen-n;
       
   138 		last->AdjustDataEnd(n);
       
   139 		}
       
   140 
       
   141 	if (lenToAllocate > 0)
       
   142 		{
       
   143 		RCommsBuf* buf = last->Pool()->Pond().Alloc(lenToAllocate, last->RawSize(), KMaxTInt);;
       
   144 
       
   145 		if (buf == NULL)
       
   146 			{
       
   147 			return KErrNoMBufs;
       
   148 			}
       
   149 		else
       
   150 			{
       
   151 			Append(buf);
       
   152 			}
       
   153 		}
       
   154 	return KErrNone;
       
   155 	}
       
   156 
       
   157 EXPORT_C void RCommsBufChain::Prepend(RCommsBufChain& aChain)
       
   158 /**
       
   159 Prepend the supplied RCommsBufChain to "this" chain. After prepend the supplied
       
   160 RCommsBufChain will be reset and becomes empty
       
   161 
       
   162 @param aChain The chain to be prepended
       
   163 */
       
   164 	{
       
   165 	if (iNext !=NULL)
       
   166 		{
       
   167 		RCommsBuf* last = aChain.Last();
       
   168 		last->SetNext(iNext);
       
   169 		}
       
   170 	iNext = aChain.iNext;
       
   171 	aChain.iNext = NULL;		
       
   172 	}
       
   173 	
       
   174 EXPORT_C void RCommsBufChain::Prepend(RCommsBuf* aBuf)
       
   175 /**
       
   176 Prepend the supplied RCommsBuf to "this" chain. 
       
   177 
       
   178 @param aBuf The RCommsBuf to be prepended
       
   179 */
       
   180 	{
       
   181 	aBuf->SetNext(iNext);
       
   182 	iNext = aBuf;
       
   183 	}
       
   184 
       
   185 EXPORT_C TInt RCommsBufChain::Prepend(TInt aLen)
       
   186 /**
       
   187 Prepend space to the end of "this" chain. More RCommsBuf will be allocated if
       
   188 available prepend length is exceeding the supplied length
       
   189 
       
   190 @aLen Length to prepended
       
   191 @return TInt KErrNoMBufs on failure otherwise KErrNone
       
   192 */
       
   193 	{
       
   194 	__ASSERT_ALWAYS(iNext, CommsBuf::Panic(EMBuf_EmptyChain));
       
   195 	// See if the length can be fit in the first comms buf
       
   196 	if(iNext->Offset() >= aLen)
       
   197 		{
       
   198 		iNext->AdjustDataStart(-aLen);			
       
   199 		return KErrNone;
       
   200 		}
       
   201 	
       
   202 	RCommsBuf* buf = iNext->Pool()->Pond().Alloc(aLen, 0, KMaxTInt);	 
       
   203 		
       
   204 	if (!buf)
       
   205 		{
       
   206 		return KErrNoMBufs;			
       
   207 		}
       
   208 	
       
   209 	RCommsBufChain chain(buf);
       
   210 	
       
   211 	aLen -= chain.Length();	
       
   212    	// Performance enhancement - most of these prepends are 20 byes or so
       
   213    	// This allows the next one to use this MBuf
       
   214    	if (aLen<0)
       
   215    		{
       
   216   		buf->SetDataRange(-aLen, buf->Length()+aLen);
       
   217    		}
       
   218 	
       
   219 	Prepend(chain);
       
   220 	return KErrNone;	
       
   221 	}
       
   222 		
       
   223 EXPORT_C TInt RCommsBufChain::Alloc(TInt aSize, TCommsBufAllocator& aAllocator)
       
   224 /**
       
   225 Allocate a chain of RCommsBuf
       
   226 
       
   227 @param 	aSize 		The requested total size
       
   228 @param	aAllocator	A handle to the allocator
       
   229 
       
   230 @return KErrNoMBufs if allocation is failed otherwise KErrNone
       
   231 */
       
   232 	{	
       
   233 	return Alloc(aSize, 0, KMaxTInt, aAllocator);	
       
   234 	}
       
   235 	
       
   236 EXPORT_C TInt RCommsBufChain::Alloc(TInt aSize, TInt aMinBufSize, TCommsBufAllocator& aAllocator)
       
   237 /**
       
   238 Allocate a chain of RCommsBuf
       
   239 
       
   240 @param 	aSize 		The requested total size
       
   241 @param 	aMinBufSize The size that the allocated buffer "must" atleast have
       
   242 
       
   243 @param	aAllocator	A handle to the allocator
       
   244 
       
   245 @return KErrNoMBufs if allocation is failed otherwise KErrNone
       
   246 */
       
   247 	{
       
   248 	return Alloc(aSize, aMinBufSize, KMaxTInt, aAllocator);	
       
   249 	}
       
   250 	
       
   251 EXPORT_C TInt RCommsBufChain::Alloc(TInt aSize, TInt aMinBufSize, TInt aMaxBufSize, TCommsBufAllocator& aAllocator)
       
   252 /**
       
   253 Allocate a chain of RCommsBuf
       
   254 
       
   255 @param 	aSize 		The requested total size
       
   256 @param 	aMinBufSize The size that the allocated buffer must atleast have
       
   257 @param 	aMinBufSize The size that the allocated buffer can have
       
   258 
       
   259 @param	aAllocator	A handle to the allocator
       
   260 
       
   261 @return KErrNoMBufs if allocation is failed otherwise KErrNone
       
   262 */
       
   263 	{
       
   264 	 __ASSERT_ALWAYS(iNext==NULL, CommsBuf::Panic(EMBuf_NotEmptyChain));
       
   265 	
       
   266 	iNext = aAllocator.iPond.Alloc(aSize, aMinBufSize, aMaxBufSize);
       
   267 	return iNext ? KErrNone : KErrNoMBufs;
       
   268 	}
       
   269 	
       
   270 EXPORT_C TInt RCommsBufChain::Split(TInt aOffset, RCommsBufChain& aNewChain)
       
   271 	{
       
   272   	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
   273     __ASSERT_ALWAYS(aOffset>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
       
   274 
       
   275 	
       
   276 	if (aOffset == 0)
       
   277 		{
       
   278 		aNewChain.iNext = NULL;
       
   279 		return KErrNone;	
       
   280 		}
       
   281 
       
   282 	TInt o, n;
       
   283 	RCommsBuf* m, *p;
       
   284 	
       
   285 	if ((m = Goto(aOffset, o, n, p)) == NULL)
       
   286 		return KErrNone;
       
   287 
       
   288 	if (o!=m->Offset()) // Not on an mbuf boundary
       
   289 		{
       
   290 		// get another mbuf and copy extra data
       
   291 		RCommsBuf* m2;
       
   292 		m2 = m->Pool()->Pond().Alloc(n, n, KMaxTInt);
       
   293 		if(!m2)
       
   294 			{
       
   295 			return KErrNoMBufs;
       
   296 			}
       
   297 
       
   298 		__ASSERT_DEBUG(m2->RawSize() >= n, CommsBuf::Panic(EMBuf_TooSmall));
       
   299 		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
       
   300 		m2->SetDataRange(m2->Offset(), n);
       
   301 		m2->SetNext(m->Next());
       
   302 		m->AdjustDataEnd(-n);
       
   303 		m->SetNext(NULL);
       
   304 		aNewChain = m2;
       
   305 		}
       
   306 	else
       
   307 		{
       
   308 		p->SetNext(NULL);
       
   309 		aNewChain = m;
       
   310 		}
       
   311 	return KErrNone;
       
   312 	}
       
   313 
       
   314 EXPORT_C void RCommsBufChain::Write(const TDesC8& aDes, TInt aOffset /* =0 */)
       
   315 /**
       
   316 Writes the supplied descriptor at the specified offset within the chain
       
   317 
       
   318 @param aDes		Descriptor to write
       
   319 @param aOffset 	Offset within the chain
       
   320 */
       
   321 	{
       
   322  	 __ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
   323      __ASSERT_ALWAYS(aOffset>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
       
   324  
       
   325 	
       
   326 	TUint8* ptr = (TUint8*)aDes.Ptr();
       
   327 	TInt len = aDes.Length();
       
   328 
       
   329 	TInt n;
       
   330 	RCommsBuf* m;
       
   331 
       
   332 	if (aOffset>0)
       
   333 		{
       
   334 		TInt o;
       
   335 
       
   336 		if ((m = Goto(aOffset, o, n)) == NULL)
       
   337 			return;
       
   338 
       
   339 		if (n>len)
       
   340 			n = len;
       
   341 
       
   342 		Mem::Copy((TUint8*)m->RawBase()+o, ptr, n);
       
   343 		ptr += n;
       
   344 		len -= n;
       
   345 		m = m->Next();
       
   346 		}
       
   347 	else
       
   348 		m = iNext;
       
   349 
       
   350 	while (len>0 && m!=NULL)
       
   351 		{
       
   352 		n = len > m->Length() ? m->Length() : len;
       
   353 		Mem::Copy((TUint8*)m->RawBase() + m->Offset(), ptr, n);
       
   354 		ptr += n;
       
   355 		len -= n;
       
   356 		m = m->Next();
       
   357 		}
       
   358 		
       
   359 	}
       
   360 
       
   361 EXPORT_C void RCommsBufChain::Read(TDes8& aDes, TInt aOffset /* =0 */) const
       
   362 /**
       
   363 Read to the supplied descriptor at the specified offset within the chain
       
   364 
       
   365 @param aDes		Descriptor to read
       
   366 @param aOffset 	Offset within the chain
       
   367 */
       
   368 	{
       
   369 //removed after internal discussion; generally unhelpful as empty == zero length is ok concept
       
   370  //	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
   371 	__ASSERT_ALWAYS(aOffset>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
       
   372 
       
   373 	TUint8* ptr = (TUint8*)aDes.Ptr();
       
   374 	TInt len = aDes.Length();
       
   375 
       
   376 	TInt n;
       
   377 	RCommsBuf* m;
       
   378 
       
   379 	if (aOffset>0)
       
   380 		{
       
   381 		TInt o;
       
   382 
       
   383 		if ((m = RCommsBufChain::Goto(aOffset, o, n) ) == NULL)
       
   384 			{
       
   385 			aDes.SetLength(0);
       
   386 			return;
       
   387 			}
       
   388 
       
   389 		if (n>len)
       
   390 			n = len;
       
   391 
       
   392 		Mem::Copy(ptr, m->RawBase()+o, n);
       
   393 		ptr += n;
       
   394 		len -= n;
       
   395 		m = m->Next();
       
   396 		}
       
   397 	else
       
   398 		m = static_cast<RCommsBuf*>(iNext);
       
   399 
       
   400 	while (len>0 && m!=NULL)
       
   401 		{
       
   402 		n = len > m->Length() ? m->Length() : len;
       
   403 		Mem::Copy(ptr, m->Ptr(), n);
       
   404 		ptr += n;
       
   405 		len -= n;
       
   406 		m = m->Next();
       
   407 		}
       
   408 	aDes.SetLength(ptr-aDes.Ptr());					
       
   409 	}
       
   410 
       
   411 EXPORT_C RCommsBuf* RCommsBufChain::Goto(TInt aDataOffset, TInt& aBufDataOffset, TInt& aBufDataLen, RCommsBuf* &resPrevBuf) const
       
   412 /**
       
   413 Goto specified byte offset into an CommsBuf chain. Used as part of Read/Write, split etc to position
       
   414 CommsBuf pointer and offset from start of iBuffer.
       
   415 
       
   416 @param anOffset The offset
       
   417 @param resBuf result buffer
       
   418 @param resOffset result offset
       
   419 @param resLength result length
       
   420 @param resPrevBuf result previous Buf in the chain
       
   421 @return ETrue if successful
       
   422 */
       
   423 
       
   424 	{
       
   425    	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
   426     __ASSERT_ALWAYS(aDataOffset>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
       
   427 
       
   428 	
       
   429 	TInt o = 0;
       
   430 	RCommsBuf* p = NULL, *m = iNext;
       
   431 
       
   432 	while (m!=NULL)
       
   433 		{
       
   434 		if (o + m->Length() > aDataOffset)
       
   435 			{
       
   436 			aBufDataOffset = (aDataOffset - o) + m->Offset();
       
   437 			aBufDataLen = m->Length() - (aDataOffset - o);
       
   438 			resPrevBuf = p;
       
   439 			return m;
       
   440 			}
       
   441 		o += m->Length();
       
   442 		p = m;
       
   443 		m = m->Next();
       
   444 		}
       
   445 
       
   446 	// Attempt to goto beyond end of chain
       
   447 	__ASSERT_ALWAYS(o==aDataOffset, CommsBuf::Panic(EMBuf_BadOffset));
       
   448 	return NULL;	
       
   449 	}
       
   450 	
       
   451 EXPORT_C void RCommsBufChain::Free()
       
   452 /**
       
   453 Frees the RCommsBuf's in the chain. Upon completion the chain will become empty
       
   454 */
       
   455 	{
       
   456 	if (iNext)
       
   457 		{
       
   458 		iNext->Pool()->Pond().Free(iNext);			
       
   459 		}
       
   460 	iNext = NULL;
       
   461 	}
       
   462 	
       
   463 EXPORT_C void RCommsBufChain::TrimStart(TInt aBytes)
       
   464 /**
       
   465 Trim the leftmost part of the data in the chain with the no. of bytes that is supplied
       
   466 
       
   467 @param aBytes The no. of bytes to be trimmed
       
   468 */
       
   469 	{
       
   470 	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
   471 	__ASSERT_ALWAYS(aBytes>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
       
   472 
       
   473 	if (aBytes==0)
       
   474 		return;
       
   475 
       
   476 	RCommsBuf* m, *p;
       
   477 	TInt o, n;
       
   478 	if ((m = Goto(aBytes, o, n, p)) == NULL)
       
   479 		{
       
   480 		Free();
       
   481 		return;
       
   482 		}
       
   483 
       
   484 	if (p!=NULL) // m not first mbuf?
       
   485 		{
       
   486 		p->SetNext(NULL);
       
   487 		iNext->Free();
       
   488 
       
   489 		iNext = m;
       
   490 		}
       
   491 
       
   492 	if (o!=m->Offset()) // not at mbuf boundary?
       
   493 		m->SetDataRange(o, n);		
       
   494 	}
       
   495 	
       
   496 EXPORT_C void RCommsBufChain::TrimEnd(TInt aBytes)
       
   497 /**
       
   498 Trim the rightmost part of the data in the chain with the no. of bytes that is supplied
       
   499 
       
   500 @param aBytes The no. of bytes to be trimmed
       
   501 */
       
   502 	{
       
   503 	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
   504 	__ASSERT_ALWAYS(aBytes>=0, CommsBuf::Panic(EMBuf_NegativeOffset));
       
   505 
       
   506 	if(aBytes==0)
       
   507 		{
       
   508 		Free();
       
   509 		return;
       
   510 		}
       
   511 
       
   512 	RCommsBuf* m, *p;
       
   513 	TInt o, n;
       
   514 	if ((m = Goto(aBytes, o, n, p)) == NULL )
       
   515 		return;
       
   516 
       
   517 	if (o!=m->Offset()) // not at mbuf boundary?
       
   518 		{
       
   519 		m->AdjustDataEnd(-n);
       
   520 		if (p = m->Next(), p!=NULL)
       
   521 			{
       
   522 			m->SetNext(NULL);
       
   523 			p->Free();
       
   524 			}
       
   525 		}
       
   526 	else
       
   527 		{
       
   528 		if (p!=NULL)
       
   529 			p->SetNext(NULL);
       
   530 		m->Free();
       
   531 		}		
       
   532 	}
       
   533 
       
   534 EXPORT_C TInt RCommsBufChain::Align(TInt aSize)
       
   535  /**
       
   536 Ensure that the first aSize bytes can be safely cast to a structure of size aSize.
       
   537 
       
   538 @param aSize A size
       
   539 @return the number of bytes actually aligned. This will be the min of aSize and chain length.
       
   540 */
       
   541 	{
       
   542 	__ASSERT_ALWAYS(iNext!=NULL, CommsBuf::Panic(EMBuf_EmptyChain));
       
   543 	__ASSERT_ALWAYS(aSize>=0, CommsBuf::Panic(EMBuf_NegativeLength));
       
   544 
       
   545 	// update length to the largest sized mbuf possible
       
   546 	if (aSize == KMaxTInt)	// trs; does the KMBufAll concept (ie. 'largest sized' mbuf) really make sense? code kept as is to avoid a functional break
       
   547 		{
       
   548 		aSize = iNext->Pool()->Pond().LargestBufSize();
       
   549 		}
       
   550 
       
   551 	// All data required is already in the first MBuf
       
   552 	if (aSize <= iNext->Length())
       
   553 		{
       
   554 		// Case 1 - allready aligned
       
   555 		// Case 2 - it needs to be aligned
       
   556 		if (!IS_ALIGNED(iNext->Offset()))
       
   557 			{
       
   558 			Mem::Copy(iNext->RawBase(), iNext->Ptr(), iNext->Length());
       
   559 			iNext->SetOffset(0);
       
   560 			}
       
   561 		return aSize; // already as required
       
   562 		}
       
   563 
       
   564 	// Get existing data at start
       
   565 	if (iNext->Offset() != 0)
       
   566 		{
       
   567 		Mem::Copy(iNext->RawBase(), iNext->Ptr(), iNext->Length());
       
   568 		iNext->SetOffset(0);
       
   569 		}
       
   570 
       
   571 	RCommsBuf* m = iNext->Next();
       
   572 	TInt len = iNext->Length();
       
   573 	while (len<aSize && m!=NULL)
       
   574 		{
       
   575 		TInt n = aSize-len;
       
   576 		if (n>m->Length())
       
   577 			n = m->Length();
       
   578 		Mem::Copy((TUint8*)iNext->RawBase() + (iNext->Offset() + iNext->Length()), (TUint8*)m->RawBase() + m->Offset(), n);
       
   579 		iNext->AdjustDataEnd(n);
       
   580 		m->AdjustDataStart(n);
       
   581 		len += n;
       
   582 
       
   583 		// MBuf might now be empty so free it
       
   584 		if (m->Length()==0)
       
   585 			{
       
   586 			iNext->SetNext(m->Next());
       
   587 			m->SetNext(NULL);
       
   588 			m->Free();
       
   589 			m = iNext->Next();
       
   590 			}
       
   591 		}
       
   592 	return len;		
       
   593 	}