Implemented symbolic::context for fast bitwise access to registers.

This commit is contained in:
Can Bölük 2020-07-24 07:02:06 +02:00
parent 22a6aca8a7
commit 565b8b43ab
5 changed files with 536 additions and 231 deletions

View file

@ -10,6 +10,7 @@
#include "../../routine/instruction.hpp"
#include "../../routine/serialization.hpp"
#include "../../symex/memory.hpp"
#include "../../symex/context.hpp"
#include "../../symex/pointer.hpp"
#include "../../symex/variable.hpp"
#include "../../symex/translation.hpp"

View file

@ -0,0 +1,188 @@
// 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.
//
#include <vtil/math>
#include "context.hpp"
namespace vtil::symbolic
{
// Checks if the symbolic context contains any writes to the given region described by the register desc.
//
bool context::contains( const register_desc& desc ) const
{
// If identifier is not in the store, return false.
//
auto it = value_map.find( desc );
if ( it == value_map.end() )
return false;
// Enumerate each bit set within (size+offset, 0]:
//
bool found = false;
math::bit_enum( it->second.bitmap & math::fill( desc.bit_count + desc.bit_offset ), [ & ] ( bitcnt_t i )
{
// If value extends into the region, declare found.
//
const expression::reference& value = it->second.linear_store[ i ];
if ( ( value.size() + i ) > desc.bit_offset )
found = true;
} );
return found;
}
// Reads the value of the given region described by the register desc.
//
expression::reference context::read( const register_desc& desc, const il_const_iterator& reference_iterator ) const
{
// If identifier is not in the store, return default.
//
auto it = value_map.find( desc );
if ( it == value_map.end() )
return CTX( reference_iterator )[ desc ];
// Allocate storage for result and create masks.
//
uint64_t known_mask = 0;
uint64_t read_mask = desc.get_mask();
expression::reference result = nullptr;
// Enumerate each bit set within (size+offset, 0]:
//
math::bit_enum( it->second.bitmap & math::fill( desc.bit_count + desc.bit_offset ), [ & ] ( bitcnt_t i )
{
// If value extends into the region:
//
const expression::reference& value = it->second.linear_store[ i ];
if ( ( value.size() + i ) > desc.bit_offset )
{
// Set known mask.
//
known_mask |= math::fill( value.size(), i );
// Adjust the value.
//
expression::reference adjusted = value;
if ( i > desc.bit_offset ) adjusted.resize( desc.bit_count ) <<= ( i - desc.bit_offset );
else if ( i < desc.bit_offset ) adjusted >>= ( desc.bit_offset - i ), adjusted.resize( desc.bit_count );
else adjusted.resize( desc.bit_count );
// Append to the result.
//
if ( result ) result |= std::move( adjusted );
else result = std::move( adjusted );
}
} );
// If no bits set in known mask, return default.
//
if ( !known_mask )
return CTX( reference_iterator )[ desc ];
// If all bits set in known mask, return as is.
//
if ( ( known_mask & read_mask ) == read_mask )
return result;
// Or with the bits that we do not know and return.
//
return result | ( variable{ reference_iterator, desc.select( 64, 0 ) }.to_expression() & ( read_mask & ~known_mask ) ) >> desc.bit_offset;
}
// Writes the given value to the region described by the register desc.
//
void context::write( const register_desc& desc, expression::reference value )
{
// Find the register in the map and determine limit of the descriptor.
//
auto& context = value_map[ desc ];
bitcnt_t reg_end = desc.bit_count + desc.bit_offset;
// Push left (size+offset, offset].
//
math::bit_enum( context.bitmap & desc.get_mask(), [ & ] ( bitcnt_t i )
{
// Reset the bit, and move the value.
//
expression::reference stored_value = std::exchange( context.linear_store[ i ], nullptr );
bitcnt_t value_end = stored_value.size() + i;
math::bit_reset( context.bitmap, i );
// If value extends beyond the region we're overwriting:
//
if ( value_end > reg_end )
{
// Shift the value and place it at the border.
//
auto& ref = context.linear_store[ reg_end ];
dassert( !ref );
ref = std::move( stored_value ) >> ( reg_end - i );
ref.resize( value_end - reg_end );
math::bit_set( context.bitmap, reg_end );
}
} );
// Push right (offset, 0].
//
math::bit_enum( context.bitmap & math::fill( desc.bit_offset, 0 ), [ & ] ( bitcnt_t i )
{
// If value extends into the region we're overwriting:
//
expression::reference& stored_value = context.linear_store[ i ];
bitcnt_t value_end = stored_value.size() + i;
if ( value_end > desc.bit_offset )
{
// If value extends beyond the region we're overwriting:
//
if ( value_end > reg_end )
{
auto& ext = context.linear_store[ reg_end ];
dassert( !ext );
ext = stored_value >> ( reg_end - i );
ext.resize( value_end - reg_end );
math::bit_set( context.bitmap, reg_end );
}
// Resize the value.
//
stored_value.resize( desc.bit_offset - i );
}
} );
// Write the value.
//
value.resize( desc.bit_count );
auto& res = context.linear_store[ desc.bit_offset ];
dassert( !res );
res = std::move( value );
math::bit_set( context.bitmap, desc.bit_offset );
}
};

View file

@ -0,0 +1,80 @@
// 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 <vtil/utility>
#include <unordered_map>
#include "variable.hpp"
#include "../arch/register_desc.hpp"
namespace vtil::symbolic
{
struct context
{
// Common typedefs.
//
struct segmented_value
{
symbolic::expression::reference linear_store[ 64 ] = { nullptr };
uint64_t bitmap = 0;
};
using store_type = std::unordered_map<register_desc::weak_id, segmented_value, hasher<>>;
// The register state.
//
store_type value_map;
// Default copy/move/construct.
//
context() = default;
context( context&& ) = default;
context( const context& ) = default;
context& operator=( context&& ) = default;
context& operator=( const context& ) = default;
// Wrap around the store type.
//
auto begin() { return value_map.begin(); }
auto end() { return value_map.end(); }
auto begin() const { return value_map.cbegin(); }
auto end() const { return value_map.cend(); }
size_t size() const { return value_map.size(); }
void reset() { value_map.clear(); }
// Checks if the symbolic context contains any writes to the given region described by the register desc.
//
bool contains( const register_desc& desc ) const;
// Reads the value of the given region described by the register desc.
//
expression::reference read( const register_desc& desc, const il_const_iterator& reference_iterator = symbolic::free_form_iterator ) const;
// Writes the given value to the region described by the register desc.
//
void write( const register_desc& desc, expression::reference value );
};
};

View file

@ -0,0 +1,259 @@
// 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.
//
#include <vtil/math>
#include "memory.hpp"
namespace vtil::symbolic
{
// Checks if the symbolic memory contains any writes to the given memory region.
//
trilean memory::contains( const pointer& ptr, bitcnt_t size ) const
{
uint64_t mask_value = math::fill( size );
// For each entry, iterating backwards:
//
for ( auto it = value_map.rbegin(); it != value_map.rend(); it++ )
{
// If pointer cannot overlap lookup, skip.
//
if ( !it->first.can_overlap( ptr ) )
continue;
// Calculate displacement, if unknown return unknown.
//
std::optional byte_distance = it->first - ptr;
if ( !byte_distance )
return trilean::unknown;
// Calculate relative mask, return true if overlapping.
//
bitcnt_t bit_distance = math::narrow_cast< bitcnt_t >( *byte_distance * 8 );
uint64_t relative_mask = math::fill( it->second.size(), bit_distance );
if ( relative_mask & mask_value )
return true;
}
// None found, return false.
//
return false;
}
// Reads N bits from the given pointer, returns null reference if alias failure occurs.
//
expression::reference memory::read( const pointer& ptr, bitcnt_t size, const il_const_iterator& reference_iterator ) const
{
uint64_t mask_pending = math::fill( size );
stack_vector<std::pair<bitcnt_t, expression::reference>, 8> merge_list;
// For each entry, iterating backwards:
//
for ( auto it = value_map.rbegin(); it != value_map.rend() && mask_pending; it++ )
{
// If pointer cannot overlap lookup, skip.
//
if ( !it->first.can_overlap( ptr ) )
continue;
// Calculate displacement, if unknown:
//
std::optional byte_distance = it->first - ptr;
if ( !byte_distance )
{
// If not relaxed aliasing, indicate alias failure by returning null.
//
if ( !relaxed_aliasing )
return nullptr;
// Otherwise, return default value, cannot be determined.
//
merge_list.clear();
break;
}
// Calculate relative mask, skip if not overlapping.
//
bitcnt_t bit_distance = math::narrow_cast<bitcnt_t>( *byte_distance * 8 );
uint64_t relative_mask = math::fill( it->second.size(), bit_distance );
if ( !( relative_mask & mask_pending ) )
continue;
// Add into merge list, clear the mask.
//
merge_list.emplace_back( bit_distance, it->second );
mask_pending &= ~relative_mask;
}
// If no overlapping keys found, return default.
//
if ( merge_list.empty() )
return MEMORY( reference_iterator )( ptr, size );
// Declare common bit selector.
//
constexpr auto select = [ ] ( symbolic::expression::reference& value, bitcnt_t size, bitcnt_t offset )
{
if ( offset < 0 ) value >>= -offset, value.resize( size );
else if ( offset > 0 ) value.resize( size ) <<= offset;
else value.resize( size );
};
// If single overlapping key with no pending bits, return as is.
//
if ( !mask_pending && merge_list.size() == 1 )
{
auto&& [dst, value] = std::move( merge_list[ 0 ] );
select( value, size, dst );
return value;
}
// Merge all in a single expression and return.
//
expression::reference result = mask_pending
? MEMORY( reference_iterator )( ptr, size )
: expression{ 0, size };
for ( auto& [dst, value] : merge_list )
{
select( value, size, dst );
result |= std::move( value );
}
return result;
}
// Writes the given value to the pointer, returns null reference if alias failure occurs.
//
optional_reference<expression::reference> memory::write( const pointer& ptr, deferred_value<expression::reference> value, bitcnt_t size )
{
uint64_t mask_pending = math::fill( size );
stack_vector<std::pair<bitcnt_t, store_type::iterator>, 8> acquisition_list;
// For each entry, iterating backwards:
//
for ( auto it = value_map.rbegin(); it != value_map.rend() && mask_pending; it++ )
{
// If pointer cannot overlap lookup, skip.
//
if ( !it->first.can_overlap( ptr ) )
continue;
// Calculate displacement, if unknown:
//
std::optional byte_distance = it->first - ptr;
if ( !byte_distance )
{
// If not relaxed aliasing, indicate alias failure by returning null.
//
if ( !relaxed_aliasing )
return std::nullopt;
// Otherwise, insert at the end, overlaps can't be determined.
//
acquisition_list.clear();
break;
}
// Calculate relative mask, skip if not overlapping.
//
bitcnt_t bit_distance = math::narrow_cast<bitcnt_t>( *byte_distance * 8 );
uint64_t relative_mask = math::fill( it->second.size(), bit_distance );
if ( !( relative_mask & mask_pending ) )
continue;
// Add into acquisition list, clear the mask.
//
acquisition_list.emplace_back( bit_distance, std::prev( it.base() ) );
mask_pending &= ~relative_mask;
}
// For each iterator we should acquire bits from:
//
for ( auto& [dst, it] : acquisition_list )
{
// If low bits start at or above our pointer:
// | v v v v | v v v v |
// | a b c d ... | a b c d ... |
//
if ( dst >= 0 )
{
bitcnt_t strip_low_cnt = size - dst;
bitcnt_t new_size = it->second->size() - strip_low_cnt;
// If value is completely overwritten, erase and continue.
//
if ( new_size <= 0 )
{
value_map.erase( it );
continue;
}
// Shift and resize the entry.
//
it->first = std::move( it->first ) + ( strip_low_cnt / 8 );
it->second >>= strip_low_cnt;
it->second.resize( new_size );
}
// If high bits end before or at our region limits:
// | v v v v | v v v v |
// | ... a b c d | ... a b c d |
//
else if ( ( size - dst ) >= it->second.size() )
{
// Shift and resize the entry.
//
it->second.resize( -dst );
}
// Split the region:
// | v v |
// | ... a b c d ... |
//
else
{
bitcnt_t low_size = -dst;
bitcnt_t high_offset = low_size + size;
bitcnt_t high_size = it->second.size() - high_offset;
// Split high value.
//
value_map.emplace(
it,
it->first + ( high_offset / 8 ),
( it->second >> high_offset ).resize( high_size )
);
// Resize low value.
//
it->second.resize( low_size );
}
}
// Insert new value.
//
return value_map.emplace_back( ptr, value.get() ).second;
}
};

View file

@ -27,7 +27,7 @@
//
#pragma once
#include <vtil/utility>
#include <vtil/math>
#include <list>
#include "pointer.hpp"
#include "variable.hpp"
#include "../arch/register_desc.hpp"
@ -40,21 +40,16 @@ namespace vtil::symbolic
//
using store_entry = std::pair<pointer, expression::reference>;
using store_type = std::list<store_entry>;
// The iterator to bind every variable onto.
//
il_const_iterator reference_iterator;
// The memory state.
//
bool relaxed_aliasing;
store_type value_map;
// Default constructor, optionally takes a boolean to indicate relaxed aliasing
// and a reference iterator to be used when creating variables.
// Default constructor, optionally takes a boolean to indicate relaxed aliasing.
//
memory( bool relaxed_aliasing = false, il_const_iterator reference_iterator = free_form_iterator )
: relaxed_aliasing( relaxed_aliasing ), reference_iterator( std::move( reference_iterator ) ) {}
memory( bool relaxed_aliasing = false )
: relaxed_aliasing( relaxed_aliasing ) {}
// Default copy/move.
//
@ -74,233 +69,15 @@ namespace vtil::symbolic
// Checks if the symbolic memory contains any writes to the given memory region.
//
trilean contains( const pointer& ptr, bitcnt_t size ) const
{
uint64_t mask_value = math::fill( size );
// For each entry, iterating backwards:
//
for ( auto it = value_map.rbegin(); it != value_map.rend(); it++ )
{
// If pointer cannot overlap lookup, skip.
//
if ( !it->first.can_overlap( ptr ) )
continue;
// Calculate displacement, if unknown return unknown.
//
std::optional byte_distance = it->first - ptr;
if ( !byte_distance )
return trilean::unknown;
// Calculate relative mask, return true if overlapping.
//
bitcnt_t bit_distance = math::narrow_cast<bitcnt_t>( *byte_distance * 8 );
uint64_t relative_mask = math::fill( it->second.size(), bit_distance );
if ( relative_mask & mask_value )
return true;
}
// None found, return false.
//
return false;
}
trilean contains( const pointer& ptr, bitcnt_t size ) const;
// Reads N bits from the given pointer, returns null reference if alias failure occurs.
//
expression::reference read( const pointer& ptr, bitcnt_t size ) const
{
uint64_t mask_pending = math::fill( size );
stack_vector<std::pair<bitcnt_t, expression::reference>, 8> merge_list;
// For each entry, iterating backwards:
//
for ( auto it = value_map.rbegin(); it != value_map.rend() && mask_pending; it++ )
{
// If pointer cannot overlap lookup, skip.
//
if ( !it->first.can_overlap( ptr ) )
continue;
// Calculate displacement, if unknown:
//
std::optional byte_distance = it->first - ptr;
if ( !byte_distance )
{
// If not relaxed aliasing, indicate alias failure by returning null.
//
if ( !relaxed_aliasing )
return nullptr;
// Otherwise, return default value, cannot be determined.
//
merge_list.clear();
break;
}
// Calculate relative mask, skip if not overlapping.
//
bitcnt_t bit_distance = math::narrow_cast<bitcnt_t>( *byte_distance * 8 );
uint64_t relative_mask = math::fill( it->second.size(), bit_distance );
if ( !( relative_mask & mask_pending ) )
continue;
// Add into merge list, clear the mask.
//
merge_list.emplace_back( bit_distance, it->second );
mask_pending &= ~relative_mask;
}
// If no overlapping keys found, return default.
//
if ( merge_list.empty() )
return MEMORY( reference_iterator )( ptr, size );
// Declare common bit selector.
//
constexpr auto select = [ ] ( symbolic::expression::reference& value, bitcnt_t size, bitcnt_t offset )
{
if ( offset < 0 ) value >>= -offset, value.resize( size );
else if ( offset > 0 ) value.resize( size ) <<= offset;
else value.resize( size );
};
// If single overlapping key with no pending bits, return as is.
//
if ( !mask_pending && merge_list.size() == 1 )
{
auto&& [dst, value] = std::move( merge_list[ 0 ] );
select( value, size, dst );
return value;
}
// Merge all in a single expression and return.
//
expression::reference result = mask_pending
? MEMORY( reference_iterator )( ptr, size )
: expression{ 0, size };
for ( auto& [ dst, value ] : merge_list )
{
select( value, size, dst );
result |= std::move( value );
}
return result;
}
expression::reference read( const pointer& ptr, bitcnt_t size, const il_const_iterator& reference_iterator = symbolic::free_form_iterator ) const;
// Writes the given value to the pointer, returns null reference if alias failure occurs.
//
optional_reference<expression::reference> write( const pointer& ptr, deferred_view<expression::reference> value, bitcnt_t size )
{
uint64_t mask_pending = math::fill( size );
stack_vector<std::pair<bitcnt_t, store_type::iterator>, 8> acquisition_list;
// For each entry, iterating backwards:
//
for ( auto it = value_map.rbegin(); it != value_map.rend() && mask_pending; it++ )
{
// If pointer cannot overlap lookup, skip.
//
if ( !it->first.can_overlap( ptr ) )
continue;
// Calculate displacement, if unknown:
//
std::optional byte_distance = it->first - ptr;
if ( !byte_distance )
{
// If not relaxed aliasing, indicate alias failure by returning null.
//
if ( !relaxed_aliasing )
return std::nullopt;
// Otherwise, insert at the end, overlaps can't be determined.
//
acquisition_list.clear();
break;
}
// Calculate relative mask, skip if not overlapping.
//
bitcnt_t bit_distance = math::narrow_cast<bitcnt_t>( *byte_distance * 8 );
uint64_t relative_mask = math::fill( it->second.size(), bit_distance );
if ( !( relative_mask & mask_pending ) )
continue;
// Add into acquisition list, clear the mask.
//
acquisition_list.emplace_back( bit_distance, std::prev( it.base() ) );
mask_pending &= ~relative_mask;
}
// For each iterator we should acquire bits from:
//
for ( auto& [ dst, it ] : acquisition_list )
{
// If low bits start at or above our pointer:
// | v v v v | v v v v |
// | a b c d ... | a b c d ... |
//
if ( dst >= 0 )
{
bitcnt_t strip_low_cnt = size - dst;
bitcnt_t new_size = it->second->size() - strip_low_cnt;
// If value is completely overwritten, erase and continue.
//
if ( new_size <= 0 )
{
value_map.erase( it );
continue;
}
// Shift and resize the entry.
//
it->first = std::move( it->first ) + ( strip_low_cnt / 8 );
it->second >>= strip_low_cnt;
it->second.resize( new_size );
}
// If high bits end before or at our region limits:
// | v v v v | v v v v |
// | ... a b c d | ... a b c d |
//
else if ( ( size - dst ) >= it->second.size() )
{
// Shift and resize the entry.
//
it->second.resize( -dst );
}
// Split the region:
// | v v |
// | ... a b c d ... |
//
else
{
bitcnt_t low_size = -dst;
bitcnt_t high_offset = low_size + size;
bitcnt_t high_size = it->second.size() - high_offset;
// Split high value.
//
value_map.emplace(
it,
it->first + ( high_offset / 8 ),
( it->second >> high_offset ).resize( high_size )
);
// Resize low value.
//
it->second.resize( low_size );
}
}
// Insert new value.
//
return value_map.emplace_back( ptr, value.get() ).second;
}
optional_reference<expression::reference> write( const pointer& ptr, expression::reference value )
{
return write( ptr, value, value.size() );
}
optional_reference<expression::reference> write( const pointer& ptr, deferred_value<expression::reference> value, bitcnt_t size );
optional_reference<expression::reference> write( const pointer& ptr, expression::reference value ) { return write( ptr, value, value.size() ); }
};
};