latest SDK
This commit is contained in:
259
foobar2000/SDK/filesystem_helper.cpp
Normal file
259
foobar2000/SDK/filesystem_helper.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
#include "foobar2000.h"
|
||||
|
||||
void stream_writer_chunk::write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
||||
t_size remaining = p_bytes, written = 0;
|
||||
while(remaining > 0) {
|
||||
t_size delta = sizeof(m_buffer) - m_buffer_state;
|
||||
if (delta > remaining) delta = remaining;
|
||||
memcpy(m_buffer,(const t_uint8*)p_buffer + written,delta);
|
||||
written += delta;
|
||||
remaining -= delta;
|
||||
|
||||
if (m_buffer_state == sizeof(m_buffer)) {
|
||||
m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort);
|
||||
m_writer->write_object(m_buffer,m_buffer_state,p_abort);
|
||||
m_buffer_state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stream_writer_chunk::flush(abort_callback & p_abort)
|
||||
{
|
||||
m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort);
|
||||
if (m_buffer_state > 0) {
|
||||
m_writer->write_object(m_buffer,m_buffer_state,p_abort);
|
||||
m_buffer_state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
stream_writer * m_writer;
|
||||
unsigned m_buffer_state;
|
||||
unsigned char m_buffer[255];
|
||||
*/
|
||||
|
||||
t_size stream_reader_chunk::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort)
|
||||
{
|
||||
t_size todo = p_bytes, done = 0;
|
||||
while(todo > 0) {
|
||||
if (m_buffer_size == m_buffer_state) {
|
||||
if (m_eof) break;
|
||||
t_uint8 temp;
|
||||
m_reader->read_lendian_t(temp,p_abort);
|
||||
m_buffer_size = temp;
|
||||
if (temp != sizeof(m_buffer)) m_eof = true;
|
||||
m_buffer_state = 0;
|
||||
if (m_buffer_size>0) {
|
||||
m_reader->read_object(m_buffer,m_buffer_size,p_abort);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
t_size delta = m_buffer_size - m_buffer_state;
|
||||
if (delta > todo) delta = todo;
|
||||
if (delta > 0) {
|
||||
memcpy((unsigned char*)p_buffer + done,m_buffer + m_buffer_state,delta);
|
||||
todo -= delta;
|
||||
done += delta;
|
||||
m_buffer_state += delta;
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
void stream_reader_chunk::flush(abort_callback & p_abort) {
|
||||
while(!m_eof) {
|
||||
p_abort.check_e();
|
||||
t_uint8 temp;
|
||||
m_reader->read_lendian_t(temp,p_abort);
|
||||
m_buffer_size = temp;
|
||||
if (temp != sizeof(m_buffer)) m_eof = true;
|
||||
m_buffer_state = 0;
|
||||
if (m_buffer_size>0) {
|
||||
m_reader->skip_object(m_buffer_size,p_abort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
stream_reader * m_reader;
|
||||
unsigned m_buffer_state, m_buffer_size;
|
||||
bool m_eof;
|
||||
unsigned char m_buffer[255];
|
||||
*/
|
||||
|
||||
void stream_reader_chunk::g_skip(stream_reader * p_stream,abort_callback & p_abort) {
|
||||
stream_reader_chunk(p_stream).flush(p_abort);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void fileSanitySeek(file::ptr f, pfc::array_t<uint8_t> const & content, size_t offset, abort_callback & aborter) {
|
||||
const size_t readAmount = 64 * 1024;
|
||||
pfc::array_staticsize_t<uint8_t> buf; buf.set_size_discard(readAmount);
|
||||
f->seek(offset, aborter);
|
||||
t_filesize positionGot = f->get_position(aborter);
|
||||
if (positionGot != offset) {
|
||||
FB2K_console_formatter() << "File sanity: at " << offset << " reported position became " << positionGot;
|
||||
throw std::runtime_error("Seek test failure");
|
||||
}
|
||||
size_t did = f->read(buf.get_ptr(), readAmount, aborter);
|
||||
size_t expected = pfc::min_t<size_t>(readAmount, content.get_size() - offset);
|
||||
if (expected != did) {
|
||||
FB2K_console_formatter() << "File sanity: at " << offset << " bytes, expected read size of " << expected << ", got " << did;
|
||||
if (did > expected) FB2K_console_formatter() << "Read past EOF";
|
||||
else FB2K_console_formatter() << "Premature EOF";
|
||||
throw std::runtime_error("Seek test failure");
|
||||
}
|
||||
if (memcmp(buf.get_ptr(), content.get_ptr() + offset, did) != 0) {
|
||||
FB2K_console_formatter() << "File sanity: data mismatch at " << offset << " - " << (offset + did) << " bytes";
|
||||
throw std::runtime_error("Seek test failure");
|
||||
}
|
||||
positionGot = f->get_position(aborter);
|
||||
if (positionGot != offset + did) {
|
||||
FB2K_console_formatter() << "File sanity: at " << offset << "+" << did << "=" << (offset + did) << " reported position became " << positionGot;
|
||||
throw std::runtime_error("Seek test failure");
|
||||
}
|
||||
}
|
||||
|
||||
bool fb2kFileSelfTest(file::ptr f, abort_callback & aborter) {
|
||||
try {
|
||||
pfc::array_t<uint8_t> fileContent;
|
||||
f->reopen(aborter);
|
||||
f->read_till_eof(fileContent, aborter);
|
||||
|
||||
{
|
||||
t_filesize sizeClaimed = f->get_size(aborter);
|
||||
if (sizeClaimed == filesize_invalid) {
|
||||
FB2K_console_formatter() << "File sanity: file reports unknown size, actual size read is " << fileContent.get_size();
|
||||
}
|
||||
else {
|
||||
if (sizeClaimed != fileContent.get_size()) {
|
||||
FB2K_console_formatter() << "File sanity: file reports size of " << sizeClaimed << ", actual size read is " << fileContent.get_size();
|
||||
throw std::runtime_error("File size mismatch");
|
||||
}
|
||||
else {
|
||||
FB2K_console_formatter() << "File sanity: file size check OK: " << sizeClaimed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
FB2K_console_formatter() << "File sanity: testing N-first-bytes reads...";
|
||||
const size_t sizeUpTo = pfc::min_t<size_t>(fileContent.get_size(), 1024 * 1024);
|
||||
pfc::array_staticsize_t<uint8_t> buf1;
|
||||
buf1.set_size_discard(sizeUpTo);
|
||||
|
||||
for (size_t w = 1; w <= sizeUpTo; w <<= 1) {
|
||||
f->reopen(aborter);
|
||||
size_t did = f->read(buf1.get_ptr(), w, aborter);
|
||||
if (did != w) {
|
||||
FB2K_console_formatter() << "File sanity: premature EOF reading first " << w << " bytes, got " << did;
|
||||
throw std::runtime_error("Premature EOF");
|
||||
}
|
||||
if (memcmp(fileContent.get_ptr(), buf1.get_ptr(), did) != 0) {
|
||||
FB2K_console_formatter() << "File sanity: file content mismatch reading first " << w << " bytes";
|
||||
throw std::runtime_error("File content mismatch");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (f->can_seek()) {
|
||||
FB2K_console_formatter() << "File sanity: testing random access...";
|
||||
|
||||
{
|
||||
size_t sizeUpTo = pfc::min_t<size_t>(fileContent.get_size(), 1024 * 1024);
|
||||
for (size_t w = 1; w < sizeUpTo; w <<= 1) {
|
||||
fileSanitySeek(f, fileContent, w, aborter);
|
||||
}
|
||||
fileSanitySeek(f, fileContent, fileContent.get_size(), aborter);
|
||||
for (size_t w = 1; w < sizeUpTo; w <<= 1) {
|
||||
fileSanitySeek(f, fileContent, fileContent.get_size() - w, aborter);
|
||||
}
|
||||
fileSanitySeek(f, fileContent, fileContent.get_size() / 2, aborter);
|
||||
}
|
||||
}
|
||||
FB2K_console_formatter() << "File sanity test: all OK";
|
||||
return true;
|
||||
}
|
||||
catch (std::exception const & e) {
|
||||
FB2K_console_formatter() << "File sanity test failure: " << e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace foobar2000_io {
|
||||
void retryFileDelete(double timeout, abort_callback & a, std::function<void()> f) {
|
||||
FB2K_RETRY_ON_EXCEPTION3(f(), a, timeout, exception_io_sharing_violation, exception_io_denied, exception_io_directory_not_empty);
|
||||
}
|
||||
void retryFileMove(double timeout, abort_callback & a, std::function<void() > f) {
|
||||
FB2K_RETRY_FILE_MOVE( f(), a, timeout );
|
||||
}
|
||||
void retryOnSharingViolation(double timeout, abort_callback & a, std::function<void() > f) {
|
||||
FB2K_RETRY_ON_SHARING_VIOLATION(f(), a, timeout);
|
||||
}
|
||||
void retryOnSharingViolation(std::function<void() > f, double timeout, abort_callback & a) {
|
||||
FB2K_RETRY_ON_SHARING_VIOLATION( f(), a, timeout );
|
||||
}
|
||||
void listDirectory( const char * path, abort_callback & aborter, listDirectoryFunc_t func) {
|
||||
listDirectoryCallbackImpl cb; cb.m_func = func;
|
||||
filesystem::g_list_directory(path, cb, aborter);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
pfc::string8 stripParentFolders( const char * inPath ) {
|
||||
PFC_ASSERT( strstr(inPath, "://" ) == nullptr || matchProtocol( inPath, "file" ) );
|
||||
size_t prefixLen = pfc::string_find_first(inPath, "://");
|
||||
if ( prefixLen != pfc_infinite ) prefixLen += 3;
|
||||
else prefixLen = 0;
|
||||
|
||||
pfc::chain_list_v2_t<pfc::string_part_ref> segments;
|
||||
pfc::splitStringByChar(segments, inPath + prefixLen, '\\' );
|
||||
for ( auto i = segments.first(); i.is_valid(); ) {
|
||||
auto n = i; ++n;
|
||||
if ( i->equals( "." ) ) {
|
||||
segments.remove_single( i );
|
||||
} else if ( i->equals( ".." ) ) {
|
||||
auto p = i; --p;
|
||||
if ( p.is_valid() ) segments.remove_single( p );
|
||||
segments.remove_single( i );
|
||||
}
|
||||
i = n;
|
||||
}
|
||||
pfc::string8 ret;
|
||||
if ( prefixLen > 0 ) ret.add_string( inPath, prefixLen );
|
||||
bool bFirst = true;
|
||||
for ( auto i = segments.first(); i.is_valid(); ++ i ) {
|
||||
if (!bFirst) ret << "\\";
|
||||
ret << *i;
|
||||
bFirst = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
pfc::string8 winGetVolumePath(const char * fb2kPath) {
|
||||
PFC_ASSERT(matchProtocol(fb2kPath, "file"));
|
||||
pfc::string8 native;
|
||||
if (!filesystem::g_get_native_path(fb2kPath, native)) throw pfc::exception_invalid_params();
|
||||
|
||||
TCHAR outBuffer[MAX_PATH+1] = {};
|
||||
WIN32_IO_OP( GetVolumePathName( pfc::stringcvt::string_os_from_utf8( native ), outBuffer, MAX_PATH ) );
|
||||
return pfc::stringcvt::string_utf8_from_os( outBuffer ).get_ptr();
|
||||
}
|
||||
|
||||
DWORD winVolumeFlags( const char * fb2kPath ) {
|
||||
PFC_ASSERT(matchProtocol(fb2kPath, "file"));
|
||||
pfc::string8 native;
|
||||
if (!filesystem::g_get_native_path(fb2kPath, native)) throw pfc::exception_invalid_params();
|
||||
|
||||
TCHAR outBuffer[MAX_PATH + 1] = {};
|
||||
WIN32_IO_OP(GetVolumePathName(pfc::stringcvt::string_os_from_utf8(native), outBuffer, MAX_PATH));
|
||||
|
||||
DWORD flags = 0;
|
||||
WIN32_IO_OP(GetVolumeInformation(outBuffer, nullptr, 0, nullptr, nullptr, &flags, nullptr, 0));
|
||||
return flags;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user