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

import Builtin

//===----------------------------------------------------------------------===//
// Bool AtomicRepresentable conformance
//===----------------------------------------------------------------------===//

@available(SwiftStdlib 6.0, *)
extension Bool: AtomicRepresentable {
  /// The storage representation type that `Self` encodes to and decodes from
  /// which is a suitable type when used in atomic operations.
  @available(SwiftStdlib 6.0, *)
  public typealias AtomicRepresentation = UInt8.AtomicRepresentation

  /// Destroys a value of `Self` and prepares an `AtomicRepresentation` storage
  /// type to be used for atomic operations.
  ///
  /// - Note: This is not an atomic operation. This simply encodes the logical
  ///   type `Self` into its storage representation suitable for atomic
  ///   operations, `AtomicRepresentation`.
  ///
  /// - Parameter value: A valid instance of `Self` that's about to be destroyed
  ///   to encode an instance of its `AtomicRepresentation`.
  /// - Returns: The newly encoded `AtomicRepresentation` storage.
  @available(SwiftStdlib 6.0, *)
  @_alwaysEmitIntoClient
  @_transparent
  public static func encodeAtomicRepresentation(
    _ value: borrowing Bool
  ) -> AtomicRepresentation {
    UInt8.encodeAtomicRepresentation(
      UInt8(Builtin.zext_Int1_Int8(value._value))
    )
  }

  /// Recovers the logical atomic type `Self` by destroying some
  /// `AtomicRepresentation` storage instance returned from an atomic operation.
  ///
  /// - Note: This is not an atomic operation. This simply decodes the storage
  ///   representation used in atomic operations back into the logical type for
  ///   normal use, `Self`.
  ///
  /// - Parameter storage: The storage representation for `Self` that's used
  ///   within atomic operations.
  /// - Returns: The newly decoded logical type `Self`.
  @available(SwiftStdlib 6.0, *)
  @_alwaysEmitIntoClient
  @_transparent
  public static func decodeAtomicRepresentation(
    _ representation: consuming AtomicRepresentation
  ) -> Bool {
    Bool(Builtin.trunc_Int8_Int1(
      UInt8.decodeAtomicRepresentation(representation)._value)
    )
  }
}

//===----------------------------------------------------------------------===//
// Bool atomic operations
//===----------------------------------------------------------------------===//

@available(SwiftStdlib 6.0, *)
extension Atomic where Value == Bool {
  /// Perform an atomic logical AND operation and return the old and new value,
  /// applying the specified memory ordering.
  ///
  /// - Parameter operand: A boolean value.
  /// - Parameter ordering: The memory ordering to apply on this operation.
  /// - Returns: A tuple with the old value before the operation and the new value
  ///   after the operation.
  @available(SwiftStdlib 6.0, *)
  @discardableResult
  @_semantics("atomics.requires_constant_orderings")
  @_alwaysEmitIntoClient
  @_transparent
  public func logicalAnd(
    _ operand: Bool,
    ordering: AtomicUpdateOrdering
  ) -> (oldValue: Bool, newValue: Bool) {
    let builtinOperand = Bool.encodeAtomicRepresentation(operand)._storage

    let original = switch ordering {
    case .relaxed:
      Builtin.atomicrmw_and_monotonic_Int8(
        _rawAddress,
        builtinOperand
      )

    case .acquiring:
      Builtin.atomicrmw_and_acquire_Int8(
        _rawAddress,
        builtinOperand
      )

    case .releasing:
      Builtin.atomicrmw_and_release_Int8(
        _rawAddress,
        builtinOperand
      )

    case .acquiringAndReleasing:
      Builtin.atomicrmw_and_acqrel_Int8(
        _rawAddress,
        builtinOperand
      )

    case .sequentiallyConsistent:
      Builtin.atomicrmw_and_seqcst_Int8(
        _rawAddress,
        builtinOperand
      )

    default:
      Builtin.unreachable()
    }

    let old = Bool.decodeAtomicRepresentation(UInt8.AtomicRepresentation(original))

    return (oldValue: old, newValue: old && operand)
  }

  /// Perform an atomic logical OR operation and return the old and new value,
  /// applying the specified memory ordering.
  ///
  /// - Parameter operand: A boolean value.
  /// - Parameter ordering: The memory ordering to apply on this operation.
  /// - Returns: A tuple with the old value before the operation and the new value
  ///   after the operation.
  @available(SwiftStdlib 6.0, *)
  @discardableResult
  @_semantics("atomics.requires_constant_orderings")
  @_alwaysEmitIntoClient
  @_transparent
  public func logicalOr(
    _ operand: Bool,
    ordering: AtomicUpdateOrdering
  ) -> (oldValue: Bool, newValue: Bool) {
    let builtinOperand = Bool.encodeAtomicRepresentation(operand)._storage

    let original = switch ordering {
    case .relaxed:
      Builtin.atomicrmw_or_monotonic_Int8(
        _rawAddress,
        builtinOperand
      )

    case .acquiring:
      Builtin.atomicrmw_or_acquire_Int8(
        _rawAddress,
        builtinOperand
      )

    case .releasing:
      Builtin.atomicrmw_or_release_Int8(
        _rawAddress,
        builtinOperand
      )

    case .acquiringAndReleasing:
      Builtin.atomicrmw_or_acqrel_Int8(
        _rawAddress,
        builtinOperand
      )

    case .sequentiallyConsistent:
      Builtin.atomicrmw_or_seqcst_Int8(
        _rawAddress,
        builtinOperand
      )

    default:
      Builtin.unreachable()
    }

    let old = Bool.decodeAtomicRepresentation(UInt8.AtomicRepresentation(original))

    return (oldValue: old, newValue: old || operand)
  }

  /// Perform an atomic logical XOR operation and return the old and new value,
  /// applying the specified memory ordering.
  ///
  /// - Parameter operand: A boolean value.
  /// - Parameter ordering: The memory ordering to apply on this operation.
  /// - Returns: A tuple with the old value before the operation and the new value
  ///   after the operation.
  @available(SwiftStdlib 6.0, *)
  @discardableResult
  @_semantics("atomics.requires_constant_orderings")
  @_alwaysEmitIntoClient
  @_transparent
  public func logicalXor(
    _ operand: Bool,
    ordering: AtomicUpdateOrdering
  ) -> (oldValue: Bool, newValue: Bool) {
    let builtinOperand = Bool.encodeAtomicRepresentation(operand)._storage

    let original = switch ordering {
    case .relaxed:
      Builtin.atomicrmw_xor_monotonic_Int8(
        _rawAddress,
        builtinOperand
      )

    case .acquiring:
      Builtin.atomicrmw_xor_acquire_Int8(
        _rawAddress,
        builtinOperand
      )

    case .releasing:
      Builtin.atomicrmw_xor_release_Int8(
        _rawAddress,
        builtinOperand
      )

    case .acquiringAndReleasing:
      Builtin.atomicrmw_xor_acqrel_Int8(
        _rawAddress,
        builtinOperand
      )

    case .sequentiallyConsistent:
      Builtin.atomicrmw_xor_seqcst_Int8(
        _rawAddress,
        builtinOperand
      )

    default:
      Builtin.unreachable()
    }

    let old = Bool.decodeAtomicRepresentation(UInt8.AtomicRepresentation(original))

    return (oldValue: old, newValue: old != operand)
  }
}
