commit
a30d846585
|
@ -39,103 +39,74 @@ extension Data {
|
|||
/// Decode Crockford's Base32
|
||||
init?(base32Encoded base32String: String, using table: [UInt8] = Base32.crockfordsDecodingTable) {
|
||||
var base32String = base32String
|
||||
while let last = base32String.last, last == "=" {
|
||||
base32String.removeLast()
|
||||
if base32String.last == "=", let index = base32String.lastIndex(where: { $0 != "=" }) {
|
||||
base32String = String(base32String[...index])
|
||||
}
|
||||
|
||||
let result: Data? = base32String.withCString(encodedAs: Unicode.UTF8.self) { (src) in
|
||||
func _strlen(_ str: UnsafePointer<UInt8>) -> Int {
|
||||
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 {
|
||||
let src = base32String.utf8
|
||||
guard [0, 2, 4, 5, 7].contains(src.count % 8) else {
|
||||
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
|
||||
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
|
||||
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)
|
||||
var dstp = dst
|
||||
defer { dst.deallocate() }
|
||||
|
@ -179,18 +150,18 @@ extension Data {
|
|||
if padding {
|
||||
switch srcleft {
|
||||
case 1:
|
||||
dstp[2] = "=".utf8.first!
|
||||
dstp[3] = "=".utf8.first!
|
||||
dstp[2] = 0x3d
|
||||
dstp[3] = 0x3d
|
||||
fallthrough
|
||||
case 2:
|
||||
dstp[4] = "=".utf8.first!
|
||||
dstp[4] = 0x3d
|
||||
fallthrough
|
||||
case 3:
|
||||
dstp[5] = "=".utf8.first!
|
||||
dstp[6] = "=".utf8.first!
|
||||
dstp[5] = 0x3d
|
||||
dstp[6] = 0x3d
|
||||
fallthrough
|
||||
case 4:
|
||||
dstp[7] = "=".utf8.first!
|
||||
dstp[7] = 0x3d
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public struct ULID: Hashable, Equatable, Comparable, CustomStringConvertible {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
withUnsafeMutableBytes(of: &ulid) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import XCTest
|
||||
|
||||
import ULIDTests
|
||||
|
||||
var tests = [XCTestCaseEntry]()
|
||||
tests += ULIDTests.allTests()
|
||||
tests += ULIDTests.__allTests()
|
||||
|
||||
XCTMain(tests)
|
||||
|
|
|
@ -224,28 +224,4 @@ final class Base32Tests: XCTestCase {
|
|||
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)
|
||||
}
|
||||
|
||||
func testParseULIDStringError() {
|
||||
let zero = ""
|
||||
XCTAssertNil(ULID(ulidString: zero))
|
||||
}
|
||||
|
||||
func testParseULIDData() {
|
||||
let expected: [UInt8] = [
|
||||
0x01, 0x68, 0x3D, 0x17, 0x73, 0x09, 0xE5, 0x2D,
|
||||
|
@ -73,6 +78,11 @@ final class ULIDTests: XCTestCase {
|
|||
XCTAssertEqual(expected, Array(actual!.ulidData))
|
||||
}
|
||||
|
||||
func testParseULIDDataError() {
|
||||
let zero = Data()
|
||||
XCTAssertNil(ULID(ulidData: zero))
|
||||
}
|
||||
|
||||
func testULIDDataLength() {
|
||||
let ulid = ULID()
|
||||
XCTAssertEqual(16, ulid.ulidData.count)
|
||||
|
@ -127,10 +137,10 @@ final class ULIDTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testComparable1() {
|
||||
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)
|
||||
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, 0))
|
||||
XCTAssertFalse(lhs < rhs)
|
||||
XCTAssertTrue(lhs > rhs)
|
||||
}
|
||||
|
||||
func testComparable2() {
|
||||
|
@ -141,6 +151,13 @@ final class ULIDTests: XCTestCase {
|
|||
}
|
||||
|
||||
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 ulid0 = ULID(timestamp: now.addingTimeInterval(-120))
|
||||
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() {
|
||||
let ulidString = "01D0YHEWR9WMPY4NNTPK1MR1TQ"
|
||||
let expected = """
|
||||
|
@ -198,27 +230,6 @@ final class ULIDTests: XCTestCase {
|
|||
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 {
|
||||
|
|
|
@ -1,18 +1,67 @@
|
|||
//
|
||||
// XCTestManifests.swift
|
||||
// ULIDTests
|
||||
//
|
||||
// Created by Yasuhiro Hatta on 2019/01/11.
|
||||
// Copyright © 2019 yaslab. All rights reserved.
|
||||
//
|
||||
|
||||
#if !canImport(ObjectiveC)
|
||||
import XCTest
|
||||
|
||||
#if !os(macOS)
|
||||
public func allTests() -> [XCTestCaseEntry] {
|
||||
extension Base32Tests {
|
||||
// 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 [
|
||||
testCase(Base32Tests.allTests),
|
||||
testCase(ULIDTests.allTests)
|
||||
testCase(Base32Tests.__allTests__Base32Tests),
|
||||
testCase(ULIDTests.__allTests__ULIDTests),
|
||||
]
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue