blob: dc24531648d422ae5c0bc91aae32ec3661bd0b43 [file] [log] [blame]
//===----------------- OSLogIntegerTypes.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 extensions for interpolating integer expressions into an
// OSLogMesage. It defines `appendInterpolation` functions for standard integer
// types. It also defines extensions for serializing integer types into the
// argument buffer passed to os_log ABIs.
//
// The `appendInterpolation` functions defined in this file accept formatting,
// privacy and alignment options along with the interpolated expression as
// shown below:
//
// 1. "\(x, format: .hex, privacy: .private, align: .right\)"
// 2. "\(x, format: .hex(minDigits: 10), align: .right(columns: 10)\)"
extension OSLogInterpolation {
/// 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: `OSLogIntegerFormatting`. The default is `.decimal`.
/// - align: Left or right alignment with the minimum number of columns as
/// defined by the type `OSLogStringAlignment`.
/// - privacy: A privacy qualifier which is either private or public.
/// It is auto-inferred by default.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
@_semantics("oslog.requires_constant_arguments")
public mutating func appendInterpolation(
_ number: @autoclosure @escaping () -> Int,
format: OSLogIntegerFormatting = .decimal,
align: OSLogStringAlignment = .none,
privacy: OSLogPrivacy = .auto
) {
appendInteger(number, format: format, align: align, privacy: privacy)
}
// 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 () -> Int32,
format: OSLogIntegerFormatting = .decimal,
align: OSLogStringAlignment = .none,
privacy: OSLogPrivacy = .auto
) {
appendInteger(number, format: format, align: align, privacy: privacy)
}
/// 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 `OSLogIntegerFormatting`.
/// - align: Left or right alignment with the minimum number of columns as
/// defined by the type `OSLogStringAlignment`.
/// - privacy: A privacy qualifier which is either private or public.
/// It is auto-inferred by default.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
@_semantics("oslog.requires_constant_arguments")
public mutating func appendInterpolation(
_ number: @autoclosure @escaping () -> UInt,
format: OSLogIntegerFormatting = .decimal,
align: OSLogStringAlignment = .none,
privacy: OSLogPrivacy = .auto
) {
appendInteger(number, format: format, align: align, privacy: privacy)
}
/// 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 OSLogArguments property.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal mutating func appendInteger<T>(
_ number: @escaping () -> T,
format: OSLogIntegerFormatting,
align: OSLogStringAlignment,
privacy: OSLogPrivacy
) where T: FixedWidthInteger {
guard argumentCount < maxOSLogArgumentCount else { return }
formatString +=
format.formatSpecifier(for: T.self, align: align, privacy: privacy)
// If minimum column width is specified, append this value first. Note that
// the format specifier would use a '*' for width e.g. %*d.
if let minColumns = align.minimumColumnWidth {
appendAlignmentArgument(minColumns)
}
// If the privacy has a mask, append the mask argument, which is a constant payload.
// Note that this should come after the width but before the precision.
if privacy.hasMask {
appendMaskArgument(privacy)
}
// If minimum number of digits (precision) is specified, append the
// precision before the argument. Note that the format specifier would use
// a '*' for precision: %.*d.
if let minDigits = format.minDigits {
appendPrecisionArgument(minDigits)
}
// Append the integer.
addIntHeaders(privacy, sizeForEncoding(T.self))
arguments.append(number)
argumentCount += 1
}
/// Update preamble and append argument headers based on the parameters of
/// the interpolation.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal mutating func addIntHeaders(
_ privacy: OSLogPrivacy,
_ byteCount: Int
) {
// Append argument header.
let argumentHeader = getArgumentHeader(privacy: privacy, type: .scalar)
arguments.append(argumentHeader)
// Append number of bytes needed to serialize the argument.
arguments.append(UInt8(byteCount))
// Increment total byte size by the number of bytes needed for this
// argument, which is the sum of the byte size of the argument and
// two bytes needed for the headers.
totalBytesForSerializingArguments += byteCount + 2
preamble = getUpdatedPreamble(privacy: privacy, isScalar: true)
}
// Append argument indicating precision or width of a format specifier to the buffer.
// These specify the value of the '*' in a format specifier like: %*.*ld.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal mutating func appendPrecisionArgument(_ count: @escaping () -> Int) {
appendPrecisionAlignCount(
count,
getArgumentHeader(privacy: .auto, type: .count))
}
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal mutating func appendAlignmentArgument(_ count: @escaping () -> Int) {
appendPrecisionAlignCount(
count,
getArgumentHeader(privacy: .auto, type: .scalar))
}
// This is made transparent to minimize compile time overheads. The function's
// implementation also uses literals whenever possible for the same reason.
@_transparent
@inlinable
internal mutating func appendPrecisionAlignCount(
_ count: @escaping () -> Int,
_ argumentHeader: UInt8
) {
arguments.append(argumentHeader)
// Append number of bytes needed to serialize the argument.
arguments.append(4)
// Increment total byte size by the number of bytes needed for this
// argument, which is the sum of the byte size of the argument and
// two bytes needed for the headers.
totalBytesForSerializingArguments += 6
// The count is expected to be a CInt.
arguments.append({ CInt(count()) })
argumentCount += 1
// Note that we don't have to update the preamble here.
}
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal mutating func appendMaskArgument(_ privacy: OSLogPrivacy) {
arguments.append(getArgumentHeader(privacy: .auto, type: .mask))
// Append number of bytes needed to serialize the mask. Mask is 64 bit payload.
arguments.append(8)
// Increment total byte size by the number of bytes needed for this
// argument, which is the sum of the byte size of the argument and
// two bytes needed for the headers.
totalBytesForSerializingArguments += 10
// Append the mask value. This is a compile-time constant.
let maskValue = privacy.maskValue
arguments.append({ maskValue })
argumentCount += 1
// Note that we don't have to update the preamble here.
}
}
extension OSLogArguments {
/// Append an (autoclosured) interpolated expression of integer type, passed to
/// `OSLogMessage.appendInterpolation`, to the array of closures tracked
/// by this instance.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal mutating func append<T>(
_ value: @escaping () -> T
) where T: FixedWidthInteger {
argumentClosures.append({ (position, _, _) in
serialize(value(), at: &position)
})
}
}
/// Return the number of bytes needed for serializing an integer argument as
/// specified by os_log. This function must be constant evaluable. Note that
/// it is marked transparent instead of @inline(__always) as it is used in
/// optimize(none) functions.
@_transparent
@_alwaysEmitIntoClient
internal func sizeForEncoding<T>(
_ type: T.Type
) -> Int where T : FixedWidthInteger {
return type.bitWidth &>> logBitsPerByte
}
/// Serialize an integer at the buffer location that `position` points to and
/// increment `position` by the byte size of `T`.
@_alwaysEmitIntoClient
@inline(__always)
internal func serialize<T>(
_ value: T,
at bufferPosition: inout ByteBufferPointer
) where T : FixedWidthInteger {
let byteCount = sizeForEncoding(T.self)
let dest =
UnsafeMutableRawBufferPointer(start: bufferPosition, count: byteCount)
withUnsafeBytes(of: value) { dest.copyMemory(from: $0) }
bufferPosition += byteCount
}