userlibandfileserver/fileserver/sfat32/inc/sl_std.inl
changeset 0 a41df078684a
child 6 0173bcd7697c
equal deleted inserted replaced
-1:000000000000 0:a41df078684a
       
     1 // Copyright (c) 1996-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 the License "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 // f32\sfat32\inc\sl_std.inl
       
    15 // 
       
    16 //
       
    17 
       
    18 #ifndef SL_STD_INL
       
    19 #define SL_STD_INL
       
    20 
       
    21 
       
    22 
       
    23 TBool IsPowerOf2(TUint32 aVal)
       
    24     {
       
    25 	if (aVal==0)
       
    26 		return EFalse;
       
    27 
       
    28     return !(aVal & (aVal-1));
       
    29     }
       
    30 
       
    31 TUint32 Pow2(TUint32 aVal)
       
    32     {
       
    33         //ASSERT(aVal<32);
       
    34         return 1 << aVal;
       
    35     }
       
    36 
       
    37 
       
    38 //---------------------------------------------------------------------------------------------------------------------------------
       
    39 // class TEntryPos
       
    40 TUint32 TEntryPos::Cluster() const 
       
    41     {
       
    42     return (TUint32) iCluster;
       
    43     }
       
    44 
       
    45 TUint32 TEntryPos::Pos() const 
       
    46     {
       
    47     return (TUint32) iPos;
       
    48     }
       
    49 
       
    50 TBool TEntryPos::operator==(const TEntryPos& aRhs) const
       
    51     {
       
    52     ASSERT(this != &aRhs);
       
    53     return (iCluster == aRhs.iCluster && iPos == aRhs.iPos);
       
    54     }
       
    55 
       
    56 
       
    57 //---------------------------------------------------------------------------------------------------------------------------------
       
    58 // class CFatMountCB
       
    59 
       
    60 inline TInt CFatMountCB::RootDirectorySector() const
       
    61     {return iVolParam.RootDirectorySector();}
       
    62 
       
    63 inline TUint CFatMountCB::RootDirEnd() const
       
    64     {return iVolParam.RootDirEnd();}
       
    65 
       
    66 inline TUint32 CFatMountCB::RootClusterNum() const
       
    67     {return iVolParam.RootClusterNum(); }        
       
    68 
       
    69 
       
    70 inline TInt CFatMountCB::StartCluster(const TFatDirEntry & anEntry) const
       
    71 	{
       
    72 	if(Is32BitFat())	
       
    73 		return anEntry.StartCluster();
       
    74 	else
       
    75 		return 0xFFFF&anEntry.StartCluster();
       
    76 	}
       
    77 
       
    78 /**
       
    79 returns true for root dir on Fat12/16 (fixed root dir versions of Fat) false on fat32 
       
    80 this function is used to handle special cases for reading/writing the root directory on FAT via the use of cluster zero.
       
    81 
       
    82 @param aEntry position on volume being queried
       
    83 @return Whether Root dir position or not
       
    84 */
       
    85 TBool CFatMountCB::IsRootDir(const TEntryPos &aEntry) const
       
    86 	{
       
    87 	if(Is32BitFat())
       
    88 		return EFalse;
       
    89 	else
       
    90 		return((aEntry.iCluster==0) ? (TBool)ETrue : (TBool)EFalse);	
       
    91 	}
       
    92 /**
       
    93 Indicates the root directory cluster, For Fat12/16 root is always indicated by cluster number zero, on Fat32 the is a root cluster number
       
    94 @return The root cluster indicator
       
    95 */
       
    96 TInt CFatMountCB::RootIndicator() const
       
    97 	{
       
    98 	if(Is32BitFat())
       
    99         return iVolParam.RootClusterNum();
       
   100 	else
       
   101 		return 0;
       
   102 	}
       
   103 
       
   104 
       
   105 /** @return Log2 of cluster size on volume */
       
   106 TInt CFatMountCB::ClusterSizeLog2() const
       
   107     {return(iVolParam.ClusterSizeLog2());}
       
   108 
       
   109 /** @return Log2 of media sector size  */
       
   110 TInt CFatMountCB::SectorSizeLog2() const
       
   111     {return(iVolParam.SectorSizeLog2());}
       
   112 
       
   113 /** @return sector per cluster */
       
   114 TInt CFatMountCB::SectorsPerCluster() const
       
   115     {return(1<<(iVolParam.ClusterSizeLog2()-iVolParam.SectorSizeLog2()));}
       
   116 
       
   117 /** @return the base position of a cluster */
       
   118 TInt CFatMountCB::ClusterBasePosition() const
       
   119 	{return(iFirstFreeByte);}
       
   120 
       
   121 /** @return the offset into a cluster of a byte address */
       
   122 TInt CFatMountCB::ClusterRelativePos(TInt aPos) const
       
   123 	{return(aPos&((1<<ClusterSizeLog2())-1));}
       
   124 
       
   125 /**
       
   126 Calculates the maximum number of clusters
       
   127 @return  maximum number of clusters
       
   128 */
       
   129 TUint32 CFatMountCB::MaxClusterNumber() const
       
   130     {return(TotalSectors()>>(ClusterSizeLog2()-SectorSizeLog2()));}
       
   131 
       
   132 /** @return the the total sectors on volume */
       
   133 TInt CFatMountCB::TotalSectors() const
       
   134     {return iVolParam.TotalSectors();}
       
   135 
       
   136 /** @return total size of a Fat in bytes */
       
   137 TInt CFatMountCB::FatSizeInBytes() const
       
   138     {return iVolParam.FatSizeInBytes();}
       
   139 
       
   140 /** @return first sector of the Fat */
       
   141 TUint32 CFatMountCB::FirstFatSector() const
       
   142     {return iVolParam.FirstFatSector();}
       
   143 
       
   144 /** @return the byte offset of the Fat */
       
   145 TInt CFatMountCB::StartOfFatInBytes() const
       
   146 	{return(FirstFatSector()<<SectorSizeLog2());}
       
   147 
       
   148 /** @return Number of Fats used by the volume */
       
   149 TInt CFatMountCB::NumberOfFats() const
       
   150     {return iVolParam.NumberOfFats();}
       
   151 
       
   152 
       
   153 /** @return refrence to the fat table owned by the mount */
       
   154 CFatTable& CFatMountCB::FAT() const
       
   155 	{return(*iFatTable);}
       
   156 /**
       
   157     @return refrence to the file system object that has produced this CFatMountCB
       
   158 */
       
   159 CFatFileSystem& CFatMountCB::FatFileSystem() const
       
   160 	{
       
   161     return (CFatFileSystem&)(*FileSystem()); //-- CMountCB::FileSystem() provides correct answer
       
   162     }
       
   163 
       
   164 
       
   165 /** @return  refrence to a raw disk object owned by the mount */
       
   166 CRawDisk& CFatMountCB::RawDisk() const
       
   167 	{return(*iRawDisk);}
       
   168 
       
   169 /**
       
   170 @return ETrue if aCluster value is bad cluster marker defined in FAT specification
       
   171 */
       
   172 TBool CFatMountCB::IsBadCluster(TInt aCluster) const
       
   173 	{return Is32BitFat() ? aCluster==0xFFFFFF7 : Is16BitFat() ? aCluster==0xFFF7 : aCluster==0xFF7;}
       
   174 
       
   175 /**
       
   176 Returns whether the current mount is running as rugged Fat or not, this is held in the file system object
       
   177 @return Is rugged fat flag
       
   178 */
       
   179 TBool CFatMountCB::IsRuggedFSys() const
       
   180 	{return Drive().IsRugged();}
       
   181 
       
   182 /**
       
   183 Sets the rugged flag in the file system object
       
   184 @param Flag to set or clear the rugged flag
       
   185 */
       
   186 void CFatMountCB::SetRuggedFSys(TBool aVal)
       
   187 	{Drive().SetRugged(aVal);}
       
   188 
       
   189 /** @return the usable clusters count for a volume */
       
   190 TUint32 CFatMountCB::UsableClusters() const
       
   191 	{return(iUsableClusters);}
       
   192 
       
   193 
       
   194 TUint CFatMountCB::StartOfRootDirInBytes() const
       
   195     {return iVolParam.RootDirectorySector()<<SectorSizeLog2();}
       
   196 
       
   197 
       
   198 /** @return FAT type for this mount */
       
   199 TFatType CFatMountCB::FatType() const
       
   200 {
       
   201     return iFatType;
       
   202 }
       
   203 
       
   204 /** @return ETrue if the mount has 16bit FAT */
       
   205 TBool CFatMountCB::Is16BitFat() const
       
   206 {
       
   207     return FatType() == EFat16;
       
   208 } 
       
   209 
       
   210 /** @return ETrue if the mount has 32bit FAT */
       
   211 TBool CFatMountCB::Is32BitFat() const
       
   212 {   
       
   213     return FatType() == EFat32;
       
   214 }
       
   215 
       
   216 CAsyncNotifier* CFatMountCB::Notifier() const
       
   217 	{return iNotifier;}	
       
   218 
       
   219 
       
   220 
       
   221 /**
       
   222     Set or reset Read Only mode for the mount.
       
   223     @param    aReadOnlyMode if ETrue, the mount will be set RO.
       
   224 */
       
   225 void  CFatMountCB::SetReadOnly(TBool aReadOnlyMode) 
       
   226     {
       
   227     iReadOnly = aReadOnlyMode;
       
   228     }
       
   229 
       
   230 
       
   231 /**
       
   232     @return ETrue if the volume is in Read-Only state
       
   233 */
       
   234 TBool CFatMountCB::ReadOnly(void) const
       
   235     {
       
   236     return iReadOnly;
       
   237     }
       
   238 
       
   239 /** @return state of the CFatMountCB*/
       
   240 CFatMountCB::TFatMntState CFatMountCB::State() const 
       
   241     {
       
   242     return iState;
       
   243     }
       
   244 
       
   245 /** 
       
   246     Set state of the CFatMountCB
       
   247     @param  aState state to set
       
   248 */
       
   249 void CFatMountCB::SetState(TFatMntState aState)
       
   250     {
       
   251     __PRINT3(_L("#- CFatMountCB::SetState() drv:%d, %d->%d\n"),DriveNumber(),iState,aState);
       
   252     iState = aState;
       
   253     }
       
   254 
       
   255 
       
   256 TFatDriveInterface& CFatMountCB::DriveInterface() const 
       
   257     {
       
   258     return (TFatDriveInterface&)iDriverInterface; 
       
   259     }
       
   260 
       
   261 const TFatConfig& CFatMountCB::FatConfig() const 
       
   262     {
       
   263     return iFatConfig;
       
   264     }
       
   265 
       
   266 //---------------------------------------------------------------------------------------------------------------------------------
       
   267 /** 
       
   268 Check if the XFileCreationHelper object is initialised.
       
   269 */
       
   270 TBool CFatMountCB::XFileCreationHelper::IsInitialised() const 
       
   271 	{
       
   272 	return isInitialised;
       
   273 	}
       
   274 
       
   275 /** 
       
   276 Get number of new entries for file creation.
       
   277 */
       
   278 TUint16	CFatMountCB::XFileCreationHelper::NumOfAddingEntries() const
       
   279 	{
       
   280 	ASSERT(isInitialised); 
       
   281 	return iNumOfAddingEntries;
       
   282 	}
       
   283 
       
   284 /** 
       
   285 Get position of new entries for file creation.
       
   286 */
       
   287 TEntryPos CFatMountCB::XFileCreationHelper::EntryAddingPos() const 
       
   288 	{
       
   289 	ASSERT(isInitialised); 
       
   290 	return iEntryAddingPos;
       
   291 	}
       
   292 
       
   293 /** 
       
   294 Check if position of new entries has been found.
       
   295 */
       
   296 TBool CFatMountCB::XFileCreationHelper::IsNewEntryPosFound() const 
       
   297 	{
       
   298 	ASSERT(isInitialised); 
       
   299 	return isNewEntryPosFound;
       
   300 	}
       
   301 
       
   302 /** 
       
   303 Check if file name of the new file is a legal dos name.
       
   304 */
       
   305 TBool CFatMountCB::XFileCreationHelper::IsTrgNameLegalDosName() const
       
   306 	{
       
   307 	ASSERT(isInitialised); 
       
   308 	return isTrgNameLegalDosName;
       
   309 	}
       
   310 
       
   311 /** 
       
   312 Set entry position for new entries to be added.
       
   313 */
       
   314 void CFatMountCB::XFileCreationHelper::SetEntryAddingPos(const TEntryPos& aEntryPos) 
       
   315 	{
       
   316 	iEntryAddingPos = aEntryPos;
       
   317 	}
       
   318 
       
   319 /** 
       
   320 Set condition if position of new entries has been found.
       
   321 */
       
   322 void CFatMountCB::XFileCreationHelper::SetIsNewEntryPosFound(TBool aFound) 
       
   323 	{
       
   324 	isNewEntryPosFound = aFound;
       
   325 	}
       
   326 
       
   327 
       
   328 //-------  debug methods
       
   329 #ifdef  _DEBUG
       
   330 /**
       
   331 Debug function indicates whether write fails are active or not, for test
       
   332 @return ETrue if write fails on or not
       
   333 */
       
   334 TBool CFatMountCB::IsWriteFail()const
       
   335 	{return(iIsWriteFail);}
       
   336 /**
       
   337 Switches write fails on or off, for test
       
   338 @param aIsWriteFail set true or false to set write fails on or off
       
   339 */
       
   340 void CFatMountCB::SetWriteFail(TBool aIsWriteFail)
       
   341 	{iIsWriteFail=aIsWriteFail;}
       
   342 
       
   343 /** @return number of write fails to occur, for test */
       
   344 TInt CFatMountCB::WriteFailCount()const
       
   345 	{return(iWriteFailCount);}
       
   346 
       
   347 /**
       
   348 Set the number of Write fails 
       
   349 @param aFailCount number of write fails, for test
       
   350 */
       
   351 void CFatMountCB::SetWriteFailCount(TInt aFailCount)
       
   352 	{iWriteFailCount=aFailCount;}
       
   353 
       
   354 /** Decrement the number of write fails, for test */
       
   355 void CFatMountCB::DecWriteFailCount()
       
   356 	{--iWriteFailCount;}
       
   357 
       
   358 /** @return Error for a write failure, for test */
       
   359 TInt CFatMountCB::WriteFailError()const
       
   360 	{return iWriteFailError;}
       
   361 
       
   362 /**
       
   363 Set the write fail error code, for test
       
   364 @param aErrorValue The Error for a write fails
       
   365 */
       
   366 void CFatMountCB::SetWriteFailError(TInt aErrorValue)
       
   367 	{iWriteFailError=aErrorValue;}
       
   368 
       
   369 #endif
       
   370 
       
   371 
       
   372 //---------------------------------------------------------------------------------------------------------------------------------
       
   373 // class CFatFormatCB
       
   374 
       
   375 /** @return pointer to the owning mount object */
       
   376 CFatMountCB& CFatFormatCB::FatMount()
       
   377 	{return *(CFatMountCB*)&Mount();}
       
   378 
       
   379 /**
       
   380 Returns the local drive used by the file systems from the owning mount
       
   381 @return Pointer to the local drive 
       
   382 */
       
   383 CProxyDrive* CFatFormatCB::LocalDrive()
       
   384 	{return(FatMount().LocalDrive());}
       
   385 
       
   386 
       
   387 //---------------------------------------------------------------------------------------------------------------------------------
       
   388 // class CFatFileCB
       
   389 
       
   390 /**
       
   391 Returns the owning mount from file object
       
   392 
       
   393 @return pointer to the owning mount object
       
   394 */
       
   395 CFatMountCB& CFatFileCB::FatMount() const
       
   396 	{return((CFatMountCB&)Mount());}
       
   397 
       
   398 /**
       
   399 Returns the fat table used by the file system for this mount
       
   400 
       
   401 @return Refrence to the Fat table owned by the mount
       
   402 */
       
   403 CFatTable& CFatFileCB::FAT()
       
   404 	{return(FatMount().FAT());}
       
   405 
       
   406 /**
       
   407 Position with in a cluster for a given address
       
   408 
       
   409 @param aPos Byte position 
       
   410 */
       
   411 TInt CFatFileCB::ClusterRelativePos(TInt aPos)
       
   412 	{return(FatMount().ClusterRelativePos(aPos));}
       
   413 /**
       
   414 Returns Log2 of cluster size from mount
       
   415 
       
   416 @return cluster size
       
   417 */
       
   418 TInt CFatFileCB::ClusterSizeLog2()
       
   419 	{return(FatMount().ClusterSizeLog2());}
       
   420 
       
   421 /*
       
   422  Note: this replaces SeekIndex() which was only used in sl_mnt
       
   423  to verify whether the seek index had been created/initialised
       
   424 */
       
   425 inline TBool CFatFileCB::IsSeekIndex() const
       
   426 {return (iSeekIndex==NULL?(TBool)EFalse:(TBool)ETrue); }
       
   427 
       
   428 
       
   429 //---------------------------------------------------------------------------------------------------------------------------------
       
   430 // class CFatDirCB
       
   431 
       
   432 /**
       
   433 Returns the owning mount from directory object
       
   434 
       
   435 @return pointer to the owning mount object
       
   436 */
       
   437 CFatMountCB& CFatDirCB::FatMount()
       
   438 	{return((CFatMountCB&)Mount());}
       
   439 
       
   440 
       
   441 
       
   442 //---------------------------------------------------------------------------------------------------------------------------------
       
   443 // class CFatTable
       
   444 
       
   445 TUint32 CFatTable::FreeClusters() const 
       
   446     {
       
   447     return iFreeClusters;
       
   448     }
       
   449 
       
   450 TBool CFatTable::IsEof32Bit(TInt aCluster) const
       
   451 	{return(aCluster>=(TInt)0x0FFFFFF8 && aCluster<=(TInt)0x0FFFFFFF);} 
       
   452 
       
   453 TBool CFatTable::IsEof16Bit(TInt aCluster) const
       
   454 	{return(aCluster>=0xFFF8 && aCluster<=0xFFFF);}
       
   455 
       
   456 TBool CFatTable::IsEof12Bit(TInt aCluster) const
       
   457 	{return(aCluster>=0xFF8 && aCluster<=0xFFF);}
       
   458 
       
   459 TInt CFatTable::SectorSizeLog2() const
       
   460 	{return(iOwner->SectorSizeLog2());}
       
   461 
       
   462 //---------------------------------------------------------------------------------------------------------------------------------
       
   463 
       
   464 inline TFatType CFatTable::FatType() const 
       
   465     {
       
   466     return iFatType;
       
   467     }
       
   468 
       
   469 inline TBool CFatTable::IsFat12() const
       
   470     {
       
   471     return iFatType == EFat12;
       
   472     }
       
   473 
       
   474 inline TBool CFatTable::IsFat16() const
       
   475     {
       
   476     return iFatType == EFat16;
       
   477     }
       
   478 
       
   479 inline TBool CFatTable::IsFat32() const
       
   480     {
       
   481     return iFatType == EFat32;
       
   482     }
       
   483 
       
   484 
       
   485 /**
       
   486 @return Maximal number of addresable FAT entries. This value is taken from the owning mount
       
   487 */
       
   488 inline TUint32 CFatTable::MaxEntries() const
       
   489     {
       
   490         ASSERT(iMaxEntries > 0);
       
   491         return iMaxEntries;
       
   492     }
       
   493 
       
   494 
       
   495 // class TFatDriveInterface
       
   496 TBool TFatDriveInterface::NotifyUser() const
       
   497 	{return(iMount->GetNotifyUser());}
       
   498 
       
   499 
       
   500 //----------------------------------------------------------------------------------------------------
       
   501 // class CRawDisk
       
   502 
       
   503 /**
       
   504     Get pointer to the directory cache interface. Any client that reads/writes directory entries 
       
   505     MUST do it via this interface.
       
   506     Default implementation returns NULL
       
   507 
       
   508     @return     pointer to the MWTCacheInterface interface, or NULL if it is not present.
       
   509 */
       
   510 MWTCacheInterface* CRawDisk::DirCacheInterface()
       
   511     {
       
   512     return NULL;
       
   513     }
       
   514 
       
   515 //---------------------------------------------------------------------------------------------------------------------------------	
       
   516 //-- class RBitVector
       
   517 
       
   518 /** @return size of the vector (number of bits) */
       
   519 inline TUint32 RBitVector::Size() const
       
   520     {
       
   521     return iNumBits;
       
   522     } 
       
   523 
       
   524 /**
       
   525     Get a bit by index
       
   526     
       
   527     @param aIndex  index in a bit vector
       
   528     @return 0 if the bit at pos aIndex is 0, not zero otherwise
       
   529     @panic EIndexOutOfRange if aIndex is out of range
       
   530 */
       
   531 inline TBool RBitVector::operator[](TUint32 aIndex) const
       
   532     {
       
   533     __ASSERT_ALWAYS(aIndex < iNumBits, Panic(EIndexOutOfRange));
       
   534     return (ipData[WordNum(aIndex)] & (1<<BitInWord(aIndex)));
       
   535     }
       
   536 
       
   537 /**
       
   538     Set a bit at pos aIndex to '1'
       
   539     @param aIndex  index in a bit vector
       
   540     @panic EIndexOutOfRange if aIndex is out of range
       
   541 */
       
   542 inline void RBitVector::SetBit(TUint32 aIndex)
       
   543     {
       
   544     __ASSERT_ALWAYS(aIndex < iNumBits, Panic(EIndexOutOfRange));
       
   545     ipData[WordNum(aIndex)] |= (1<<BitInWord(aIndex));
       
   546     }
       
   547 
       
   548 /**
       
   549     Set a bit at pos aIndex to '0'
       
   550     @param aIndex  index in a bit vector
       
   551     @panic EIndexOutOfRange if aIndex is out of range
       
   552 */
       
   553 inline void RBitVector::ResetBit(TUint32 aIndex)
       
   554     {
       
   555     __ASSERT_ALWAYS(aIndex < iNumBits, Panic(EIndexOutOfRange));
       
   556     ipData[WordNum(aIndex)] &= ~(1<<BitInWord(aIndex));
       
   557     }
       
   558 
       
   559 /**
       
   560     Invert a bit at pos aIndex
       
   561     @param aIndex  index in a bit vector
       
   562     @panic EIndexOutOfRange if aIndex is out of range
       
   563 */
       
   564 inline void RBitVector::InvertBit(TUint32 aIndex)
       
   565     {
       
   566     __ASSERT_ALWAYS(aIndex < iNumBits, Panic(EIndexOutOfRange));
       
   567     ipData[WordNum(aIndex)] ^= (1<<BitInWord(aIndex));
       
   568     }
       
   569 
       
   570 /**
       
   571     Set bit value at position aIndex
       
   572     @param aIndex  index in a bit vector
       
   573     @panic EIndexOutOfRange if aIndex is out of range
       
   574 */
       
   575 inline void RBitVector::SetBitVal(TUint32 aIndex, TBool aVal)
       
   576     {
       
   577     if(aVal) 
       
   578         SetBit(aIndex);
       
   579     else 
       
   580         ResetBit(aIndex);
       
   581     }
       
   582 
       
   583 
       
   584 inline TUint32 RBitVector::MaskLastWord(TUint32 aVal) const
       
   585     {
       
   586     const TUint32 shift = (32-(iNumBits & 0x1F)) & 0x1F;
       
   587     return (aVal << shift) >> shift; //-- mask unused high bits
       
   588     }
       
   589 
       
   590 
       
   591 
       
   592 inline TUint32 RBitVector::WordNum(TUint32 aBitPos)  const
       
   593     {
       
   594     return aBitPos >> 5;
       
   595     }
       
   596 
       
   597 inline TUint32 RBitVector::BitInWord(TUint32 aBitPos) const 
       
   598     {
       
   599     return aBitPos & 0x1F;
       
   600     }
       
   601 
       
   602 /**
       
   603     Calculate offset of the page starting position in the cluster 
       
   604     @param aPos  the current entry position in bytes in the cluster
       
   605     @param aPageSzLog2	page size in log2
       
   606     @return		the starting position of the page that contains aPos
       
   607 */
       
   608 inline TUint32 CalculatePageOffsetInCluster(TUint32 aPos, TUint aPageSzLog2)
       
   609 	{
       
   610 	return (aPos >> aPageSzLog2) << aPageSzLog2;
       
   611 	}
       
   612 
       
   613 #endif //SL_STD_INL
       
   614 
       
   615 
       
   616 
       
   617 
       
   618