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 <t.lampre...@proxmox.com>
---

changes v2 -> v3:
 * remove returning the remainder, it will not get used yet and feels
   not clean. If we want such behavior we can add it easily later and
   then, with a real use case, we should be able to create a better
   interface then
 * die if from or to was not fount in unit hash, normally from/to are
   constant not dynamic so this is more convenience for the dev.

I did not resent the qemu-server part as it was not change in this
iteration

 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.11.0


_______________________________________________
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to