+sub parse_size {
+ my ($value) = @_;
+
+ return undef if $value !~ m/^(\d+(\.\d+)?)([KMGT])?$/;
+ my ($size, $unit) = ($1, $3);
+ if ($unit) {
+ if ($unit eq 'K') {
+ $size = $size * 1024;
+ } elsif ($unit eq 'M') {
+ $size = $size * 1024 * 1024;
+ } elsif ($unit eq 'G') {
+ $size = $size * 1024 * 1024 * 1024;
+ } elsif ($unit eq 'T') {
+ $size = $size * 1024 * 1024 * 1024 * 1024;
+ }
+ }
+ return int($size);
+};
+
+sub format_size {
+ my ($size) = @_;
+
+ $size = int($size);
+
+ my $kb = int($size/1024);
+ return $size if $kb*1024 != $size;
+
+ my $mb = int($kb/1024);
+ return "${kb}K" if $mb*1024 != $kb;
+
+ my $gb = int($mb/1024);
+ return "${mb}M" if $gb*1024 != $mb;
+
+ my $tb = int($gb/1024);
+ return "${gb}G" if $tb*1024 != $gb;
+
+ return "${tb}T";
+};
+
+sub parse_boolean {
+ my ($bool) = @_;
+ return 1 if $bool =~ m/^(1|on|yes|true)$/i;
+ return 0 if $bool =~ m/^(0|off|no|false)$/i;
+ return undef;
+}
+
+sub parse_property_string {
+ my ($format, $data, $path, $additional_properties) = @_;
+
+ # In property strings we default to not allowing additional properties
+ $additional_properties = 0 if !defined($additional_properties);
+
+ # Support named formats here, too:
+ if (!ref($format)) {
+ if (my $desc = $format_list->{$format}) {
+ $format = $desc;
+ } else {
+ die "unknown format: $format\n";
+ }
+ } elsif (ref($format) ne 'HASH') {
+ die "unexpected format value of type ".ref($format)."\n";
+ }
+
+ my $default_key;
+
+ my $res = {};
+ foreach my $part (split(/,/, $data)) {
+ next if $part =~ /^\s*$/;
+
+ if ($part =~ /^([^=]+)=(.+)$/) {
+ my ($k, $v) = ($1, $2);
+ die "duplicate key in comma-separated list property: $k\n" if defined($res->{$k});
+ my $schema = $format->{$k};
+ if (my $alias = $schema->{alias}) {
+ if (my $key_alias = $schema->{keyAlias}) {
+ die "key alias '$key_alias' is already defined\n" if defined($res->{$key_alias});
+ $res->{$key_alias} = $k;
+ }
+ $k = $alias;
+ $schema = $format->{$k};
+ }
+
+ die "invalid key in comma-separated list property: $k\n" if !$schema;
+ if ($schema->{type} && $schema->{type} eq 'boolean') {
+ $v = parse_boolean($v) // $v;
+ }
+ $res->{$k} = $v;
+ } elsif ($part !~ /=/) {
+ die "duplicate key in comma-separated list property: $default_key\n" if $default_key;
+ foreach my $key (keys %$format) {
+ if ($format->{$key}->{default_key}) {
+ $default_key = $key;
+ if (!$res->{$default_key}) {
+ $res->{$default_key} = $part;
+ last;
+ }
+ die "duplicate key in comma-separated list property: $default_key\n";
+ }
+ }
+ die "value without key, but schema does not define a default key\n" if !$default_key;
+ } else {
+ die "missing key in comma-separated list property\n";
+ }
+ }
+
+ my $errors = {};
+ check_object($path, $format, $res, $additional_properties, $errors);
+ if (scalar(%$errors)) {
+ raise "format error\n", errors => $errors;
+ }
+
+ return $res;
+}
+