Merge pull request #4857 from t-nelson/Gotham_13.2_backports
[vuplus_xbmc] / xbmc / filesystem / CacheStrategy.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "threads/SystemClock.h"
22 #include "CacheStrategy.h"
23 #ifdef TARGET_POSIX
24 #include "PlatformInclude.h"
25 #endif
26 #include "Util.h"
27 #include "utils/log.h"
28 #include "threads/SingleLock.h"
29 #include "utils/TimeUtils.h"
30 #include "SpecialProtocol.h"
31 #ifdef TARGET_WINDOWS
32 #include "PlatformDefs.h" //for PRIdS, PRId64
33 #endif
34
35 using namespace XFILE;
36
37 CCacheStrategy::CCacheStrategy() : m_bEndOfInput(false)
38 {
39 }
40
41
42 CCacheStrategy::~CCacheStrategy()
43 {
44 }
45
46 void CCacheStrategy::EndOfInput() {
47   m_bEndOfInput = true;
48 }
49
50 bool CCacheStrategy::IsEndOfInput()
51 {
52   return m_bEndOfInput;
53 }
54
55 void CCacheStrategy::ClearEndOfInput()
56 {
57   m_bEndOfInput = false;
58 }
59
60 CSimpleFileCache::CSimpleFileCache()
61   : m_hCacheFileRead(NULL)
62   , m_hCacheFileWrite(NULL)
63   , m_hDataAvailEvent(NULL)
64   , m_nStartPosition(0)
65   , m_nWritePosition(0)
66   , m_nReadPosition(0) {
67 }
68
69 CSimpleFileCache::~CSimpleFileCache()
70 {
71   Close();
72 }
73
74 int CSimpleFileCache::Open()
75 {
76   Close();
77
78   m_hDataAvailEvent = new CEvent;
79
80   CStdString fileName = CSpecialProtocol::TranslatePath(CUtil::GetNextFilename("special://temp/filecache%03d.cache", 999));
81   if(fileName.empty())
82   {
83     CLog::Log(LOGERROR, "%s - Unable to generate a new filename", __FUNCTION__);
84     Close();
85     return CACHE_RC_ERROR;
86   }
87
88   m_hCacheFileWrite = CreateFile(fileName.c_str()
89             , GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE
90             , NULL
91             , CREATE_ALWAYS
92             , FILE_ATTRIBUTE_NORMAL
93             , NULL);
94
95   if(m_hCacheFileWrite == INVALID_HANDLE_VALUE)
96   {
97     CLog::Log(LOGERROR, "%s - failed to create file %s with error code %d", __FUNCTION__, fileName.c_str(), GetLastError());
98     Close();
99     return CACHE_RC_ERROR;
100   }
101
102   m_hCacheFileRead = CreateFile(fileName.c_str()
103             , GENERIC_READ, FILE_SHARE_WRITE
104             , NULL
105             , OPEN_EXISTING
106             , FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE
107             , NULL);
108
109   if(m_hCacheFileRead == INVALID_HANDLE_VALUE)
110   {
111     CLog::Log(LOGERROR, "%s - failed to open file %s with error code %d", __FUNCTION__, fileName.c_str(), GetLastError());
112     Close();
113     return CACHE_RC_ERROR;
114   }
115
116   return CACHE_RC_OK;
117 }
118
119 void CSimpleFileCache::Close()
120 {
121   if (m_hDataAvailEvent)
122     delete m_hDataAvailEvent;
123
124   m_hDataAvailEvent = NULL;
125
126   if (m_hCacheFileWrite)
127     CloseHandle(m_hCacheFileWrite);
128
129   m_hCacheFileWrite = NULL;
130
131   if (m_hCacheFileRead)
132     CloseHandle(m_hCacheFileRead);
133
134   m_hCacheFileRead = NULL;
135 }
136
137 int CSimpleFileCache::WriteToCache(const char *pBuffer, size_t iSize)
138 {
139   DWORD iWritten=0;
140   if (!WriteFile(m_hCacheFileWrite, pBuffer, iSize, &iWritten, NULL))
141   {
142     CLog::Log(LOGERROR, "%s - failed to write to file. err: %u",
143                           __FUNCTION__, GetLastError());
144     return CACHE_RC_ERROR;
145   }
146
147   m_nWritePosition += iWritten;
148
149   // when reader waits for data it will wait on the event.
150   m_hDataAvailEvent->Set();
151
152   return iWritten;
153 }
154
155 int64_t CSimpleFileCache::GetAvailableRead()
156 {
157   return m_nWritePosition - m_nReadPosition;
158 }
159
160 int CSimpleFileCache::ReadFromCache(char *pBuffer, size_t iMaxSize)
161 {
162   int64_t iAvailable = GetAvailableRead();
163   if ( iAvailable <= 0 ) {
164     return m_bEndOfInput? 0 : CACHE_RC_WOULD_BLOCK;
165   }
166
167   if (iMaxSize > (size_t)iAvailable)
168     iMaxSize = (size_t)iAvailable;
169
170   DWORD iRead = 0;
171   if (!ReadFile(m_hCacheFileRead, pBuffer, iMaxSize, &iRead, NULL)) {
172     CLog::Log(LOGERROR,"CSimpleFileCache::ReadFromCache - failed to read %"PRIdS" bytes.", iMaxSize);
173     return CACHE_RC_ERROR;
174   }
175   m_nReadPosition += iRead;
176
177   if (iRead > 0)
178     m_space.Set();
179
180   return iRead;
181 }
182
183 int64_t CSimpleFileCache::WaitForData(unsigned int iMinAvail, unsigned int iMillis)
184 {
185   if( iMillis == 0 || IsEndOfInput() )
186     return GetAvailableRead();
187
188   XbmcThreads::EndTime endTime(iMillis);
189   while (!IsEndOfInput())
190   {
191     int64_t iAvail = GetAvailableRead();
192     if (iAvail >= iMinAvail)
193       return iAvail;
194
195     if (!m_hDataAvailEvent->WaitMSec(endTime.MillisLeft()))
196       return CACHE_RC_TIMEOUT;
197   }
198   return GetAvailableRead();
199 }
200
201 int64_t CSimpleFileCache::Seek(int64_t iFilePosition)
202 {
203   int64_t iTarget = iFilePosition - m_nStartPosition;
204
205   if (iTarget < 0)
206   {
207     CLog::Log(LOGDEBUG,"CSimpleFileCache::Seek, request seek before start of cache.");
208     return CACHE_RC_ERROR;
209   }
210
211   int64_t nDiff = iTarget - m_nWritePosition;
212   if (nDiff > 500000 || (nDiff > 0 && WaitForData((unsigned int)(iTarget - m_nReadPosition), 5000) == CACHE_RC_TIMEOUT))
213   {
214     CLog::Log(LOGDEBUG,"CSimpleFileCache::Seek - Attempt to seek past read data");
215     return CACHE_RC_ERROR;
216   }
217
218   LARGE_INTEGER pos;
219   pos.QuadPart = iTarget;
220
221   if(!SetFilePointerEx(m_hCacheFileRead, pos, NULL, FILE_BEGIN))
222     return CACHE_RC_ERROR;
223
224   m_nReadPosition = iTarget;
225   m_space.Set();
226
227   return iFilePosition;
228 }
229
230 void CSimpleFileCache::Reset(int64_t iSourcePosition, bool clearAnyway)
231 {
232   LARGE_INTEGER pos;
233   if (!clearAnyway && IsCachedPosition(iSourcePosition))
234   {
235     pos.QuadPart = m_nReadPosition = iSourcePosition - m_nStartPosition;
236     SetFilePointerEx(m_hCacheFileRead, pos, NULL, FILE_BEGIN);
237     return;
238   }
239
240   pos.QuadPart = 0;
241
242   SetFilePointerEx(m_hCacheFileWrite, pos, NULL, FILE_BEGIN);
243   SetFilePointerEx(m_hCacheFileRead, pos, NULL, FILE_BEGIN);
244   m_nStartPosition = iSourcePosition;
245   m_nReadPosition = 0;
246   m_nWritePosition = 0;
247 }
248
249 void CSimpleFileCache::EndOfInput()
250 {
251   CCacheStrategy::EndOfInput();
252   m_hDataAvailEvent->Set();
253 }
254
255 int64_t CSimpleFileCache::CachedDataEndPosIfSeekTo(int64_t iFilePosition)
256 {
257   if (iFilePosition >= m_nStartPosition && iFilePosition <= m_nStartPosition + m_nWritePosition)
258     return m_nStartPosition + m_nWritePosition;
259   return iFilePosition;
260 }
261
262 int64_t CSimpleFileCache::CachedDataEndPos()
263 {
264   return m_nStartPosition + m_nWritePosition;
265 }
266
267 bool CSimpleFileCache::IsCachedPosition(int64_t iFilePosition)
268 {
269   return iFilePosition >= m_nStartPosition && iFilePosition <= m_nStartPosition + m_nWritePosition;
270 }
271
272 CCacheStrategy *CSimpleFileCache::CreateNew()
273 {
274   return new CSimpleFileCache();
275 }
276
277
278 CSimpleDoubleCache::CSimpleDoubleCache(CCacheStrategy *impl)
279 {
280   assert(NULL != impl);
281   m_pCache = impl;
282   m_pCacheOld = NULL;
283 }
284
285 CSimpleDoubleCache::~CSimpleDoubleCache()
286 {
287   delete m_pCache;
288   delete m_pCacheOld;
289 }
290
291 int CSimpleDoubleCache::Open()
292 {
293   return m_pCache->Open();
294 }
295
296 void CSimpleDoubleCache::Close()
297 {
298   m_pCache->Close();
299   if (m_pCacheOld)
300   {
301     delete m_pCacheOld;
302     m_pCacheOld = NULL;
303   }
304 }
305
306 int CSimpleDoubleCache::WriteToCache(const char *pBuffer, size_t iSize)
307 {
308   return m_pCache->WriteToCache(pBuffer, iSize);
309 }
310
311 int CSimpleDoubleCache::ReadFromCache(char *pBuffer, size_t iMaxSize)
312 {
313   return m_pCache->ReadFromCache(pBuffer, iMaxSize);
314 }
315
316 int64_t CSimpleDoubleCache::WaitForData(unsigned int iMinAvail, unsigned int iMillis)
317 {
318   return m_pCache->WaitForData(iMinAvail, iMillis);
319 }
320
321 int64_t CSimpleDoubleCache::Seek(int64_t iFilePosition)
322 {
323   return m_pCache->Seek(iFilePosition);
324 }
325
326 void CSimpleDoubleCache::Reset(int64_t iSourcePosition, bool clearAnyway)
327 {
328   if (!clearAnyway && m_pCache->IsCachedPosition(iSourcePosition)
329       && (!m_pCacheOld || !m_pCacheOld->IsCachedPosition(iSourcePosition)
330           || m_pCache->CachedDataEndPos() >= m_pCacheOld->CachedDataEndPos()))
331   {
332     m_pCache->Reset(iSourcePosition, clearAnyway);
333     return;
334   }
335   if (!m_pCacheOld)
336   {
337     CCacheStrategy *pCacheNew = m_pCache->CreateNew();
338     if (pCacheNew->Open() != CACHE_RC_OK)
339     {
340       delete pCacheNew;
341       m_pCache->Reset(iSourcePosition, clearAnyway);
342       return;
343     }
344     pCacheNew->Reset(iSourcePosition, clearAnyway);
345     m_pCacheOld = m_pCache;
346     m_pCache = pCacheNew;
347     return;
348   }
349   m_pCacheOld->Reset(iSourcePosition, clearAnyway);
350   CCacheStrategy *tmp = m_pCacheOld;
351   m_pCacheOld = m_pCache;
352   m_pCache = tmp;
353 }
354
355 void CSimpleDoubleCache::EndOfInput()
356 {
357   m_pCache->EndOfInput();
358 }
359
360 bool CSimpleDoubleCache::IsEndOfInput()
361 {
362   return m_pCache->IsEndOfInput();
363 }
364
365 void CSimpleDoubleCache::ClearEndOfInput()
366 {
367   m_pCache->ClearEndOfInput();
368 }
369
370 int64_t CSimpleDoubleCache::CachedDataEndPos()
371 {
372   return m_pCache->CachedDataEndPos();
373 }
374
375 int64_t CSimpleDoubleCache::CachedDataEndPosIfSeekTo(int64_t iFilePosition)
376 {
377   int64_t ret = m_pCache->CachedDataEndPosIfSeekTo(iFilePosition);
378   if (m_pCacheOld)
379     return std::max(ret, m_pCacheOld->CachedDataEndPosIfSeekTo(iFilePosition));
380   return ret;
381 }
382
383 bool CSimpleDoubleCache::IsCachedPosition(int64_t iFilePosition)
384 {
385   return m_pCache->IsCachedPosition(iFilePosition) || (m_pCacheOld && m_pCacheOld->IsCachedPosition(iFilePosition));
386 }
387
388 CCacheStrategy *CSimpleDoubleCache::CreateNew()
389 {
390   return new CSimpleDoubleCache(m_pCache->CreateNew());
391 }
392