diff --git a/VTIL-Architecture/includes/vtil/arch b/VTIL-Architecture/includes/vtil/arch index 776037d..617c350 100644 --- a/VTIL-Architecture/includes/vtil/arch +++ b/VTIL-Architecture/includes/vtil/arch @@ -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" diff --git a/VTIL-Architecture/symex/context.cpp b/VTIL-Architecture/symex/context.cpp new file mode 100644 index 0000000..2967ffd --- /dev/null +++ b/VTIL-Architecture/symex/context.cpp @@ -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 +#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 ); + } +}; diff --git a/VTIL-Architecture/symex/context.hpp b/VTIL-Architecture/symex/context.hpp new file mode 100644 index 0000000..824afe6 --- /dev/null +++ b/VTIL-Architecture/symex/context.hpp @@ -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 +#include +#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>; + + // 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 ); + }; +}; \ No newline at end of file diff --git a/VTIL-Architecture/symex/memory.cpp b/VTIL-Architecture/symex/memory.cpp new file mode 100644 index 0000000..bf569f5 --- /dev/null +++ b/VTIL-Architecture/symex/memory.cpp @@ -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 +#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, 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( *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 memory::write( const pointer& ptr, deferred_value value, bitcnt_t size ) + { + uint64_t mask_pending = math::fill( size ); + stack_vector, 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( *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; + } +}; \ No newline at end of file diff --git a/VTIL-Architecture/symex/memory.hpp b/VTIL-Architecture/symex/memory.hpp index e5bd3f4..2e815e8 100644 --- a/VTIL-Architecture/symex/memory.hpp +++ b/VTIL-Architecture/symex/memory.hpp @@ -27,7 +27,7 @@ // #pragma once #include -#include +#include #include "pointer.hpp" #include "variable.hpp" #include "../arch/register_desc.hpp" @@ -40,21 +40,16 @@ namespace vtil::symbolic // using store_entry = std::pair; using store_type = std::list; - - // 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( *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, 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( *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 write( const pointer& ptr, deferred_view value, bitcnt_t size ) - { - uint64_t mask_pending = math::fill( size ); - stack_vector, 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( *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 write( const pointer& ptr, expression::reference value ) - { - return write( ptr, value, value.size() ); - } + optional_reference write( const pointer& ptr, deferred_value value, bitcnt_t size ); + optional_reference write( const pointer& ptr, expression::reference value ) { return write( ptr, value, value.size() ); } }; }; \ No newline at end of file