mirror of
https://github.com/vtil-project/VTIL-Core
synced 2026-08-17 08:23:03 -04:00
Implemented basic std::fs support, reworked time/iterator helpers.
This commit is contained in:
parent
4b8cecba25
commit
0b316c6cfd
16 changed files with 372 additions and 187 deletions
|
|
@ -31,6 +31,7 @@
|
|||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include "routine.hpp"
|
||||
#include "basic_block.hpp"
|
||||
#include "instruction.hpp"
|
||||
|
|
@ -218,12 +219,12 @@ namespace vtil
|
|||
|
||||
// Simple wrappers for serialize / deserialize routine.
|
||||
//
|
||||
static void save_routine( const routine* rtn, const std::string& path )
|
||||
static void save_routine( const routine* rtn, const std::filesystem::path& path )
|
||||
{
|
||||
std::ofstream fs( path, std::ios::binary );
|
||||
serialize( fs, rtn );
|
||||
}
|
||||
static routine* load_routine( const std::string& path )
|
||||
static routine* load_routine( const std::filesystem::path& path )
|
||||
{
|
||||
routine* rtn;
|
||||
std::ifstream fs( path, std::ios::binary );
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@
|
|||
<ClInclude Include="includes\vtil\utility" />
|
||||
<ClInclude Include="io\asserts.hpp" />
|
||||
<ClInclude Include="io\enum_name.hpp" />
|
||||
<ClInclude Include="io\fileio.hpp" />
|
||||
<ClInclude Include="io\formatting.hpp" />
|
||||
<ClInclude Include="io\logger.hpp" />
|
||||
<ClInclude Include="math\bitwise.hpp" />
|
||||
|
|
@ -147,10 +148,11 @@
|
|||
<ClInclude Include="util\detached_queue.hpp" />
|
||||
<ClInclude Include="util\enumerator.hpp" />
|
||||
<ClInclude Include="util\function_view.hpp" />
|
||||
<ClInclude Include="util\literals.hpp" />
|
||||
<ClInclude Include="util\relaxed_atomics.hpp" />
|
||||
<ClInclude Include="util\time.hpp" />
|
||||
<ClInclude Include="util\trilean.hpp" />
|
||||
<ClInclude Include="util\type_helpers.hpp" />
|
||||
<ClInclude Include="util\profiler.hpp" />
|
||||
<ClInclude Include="util\random.hpp" />
|
||||
<ClInclude Include="util\static_warning.hpp" />
|
||||
<ClInclude Include="util\thread_identifier.hpp" />
|
||||
|
|
|
|||
|
|
@ -145,9 +145,6 @@
|
|||
<ClInclude Include="util\random.hpp">
|
||||
<Filter>Utility</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\profiler.hpp">
|
||||
<Filter>Utility</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\bitmap.hpp">
|
||||
<Filter>Utility</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -181,6 +178,15 @@
|
|||
<ClInclude Include="util\function_view.hpp">
|
||||
<Filter>Utility</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="io\fileio.hpp">
|
||||
<Filter>I/O</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\literals.hpp">
|
||||
<Filter>Utility</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\time.hpp">
|
||||
<Filter>Utility</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="io\logger.cpp">
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@
|
|||
#include "../../io/formatting.hpp"
|
||||
#include "../../io/logger.hpp"
|
||||
#include "../../io/enum_name.hpp"
|
||||
#include "../../io/fileio.hpp"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#include "../../util/multivariate.hpp"
|
||||
#include "../../util/static_warning.hpp"
|
||||
#include "../../util/random.hpp"
|
||||
#include "../../util/profiler.hpp"
|
||||
#include "../../util/time.hpp"
|
||||
#include "../../util/bitmap.hpp"
|
||||
#include "../../util/type_helpers.hpp"
|
||||
#include "../../util/enumerator.hpp"
|
||||
|
|
@ -27,13 +27,4 @@
|
|||
#include "../../util/detached_queue.hpp"
|
||||
#include "../../util/relaxed_atomics.hpp"
|
||||
#include "../../util/function_view.hpp"
|
||||
|
||||
// VTIL namespace should be able to use literals by default without having to include it.
|
||||
//
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
namespace vtil
|
||||
{
|
||||
using namespace std::literals::string_literals;
|
||||
using namespace std::literals::chrono_literals;
|
||||
};
|
||||
#include "../../util/literals.hpp"
|
||||
69
VTIL-Common/io/fileio.hpp
Normal file
69
VTIL-Common/io/fileio.hpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) 2020 Can Boluk and contributors of the VTIL Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. Neither the name of VTIL Project nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "../io/logger.hpp"
|
||||
|
||||
namespace vtil::file
|
||||
{
|
||||
// Declare a simple interface to read/write files for convenience.
|
||||
//
|
||||
static std::vector<uint8_t> read_raw( const std::filesystem::path& path )
|
||||
{
|
||||
// Try to open file as binary for read.
|
||||
//
|
||||
std::ifstream file( path, std::ios::binary );
|
||||
if ( !file.good() ) logger::error( "File %s cannot be opened.", path );
|
||||
|
||||
// Read the whole file and return.
|
||||
//
|
||||
return std::vector<uint8_t>( std::istreambuf_iterator<char>( file ), {} );
|
||||
}
|
||||
|
||||
static void write_raw( const std::filesystem::path& path, void* data, size_t size )
|
||||
{
|
||||
// Try to open file as binary for write.
|
||||
//
|
||||
std::ofstream file( path, std::ios::binary );
|
||||
if ( !file.good() ) logger::error( "File cannot be opened for write." );
|
||||
|
||||
// Write the data and return.
|
||||
//
|
||||
file.write( ( char* ) data, size );
|
||||
}
|
||||
|
||||
template<Iterable T> requires ( is_linear_iterable_v<T> && std::is_trivial_v<iterated_type_t<T>> )
|
||||
static void write_raw( const std::filesystem::path& path, T&& container )
|
||||
{
|
||||
write_raw( path, &*std::begin( container ), std::size( container ) * sizeof( iterated_type_t<T> ) );
|
||||
}
|
||||
};
|
||||
|
|
@ -33,8 +33,10 @@
|
|||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include "../util/lt_typeid.hpp"
|
||||
#include "../util/type_helpers.hpp"
|
||||
#include "../util/time.hpp"
|
||||
#include "enum_name.hpp"
|
||||
|
||||
#ifdef __GNUG__
|
||||
|
|
@ -163,6 +165,10 @@ namespace vtil::format
|
|||
{
|
||||
return enum_name<T>::resolve( x );
|
||||
}
|
||||
else if constexpr ( Duration<T> )
|
||||
{
|
||||
return time::to_string( x );
|
||||
}
|
||||
else if constexpr ( StdStringConvertible<T> )
|
||||
{
|
||||
return std::to_string( x );
|
||||
|
|
@ -176,6 +182,14 @@ namespace vtil::format
|
|||
{
|
||||
return std::string{ x };
|
||||
}
|
||||
else if constexpr ( std::is_same_v<base_type, std::filesystem::directory_entry> )
|
||||
{
|
||||
return x.path().string();
|
||||
}
|
||||
else if constexpr ( std::is_same_v<base_type, std::filesystem::path> )
|
||||
{
|
||||
return x.string();
|
||||
}
|
||||
else if constexpr ( std::is_same_v<base_type, std::wstring> )
|
||||
{
|
||||
return std::string{ x.begin(), x.end() };
|
||||
|
|
@ -184,29 +198,6 @@ namespace vtil::format
|
|||
{
|
||||
return std::string{ x, x + wcslen( x ) };
|
||||
}
|
||||
else if constexpr ( is_specialization_v<std::chrono::duration, base_type> )
|
||||
{
|
||||
static constexpr auto flt2str = [ ] ( float f ) -> std::string
|
||||
{
|
||||
char buffer[ 32 ];
|
||||
snprintf( buffer, 32, "%.2f", f );
|
||||
return buffer;
|
||||
};
|
||||
|
||||
static constexpr std::tuple<base_type, const char*, bool> durations[] =
|
||||
{
|
||||
{ std::chrono::duration_cast<base_type>( std::chrono::hours{ 1 } ), "hrs", false },
|
||||
{ std::chrono::duration_cast<base_type>( std::chrono::minutes{ 1 } ), "min", false },
|
||||
{ std::chrono::duration_cast<base_type>( std::chrono::seconds{ 1 } ), "sec", false },
|
||||
{ std::chrono::duration_cast<base_type>( std::chrono::milliseconds{ 1 } ), "ms", false },
|
||||
{ std::chrono::duration_cast<base_type>( std::chrono::nanoseconds{ 1 } ), "ns", true },
|
||||
};
|
||||
|
||||
for ( auto& [dur, name, last] : durations )
|
||||
if ( last || x > dur )
|
||||
return flt2str( x.count() / float( dur.count() ) ) + name;
|
||||
unreachable();
|
||||
}
|
||||
else if constexpr ( std::is_pointer_v<base_type> )
|
||||
{
|
||||
char buffer[ 17 ];
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <functional>
|
||||
#include "formatting.hpp"
|
||||
#include "../util/intrinsics.hpp"
|
||||
#include "../util/literals.hpp"
|
||||
|
||||
// [Configuration]
|
||||
// Determine which file stream we should use for logging/errors and whether to
|
||||
|
|
@ -106,12 +107,12 @@ namespace vtil::logger
|
|||
void lock() { mtx.lock(); }
|
||||
void unlock() { mtx.unlock(); }
|
||||
bool try_lock() { return mtx.try_lock(); }
|
||||
bool try_lock( uint64_t milliseconds )
|
||||
bool try_lock( timeunit_t max_wait )
|
||||
{
|
||||
bool locked = false;
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t0 = time::now();
|
||||
while ( !( locked = try_lock() ) )
|
||||
if ( ( std::chrono::steady_clock::now() - t0 ) > std::chrono::milliseconds( milliseconds ) )
|
||||
if ( ( time::now() - t0 ) > max_wait )
|
||||
break;
|
||||
return locked;
|
||||
}
|
||||
|
|
@ -253,7 +254,7 @@ namespace vtil::logger
|
|||
|
||||
// Try acquiring the lock.
|
||||
//
|
||||
bool locked = logger_state.try_lock( 100 );
|
||||
bool locked = logger_state.try_lock( 100ms );
|
||||
|
||||
// Print the warning.
|
||||
//
|
||||
|
|
@ -288,7 +289,7 @@ namespace vtil::logger
|
|||
|
||||
// Try acquiring the lock.
|
||||
//
|
||||
bool locked = logger_state.try_lock( 100 );
|
||||
bool locked = logger_state.try_lock( 100ms );
|
||||
|
||||
// Print the error message.
|
||||
//
|
||||
|
|
@ -317,7 +318,7 @@ namespace vtil::logger
|
|||
std::string message = format::as_string( e );
|
||||
set_color( CON_RED );
|
||||
fprintf( VTIL_LOGGER_ERR_DST, "\n[*] Error: %s\n", message.c_str() );
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 500 ) );
|
||||
sleep_for( 1000ms );
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,44 +27,12 @@
|
|||
//
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
|
||||
namespace vtil
|
||||
{
|
||||
// Times the callable given and returns pair [result, duration] if it has
|
||||
// a return value or just [duration].
|
||||
// VTIL namespace should be able to use literals by default without having to include it.
|
||||
//
|
||||
template<typename T, typename... Tx>
|
||||
static auto profile( T&& f, Tx&&... args )
|
||||
{
|
||||
using result_t = decltype( std::declval<T>()( std::forward<Tx>( args )... ) );
|
||||
|
||||
if constexpr ( std::is_same_v<result_t, void> )
|
||||
{
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
f( std::forward<Tx>( args )... );
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
return t1 - t0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
result_t res = f();
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
return std::make_pair( res, t1 - t0 );
|
||||
}
|
||||
}
|
||||
|
||||
// Same as ::profile but ignores the return value and runs N times.
|
||||
//
|
||||
template<size_t N, typename T, typename... Tx>
|
||||
static auto profile_n( T&& f, Tx&&... args )
|
||||
{
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
for ( size_t i = 0; i != N; i++ )
|
||||
f( args... ); // Not forwarded since we can't move N times.
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
return t1 - t0;
|
||||
}
|
||||
using namespace std::literals::string_literals;
|
||||
using namespace std::literals::chrono_literals;
|
||||
};
|
||||
|
|
@ -107,7 +107,7 @@ namespace vtil
|
|||
template<Iterable T>
|
||||
static decltype( auto ) pick_randomi( T&& source )
|
||||
{
|
||||
auto size = dynamic_size( source );
|
||||
auto size = std::size( source );
|
||||
fassert( size != 0 );
|
||||
return dynamic_get( source, make_random<size_t>( 0, size - 1 ) );
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ namespace vtil
|
|||
template<size_t offset = 0, Iterable T>
|
||||
static constexpr decltype( auto ) pick_crandomi( T& source )
|
||||
{
|
||||
auto size = dynamic_size( source );
|
||||
auto size = std::size( source );
|
||||
fassert( size != 0 );
|
||||
return dynamic_get( source, make_crandom( offset ) % size );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
//
|
||||
#pragma once
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
namespace vtil
|
||||
{
|
||||
|
|
@ -39,11 +39,15 @@ namespace vtil
|
|||
|
||||
constexpr iterator_type begin() const { return ibegin; }
|
||||
constexpr iterator_type end() const { return iend; }
|
||||
constexpr size_t size() const { return ( size_t ) std::distance( begin(), end() ); }
|
||||
};
|
||||
|
||||
template<typename iterator_type>
|
||||
static constexpr auto make_range( iterator_type begin, iterator_type end )
|
||||
static constexpr auto make_range( iterator_type&& begin, iterator_type&& end )
|
||||
{
|
||||
return range_t<iterator_type>{ std::move( begin ), std::move( end ) };
|
||||
return range_t<iterator_type>{
|
||||
std::forward<iterator_type>( begin ),
|
||||
std::forward<iterator_type>( end )
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
#pragma once
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include "../io/asserts.hpp"
|
||||
#include "type_helpers.hpp"
|
||||
|
||||
namespace vtil
|
||||
{
|
||||
|
|
@ -45,38 +45,34 @@ namespace vtil
|
|||
|
||||
// Constructed by the original iterator type and the limit.
|
||||
//
|
||||
reversed_iterator( const iterator& i, const iterator& limit )
|
||||
constexpr reversed_iterator( const iterator& i, const iterator& limit )
|
||||
: iterator( i ), limit( limit ) {}
|
||||
reversed_iterator( iterator&& i, iterator&& limit )
|
||||
constexpr reversed_iterator( iterator&& i, iterator&& limit )
|
||||
: iterator( std::move( i ) ), limit( std::move( limit ) ) {}
|
||||
|
||||
// Default copy/move.
|
||||
//
|
||||
reversed_iterator( reversed_iterator&& ) = default;
|
||||
reversed_iterator( const reversed_iterator& ) = default;
|
||||
reversed_iterator& operator=( reversed_iterator&& ) = default;
|
||||
reversed_iterator& operator=( const reversed_iterator& ) = default;
|
||||
constexpr reversed_iterator( reversed_iterator&& ) = default;
|
||||
constexpr reversed_iterator( const reversed_iterator& ) = default;
|
||||
constexpr reversed_iterator& operator=( reversed_iterator&& ) = default;
|
||||
constexpr reversed_iterator& operator=( const reversed_iterator& ) = default;
|
||||
|
||||
// Reverts back to a normal iterator.
|
||||
//
|
||||
iterator& revert() { return *this; }
|
||||
const iterator& revert() const { return *this; }
|
||||
constexpr iterator& revert() { return *this; }
|
||||
constexpr const iterator& revert() const { return *this; }
|
||||
|
||||
// Reverse inc/dec.
|
||||
//
|
||||
reversed_iterator& operator--()
|
||||
constexpr reversed_iterator& operator--()
|
||||
{
|
||||
fassert( !at_limit );
|
||||
|
||||
// Invoke inc, make sure it returns a reference and return self.
|
||||
//
|
||||
auto& _ = iterator::operator++();
|
||||
return *this;
|
||||
}
|
||||
reversed_iterator& operator++()
|
||||
constexpr reversed_iterator& operator++()
|
||||
{
|
||||
fassert( !at_limit );
|
||||
|
||||
// If equal to the limit, set limit and return as is.
|
||||
//
|
||||
if ( operator==( limit ) )
|
||||
|
|
@ -90,44 +86,55 @@ namespace vtil
|
|||
auto& _ = iterator::operator--();
|
||||
return *this;
|
||||
}
|
||||
constexpr reversed_iterator operator++( int ) { auto s = *this; operator--(); return s; }
|
||||
constexpr reversed_iterator operator--( int ) { auto s = *this; operator++(); return s; }
|
||||
|
||||
// Implement (not-)equals operator with the special end tag.
|
||||
//
|
||||
bool operator==( reversed_iterator_end_tag ) const { return at_limit; }
|
||||
bool operator!=( reversed_iterator_end_tag ) const { return !at_limit; }
|
||||
constexpr bool operator==( reversed_iterator_end_tag ) const { return at_limit; }
|
||||
constexpr bool operator!=( reversed_iterator_end_tag ) const { return !at_limit; }
|
||||
|
||||
// Inherit rest from operator base.
|
||||
//
|
||||
using iterator::operator==;
|
||||
using iterator::operator!=;
|
||||
using iterator::operator->;
|
||||
using iterator::operator*;
|
||||
};
|
||||
|
||||
// Returns a tuple that behaves equivalent to .rbegin and .rend.
|
||||
//
|
||||
template<typename T>
|
||||
static auto reverse_iterators( T& cont )
|
||||
template<Iterable T>
|
||||
static constexpr auto reverse_iterators( T&& cont )
|
||||
{
|
||||
using iterator_type = decltype( cont.begin() );
|
||||
return std::make_tuple(
|
||||
reversed_iterator<iterator_type>{ std::prev( cont.end() ), cont.begin() },
|
||||
return std::pair {
|
||||
reversed_iterator<iterator_type>{ std::prev( std::end( cont ) ), std::begin( cont ) },
|
||||
reversed_iterator_end_tag{}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// Reverses entire container iteration.
|
||||
//
|
||||
template<typename T>
|
||||
struct reversed_container_proxy
|
||||
namespace impl
|
||||
{
|
||||
T& proxy;
|
||||
decltype( auto ) begin() { return proxy.rbegin(); }
|
||||
decltype( auto ) end() { return proxy.rend(); }
|
||||
template<Iterable T>
|
||||
struct reversed_container_proxy
|
||||
{
|
||||
T container;
|
||||
decltype( auto ) begin() { return reverse_iterators( container ).first; }
|
||||
decltype( auto ) end() { return reverse_iterators( container ).second; }
|
||||
};
|
||||
|
||||
template<ReverseIterable T>
|
||||
struct reversed_container_proxy<T>
|
||||
{
|
||||
T container;
|
||||
decltype( auto ) begin() { return std::rbegin( container ); }
|
||||
decltype( auto ) end() { return std::rend( container ); }
|
||||
};
|
||||
};
|
||||
template<typename T>
|
||||
static constexpr auto backwards( T& cont )
|
||||
template<Iterable T>
|
||||
static constexpr auto backwards( T&& container )
|
||||
{
|
||||
return reversed_container_proxy<T>{ cont };
|
||||
return impl::reversed_container_proxy<T>{ std::forward<T>( container ) };
|
||||
}
|
||||
};
|
||||
144
VTIL-Common/util/time.hpp
Normal file
144
VTIL-Common/util/time.hpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright (c) 2020 Can Boluk and contributors of the VTIL Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. Neither the name of VTIL Project nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "literals.hpp"
|
||||
#include "type_helpers.hpp"
|
||||
#include "zip.hpp"
|
||||
#include "reverse_iterator.hpp"
|
||||
|
||||
// No-bloat chrono interface with some helpers and a profiler.
|
||||
//
|
||||
namespace vtil
|
||||
{
|
||||
namespace time
|
||||
{
|
||||
// Declare basic units.
|
||||
//
|
||||
using hours = std::chrono::hours;
|
||||
using minutes = std::chrono::minutes;
|
||||
using seconds = std::chrono::seconds;
|
||||
using milliseconds = std::chrono::milliseconds;
|
||||
using nanoseconds = std::chrono::nanoseconds;
|
||||
using unit_t = nanoseconds;
|
||||
|
||||
using basic_units = std::tuple< nanoseconds, milliseconds, seconds, minutes, hours >;
|
||||
static constexpr std::array basic_unit_names = { "nanoseconds", "milliseconds", "seconds", "minutes", "hours" };
|
||||
static constexpr std::array basic_unit_abbreviations = { "ns", "ms", "sec", "min", "hrs" };
|
||||
static constexpr std::array basic_unit_durations = make_constant_series<std::tuple_size_v<basic_units>>( [ ] ( auto x )
|
||||
{
|
||||
return std::chrono::duration_cast<unit_t>( std::tuple_element_t<decltype(x)::value, basic_units>( 1 ) );
|
||||
} );
|
||||
|
||||
// Declare prefered clock and units.
|
||||
//
|
||||
using base_clock = std::chrono::steady_clock;
|
||||
using stamp_t = base_clock::time_point;
|
||||
|
||||
// Wrap around base clock.
|
||||
//
|
||||
static stamp_t now() { return base_clock::now(); }
|
||||
|
||||
// Declare conversion to string.
|
||||
//
|
||||
template<Duration T>
|
||||
static std::string to_string( T duration )
|
||||
{
|
||||
// Convert to unit time.
|
||||
//
|
||||
unit_t t = std::chrono::duration_cast<unit_t>( duration );
|
||||
|
||||
// Iterate duration list in descending order.
|
||||
//
|
||||
for ( auto [duration, abbrv] : backwards( zip( time::basic_unit_durations, time::basic_unit_abbreviations ) ) )
|
||||
{
|
||||
// If time is larger than the duration given or if we're at the last duration:
|
||||
//
|
||||
if ( t > duration || duration == *std::begin( time::basic_unit_durations ) )
|
||||
{
|
||||
// Convert float to string.
|
||||
//
|
||||
char buffer[ 32 ];
|
||||
snprintf( buffer, 32, "%.2lf%s", t.count() / double( duration.count() ), abbrv );
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
};
|
||||
using timestamp_t = time::stamp_t;
|
||||
using timeunit_t = time::unit_t;
|
||||
|
||||
// Wrappers around std::this_thread::sleep_*.
|
||||
//
|
||||
template<Duration T>
|
||||
static void sleep_for( T&& d ) { std::this_thread::sleep_for( std::forward<T>( d ) ); }
|
||||
template<Timestamp T>
|
||||
static void sleep_until( T&& d ) { std::this_thread::sleep_until( std::forward<T>( d ) ); }
|
||||
|
||||
// Times the callable given and returns pair [result, duration] if it has
|
||||
// a return value or just [duration].
|
||||
//
|
||||
template<typename T, typename... Tx> requires InvocableWith<T, Tx...>
|
||||
static auto profile( T&& f, Tx&&... args )
|
||||
{
|
||||
using result_t = decltype( std::declval<T>()( std::forward<Tx>( args )... ) );
|
||||
|
||||
if constexpr ( std::is_same_v<result_t, void> )
|
||||
{
|
||||
timestamp_t t0 = time::now();
|
||||
f( std::forward<Tx>( args )... );
|
||||
timestamp_t t1 = time::now();
|
||||
return t1 - t0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
timestamp_t t0 = time::now();
|
||||
result_t res = f();
|
||||
timestamp_t t1 = time::now();
|
||||
return std::make_pair( res, t1 - t0 );
|
||||
}
|
||||
}
|
||||
|
||||
// Same as ::profile but ignores the return value and runs N times.
|
||||
//
|
||||
template<size_t N, typename T, typename... Tx> requires InvocableWith<T, Tx...>
|
||||
static timeunit_t profile_n( T&& f, Tx&&... args )
|
||||
{
|
||||
auto t0 = time::now();
|
||||
for ( size_t i = 0; i != N; i++ )
|
||||
f( args... ); // Not forwarded since we can't move N times.
|
||||
auto t1 = time::now();
|
||||
return t1 - t0;
|
||||
}
|
||||
};
|
||||
|
|
@ -34,6 +34,9 @@
|
|||
#include <string_view>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
#include <chrono>
|
||||
#include "intrinsics.hpp"
|
||||
|
||||
namespace vtil
|
||||
|
|
@ -93,6 +96,17 @@ namespace vtil
|
|||
template <template<typename...> typename Tmp, typename T>
|
||||
static constexpr bool is_specialization_v = impl::is_specialization_v<Tmp, std::remove_cvref_t<T>>;
|
||||
|
||||
// Check whether data is stored linearly in the iterable.
|
||||
//
|
||||
template<typename T>
|
||||
static constexpr bool is_linear_iterable_v =
|
||||
(
|
||||
is_specialization_v<std::vector, T> ||
|
||||
is_specialization_v<std::basic_string, T> ||
|
||||
is_specialization_v<std::initializer_list, T> ||
|
||||
std::is_array_v<T&>
|
||||
);
|
||||
|
||||
// Checks if the given lambda can be evaluated in compile time.
|
||||
//
|
||||
template<typename F, std::enable_if_t<(F{}(), true), int> = 0>
|
||||
|
|
@ -135,9 +149,13 @@ namespace vtil
|
|||
|
||||
template<typename T, typename Ret, typename... Args>
|
||||
concept Invocable = requires( T&& x, Args&&... args ) { Convertible<decltype( x( std::forward<Args>( args )... ) ), Ret>; };
|
||||
template<typename T, typename... Args>
|
||||
concept InvocableWith = requires( T&& x, Args&&... args ) { x( std::forward<Args>( args )... ); };
|
||||
|
||||
template<typename T>
|
||||
concept Iterable = requires( T v ) { std::begin( v ); std::end( v ); };
|
||||
template<typename T>
|
||||
concept ReverseIterable = requires( T v ) { std::rbegin( v ); std::rend( v ); };
|
||||
template<typename V, typename T>
|
||||
concept TypedIterable = Iterable<T> && requires( T v ) { Convertible<decltype( *std::begin( v ) ), V&>; };
|
||||
|
||||
|
|
@ -153,6 +171,16 @@ namespace vtil
|
|||
template<typename T>
|
||||
concept Atomic = is_specialization_v<std::atomic, T>;
|
||||
|
||||
template<typename T>
|
||||
concept Duration = is_specialization_v<std::chrono::duration, T>;
|
||||
template<typename T>
|
||||
concept Timestamp = is_specialization_v<std::chrono::time_point, T>;
|
||||
|
||||
// Type of the iterated value.
|
||||
//
|
||||
template<Iterable T>
|
||||
using iterated_type_t = std::remove_cvref_t<decltype( *std::begin( std::declval<T>() ) )>;
|
||||
|
||||
// Constructs a static constant given the type and parameters, returns a reference to it.
|
||||
//
|
||||
namespace impl
|
||||
|
|
@ -379,31 +407,6 @@ namespace vtil
|
|||
template<typename T>
|
||||
concept Possessable = !std::is_void_v<decltype( possess_value( std::declval<T&>() ) )>;
|
||||
|
||||
// Gets the size of the given container, 0 if N/A.
|
||||
//
|
||||
template<typename T>
|
||||
static constexpr size_t dynamic_size( T&& o )
|
||||
{
|
||||
if constexpr ( DefaultRandomAccessible<T> )
|
||||
return std::size( o );
|
||||
else if constexpr ( CustomRandomAccessible<T> )
|
||||
return o.size();
|
||||
else if constexpr ( Iterable<T> )
|
||||
return std::distance( std::begin( o ), std::end( o ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Gets the Nth element from the object, void if N/A.
|
||||
//
|
||||
template<typename T>
|
||||
static constexpr decltype( auto ) dynamic_get( T&& o, size_t N )
|
||||
{
|
||||
if constexpr( RandomAccessible<T> )
|
||||
return o[ N ];
|
||||
else if constexpr ( Iterable<T> )
|
||||
return *std::next( std::begin( o ), N );
|
||||
}
|
||||
|
||||
// Bitcasting.
|
||||
//
|
||||
template<TriviallyCopyable To, TriviallyCopyable From>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace vtil
|
|||
{
|
||||
constexpr decltype( auto ) operator()( T& o, size_t N ) const
|
||||
{
|
||||
return o[ N % dynamic_size( o ) ];
|
||||
return o[ N % std::size( o ) ];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -69,9 +69,9 @@ namespace vtil
|
|||
constexpr auto operator()( T& o, size_t N ) const
|
||||
{
|
||||
if constexpr ( std::is_reference_v<o[ N ]> )
|
||||
return dereference_if_n( N < dynamic_size( o ), std::begin( o ), N );
|
||||
return dereference_if_n( N < std::size( o ), std::begin( o ), N );
|
||||
else
|
||||
return N < dynamic_size( o ) ? std::optional{ o[ N ] } : std::nullopt;
|
||||
return N < std::size( o ) ? std::optional{ o[ N ] } : std::nullopt;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -81,84 +81,84 @@ namespace vtil
|
|||
{
|
||||
// Declare the entry type.
|
||||
//
|
||||
using value_type = std::tuple<decltype( accessor<Tx>{}( std::declval<Tx&>(), 0 ) )... >;
|
||||
using entry_type = std::tuple<decltype( accessor<Tx>{}( std::declval<Tx&>(), 0 ) )... >;
|
||||
|
||||
// Declare the iterator type.
|
||||
//
|
||||
struct iterator_end_tag_t {};
|
||||
struct iterator
|
||||
struct base_iterator
|
||||
{
|
||||
// Generic iterator typedefs.
|
||||
//
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using difference_type = size_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
using difference_type = int;
|
||||
using value_type = entry_type*;
|
||||
using pointer = entry_type*;
|
||||
using reference = entry_type&;
|
||||
|
||||
// Self reference.
|
||||
// Self reference and the index.
|
||||
//
|
||||
const joint_container& container;
|
||||
|
||||
// Range of iteration.
|
||||
//
|
||||
size_t index;
|
||||
size_t limit;
|
||||
|
||||
// Default constructor.
|
||||
//
|
||||
iterator( const joint_container& container, size_t index = 0 ) :
|
||||
container( container ), index( index ), limit( container.size() ) {}
|
||||
base_iterator( const joint_container& container, size_t index = 0 ) :
|
||||
container( container ), index( index ) {}
|
||||
|
||||
// Support bidirectional iteration.
|
||||
//
|
||||
constexpr iterator& operator++() { index++; return *this; }
|
||||
constexpr iterator& operator--() { index--; return *this; }
|
||||
constexpr base_iterator& operator++() { ++index; return *this; }
|
||||
constexpr base_iterator& operator--() { --index; return *this; }
|
||||
constexpr base_iterator operator++( int ) { auto s = *this; operator--(); return s; }
|
||||
constexpr base_iterator operator--( int ) { auto s = *this; operator++(); return s; }
|
||||
|
||||
// Equality check against another iterator.
|
||||
//
|
||||
constexpr bool operator==( const iterator& other ) const { return index == other.index && &container == &other.container; }
|
||||
constexpr bool operator!=( const iterator& other ) const { return index != other.index || &container != &other.container; }
|
||||
|
||||
// Equality check against special end iterator.
|
||||
//
|
||||
constexpr bool operator==( iterator_end_tag_t ) const { return index == limit; }
|
||||
constexpr bool operator!=( iterator_end_tag_t ) const { return index != limit; }
|
||||
constexpr bool operator==( const base_iterator& other ) const { return index == other.index && &container == &other.container; }
|
||||
constexpr bool operator!=( const base_iterator& other ) const { return index != other.index || &container != &other.container; }
|
||||
|
||||
// Redirect dereferencing to container.
|
||||
//
|
||||
constexpr value_type operator*() const { return container.at( index ); }
|
||||
constexpr entry_type operator*() const { return container.at( index ); }
|
||||
};
|
||||
using const_iterator = iterator;
|
||||
using iterator = base_iterator;
|
||||
using const_iterator = base_iterator;
|
||||
|
||||
// Tuple containing data sources.
|
||||
//
|
||||
std::tuple<Tx&...> sources;
|
||||
size_t size_0;
|
||||
|
||||
constexpr joint_container( std::tuple<Tx&...>&& source )
|
||||
: sources( std::move( source ) ), size_0( std::size( std::get<0>( sources ) ) ) {}
|
||||
|
||||
// Declare random access helper.
|
||||
//
|
||||
template<size_t... I>
|
||||
constexpr value_type at( size_t idx, std::index_sequence<I...> ) const
|
||||
constexpr entry_type at( size_t idx, std::index_sequence<I...> ) const
|
||||
{
|
||||
return { accessor<Tx>{}( std::get<I>( sources ), idx )... };
|
||||
}
|
||||
constexpr value_type at( size_t idx ) const
|
||||
constexpr entry_type at( size_t idx ) const
|
||||
{
|
||||
return at( idx, std::index_sequence_for<Tx...>{} );
|
||||
}
|
||||
|
||||
// Generic container helpers.
|
||||
//
|
||||
constexpr size_t size() const { return dynamic_size( std::get<0>( sources ) ); }
|
||||
constexpr size_t size() const { return size_0; }
|
||||
constexpr iterator begin() const { return { *this, 0 }; }
|
||||
constexpr iterator_end_tag_t end() const { return {}; }
|
||||
constexpr iterator end() const { return { *this, size_0 }; }
|
||||
};
|
||||
|
||||
// Simple joint container creation from wrappers.
|
||||
//
|
||||
template <typename... Tx>
|
||||
template <typename... Tx> requires ( Iterable<Tx&> && ... )
|
||||
static constexpr auto zip_s( Tx&... args ) -> joint_container<impl::optref_wrapper, Tx...> { return { std::tie( args... ) }; }
|
||||
template <typename... Tx>
|
||||
|
||||
template <typename... Tx> requires ( Iterable<Tx&> && ... )
|
||||
static constexpr auto zip_c( Tx&... args ) -> joint_container<impl::modref_wrapper, Tx...> { return { std::tie( args... ) }; }
|
||||
template <typename... Tx>
|
||||
|
||||
template <typename... Tx> requires ( Iterable<Tx&> && ... )
|
||||
static constexpr auto zip( Tx&... args ) -> joint_container<impl::doref_wrapper, Tx...> { return { std::tie( args... ) }; }
|
||||
};
|
||||
|
|
@ -115,7 +115,7 @@ namespace vtil::optimizer
|
|||
template<Iterable C, typename F> requires Invocable<F, void, decltype( *std::begin( std::declval<C&>() ) )>
|
||||
static void transform_parallel( C&& container, const F& worker )
|
||||
{
|
||||
size_t container_size = dynamic_size( container );
|
||||
size_t container_size = std::size( container );
|
||||
|
||||
// If parallel transformation is disabled or if the container only has one entry,
|
||||
// fallback to serial transformation.
|
||||
|
|
@ -445,21 +445,18 @@ namespace vtil::optimizer
|
|||
{
|
||||
if ( !xblock )
|
||||
logger::log( "Block %08x => %-64s |", blk->entry_vip, T{}.name() );
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
size_t cnt = T::pass( blk, xblock );
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
auto [cnt, time] = profile( [ & ] () { return T::pass( blk, xblock ); } );
|
||||
if ( !xblock )
|
||||
logger::log( " Took %-8.2fms (N=%d).\n", ( t1 - t0 ).count() * 1e-6f, cnt );
|
||||
logger::log( " Took %-10s (N=%d).\n", time, cnt );
|
||||
return cnt;
|
||||
}
|
||||
|
||||
size_t xpass( routine* rtn ) override
|
||||
{
|
||||
logger::log( "Routine => %-64s |", T{}.name() );
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
size_t cnt = T::xpass( rtn );
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
logger::log( " Took %-8.2fms (N=%d).\n", ( t1 - t0 ).count() * 1e-6f, cnt );
|
||||
auto [cnt, time] = profile( [ & ] () { return T::xpass( rtn ); } );
|
||||
logger::log( " Took %-10s (N=%d).\n", time, cnt );
|
||||
return cnt;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue