Implemented basic std::fs support, reworked time/iterator helpers.

This commit is contained in:
Can Bölük 2020-08-24 04:00:44 +02:00
parent 4b8cecba25
commit 0b316c6cfd
16 changed files with 372 additions and 187 deletions

View file

@ -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 );

View file

@ -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" />

View file

@ -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">

View file

@ -3,3 +3,4 @@
#include "../../io/formatting.hpp"
#include "../../io/logger.hpp"
#include "../../io/enum_name.hpp"
#include "../../io/fileio.hpp"

View file

@ -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
View 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> ) );
}
};

View file

@ -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 ];

View file

@ -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 ( ... ) {}

View file

@ -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;
};

View file

@ -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 );
}

View file

@ -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 )
};
}
};

View file

@ -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
View 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;
}
};

View file

@ -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>

View file

@ -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... ) }; }
};

View file

@ -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;
}
};