1
+ # C-style formatting functions, originally based on Printf.jl
2
+
1
3
using Base. Ryu
2
4
3
5
abstract type FmtType end
@@ -117,7 +119,7 @@ function FmtSpec(f::AbstractString)
117
119
end
118
120
end
119
121
# parse prec
120
- prec = - 1
122
+ prec = 0
121
123
parsedprecdigits = false
122
124
if b == UInt8 (' .' )
123
125
pos > len && throw (ArgumentError (" incomplete format string: '$f '" ))
@@ -141,6 +143,8 @@ function FmtSpec(f::AbstractString)
141
143
# parse length modifier (ignored)
142
144
if b == UInt8 (' h' ) || b == UInt8 (' l' )
143
145
prev = b
146
+ pos > len &&
147
+ throw (ArgumentError (" format string - length modifier is missing type specifier: '$f '" ))
144
148
b = bytes[pos]
145
149
pos += 1
146
150
if b == prev
@@ -149,6 +153,8 @@ function FmtSpec(f::AbstractString)
149
153
pos += 1
150
154
end
151
155
elseif b in b " Ljqtz"
156
+ pos > len &&
157
+ throw (ArgumentError (" format string - length modifier is missing type specifier: '$f '" ))
152
158
b = bytes[pos]
153
159
pos += 1
154
160
end
206
212
207
213
function _fmt (buf, pos, spec:: FmtSpec{FmtChr} , arg)
208
214
ch = arg isa String ? arg[1 ] : Char (arg)
209
- width = spec. width - 1
215
+ width = spec. width - textwidth (ch)
210
216
width <= 0 && return writechar (buf, pos, ch)
211
217
if spec. leftalign
212
218
padn (buf, writechar (buf, pos, ch), width)
@@ -277,21 +283,24 @@ _fmt(buf, pos, spec::FmtSpec{<:FmtInts}, arg::BaseUns) =
277
283
_fmt (buf, pos, spec:: FmtSpec{<:FmtInts} , arg:: BaseInt ) =
278
284
_fmt (buf, pos, spec, arg < 0 , unsigned (abs (arg)))
279
285
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
+
280
289
function _fmt (buf, pos, spec:: FmtSpec{F} , neg, x:: T ) where {F<: FmtInts ,T<: Union{String,BaseUns} }
281
290
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)
285
292
i = n
286
293
arglen = n + (neg || (spec. plus | spec. space)) +
287
294
((spec. altf && (F != = FmtDec)) ? ifelse (F === FmtOct, 1 , 2 ) : 0 )
288
295
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
290
299
291
300
# Make sure that remaining output buffer is large enough
292
301
# 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 )
295
304
296
305
! spec. leftalign && ! spec. zero && arglen2 < width &&
297
306
(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{
312
321
if spec. zero && arglen2 < width
313
322
pos = padzero (buf, pos, width - arglen2)
314
323
elseif n < prec
315
- pos = padzero (buf, pos, prec - n )
324
+ pos = padzero (buf, pos, precpad )
316
325
elseif arglen < arglen2
317
326
pos = padzero (buf, pos, arglen2 - arglen)
318
327
end
@@ -380,7 +389,7 @@ function output_fmt_a(buf, pos, spec, neg, x)
380
389
pos = outch (buf, pos, ' 0' , upchar (spec, ' X' ))
381
390
if x == 0
382
391
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))
384
393
return outch (buf, pos, upchar (spec, ' P' ), ' +' , ' 0' )
385
394
end
386
395
s, p = frexp (x)
@@ -471,26 +480,48 @@ end
471
480
472
481
function _fmt (buf, pos, spec:: FmtSpec{T} , arg) where {T <: FmtFlts }
473
482
# 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
475
484
buflen = sizeof (buf) - pos
476
485
needed = max (width, 309 + 17 + 5 )
477
486
buflen < needed && resize! (buf, pos + needed)
478
487
479
488
x = tofloat (arg)
480
489
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 (' .' ))
483
491
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 (' .' ))
486
493
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
492
522
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)
494
525
end
495
526
if newpos - pos < width
496
527
# need to pad
@@ -500,8 +531,8 @@ function _fmt(buf, pos, spec::FmtSpec{T}, arg) where {T <: FmtFlts}
500
531
else
501
532
# right aligned
502
533
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 )
505
536
so = pos + ex
506
537
len = (newpos - pos) - ex
507
538
copyto! (buf, so + n, buf, so, len)
@@ -523,12 +554,14 @@ end
523
554
524
555
# pointers
525
556
_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))
527
558
528
559
@inline _dec_len1 (v) = ifelse (v < 100 , ifelse (v < 10 , 1 , 2 ), 3 )
529
560
@inline _dec_len2 (v) = v < 1_000 ? _dec_len1 (v) : ifelse (v < 10_000 , 4 , 5 )
530
561
@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 )))
532
565
@inline function _dec_len8 (v)
533
566
if v < 1_000_000_000 # 1 - 9 digits
534
567
_dec_len4 (v)
0 commit comments