blob: dc63ae486a1b81e54247f33fd2a73ed68fc8685e [file] [log] [blame]
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -o %t/a.out -O
// RUN: %target-run %t/a.out
// REQUIRES: executable_test
// REQUIRES: objc_interop
import SwiftPrivate
import StdlibUnittest
import Foundation
protocol TestableUnicodeCodec : UnicodeCodec {
associatedtype CodeUnit : FixedWidthInteger
static func encodingId() -> String.Encoding
static func name() -> NSString
}
extension UTF8 : TestableUnicodeCodec {
static func encodingId() -> String.Encoding {
return .utf8
}
static func name() -> NSString {
return "UTF8"
}
}
extension UTF16 : TestableUnicodeCodec {
static func encodingId() -> String.Encoding {
return .utf16LittleEndian
}
static func name() -> NSString {
return "UTF16"
}
}
extension UTF32 : TestableUnicodeCodec {
static func encodingId() -> String.Encoding {
return .utf32LittleEndian
}
static func name() -> NSString {
return "UTF32"
}
}
// The valid ranges of Unicode scalar values
var unicodeScalarRanges: [CountableClosedRange<UInt32>] = [UInt32(0)...0xd7ff, 0xe000...0x10ffff]
var unicodeScalarCount: Int {
var count = 0
for r in unicodeScalarRanges {
count += Int(r.upperBound - r.lowerBound)
}
return count
}
func nthUnicodeScalar(_ n: UInt32) -> UnicodeScalar {
var count: UInt32 = 0
for r in unicodeScalarRanges {
count += r.upperBound - r.lowerBound
if count > n {
return UnicodeScalar(r.upperBound - (count - n))!
}
}
_preconditionFailure("Index out of range")
}
// `buffer` should have a length >= 4
func nsEncode<CodeUnit>(
_ c: UInt32,
_ encoding: String.Encoding,
_ buffer: inout [CodeUnit],
_ used: inout Int
) {
var c = c
_precondition(buffer.count >= 4, "buffer is not large enough")
let s = NSString(
bytes: &c,
length: 4,
encoding: String.Encoding.utf32LittleEndian.rawValue)!
s.getBytes(
&buffer,
maxLength: buffer.count,
usedLength: &used,
encoding: encoding.rawValue,
options: [],
range: NSRange(location: 0, length: s.length),
remaining: nil)
}
final class CodecTest<Codec : TestableUnicodeCodec> {
var used = 0
typealias CodeUnit = Codec.CodeUnit
var nsEncodeBuffer: [CodeUnit] = Array(repeating: 0, count: 4)
var encodeBuffer: [CodeUnit] = Array(repeating: 0, count: 4)
final func testOne(_ scalar: UnicodeScalar) {
/* Progress reporter
if (scalar.value % 0x1000) == 0 {
print("\(asHex(scalar.value))")
}
*/
// Use Cocoa to encode the scalar
nsEncode(scalar.value, Codec.encodingId(), &nsEncodeBuffer, &used)
let nsEncoded = nsEncodeBuffer[0..<(used/MemoryLayout<CodeUnit>.size)]
var encodeIndex = encodeBuffer.startIndex
let encodeOutput: (CodeUnit) -> Void = {
self.encodeBuffer[encodeIndex] = $0
encodeIndex += 1
}
var iter = nsEncoded.makeIterator()
var decoded: UnicodeScalar
var decoder = Codec()
switch decoder.decode(&iter) {
case .scalarValue(let us):
decoded = us
default:
fatalError("decoding failed")
}
expectEqualTest(
scalar, decoded,
"Decoding failed: \(asHex(scalar.value)) => " +
"\(asHex(nsEncoded)) => \(asHex(decoded.value))"
) { $0 == $1 }
encodeIndex = encodeBuffer.startIndex
Codec.encode(scalar, into: encodeOutput)
expectEqualTest(
nsEncoded, encodeBuffer[0..<encodeIndex],
"Decoding failed: \(asHex(nsEncoded)) => " +
"\(asHex(scalar.value)) => \(asHex(self.encodeBuffer[0]))"
) { $0 == $1 }
}
final func run(_ minScalarOrd: Int, _ maxScalarOrd: Int) {
print("testing \(Codec.name())")
for i in minScalarOrd..<maxScalarOrd {
testOne(nthUnicodeScalar(UInt32(i)))
}
}
}
var UTFEncoders = TestSuite("UTFEncoders")
UTFEncoders.test("encode") {
let minScalarOrd = 0
let maxScalarOrd = unicodeScalarCount
CodecTest<UTF8>().run(minScalarOrd, maxScalarOrd)
CodecTest<UTF16>().run(minScalarOrd, maxScalarOrd)
CodecTest<UTF32>().run(minScalarOrd, maxScalarOrd)
}
runAllTests()