1/*
2 * Copyright 2009, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "ChunkCache.h"
8
9#include <new>
10#include <stdlib.h>
11#include <string.h>
12
13#include "MediaDebug.h"
14
15// #pragma mark -
16
17
18ChunkCache::ChunkCache(sem_id waitSem, size_t maxBytes)
19	:
20	BLocker("media chunk cache"),
21	fWaitSem(waitSem)
22{
23	rtm_create_pool(&fRealTimePool, maxBytes, "media chunk cache");
24	fMaxBytes = rtm_available(fRealTimePool);
25}
26
27
28ChunkCache::~ChunkCache()
29{
30	rtm_delete_pool(fRealTimePool);
31}
32
33
34status_t
35ChunkCache::InitCheck() const
36{
37	if (fRealTimePool == NULL)
38		return B_NO_MEMORY;
39
40	return B_OK;
41}
42
43
44void
45ChunkCache::MakeEmpty()
46{
47	ASSERT(IsLocked());
48
49	while (!fChunkCache.empty()) {
50		RecycleChunk(fChunkCache.front());
51		fChunkCache.pop();
52	}
53
54	release_sem(fWaitSem);
55}
56
57
58bool
59ChunkCache::SpaceLeft() const
60{
61	ASSERT(IsLocked());
62
63	if (fChunkCache.size() >= CACHE_MAX_ENTRIES) {
64		return false;
65	}
66
67	// If there is no more memory we are likely to fail soon after
68	return sizeof(chunk_buffer) + 2048 < rtm_available(fRealTimePool);
69}
70
71
72chunk_buffer*
73ChunkCache::NextChunk(Reader* reader, void* cookie)
74{
75	ASSERT(IsLocked());
76
77	chunk_buffer* chunk = NULL;
78
79	if (fChunkCache.empty()) {
80		TRACE("ChunkCache is empty, going direct to reader\n");
81		if (ReadNextChunk(reader, cookie)) {
82			return NextChunk(reader, cookie);
83		}
84	} else {
85		chunk = fChunkCache.front();
86		fChunkCache.pop();
87
88		release_sem(fWaitSem);
89	}
90
91	return chunk;
92}
93
94
95/*	Moves the specified chunk to the unused list.
96	This means the chunk data can be overwritten again.
97*/
98void
99ChunkCache::RecycleChunk(chunk_buffer* chunk)
100{
101	ASSERT(IsLocked());
102
103	rtm_free(chunk->buffer);
104	chunk->capacity = 0;
105	chunk->size = 0;
106	chunk->buffer = NULL;
107	fUnusedChunks.push_back(chunk);
108}
109
110
111bool
112ChunkCache::ReadNextChunk(Reader* reader, void* cookie)
113{
114	ASSERT(IsLocked());
115
116	// retrieve chunk buffer
117	chunk_buffer* chunk = NULL;
118	if (fUnusedChunks.empty()) {
119		// allocate a new one
120		chunk = (chunk_buffer*)rtm_alloc(fRealTimePool, sizeof(chunk_buffer));
121		if (chunk == NULL) {
122			ERROR("RTM Pool empty allocating chunk buffer structure");
123			return false;
124		}
125
126		chunk->size = 0;
127		chunk->capacity = 0;
128		chunk->buffer = NULL;
129
130	} else {
131		chunk = fUnusedChunks.front();
132		fUnusedChunks.pop_front();
133	}
134
135	const void* buffer;
136	size_t bufferSize;
137	chunk->status = reader->GetNextChunk(cookie, &buffer, &bufferSize,
138		&chunk->header);
139	if (chunk->status == B_OK) {
140		if (chunk->capacity < bufferSize) {
141			// adapt buffer size
142			rtm_free(chunk->buffer);
143			chunk->capacity = (bufferSize + 2047) & ~2047;
144			chunk->buffer = rtm_alloc(fRealTimePool, chunk->capacity);
145			if (chunk->buffer == NULL) {
146				rtm_free(chunk);
147				ERROR("RTM Pool empty allocating chunk buffer\n");
148				return false;
149			}
150		}
151
152		memcpy(chunk->buffer, buffer, bufferSize);
153		chunk->size = bufferSize;
154	}
155
156	fChunkCache.push(chunk);
157	return chunk->status == B_OK;
158}
159