Darwin-Streaming-Server/CommonUtilitiesLib/OSFileSource.cpp
Darren VanBuren 849723c9cf Add even more of the source
This should be about everything needed to build so far?
2017-03-07 17:14:16 -08:00

633 lines
18 KiB
C++

/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
*/
/*
File: osfile.cpp
Contains: simple file abstraction
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#ifndef __Win32__
#include <unistd.h>
#endif
#include "OSFileSource.h"
#include "OSMemory.h"
#include "OSThread.h"
#include "OS.h"
#include "OSQueue.h"
#include "OSHeaders.h"
#define FILE_SOURCE_DEBUG 0
#define FILE_SOURCE_BUFFTEST 0
#define TEST_TIME 0
#if TEST_TIME
static SInt64 startTime = 0;
static SInt64 durationTime = 0;
static SInt32 sReadCount = 0;
static SInt32 sByteCount = 0;
static Bool16 sMovie = false;
#endif
#if READ_LOG
extern UInt32 xTrackID;
void OSFileSource::SetLog(const char *inPath)
{
fFilePath[0] =0;
::strcpy(fFilePath,inPath);
if (fFile != -1 && fFileLog == NULL)
{
::strcat(fFilePath,inPath);
::strcat(fFilePath,".readlog");
fFileLog = ::fopen(fFilePath,"w+");
if (fFileLog && IsValid())
{ qtss_fprintf(fFileLog, "%s","QTFILE_READ_LOG\n");
qtss_fprintf(fFileLog, "size: %qu\n",GetLength());
qtss_printf("OSFileSource::SetLog=%s\n",fFilePath);
}
::fclose(fFileLog);
}
}
#else
void OSFileSource::SetLog(const char *inPath)
{
#if FILE_SOURCE_DEBUG
qtss_printf("OSFileSource::SetLog=%s\n",inPath);
#endif
}
#endif
FileBlockBuffer::~FileBlockBuffer(void)
{
if (fDataBuffer != NULL)
{
Assert (fDataBuffer[fBufferSize] == 0);
#if FILE_SOURCE_DEBUG
::memset( (char *)fDataBuffer,0, fBufferSize);
qtss_printf("FileBlockBuffer::~FileBlockBuffer delete %"_U32BITARG_" this=%"_U32BITARG_"\n",fDataBuffer, this);
#endif
delete fDataBuffer;
fDataBuffer = NULL;
fArrayIndex = -1;
}
else
Assert(false);
}
void FileBlockBuffer::AllocateBuffer(UInt32 buffSize)
{
fBufferSize = buffSize;
fDataBuffer = NEW char[buffSize + 1];
fDataBuffer[buffSize] = 0;
#if FILE_SOURCE_DEBUG
this->CleanBuffer();
qtss_printf("FileBlockBuffer::FileBlockBuffer allocate buff ptr =%"_U32BITARG_" len=%"_U32BITARG_" this=%"_U32BITARG_"\n",fDataBuffer,buffSize,this);
#endif
}
void FileBlockBuffer::TestBuffer(void)
{
#if FILE_SOURCE_BUFFTEST
if (fDataBuffer != NULL)
Assert (fDataBuffer[fBufferSize] == 0);
#endif
}
void FileBlockPool::MarkUsed(FileBlockBuffer* inBuffPtr)
{
if (NULL == inBuffPtr)
return;
if (fQueue.GetTail() != inBuffPtr->GetQElem()) // Least Recently Used tail is last accessed
{
fQueue.Remove(inBuffPtr->GetQElem());
fQueue.EnQueue(inBuffPtr->GetQElem()); // put on tail
}
}
FileBlockBuffer *FileBlockPool::GetBufferElement(UInt32 bufferSizeBytes)
{
FileBlockBuffer* theNewBuf = NULL;
if ( fNumCurrentBuffers < fMaxBuffers)
{
#if FILE_SOURCE_DEBUG
qtss_printf("FileBlockPool::GetBufferElement NEW element fNumCurrentBuffers=%"_U32BITARG_" fMaxBuffers=%"_U32BITARG_" fBufferUnitSizeBytes=%"_U32BITARG_" bufferSizeBytes=%"_U32BITARG_"\n",fNumCurrentBuffers,fMaxBuffers,fBufferUnitSizeBytes,bufferSizeBytes);
#endif
theNewBuf = NEW FileBlockBuffer();
theNewBuf->AllocateBuffer(bufferSizeBytes);
fNumCurrentBuffers++;
theNewBuf->fQElem.SetEnclosingObject(theNewBuf);
fQueue.EnQueue(theNewBuf->GetQElem()); // put on tail
Assert(theNewBuf != NULL);
return theNewBuf;
}
OSQueueElem *theElem = fQueue.DeQueue(); // get head
Assert(theElem != NULL);
if (theElem == NULL)
return NULL;
theNewBuf = (FileBlockBuffer*) theElem->GetEnclosingObject();
Assert(theNewBuf != NULL);
//qtss_printf("FileBlockPool::GetBufferElement reuse buffer theNewBuf=%"_U32BITARG_" fDataBuffer=%"_U32BITARG_" fArrayIndex=%"_S32BITARG_"\n",theNewBuf,theNewBuf->fDataBuffer,theNewBuf->fArrayIndex);
return theNewBuf;
}
void FileBlockPool::DeleteBlockPool(void)
{
FileBlockBuffer *buffer = NULL;
OSQueueElem* theElem = fQueue.DeQueue();
while (theElem != NULL)
{ buffer = (FileBlockBuffer *) theElem->GetEnclosingObject();
delete buffer;
theElem = fQueue.DeQueue();
}
fMaxBuffers = 1;
fNumCurrentBuffers = 0;
fBufferUnitSizeBytes = kBufferUnitSize;
}
FileBlockPool::~FileBlockPool(void)
{
this->DeleteBlockPool();
}
void FileMap::AllocateBufferMap(UInt32 inUnitSizeInK, UInt32 inNumBuffSizeUnits, UInt32 inBufferIncCount, UInt32 inMaxBitRateBuffSizeInBlocks, UInt64 fileLen, UInt32 inBitRate)
{
if (fFileMapArray != NULL && fNumBuffSizeUnits == inNumBuffSizeUnits && inBufferIncCount == fBlockPool.GetMaxBuffers())
return;
if( inUnitSizeInK < 1 )
inUnitSizeInK = 1;
fBlockPool.SetBufferUnitSize(inUnitSizeInK);
if (inBitRate == 0) // just use the maximum possible size
inBitRate = inMaxBitRateBuffSizeInBlocks * fBlockPool.GetBufferUnitSizeBytes();
if (inNumBuffSizeUnits == 0) // calculate the buffer size ourselves
{
inNumBuffSizeUnits = inBitRate / fBlockPool.GetBufferUnitSizeBytes();
if( inNumBuffSizeUnits > inMaxBitRateBuffSizeInBlocks) // max is 8 * buffUnit Size (32k) = 256K
{ inNumBuffSizeUnits = inMaxBitRateBuffSizeInBlocks;
}
} //else the inNumBuffSizeUnits is explicitly defined so just use that value
if( inNumBuffSizeUnits < 1 )
inNumBuffSizeUnits = 1;
this->DeleteMap();
fBlockPool.DeleteBlockPool();
fNumBuffSizeUnits = inNumBuffSizeUnits;
fDataBufferSize = fBlockPool.GetBufferUnitSizeBytes() * inNumBuffSizeUnits;
fBlockPool.SetMaxBuffers(inBufferIncCount);
fBlockPool.SetBuffIncValue(inBufferIncCount);
fMapArraySize = (fileLen / fDataBufferSize) + 1;
fFileMapArray = NEW FileBlockBuffer *[ (SInt32) (fMapArraySize + 1) ];
this->Clean(); // required because fFileMapArray's array is used to store buffer pointers.
#if FILE_SOURCE_DEBUG
qtss_printf("FileMap::AllocateBufferMap shared buffers fFileMapArray=%"_U32BITARG_" fDataBufferSize= %"_U32BITARG_" fMapArraySize=%"_U32BITARG_" fileLen=%qu \n",fFileMapArray, fDataBufferSize, fMapArraySize,fileLen);
#endif
}
void FileMap::DeleteOldBuffs()
{
while (fBlockPool.GetNumCurrentBuffers() > fBlockPool.GetMaxBuffers()) // delete any old buffers
{
FileBlockBuffer *theElem = fBlockPool.GetBufferElement(fDataBufferSize);
fFileMapArray[theElem->fArrayIndex] = NULL;
delete theElem;
fBlockPool.DecCurBuffers();
}
}
char *FileMap::GetBuffer(SInt64 buffIndex, Bool16 *outFillBuff)
{
Assert(outFillBuff != NULL);
*outFillBuff = true; // we are re-using or just created a buff
this->DeleteOldBuffs();
Assert(buffIndex < (SInt32) fMapArraySize);
FileBlockBuffer *theElem = fFileMapArray[buffIndex];
if ( NULL == theElem)
{
#if FILE_SOURCE_DEBUG
qtss_printf("FileMap::GetBuffer call fBlockPool.GetBufferElement(); buffIndex=%"_S32BITARG_"\n",buffIndex);
#endif
theElem = fBlockPool.GetBufferElement(fDataBufferSize);
Assert(theElem);
}
fBlockPool.MarkUsed(theElem); // must happen here after getting a pre-allocated or used buffer.
if (theElem->fArrayIndex == buffIndex) // found a pre-allocated and filled buffer
{
#if FILE_SOURCE_DEBUG
//qtss_printf("FileMap::GetBuffer pre-allocated buff buffIndex=%"_S32BITARG_"\n",buffIndex);
#endif
*outFillBuff = false;
return theElem->fDataBuffer;
}
if (theElem->fArrayIndex >= 0)
{
fFileMapArray[theElem->fArrayIndex] = NULL; // reset the old map location
}
fFileMapArray[buffIndex] = theElem; // a new buffer
theElem->fArrayIndex = buffIndex; // record the index
#if FILE_SOURCE_DEBUG
theElem->CleanBuffer();
#endif
return theElem->fDataBuffer;
}
void FileMap::Clean(void)
{
if (fFileMapArray != NULL)
::memset( (char *)fFileMapArray,0, (SInt32) (sizeof(FileBlockBuffer *) * fMapArraySize) );
}
void FileMap::DeleteMap(void)
{
if (NULL == fFileMapArray)
return;
#if FILE_SOURCE_DEBUG
qtss_printf("FileMap::DeleteMap fFileMapArray=%"_U32BITARG_" fMapArraySize=%"_S32BITARG_" \n",fFileMapArray, fMapArraySize);
this->Clean();
#endif
delete fFileMapArray;
fFileMapArray = NULL;
}
void OSFileSource::Set(const char *inPath)
{
Close();
#if __Win32__
fFile = open(inPath, O_RDONLY | O_BINARY);
#elif __linux__
fFile = open(inPath, O_RDONLY | O_LARGEFILE);
#else
fFile = open(inPath, O_RDONLY);
#endif
if (fFile != -1)
{
struct stat buf;
::memset(&buf,sizeof(buf),0);
if (::fstat(fFile, &buf) >= 0)
{
fLength = buf.st_size;
fModDate = buf.st_mtime;
if (fModDate < 0)
fModDate = 0;
#ifdef __Win32__
fIsDir = buf.st_mode & _S_IFDIR;
#else
fIsDir = S_ISDIR(buf.st_mode);
#endif
this->SetLog(inPath);
}
else
this->Close();
}
}
void OSFileSource::Advise(UInt64 , UInt32 )
{
// does nothing on platforms other than MacOSXServer
}
OS_Error OSFileSource::FillBuffer(char* ioBuffer, char *buffStart, SInt32 buffIndex)
{
UInt32 buffSize = fFileMap.GetMaxBufSize();
UInt64 startPos = (UInt64) buffIndex * (UInt64) buffSize;
UInt32 readLen = 0;
OS_Error theErr = this->ReadFromPos(startPos, buffStart, buffSize, &readLen);
fFileMap.SetIndexBuffFillSize(buffIndex, readLen);
fFileMap.TestBuffer(buffIndex);
return theErr;
}
#if FILE_SOURCE_BUFFTEST
static SInt32 sBuffCount = 1;
#endif
OS_Error OSFileSource::Read(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen)
{
if ( ( !fFileMap.Initialized() )
|| ( !fCacheEnabled )
|| ( fFileMap.GetBuffIndex(inPosition+inLength) > fFileMap.GetMaxBuffIndex() )
)
return this->ReadFromPos(inPosition, inBuffer, inLength, outRcvLen);
return this->ReadFromCache(inPosition, inBuffer, inLength, outRcvLen);
}
OS_Error OSFileSource::ReadFromCache(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen)
{
OSMutexLocker locker(&fMutex);
if (!fFileMap.Initialized() || !fCacheEnabled)
{ Assert(0);
}
Assert(outRcvLen != NULL);
*outRcvLen = 0;
if (inPosition >= fLength) // eof
return OS_NoErr;
SInt64 buffIndex = fFileMap.GetBuffIndex(inPosition);
SInt64 buffSize = 0;
SInt64 maxBuffSize = fFileMap.GetMaxBufSize();
SInt64 endIndex = fFileMap.GetBuffIndex(inPosition+inLength);
SInt64 maxIndex = fFileMap.GetMaxBuffIndex();
SInt64 buffPos = inPosition - fFileMap.GetBuffOffset(buffIndex);
SInt64 buffOffsetLen = 0;
char *buffStart = NULL;
SInt64 buffCopyLen = inLength;
SInt64 bytesToCopy = inLength;
char *buffOut = (char*)inBuffer;
Bool16 fillBuff = true;
char *buffOffset = NULL;
#if FILE_SOURCE_BUFFTEST
char testBuff[inLength + 1];
buffOut = (char*)testBuff;
sBuffCount ++;
::memset(inBuffer,0,inLength);
::memset(testBuff,0,inLength);
#endif
if (buffIndex > endIndex || endIndex > maxIndex)
{
#if FILE_SOURCE_DEBUG
qtss_printf("OSFileSource::ReadFromCache bad index: buffIndex=%"_S32BITARG_" endIndex=%"_S32BITARG_" maxIndex=%"_S32BITARG_"\n",buffIndex,endIndex,maxIndex);
qtss_printf("OSFileSource::ReadFromCache inPosition =%qu buffSize = %"_U32BITARG_" index=%"_S32BITARG_"\n",inPosition, fFileMap.GetMaxBufSize(),buffIndex);
#endif
Assert(0);
}
while (buffIndex <= endIndex && buffIndex <= maxIndex)
{
#if FILE_SOURCE_DEBUG
qtss_printf("OSFileSource::ReadFromCache inPosition =%qu buffSize = %"_U32BITARG_" index=%"_S32BITARG_"\n",inPosition, fFileMap.GetMaxBufSize(),buffIndex);
#endif
buffStart = fFileMap.GetBuffer(buffIndex, &fillBuff);
Assert(buffStart != NULL);
if (fillBuff)
{
OS_Error theErr = this->FillBuffer( (char *) inBuffer, (char *) buffStart, (SInt32) buffIndex);
if (theErr != OS_NoErr)
return theErr;
}
buffSize = fFileMap.GetBuffSize(buffIndex);
buffOffset = &buffStart[buffPos];
if ( (buffPos == 0) &&
(bytesToCopy <= maxBuffSize) &&
(buffSize < bytesToCopy)
) // that's all there is in the file
{
#if FILE_SOURCE_DEBUG
qtss_printf("OSFileSource::ReadFromCache end of file reached buffIndex=%"_U32BITARG_" buffSize = %"_S32BITARG_" bytesToCopy=%"_U32BITARG_"\n",buffIndex, buffSize,bytesToCopy);
#endif
Assert(buffSize <= (SInt64) kUInt32_Max);
::memcpy(buffOut,buffOffset,(UInt32) buffSize);
*outRcvLen += (UInt32) buffSize;
break;
}
buffOffsetLen = buffSize - buffPos;
if (buffCopyLen >= buffOffsetLen)
buffCopyLen = buffOffsetLen;
Assert(buffCopyLen <= buffSize);
::memcpy(buffOut,buffOffset, (UInt32) buffCopyLen);
buffOut += buffCopyLen;
*outRcvLen += (UInt32) buffCopyLen;
bytesToCopy -= buffCopyLen;
Assert(bytesToCopy >= 0);
buffCopyLen = bytesToCopy;
buffPos = 0;
buffIndex ++;
}
#if FILE_SOURCE_DEBUG
//qtss_printf("OSFileSource::ReadFromCache inLength= %"_U32BITARG_" *outRcvLen=%"_U32BITARG_"\n",inLength, *outRcvLen);
#endif
#if FILE_SOURCE_BUFFTEST
{ UInt32 outLen = 0;
OS_Error theErr = this->ReadFromPos(inPosition, inBuffer, inLength, &outLen);
Assert(*outRcvLen == outLen);
if (*outRcvLen != outLen)
qtss_printf("OSFileSource::ReadFromCache *outRcvLen != outLen *outRcvLen=%"_U32BITARG_" outLen=%"_U32BITARG_"\n",*outRcvLen,outLen);
for (int i = 0; i < inLength; i++)
{ if ( ((char*)inBuffer)[i] != testBuff[i])
{ qtss_printf("OSFileSource::ReadFromCache byte pos %d of %"_U32BITARG_" failed len=%"_U32BITARG_" inPosition=%qu sBuffCount=%"_S32BITARG_"\n",i,inLength,outLen,inPosition,sBuffCount);
break;
}
}
}
#endif
return OS_NoErr;
}
OS_Error OSFileSource::ReadFromDisk(void* inBuffer, UInt32 inLength, UInt32* outRcvLen)
{
#if FILE_SOURCE_BUFFTEST
qtss_printf("OSFileSource::Read inLength=%"_U32BITARG_" fFile=%d\n",inLength,fFile);
#endif
#if __Win32__
if (_lseeki64(fFile, fPosition, SEEK_SET) == -1)
return OSThread::GetErrno();
#else
if (lseek(fFile, fPosition, SEEK_SET) == -1)
return OSThread::GetErrno();
#endif
int rcvLen = ::read(fFile, (char*)inBuffer, inLength);
if (rcvLen == -1)
return OSThread::GetErrno();
if (outRcvLen != NULL)
*outRcvLen = rcvLen;
fPosition += rcvLen;
fReadPos = fPosition;
return OS_NoErr;
}
OS_Error OSFileSource::ReadFromPos(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen)
{
#if TEST_TIME
{
startTime = OS::Milliseconds();
sReadCount++;
if (outRcvLen)
*outRcvLen = 0;
qtss_printf("OSFileSource::Read sReadCount = %"_S32BITARG_" totalbytes=%"_S32BITARG_" readsize=%"_U32BITARG_"\n",sReadCount,sByteCount,inLength);
}
#endif
this->Seek(inPosition);
OS_Error err = this->ReadFromDisk(inBuffer,inLength,outRcvLen);
#if READ_LOG
if (fFileLog)
{ fFileLog = ::fopen(fFilePath,"a");
if (fFileLog)
{ qtss_fprintf(fFileLog, "read: %qu %"_U32BITARG_" %"_U32BITARG_"\n",inPosition, *outRcvLen, xTrackID);
::fclose(fFileLog);
}
}
#endif
#if TEST_TIME
{
durationTime += OS::Milliseconds() - startTime;
sByteCount += *outRcvLen;
}
#endif
return err;
}
void OSFileSource::SetTrackID(UInt32 trackID)
{
#if READ_LOG
fTrackID = trackID;
// qtss_printf("OSFileSource::SetTrackID = %"_U32BITARG_" this=%"_U32BITARG_"\n",fTrackID,(UInt32) this);
#endif
}
void OSFileSource::Close()
{
if ((fFile != -1) && (fShouldClose))
{ ::close(fFile);
#if READ_LOG
if ( 0 && fFileLog != NULL )
{ ::fclose(fFileLog);
fFileLog = NULL;
fFilePath[0] =0;
}
#endif
}
fFile = -1;
fModDate = 0;
fLength = 0;
fPosition = 0;
fReadPos = 0;
#if TEST_TIME
if (fShouldClose)
{ sMovie = 0;
// qtss_printf("OSFileSource::Close sReadCount = %"_S32BITARG_" totalbytes=%"_S32BITARG_"\n",sReadCount,sByteCount);
// qtss_printf("OSFileSource::Close durationTime = %qd\n",durationTime);
}
#endif
}