//===--- StaticPrint.swift ------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021 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
//
//===----------------------------------------------------------------------===//

#if SWIFT_STDLIB_STATIC_PRINT

import SwiftShims

extension String {
  /// Replace all percents "%" in the string by "%%" so that the string can be
  /// interpreted as a C format string. This function is constant evaluable
  /// and its semantics is modeled within the evaluator.
  @inlinable
  internal var percentEscapedString: String {
    @_semantics("string.escapePercent.get")
    @_effects(readonly)
    @_optimize(none)
    get {
      return self
        .split(separator: "%", omittingEmptySubsequences: false)
        .joined(separator: "%%")
    }
  }
}

extension ConstantVPrintFInterpolation {

  /// Defines interpolation for UnsafeRawBufferPointer.
  ///
  /// Do not call this function directly.
  ///
  /// - Parameters:
  ///   - pointer: The interpolated expression of type `UnsafeRawBufferPointer`,
  ///     which is autoclosured.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ pointer: @autoclosure @escaping () -> UnsafeRawBufferPointer
  ) {
    unsafe appendInterpolation(pointer().baseAddress!)
  }

  /// Defines interpolation for UnsafeRawPointer.
  ///
  /// Do not call this function directly.
  ///
  /// - Parameters:
  ///   - pointer: The interpolated expression of type `UnsafeRawPointer`, which is autoclosured.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ pointer: @autoclosure @escaping () -> UnsafeRawPointer
  ) {
    formatString += "%p"
    unsafe arguments.append(pointer)
  }
}

@frozen
public struct ConstantVPrintFIntegerFormatting {
  /// The base to use for the string representation. `radix` must be at least 2
  /// and at most 36. The default is 10.
  @usableFromInline
  internal var radix: Int

  /// When set, a `+` will be printed for all non-negative integers.
  @usableFromInline
  internal var explicitPositiveSign: Bool

  /// When set, a prefix: 0b or 0o or 0x will be added when the radix is 2, 8 or
  /// 16 respectively.
  @usableFromInline
  internal var includePrefix: Bool

  /// Whether to use uppercase letters to represent numerals
  /// greater than 9 (default is to use lowercase).
  @usableFromInline
  internal var uppercase: Bool

  /// Minimum number of digits to display. Numbers having fewer digits than
  /// minDigits will be displayed with leading zeros.
  @usableFromInline
  internal var minDigits: (() -> Int)?

  /// Initializes all stored properties.
  ///
  /// - Parameters:
  ///   - radix: The base to use for the string representation. `radix` must be
  ///     at least 2 and at most 36. The default is 10.
  ///   - explicitPositiveSign: Pass `true` to add a + sign to non-negative
  ///     numbers. Default is `false`.
  ///   - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding
  ///     radices. Default is `false`.
  ///   - uppercase: Pass `true` to use uppercase letters to represent numerals
  ///     greater than 9, or `false` to use lowercase letters. The default is
  ///     `false`.
  ///   - minDigits: minimum number of digits to display. Numbers will be
  ///     prefixed with zeros if necessary to meet the minimum. The default is 1.
  @_transparent
  @usableFromInline
  internal init(
    radix: Int = 10,
    explicitPositiveSign: Bool = false,
    includePrefix: Bool = false,
    uppercase: Bool = false,
    minDigits: (() -> Int)?
  ) {
    self.radix = radix
    self.explicitPositiveSign = explicitPositiveSign
    self.includePrefix = includePrefix
    self.uppercase = uppercase
    self.minDigits = minDigits
  }

  /// Displays an interpolated integer as a decimal number with the specified number
  /// of digits and an optional sign.
  ///
  /// The parameter `explicitPositiveSign` must be a boolean literal. The
  /// parameter `minDigits` can be an arbitrary expression.
  ///
  /// - Parameters:
  ///   - explicitPositiveSign: Pass `true` to add a + sign to non-negative
  ///     numbers.
  ///   - minDigits: minimum number of digits to display. Numbers will be
  ///     prefixed with zeros if necessary to meet the minimum.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static func decimal(
    explicitPositiveSign: Bool = false,
    minDigits: @escaping @autoclosure () -> Int
  ) -> ConstantVPrintFIntegerFormatting {
    return ConstantVPrintFIntegerFormatting(
      radix: 10,
      explicitPositiveSign: explicitPositiveSign,
      minDigits: minDigits)
  }

  /// Displays an interpolated integer as a decimal number with an optional sign.
  ///
  /// The parameter `explicitPositiveSign` must be a boolean literal.
  ///
  /// - Parameters:
  ///   - explicitPositiveSign: Pass `true` to add a + sign to non-negative
  ///     numbers.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static func decimal(
    explicitPositiveSign: Bool = false
  ) -> ConstantVPrintFIntegerFormatting {
    return ConstantVPrintFIntegerFormatting(
      radix: 10,
      explicitPositiveSign: explicitPositiveSign,
      minDigits: nil)
  }

  /// Displays an interpolated integer as a decimal number. This is the default format for
  /// integers.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static var decimal: ConstantVPrintFIntegerFormatting { .decimal() }

  /// Displays an interpolated unsigned integer as a hexadecimal number with the
  /// specified parameters. This formatting option should be used only with unsigned
  /// integers.
  ///
  /// All parameters except `minDigits` should be boolean literals. `minDigits`
  /// can be an arbitrary expression.
  ///
  /// - Parameters:
  ///   - explicitPositiveSign: Pass `true` to add a + sign to non-negative
  ///     numbers.
  ///   - includePrefix: Pass `true` to add a prefix 0x.
  ///   - uppercase: Pass `true` to use uppercase letters to represent numerals
  ///     greater than 9, or `false` to use lowercase letters. The default is `false`.
  ///   - minDigits: minimum number of digits to display. Numbers will be
  ///     prefixed with zeros if necessary to meet the minimum.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static func hex(
    explicitPositiveSign: Bool = false,
    includePrefix: Bool = false,
    uppercase: Bool = false,
    minDigits: @escaping @autoclosure () -> Int
  ) -> ConstantVPrintFIntegerFormatting {
    return ConstantVPrintFIntegerFormatting(
      radix: 16,
      explicitPositiveSign: explicitPositiveSign,
      includePrefix: includePrefix,
      uppercase: uppercase,
      minDigits: minDigits)
  }

  /// Displays an interpolated unsigned integer as a hexadecimal number with the specified
  /// parameters. This formatting option should be used only with unsigned integers.
  ///
  /// All parameters  should be boolean literals.
  ///
  /// - Parameters:
  ///   - explicitPositiveSign: Pass `true` to add a + sign to non-negative
  ///     numbers.
  ///   - includePrefix: Pass `true` to add a prefix 0x.
  ///   - uppercase: Pass `true` to use uppercase letters to represent numerals
  ///     greater than 9, or `false` to use lowercase letters. The default is `false`.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static func hex(
    explicitPositiveSign: Bool = false,
    includePrefix: Bool = false,
    uppercase: Bool = false
  ) -> ConstantVPrintFIntegerFormatting {
    return ConstantVPrintFIntegerFormatting(
      radix: 16,
      explicitPositiveSign: explicitPositiveSign,
      includePrefix: includePrefix,
      uppercase: uppercase,
      minDigits: nil)
  }

  /// Displays an interpolated unsigned integer as a hexadecimal number.
  /// This formatting option should be used only with unsigned integers.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static var hex: ConstantVPrintFIntegerFormatting { .hex() }

  /// Displays an interpolated unsigned integer as an octal number with the specified
  /// parameters. This formatting option should be used only with unsigned
  /// integers.
  ///
  /// All parameters except `minDigits` should be boolean literals. `minDigits`
  /// can be an arbitrary expression.
  ///
  /// - Parameters:
  ///   - explicitPositiveSign: Pass `true` to add a + sign to non-negative
  ///     numbers.
  ///   - includePrefix: Pass `true` to add a prefix 0o.
  ///   - uppercase: Pass `true` to use uppercase letters to represent numerals
  ///     greater than 9, or `false` to use lowercase letters. The default is `false`.
  ///   - minDigits: minimum number of digits to display. Numbers will be
  ///     prefixed with zeros if necessary to meet the minimum.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static func octal(
    explicitPositiveSign: Bool = false,
    includePrefix: Bool = false,
    uppercase: Bool = false,
    minDigits: @autoclosure @escaping () -> Int
  ) -> ConstantVPrintFIntegerFormatting {
    ConstantVPrintFIntegerFormatting(
      radix: 8,
      explicitPositiveSign: explicitPositiveSign,
      includePrefix: includePrefix,
      uppercase: uppercase,
      minDigits: minDigits)
  }

  /// Displays an interpolated unsigned integer as an octal number with the specified parameters.
  /// This formatting option should be used only with unsigned integers.
  ///
  /// All parameters must be boolean literals.
  ///
  /// - Parameters:
  ///   - explicitPositiveSign: Pass `true` to add a + sign to non-negative
  ///     numbers.
  ///   - includePrefix: Pass `true` to add a prefix 0o.
  ///   - uppercase: Pass `true` to use uppercase letters to represent numerals
  ///     greater than 9, or `false` to use lowercase letters.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static func octal(
    explicitPositiveSign: Bool = false,
    includePrefix: Bool = false,
    uppercase: Bool = false
  ) -> ConstantVPrintFIntegerFormatting {
    ConstantVPrintFIntegerFormatting(
      radix: 8,
      explicitPositiveSign: explicitPositiveSign,
      includePrefix: includePrefix,
      uppercase: uppercase,
      minDigits: nil)
  }

  /// Displays an interpolated unsigned integer as an octal number.
  /// This formatting option should be used only with unsigned integers.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public static var octal: ConstantVPrintFIntegerFormatting { .octal() }
}

extension ConstantVPrintFIntegerFormatting {
  /// The prefix for the radix in the Swift literal syntax.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  internal var _prefix: String {
    guard includePrefix else { return "" }
    switch radix {
    case 2: return "0b"
    case 8: return "0o"
    case 16: return "0x"
    default: return ""
    }
  }
}


extension ConstantVPrintFIntegerFormatting {

  /// Returns a fprintf-compatible length modifier for a given argument type.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  internal static func formatSpecifierLengthModifier<I: FixedWidthInteger>(
    _ type: I.Type
  ) -> String? {
    // IEEE Std 1003.1-2017, length modifiers:
    switch type {
    //   hh - d, i, o, u, x, or X conversion specifier applies to
    // (signed|unsigned) char
    case is CChar.Type: return "hh"
    case is CUnsignedChar.Type: return "hh"

    //   h  - d, i, o, u, x, or X conversion specifier applies to
    // (signed|unsigned) short
    case is CShort.Type: return "h"
    case is CUnsignedShort.Type: return "h"

    case is CInt.Type: return ""
    case is CUnsignedInt.Type: return ""

    //   l  - d, i, o, u, x, or X conversion specifier applies to
    // (signed|unsigned) long
    case is CLong.Type: return "l"
    case is CUnsignedLong.Type: return "l"

    //   ll - d, i, o, u, x, or X conversion specifier applies to
    // (signed|unsigned) long long
    case is CLongLong.Type: return "ll"
    case is CUnsignedLongLong.Type: return "ll"

    default: return nil
    }
  }

  /// Constructs an os_log format specifier for the given `type`.
  @_semantics("constant_evaluable")
  @_alwaysEmitIntoClient
  @_optimize(none)
  @_effects(readonly)
  internal func formatSpecifier<I: FixedWidthInteger>(
    for type: I.Type,
    attributes: String
  ) -> String {
    // Based on IEEE Std 1003.1-2017
    // `d`/`i` is the only signed integral conversions allowed
    if (type.isSigned && radix != 10) {
      fatalError("Signed integers must be formatted using .decimal")
    }

    // IEEE: Each conversion specification is introduced by the '%' character
    // after which the following appear in sequence:
    //   1. Zero or more flags (in any order), which modify the meaning of the
    //      conversion specification.
    //   2. An optional minimum field width (for alignment). If the converted
    //      value has fewer bytes than the field width, it shall be padded with
    //      <space> characters by default on the left; it shall be padded on the
    //      right if the left-adjustment flag ( '-' ), is given to the
    //      field width.
    //   3. An optional precision that gives the minimum number of digits to
    //      display for the d, i, o, u, x, and X conversion specifiers.
    //   4. An optional length modifier that specifies the size of the argument.
    //   5. A conversion specifier character that indicates the type of
    //      conversion to be applied.

    // Use Swift style prefixes rather than fprintf style prefixes.
    var specification = _prefix
    specification += "%"

    //
    // 1. Flags
    //
    // Use `+` flag if signed, otherwise prefix a literal `+` for unsigned.
    if explicitPositiveSign {
      // IEEE: `+` The result of a signed conversion shall always begin with a
      // sign ( '+' or '-' )
      if type.isSigned {
        specification += "+"
      } else {
        var newSpecification = "+"
        newSpecification += specification
        specification = newSpecification
      }
    }

    // 3. Precision

    // Default precision for integers is 1, otherwise use requested precision.
    // The precision could be a dynamic value.
    // Therefore, use a star here and pass it as an additional argument.
    if let _ = minDigits {
      specification += ".*"
    }

    // 4. Length modifier
    guard let lengthModifier =
      ConstantVPrintFIntegerFormatting.formatSpecifierLengthModifier(type) else {
      fatalError("Integer type has unknown byte length")
    }
    specification += lengthModifier

    // 5. The conversion specifier
    switch radix {
    case 10:
      specification += type.isSigned ? "d" : "u"
    case 8:
      specification += "o"
    case 16:
      specification += uppercase ? "X" : "x"
    default:
      fatalError("radix must be 10, 8 or 16")
    }
    return specification
  }
}

@frozen
@usableFromInline
internal struct ConstantVPrintFArguments {
  @usableFromInline
  internal var argumentClosures: [(([Int]) -> ()) -> ()]

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  internal init() {
    argumentClosures = []
  }

  /// `append` for other types must be implemented by extensions.
}

extension ConstantVPrintFInterpolation {

  /// Defines interpolation for expressions of type Int.
  ///
  /// Do not call this function directly. It will be called automatically when interpolating
  /// a value of type `Int` in the string interpolations passed to the log APIs.
  ///
  /// - Parameters:
  ///   - number: The interpolated expression of type Int, which is autoclosured.
  ///   - format: A formatting option available for integer types, defined by the
  ///     type: `ConstantVPrintFIntegerFormatting`. The default is `.decimal`.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> Int,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  // Define appendInterpolation overloads for fixed-size integers.

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> Int8,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> Int16,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> Int32,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> Int64,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  /// Defines interpolation for expressions of type UInt.
  ///
  /// Do not call this function directly. It will be called automatically when interpolating
  /// a value of type `Int` in the string interpolations passed to the log APIs.
  ///
  /// - Parameters:
  ///   - number: The interpolated expression of type UInt, which is autoclosured.
  ///   - format: A formatting option available for integer types, defined by the
  ///     type `ConstantVPrintFIntegerFormatting`.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> UInt,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  // Define appendInterpolation overloads for unsigned integers.

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> UInt8,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> UInt16,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> UInt32,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ number: @autoclosure @escaping () -> UInt64,
    format: ConstantVPrintFIntegerFormatting = .decimal
  ) {
    appendInteger(number, format: format)
  }

  /// Defines interpolation for expressions of type Int.
  ///
  /// Do not call this function directly. It will be called automatically when interpolating
  /// a value of type `Int` in the string interpolations passed to the log APIs.
  ///
  /// - Parameters:
  ///   - number: The interpolated expression of type Int, which is autoclosured.
  ///   - format: A formatting option available for integer types, defined by the
  ///     type: `ConstantVPrintFIntegerFormatting`. The default is `.decimal`.
  ///   - attributes: A string that specifies an attribute for the interpolated value,
  ///     which can be used to provide additional information about the interpolated
  ///     value to tools such as Xcode that can process and render os_log and os_signpost
  ///     messages. An example of an attribute is "xcode:size-in-bytes". If the target tool
  ///     that processes these messages doesn't understand the attribute it would be ignored.
  @_semantics("constant_evaluable")
  @_alwaysEmitIntoClient
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation<T: FixedWidthInteger>(
    _ number: @autoclosure @escaping () -> T,
    format: ConstantVPrintFIntegerFormatting = .decimal,
    attributes: String
  ) {
    appendInteger(number, format: format, attributes: attributes)
  }

  /// Given an integer, create and append a format specifier for the integer to the
  /// format string property. Also, append the integer along with necessary headers
  /// to the ConstantVPrintFArguments property.
  @_semantics("constant_evaluable")
  @_alwaysEmitIntoClient
  @_optimize(none)
  internal mutating func appendInteger<T>(
    _ number: @escaping () -> T,
    format: ConstantVPrintFIntegerFormatting,
    attributes: String = ""
  ) where T: FixedWidthInteger {
    formatString += format.formatSpecifier(for: T.self,  attributes: attributes)
    arguments.append(number)
  }
}

@frozen
public struct ConstantVPrintFInterpolation : StringInterpolationProtocol {
  /// A format string constructed from the given string interpolation to be
  /// passed to vprintf
  @usableFromInline
  internal var formatString: String

  @usableFromInline
  internal var arguments: ConstantVPrintFArguments

  // Some methods defined below are marked @_optimize(none) to prevent inlining
  // of string internals (such as String._StringGuts) which will interfere with
  // constant evaluation and folding. Note that these methods will be inlined,
  // constant evaluated/folded and optimized in the context of a caller.

  @_semantics("oslog.interpolation.init")
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public init(literalCapacity: Int, interpolationCount: Int) {
    // Since the format string and the arguments array are fully constructed
    // at compile time, the parameters are ignored.
    formatString = ""
    arguments = ConstantVPrintFArguments()
  }

  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  public mutating func appendLiteral(_ literal: String) {
    formatString += literal.percentEscapedString
  }

  /// `appendInterpolation` conformances will be added by extensions to this type.
}

extension ConstantVPrintFInterpolation {
  /// Defines interpolation for expressions of type String.
  ///
  /// Do not call this function directly. It will be called automatically when interpolating
  /// a value of type `String` in the string interpolations passed to the log APIs.
  ///
  /// - Parameters:
  ///   - argumentString: The interpolated expression of type String, which is autoclosured.
  @_semantics("constant_evaluable")
  @_alwaysEmitIntoClient
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ argumentString: @autoclosure @escaping () -> String
  ) {
    formatString += "%s"
    arguments.append(argumentString)
  }
}

extension ConstantVPrintFInterpolation {

  /// Defines interpolation for values conforming to CustomStringConvertible. The values
  /// are displayed using the description methods on them.
  ///
  /// Do not call this function directly. It will be called automatically when interpolating
  /// a value conforming to CustomStringConvertible in the string interpolations passed
  /// to the log APIs.
  ///
  /// - Parameters:
  ///   - value: The interpolated expression conforming to CustomStringConvertible.
  @_optimize(none)
  @_transparent
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation<T : CustomStringConvertible>(
    _ value: @autoclosure @escaping () -> T
  ) {
    appendInterpolation(value().description)
  }

  /// Defines interpolation for meta-types.
  ///
  /// Do not call this function directly. It will be called automatically when interpolating
  /// a value of type `Any.Type` in the string interpolations passed to the log APIs.
  ///
  /// - Parameters:
  ///   - value: An interpolated expression of type Any.Type, which is autoclosured.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  @_semantics("oslog.requires_constant_arguments")
  public mutating func appendInterpolation(
    _ value: @autoclosure @escaping () -> Any.Type
  ) {
    appendInterpolation(_typeName(value(), qualified: false))
  }
}

extension UnsafeRawPointer: CVarArg {
  /// Transform `self` into a series of machine words that can be
  /// appropriately interpreted by C varargs.
  @inlinable // c-abi
  public var _cVarArgEncoding: [Int] {
    return unsafe _encodeBitsAsWords(self)
  }
}

extension ConstantVPrintFArguments {
  /// Append an (autoclosured) interpolated expression of type `UnsafeRawPointer`, passed
  /// to `ConstantVPrintFMessage.appendInterpolation`, to the array of closures tracked
  /// by this instance.
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  internal mutating func append(_ value: @escaping () -> UnsafeRawPointer) {
    argumentClosures.append({ continuation in
      unsafe continuation(value()._cVarArgEncoding)
    })
  }
}

extension ConstantVPrintFArguments {
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  internal mutating func append(_ value: @escaping () -> String) {
    argumentClosures.append({ continuation in
      unsafe value().withCString { str in
        unsafe continuation(str._cVarArgEncoding)
      }
    })
  }
}

extension ConstantVPrintFArguments {
  @_semantics("constant_evaluable")
  @inlinable
  @_optimize(none)
  internal mutating func append<T>(
    _ value: @escaping () -> T
  ) where T: FixedWidthInteger {
    argumentClosures.append({ continuation in
      continuation(_encodeBitsAsWords(value()))
    })
  }
}

@frozen @_semantics("oslog.message.type")
public struct ConstantVPrintFMessage :
  ExpressibleByStringInterpolation, ExpressibleByStringLiteral
{
  public let interpolation: ConstantVPrintFInterpolation

  @inlinable
  @_optimize(none)
  @_semantics("oslog.message.init_interpolation")
  @_semantics("constant_evaluable")
  public init(stringInterpolation: ConstantVPrintFInterpolation) {
    var s = stringInterpolation
    s.appendLiteral("\n")
    self.interpolation = s
  }

  @inlinable
  @_optimize(none)
  @_semantics("oslog.message.init_stringliteral")
  @_semantics("constant_evaluable")
  public init(stringLiteral value: String) {
    var s = ConstantVPrintFInterpolation(
      literalCapacity: 1,
      interpolationCount: 0
    )
    s.appendLiteral(value)
    s.appendLiteral("\n")
    self.interpolation = s
  }
}

internal func constant_vprintf_backend_recurse(
  fmt: UnsafePointer<CChar>,
  argumentClosures: ArraySlice<(([Int]) -> ()) -> ()>,
  args: inout [CVarArg]
) {
  if let closure = argumentClosures.first {
    closure { newArg in
      args.append(contentsOf: newArg)
      unsafe constant_vprintf_backend_recurse(
        fmt: fmt,
        argumentClosures: argumentClosures.dropFirst(),
        args: &args
      )
    }
  } else {
    _ = unsafe withVaList(args) { valist in
      unsafe _swift_stdlib_vprintf(fmt, valist)
    }
  }
}

@inline(never) @usableFromInline
internal func constant_vprintf_backend(
  fmt: UnsafePointer<CChar>,
  argumentClosures: [(([Int]) -> ()) -> ()]
) {
  var args:[CVarArg] = []
  if let closure = argumentClosures.first {
    closure { newArg in
      args.append(contentsOf: newArg)
      unsafe constant_vprintf_backend_recurse(
        fmt: fmt,
        argumentClosures: argumentClosures.dropFirst(),
        args: &args
      )
    }
  } else {
    unsafe constant_vprintf_backend_recurse(
      fmt: fmt,
      argumentClosures: ArraySlice(argumentClosures),
      args: &args
    )
  }
}

@_semantics("oslog.requires_constant_arguments")
@inlinable
@_transparent
@_alwaysEmitIntoClient
@_optimize(none)
public func print(_ message: ConstantVPrintFMessage) {
  let formatString = message.interpolation.formatString
  let argumentClosures = message.interpolation.arguments.argumentClosures
  if Bool(_builtinBooleanLiteral: Builtin.ifdef_SWIFT_STDLIB_PRINT_DISABLED()) { return }
  let formatStringPointer = unsafe _getGlobalStringTablePointer(formatString)
  unsafe constant_vprintf_backend(
    fmt: formatStringPointer,
    argumentClosures: argumentClosures
  )
}

#endif
