--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btsdp/database/responsesizevisitor.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,571 @@
+// Copyright (c) 2000-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 <btsdp.h>
+#include "responsesizevisitor.h"
+#include "SDPDatabase.h"
+#include "mignorer.h"
+#include "DataEncoder.h"
+
+
+// Class CHandleItem
+EXPORT_C CHandleItem* CHandleItem::NewLC()
+ {
+ CHandleItem* self = new(ELeave)CHandleItem();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+EXPORT_C CHandleItem* CHandleItem::NewL()
+ {
+ CHandleItem* self = CHandleItem::NewLC();
+ CleanupStack::Pop();
+ return self;
+ }
+
+void CHandleItem::ConstructL()
+ {
+ iAttrSizeList = new (ELeave) CArrayPtrFlat<CAttrSizeItem>(KSDPAttListGran);
+ }
+
+CHandleItem::~CHandleItem()
+ {
+ if (iAttrSizeList)
+ {
+ iAttrSizeList->ResetAndDestroy();
+ delete iAttrSizeList;
+ }
+ }
+
+EXPORT_C CHandleItem* CHandleItem::CHandleItemL(TSdpServRecordHandle aHandleID, CSdpServRecord* aRecord)
+ {
+ CHandleItem* self = NewL();
+ self->iHandleID = aHandleID;
+ self->iRecord = aRecord;
+ self->iRecordSize = 0;
+ return self;
+ }
+
+void CHandleItem::AddAttrItemL(CAttrSizeItem* aItem)
+ {
+ iAttrSizeList->AppendL(aItem);
+ iRecordSize += aItem->Size() + 3; // add the attrID header size;
+ }
+
+void CHandleItem::AddAttrItemL(const TSdpAttributeID aAttributeID, TUint aSize, CSdpAttr* aAttribute)
+ {
+ CAttrSizeItem* sizeItem = new (ELeave) CAttrSizeItem(aAttributeID, aSize, aAttribute);
+ AddAttrItemL(sizeItem);
+ }
+
+
+// Class CSizeAccumulator
+
+EXPORT_C CSizeAccumulator* CSizeAccumulator::NewLC()
+ {
+ CSizeAccumulator* self = new(ELeave)CSizeAccumulator();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+EXPORT_C CSizeAccumulator* CSizeAccumulator::NewL()
+ {
+ CSizeAccumulator* self = CSizeAccumulator::NewLC();
+ CleanupStack::Pop();
+ return self;
+ }
+
+void CSizeAccumulator::ConstructL()
+ {
+ iHandleList = new (ELeave) CArrayPtrFlat<CHandleItem>(KSDPHandListGran);
+ }
+
+CSizeAccumulator::~CSizeAccumulator()
+ {
+ if ( iHandleList )
+ {
+ iHandleList->ResetAndDestroy();
+ delete iHandleList;
+ }
+ }
+
+void CSizeAccumulator::AddHandleL(CHandleItem* aHandleItem)
+ {
+ iHandleList->AppendL(aHandleItem);
+ }
+
+// do a crc across all the attributes
+EXPORT_C TUint16 CSizeAccumulator::CrcAttribs()
+ {
+ TUint16 totalCrc=0;
+ for (TInt i = 0; i<HandleCount(); i++)
+ {
+ for (TInt j = 0; j<AttrCount(i); j++)
+ {
+ CAttrSizeItem* attr = AttributeOf(i,j);
+ CSdpAttrValue& theVal = attr->Attr()->Value();
+ Mem::Crc(totalCrc, reinterpret_cast<const TAny*>(theVal.Des()[0]), attr->Size());
+ }
+ }
+ return totalCrc;
+
+ }
+
+/**
+ CSizeAccumulator::SizeLeft
+ calculate the size of the attribute list.
+
+ Foundation of the continuation handling.
+ It is used together with StartAt.
+ It walks through the list of records, each should contain
+ a list of attributes. There are internal start offsets for
+ both the record and attribute lists. These are set with StartAt.
+
+ The method returns the size (in bytes) of the attributes, together with
+ 3 bytes for each attribute ID (1 byte header, 2 byte UInt) and the correct
+ number of bytes for each DES header for the attributes of each record.
+
+ It does NOT calculate the DES header for a list of records. So an extra
+ DES must be created for Searvice Search Attribute.
+
+ This allows the total length to be written before the rest of the response
+ packet. It also allows the request handler to decide if it can fit the whole
+ response in to the response packet with or without continuation.
+ Return format is
+ @verbatim
+ Size left TUint
+ @endverbatim
+**/
+EXPORT_C TUint CSizeAccumulator::SizeLeft()
+ {
+ TUint totalSize=0;
+ TInt handleCount = HandleCount(); // for testing
+ for (TInt i = iFirstRec; i < handleCount; i++)
+ {
+ TUint recordSize = 0;
+ TInt attrCount = AttrCount(i); // for testing
+ for (TInt j = iFirstAtt; j < attrCount; j++)
+ {
+ CAttrSizeItem* attr = AttributeOf(i,j);
+ recordSize += attr->Size();
+ recordSize +=3; // the size of a attribute ID in the list
+ }
+ if (iFirstAtt == 0)
+ { // are we sizing the whole record ?
+ TUint recRecSize = iHandleList->At(i)->CHandleItem::iRecordSize; // for testing
+ if (recordSize != recRecSize) DbPanic(ESdpDbBadSearchPattern);
+ }
+ if (recordSize)
+ { // only add the header if there is any data
+ TUint desSize;
+ desSize = (recordSize > 0xff) ? 3 : 2;
+ desSize = (recordSize > 0xffff) ? 5 : desSize;
+ totalSize += recordSize + desSize; // size of the DES header for this record
+ }
+ }
+ return totalSize;
+
+ }
+
+
+/**
+ CSizeAccumulator::StartAt
+ Supplied with the data sent so far.
+ This walks through the list of service search record handles
+ and the the list of attributes associated with each record.
+
+ Once it has found the location where the size is less than or
+ equal to the parameter supplied, it sets CSizeAccumulators
+ internal record and attribute offsets to the point it has
+ reached. It will not update the offsets if the data is greater
+ than the input offset.
+
+ The method returns the two offsets and the data already sent
+ from the current attribute.
+
+ The method counts the DES header at the start of any record. It
+ does NOT count the overall DES header if there is more than one
+ record. To do this would require a count of all bytes to evaluate
+ the DES header size. This action is the same as that of SizeLeft.
+
+ ********************************************************
+ IMPORTANT NOTE: If aOffset traverses the database and
+ STOPS IN THE MIDDLE OF A NEW RECORD DES HEADER, it will return
+ THE DES HEADER OFFSET in the ATTRIBUTE OFFSET PARAMETER.
+ This is ambiguous...
+ ****A calling function should ensure aOffset cannot do this***
+
+ The method returns a bool to indicate the setting was OK. It will
+ be false if the two index parameters were used and they would
+ have set the lists beyond their end. Also it returns false if the
+ offset was beyond the end of the data. If the method returns EFalse,
+ the two index parameters are set to 0.
+
+ This method can reset the parameters by being called with offset 0.
+ However it will then return false.
+ Parameter format is
+ @verbatim
+ Offset in bytes from start of attribute data TUint
+ Record index (0...) TInt
+ Attribute index (0...) TInt
+ @endverbatim
+ Return format is
+ @verbatim
+ Were the offsets set OK TBool
+ Record index is set TInt
+ Attribute index is set TInt
+ @endverbatim
+
+**/
+EXPORT_C TBool CSizeAccumulator::StartAt(TUint aOffset, TUint& aPartSent, TInt& aRec, TInt& aAtt)
+ {
+ // calculates and sets the next record and attribute
+ if (aOffset == 0)
+ {
+ aRec = 0; iFirstRec = 0;
+ aAtt = 0; iFirstAtt = 0;
+ aPartSent = 0;
+ return EFalse;
+ }
+ TUint totalSize=0;
+ TBool more = ETrue;
+ TInt topRec = HandleCount();
+ if (aRec > topRec)
+ {
+ aRec = 0; iFirstRec = 0;
+ aAtt = 0; iFirstAtt = 0;
+ aPartSent = 0;
+ return EFalse;
+ }
+ else if (aRec == topRec)
+ {
+ TInt partAtt = AttrCount(topRec);
+ if (aAtt > partAtt)
+ {
+ aRec = 0; iFirstRec = 0;
+ aAtt = 0; iFirstAtt = 0;
+ aPartSent = 0;
+ return EFalse;
+ }
+ }
+ iFirstRec = aRec;
+ TInt firstAttForNextRecord = aAtt;
+ while (more && (iFirstRec < topRec))
+ {
+ TUint recordSize = iHandleList->At(iFirstRec)->CHandleItem::iRecordSize;
+ TInt topAtt = AttrCount(iFirstRec);
+ iFirstAtt = firstAttForNextRecord < topAtt ? firstAttForNextRecord : topAtt;
+// except for first record we always start with a record's first attribute
+ firstAttForNextRecord = 0;
+// add in the DES header size for this record
+ TUint desSize;
+ desSize = (recordSize > 0xff) ? 3 : 2;
+ desSize = (recordSize > 0xffff) ? 5 : desSize;
+ if (totalSize + desSize > aOffset)
+ //see "IMPORTANT NOTE" above this method
+ {
+ more = EFalse;
+ break;
+ }
+ totalSize += desSize;
+ while (more && (iFirstAtt < topAtt))
+ {
+ CAttrSizeItem* attr = AttributeOf(iFirstRec, iFirstAtt);
+ TUint nextSize = attr->Size();
+ nextSize += 3; // Attribute ID and its header
+ if (totalSize + nextSize > aOffset)
+ {
+ more = EFalse;
+ break;
+ }
+ totalSize += nextSize;
+ iFirstAtt++;
+ }
+ if (more) iFirstRec++;
+ }
+ if (totalSize == aOffset) more = EFalse;
+ if (more)
+ {
+ aRec = 0; iFirstRec = 0;
+ aAtt = 0; iFirstAtt = 0;
+ aPartSent = 0;
+ return EFalse;
+ }
+ aRec = iFirstRec;
+ aAtt = iFirstAtt;
+ aPartSent = aOffset - totalSize;
+ return ETrue;
+// __ASSERT_DEBUG(totalSize != aOffset, DbPanic(ESdpDbBadSearchPattern));
+ }
+
+EXPORT_C TInt CSizeAccumulator::HandleCount()
+ {
+ return iHandleList->Count();
+ }
+
+EXPORT_C TSdpServRecordHandle CSizeAccumulator::HandleAt(TInt aOffset)
+ {
+ __ASSERT_DEBUG(aOffset <= iHandleList->Count(), DbPanic(ESdpDbBadSearchPattern)); // FIXME different panic code please
+ return iHandleList->At(aOffset)->CHandleItem::iHandleID;
+ }
+
+EXPORT_C TUint CSizeAccumulator::HandleSize(TInt aOffset)
+ {
+ return iHandleList->At(aOffset)->CHandleItem::iRecordSize;
+ }
+
+EXPORT_C TInt CSizeAccumulator::AttrCount(TInt aOffset)
+ {
+ if (iHandleList->Count() == 0) return 0;
+ __ASSERT_DEBUG(aOffset <= iHandleList->Count(), DbPanic(ESdpDbBadSearchPattern));
+ if (iHandleList->At(aOffset) == NULL) return 0;
+ return iHandleList->At(aOffset)->CHandleItem::iAttrSizeList->Count();
+ }
+
+EXPORT_C CAttrSizeItem* CSizeAccumulator::AttributeOf(TInt aHandleOffset, TInt aAttOffset)
+ {
+ if (iHandleList->Count() == 0) return (CAttrSizeItem*)0;
+ __ASSERT_DEBUG(aHandleOffset < iHandleList->Count(), DbPanic(ESdpDbBadSearchPattern));
+ CHandleItem* hnd = iHandleList->At(aHandleOffset);
+ if (hnd->iAttrSizeList->Count() == 0) return (CAttrSizeItem*)0;
+ __ASSERT_DEBUG(aAttOffset < hnd->iAttrSizeList->Count(), DbPanic(ESdpDbBadSearchPattern));
+// return iHandleList->At(aHandleOffset)->CHandleItem::iAttrSizeList->At(aAttOffset);
+ return hnd->iAttrSizeList->At(aAttOffset);
+ }
+
+
+
+// need this because CResponseSizeVisitor already has start list method...
+class CSizeEncVisitorAdaptor : public MIgnorer
+ {
+public:
+ CSizeEncVisitorAdaptor(CResponseSizeVisitor& aVisitor)
+ :iVisitor(aVisitor)
+ {}
+ MSdpElementBuilder* BuildUUIDL(const TUUID& aUUID)
+ {
+ iVisitor.FoundUUIDL(aUUID);
+ return this;
+ }
+private:
+ CResponseSizeVisitor& iVisitor;
+ };
+
+
+
+
+// Class CResponseSizeVisitor
+
+CResponseSizeVisitor::CResponseSizeVisitor()
+ {
+ }
+
+CResponseSizeVisitor::~CResponseSizeVisitor()
+ {
+ delete iFoundIndex;
+ delete iCurrentRec;
+ delete iAdapter;
+ delete iParser;
+ }
+
+CResponseSizeVisitor* CResponseSizeVisitor::NewLC()
+ {
+ CResponseSizeVisitor* self = new(ELeave)CResponseSizeVisitor();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+CResponseSizeVisitor* CResponseSizeVisitor::NewL()
+ {
+ CResponseSizeVisitor* self = CResponseSizeVisitor::NewLC();
+ CleanupStack::Pop();
+ return self;
+ }
+
+void CResponseSizeVisitor::ConstructL()
+ {
+ iAdapter = new(ELeave) CSizeEncVisitorAdaptor(*this);
+ iParser = CElementParser::NewL(iAdapter);
+ }
+
+// Iterate thru attributes in record
+/* Search through all attributes in a record
+ either to find if it matches the UUIDlist,
+ or if the attributes match the AttrIdMatchList
+ or both. If there is no UUIDList then the sizes
+ of matched attributes is saved to the size collector
+*/
+void CResponseSizeVisitor::SearchRecordL(CSdpServRecord& aRec)
+ {
+// could be in calling routine
+ if (iSearchPattern)
+ {
+ if (iFoundIndex)
+ {
+ delete iFoundIndex;
+ iFoundIndex = 0;
+ }
+ iFoundIndex = CBitMapAllocator::NewL(iSearchSize);
+ iUseThis = EFalse;
+ }
+ else
+ iUseThis = ETrue;
+// if (iCurrentRec) delete iCurrentRec; // I want a reset.
+ iCurrentRec = CHandleItem::CHandleItemL(aRec.Handle(), &aRec);
+
+ for(TServAttrIter attrIter(aRec.AttributeIter()); attrIter; attrIter++)
+ {
+/*
+ we are checking every attribute, searching for the UUIDs. We don't exit if
+ we find them all because we also want to get the sizes of any attributes which
+ match our list if we have one.
+ The array of sizes is built, then thrown away if it doesn't match.
+
+*/
+ if (iAtMatList)
+ {
+ TSdpAttributeID theAttrID = (*attrIter).AttributeID();
+ if(iAtMatList->InMatchList(theAttrID))
+ {// create a new array entry
+ TUint size = (*attrIter).Value().DataSize();
+ TSdpElementType type = (*attrIter).Value().Type();
+ if (type != ETypeEncoded)
+ {
+// some server records may be un-encoded
+ size += TElementEncoder::HeaderSize(type, size); // add the header size
+ }
+ if(!(type==ETypeNil)&&!(type==ETypeEncoded&&size<=1))
+ { // only include attribute if not either null or encoded null ...
+ // (size of encoded (which INCLUDES header) in all non-null cases is > 1
+ // because other attribute types either have
+ // to have a header plus at least one byte, or they have at least a two byte header.)
+ iCurrentRec->AddAttrItemL(theAttrID, size, attrIter);
+ }
+ }
+ }
+ // check if any UUIDs match only if we have a UUIDList.
+ if (iSearchPattern) (*attrIter).AcceptVisitorL(*this);
+ }
+ if (iUseThis)
+ {
+ // now we have to copy the array(s) we've built up.
+ iCollector->AddHandleL(iCurrentRec);
+ iCurrentRec = 0;
+ }
+ else
+ {
+ delete iCurrentRec; // I don't want a heap fault.
+ iCurrentRec = 0;
+ }
+ }
+
+
+// static void SizeRespSSL(CSdpDatabase& aDb, const CSdpSearchPattern &aPattern, const CSizeAccumulator& aCollector);
+// static void SizeRespARL(CSdpServRecord& aRec, CSdpAttrIdMatchList &aList, const CSizeAccumulator& aCollector);
+// static void SizeRespSAL(CSdpDatabase &aDb, const CSdpSearchPattern &aPattern, CSdpAttrIdMatchList &aList, const CSizeAccumulator& aCollector);
+// we need three of these calls, all slightly different
+ EXPORT_C void CResponseSizeVisitor::SizeRespSSL(CSdpDatabase& aDb, const CSdpSearchPattern& aPattern, CSizeAccumulator& aCollector)
+ {
+ if (aPattern.Count() == 0) User::Leave(KErrArgument); // this is part of spec.
+// SDP_DEBUG(3, FPrint(_L("Sizing SDP DB for pattern with %d entries\n"), aPattern.Count()));
+ CResponseSizeVisitor* theVisitor = CResponseSizeVisitor::NewLC();
+ theVisitor->iCollector = &aCollector;
+ theVisitor->iAtMatList = NULL; // using it as a flag...
+ theVisitor->iSearchPattern = &aPattern;
+ theVisitor->iSearchSize = theVisitor->iSearchPattern->Count();
+
+ for(TServRecordIter recIter(aDb.RecordIter()); recIter; recIter++)
+ {// Iterate thru records in Db
+ theVisitor->SearchRecordL(*recIter);
+ }
+ CleanupStack::PopAndDestroy();//theVisitor
+ }
+
+ EXPORT_C void CResponseSizeVisitor::SizeRespARL(CSdpServRecord& aRec, const CSdpAttrIdMatchList& aList, CSizeAccumulator& aCollector)
+ {
+// SDP_DEBUG(3, FPrint(_L("Sizing SDP DB for single record\n")));
+ CResponseSizeVisitor* theVisitor = CResponseSizeVisitor::NewLC();
+ theVisitor->iCollector = &aCollector;
+ theVisitor->iAtMatList = &aList; // using it as a flag...
+ theVisitor->iSearchPattern = NULL;
+ theVisitor->iSearchSize = 0;
+ theVisitor->SearchRecordL(aRec);
+ CleanupStack::PopAndDestroy();//theVisitor
+ }
+
+ EXPORT_C void CResponseSizeVisitor::SizeRespSAL(CSdpDatabase &aDb, const CSdpSearchPattern &aPattern, const CSdpAttrIdMatchList& aList, CSizeAccumulator& aCollector)
+ {
+ if (aPattern.Count() == 0) User::Leave(KErrArgument); // this is part of spec.
+// SDP_DEBUG(3, FPrint(_L("Sizing SDP DB (and attribs) with %d UUIDs\n"), aPattern.Count()));
+ CResponseSizeVisitor* theVisitor = CResponseSizeVisitor::NewLC();
+ theVisitor->iCollector = &aCollector;
+ theVisitor->iAtMatList = &aList; // using it as a flag...
+ theVisitor->iSearchPattern = &aPattern;
+ theVisitor->iSearchSize = theVisitor->iSearchPattern->Count();
+
+ for(TServRecordIter recIter(aDb.RecordIter()); recIter; recIter++)
+ {// Iterate thru records in Db
+ theVisitor->SearchRecordL(*recIter);
+ }
+ CleanupStack::PopAndDestroy();//theVisitor
+ }
+
+
+
+void CResponseSizeVisitor::VisitAttributeL(CSdpAttr& /*aAttribute*/)
+ {
+ }
+
+void CResponseSizeVisitor::VisitAttributeValueL(CSdpAttrValue &aValue, TSdpElementType aType)
+ {
+ switch (aType)
+ {
+ case ETypeUUID:
+ FoundUUIDL(aValue.UUID());
+ break;
+ case ETypeEncoded:
+// parse out any UUIDs in this encoded attribute
+ iParser->Reset();
+ /*rem = */iParser->ParseElementsL(aValue.Des());
+ break;
+ default:
+ return;
+ }
+ }
+
+void CResponseSizeVisitor::StartListL(CSdpAttrValueList &/*aList*/)
+ {
+ }
+
+void CResponseSizeVisitor::EndListL()
+ {
+ }
+
+void CResponseSizeVisitor::FoundUUIDL(const TUUID& aUUID)
+ {
+ TInt pos;
+ if (iSearchPattern->Find(aUUID, pos)==0 &&
+ iFoundIndex->IsFree(pos))
+ {
+ iFoundIndex->AllocAt(pos);
+ if (iFoundIndex->Avail() == 0)
+ {// We've found what we were searching for.
+ iUseThis = ETrue;
+ }
+ }
+ }