Files
Rocky5 921f9fe9f8 Clean Copy
If ever adding the XBMC4Xbox source to GitHub use "git add -f -A" or it wont add all the files.
2024-01-08 19:01:29 +00:00

204 lines
4.4 KiB
C++

/*
* Copyright (C) 2005-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#include "utils/log.h"
#include "utils/SingleLock.h"
#include "utils/TimeUtils.h"
#include "CircularCache.h"
using namespace XFILE;
CCircularCache::CCircularCache(size_t front, size_t back)
: CCacheStrategy()
, m_beg(0)
, m_end(0)
, m_cur(0)
, m_buf(NULL)
, m_size(front + back)
, m_size_back(back)
{
}
CCircularCache::~CCircularCache()
{
Close();
}
int CCircularCache::Open()
{
m_buf = new uint8_t[m_size];
if(m_buf == 0)
return CACHE_RC_ERROR;
m_beg = 0;
m_end = 0;
m_cur = 0;
return CACHE_RC_OK;
}
void CCircularCache::Close()
{
delete[] m_buf;
m_buf = NULL;
}
/**
* Function will write to m_buf at m_end % m_size location
* it will write at maximum m_size, but it will only write
* as much it can without wrapping around in the buffer
*
* It will always leave m_size_back of the backbuffer intact
* but if the back buffer is less than that, that space is
* usable to write.
*
* If back buffer is filled to an larger extent than
* m_size_back, it will allow it to be overwritten
* until only m_size_back data remains.
*
* The following always apply:
* * m_end <= m_cur <= m_end
* * m_end - m_beg <= m_size
*
* Multiple calls may be needed to fill buffer completely.
*/
int CCircularCache::WriteToCache(const char *buf, size_t len)
{
CSingleLock lock(m_sync);
// where are we in the buffer
size_t pos = m_end % m_size;
size_t back = (size_t)(m_cur - m_beg);
size_t front = (size_t)(m_end - m_cur);
size_t limit = m_size - std::min(back, m_size_back) - front;
size_t wrap = m_size - pos;
// limit by max forward size
if(len > limit)
len = limit;
// limit to wrap point
if(len > wrap)
len = wrap;
if(len == 0)
return 0;
// write the data
memcpy(m_buf + pos, buf, len);
m_end += len;
// drop history that was overwritten
if(m_end - m_beg > (int64_t)m_size)
m_beg = m_end - m_size;
m_written.Set();
return len;
}
/**
* Reads data from cache. Will only read up till
* the buffer wrap point. So multiple calls
* may be needed to empty the whole cache
*/
int CCircularCache::ReadFromCache(char *buf, size_t len)
{
CSingleLock lock(m_sync);
size_t pos = m_cur % m_size;
size_t front = (size_t)(m_end - m_cur);
size_t avail = std::min(m_size - pos, front);
if(avail == 0)
{
if(IsEndOfInput())
return 0;
else
return CACHE_RC_WOULD_BLOCK;
}
if(len > avail)
len = avail;
if(len == 0)
return 0;
memcpy(buf, m_buf + pos, len);
m_cur += len;
m_space.Set();
return len;
}
int64_t CCircularCache::WaitForData(unsigned int minumum, unsigned int millis)
{
CSingleLock lock(m_sync);
uint64_t avail = m_end - m_cur;
if(millis == 0 || IsEndOfInput())
return avail;
if(minumum > m_size - m_size_back)
minumum = m_size - m_size_back;
unsigned int time = CTimeUtils::GetTimeMS() + millis;
while (!IsEndOfInput() && avail < minumum && CTimeUtils::GetTimeMS() < time )
{
lock.Leave();
m_written.WaitMSec(50); // may miss the deadline. shouldn't be a problem.
lock.Enter();
avail = m_end - m_cur;
}
return avail;
}
int64_t CCircularCache::Seek(int64_t pos)
{
CSingleLock lock(m_sync);
// if seek is a bit over what we have, try to wait a few seconds for the data to be available.
// we try to avoid a (heavy) seek on the source
if ((uint64_t)pos >= m_end && (uint64_t)pos < m_end + 100000)
{
lock.Leave();
WaitForData((size_t)(pos - m_cur), 5000);
lock.Enter();
}
if((uint64_t)pos >= m_beg && (uint64_t)pos <= m_end)
{
m_cur = pos;
return pos;
}
return CACHE_RC_ERROR;
}
void CCircularCache::Reset(int64_t pos)
{
CSingleLock lock(m_sync);
m_end = pos;
m_beg = pos;
m_cur = pos;
}