mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The compiler stubs for testing the OSLog implementation are in need of an update. This change updates the stubs to be consistent with the current OSLog implementation, updates the existing tests, and adds new tests for String and metatype interpolations. rdar://69719729
427 lines
15 KiB
Swift
427 lines
15 KiB
Swift
//===--------------------------- OSLogFloatFormatting.swift ------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===------------------------------------------------------------------------------===//
|
|
|
|
// This file defines types and functions for specifying formatting of
|
|
// floating-point typed interpolations passed to the os log APIs.
|
|
|
|
@frozen
|
|
public struct OSLogFloatFormatting {
|
|
/// When set, a `+` will be printed for all non-negative floats.
|
|
@usableFromInline
|
|
internal var explicitPositiveSign: Bool
|
|
|
|
/// Whether to use uppercase letters to represent numerals greater than 9
|
|
/// (default is to use lowercase). This applies to hexadecimal digits, NaN, Inf,
|
|
/// the symbols E and X used to denote exponent and hex format.
|
|
@usableFromInline
|
|
internal var uppercase: Bool
|
|
|
|
// Note: includePrefix is not supported for FloatFormatting. The format specifier %a
|
|
// always prints a prefix, %efg don't need one.
|
|
|
|
/// Number of digits to display following the radix point. Hex notation does not accept
|
|
/// a precision. For non-hex notations, precision can be a dynamic value. The default
|
|
/// precision is 6 for non-hex notations.
|
|
@usableFromInline
|
|
internal var precision: (() -> Int)?
|
|
|
|
@usableFromInline
|
|
internal enum Notation {
|
|
/// Hexadecimal formatting.
|
|
case hex
|
|
|
|
/// fprintf's `%f` formatting.
|
|
///
|
|
/// Prints all digits before the radix point, and `precision` digits following
|
|
/// the radix point. If `precision` is zero, the radix point is omitted.
|
|
///
|
|
/// Note that very large floating-point values may print quite a lot of digits
|
|
/// when using this format, even if `precision` is zero--up to hundreds for
|
|
/// `Double`, and thousands for `Float80`. Note also that this format is
|
|
/// very likely to print non-zero values as all-zero. If these are a concern, use
|
|
/// `.exponential` or `.hybrid` instead.
|
|
///
|
|
/// Systems may impose an upper bound on the number of digits that are
|
|
/// supported following the radix point.
|
|
case fixed
|
|
|
|
/// fprintf's `%e` formatting.
|
|
///
|
|
/// Prints the number in the form [-]d.ddd...dde±dd, with `precision` significant
|
|
/// digits following the radix point. Systems may impose an upper bound on the number
|
|
/// of digits that are supported.
|
|
case exponential
|
|
|
|
/// fprintf's `%g` formatting.
|
|
///
|
|
/// Behaves like `.fixed` when the number is scaled close to 1.0, and like
|
|
/// `.exponential` if it has a very large or small exponent.
|
|
case hybrid
|
|
}
|
|
|
|
@usableFromInline
|
|
internal var notation: Notation
|
|
|
|
@_transparent
|
|
@usableFromInline
|
|
internal init(
|
|
explicitPositiveSign: Bool = false,
|
|
uppercase: Bool = false,
|
|
precision: (() -> Int)?,
|
|
notation: Notation
|
|
) {
|
|
self.explicitPositiveSign = explicitPositiveSign
|
|
self.uppercase = uppercase
|
|
self.precision = precision
|
|
self.notation = notation
|
|
}
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%f` format with
|
|
/// default precision.
|
|
///
|
|
/// Prints all digits before the radix point, and 6 digits following the radix point.
|
|
/// Note also that this format is very likely to print non-zero values as all-zero.
|
|
///
|
|
/// Note that very large floating-point values may print quite a lot of digits
|
|
/// when using this format --up to hundreds for `Double`. Note also that this
|
|
/// format is very likely to print non-zero values as all-zero. If these are a concern,
|
|
/// use `.exponential` or `.hybrid` instead.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static var fixed: OSLogFloatFormatting { .fixed() }
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%f` format with
|
|
/// specified precision, and optional sign and case.
|
|
///
|
|
/// Prints all digits before the radix point, and `precision` digits following
|
|
/// the radix point. If `precision` is zero, the radix point is omitted.
|
|
///
|
|
/// Note that very large floating-point values may print quite a lot of digits
|
|
/// when using this format, even if `precision` is zero--up to hundreds for
|
|
/// `Double`. Note also that this format is very likely to print non-zero values as
|
|
/// all-zero. If these are a concern, use `.exponential` or `.hybrid` instead.
|
|
///
|
|
/// Systems may impose an upper bound on the number of digits that are
|
|
/// supported following the radix point.
|
|
///
|
|
/// All parameters to this function except `precision` must be boolean literals.
|
|
///
|
|
/// - Parameters:
|
|
/// - precision: Number of digits to display after the radix point.
|
|
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
|
/// numbers.
|
|
/// - uppercase: Pass `true` to use uppercase letters or `false` to use
|
|
/// lowercase letters. The default is `false`.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static func fixed(
|
|
precision: @escaping @autoclosure () -> Int,
|
|
explicitPositiveSign: Bool = false,
|
|
uppercase: Bool = false
|
|
) -> OSLogFloatFormatting {
|
|
return OSLogFloatFormatting(
|
|
explicitPositiveSign: explicitPositiveSign,
|
|
uppercase: uppercase,
|
|
precision: precision,
|
|
notation: .fixed
|
|
)
|
|
}
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%f` format with
|
|
/// default precision, and optional sign and case.
|
|
///
|
|
/// Prints all digits before the radix point, and 6 digits following the radix point.
|
|
/// Note also that this format is very likely to print non-zero values as all-zero.
|
|
///
|
|
/// Note that very large floating-point values may print quite a lot of digits
|
|
/// when using this format, even if `precision` is zero--up to hundreds for
|
|
/// `Double`. Note also that this format is very likely to print non-zero values as
|
|
/// all-zero. If these are a concern, use `.exponential` or `.hybrid` instead.
|
|
///
|
|
/// Systems may impose an upper bound on the number of digits that are
|
|
/// supported following the radix point.
|
|
///
|
|
/// All parameters to this function must be boolean literals.
|
|
/// - Parameters:
|
|
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
|
/// numbers.
|
|
/// - uppercase: Pass `true` to use uppercase letters or `false` to use
|
|
/// lowercase letters. The default is `false`.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static func fixed(
|
|
explicitPositiveSign: Bool = false,
|
|
uppercase: Bool = false
|
|
) -> OSLogFloatFormatting {
|
|
return OSLogFloatFormatting(
|
|
explicitPositiveSign: explicitPositiveSign,
|
|
uppercase: uppercase,
|
|
precision: nil,
|
|
notation: .fixed
|
|
)
|
|
}
|
|
|
|
/// Displays an interpolated floating-point value in hexadecimal format.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static var hex: OSLogFloatFormatting { .hex() }
|
|
|
|
/// Displays an interpolated floating-point value in hexadecimal format with
|
|
/// optional sign and case.
|
|
///
|
|
/// All parameters to this function must be boolean literals.
|
|
///
|
|
/// - Parameters:
|
|
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
|
/// numbers.
|
|
/// - uppercase: Pass `true` to use uppercase letters or `false` to use
|
|
/// lowercase letters. The default is `false`.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static func hex(
|
|
explicitPositiveSign: Bool = false,
|
|
uppercase: Bool = false
|
|
) -> OSLogFloatFormatting {
|
|
return OSLogFloatFormatting(
|
|
explicitPositiveSign: explicitPositiveSign,
|
|
uppercase: uppercase,
|
|
precision: nil,
|
|
notation: .hex
|
|
)
|
|
}
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%e` format.
|
|
///
|
|
/// Prints the number in the form [-]d.ddd...dde±dd.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static var exponential: OSLogFloatFormatting { .exponential() }
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%e` format with
|
|
/// specified precision, and optional sign and case.
|
|
///
|
|
/// Prints the number in the form [-]d.ddd...dde±dd, with `precision` significant
|
|
/// digits following the radix point. Systems may impose an upper bound on the number
|
|
/// of digits that are supported.
|
|
///
|
|
/// All parameters except `precision` must be boolean literals.
|
|
///
|
|
/// - Parameters:
|
|
/// - precision: Number of digits to display after the radix point.
|
|
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
|
/// numbers.
|
|
/// - uppercase: Pass `true` to use uppercase letters or `false` to use
|
|
/// lowercase letters. The default is `false`.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static func exponential(
|
|
precision: @escaping @autoclosure () -> Int,
|
|
explicitPositiveSign: Bool = false,
|
|
uppercase: Bool = false
|
|
) -> OSLogFloatFormatting {
|
|
return OSLogFloatFormatting(
|
|
explicitPositiveSign: explicitPositiveSign,
|
|
uppercase: uppercase,
|
|
precision: precision,
|
|
notation: .exponential
|
|
)
|
|
}
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%e` format with
|
|
/// an optional sign and case.
|
|
///
|
|
/// Prints the number in the form [-]d.ddd...dde±dd.
|
|
///
|
|
/// All parameters to this function must be boolean literals.
|
|
///
|
|
/// - Parameters:
|
|
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
|
/// numbers.
|
|
/// - uppercase: Pass `true` to use uppercase letters or `false` to use
|
|
/// lowercase letters. The default is `false`.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static func exponential(
|
|
explicitPositiveSign: Bool = false,
|
|
uppercase: Bool = false
|
|
) -> OSLogFloatFormatting {
|
|
return OSLogFloatFormatting(
|
|
explicitPositiveSign: explicitPositiveSign,
|
|
uppercase: uppercase,
|
|
precision: nil,
|
|
notation: .exponential
|
|
)
|
|
}
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%g` format.
|
|
///
|
|
/// Behaves like `.fixed` when the number is scaled close to 1.0, and like
|
|
/// `.exponential` if it has a very large or small exponent.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static var hybrid: OSLogFloatFormatting { .hybrid() }
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%g` format with the
|
|
/// specified precision, and optional sign and case.
|
|
///
|
|
/// Behaves like `.fixed` when the number is scaled close to 1.0, and like
|
|
/// `.exponential` if it has a very large or small exponent.
|
|
///
|
|
/// All parameters except `precision` must be boolean literals.
|
|
///
|
|
/// - Parameters:
|
|
/// - precision: Number of digits to display after the radix point.
|
|
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
|
/// numbers.
|
|
/// - uppercase: Pass `true` to use uppercase letters or `false` to use
|
|
/// lowercase letters. The default is `false`.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static func hybrid(
|
|
precision: @escaping @autoclosure () -> Int,
|
|
explicitPositiveSign: Bool = false,
|
|
uppercase: Bool = false
|
|
) -> OSLogFloatFormatting {
|
|
return OSLogFloatFormatting(
|
|
explicitPositiveSign: explicitPositiveSign,
|
|
uppercase: uppercase,
|
|
precision: precision,
|
|
notation: .hybrid
|
|
)
|
|
}
|
|
|
|
/// Displays an interpolated floating-point value in fprintf's `%g` format with
|
|
/// optional sign and case.
|
|
///
|
|
/// Behaves like `.fixed` when the number is scaled close to 1.0, and like
|
|
/// `.exponential` if it has a very large or small exponent.
|
|
///
|
|
/// All parameters to this function must be boolean literals.
|
|
///
|
|
/// - Parameters:
|
|
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
|
/// numbers.
|
|
/// - uppercase: Pass `true` to use uppercase letters or `false` to use
|
|
/// lowercase letters. The default is `false`.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
public static func hybrid(
|
|
explicitPositiveSign: Bool = false,
|
|
uppercase: Bool = false
|
|
) -> OSLogFloatFormatting {
|
|
return OSLogFloatFormatting(
|
|
explicitPositiveSign: explicitPositiveSign,
|
|
uppercase: uppercase,
|
|
precision: nil,
|
|
notation: .hybrid
|
|
)
|
|
}
|
|
}
|
|
|
|
extension OSLogFloatFormatting {
|
|
/// Returns a fprintf-compatible length modifier for a given argument type
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
internal static func _formatStringLengthModifier<I: FloatingPoint>(
|
|
_ type: I.Type
|
|
) -> String? {
|
|
switch type {
|
|
// fprintf formatters promote Float to Double
|
|
case is Float.Type: return ""
|
|
case is Double.Type: return ""
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
// fprintf formatters use L for Float80
|
|
case is Float80.Type: return "L"
|
|
#endif
|
|
default: return nil
|
|
}
|
|
}
|
|
|
|
/// Constructs an os_log format specifier for the given type `type`
|
|
/// using the specified alignment `align` and privacy qualifier `privacy`.
|
|
@_semantics("constant_evaluable")
|
|
@inlinable
|
|
@_optimize(none)
|
|
internal func formatSpecifier<I: FloatingPoint>(
|
|
for type: I.Type,
|
|
align: OSLogStringAlignment,
|
|
privacy: OSLogPrivacy
|
|
) -> String {
|
|
var specification = "%"
|
|
// Add privacy qualifier after % sign within curly braces. This is an
|
|
// os log specific flag.
|
|
if let privacySpecifier = privacy.privacySpecifier {
|
|
specification += "{"
|
|
specification += privacySpecifier
|
|
specification += "}"
|
|
}
|
|
|
|
// 1. Flags
|
|
// IEEE: `+` The result of a signed conversion shall always begin with a sign
|
|
// ( '+' or '-' )
|
|
if explicitPositiveSign {
|
|
specification += "+"
|
|
}
|
|
|
|
// IEEE: `-` The result of the conversion shall be left-justified within the field.
|
|
// The conversion is right-justified if this flag is not specified.
|
|
if case .start = align.anchor {
|
|
specification += "-"
|
|
}
|
|
|
|
if let _ = align.minimumColumnWidth {
|
|
// The alignment could be a dynamic value. Therefore, use a star here and pass it
|
|
// as an additional argument.
|
|
specification += "*"
|
|
}
|
|
|
|
if let _ = precision {
|
|
specification += ".*"
|
|
}
|
|
|
|
guard let lengthModifier =
|
|
OSLogFloatFormatting._formatStringLengthModifier(type) else {
|
|
fatalError("Float type has unknown length")
|
|
}
|
|
specification += lengthModifier
|
|
|
|
// 3. Precision and conversion specifier.
|
|
switch notation {
|
|
case .fixed:
|
|
specification += (uppercase ? "F" : "f")
|
|
case .exponential:
|
|
specification += (uppercase ? "E" : "e")
|
|
case .hybrid:
|
|
specification += (uppercase ? "G" : "g")
|
|
case .hex:
|
|
//guard type.radix == 2 else { return nil }
|
|
specification += (uppercase ? "A" : "a")
|
|
default:
|
|
fatalError("Unknown float notation")
|
|
}
|
|
return specification
|
|
}
|
|
}
|
|
|