|
1
|
1 #include "StdAfx.h"
|
|
|
2
|
|
|
3 #include "seekabilizer.h"
|
|
|
4
|
|
|
5 enum {backread_on_seek = 1024};
|
|
|
6
|
|
|
7 void seekabilizer_backbuffer::initialize(t_size p_size)
|
|
|
8 {
|
|
|
9 m_depth = m_cursor = 0;
|
|
|
10
|
|
|
11 m_buffer.set_size(p_size);
|
|
|
12 }
|
|
|
13
|
|
|
14 void seekabilizer_backbuffer::write(const void * p_buffer,t_size p_bytes)
|
|
|
15 {
|
|
|
16 if (p_bytes >= m_buffer.get_size())
|
|
|
17 {
|
|
|
18 memcpy(m_buffer.get_ptr(),(const t_uint8*)p_buffer + p_bytes - m_buffer.get_size(),m_buffer.get_size());
|
|
|
19 m_cursor = 0;
|
|
|
20 m_depth = m_buffer.get_size();
|
|
|
21 }
|
|
|
22 else
|
|
|
23 {
|
|
|
24 const t_uint8* sourceptr = (const t_uint8*) p_buffer;
|
|
|
25 t_size remaining = p_bytes;
|
|
|
26 while(remaining > 0)
|
|
|
27 {
|
|
|
28 t_size delta = m_buffer.get_size() - m_cursor;
|
|
|
29 if (delta > remaining) delta = remaining;
|
|
|
30
|
|
|
31 memcpy(m_buffer.get_ptr() + m_cursor,sourceptr,delta);
|
|
|
32
|
|
|
33 sourceptr += delta;
|
|
|
34 remaining -= delta;
|
|
|
35 m_cursor = (m_cursor + delta) % m_buffer.get_size();
|
|
|
36
|
|
|
37 m_depth = pfc::min_t<t_size>(m_buffer.get_size(),m_depth + delta);
|
|
|
38
|
|
|
39 }
|
|
|
40 }
|
|
|
41 }
|
|
|
42
|
|
|
43 void seekabilizer_backbuffer::read(t_size p_backlogdepth,void * p_buffer,t_size p_bytes) const
|
|
|
44 {
|
|
|
45 PFC_ASSERT(p_backlogdepth <= m_depth);
|
|
|
46 PFC_ASSERT(p_backlogdepth >= p_bytes);
|
|
|
47
|
|
|
48
|
|
|
49 t_uint8* targetptr = (t_uint8*) p_buffer;
|
|
|
50 t_size remaining = p_bytes;
|
|
|
51 t_size cursor = (m_cursor + m_buffer.get_size() - p_backlogdepth) % m_buffer.get_size();
|
|
|
52
|
|
|
53 while(remaining > 0)
|
|
|
54 {
|
|
|
55 t_size delta = m_buffer.get_size() - cursor;
|
|
|
56 if (delta > remaining) delta = remaining;
|
|
|
57
|
|
|
58 memcpy(targetptr,m_buffer.get_ptr() + cursor,delta);
|
|
|
59
|
|
|
60 targetptr += delta;
|
|
|
61 remaining -= delta;
|
|
|
62 cursor = (cursor + delta) % m_buffer.get_size();
|
|
|
63 }
|
|
|
64 }
|
|
|
65
|
|
|
66 t_size seekabilizer_backbuffer::get_depth() const
|
|
|
67 {
|
|
|
68 return m_depth;
|
|
|
69 }
|
|
|
70
|
|
|
71 t_size seekabilizer_backbuffer::get_max_depth() const
|
|
|
72 {
|
|
|
73 return m_buffer.get_size();
|
|
|
74 }
|
|
|
75
|
|
|
76 void seekabilizer_backbuffer::reset()
|
|
|
77 {
|
|
|
78 m_depth = m_cursor = 0;
|
|
|
79 }
|
|
|
80
|
|
|
81
|
|
|
82 void seekabilizer::initialize(service_ptr_t<file> p_base,t_size p_buffer_size,abort_callback & p_abort) {
|
|
|
83 m_buffer.initialize(p_buffer_size);
|
|
|
84 m_file = p_base;
|
|
|
85 m_position = m_position_base = 0;
|
|
|
86 m_size = m_file->get_size(p_abort);
|
|
|
87 }
|
|
|
88
|
|
|
89 void seekabilizer::g_seekabilize(service_ptr_t<file> & p_reader,t_size p_buffer_size,abort_callback & p_abort) {
|
|
|
90 if (p_reader.is_valid() && (p_reader->is_remote() || !p_reader->can_seek()) && p_buffer_size > 0) {
|
|
|
91 auto instance = fb2k::service_new<seekabilizer>();
|
|
|
92 instance->initialize(p_reader,p_buffer_size,p_abort);
|
|
|
93 p_reader = instance.get_ptr();
|
|
|
94 }
|
|
|
95 }
|
|
|
96
|
|
|
97 t_size seekabilizer::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
|
|
98 p_abort.check_e();
|
|
|
99
|
|
|
100 if (m_position > m_position_base + pfc::max_t<t_size>(m_buffer.get_max_depth(),backread_on_seek) && m_file->can_seek()) {
|
|
|
101 m_buffer.reset();
|
|
|
102 t_filesize target = m_position;
|
|
|
103 if (target < backread_on_seek) target = 0;
|
|
|
104 else target -= backread_on_seek;
|
|
|
105 m_file->seek(target,p_abort);
|
|
|
106 m_position_base = target;
|
|
|
107 }
|
|
|
108
|
|
|
109 //seek ahead
|
|
|
110 while(m_position > m_position_base) {
|
|
|
111 enum {tempsize = 1024};
|
|
|
112 t_uint8 temp[tempsize];
|
|
|
113 t_size delta = (t_size) pfc::min_t<t_filesize>(tempsize,m_position - m_position_base);
|
|
|
114 t_size bytes_read = 0;
|
|
|
115 bytes_read = m_file->read(temp,delta,p_abort);
|
|
|
116 m_buffer.write(temp,bytes_read);
|
|
|
117 m_position_base += bytes_read;
|
|
|
118
|
|
|
119 if (bytes_read < delta) {
|
|
|
120 return 0;
|
|
|
121 }
|
|
|
122 }
|
|
|
123
|
|
|
124 t_size done = 0;
|
|
|
125 t_uint8 * targetptr = (t_uint8*) p_buffer;
|
|
|
126
|
|
|
127 //try to read backbuffer
|
|
|
128 if (m_position < m_position_base) {
|
|
|
129 if (m_position_base - m_position > (t_filesize)m_buffer.get_depth()) throw exception_io_seek_out_of_range();
|
|
|
130 t_size backread_depth = (t_size) (m_position_base - m_position);
|
|
|
131 t_size delta = pfc::min_t<t_size>(backread_depth,p_bytes-done);
|
|
|
132 m_buffer.read(backread_depth,targetptr,delta);
|
|
|
133 done += delta;
|
|
|
134 m_position += delta;
|
|
|
135 }
|
|
|
136
|
|
|
137 //regular read
|
|
|
138 if (done < p_bytes)
|
|
|
139 {
|
|
|
140 t_size bytes_read;
|
|
|
141 bytes_read = m_file->read(targetptr+done,p_bytes-done,p_abort);
|
|
|
142
|
|
|
143 m_buffer.write(targetptr+done,bytes_read);
|
|
|
144
|
|
|
145 done += bytes_read;
|
|
|
146 m_position += bytes_read;
|
|
|
147 m_position_base += bytes_read;
|
|
|
148 }
|
|
|
149
|
|
|
150 return done;
|
|
|
151 }
|
|
|
152
|
|
|
153 t_filesize seekabilizer::get_size(abort_callback & p_abort) {
|
|
|
154 p_abort.check_e();
|
|
|
155 return m_size;
|
|
|
156 }
|
|
|
157
|
|
|
158 t_filesize seekabilizer::get_position(abort_callback & p_abort) {
|
|
|
159 p_abort.check_e();
|
|
|
160 return m_position;
|
|
|
161 }
|
|
|
162
|
|
|
163 void seekabilizer::seek(t_filesize p_position,abort_callback & p_abort) {
|
|
|
164 PFC_ASSERT(m_position_base >= m_buffer.get_depth());
|
|
|
165 p_abort.check_e();
|
|
|
166
|
|
|
167 if (m_size != filesize_invalid && p_position > m_size) throw exception_io_seek_out_of_range();
|
|
|
168
|
|
|
169 t_filesize lowest = m_position_base - m_buffer.get_depth();
|
|
|
170
|
|
|
171 if (p_position < lowest) {
|
|
|
172 if (m_file->can_seek()) {
|
|
|
173 m_buffer.reset();
|
|
|
174 t_filesize target = p_position;
|
|
|
175 t_size delta = m_buffer.get_max_depth();
|
|
|
176 if (delta > backread_on_seek) delta = backread_on_seek;
|
|
|
177 if (target > delta) target -= delta;
|
|
|
178 else target = 0;
|
|
|
179 m_file->seek(target,p_abort);
|
|
|
180 m_position_base = target;
|
|
|
181 }
|
|
|
182 else {
|
|
|
183 m_buffer.reset();
|
|
|
184 m_file->reopen(p_abort);
|
|
|
185 m_position_base = 0;
|
|
|
186 }
|
|
|
187 }
|
|
|
188
|
|
|
189 m_position = p_position;
|
|
|
190 }
|
|
|
191
|
|
|
192 bool seekabilizer::can_seek()
|
|
|
193 {
|
|
|
194 return true;
|
|
|
195 }
|
|
|
196
|
|
|
197 bool seekabilizer::get_content_type(pfc::string_base & p_out) {return m_file->get_content_type(p_out);}
|
|
|
198
|
|
|
199 bool seekabilizer::is_in_memory() {return false;}
|
|
|
200
|
|
|
201 void seekabilizer::on_idle(abort_callback & p_abort) {return m_file->on_idle(p_abort);}
|
|
|
202
|
|
|
203 t_filetimestamp seekabilizer::get_timestamp(abort_callback & p_abort) {
|
|
|
204 p_abort.check_e();
|
|
|
205 return m_file->get_timestamp(p_abort);
|
|
|
206 }
|
|
|
207
|
|
|
208 void seekabilizer::reopen(abort_callback & p_abort) {
|
|
|
209 if (m_position_base - m_buffer.get_depth() == 0) {
|
|
|
210 seek(0,p_abort);
|
|
|
211 } else {
|
|
|
212 m_position = m_position_base = 0;
|
|
|
213 m_buffer.reset();
|
|
|
214 m_file->reopen(p_abort);
|
|
|
215 }
|
|
|
216 }
|
|
|
217
|
|
|
218 bool seekabilizer::is_remote()
|
|
|
219 {
|
|
|
220 return m_file->is_remote();
|
|
|
221 }
|
|
|
222
|
|
|
223 service_ptr seekabilizer::get_metadata(abort_callback& a) {
|
|
|
224 return m_file->get_metadata_(a);
|
|
|
225 }
|
|
|
226
|
|
|
227 t_filestats2 seekabilizer::get_stats2(uint32_t s2flags, abort_callback& a) {
|
|
|
228 return m_file->get_stats2_(s2flags, a);
|
|
|
229 }
|
|
|
230
|
|
|
231 size_t seekabilizer::lowLevelIO(const GUID& guid, size_t arg1, void* arg2, size_t arg2size, abort_callback& abort) {
|
|
|
232 return m_file->lowLevelIO_(guid, arg1, arg2, arg2size, abort);
|
|
|
233 }
|