3434/* Divide by two and round away from zero */
3535#define half_rounded (x ) (((x) + ((x) < 0 ? -1 : 1)) / 2)
3636
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+
3758/* Return physical size of directory contents, or 0 if dir doesn't exist */
3859static int64
3960db_dir_size (const char * path )
@@ -535,41 +556,34 @@ pg_size_pretty(PG_FUNCTION_ARGS)
535556{
536557 int64 size = PG_GETARG_INT64 (0 );
537558 char buf [64 ];
538- int64 limit = 10 * 1024 ;
539- int64 limit2 = limit * 2 - 1 ;
559+ const struct size_pretty_unit * unit ;
540560
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 ++ )
544562 {
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 )
554567 {
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 ;
572573 }
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 ;
573587 }
574588
575589 PG_RETURN_TEXT_P (cstring_to_text (buf ));
@@ -640,57 +654,35 @@ Datum
640654pg_size_pretty_numeric (PG_FUNCTION_ARGS )
641655{
642656 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 ;
649659
650- if ( numeric_is_less ( numeric_absolute ( size ), limit ) )
660+ for ( unit = size_pretty_units ; unit -> name != NULL ; unit ++ )
651661 {
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 ;
659663
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 )))
661668 {
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 )
672670 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 ;
693674 }
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 );
694686 }
695687
696688 PG_RETURN_TEXT_P (cstring_to_text (result ));
@@ -791,6 +783,7 @@ pg_size_bytes(PG_FUNCTION_ARGS)
791783 /* Handle possible unit */
792784 if (* strptr != '\0' )
793785 {
786+ const struct size_pretty_unit * unit ;
794787 int64 multiplier = 0 ;
795788
796789 /* Trim any trailing whitespace */
@@ -802,21 +795,18 @@ pg_size_bytes(PG_FUNCTION_ARGS)
802795 endptr ++ ;
803796 * endptr = '\0' ;
804797
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+ }
818807
819- else
808+ /* Verify we found a valid unit in the loop above */
809+ if (unit -> name == NULL )
820810 ereport (ERROR ,
821811 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
822812 errmsg ("invalid size: \"%s\"" , text_to_cstring (arg )),
0 commit comments