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

import TestsUtils
import Foundation

let d: [BenchmarkCategory] =  [.validation, .api, .Data, .cpubench]

public let benchmarks = [
  BenchmarkInfo(name: "DataCreateEmpty",
    runFunction: { for _ in 0..<$0*10_000 { blackHole(Data()) } },
    tags: d, legacyFactor: 10),
  BenchmarkInfo(name: "DataCreateSmall",
    runFunction: { for _ in 0..<$0*10_000 { blackHole(sampleData(.small)) } },
    tags: d, legacyFactor: 10),
  BenchmarkInfo(name: "DataCreateMedium",
    runFunction: { for _ in 0..<$0*100 { blackHole(sampleData(.medium)) } },
    tags: d, legacyFactor: 100),

  BenchmarkInfo(name: "DataCreateEmptyArray",
    runFunction: { for _ in 0..<$0*2_000 { blackHole(Data([])) } }, tags: d,
    legacyFactor: 50),
  BenchmarkInfo(name: "DataCreateSmallArray",
    runFunction: { for _ in 0..<$0*2_000 { blackHole(Data(
      [0, 1, 2, 3, 4, 5, 6])) } }, tags: d, legacyFactor: 50),
  BenchmarkInfo(name: "DataCreateMediumArray",
    runFunction: { for _ in 0..<$0*500 { blackHole(Data([
      0, 1, 2, 3, 4, 5, 6,
      0, 1, 2, 3, 4, 5, 6,
      0, 1, 2, 3, 4, 5, 6,
      0, 1, 2, 3, 4, 5, 6,
    ])) } }, tags: d, legacyFactor: 20),

  BenchmarkInfo(name: "Data.init.Sequence.809B.Count", runFunction: {
    _init($0*100, sequence: Bytes(count: 809, exact: true)) }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.809B.Count0", runFunction: {
    _init($0*100, sequence: Bytes(count: 809, exact: false)) }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.809B.Count.I", runFunction: {
    for _ in 0..<$0*100 {
      blackHole(Data(Bytes(count: 809, exact: true))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.809B.Count0.I", runFunction: {
    for _ in 0..<$0*100 {
      blackHole(Data(Bytes(count: 809, exact: false))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.2047B.Count.I", runFunction: {
    for _ in 0..<$0*50 {
    blackHole(Data(Bytes(count: 2047, exact: true))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.2047B.Count0.I", runFunction: {
    for _ in 0..<$0*50 {
      blackHole(Data(Bytes(count: 2047, exact: false))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.2049B.Count.I", runFunction: {
    for _ in 0..<$0*50 {
    blackHole(Data(Bytes(count: 2049, exact: true))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.2049B.Count0.I", runFunction: {
    for _ in 0..<$0*50 {
    blackHole(Data(Bytes(count: 2049, exact: false))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.511B.Count.I", runFunction: {
    for _ in 0..<$0*150 {
    blackHole(Data(Bytes(count: 511, exact: true))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.511B.Count0.I", runFunction: {
    for _ in 0..<$0*150 {
      blackHole(Data(Bytes(count: 511, exact: false))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.513B.Count.I", runFunction: {
    for _ in 0..<$0*150 {
    blackHole(Data(Bytes(count: 513, exact: true))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.513B.Count0.I", runFunction: {
    for _ in 0..<$0*150 {
    blackHole(Data(Bytes(count: 513, exact: false))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.64kB.Count", runFunction: {
    _init($0, sequence: Bytes(count: 2<<15, exact: true)) }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.64kB.Count0", runFunction: {
    _init($0, sequence: Bytes(count: 2<<15, exact: false)) }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.64kB.Count.I", runFunction: {
    for _ in 0..<$0 {
      blackHole(Data(Bytes(count: 2<<15, exact: true))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.64kB.Count0.I", runFunction: {
    for _ in 0..<$0 {
      blackHole(Data(Bytes(count: 2<<15, exact: false))) } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.809B.Count.RE", runFunction: {
    _init($0*100, sequence: repeatElement(UInt8(0xA0), count: 809)) }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.809B.Count0.RE", runFunction: {
    _init($0*100, sequence: Count0(repeatElement(UInt8(0xA0), count: 809))) },
    tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.809B.Count.RE.I", runFunction: {
    for _ in 0..<$0*100 {
      blackHole(Data(repeatElement(UInt8(0xA0), count: 809)))
    } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.809B.Count0.RE.I", runFunction: {
    for _ in 0..<$0*100 {
      blackHole(Data(Count0(repeatElement(UInt8(0xA0), count: 809))))
    } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.64kB.Count.RE", runFunction: {
    _init($0, sequence: repeatElement(UInt8(0xA0), count: 2<<15)) }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.64kB.Count0.RE", runFunction: {
    _init($0, sequence: Count0(repeatElement(UInt8(0xA0), count: 2<<15))) },
    tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.64kB.Count.RE.I", runFunction: {
    for _ in 0..<$0 {
      blackHole(Data(repeatElement(UInt8(0xA0), count: 2<<15)))
    } }, tags: d),
  BenchmarkInfo(name: "Data.init.Sequence.64kB.Count0.RE.I", runFunction: {
    for _ in 0..<$0 {
      blackHole(Data(Count0(repeatElement(UInt8(0xA0), count: 2<<15))))
    } }, tags: d),

  BenchmarkInfo(name: "DataSubscriptSmall",
    runFunction: { let data = small
      for _ in 0..<$0*10_000 { blackHole(data[1]) } }, tags: d),
  BenchmarkInfo(name: "DataSubscriptMedium",
    runFunction: { let data = medium
      for _ in 0..<$0*10_000 { blackHole(data[521]) } }, tags: d),

  BenchmarkInfo(name: "DataCountSmall",
    runFunction: { count($0*10_000, data: small) }, tags: d),
  BenchmarkInfo(name: "DataCountMedium",
    runFunction: { count($0*10_000, data: medium) }, tags: d),

  BenchmarkInfo(name: "DataSetCountSmall",
    runFunction: { setCount($0*10_000, data: small, extra: 3) }, tags: d),
  BenchmarkInfo(name: "DataSetCountMedium",
    runFunction: { setCount($0*1_000, data: medium, extra: 100) }, tags: d,
    legacyFactor: 10),

  BenchmarkInfo(name: "DataAccessBytesSmall",
    runFunction: { withUnsafeBytes($0*10_000, data: small) }, tags: d),
  BenchmarkInfo(name: "DataAccessBytesMedium",
    runFunction: { withUnsafeBytes($0*10_000, data: medium) }, tags: d),

  BenchmarkInfo(name: "DataMutateBytesSmall",
    runFunction: { withUnsafeMutableBytes($0*500, data: small) }, tags: d,
    legacyFactor: 20),
  BenchmarkInfo(name: "DataMutateBytesMedium",
    runFunction: { withUnsafeMutableBytes($0*500, data: medium) }, tags: d,
    legacyFactor: 20),

  BenchmarkInfo(name: "DataCopyBytesSmall",
    runFunction: { copyBytes($0*10_000, data: small) }, tags: d),
  BenchmarkInfo(name: "DataCopyBytesMedium",
    runFunction: { copyBytes($0*5_000, data: medium) }, tags: d,
    legacyFactor: 2),

  BenchmarkInfo(name: "DataAppendBytesSmall",
    runFunction: { append($0*10_000, bytes: 3, to: small) }, tags: d),
  BenchmarkInfo(name: "DataAppendBytesMedium",
    runFunction: { append($0*500, bytes: 809, to: medium) }, tags: d,
    legacyFactor: 20),

  BenchmarkInfo(name: "DataAppendArray",
    runFunction: { append($0*100, array: array809, to: medium) }, tags: d,
    legacyFactor: 100),

  BenchmarkInfo(name: "DataReset",
    runFunction: { resetBytes($0*100, in: 431..<809, data: medium) },
    tags: d, legacyFactor: 100),

  BenchmarkInfo(name: "DataReplaceSmall", runFunction: {
    replace($0*100, data: medium, subrange:431..<809, with: small) },
    tags: d, legacyFactor: 100),
  BenchmarkInfo(name: "DataReplaceMedium", runFunction: {
    replace($0*100, data: medium, subrange:431..<809, with: medium) },
    tags: d, legacyFactor: 100),
  BenchmarkInfo(name: "DataReplaceLarge", runFunction: {
    replace($0*100, data: medium, subrange:431..<809, with: large) },
    tags: d, legacyFactor: 100),

  BenchmarkInfo(name: "DataReplaceSmallBuffer", runFunction: {
    replaceBuffer($0*100, data: medium, subrange:431..<809, with: small) },
    tags: d, legacyFactor: 100),
  BenchmarkInfo(name: "DataReplaceMediumBuffer", runFunction: {
    replaceBuffer($0*100, data: medium, subrange:431..<809, with: medium) },
    tags: d, legacyFactor: 100),
  BenchmarkInfo(name: "DataReplaceLargeBuffer", runFunction: {
    replaceBuffer($0*10, data: medium, subrange:431..<809, with: large) },
    tags: d),

  BenchmarkInfo(name: "DataAppendSequence",
    runFunction: { append($0*100, sequenceLength: 809, to: medium) },
    tags: d, legacyFactor: 100),
  BenchmarkInfo(name: "Data.append.Sequence.809B.Count", runFunction: {
    append($0*100, sequence: Bytes(count: 809, exact: true), to: medium) },
    tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.809B.Count0", runFunction: {
    append($0*100, sequence: Bytes(count: 809, exact: false) , to: medium) },
    tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.809B.Count.I",
    runFunction: { for _ in 1...$0*100 { var copy = medium
      copy.append(contentsOf: Bytes(count: 809, exact: true)) } }, tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.809B.Count0.I",
    runFunction: { for _ in 1...$0*100 { var copy = medium
      copy.append(contentsOf: Bytes(count: 809, exact: false)) } }, tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.64kB.Count", runFunction: {
    append($0, sequence: Bytes(count: 2<<15, exact: true), to: medium) },
    tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.64kB.Count0", runFunction: {
    append($0, sequence: Bytes(count: 2<<15, exact: false), to: medium) },
    tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.64kB.Count.I", runFunction: {
    for _ in 1...$0 { var copy = medium
      copy.append(contentsOf: Bytes(count: 2<<15, exact: true)) } }, tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.64kB.Count0.I", runFunction: {
    for _ in 1...$0 { var copy = medium
      copy.append(contentsOf: Bytes(count: 2<<15, exact: false)) } }, tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.809B.Count.RE.I", runFunction: {
    for _ in 1...$0*100 { var copy = medium
      copy.append(contentsOf: repeatElement(UInt8(0xA0), count: 809)) } },
    tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.809B.Count0.RE.I", runFunction: {
    for _ in 1...$0*100 { var copy = medium
    copy.append(contentsOf: Count0(repeatElement(UInt8(0xA0), count: 809))) } },
    tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.809B.Count.RE", runFunction: {
    append($0*100, sequence: repeatElement(UInt8(0xA0), count: 809),
           to: medium) }, tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.809B.Count0.RE", runFunction: {
    append($0*100, sequence: Count0(repeatElement(UInt8(0xA0), count: 809)),
           to: medium) }, tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.64kB.Count.RE.I", runFunction: {
    for _ in 1...$0 { var copy = medium
      copy.append(contentsOf: repeatElement(UInt8(0xA0), count: 2<<15)) } },
    tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.64kB.Count0.RE.I", runFunction: {
    for _ in 1...$0 { var copy = medium
    copy.append(contentsOf: Count0(repeatElement(UInt8(0xA0), count: 2<<15))) }
  }, tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.64kB.Count.RE", runFunction: {
    append($0, sequence: repeatElement(UInt8(0xA0), count: 2<<15),
           to: medium) }, tags: d),
  BenchmarkInfo(name: "Data.append.Sequence.64kB.Count0.RE", runFunction: {
    append($0, sequence: Count0(repeatElement(UInt8(0xA0), count: 2<<15)),
           to: medium) }, tags: d),

  BenchmarkInfo(name: "DataAppendDataSmallToSmall",
    runFunction: { append($0*500, data: small, to: small) }, tags: d,
    legacyFactor: 20),
  BenchmarkInfo(name: "DataAppendDataSmallToMedium",
    runFunction: { append($0*500, data: small, to: medium) }, tags: d,
    legacyFactor: 20),
  BenchmarkInfo(name: "DataAppendDataSmallToLarge",
    runFunction: { append($0*50, data: small, to: large) }, tags: d,
    legacyFactor: 200),
  BenchmarkInfo(name: "DataAppendDataMediumToSmall",
    runFunction: { append($0*500, data: medium, to: small) }, tags: d,
    legacyFactor: 20),
  BenchmarkInfo(name: "DataAppendDataMediumToMedium",
    runFunction: { append($0*500, data: medium, to: medium) }, tags: d,
    legacyFactor: 20),
  BenchmarkInfo(name: "DataAppendDataMediumToLarge",
    runFunction: { append($0*50, data: medium, to: large) }, tags: d,
    legacyFactor: 200),
  BenchmarkInfo(name: "DataAppendDataLargeToSmall",
    runFunction: { append($0*50, data: large, to: small) }, tags: d,
    legacyFactor: 200),
  BenchmarkInfo(name: "DataAppendDataLargeToMedium",
    runFunction: { append($0*50, data: large, to: medium) }, tags: d,
    legacyFactor: 200),
  BenchmarkInfo(name: "DataAppendDataLargeToLarge",
    runFunction: { append($0*50, data: large, to: large) }, tags: d + [.unstable],
    legacyFactor: 200),

  BenchmarkInfo(name: "DataToStringEmpty",
    runFunction: { string($0*200, from: emptyData) }, tags: d,
    legacyFactor: 50),
  BenchmarkInfo(name: "DataToStringSmall",
    runFunction: { string($0*200, from: smallData) }, tags: d,
    legacyFactor: 50),
  BenchmarkInfo(name: "DataToStringMedium",
    runFunction: { string($0*200, from: mediumData) }, tags: d,
    legacyFactor: 50),
  BenchmarkInfo(name: "DataToStringLargeUnicode",
    runFunction: { string($0*200, from: largeUnicodeData) }, tags: d,
    legacyFactor: 50),

  BenchmarkInfo(name: "StringToDataEmpty",
    runFunction: { dataFromUTF8View($0*200, from: emptyString) }, tags: d,
    legacyFactor: 50),
  BenchmarkInfo(name: "StringToDataSmall",
    runFunction: { dataFromUTF8View($0*200, from: smallString) }, tags: d,
    legacyFactor: 50),
  BenchmarkInfo(name: "StringToDataMedium",
    runFunction: { dataFromUTF8View($0*200, from: mediumString) }, tags: d,
    legacyFactor: 50),
  BenchmarkInfo(name: "StringToDataLargeUnicode",
    runFunction: { dataFromUTF8View($0*200, from: largeUnicodeString) }, tags: d,
    legacyFactor: 50),

  BenchmarkInfo(name: "String.data.Empty",
    runFunction: { dataUsingUTF8Encoding($0*200, from: emptyString) }, tags: d),
  BenchmarkInfo(name: "String.data.Small",
    runFunction: { dataUsingUTF8Encoding($0*200, from: smallString) }, tags: d),
  BenchmarkInfo(name: "String.data.Medium",
    runFunction: { dataUsingUTF8Encoding($0*200, from: mediumString) }, tags: d),
  BenchmarkInfo(name: "String.data.LargeUnicode",
    runFunction: { dataUsingUTF8Encoding($0*200, from: largeUnicodeString) }, tags: d),

  BenchmarkInfo(name: "Data.hash.Empty",
    runFunction: { hash($0*10_000, data: Data()) }, tags: d),
  BenchmarkInfo(name: "Data.hash.Small",
    runFunction: { hash($0*10_000, data: small) }, tags: d),
  BenchmarkInfo(name: "Data.hash.Medium",
    runFunction: { hash($0*1_000, data: medium) }, tags: d),
]

let emptyString = ""
let smallString = "\r\n"
let mediumString = "\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
let largeUnicodeString = 
  "Swiftに大幅な改良が施され、𓀀𓀁𓀂𓀃, 🇺🇸🇨🇦🇲🇽" + mediumString
let emptyData = Data()
let smallData = Data(smallString.utf8)
let mediumData = Data(mediumString.utf8)
let largeUnicodeData = Data(largeUnicodeString.utf8)

let small = sampleData(.small)
let medium = sampleData(.medium)
let large = sampleData(.large)

let array809 = byteArray(size: 809)

struct Count0<S: Sequence> : Sequence {
  let base: S
  init (_ base:S) { self.base = base }
  func makeIterator() -> S.Iterator { return base.makeIterator() }
  var underestimatedCount: Int { return 0 }
}

struct Bytes: Sequence, IteratorProtocol {
    let count: Int
    let exact: Bool
    var  i: Int = 0
    init(count: Int, exact: Bool) {
        self.count = count
        self.exact = exact
    }
    mutating func next() -> UInt8? {
        defer { i = i &+ 1 }
        return (i < count) ? UInt8(truncatingIfNeeded: i) : nil
    }
    var underestimatedCount: Int {
        return exact ? count : 0
    }
}

enum SampleKind {
  case small
  case medium
  case large
  case veryLarge
  case immutableBacking
}

func byteArray(size: Int) -> [UInt8] {
  var bytes = [UInt8](repeating: 0, count: size)
  bytes.withUnsafeMutableBufferPointer { buffer in
    for i in buffer.indices {
      buffer[i] = UInt8(truncatingIfNeeded: i)
    }
  }
  return bytes
}

func sampleData(size: Int) -> Data {
  var data = Data(count: size)
  data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> () in
    for i in 0..<size {
      bytes[i] = UInt8(truncatingIfNeeded: i)
    }
  }
  return data
}

func sampleBridgedNSData() -> Data {
  let count = 1033
  let data = NSData(bytes: byteArray(size: count), length: count)
  return Data(referencing: data)
}

func sampleData(_ type: SampleKind) -> Data {
  switch type {
  case .small: return sampleData(size: 11)
  case .medium: return sampleData(size: 1033)
  case .large: return sampleData(size: 40980)
  case .veryLarge: return sampleData(size: 1024 * 1024 * 1024 + 128)
  case .immutableBacking: return sampleBridgedNSData()
  }

}

@inline(never)
func withUnsafeBytes(_ n: Int, data: Data) {
  for _ in 1...n {
    data.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) in
      blackHole(ptr.pointee)
    }
  }
}

@inline(never)
func withUnsafeMutableBytes(_ n: Int, data: Data) {
  for _ in 1...n {
    var copy = data
    copy.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer<UInt8>) in
      // Mutate a byte
      ptr.pointee = 42
    }
  }
}

@inline(never)
func copyBytes(_ n: Int, data: Data) {
  let amount = data.count
  let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: amount)
  defer { buffer.deallocate() }
  for _ in 1...n {
    data.copyBytes(to: buffer, from: 0..<amount)
  }
}

@inline(never)
func append(_ n: Int, bytes count: Int, to data: Data) {
  let bytes = malloc(count).assumingMemoryBound(to: UInt8.self)
  defer { free(bytes) }
  for _ in 1...n {
    var copy = data
    copy.append(bytes, count: count)
  }
}

@inline(never)
func append(_ n: Int, array bytes: [UInt8], to data: Data) {
  for _ in 1...n {
    var copy = data
    copy.append(contentsOf: bytes)
  }
}

@inline(never)
func append(_ n: Int, sequenceLength: Int, to data: Data) {
  let bytes = repeatElement(UInt8(0xA0), count: sequenceLength)
  for _ in 1...n {
    var copy = data
    copy.append(contentsOf: bytes)
  }
}

@inline(never)
func append<S: Sequence>(_ n: Int, sequence: S, to data: Data)
where S.Element == UInt8 {
  for _ in 1...n {
    var copy = data
    copy.append(contentsOf: sequence)
  }
}

@inline(never)
func _init<S: Sequence>(_ n: Int, sequence: S) where S.Element == UInt8 {
  for _ in 1...n {
    blackHole(Data(sequence))
  }
}

@inline(never)
func resetBytes(_ n: Int, in range: Range<Data.Index>, data: Data) {
  for _ in 1...n {
    var copy = data
    copy.resetBytes(in: range)
  }
}

@inline(never)
func replace(
  _ n: Int,
  data: Data,
  subrange range: Range<Data.Index>,
  with replacement: Data
) {
  for _ in 1...n {
    var copy = data
    copy.replaceSubrange(range, with: replacement)
  }
}

@inline(never)
func replaceBuffer(
  _ n: Int,
  data: Data,
  subrange range: Range<Data.Index>,
  with replacement: Data
) {
  replacement.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) in
    let buffer = UnsafeBufferPointer(start: bytes, count: replacement.count)

    for _ in 1...n {
      var copy = data
      copy.replaceSubrange(range, with: buffer)
    }
  }
}

@inline(never)
func append(_ n: Int, data: Data, to target: Data) {
  var copy: Data
  for _ in 1...n {
    copy = target
    copy.append(data)
  }
}

@inline(never)
public func count(_ n: Int, data: Data) {
  for _ in 1...n {
    blackHole(data.count)
  }
}

@inline(never)
public func setCount(_ n: Int, data: Data, extra: Int) {
  var copy = data
  let count = data.count + extra
  let orig = data.count
  for _ in 1...n {
    copy.count = count
    copy.count = orig
  }
}

@inline(never)
public func string(_ n: Int, from data: Data) {
  for _ in 1...n {
    blackHole(String(decoding: data, as: UTF8.self))
  }
}

@inline(never)
public func dataFromUTF8View(_ n: Int, from string: String) {
  for _ in 1...n {
    blackHole(Data(string.utf8))
  }
}

@inline(never)
public func dataUsingUTF8Encoding(_ n: Int, from string: String) {
  for _ in 1...n {
    autoreleasepool { blackHole(string.data(using: .utf8)) }
  }
}

@inline(never)
public func hash(_ n: Int, data: Data) {
  var hasher = Hasher()
  for _ in 0 ..< n {
    hasher.combine(data)
  }

  let _ = hasher.finalize()
}
