From 8e677e74e72ba1e05cc82376df8e00ec6d9b89af Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Mon, 11 Sep 2017 10:41:34 +0200 Subject: [PATCH] Tools: add `convert_size` for generic byte conversion We often need to convert between file sizes, for formatting output, but also code-internal. Some methods expect kilobytes, some gigabytes and sometimes we need bytes. While conversion from smaller to bigger units can be simply done with a left-shift, the opposite conversion may need more attention - depending on the used context. If we allocate disks this is quite critical. For example, if we need to allocate a disk with size 1023 bytes using the PVE::Storage::vdisk_alloc method (which expects kilobytes) a right shift by 10 (<=> division by 1024) would result in "0", which obviously fails. Thus we round up the converted value if a remainder was lost on the transformation in this new method. This behaviour is opt-out, to be on the safe side. The method can be used in a clear way, as it gives information about the source and target unit size, unlike "$var *= 1024", which doesn't gives direct information at all, if not commented or derived somewhere from its context. For example: > my $size = convert_unit($value, 'gb' => 'kb'); is more clear than: > my $size = $value*1024*1024; Signed-off-by: Thomas Lamprecht --- src/PVE/Tools.pm | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm index 9ddcfda..81662b1 100644 --- a/src/PVE/Tools.pm +++ b/src/PVE/Tools.pm @@ -1617,4 +1617,37 @@ sub encrypt_pw { return crypt(encode("utf8", $pw), "\$5\$$salt\$"); } +# intended usage: convert_size($val, "kb" => "gb") +# on reduction (converting to a bigger unit) we round up by default if +# information got lost. E.g. `convert_size(1023, "b" => "kb")` returns 1 +# use $no_round_up to switch this off, above example would then return 0 +sub convert_size { + my ($value, $from, $to, $no_round_up) = @_; + + my $units = { + b => 0, + kb => 1, + mb => 2, + gb => 3, + tb => 4, + pb => 5, + }; + + $from = lc($from); $to = lc($to); + die "unknown 'from' and/or 'to' units ($from => $to)" + if !(defined($units->{$from}) && defined($units->{$to})); + + my $shift_amount = $units->{$from} - $units->{$to}; + + if ($shift_amount > 0) { + $value <<= ($shift_amount * 10); + } elsif ($shift_amount < 0) { + my $remainder = ($value & (1 << abs($shift_amount)*10) - 1); + $value >>= abs($shift_amount) * 10; + $value++ if $remainder && !$no_round_up; + } + + return $value; +} + 1; -- 2.39.2