diff options
Diffstat (limited to 'libs/appleutility/CoreAudio/AudioFile/AFPublic/DataSource.cpp')
-rw-r--r-- | libs/appleutility/CoreAudio/AudioFile/AFPublic/DataSource.cpp | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/libs/appleutility/CoreAudio/AudioFile/AFPublic/DataSource.cpp b/libs/appleutility/CoreAudio/AudioFile/AFPublic/DataSource.cpp new file mode 100644 index 0000000000..c8a93d2756 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioFile/AFPublic/DataSource.cpp @@ -0,0 +1,689 @@ +/* + File: DataSource.cpp + Abstract: DataSource.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "DataSource.h" +#if TARGET_OS_WIN32 + #include <io.h> +#else + #include <unistd.h> + #include <fcntl.h> +#endif +#include <sys/stat.h> +#include <algorithm> + +#define VERBOSE 0 + +////////////////////////////////////////////////////////////////////////////////////////// + + +const UInt16 kPositionModeMask = 3; + +DataSource::DataSource(Boolean inCloseOnDelete) + : mCloseOnDelete(inCloseOnDelete) +{ +} + +DataSource::~DataSource() +{ +} + +SInt64 DataSource::CalcOffset( UInt16 positionMode, + SInt64 positionOffset, + SInt64 currentOffset, + SInt64 size) +{ + SInt64 newOffset = 0; + switch (positionMode & kPositionModeMask) { + //case fsAtMark : newOffset = currentOffset; break; + case SEEK_SET : newOffset = positionOffset; break; + case SEEK_END : newOffset = size + positionOffset; break; + case SEEK_CUR : newOffset = positionOffset + currentOffset; break; + } + return newOffset; +} + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +#if 0 + +MacFile_DataSource::MacFile_DataSource( FSIORefNum inForkRefNum, SInt8 inPermissions, Boolean inCloseOnDelete) + : DataSource(inCloseOnDelete), mFileNum(inForkRefNum), mPermissions(inPermissions) +{ +} + +MacFile_DataSource::~MacFile_DataSource() +{ + if (mCloseOnDelete) FSCloseFork(mFileNum); +} + + +OSStatus MacFile_DataSource::GetSize(SInt64& outSize) +{ + outSize = -1; // in case of error + OSStatus err = FSGetForkSize(mFileNum, &outSize); + return err; +} + +OSStatus MacFile_DataSource::GetPos(SInt64& outPos) const +{ + return FSGetForkPosition(mFileNum, &outPos); +} + +OSStatus MacFile_DataSource::SetSize(SInt64 inSize) +{ + return FSSetForkSize(mFileNum, SEEK_SET, inSize); +} + + +OSStatus MacFile_DataSource::ReadBytes( + UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + void *buffer, + UInt32* actualCount) +{ + if (actualCount) *actualCount = 0; + if (!buffer) return kAudio_ParamError; + ByteCount bc_actualCount = 0; + OSStatus err = FSReadFork(mFileNum, positionMode, positionOffset, requestCount, buffer, &bc_actualCount); + if (actualCount) *actualCount = (UInt32)bc_actualCount; + return err; +} + +OSStatus MacFile_DataSource::WriteBytes( + UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + const void *buffer, + UInt32* actualCount) +{ + if (!buffer) return kAudio_ParamError; + ByteCount bc_actualCount = 0; + OSStatus err = FSWriteFork(mFileNum, positionMode, positionOffset, requestCount, buffer, &bc_actualCount); + if (actualCount) *actualCount = (UInt32)bc_actualCount; + return err; +} +#endif + +////////////////////////////////////////////////////////////////////////////////////////// + +#define kAudioFileNoCacheMask 0x20 + +UnixFile_DataSource::UnixFile_DataSource( int inFD, SInt8 inPermissions, Boolean inCloseOnDelete) + : DataSource(inCloseOnDelete), mFileD(inFD), mPermissions(inPermissions), mNoCache(0), mCachedSize(-1), mFilePointer(0) +{ +} + +UnixFile_DataSource::~UnixFile_DataSource() +{ + if (mCloseOnDelete) close(mFileD); +} + +OSStatus UnixFile_DataSource::GetSize(SInt64& outSize) +{ +#if 0 // 6764274 using the cached file size causes a regression for apps that play a file while writing to it. + if (mCachedSize >= 0) { + outSize = mCachedSize; + return noErr; + } +#endif + outSize = -1; // in case of error + struct stat stbuf; + if (fstat (mFileD, &stbuf) == -1) return kAudio_FileNotFoundError; + outSize = mCachedSize = stbuf.st_size; + return noErr; +} + +OSStatus UnixFile_DataSource::SetSize(SInt64 inSize) +{ + mCachedSize = -1; +#if TARGET_OS_WIN32 + if (chsize (mFileD, inSize)) return kAudioFilePermissionsError; +#else + if (ftruncate (mFileD, inSize) == -1) return kAudioFilePermissionsError; +#endif + mCachedSize = inSize; + return noErr; +} + + +OSStatus UnixFile_DataSource::GetPos(SInt64& outPos) const +{ + outPos = mFilePointer; + return noErr; +} + +SInt64 UnixFile_DataSource::UFCurrentOffset (UInt16 positionMode, + SInt64 positionOffset) +{ + SInt64 offset = -1; + switch (positionMode & kPositionModeMask) + { + /*case fsAtMark : + { + SInt64 pos; + OSStatus result = GetPos (pos); + if (result) return result; + offset = pos; + break; + }*/ + case SEEK_SET : + { + offset = positionOffset; + break; + } + case SEEK_END : + { + SInt64 size; + OSStatus result = GetSize (size); + if (result) return result; + offset = size + positionOffset; + break; + } + case SEEK_CUR : + { + SInt64 pos; + OSStatus result = GetPos (pos); + if (result) return result; + offset = positionOffset + pos; + break; + } + } + return offset; +} + +OSStatus UnixFile_DataSource::ReadBytes( UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + void *buffer, + UInt32* actualCount) +{ + if (actualCount) *actualCount = 0; + if (!buffer) return kAudio_ParamError; + + // can't use current offset as we need to go to the disk too much + SInt64 offset = UFCurrentOffset (positionMode, positionOffset); + if (offset < 0) return kAudioFilePositionError; + +#if 0 // 6571050 fstat-ing the file every read causes a performance regression + // 5931571 check that read may exceed eof and curtail it. + do { + SInt64 size; + OSStatus serr = GetSize(size); + if (serr) break; + SInt64 remain = size - offset; + if (remain < 0) requestCount = 0; + else if (requestCount > remain) requestCount = remain; + } while (false); +#endif + + if (requestCount <= 0) { + if (actualCount) *actualCount = 0; + return noErr; + } + +#if !TARGET_OS_WIN32 + UInt32 noCache = positionMode & kAudioFileNoCacheMask ? 1 : 0; + if (noCache != mNoCache) { + mNoCache = noCache; + fcntl(mFileD, F_NOCACHE, mNoCache); + } +#endif + + size_t readBytes = requestCount; +#if TARGET_OS_WIN32 + lseek(mFileD, offset, SEEK_SET); + int numBytes = read (mFileD, buffer, readBytes); +#else + ssize_t numBytes = pread (mFileD, buffer, readBytes, offset); +#endif + if (numBytes == -1) return kAudioFilePositionError; + mFilePointer = offset + numBytes; + + if (actualCount) *actualCount = (UInt32)numBytes; + return noErr; +} + +OSStatus UnixFile_DataSource::WriteBytes(UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + const void *buffer, + UInt32* actualCount) +{ + if (!buffer) return kAudio_ParamError; + + // can't use current offset as we need to go to the disk too much + SInt64 offset = UFCurrentOffset (positionMode, positionOffset); + if (offset < 0) return kAudioFilePositionError; + + mCachedSize = -1; + + size_t writeBytes = requestCount; + +#if !TARGET_OS_WIN32 + UInt32 noCache = positionMode & kAudioFileNoCacheMask ? 1 : 0; + if (noCache != mNoCache) { + mNoCache = noCache; + fcntl(mFileD, F_NOCACHE, mNoCache); + } +#endif + +#if TARGET_OS_WIN32 + lseek(mFileD, offset, SEEK_SET); + int numBytes = write (mFileD, buffer, writeBytes); +#else + ssize_t numBytes = pwrite (mFileD, buffer, writeBytes, offset); +#endif + if (numBytes == -1) return kAudioFilePositionError; + mFilePointer = offset + numBytes; + + if (actualCount) *actualCount = (UInt32)numBytes; + return noErr; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +#define NO_CACHE 0 + +OSStatus Cached_DataSource::ReadFromHeaderCache( + SInt64 offset, + UInt32 requestCount, + void *buffer, + UInt32* actualCount) +{ + if (actualCount) *actualCount = 0; + OSStatus err = noErr; + ByteCount theActualCount = 0; + +#if VERBOSE + printf("read from header %lld %lu %lld %lu\n", offset, requestCount, 0LL, mHeaderCacheSize); +#endif + + if (!mHeaderCache()) + { + mHeaderCache.allocBytes(mHeaderCacheSize, true); + err = mDataSource->ReadBytes(SEEK_SET, 0, mHeaderCacheSize, mHeaderCache(), &mHeaderCacheSize); + if (err == kAudioFileEndOfFileError) err = noErr; + if (err) return err; + } + + ByteCount firstPart = std::min((ByteCount)requestCount, (ByteCount)(mHeaderCacheSize - offset)); + ByteCount secondPart = requestCount - firstPart; + + memcpy(buffer, mHeaderCache + (ByteCount)offset, firstPart); + theActualCount = firstPart; + + if (secondPart) { + UInt32 secondPartActualCount = 0; + err = mDataSource->ReadBytes(SEEK_SET, mHeaderCacheSize, static_cast<UInt32>(secondPart), (char*)buffer + firstPart, &secondPartActualCount); + theActualCount += secondPartActualCount; + } + + if (actualCount) *actualCount = (UInt32)theActualCount; + mOffset = offset + theActualCount; + + return err; +} + +OSStatus Cached_DataSource::ReadBytes( + UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + void *buffer, + UInt32* actualCount) +{ + if (actualCount) *actualCount = 0; + OSStatus err = noErr; + SInt64 size; + UInt32 theActualCount = 0; + + if (!buffer) return kAudio_ParamError; + + if ((positionMode & kPositionModeMask) != SEEK_END) size = 0; // not used in this case + else + { + err = GetSize(size); + if (err) return err; + } + + SInt64 offset = CalcOffset(positionMode, positionOffset, mOffset, size); + if (offset < 0) return kAudioFilePositionError; + + if (offset < mHeaderCacheSize) { + return ReadFromHeaderCache(offset, requestCount, buffer, actualCount); + } + +#if NO_CACHE + err = mDataSource->ReadBytes(positionMode, positionOffset, requestCount, buffer, &theActualCount); + mOffset = offset + theActualCount; +#else + + SInt64 cacheEnd = mBodyCacheOffset + mBodyCacheCurSize; + if (mBodyCache() && requestCount < mBodyCacheSize && offset >= mBodyCacheOffset && offset < cacheEnd) + { + if (offset + requestCount <= cacheEnd) + { + // request is entirely within cache +#if VERBOSE + printf("request is entirely within cache %lld %lu %lld %lu\n", offset, requestCount, mBodyCacheOffset, mBodyCacheCurSize); +#endif + memcpy(buffer, mBodyCache + (size_t)(offset - mBodyCacheOffset), requestCount); + theActualCount = requestCount; + } + else + { + // part of request is within cache. copy, read next cache block, copy. +#if VERBOSE + printf("part of request is within cache %lld %lu %lld %lu\n", offset, requestCount, mBodyCacheOffset, mBodyCacheCurSize); +#endif + + // copy first part. + ByteCount firstPart = (ByteCount)(cacheEnd - offset); + ByteCount secondPart = requestCount - firstPart; +#if VERBOSE + printf("memcpy offset %lld mBodyCacheOffset %lld offset - mBodyCacheOffset %lld firstPart %lu requestCount %lu\n", + offset, mBodyCacheOffset, offset - mBodyCacheOffset, firstPart, requestCount); +#endif + memcpy(buffer, mBodyCache + (size_t)(offset - mBodyCacheOffset), firstPart); + + theActualCount = static_cast<UInt32>(firstPart); + + // read new block + SInt64 nextOffset = mBodyCacheOffset + mBodyCacheCurSize; + err = mDataSource->ReadBytes(SEEK_SET, nextOffset, mBodyCacheSize, mBodyCache(), &mBodyCacheCurSize); + + if (err == kAudioFileEndOfFileError) err = noErr; + if (err) goto leave; + + mBodyCacheOffset = nextOffset; + + // copy second part + secondPart = std::min(secondPart, (ByteCount)mBodyCacheCurSize); + if (secondPart) memcpy((char*)buffer + firstPart, mBodyCache(), secondPart); + theActualCount = static_cast<UInt32>(firstPart + secondPart); + } + } + else + { + if (requestCount > mBodyCacheSize) + { +#if VERBOSE + printf("large request %lld %lu %lld %lu\n", offset, requestCount, mBodyCacheOffset, mBodyCacheCurSize); +#endif + // the request is larger than we normally cache, just do a read and don't cache. + err = mDataSource->ReadBytes(positionMode, positionOffset, requestCount, buffer, &theActualCount); + mOffset = offset + theActualCount; + } + else + { + // request is outside cache. read new block. +#if VERBOSE + printf("request is outside cache %lld %lu %lld %lu\n", offset, requestCount, mBodyCacheOffset, mBodyCacheCurSize); +#endif + if (!mBodyCache()) + { + mBodyCache.allocBytes(mBodyCacheSize, true); +#if VERBOSE + printf("alloc mBodyCache %08X\n", mBodyCache()); +#endif + } + mBodyCacheOffset = offset; + err = mDataSource->ReadBytes(SEEK_SET, mBodyCacheOffset, mBodyCacheSize, mBodyCache(), &mBodyCacheCurSize); +#if VERBOSE + printf("read %08X %d mBodyCacheOffset %lld %lu %lu\n", err, err, mBodyCacheOffset, mBodyCacheSize, mBodyCacheCurSize); +#endif + if (err == kAudioFileEndOfFileError) err = noErr; + if (err) return err; + + theActualCount = std::min(requestCount, mBodyCacheCurSize); + memcpy(buffer, mBodyCache(), theActualCount); + } + + } + +leave: +#endif + if (actualCount) *actualCount = (UInt32)theActualCount; +#if VERBOSE + printf("<<read err %d actualCount %lu\n", err, *actualCount); +#endif + return err; +} + +OSStatus Cached_DataSource::WriteBytes( + UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + const void *buffer, + UInt32* actualCount) +{ + OSStatus err = noErr; + SInt64 size; + + if (!buffer) return kAudio_ParamError; + + if ((positionMode & kPositionModeMask) != SEEK_END) size = 0; // not used in this case + else + { + err = GetSize(size); + if (err) return err; + } + + SInt64 offset = CalcOffset(positionMode, positionOffset, mOffset, size); + if (offset < 0) return kAudioFilePositionError; + + if (mHeaderCache() && offset < mHeaderCacheSize) + { + // header cache write through + ByteCount firstPart = std::min((ByteCount)requestCount, (ByteCount)(mHeaderCacheSize - offset)); +#if VERBOSE + printf("header cache write through %lu %lu\n", mHeaderCacheSize, firstPart); +#endif + memcpy(mHeaderCache + (size_t)offset, buffer, firstPart); + } + +#if VERBOSE + printf("write %lld %lu %lld %d %lld\n", offset, requestCount, mOffset, positionMode, positionOffset); +#endif + + SInt64 cacheEnd = mBodyCacheOffset + mBodyCacheCurSize; + if (mBodyCache() && offset >= mBodyCacheOffset && offset < cacheEnd) + { + // body cache write through + ByteCount firstPart = std::min((SInt64)requestCount, cacheEnd - offset); +#if VERBOSE + printf("body cache write through %lld %lu %lld %lu\n", mBodyCacheOffset, mBodyCacheCurSize, offset, firstPart); +#endif + memcpy(mBodyCache + (offset - mBodyCacheOffset), buffer, firstPart); + } + + UInt32 theActualCount; + err = mDataSource->WriteBytes(positionMode, positionOffset, requestCount, buffer, &theActualCount); + + mOffset = offset + theActualCount; + if (actualCount) *actualCount = (UInt32)theActualCount; + + return err; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +Seekable_DataSource::Seekable_DataSource( void * inClientData, + AudioFile_ReadProc inReadFunc, + AudioFile_WriteProc inWriteFunc, + AudioFile_GetSizeProc inGetSizeFunc, + AudioFile_SetSizeProc inSetSizeFunc) + : DataSource(false), mClientData(inClientData), mReadFunc(inReadFunc), mWriteFunc(inWriteFunc), + mSizeFunc(inGetSizeFunc), mSetSizeFunc(inSetSizeFunc) +{ +} + +Seekable_DataSource::~Seekable_DataSource() +{ +} + + +OSStatus Seekable_DataSource::GetSize(SInt64& outSize) +{ + if (!mSizeFunc) { + outSize = LLONG_MAX; + } else { + outSize = (*mSizeFunc)(mClientData); + } + return noErr; +} + +OSStatus Seekable_DataSource::SetSize(SInt64 inSize) +{ + if (!mSetSizeFunc) return kAudioFileOperationNotSupportedError; + return (*mSetSizeFunc)(mClientData, inSize); +} + +OSStatus Seekable_DataSource::ReadBytes( + UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + void *buffer, + UInt32* actualCount) +{ + OSStatus err; + + if (actualCount) *actualCount = 0; + if (!mReadFunc) return kAudioFileOperationNotSupportedError; + if (!buffer) return kAudio_ParamError; + + positionMode &= kPositionModeMask; + + SInt64 size; + err = GetSize(size); + if (err) return err; + + SInt64 offset = CalcOffset(positionMode, positionOffset, mOffset, size); + + // request is outside bounds of file + if (offset < 0) + return kAudioFilePositionError; + if (offset >= size) + return kAudioFileEndOfFileError; + + // reduce request if it exceeds the amount available + requestCount = static_cast<UInt32>(std::min((SInt64)requestCount, size - offset)); + + UInt32 theActualCount = 0; + err = (*mReadFunc)(mClientData, offset, requestCount, buffer, &theActualCount); + if (actualCount) *actualCount = theActualCount; + mOffset = offset + theActualCount; + return err; +} + + +OSStatus Seekable_DataSource::WriteBytes( + UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + const void *buffer, + UInt32* actualCount) +{ + OSStatus err; + + if (!mWriteFunc) return kAudioFileOperationNotSupportedError; + if (!buffer) return kAudio_ParamError; + + SInt64 size; + positionMode &= kPositionModeMask; + if (positionMode != SEEK_END) size = 0; // not used in this case + else + { + err = GetSize(size); + if (err) return err; + } + + SInt64 offset = CalcOffset(positionMode, positionOffset, mOffset, size); + if (offset < 0) return kAudioFilePositionError; + + UInt32 theActualCount; + err = (*mWriteFunc)(mClientData, offset, requestCount, buffer, &theActualCount); + if (err) return err; + if (actualCount) *actualCount = theActualCount; + mOffset = offset + theActualCount; + return noErr; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +OSStatus Buffer_DataSource::ReadBytes( + UInt16 positionMode, + SInt64 positionOffset, + UInt32 requestCount, + void *buffer, + UInt32* actualCount) +{ + if (actualCount) *actualCount = 0; + SInt64 offsetWithinBuffer = CalcOffset(positionMode, positionOffset, mOffset, mDataByteSize + mStartOffset) - mStartOffset; + if (offsetWithinBuffer < 0 || offsetWithinBuffer >= mDataByteSize) return kAudioFilePositionError; + + SInt64 bytesAfterOffset = mDataByteSize - offsetWithinBuffer; + SInt64 theActualCount = std::min(bytesAfterOffset, (SInt64)requestCount); + + if (theActualCount <= 0) { + if (actualCount) *actualCount = 0; + return kAudio_ParamError; + } + + memcpy(buffer, mData + offsetWithinBuffer, theActualCount); + + if (actualCount) *actualCount = static_cast<UInt32>(theActualCount); + mOffset = offsetWithinBuffer + theActualCount; + + return noErr; +} + + + +////////////////////////////////////////////////////////////////////////////////////////// |