← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2776: File reader & test utility, wip

 

------------------------------------------------------------
revno: 2776
committer: Jacek Sieka <arnetheduck@xxxxxxxxx>
branch nick: dcplusplus
timestamp: Sat 2011-12-31 12:50:24 +0100
message:
  File reader & test utility, wip
added:
  dcpp/FileReader.cpp
  dcpp/FileReader.h
  utils/
  utils/SConscript
  utils/xsum.cpp
modified:
  SConstruct
  dcpp/Util.cpp
  test/SConscript


--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk

Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'SConstruct'
--- SConstruct	2011-12-27 19:47:56 +0000
+++ SConstruct	2011-12-31 11:50:24 +0000
@@ -84,6 +84,7 @@
 	BoolVariable('help', 'Build help files (requires i18n=1)', 'yes'),
 	BoolVariable('webhelp', 'Build help files for the web (requires help=1)', 'no'),
 	BoolVariable('test', 'Build test suite', 'no'),
+	BoolVariable('utils', 'Build utils suite', 'no'),
 	('prefix', 'Prefix to use when cross compiling', ''),
 	EnumVariable('arch', 'Target architecture', 'x86', ['x86', 'x64', 'ia64']),
 	BoolVariable('msvcproj', 'Build MSVC project files', 'no'),
@@ -264,8 +265,12 @@
 if dev.env['test']:
 	dev.test = dev.build('test/')
 	env.Default(dev.test)
+elif dev.env['utils']:
+	dev.utils = dev.build('utils/')
+	env.Default(dev.utils)
 else:
 	dev.win32 = dev.build('win32/')
+	
 dev.installer = dev.build('installer/')
 
 dev.finalize()

=== added file 'dcpp/FileReader.cpp'
--- dcpp/FileReader.cpp	1970-01-01 00:00:00 +0000
+++ dcpp/FileReader.cpp	2011-12-31 11:50:24 +0000
@@ -0,0 +1,174 @@
+#include "stdinc.h"
+
+#include "FileReader.h"
+
+#include "File.h"
+#include "Text.h"
+#include "Util.h"
+
+namespace dcpp {
+
+using std::make_pair;
+using std::swap;
+
+namespace {
+static const size_t READ_FAILED = static_cast<size_t>(-1);
+}
+
+size_t FileReader::read(const string& file, const DataCallback& callback) {
+	size_t ret = READ_FAILED;
+
+	if(direct) {
+		ret = readDirect(file, callback);
+	}
+
+	if(ret == READ_FAILED) {
+		ret = readMapped(file, callback);
+
+		if(ret == READ_FAILED) {
+			ret = readCached(file, callback);
+		}
+	}
+
+	return ret;
+}
+
+
+/** Read entire file, never returns READ_FAILED */
+size_t FileReader::readCached(const string& file, const DataCallback& callback) {
+	auto buf = getBuffer(512);
+
+	File f(file, File::READ, File::OPEN | File::SHARED);
+
+	size_t total = 0;
+	size_t n = buf.second;
+	while(f.read(buf.first, n) > 0) {
+		callback(buf.first, n);
+		total += n;
+		n = buf.second;
+	}
+
+	return total;
+}
+
+pair<void*, size_t> FileReader::getBuffer(size_t alignment) {
+	auto block = (((blockSize == 0 ? DEFAULT_BLOCK_SIZE : blockSize) + alignment - 1) / alignment) * alignment;
+
+	buffer.resize(block * 2 + alignment); // Prepare for worst case alignment
+
+	auto start = reinterpret_cast<void*>(((reinterpret_cast<size_t>(&buffer[0]) + alignment - 1) / alignment) * alignment);
+	return make_pair(start, block);
+}
+
+#ifdef _WIN32
+
+struct Handle : boost::noncopyable {
+	Handle(HANDLE h) : h(h) { }
+	~Handle() { ::CloseHandle(h); }
+
+	operator HANDLE() { return h; }
+
+	HANDLE h;
+};
+
+size_t FileReader::readDirect(const string& file, const DataCallback& callback) {
+	DWORD sector = 0, y;
+
+	auto tfile = Text::toT(file);
+
+	if (!::GetDiskFreeSpace(Util::getFilePath(tfile).c_str(), &y, &sector, &y, &y)) {
+		dcdebug("Failed to get sector size: %s\n", Util::translateError(::GetLastError()).c_str());
+		return READ_FAILED;
+	}
+
+	auto tmp = ::CreateFile(tfile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+		FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
+
+	if (tmp == INVALID_HANDLE_VALUE) {
+		dcdebug("Failed to open unbuffered file: %s\n", Util::translateError(::GetLastError()).c_str());
+		return false;
+	}
+
+	Handle h(tmp);
+
+	auto buf = getBuffer(sector);
+	DWORD bufSize = static_cast<DWORD>(buf.second);
+
+	DWORD hn = 0;
+	DWORD rn = 0;
+	uint8_t* hbuf = static_cast<uint8_t*>(buf.first) + bufSize;
+	uint8_t* rbuf = static_cast<uint8_t*>(buf.first);
+	OVERLAPPED over = { 0 };
+
+	// Read the first block
+	auto res = ::ReadFile(h, hbuf, buf.second, &hn, &over);
+
+	if(!res) {
+		auto err = ::GetLastError();
+		if (err == ERROR_HANDLE_EOF) {
+			hn = 0;
+		} else if(err == ERROR_IO_PENDING) {
+			// Finish the read and see how it went
+			if(!GetOverlappedResult(h, &over, &hn, TRUE)) {
+				if (::GetLastError() == ERROR_HANDLE_EOF) {
+					hn = 0;
+				} else {
+					dcdebug("First overlapped read failed: %s\n", Util::translateError(::GetLastError()).c_str());
+					return READ_FAILED;
+				}
+			}
+		}
+	}
+
+	over.Offset = hn;
+
+	for (; hn > 0;) {
+		// Last read returned some bytes, start a new overlapped read
+		res = ::ReadFile(h, rbuf, buf.second, &rn, &over);
+
+		callback(hbuf, hn);
+
+		if (!res) {
+			auto err = ::GetLastError();
+			if(err == ERROR_HANDLE_EOF) {
+				rn = 0;
+			} else if(err == ERROR_IO_PENDING) {
+				// Finish the read
+				if (!GetOverlappedResult(h, &over, &rn, TRUE)) {
+					err = ::GetLastError();
+					if(err != ERROR_HANDLE_EOF) {
+						throw FileException(Util::translateError(err));
+					}
+
+					rn = 0;
+				}
+			} else {
+				throw FileException(Util::translateError(::GetLastError()));
+			}
+		}
+
+		*((uint64_t*)&over.Offset) += rn;
+
+		swap(rbuf, hbuf);
+		swap(rn, hn);
+	}
+
+	return *((uint64_t*)&over.Offset);
+}
+
+size_t FileReader::readMapped(const string& file, const DataCallback& callback) {
+	return READ_FAILED;
+}
+
+#else
+
+size_t FileReader::readDirect(const string& file, const DataCallback& callback) {
+	return READ_FAILED;
+}
+
+size_t FileReader::readMapped(const string& file, const DataCallback& callback) {
+	return READ_FAILED;
+}
+
+#endif
+}

=== added file 'dcpp/FileReader.h'
--- dcpp/FileReader.h	1970-01-01 00:00:00 +0000
+++ dcpp/FileReader.h	2011-12-31 11:50:24 +0000
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2001-2011 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DCPLUSPLUS_DCPP_CONNECTION_MANAGER_H
+#define DCPLUSPLUS_DCPP_CONNECTION_MANAGER_H
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <boost/noncopyable.hpp>
+
+namespace dcpp {
+
+using std::function;
+using std::pair;
+using std::string;
+using std::vector;
+
+/** Helper class for reading an entire file */
+
+class FileReader : boost::noncopyable {
+public:
+
+	enum Strategy {
+		DIRECT,
+		MAPPED,
+		CACHED
+	};
+
+	typedef function<void(void*, size_t)> DataCallback;
+
+	/**
+	 * Set up file reader
+	 * @param direct Bypass system caches - good for reading files which are not in the cache and should not be there (for example when hashing)
+	 * @param blockSize Read block size, 0 = use default
+	 */
+	FileReader(bool direct = false, size_t blockSize = 0) : direct(direct), blockSize(blockSize) { }
+
+	/**
+	 * Read file - callback will be called for each read chunk which may or may not be a multiple of the requested block size.
+	 * @param file File name
+	 * @param callback Called for each block - the memory is owned by the reader object and
+	 * @return The number of bytes actually read
+	 * @throw FileException if the read fails
+	 */
+	size_t read(const string& file, const DataCallback& callback);
+private:
+	static const size_t DEFAULT_BLOCK_SIZE = 64*1024;
+	static const size_t DEFAULT_MMAP_SIZE = 64*1024*1024;
+
+	string file;
+	bool direct;
+	size_t blockSize;
+
+	vector<uint8_t> buffer;
+
+	/** Return an aligned buffer which is at least twice the size of ret.second */
+	pair<void*, size_t> getBuffer(size_t alignment);
+
+	size_t readDirect(const string& file, const DataCallback& callback);
+	size_t readMapped(const string& file, const DataCallback& callback);
+	size_t readCached(const string& file, const DataCallback& callback);
+};
+
+}
+
+#endif

=== modified file 'dcpp/Util.cpp'
--- dcpp/Util.cpp	2011-12-22 22:14:45 +0000
+++ dcpp/Util.cpp	2011-12-31 11:50:24 +0000
@@ -501,7 +501,7 @@
 }
 
 string Util::formatBytes(int64_t aBytes) {
-	char buf[128];
+	char buf[128] = { 0 };
 	if(aBytes < 1024) {
 		snprintf(buf, sizeof(buf), _("%d B"), (int)(aBytes&0xffffffff));
 	} else if(aBytes < 1024*1024) {

=== modified file 'test/SConscript'
--- test/SConscript	2011-10-23 13:22:23 +0000
+++ test/SConscript	2011-12-31 11:50:24 +0000
@@ -39,7 +39,7 @@
 	openssl_lib += env['arch'] + '/'
 env.Append(LIBPATH = [openssl_lib])
 
-ret = env.Program(target, [sources, dev.client, dev.zlib, dev.bzip2, dev.intl])
+ret = env.Program(target, [sources, dev.client, dev.dwarf, dev.zlib, dev.boost, dev.bzip2, dev.geoip, dev.miniupnpc, dev.natpmp, dev.intl])
 
 ret = env.Command(dev.get_target(source_path, 'gtest.passed', in_bin=False), ret[0].abspath, runUnitTest)
 

=== added directory 'utils'
=== added file 'utils/SConscript'
--- utils/SConscript	1970-01-01 00:00:00 +0000
+++ utils/SConscript	2011-12-31 11:50:24 +0000
@@ -0,0 +1,38 @@
+# vim: set filetype=py
+ 
+Import('dev source_path')
+
+env, target, sources = dev.prepare_build(source_path, 'xsum', source_glob="xsum.cpp", in_bin=False)
+
+if 'msvc' in env['TOOLS']:
+	if env['mode'] == 'debug':
+		env.Prepend(LIBS = ['ssleay32d', 'libeay32d'])
+	else:
+		env.Prepend(LIBS = ['ssleay32', 'libeay32'])
+else:
+	env.Prepend(LIBS = ['ssl', 'crypto'])
+
+if 'HAVE_HTMLHELP_H' in env['CPPDEFINES']:
+	env.Append(LIBS='htmlhelp')
+
+env.Append(LIBS = ['comctl32', 'ws2_32', 'ole32', 'gdi32', 'comdlg32', 'iphlpapi', 'winmm', 'shlwapi', 'oleaut32', 'uuid'])
+
+env.Append(CPPPATH = ['#/openssl/include', '#/miniupnpc', '#/dwt/include', '#/', '#/bzip2'])
+
+if '-mwindows' in env['CCFLAGS']:
+	env['CCFLAGS'].remove('-mwindows')
+
+if '-mwindows' in env['LINKFLAGS']:
+	env['LINKFLAGS'].remove('-mwindows')
+
+env.Append(CCFLAGS = ['-mconsole'])	
+env.Append(LINKFLAGS = ['-mconsole'])	
+	
+openssl_lib = '#/openssl/lib/'
+if env['arch'] != 'x86':
+	openssl_lib += env['arch'] + '/'
+env.Append(LIBPATH = [openssl_lib])
+
+ret = env.Program(target, [sources, dev.client, dev.dwarf, dev.zlib, dev.boost, dev.bzip2, dev.geoip, dev.miniupnpc, dev.natpmp, dev.intl])
+
+Return('ret')

=== added file 'utils/xsum.cpp'
--- utils/xsum.cpp	1970-01-01 00:00:00 +0000
+++ utils/xsum.cpp	2011-12-31 11:50:24 +0000
@@ -0,0 +1,49 @@
+#include <iostream>
+
+#include <dcpp/stdinc.h>
+#include <dcpp/FileReader.h>
+#include <dcpp/Util.h>
+
+#include <boost/date_time/posix_time/ptime.hpp>
+using namespace boost::posix_time;
+
+using namespace std;
+using namespace dcpp;
+
+int main(int argc, char** argv)
+{
+	if(argc != 2) {
+		cout << "You need to supply a file name" << endl;
+		return 1;
+	}
+
+	char x[_MAX_PATH] = { 0 };
+	char * tmp;
+	if(!(tmp = _fullpath(x, argv[1], _MAX_PATH))) {
+		cout << "Can't get full path" << endl;
+		return 1;
+	}
+	try {
+		auto start = microsec_clock::universal_time();
+		FileReader fr(true, 0);
+
+		size_t total = 0;
+		fr.read(tmp, [&](void* x, size_t n) {
+			total += n;
+			if(total % (1024*1024) == 0) {
+				std::cout << ".";
+			}
+		});
+		auto diff = (microsec_clock::universal_time() - start).total_microseconds();
+		auto s = diff / 1000000.0;
+		if(s == 0) s = 1;
+		cout << endl << Util::formatBytes(total) << ", " << s << " s, " << Util::formatBytes(total / s) << " b/s" << endl;
+	} catch(const std::exception& e) {
+		cout << "Error: " << e.what() << endl;
+	}
+
+	return 0;
+}
+
+
+