commit
a30d846585
|
@ -39,103 +39,74 @@ extension Data {
|
||||||
/// Decode Crockford's Base32
|
/// Decode Crockford's Base32
|
||||||
init?(base32Encoded base32String: String, using table: [UInt8] = Base32.crockfordsDecodingTable) {
|
init?(base32Encoded base32String: String, using table: [UInt8] = Base32.crockfordsDecodingTable) {
|
||||||
var base32String = base32String
|
var base32String = base32String
|
||||||
while let last = base32String.last, last == "=" {
|
if base32String.last == "=", let index = base32String.lastIndex(where: { $0 != "=" }) {
|
||||||
base32String.removeLast()
|
base32String = String(base32String[...index])
|
||||||
}
|
}
|
||||||
|
|
||||||
let result: Data? = base32String.withCString(encodedAs: Unicode.UTF8.self) { (src) in
|
let src = base32String.utf8
|
||||||
func _strlen(_ str: UnsafePointer<UInt8>) -> Int {
|
guard [0, 2, 4, 5, 7].contains(src.count % 8) else {
|
||||||
var str = str
|
|
||||||
var i = 0
|
|
||||||
while str.pointee != 0 {
|
|
||||||
str += 1
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
let srclen = _strlen(src)
|
|
||||||
guard [0, 2, 4, 5, 7].contains(srclen % 8) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let dstlen = srclen * 5 / 8
|
|
||||||
|
|
||||||
var buffer = Data(count: dstlen)
|
|
||||||
let success: Bool = buffer.withUnsafeMutableBytes { (dst: UnsafeMutableRawBufferPointer) -> Bool in
|
|
||||||
var srcleft = srclen
|
|
||||||
var srcp = src
|
|
||||||
|
|
||||||
var dsti: UnsafeMutableRawBufferPointer.Index = 0
|
|
||||||
|
|
||||||
let work = UnsafeMutablePointer<UInt8>.allocate(capacity: 8)
|
|
||||||
defer { work.deallocate() }
|
|
||||||
|
|
||||||
while srcleft > 0 {
|
|
||||||
let worklen = Swift.min(8, srcleft)
|
|
||||||
for i in 0 ..< worklen {
|
|
||||||
work[i] = table[Int(srcp[i])]
|
|
||||||
if work[i] == 0xff {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch worklen {
|
|
||||||
case 8:
|
|
||||||
dst[dsti + 4] = (work[6] << 5) | (work[7] )
|
|
||||||
fallthrough
|
|
||||||
case 7:
|
|
||||||
dst[dsti + 3] = (work[4] << 7) | (work[5] << 2) | (work[6] >> 3)
|
|
||||||
fallthrough
|
|
||||||
case 5:
|
|
||||||
dst[dsti + 2] = (work[3] << 4) | (work[4] >> 1)
|
|
||||||
fallthrough
|
|
||||||
case 4:
|
|
||||||
dst[dsti + 1] = (work[1] << 6) | (work[2] << 1) | (work[3] >> 4)
|
|
||||||
fallthrough
|
|
||||||
case 2:
|
|
||||||
dst[dsti + 0] = (work[0] << 3) | (work[1] >> 2)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
srcp += 8
|
|
||||||
srcleft -= 8
|
|
||||||
dsti += 5
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
guard success else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let data = result else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
var srcleft = src.count
|
||||||
|
var srci = 0
|
||||||
|
|
||||||
self = data
|
let dstlen = src.count * 5 / 8
|
||||||
|
let dst = UnsafeMutablePointer<UInt8>.allocate(capacity: dstlen)
|
||||||
|
var dsti = 0
|
||||||
|
defer { dst.deallocate() }
|
||||||
|
|
||||||
|
let work = UnsafeMutablePointer<UInt8>.allocate(capacity: 8)
|
||||||
|
defer { work.deallocate() }
|
||||||
|
|
||||||
|
while srcleft > 0 {
|
||||||
|
let worklen = Swift.min(8, srcleft)
|
||||||
|
for i in 0 ..< worklen {
|
||||||
|
work[i] = table[Int(src[src.index(src.startIndex, offsetBy: srci + i)])]
|
||||||
|
if work[i] == 0xff {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch worklen {
|
||||||
|
case 8:
|
||||||
|
dst[dsti + 4] = (work[6] << 5) | (work[7] )
|
||||||
|
fallthrough
|
||||||
|
case 7:
|
||||||
|
dst[dsti + 3] = (work[4] << 7) | (work[5] << 2) | (work[6] >> 3)
|
||||||
|
fallthrough
|
||||||
|
case 5:
|
||||||
|
dst[dsti + 2] = (work[3] << 4) | (work[4] >> 1)
|
||||||
|
fallthrough
|
||||||
|
case 4:
|
||||||
|
dst[dsti + 1] = (work[1] << 6) | (work[2] << 1) | (work[3] >> 4)
|
||||||
|
fallthrough
|
||||||
|
case 2:
|
||||||
|
dst[dsti + 0] = (work[0] << 3) | (work[1] >> 2)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
srci += 8
|
||||||
|
srcleft -= 8
|
||||||
|
dsti += 5
|
||||||
|
}
|
||||||
|
|
||||||
|
self = Data(bytes: dst, count: dstlen)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode Crockford's Base32
|
/// Encode Crockford's Base32
|
||||||
func base32EncodedString(padding: Bool = true, using table: [UInt8] = Base32.crockfordsEncodingTable) -> String {
|
func base32EncodedString(padding: Bool = true, using table: [UInt8] = Base32.crockfordsEncodingTable) -> String {
|
||||||
var srcleft = self.count
|
|
||||||
|
|
||||||
let dstlen: Int
|
|
||||||
if padding {
|
|
||||||
dstlen = (self.count + 4) / 5 * 8
|
|
||||||
} else {
|
|
||||||
dstlen = (self.count * 8 + 4) / 5
|
|
||||||
}
|
|
||||||
var dstleft = dstlen
|
|
||||||
|
|
||||||
return self.withUnsafeBytes { (src: UnsafeRawBufferPointer) -> String in
|
return self.withUnsafeBytes { (src: UnsafeRawBufferPointer) -> String in
|
||||||
var srci: UnsafeRawBufferPointer.Index = 0
|
var srcleft = src.count
|
||||||
|
var srci = 0
|
||||||
|
|
||||||
|
let dstlen: Int
|
||||||
|
if padding {
|
||||||
|
dstlen = (src.count + 4) / 5 * 8
|
||||||
|
} else {
|
||||||
|
dstlen = (src.count * 8 + 4) / 5
|
||||||
|
}
|
||||||
|
var dstleft = dstlen
|
||||||
let dst = UnsafeMutablePointer<UInt8>.allocate(capacity: dstlen + 1)
|
let dst = UnsafeMutablePointer<UInt8>.allocate(capacity: dstlen + 1)
|
||||||
var dstp = dst
|
var dstp = dst
|
||||||
defer { dst.deallocate() }
|
defer { dst.deallocate() }
|
||||||
|
@ -179,18 +150,18 @@ extension Data {
|
||||||
if padding {
|
if padding {
|
||||||
switch srcleft {
|
switch srcleft {
|
||||||
case 1:
|
case 1:
|
||||||
dstp[2] = "=".utf8.first!
|
dstp[2] = 0x3d
|
||||||
dstp[3] = "=".utf8.first!
|
dstp[3] = 0x3d
|
||||||
fallthrough
|
fallthrough
|
||||||
case 2:
|
case 2:
|
||||||
dstp[4] = "=".utf8.first!
|
dstp[4] = 0x3d
|
||||||
fallthrough
|
fallthrough
|
||||||
case 3:
|
case 3:
|
||||||
dstp[5] = "=".utf8.first!
|
dstp[5] = 0x3d
|
||||||
dstp[6] = "=".utf8.first!
|
dstp[6] = 0x3d
|
||||||
fallthrough
|
fallthrough
|
||||||
case 4:
|
case 4:
|
||||||
dstp[7] = "=".utf8.first!
|
dstp[7] = 0x3d
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ public struct ULID: Hashable, Equatable, Comparable, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
|
|
||||||
public init?(ulidString string: String) {
|
public init?(ulidString string: String) {
|
||||||
guard string.count == 26, let data = Data(base32Encoded: "000000" + string) else {
|
guard string.utf8.count == 26, let data = Data(base32Encoded: "000000" + string) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
withUnsafeMutableBytes(of: &ulid) {
|
withUnsafeMutableBytes(of: &ulid) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
import ULIDTests
|
import ULIDTests
|
||||||
|
|
||||||
var tests = [XCTestCaseEntry]()
|
var tests = [XCTestCaseEntry]()
|
||||||
tests += ULIDTests.allTests()
|
tests += ULIDTests.__allTests()
|
||||||
|
|
||||||
XCTMain(tests)
|
XCTMain(tests)
|
||||||
|
|
|
@ -224,28 +224,4 @@ final class Base32Tests: XCTestCase {
|
||||||
XCTAssertNil(data)
|
XCTAssertNil(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
static var allTests = [
|
|
||||||
("testEncodeBase32", testEncodeBase32),
|
|
||||||
("testEncode1", testEncode1),
|
|
||||||
("testEncode2", testEncode2),
|
|
||||||
("testEncode3", testEncode3),
|
|
||||||
("testEncode4", testEncode4),
|
|
||||||
("testEncode5", testEncode5),
|
|
||||||
("testEncode6", testEncode6),
|
|
||||||
("testEncode7", testEncode7),
|
|
||||||
("testEncode8", testEncode8),
|
|
||||||
("testEncodePad1", testEncodePad1),
|
|
||||||
("testEncodePad2", testEncodePad2),
|
|
||||||
("testEncodePad3", testEncodePad3),
|
|
||||||
("testEncodePad4", testEncodePad4),
|
|
||||||
("testEncodeNoPad", testEncodeNoPad),
|
|
||||||
("testDecodeBase32", testDecodeBase32),
|
|
||||||
("testDecodeTable", testDecodeTable),
|
|
||||||
("testDecodeInvalidCharacter", testDecodeInvalidCharacter),
|
|
||||||
("testDecodePadding", testDecodePadding),
|
|
||||||
("testDecodeIncorrectLength", testDecodeIncorrectLength)
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,11 @@ final class ULIDTests: XCTestCase {
|
||||||
XCTAssertEqual(expected, actual!.ulidString)
|
XCTAssertEqual(expected, actual!.ulidString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testParseULIDStringError() {
|
||||||
|
let zero = ""
|
||||||
|
XCTAssertNil(ULID(ulidString: zero))
|
||||||
|
}
|
||||||
|
|
||||||
func testParseULIDData() {
|
func testParseULIDData() {
|
||||||
let expected: [UInt8] = [
|
let expected: [UInt8] = [
|
||||||
0x01, 0x68, 0x3D, 0x17, 0x73, 0x09, 0xE5, 0x2D,
|
0x01, 0x68, 0x3D, 0x17, 0x73, 0x09, 0xE5, 0x2D,
|
||||||
|
@ -73,6 +78,11 @@ final class ULIDTests: XCTestCase {
|
||||||
XCTAssertEqual(expected, Array(actual!.ulidData))
|
XCTAssertEqual(expected, Array(actual!.ulidData))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testParseULIDDataError() {
|
||||||
|
let zero = Data()
|
||||||
|
XCTAssertNil(ULID(ulidData: zero))
|
||||||
|
}
|
||||||
|
|
||||||
func testULIDDataLength() {
|
func testULIDDataLength() {
|
||||||
let ulid = ULID()
|
let ulid = ULID()
|
||||||
XCTAssertEqual(16, ulid.ulidData.count)
|
XCTAssertEqual(16, ulid.ulidData.count)
|
||||||
|
@ -127,10 +137,10 @@ final class ULIDTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testComparable1() {
|
func testComparable1() {
|
||||||
let lhs = ULID(ulid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
|
let lhs = ULID(ulid: (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
|
||||||
let rhs = ULID(ulid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
|
let rhs = ULID(ulid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
|
||||||
XCTAssertTrue(lhs < rhs)
|
XCTAssertFalse(lhs < rhs)
|
||||||
XCTAssertFalse(lhs > rhs)
|
XCTAssertTrue(lhs > rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testComparable2() {
|
func testComparable2() {
|
||||||
|
@ -141,6 +151,13 @@ final class ULIDTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testComparable3() {
|
func testComparable3() {
|
||||||
|
let lhs = ULID(ulid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
|
||||||
|
let rhs = ULID(ulid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
|
||||||
|
XCTAssertTrue(lhs < rhs)
|
||||||
|
XCTAssertFalse(lhs > rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testComparable4() {
|
||||||
let now = Date()
|
let now = Date()
|
||||||
let ulid0 = ULID(timestamp: now.addingTimeInterval(-120))
|
let ulid0 = ULID(timestamp: now.addingTimeInterval(-120))
|
||||||
let ulid1 = ULID(timestamp: now.addingTimeInterval(-60))
|
let ulid1 = ULID(timestamp: now.addingTimeInterval(-60))
|
||||||
|
@ -179,6 +196,21 @@ final class ULIDTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDecodableError() {
|
||||||
|
let json = """
|
||||||
|
{ "ulid" : "" }
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
_ = try decoder.decode(CodableModel.self, from: json.data(using: .utf8)!)
|
||||||
|
XCTFail()
|
||||||
|
} catch DecodingError.dataCorrupted {
|
||||||
|
// Success
|
||||||
|
} catch {
|
||||||
|
XCTFail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testEncodable() {
|
func testEncodable() {
|
||||||
let ulidString = "01D0YHEWR9WMPY4NNTPK1MR1TQ"
|
let ulidString = "01D0YHEWR9WMPY4NNTPK1MR1TQ"
|
||||||
let expected = """
|
let expected = """
|
||||||
|
@ -198,27 +230,6 @@ final class ULIDTests: XCTestCase {
|
||||||
XCTAssertEqual(16, MemoryLayout<ULID>.size)
|
XCTAssertEqual(16, MemoryLayout<ULID>.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var allTests = [
|
|
||||||
("testGenerateTimestamp", testGenerateTimestamp),
|
|
||||||
("testGenerateRandomness", testGenerateRandomness),
|
|
||||||
("testParseULIDString", testParseULIDString),
|
|
||||||
("testParseULIDData", testParseULIDData),
|
|
||||||
("testULIDDataLength", testULIDDataLength),
|
|
||||||
("testULIDStringLength", testULIDStringLength),
|
|
||||||
("testHashable1", testHashable1),
|
|
||||||
("testHashable2", testHashable2),
|
|
||||||
("testHashable3", testHashable3),
|
|
||||||
("testEquatable1", testEquatable1),
|
|
||||||
("testEquatable2", testEquatable2),
|
|
||||||
("testComparable1", testComparable1),
|
|
||||||
("testComparable2", testComparable2),
|
|
||||||
("testComparable3", testComparable3),
|
|
||||||
("testCustomStringConvertible", testCustomStringConvertible),
|
|
||||||
("testDecodable", testDecodable),
|
|
||||||
("testEncodable", testEncodable),
|
|
||||||
("testMemorySize", testMemorySize)
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct MockRandomNumberGenerator: RandomNumberGenerator {
|
private struct MockRandomNumberGenerator: RandomNumberGenerator {
|
||||||
|
|
|
@ -1,18 +1,67 @@
|
||||||
//
|
#if !canImport(ObjectiveC)
|
||||||
// XCTestManifests.swift
|
|
||||||
// ULIDTests
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2019/01/11.
|
|
||||||
// Copyright © 2019 yaslab. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
#if !os(macOS)
|
extension Base32Tests {
|
||||||
public func allTests() -> [XCTestCaseEntry] {
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__Base32Tests = [
|
||||||
|
("testDecodeBase32", testDecodeBase32),
|
||||||
|
("testDecodeIncorrectLength", testDecodeIncorrectLength),
|
||||||
|
("testDecodeInvalidCharacter", testDecodeInvalidCharacter),
|
||||||
|
("testDecodePadding", testDecodePadding),
|
||||||
|
("testDecodeTable", testDecodeTable),
|
||||||
|
("testEncode1", testEncode1),
|
||||||
|
("testEncode2", testEncode2),
|
||||||
|
("testEncode3", testEncode3),
|
||||||
|
("testEncode4", testEncode4),
|
||||||
|
("testEncode5", testEncode5),
|
||||||
|
("testEncode6", testEncode6),
|
||||||
|
("testEncode7", testEncode7),
|
||||||
|
("testEncode8", testEncode8),
|
||||||
|
("testEncodeBase32", testEncodeBase32),
|
||||||
|
("testEncodeNoPad", testEncodeNoPad),
|
||||||
|
("testEncodePad1", testEncodePad1),
|
||||||
|
("testEncodePad2", testEncodePad2),
|
||||||
|
("testEncodePad3", testEncodePad3),
|
||||||
|
("testEncodePad4", testEncodePad4),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ULIDTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__ULIDTests = [
|
||||||
|
("testComparable1", testComparable1),
|
||||||
|
("testComparable2", testComparable2),
|
||||||
|
("testComparable3", testComparable3),
|
||||||
|
("testComparable4", testComparable4),
|
||||||
|
("testCustomStringConvertible", testCustomStringConvertible),
|
||||||
|
("testDecodable", testDecodable),
|
||||||
|
("testDecodableError", testDecodableError),
|
||||||
|
("testEncodable", testEncodable),
|
||||||
|
("testEquatable1", testEquatable1),
|
||||||
|
("testEquatable2", testEquatable2),
|
||||||
|
("testGenerateRandomness", testGenerateRandomness),
|
||||||
|
("testGenerateTimestamp", testGenerateTimestamp),
|
||||||
|
("testHashable1", testHashable1),
|
||||||
|
("testHashable2", testHashable2),
|
||||||
|
("testHashable3", testHashable3),
|
||||||
|
("testMemorySize", testMemorySize),
|
||||||
|
("testParseULIDData", testParseULIDData),
|
||||||
|
("testParseULIDDataError", testParseULIDDataError),
|
||||||
|
("testParseULIDString", testParseULIDString),
|
||||||
|
("testParseULIDStringError", testParseULIDStringError),
|
||||||
|
("testULIDDataLength", testULIDDataLength),
|
||||||
|
("testULIDStringLength", testULIDStringLength),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func __allTests() -> [XCTestCaseEntry] {
|
||||||
return [
|
return [
|
||||||
testCase(Base32Tests.allTests),
|
testCase(Base32Tests.__allTests__Base32Tests),
|
||||||
testCase(ULIDTests.allTests)
|
testCase(ULIDTests.__allTests__ULIDTests),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue