+template <typename Block, typename Allocator>
+dynamic_bitset<Block, Allocator>& dynamic_bitset<Block, Allocator>::range_operation(
+ size_type pos, size_type len,
+ Block (*partial_block_operation)(Block, size_type, size_type),
+ Block (*full_block_operation)(Block))
+{
+ assert(pos + len <= m_num_bits);
+
+ // Do nothing in case of zero length
+ if (!len)
+ return *this;
+
+ // Use an additional asserts in order to detect size_type overflow
+ // For example: pos = 10, len = size_type_limit - 2, pos + len = 7
+ // In case of overflow, 'pos + len' is always smaller than 'len'
+ assert(pos + len >= len);
+
+ // Start and end blocks of the [pos; pos + len - 1] sequence
+ const size_type first_block = block_index(pos);
+ const size_type last_block = block_index(pos + len - 1);
+
+ const size_type first_bit_index = bit_index(pos);
+ const size_type last_bit_index = bit_index(pos + len - 1);
+
+ if (first_block == last_block) {
+ // Filling only a sub-block of a block
+ m_bits[first_block] = partial_block_operation(m_bits[first_block],
+ first_bit_index, last_bit_index);
+ } else {
+ // Check if the corner blocks won't be fully filled with 'val'
+ const size_type first_block_shift = bit_index(pos) ? 1 : 0;
+ const size_type last_block_shift = (bit_index(pos + len - 1)
+ == bits_per_block - 1) ? 0 : 1;
+
+ // Blocks that will be filled with ~0 or 0 at once
+ const size_type first_full_block = first_block + first_block_shift;
+ const size_type last_full_block = last_block - last_block_shift;
+
+ for (size_type i = first_full_block; i <= last_full_block; ++i) {
+ m_bits[i] = full_block_operation(m_bits[i]);
+ }
+
+ // Fill the first block from the 'first' bit index to the end
+ if (first_block_shift) {
+ m_bits[first_block] = partial_block_operation(m_bits[first_block],
+ first_bit_index, bits_per_block - 1);
+ }
+
+ // Fill the last block from the start to the 'last' bit index
+ if (last_block_shift) {
+ m_bits[last_block] = partial_block_operation(m_bits[last_block],
+ 0, last_bit_index);
+ }
+ }
+
+ return *this;
+}