Skip to content

Commit 4c02530

Browse files
committed
Split off format-human-size and human-size-formatter.
See ruricolist#37.
1 parent 193999c commit 4c02530

File tree

4 files changed

+84
-37
lines changed

4 files changed

+84
-37
lines changed

files.lisp

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,18 @@ only cares about its name and type."
165165

166166
(defun format-file-size-human-readable (stream file-size
167167
&key flavor
168-
(space (and (eql flavor :si) t))
168+
(space (eql flavor :si))
169169
(suffix (if (eql flavor :iec) "B" "")))
170-
"Write SIZE, a file size in bytes, to STREAM, in human-readable form.
170+
"Write FILE-SIZE, a file size in bytes, to STREAM, in human-readable form.
171+
172+
STREAM is interpreted as by `format'.
173+
171174
If FLAVOR is nil, kilobytes are 1024 bytes and SI prefixes are used.
172175
173176
If FLAVOR is `:si', kilobytes are 1000 bytes and SI prefixes are used.
174177
175-
If FLAVOR is `:iec', kilobytes are 1024 bytes and SI prefixes (KiB,
176-
MiB, etc.) are used.
178+
If FLAVOR is `:iec', kilobytes are 1024 bytes and IEC prefixes (Ki,
179+
Mi, etc.) are used.
177180
178181
If SPACE is non-nil, include a space between the number and the
179182
prefix. (Defaults to T if FLAVOR is `:si'.)
@@ -182,37 +185,13 @@ SUFFIX is the suffix to use; defaults to B if FLAVOR is `:iec',
182185
otherwise empty.
183186
184187
Inspired by the function of the same name in Emacs."
185-
(when (zerop file-size)
186-
(return-from format-file-size-human-readable
187-
(format stream "0")))
188-
(let ((file-size (coerce file-size 'double-float))
189-
;; Avoid printing exponent markers.
190-
(*read-default-float-format* 'double-float)
191-
(base (ecase flavor
192-
((nil) 1024)
193-
(:si 1000)
194-
(:iec 2))))
195-
(multiple-value-bind (long short factor)
196-
(si-prefix file-size :base base)
197-
(declare (ignore long))
198-
(let* ((file-size (/ file-size factor))
199-
(int (round file-size))
200-
(file-size
201-
(if (> (abs (- file-size int))
202-
0.05d0)
203-
file-size
204-
int))
205-
(control-string
206-
(if (floatp file-size)
207-
(formatter "~,1f")
208-
(formatter "~d"))))
209-
(format stream
210-
"~@?~:[~; ~]~a~a"
211-
control-string
212-
file-size
213-
space
214-
short
215-
suffix)))))
188+
(check-type file-size (integer 0 *))
189+
(if (zerop file-size)
190+
(format stream "0")
191+
(let ((flavor (if (null flavor) :file flavor)))
192+
(multiple-value-bind (formatter args)
193+
(human-size-formatter file-size :flavor flavor :space space)
194+
(format stream "~?~a" formatter args suffix)))))
216195

217196
(defun file-size-human-readable (file &key flavor space suffix stream)
218197
"Format the size of FILE (in octets) using `format-file-size-human-readable'.

package.lisp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@
252252
#:random-in-range
253253
#:float-precision-contagion
254254
#:range
255-
#:si-prefix
256255
;; Octets.
257256
#:octet
258257
#:octet-vector
@@ -423,7 +422,11 @@
423422
#:package-exports
424423
#:package-names
425424
#:package-name-keyword
426-
#:find-external-symbol))
425+
#:find-external-symbol
426+
;; Units.
427+
#:si-prefix
428+
#:human-size-formatter
429+
#:format-human-size))
427430

428431
(defpackage #:serapeum-user
429432
(:use #:cl #:alexandria #:serapeum))

tests/units.lisp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,14 @@
2323
(is (equal "kilo" (si-prefix (expt 2 10))))
2424
(is (equal "kibi" (si-prefix (expt 2 10) :base 2)))
2525
(is (equal "yocto" (si-prefix (expt 10 -23)))))
26+
27+
(test format-human-size
28+
(is (equal "0" (format-human-size nil 0)))
29+
(is (equal "0" (format-human-size nil 0 :flavor :iec)))
30+
(is (equal "0" (format-human-size nil 0 :flavor :si)))
31+
(is (equal "1 k" (format-human-size nil 1000)))
32+
(is (equal "-1 k" (format-human-size nil -1000)))
33+
(is (equal "1Ki" (format-human-size nil 1024 :flavor :iec)))
34+
(is (equal "-1Ki" (format-human-size nil -1024 :flavor :iec)))
35+
(is (equal "500 k" (format-human-size nil 500000 :flavor :si)))
36+
(is (equal "-500 k" (format-human-size nil -500000 :flavor :si))))

units.lisp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,57 @@ sizes. Base 2 uses the IEC binary prefixes."
7373
(10 (si-prefix-rec n 10d0 #.si-prefixes))
7474
(1000 (si-prefix-rec n 1000d0 #.si-prefixes-base-1000))
7575
(1024 (si-prefix-rec n 1024d0 #.si-prefixes-base-1000))))))
76+
77+
(defun human-size-formatter (size &key (flavor :si)
78+
(space (eql flavor :si)))
79+
"Auxiliary function for formatting quantities human-readably.
80+
Returns two values: a format control and a list of arguments.
81+
82+
This can be used to integrate the human-readable printing of
83+
quantities into larger format control strings using the recursive
84+
processing format directive (~?):
85+
86+
(multiple-value-bind (control args)
87+
(human-size-formatter size)
88+
(format t \"~?\" control args))"
89+
(let ((size (coerce size 'double-float))
90+
;; Avoid printing exponent markers.
91+
(*read-default-float-format* 'double-float)
92+
(base (ecase flavor
93+
(:file 1024)
94+
(:si 1000)
95+
(:iec 2))))
96+
(multiple-value-bind (long short factor)
97+
(si-prefix size :base base)
98+
(declare (ignore long))
99+
(let* ((size (/ size factor))
100+
(int (round size))
101+
(size
102+
(if (> (abs (- size int))
103+
0.05d0)
104+
size
105+
int)))
106+
(values (formatter "~:[~d~;~,1f~]~:[~; ~]~a")
107+
(list (floatp size) size space short))))))
108+
109+
(defun format-human-size (stream size
110+
&key (flavor :si)
111+
(space (eql flavor :si)))
112+
"Write SIZE to STREAM, in human-readable form.
113+
114+
STREAM is interpreted as by `format'.
115+
116+
If FLAVOR is `:si' (the default) the base is 1000 and SI prefixes are used.
117+
118+
If FLAVOR is `:file', the base is 1024 and SI prefixes are used.
119+
120+
If FLAVOR is `:iec', the base is 1024 bytes and IEC prefixes (Ki, Mi,
121+
etc.) are used.
122+
123+
If SPACE is non-nil, include a space between the number and the
124+
prefix. (Defaults to T if FLAVOR is `:si'.)"
125+
(if (zerop size)
126+
(format stream "0")
127+
(multiple-value-bind (formatter args)
128+
(human-size-formatter size :flavor flavor :space space)
129+
(format stream "~?" formatter args))))

0 commit comments

Comments
 (0)