// RUN: %target-run-simple-swiftgyb
// REQUIRES: executable_test
// REQUIRES: reflection

import StdlibUnittest


protocol TestProtocol1 {}

// Check that the generic parameter is called 'Pointee'.
#if _runtime(_ObjC)
extension AutoreleasingUnsafeMutablePointer where Pointee : TestProtocol1 {
  var _memoryIsTestProtocol1: Bool {
    fatalError("not implemented")
  }
}
#endif

extension UnsafePointer where Pointee : TestProtocol1 {
  var _memoryIsTestProtocol1: Bool {
    fatalError("not implemented")
  }
}

extension UnsafeMutablePointer where Pointee : TestProtocol1 {
  var _memoryIsTestProtocol1: Bool {
    fatalError("not implemented")
  }
}

// Check that the generic parameter is called 'Element'.
extension UnsafeBufferPointer.Iterator where Element : TestProtocol1 {
  var _elementIsTestProtocol1: Bool {
    fatalError("not implemented")
  }
}

extension UnsafeBufferPointer where Element : TestProtocol1 {
  var _elementIsTestProtocol1: Bool {
    fatalError("not implemented")
  }
}

extension UnsafeMutableBufferPointer where Element : TestProtocol1 {
  var _elementIsTestProtocol1: Bool {
    fatalError("not implemented")
  }
}

var UnsafePointerTestSuite = TestSuite("UnsafePointer")
var UnsafeMutablePointerTestSuite = TestSuite("UnsafeMutablePointer")
var UnsafeRawPointerTestSuite = TestSuite("UnsafeRawPointer")
var UnsafeMutableRawPointerTestSuite = TestSuite("UnsafeMutableRawPointer")
var OpaquePointerTestSuite = TestSuite("OpaquePointer")

% for (SelfName, SelfType) in [
%         ('UnsafePointer', 'UnsafePointer<Float>'),
%         ('UnsafeMutablePointer', 'UnsafeMutablePointer<Float>'),
%         ('UnsafeRawPointer', 'UnsafeRawPointer'),
%         ('UnsafeMutableRawPointer', 'UnsafeMutableRawPointer')]:

${SelfName}TestSuite.test("initFromOpaquePointer") {
  let other = OpaquePointer(bitPattern: 0x12345678)!
  let ptr = ${SelfType}(other)
  expectEqual(0x12345678, unsafeBitCast(ptr, to: Int.self))

  let optionalOther: Optional = other
  let optionalPointer = ${SelfType}(optionalOther)
  expectNotNil(optionalPointer)
  expectEqual(0x12345678, unsafeBitCast(optionalPointer, to: Int.self))

  let nilOther: Optional<OpaquePointer> = nil
  let nilPointer = ${SelfType}(nilOther)
  expectNil(nilPointer)
}

% end

% for (SelfName, SelfType) in [
%         ('UnsafeRawPointer', 'UnsafeRawPointer'),
%         ('UnsafeMutableRawPointer', 'UnsafeMutableRawPointer')]:
UnsafeMutableRawPointerTestSuite.test("initFromMutating") {
  let other = UnsafeRawPointer(bitPattern: 0x12345678)!
  let ptr = UnsafeMutableRawPointer(mutating: other)
  expectEqual(0x12345678, unsafeBitCast(ptr, to: Int.self))

  let optionalOther: Optional = other
  let optionalPointer = UnsafeMutableRawPointer(mutating: optionalOther)
  expectNotNil(optionalPointer)
  expectEqual(0x12345678, unsafeBitCast(optionalPointer, to: Int.self))

  let nilOther: Optional<UnsafeRawPointer> = nil
  let nilPointer = UnsafeMutableRawPointer(mutating: nilOther)
  expectNil(nilPointer)
}
% end

% for (SelfName, SelfType) in [
%         ('UnsafePointer', 'UnsafePointer<Float>'),
%         ('UnsafeMutablePointer', 'UnsafeMutablePointer<Float>'),
%         ('UnsafeRawPointer', 'UnsafeRawPointer'),
%         ('UnsafeMutableRawPointer', 'UnsafeMutableRawPointer'),
%         ('OpaquePointer', 'OpaquePointer')]:

${SelfName}TestSuite.test("convertFromNil") {
  let ptr: ${SelfType}? = nil
  expectEqual(0, unsafeBitCast(ptr, to: Int.self))
}

${SelfName}TestSuite.test("initFromInteger") {
  do {
    let word: Int = 0x12345678
    let ptr = ${SelfType}(bitPattern: word)
    expectNotNil(ptr)
    expectEqual(word, unsafeBitCast(ptr, to: Int.self))
  }
  do {
    let uword: UInt = 0x12345678
    let ptr = ${SelfType}(bitPattern: uword)
    expectNotNil(ptr)
    expectEqual(uword, unsafeBitCast(ptr, to: UInt.self))
  }
}

${SelfName}TestSuite.test("initFromNilBitPattern") {
  do {
    let word = unsafeBitCast(nil as ${SelfType}?, to: Int.self)
    let ptr = ${SelfType}(bitPattern: word)
    expectNil(ptr)
    expectEqual(word, unsafeBitCast(ptr, to: Int.self))
  }
  do {
    let uword = unsafeBitCast(nil as ${SelfType}?, to: UInt.self)
    let ptr = ${SelfType}(bitPattern: uword)
    expectNil(ptr)
    expectEqual(uword, unsafeBitCast(ptr, to: UInt.self))
  }
}

${SelfName}TestSuite.test("Hashable") {
  let ptrs = [
    ${SelfType}(bitPattern: 0x12345678)!,
    ${SelfType}(bitPattern: 0x87654321 as UInt)!,
  ]
  checkHashable(ptrs, equalityOracle: { $0 == $1 })
}

${SelfName}TestSuite.test("toInteger") {
  do {
    let word: Int = 0x12345678
    let ptr = ${SelfType}(bitPattern: word)
    expectEqual(word, Int(bitPattern: ptr))
  }
  do {
    let ptr: ${SelfType}? = nil
    expectEqual(Int(0), Int(bitPattern: ptr))
  }
  do {
    let uword: UInt = 0x12345678
    let ptr = ${SelfType}(bitPattern: uword)
    expectEqual(uword, UInt(bitPattern: ptr))
  }
  do {
    let ptr: ${SelfType}? = nil
    expectEqual(UInt(0), UInt(bitPattern: ptr))
  }
}

% end

% for (SelfName, SelfType) in [
%         ('UnsafePointer', 'UnsafePointer<Float>'),
%         ('UnsafeRawPointer', 'UnsafeRawPointer'),
%         ('UnsafeMutableRawPointer', 'UnsafeMutableRawPointer')]:

${SelfName}TestSuite.test("initFromUnsafeMutablePointer") {
  let other = UnsafeMutablePointer<Float>(bitPattern: 0x12345678)!
  let ptr = ${SelfType}(other)
  expectEqual(0x12345678, unsafeBitCast(ptr, to: Int.self))

  let optionalOther: Optional = other
  let optionalPointer = ${SelfType}(optionalOther)
  expectNotNil(optionalPointer)
  expectEqual(0x12345678, unsafeBitCast(optionalPointer, to: Int.self))

  let nilOther: Optional<UnsafeMutablePointer<Float>> = nil
  let nilPointer = ${SelfType}(nilOther)
  expectNil(nilPointer)
}

% end

% for (SelfName, SelfType) in [
%         ('UnsafePointer', 'UnsafePointer<Float>'),
%         ('UnsafeRawPointer', 'UnsafeRawPointer')]:

${SelfName}TestSuite.test("initFromUnsafePointer") {
  let other = UnsafePointer<Float>(bitPattern: 0x12345678)!
  let ptr = ${SelfType}(other)
  expectEqual(0x12345678, unsafeBitCast(ptr, to: Int.self))

  let optionalOther: Optional = other
  let optionalPointer = ${SelfType}(optionalOther)
  expectNotNil(optionalPointer)
  expectEqual(0x12345678, unsafeBitCast(optionalPointer, to: Int.self))

  let nilOther: Optional<UnsafePointer<Float>> = nil
  let nilPointer = ${SelfType}(nilOther)
  expectNil(nilPointer)
}

% end

UnsafeRawPointerTestSuite.test("initFromUnsafeMutableRawPointer") {
  let other = UnsafeMutableRawPointer(bitPattern: 0x12345678)!
  let ptr = UnsafeRawPointer(other)
  expectEqual(0x12345678, unsafeBitCast(ptr, to: Int.self))

  let optionalOther: Optional = other
  let optionalPointer = UnsafeRawPointer(optionalOther)
  expectNotNil(optionalPointer)
  expectEqual(0x12345678, unsafeBitCast(optionalPointer, to: Int.self))

  let nilOther: Optional<UnsafeMutableRawPointer> = nil
  let nilPointer = UnsafeRawPointer(nilOther)
  expectNil(nilPointer)
}

enum Check {
  case LeftOverlap
  case RightOverlap
  case Disjoint
}

class Missile {
  static var missilesLaunched = 0
  let number: Int
  init(_ number: Int) { self.number = number }
  deinit { Missile.missilesLaunched += 1 }
}

func checkPointerCorrectness(_ check: Check,
  _ withMissiles: Bool = false,
  _ f: (UnsafeMutablePointer<Missile>) ->
    (UnsafeMutablePointer<Missile>, _ count: Int) -> Void) {
  let ptr = UnsafeMutablePointer<Missile>.allocate(capacity: 4)
  switch check {
  case .RightOverlap:
    ptr.initialize(to: Missile(1))
    (ptr + 1).initialize(to: Missile(2))
    if withMissiles {
      (ptr + 2).initialize(to: Missile(3))
    }
    f(ptr + 1)(ptr, 2)
    expectEqual(1, ptr[1].number)
    expectEqual(2, ptr[2].number)
  case .LeftOverlap:
    if withMissiles {
      ptr.initialize(to: Missile(1))
    }
    (ptr + 1).initialize(to: Missile(2))
    (ptr + 2).initialize(to: Missile(3))
    f(ptr)(ptr + 1, 2)
    expectEqual(2, ptr[0].number)
    expectEqual(3, ptr[1].number)
  case .Disjoint:
    if withMissiles {
      ptr.initialize(to: Missile(0))
      (ptr + 1).initialize(to: Missile(1))
    }
    (ptr + 2).initialize(to: Missile(2))
    (ptr + 3).initialize(to: Missile(3))
    f(ptr)(ptr + 2, 2)
    expectEqual(2, ptr[0].number)
    expectEqual(3, ptr[1].number)
    // backwards
    let ptr2 = UnsafeMutablePointer<Missile>.allocate(capacity: 4)
    ptr2.initialize(to: Missile(0))
    (ptr2 + 1).initialize(to: Missile(1))
    if withMissiles {
      (ptr2 + 2).initialize(to: Missile(2))
      (ptr2 + 3).initialize(to: Missile(3))
    }
    f(ptr2 + 2)(ptr2, 2)
    expectEqual(0, ptr2[2].number)
    expectEqual(1, ptr2[3].number)
  }
}

func checkPtr(
  _ f: @escaping ((UnsafeMutablePointer<Missile>) -> (UnsafeMutablePointer<Missile>, _ count: Int) -> Void),
  _ m: Bool
) -> (Check) -> Void {
  return { checkPointerCorrectness($0, m, f) }
}

func checkPtr(
  _ f: @escaping ((UnsafeMutablePointer<Missile>) -> (UnsafePointer<Missile>, _ count: Int) -> Void),
  _ m: Bool
) -> (Check) -> Void {
  return {
    checkPointerCorrectness($0, m) { destPtr in
      return { f(destPtr)(UnsafeMutablePointer($0), $1) }
    }
  }
}

UnsafeMutablePointerTestSuite.test("moveAssign:from:")
.require(.crashTesting)
.code {
  let check = checkPtr(UnsafeMutablePointer.moveAssign, true)
  check(Check.Disjoint)
  // This check relies on _debugPrecondition() so will only trigger in -Onone mode.
  if _isDebugAssertConfiguration() {
    expectCrashLater()
    check(Check.LeftOverlap)
  }
}

UnsafeMutablePointerTestSuite.test("moveAssign:from:.Right")
.require(.crashTesting)
.code {
  let check = checkPtr(UnsafeMutablePointer.moveAssign, true)
  // This check relies on _debugPrecondition() so will only trigger in -Onone mode.
  if _isDebugAssertConfiguration() {
    expectCrashLater()
    check(Check.RightOverlap)
  }
}

UnsafeMutablePointerTestSuite.test("assign:from:") {
  let check = checkPtr(UnsafeMutablePointer.assign, true)
  check(Check.LeftOverlap)
  check(Check.Disjoint)
  check(Check.RightOverlap)
}

UnsafeMutablePointerTestSuite.test("moveInitialize:from:") {
  let check = checkPtr(UnsafeMutablePointer.moveInitialize, false)
  check(Check.LeftOverlap)
  check(Check.Disjoint)
  check(Check.RightOverlap)
}

UnsafeMutablePointerTestSuite.test("initialize:from:")
.require(.crashTesting)
.code {
  let check = checkPtr(UnsafeMutablePointer.initialize(from:count:), false)
  check(Check.Disjoint)
  // This check relies on _debugPrecondition() so will only trigger in -Onone mode.
  if _isDebugAssertConfiguration() {
    expectCrashLater()
    check(Check.LeftOverlap)
  }
}

UnsafeMutablePointerTestSuite.test("initialize:from:.Right")
.require(.crashTesting)
.code {
  let check = checkPtr(UnsafeMutablePointer.initialize(from:count:), false)
  // This check relies on _debugPrecondition() so will only trigger in -Onone mode.
  if _isDebugAssertConfiguration() {
    expectCrashLater()
    check(Check.RightOverlap)
  }
}

UnsafeMutablePointerTestSuite.test("initialize:from:/immutable") {
  let ptr = UnsafeMutablePointer<Missile>.allocate(capacity: 3)
  defer {
    ptr.deinitialize(count: 3)
    ptr.deallocate()
  }
  let source = (0..<3).map(Missile.init)
  source.withUnsafeBufferPointer { bufferPtr in
    ptr.initialize(from: bufferPtr.baseAddress!, count: 3)
    expectEqual(0, ptr[0].number)
    expectEqual(1, ptr[1].number)
    expectEqual(2, ptr[2].number)
  }
}

UnsafeMutablePointerTestSuite.test("assign/immutable") {
  let ptr = UnsafeMutablePointer<Missile>.allocate(capacity: 2)
  defer {
    ptr.deinitialize(count: 2)
    ptr.deallocate()
  }
  ptr.initialize(to: Missile(1))
  (ptr + 1).initialize(to: Missile(2))
  let source = (3..<5).map(Missile.init)
  source.withUnsafeBufferPointer { bufferPtr in
    ptr.assign(from: bufferPtr.baseAddress!, count: 2)
    expectEqual(3, ptr[0].number)
    expectEqual(4, ptr[1].number)
  }
}

% for (SelfName, SelfType) in [
%         ('UnsafePointer', 'UnsafePointer<Float>'),
%         ('UnsafeMutablePointer', 'UnsafeMutablePointer<Float>'),
%         ('UnsafeRawPointer', 'UnsafeRawPointer'),
%         ('UnsafeMutableRawPointer', 'UnsafeMutableRawPointer')]:

${SelfName}TestSuite.test("customMirror") {
  // Ensure that the custom mirror works properly, including when the raw value
  // is greater than Int.max
  let reallyBigInt: UInt = UInt(Int.max) + 1
  let ptr = ${SelfType}(bitPattern: reallyBigInt)!
  let mirror = ptr.customMirror
  expectEqual(1, mirror.children.count)
#if _pointerBitWidth(_32)
  expectEqual("18446744071562067968", String(describing: mirror.children.first!.1))
#elseif _pointerBitWidth(_64)
  expectEqual("9223372036854775808", String(describing: mirror.children.first!.1))
#else
  fatalError("Unimplemented")
#endif
}

${SelfName}TestSuite.test("customPlaygroundQuickLook") {
  // Ensure that the custom playground quicklook works properly, including when
  // the raw value is greater than Int.max
  let reallyBigInt: UInt = UInt(Int.max) + 1
  let ptr = ${SelfType}(bitPattern: reallyBigInt)!
  if case let .text(desc) = ptr.customPlaygroundQuickLook {
#if _pointerBitWidth(_32)
    expectEqual("${SelfName}(0xFFFFFFFF80000000)", desc)
#elseif _pointerBitWidth(_64)
    expectEqual("${SelfName}(0x8000000000000000)", desc)
#else
    fatalError("Unimplemented")
#endif
  } else {
    expectTrue(false)
  }
}

${SelfName}TestSuite.test("Comparable") {
  var addresses: [UInt] = [
    0x00000001,
    0xFF00FF00,
    0x00001111,
    0x00000002,
    0xFFFFFFFF,
    0x00001111
  ]

  #if arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le)
  addresses += [
    0xFFFFFFFF80000000,
    0x8000000000000000
  ]
  #endif

  let instances = addresses.map { ($0, ${SelfType}(bitPattern: $0)!) }

  func comparisonOracle(i: Int, j: Int) -> ExpectedComparisonResult {
    return instances[i].0 <=> instances[j].0
  }
  checkComparable(instances.map { $0.1 }, oracle: comparisonOracle)
}

% end

% for SelfName in ['UnsafePointer', 'UnsafeMutablePointer']:

${SelfName}TestSuite.test("withMemoryRebound") {
  let mutablePtrI = UnsafeMutablePointer<Int>.allocate(capacity: 4)
  defer { mutablePtrI.deallocate() }
  let ptrI = ${SelfName}<Int>(mutablePtrI)
  ptrI.withMemoryRebound(to: UInt.self, capacity: 4) {
    // Make sure the closure argument isa $SelfName
    let ptrU: ${SelfName}<UInt> = $0
    // and that the element type is UInt.
    let mutablePtrU = UnsafeMutablePointer(mutating: ptrU)
    expectType(UInt.self, &mutablePtrU.pointee)
  }
}

${SelfName}TestSuite.test("withMemoryReboundSE0333") {
  let mutablePtrI = UnsafeMutablePointer<Int>.allocate(capacity: 4)
  defer { mutablePtrI.deallocate() }
  let ptrI = ${SelfName}<Int>(mutablePtrI)
  // test rebinding to a wider type with the same alignment
  ptrI.withMemoryRebound(to: (Int, Int).self, capacity: 2) {
    // Make sure the closure argument isa $SelfName
    let ptrT: ${SelfName}<(Int, Int)> = $0
    // and that the element type is (Int, Int).
    let mutablePtrT = UnsafeMutablePointer(mutating: ptrT)
    expectType((Int, Int).self, &mutablePtrT.pointee)
    // test rebinding to a narrower type
    ptrT.withMemoryRebound(to: UInt.self, capacity: 4) {
      // Make sure the closure argument isa $SelfName
      let ptrU: ${SelfName}<UInt> = $0
      // and that the element type is UInt.
      let mutablePtrU = UnsafeMutablePointer(mutating: ptrU)
      expectType(UInt.self, &mutablePtrU.pointee)
    }
  }
}

${SelfName}TestSuite.test("withMemoryReboundSE0333.capacity1") {
  let mutablePtr = UnsafeMutablePointer<(Int,Bool,Int64)>.allocate(capacity: 1)
  let i = Int.random(in: 0 ... .max)
  mutablePtr.initialize(to: (i, i.isMultiple(of: 2), Int64(0xff & i)))
  defer { mutablePtr.deallocate() }
  let ptr = ${SelfName}<(Int, Bool, Int64)>(mutablePtr)
  // test rebinding to a shorter type with the same alignment
  ptr.withMemoryRebound(to: (Int, Bool).self, capacity: 1) {
    // Make sure the closure argument is a ${SelfName}
    let ptrT: ${SelfName}<(Int, Bool)> = $0
    // and that the element is (Int, Bool).
    let mutablePtrT = UnsafeMutablePointer(mutating: ptrT)
    expectType((Int, Bool).self, &mutablePtrT.pointee)
    expectEqual($0.pointee.0, i)
    expectEqual($0.pointee.1, i.isMultiple(of: 2))
  }
}

${SelfName}TestSuite.test("pointer(to:)") {
  struct Example {
    var a = false
    var b = 0
    var c: String { "\(a),\(b)" }
  }
  let allocated = UnsafeMutablePointer<Example>.allocate(capacity: 1)
  allocated.pointee = Example()
  defer { allocated.deallocate() }
  let p = ${SelfName}(allocated)
  expectEqual(p.pointer(to: \.a)!.pointee, false)
  expectEqual(p.pointer(to: \.b)!.pointee, 0)
  expectNil(p.pointer(to: \.c))
% if SelfName == 'UnsafeMutablePointer':
  p.pointer(to: \.a)!.pointee = true
  p.pointer(to: \.b)!.pointee += 1
  expectEqual(p.pointer(to: \.a as KeyPath)!.pointee, true)
  expectEqual(p.pointer(to: \.b as KeyPath)!.pointee, 1)
% end
}

${SelfName}TestSuite.test("pointer(to:).overflow")
.require(.crashTesting)
.code {
  struct Example {
    var a = false
    var b = 0
    var c: String { "\(a),\(b)" }
    var d = 0.0
  }
  let o = MemoryLayout<Example>.offset(of: \.b)!
  let p = UnsafePointer<Example>(bitPattern: -o)
  expectNotNil(p)
  guard let p else { fatalError() }
  if _isDebugAssertConfiguration() {
    expectCrashLater()
  }
  let intPointer = p.pointer(to: \.b)
  expectNil(intPointer) // nil because of overflow
  let stringPointer = p.pointer(to: \.c)
  expectNil(stringPointer) // nil because c is a computed property
  let doublePointer = p.pointer(to: \.d)
  expectNotNil(doublePointer)
}

% end

runAllTests()
