#include "stdafx.h" #include "seekabilizer.h" enum {backread_on_seek = 1024}; void seekabilizer_backbuffer::initialize(t_size p_size) { m_depth = m_cursor = 0; m_buffer.set_size(p_size); } void seekabilizer_backbuffer::write(const void * p_buffer,t_size p_bytes) { if (p_bytes >= m_buffer.get_size()) { memcpy(m_buffer.get_ptr(),(const t_uint8*)p_buffer + p_bytes - m_buffer.get_size(),m_buffer.get_size()); m_cursor = 0; m_depth = m_buffer.get_size(); } else { const t_uint8* sourceptr = (const t_uint8*) p_buffer; t_size remaining = p_bytes; while(remaining > 0) { t_size delta = m_buffer.get_size() - m_cursor; if (delta > remaining) delta = remaining; memcpy(m_buffer.get_ptr() + m_cursor,sourceptr,delta); sourceptr += delta; remaining -= delta; m_cursor = (m_cursor + delta) % m_buffer.get_size(); m_depth = pfc::min_t(m_buffer.get_size(),m_depth + delta); } } } void seekabilizer_backbuffer::read(t_size p_backlogdepth,void * p_buffer,t_size p_bytes) const { PFC_ASSERT(p_backlogdepth <= m_depth); PFC_ASSERT(p_backlogdepth >= p_bytes); t_uint8* targetptr = (t_uint8*) p_buffer; t_size remaining = p_bytes; t_size cursor = (m_cursor + m_buffer.get_size() - p_backlogdepth) % m_buffer.get_size(); while(remaining > 0) { t_size delta = m_buffer.get_size() - cursor; if (delta > remaining) delta = remaining; memcpy(targetptr,m_buffer.get_ptr() + cursor,delta); targetptr += delta; remaining -= delta; cursor = (cursor + delta) % m_buffer.get_size(); } } t_size seekabilizer_backbuffer::get_depth() const { return m_depth; } t_size seekabilizer_backbuffer::get_max_depth() const { return m_buffer.get_size(); } void seekabilizer_backbuffer::reset() { m_depth = m_cursor = 0; } void seekabilizer::initialize(service_ptr_t p_base,t_size p_buffer_size,abort_callback & p_abort) { m_buffer.initialize(p_buffer_size); m_file = p_base; m_position = m_position_base = 0; m_size = m_file->get_size(p_abort); } void seekabilizer::g_seekabilize(service_ptr_t & p_reader,t_size p_buffer_size,abort_callback & p_abort) { if (p_reader.is_valid() && p_reader->is_remote() && p_buffer_size > 0) { service_ptr_t instance = new service_impl_t(); instance->initialize(p_reader,p_buffer_size,p_abort); p_reader = instance.get_ptr(); } } t_size seekabilizer::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) { p_abort.check_e(); if (m_position > m_position_base + pfc::max_t(m_buffer.get_max_depth(),backread_on_seek) && m_file->can_seek()) { m_buffer.reset(); t_filesize target = m_position; if (target < backread_on_seek) target = 0; else target -= backread_on_seek; m_file->seek(target,p_abort); m_position_base = target; } //seek ahead while(m_position > m_position_base) { enum {tempsize = 1024}; t_uint8 temp[tempsize]; t_size delta = (t_size) pfc::min_t(tempsize,m_position - m_position_base); t_size bytes_read = 0; bytes_read = m_file->read(temp,delta,p_abort); m_buffer.write(temp,bytes_read); m_position_base += bytes_read; if (bytes_read < delta) { return 0; } } t_size done = 0; t_uint8 * targetptr = (t_uint8*) p_buffer; //try to read backbuffer if (m_position < m_position_base) { if (m_position_base - m_position > (t_filesize)m_buffer.get_depth()) throw exception_io_seek_out_of_range(); t_size backread_depth = (t_size) (m_position_base - m_position); t_size delta = pfc::min_t(backread_depth,p_bytes-done); m_buffer.read(backread_depth,targetptr,delta); done += delta; m_position += delta; } //regular read if (done < p_bytes) { t_size bytes_read; bytes_read = m_file->read(targetptr+done,p_bytes-done,p_abort); m_buffer.write(targetptr+done,bytes_read); done += bytes_read; m_position += bytes_read; m_position_base += bytes_read; } return done; } t_filesize seekabilizer::get_size(abort_callback & p_abort) { p_abort.check_e(); return m_size; } t_filesize seekabilizer::get_position(abort_callback & p_abort) { p_abort.check_e(); return m_position; } void seekabilizer::seek(t_filesize p_position,abort_callback & p_abort) { PFC_ASSERT(m_position_base >= m_buffer.get_depth()); p_abort.check_e(); if (m_size != filesize_invalid && p_position > m_size) throw exception_io_seek_out_of_range(); t_filesize lowest = m_position_base - m_buffer.get_depth(); if (p_position < lowest) { if (m_file->can_seek()) { m_buffer.reset(); t_filesize target = p_position; t_size delta = m_buffer.get_max_depth(); if (delta > backread_on_seek) delta = backread_on_seek; if (target > delta) target -= delta; else target = 0; m_file->seek(target,p_abort); m_position_base = target; } else { m_buffer.reset(); m_file->reopen(p_abort); m_position_base = 0; } } m_position = p_position; } bool seekabilizer::can_seek() { return true; } bool seekabilizer::get_content_type(pfc::string_base & p_out) {return m_file->get_content_type(p_out);} bool seekabilizer::is_in_memory() {return false;} void seekabilizer::on_idle(abort_callback & p_abort) {return m_file->on_idle(p_abort);} t_filetimestamp seekabilizer::get_timestamp(abort_callback & p_abort) { p_abort.check_e(); return m_file->get_timestamp(p_abort); } void seekabilizer::reopen(abort_callback & p_abort) { if (m_position_base - m_buffer.get_depth() == 0) { seek(0,p_abort); } else { m_position = m_position_base = 0; m_buffer.reset(); m_file->reopen(p_abort); } } bool seekabilizer::is_remote() { return m_file->is_remote(); }