34
34
/* Divide by two and round away from zero */
35
35
#define half_rounded (x ) (((x) + ((x) < 0 ? -1 : 1)) / 2)
36
36
37
+ /* Units used in pg_size_pretty functions. All units must be powers of 2 */
38
+ struct size_pretty_unit
39
+ {
40
+ const char * name ; /* bytes, kB, MB, GB etc */
41
+ uint32 limit ; /* upper limit, prior to half rounding after
42
+ * converting to this unit. */
43
+ bool round ; /* do half rounding for this unit */
44
+ uint8 unitbits ; /* (1 << unitbits) bytes to make 1 of this
45
+ * unit */
46
+ };
47
+
48
+ /* When adding units here also update the error message in pg_size_bytes */
49
+ static const struct size_pretty_unit size_pretty_units [] = {
50
+ {"bytes" , 10 * 1024 , false, 0 },
51
+ {"kB" , 20 * 1024 - 1 , true, 10 },
52
+ {"MB" , 20 * 1024 - 1 , true, 20 },
53
+ {"GB" , 20 * 1024 - 1 , true, 30 },
54
+ {"TB" , 20 * 1024 - 1 , true, 40 },
55
+ {NULL , 0 , false, 0 }
56
+ };
57
+
37
58
/* Return physical size of directory contents, or 0 if dir doesn't exist */
38
59
static int64
39
60
db_dir_size (const char * path )
@@ -535,41 +556,34 @@ pg_size_pretty(PG_FUNCTION_ARGS)
535
556
{
536
557
int64 size = PG_GETARG_INT64 (0 );
537
558
char buf [64 ];
538
- int64 limit = 10 * 1024 ;
539
- int64 limit2 = limit * 2 - 1 ;
559
+ const struct size_pretty_unit * unit ;
540
560
541
- if (Abs (size ) < limit )
542
- snprintf (buf , sizeof (buf ), INT64_FORMAT " bytes" , size );
543
- else
561
+ for (unit = size_pretty_units ; unit -> name != NULL ; unit ++ )
544
562
{
545
- /*
546
- * We use divide instead of bit shifting so that behavior matches for
547
- * both positive and negative size values.
548
- */
549
- size /= (1 << 9 ); /* keep one extra bit for rounding */
550
- if (Abs (size ) < limit2 )
551
- snprintf (buf , sizeof (buf ), INT64_FORMAT " kB" ,
552
- half_rounded (size ));
553
- else
563
+ uint8 bits ;
564
+
565
+ /* use this unit if there are no more units or we're below the limit */
566
+ if (unit [1 ].name == NULL || Abs (size ) < unit -> limit )
554
567
{
555
- size /= (1 << 10 );
556
- if (Abs (size ) < limit2 )
557
- snprintf (buf , sizeof (buf ), INT64_FORMAT " MB" ,
558
- half_rounded (size ));
559
- else
560
- {
561
- size /= (1 << 10 );
562
- if (Abs (size ) < limit2 )
563
- snprintf (buf , sizeof (buf ), INT64_FORMAT " GB" ,
564
- half_rounded (size ));
565
- else
566
- {
567
- size /= (1 << 10 );
568
- snprintf (buf , sizeof (buf ), INT64_FORMAT " TB" ,
569
- half_rounded (size ));
570
- }
571
- }
568
+ if (unit -> round )
569
+ size = half_rounded (size );
570
+
571
+ snprintf (buf , sizeof (buf ), INT64_FORMAT " %s" , size , unit -> name );
572
+ break ;
572
573
}
574
+
575
+ /*
576
+ * Determine the number of bits to use to build the divisor. We may
577
+ * need to use 1 bit less than the difference between this and the
578
+ * next unit if the next unit uses half rounding. Or we may need to
579
+ * shift an extra bit if this unit uses half rounding and the next one
580
+ * does not. We use division rather than shifting right by this
581
+ * number of bits to ensure positive and negative values are rounded
582
+ * in the same way.
583
+ */
584
+ bits = (unit [1 ].unitbits - unit -> unitbits - (unit [1 ].round == true)
585
+ + (unit -> round == true));
586
+ size /= ((int64 ) 1 ) << bits ;
573
587
}
574
588
575
589
PG_RETURN_TEXT_P (cstring_to_text (buf ));
@@ -640,57 +654,35 @@ Datum
640
654
pg_size_pretty_numeric (PG_FUNCTION_ARGS )
641
655
{
642
656
Numeric size = PG_GETARG_NUMERIC (0 );
643
- Numeric limit ,
644
- limit2 ;
645
- char * result ;
646
-
647
- limit = int64_to_numeric (10 * 1024 );
648
- limit2 = int64_to_numeric (10 * 1024 * 2 - 1 );
657
+ char * result = NULL ;
658
+ const struct size_pretty_unit * unit ;
649
659
650
- if ( numeric_is_less ( numeric_absolute ( size ), limit ) )
660
+ for ( unit = size_pretty_units ; unit -> name != NULL ; unit ++ )
651
661
{
652
- result = psprintf ("%s bytes" , numeric_to_cstring (size ));
653
- }
654
- else
655
- {
656
- /* keep one extra bit for rounding */
657
- /* size /= (1 << 9) */
658
- size = numeric_truncated_divide (size , 1 << 9 );
662
+ unsigned int shiftby ;
659
663
660
- if (numeric_is_less (numeric_absolute (size ), limit2 ))
664
+ /* use this unit if there are no more units or we're below the limit */
665
+ if (unit [1 ].name == NULL ||
666
+ numeric_is_less (numeric_absolute (size ),
667
+ int64_to_numeric (unit -> limit )))
661
668
{
662
- size = numeric_half_rounded (size );
663
- result = psprintf ("%s kB" , numeric_to_cstring (size ));
664
- }
665
- else
666
- {
667
- /* size /= (1 << 10) */
668
- size = numeric_truncated_divide (size , 1 << 10 );
669
-
670
- if (numeric_is_less (numeric_absolute (size ), limit2 ))
671
- {
669
+ if (unit -> round )
672
670
size = numeric_half_rounded (size );
673
- result = psprintf ("%s MB" , numeric_to_cstring (size ));
674
- }
675
- else
676
- {
677
- /* size /= (1 << 10) */
678
- size = numeric_truncated_divide (size , 1 << 10 );
679
-
680
- if (numeric_is_less (numeric_absolute (size ), limit2 ))
681
- {
682
- size = numeric_half_rounded (size );
683
- result = psprintf ("%s GB" , numeric_to_cstring (size ));
684
- }
685
- else
686
- {
687
- /* size /= (1 << 10) */
688
- size = numeric_truncated_divide (size , 1 << 10 );
689
- size = numeric_half_rounded (size );
690
- result = psprintf ("%s TB" , numeric_to_cstring (size ));
691
- }
692
- }
671
+
672
+ result = psprintf ("%s %s" , numeric_to_cstring (size ), unit -> name );
673
+ break ;
693
674
}
675
+
676
+ /*
677
+ * Determine the number of bits to use to build the divisor. We may
678
+ * need to use 1 bit less than the difference between this and the
679
+ * next unit if the next unit uses half rounding. Or we may need to
680
+ * shift an extra bit if this unit uses half rounding and the next one
681
+ * does not.
682
+ */
683
+ shiftby = (unit [1 ].unitbits - unit -> unitbits - (unit [1 ].round == true)
684
+ + (unit -> round == true));
685
+ size = numeric_truncated_divide (size , ((int64 ) 1 ) << shiftby );
694
686
}
695
687
696
688
PG_RETURN_TEXT_P (cstring_to_text (result ));
@@ -791,6 +783,7 @@ pg_size_bytes(PG_FUNCTION_ARGS)
791
783
/* Handle possible unit */
792
784
if (* strptr != '\0' )
793
785
{
786
+ const struct size_pretty_unit * unit ;
794
787
int64 multiplier = 0 ;
795
788
796
789
/* Trim any trailing whitespace */
@@ -802,21 +795,18 @@ pg_size_bytes(PG_FUNCTION_ARGS)
802
795
endptr ++ ;
803
796
* endptr = '\0' ;
804
797
805
- /* Parse the unit case-insensitively */
806
- if (pg_strcasecmp (strptr , "bytes" ) == 0 )
807
- multiplier = (int64 ) 1 ;
808
- else if (pg_strcasecmp (strptr , "kb" ) == 0 )
809
- multiplier = (int64 ) 1024 ;
810
- else if (pg_strcasecmp (strptr , "mb" ) == 0 )
811
- multiplier = ((int64 ) 1024 ) * 1024 ;
812
-
813
- else if (pg_strcasecmp (strptr , "gb" ) == 0 )
814
- multiplier = ((int64 ) 1024 ) * 1024 * 1024 ;
815
-
816
- else if (pg_strcasecmp (strptr , "tb" ) == 0 )
817
- multiplier = ((int64 ) 1024 ) * 1024 * 1024 * 1024 ;
798
+ for (unit = size_pretty_units ; unit -> name != NULL ; unit ++ )
799
+ {
800
+ /* Parse the unit case-insensitively */
801
+ if (pg_strcasecmp (strptr , unit -> name ) == 0 )
802
+ {
803
+ multiplier = ((int64 ) 1 ) << unit -> unitbits ;
804
+ break ;
805
+ }
806
+ }
818
807
819
- else
808
+ /* Verify we found a valid unit in the loop above */
809
+ if (unit -> name == NULL )
820
810
ereport (ERROR ,
821
811
(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
822
812
errmsg ("invalid size: \"%s\"" , text_to_cstring (arg )),
0 commit comments