Skip to content

Commit 2cbabf4

Browse files
committed
Update cfmt code based on latest changes to Printf.jl
1 parent c358356 commit 2cbabf4

File tree

4 files changed

+792
-26
lines changed

4 files changed

+792
-26
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ keywords = ["Strings", "Formatting"]
2323
license = "MIT"
2424
name = "Format"
2525
uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
26-
version = "1.3.6"
26+
version = "1.3.7"
2727

2828
[deps]
2929

src/printf.jl

+58-25
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# C-style formatting functions, originally based on Printf.jl
2+
13
using Base.Ryu
24

35
abstract type FmtType end
@@ -117,7 +119,7 @@ function FmtSpec(f::AbstractString)
117119
end
118120
end
119121
# parse prec
120-
prec = -1
122+
prec = 0
121123
parsedprecdigits = false
122124
if b == UInt8('.')
123125
pos > len && throw(ArgumentError("incomplete format string: '$f'"))
@@ -141,6 +143,8 @@ function FmtSpec(f::AbstractString)
141143
# parse length modifier (ignored)
142144
if b == UInt8('h') || b == UInt8('l')
143145
prev = b
146+
pos > len &&
147+
throw(ArgumentError("format string - length modifier is missing type specifier: '$f'"))
144148
b = bytes[pos]
145149
pos += 1
146150
if b == prev
@@ -149,6 +153,8 @@ function FmtSpec(f::AbstractString)
149153
pos += 1
150154
end
151155
elseif b in b"Ljqtz"
156+
pos > len &&
157+
throw(ArgumentError("format string - length modifier is missing type specifier: '$f'"))
152158
b = bytes[pos]
153159
pos += 1
154160
end
@@ -206,7 +212,7 @@ end
206212

207213
function _fmt(buf, pos, spec::FmtSpec{FmtChr}, arg)
208214
ch = arg isa String ? arg[1] : Char(arg)
209-
width = spec.width - 1
215+
width = spec.width - textwidth(ch)
210216
width <= 0 && return writechar(buf, pos, ch)
211217
if spec.leftalign
212218
padn(buf, writechar(buf, pos, ch), width)
@@ -277,21 +283,24 @@ _fmt(buf, pos, spec::FmtSpec{<:FmtInts}, arg::BaseUns) =
277283
_fmt(buf, pos, spec::FmtSpec{<:FmtInts}, arg::BaseInt) =
278284
_fmt(buf, pos, spec, arg < 0, unsigned(abs(arg)))
279285

286+
hex_len(x) = x == 0 ? 1 : (sizeof(x)<<1) - (leading_zeros(x)>>2)
287+
oct_len(x) = x == 0 ? 1 : div((sizeof(x)<<3) - leading_zeros(x)+2, 3)
288+
280289
function _fmt(buf, pos, spec::FmtSpec{F}, neg, x::T) where {F<:FmtInts,T<:Union{String,BaseUns}}
281290
n = T === String ? sizeof(x) :
282-
F === FmtDec ? dec_len(x) :
283-
F === FmtHex ? (sizeof(x)<<1) - (leading_zeros(x)>>2) :
284-
div((sizeof(x)<<3) - leading_zeros(x)+2, 3)
291+
F === FmtDec ? dec_len(x) : F === FmtHex ? hex_len(x) : oct_len(x)
285292
i = n
286293
arglen = n + (neg || (spec.plus | spec.space)) +
287294
((spec.altf && (F !== FmtDec)) ? ifelse(F === FmtOct, 1, 2) : 0)
288295
width, prec = spec.width, spec.prec
289-
arglen2 = arglen < width && prec > 0 ? arglen + min(max(0, prec - n), width - arglen) : arglen
296+
precpad = max(0, prec - n)
297+
# Calculate width including padding due to width or precision
298+
arglen2 = arglen < width && prec > 0 ? arglen + min(precpad, width - arglen) : arglen
290299

291300
# Make sure that remaining output buffer is large enough
292301
# This means that it isn't necessary to preallocate for cases that usually will never happen
293-
buflen = sizeof(buf) - pos
294-
buflen < arglen2 && resize!(buf, arglen2 + pos)
302+
buflen = pos + max(width, arglen + precpad)
303+
buflen > sizeof(buf) && resize!(buf, buflen)
295304

296305
!spec.leftalign && !spec.zero && arglen2 < width &&
297306
(pos = padn(buf, pos, width - arglen2))
@@ -312,7 +321,7 @@ function _fmt(buf, pos, spec::FmtSpec{F}, neg, x::T) where {F<:FmtInts,T<:Union{
312321
if spec.zero && arglen2 < width
313322
pos = padzero(buf, pos, width - arglen2)
314323
elseif n < prec
315-
pos = padzero(buf, pos, prec - n)
324+
pos = padzero(buf, pos, precpad)
316325
elseif arglen < arglen2
317326
pos = padzero(buf, pos, arglen2 - arglen)
318327
end
@@ -380,7 +389,7 @@ function output_fmt_a(buf, pos, spec, neg, x)
380389
pos = outch(buf, pos, '0', upchar(spec, 'X'))
381390
if x == 0
382391
pos = outch(buf, pos, '0')
383-
prec > 0 && (pos = padzero(buf, pos, prec))
392+
prec > 0 && (pos = outch(buf, pos, '.'); pos = padzero(buf, pos, prec))
384393
return outch(buf, pos, upchar(spec, 'P'), '+', '0')
385394
end
386395
s, p = frexp(x)
@@ -471,26 +480,48 @@ end
471480

472481
function _fmt(buf, pos, spec::FmtSpec{T}, arg) where {T <: FmtFlts}
473482
# Make sure there is enough room
474-
width = spec.width
483+
width, prec, plus, space, hash = spec.width, spec.prec, spec.plus, spec.space, spec.altf
475484
buflen = sizeof(buf) - pos
476485
needed = max(width, 309 + 17 + 5)
477486
buflen < needed && resize!(buf, pos + needed)
478487

479488
x = tofloat(arg)
480489
if T === FmtFltE
481-
newpos = Ryu.writeexp(buf, pos, x, spec.prec, spec.plus, spec.space,
482-
spec.altf, upchar(spec, 'E'), UInt8('.'))
490+
newpos = Ryu.writeexp(buf, pos, x, prec, plus, space, hash, upchar(spec, 'E'), UInt8('.'))
483491
elseif T === FmtFltF
484-
newpos = Ryu.writefixed(buf, pos, x, spec.prec, spec.plus, spec.space, spec.altf,
485-
UInt8('.'))
492+
newpos = Ryu.writefixed(buf, pos, x, prec, plus, space, hash, UInt8('.'))
486493
elseif T === FmtFltG
487-
prec = spec.prec
488-
prec = prec == 0 ? 1 : prec
489-
x = round(x, sigdigits=prec)
490-
newpos = Ryu.writeshortest(buf, pos, x, spec.plus, spec.space, spec.altf, prec,
491-
upchar(spec, 'E'), true, UInt8('.'))
494+
if isinf(x) || isnan(x)
495+
newpos = Ryu.writeshortest(buf, pos, x, plus, space)
496+
else
497+
# C11-compliant general format
498+
prec = prec == 0 ? 1 : prec
499+
# format the value in scientific notation and parse the exponent part
500+
exp = let p = Ryu.writeexp(buf, pos, x, prec)
501+
b1, b2, b3, b4 = buf[p-4], buf[p-3], buf[p-2], buf[p-1]
502+
Z = UInt8('0')
503+
if b1 == UInt8('e')
504+
# two-digit exponent
505+
sign = b2 == UInt8('+') ? 1 : -1
506+
exp = 10 * (b3 - Z) + (b4 - Z)
507+
else
508+
# three-digit exponent
509+
sign = b1 == UInt8('+') ? 1 : -1
510+
exp = 100 * (b2 - Z) + 10 * (b3 - Z) + (b4 - Z)
511+
end
512+
flipsign(exp, sign)
513+
end
514+
if -4 exp < prec
515+
newpos = Ryu.writefixed(buf, pos, x, prec - (exp + 1), plus, space, hash,
516+
UInt8('.'), !hash)
517+
else
518+
newpos = Ryu.writeexp(buf, pos, x, prec - 1, plus, space, hash,
519+
upchar(spec, 'E'), UInt8('.'), !hash)
520+
end
521+
end
492522
elseif T === FmtFltA
493-
newpos = output_fmt_a(buf, pos, spec, x < 0, abs(x))
523+
x, neg = x < 0 || x === -Base.zero(x) ? (-x, true) : (x, false)
524+
newpos = output_fmt_a(buf, pos, spec, neg, x)
494525
end
495526
if newpos - pos < width
496527
# need to pad
@@ -500,8 +531,8 @@ function _fmt(buf, pos, spec::FmtSpec{T}, arg) where {T <: FmtFlts}
500531
else
501532
# right aligned
502533
n = width - (newpos - pos)
503-
if spec.zero
504-
ex = (arg < 0 || (spec.plus | spec.space)) + ifelse(T === FmtFltA, 2, 0)
534+
if spec.zero && isfinite(x)
535+
ex = (arg < 0 || (plus | space)) + ifelse(T === FmtFltA, 2, 0)
505536
so = pos + ex
506537
len = (newpos - pos) - ex
507538
copyto!(buf, so + n, buf, so, len)
@@ -523,12 +554,14 @@ end
523554

524555
# pointers
525556
_fmt(buf, pos, spec::FmtSpec{FmtPtr}, arg) =
526-
_fmt(buf, pos, ptrfmt(spec, arg), UInt(arg))
557+
_fmt(buf, pos, ptrfmt(spec, arg), UInt64(arg))
527558

528559
@inline _dec_len1(v) = ifelse(v < 100, ifelse(v < 10, 1, 2), 3)
529560
@inline _dec_len2(v) = v < 1_000 ? _dec_len1(v) : ifelse(v < 10_000, 4, 5)
530561
@inline _dec_len4(v) = v < 100_000 ? _dec_len2(v) :
531-
(v < 10_000_000 ? ifelse(v < 1_000_000, 6, 7) : ifelse(v < 100_000_000, 8, 9))
562+
(v < 10_000_000
563+
? ifelse(v < 1_000_000, 6, 7)
564+
: ifelse(v < 100_000_000, 8, ifelse(v < 1_000_000_000, 9, 10)))
532565
@inline function _dec_len8(v)
533566
if v < 1_000_000_000 # 1 - 9 digits
534567
_dec_len4(v)

0 commit comments

Comments
 (0)