+=head2 C<< vshift $v, $start, $length => $bits [, $insert ] >>
+
+In the area starting at C<$start> and of length C<$length> in C<$v>, shift bits C<abs $bits> positions left if C<< $bits > 0 >> and right otherwise.
+If C<$insert> is defined, also fills the resulting gap with ones if C<$insert> is true and zeros if it's false.
+Bits outside of the specified area are left untouched.
+Doesn't need to allocate any extra memory.
+
+=cut
+
+sub vshift ($$$$;$) {
+ my ($start, $length, $bits, $insert) = @_[1 .. 4];
+ return unless $length and $bits;
+ croak 'Invalid negative offset' if $start < 0;
+ croak 'Invalid negative length' if $length < 0;
+ my $left = 1;
+ if ($bits < 0) {
+ $left = 0;
+ $bits = -$bits;
+ }
+ if ($bits < $length) {
+ $length -= $bits;
+ if ($left) {
+ vcopy($_[0], $start, $_[0], $start + $bits, $length);
+ vfill($_[0], $start, $bits, $insert) if defined $insert;
+ } else {
+ vcopy($_[0], $start + $bits, $_[0], $start, $length);
+ vfill($_[0], $start + $length, $bits, $insert) if defined $insert;
+ }
+ } else {
+ vfill($_[0], $start, $length, $insert) if defined $insert;
+ }
+}
+
+=head2 C<< vrot $v, $start, $length, $bits >>
+
+In the area starting at C<$start> and of length C<$length> in C<$v>, rotates bits C<abs $bits> positions left if C<< $bits > 0 >> and right otherwise.
+Bits outside of the specified area are left untouched.
+Currently allocates an extra buffer of size C<O($bits)>.
+
+=cut
+
+sub vrot ($$$$) {
+ my ($start, $length, $bits) = @_[1 .. 3];
+ return unless $length and $bits;
+ croak 'Invalid negative offset' if $start < 0;
+ croak 'Invalid negative length' if $length < 0;
+ my $left = 1;
+ if ($bits < 0) {
+ $left = 0;
+ $bits = -$bits;
+ }
+ $bits %= $length;
+ return unless $bits;
+ $length -= $bits;
+ my $buf = '';
+ if ($left) {
+ vcopy($_[0], $start + $length, $buf, 0, $bits);
+ vcopy($_[0], $start, $_[0], $start + $bits, $length);
+ vcopy($buf, 0, $_[0], $start, $bits);
+ } else {
+ vcopy($_[0], $start, $buf, 0, $bits);
+ vcopy($_[0], $start + $bits, $_[0], $start, $length);
+ vcopy($buf, 0, $_[0], $start + $length, $bits);
+ }
+}
+