1+ # C-style formatting functions, originally based on Printf.jl
2+
13using Base. Ryu
24
35abstract 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
206212
207213function _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+
280289function _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
472481function _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