Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring #4

Merged
merged 4 commits into from
Jul 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 60 additions & 89 deletions Sources/ULID/Data+Base32.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
let src = base32String.utf8
guard [0, 2, 4, 5, 7].contains(src.count % 8) else {
return nil
}
var srcleft = src.count
var srci = 0

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
}

return true
}

guard success else {
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
}

return buffer
}

guard let data = result else {
return nil
srci += 8
srcleft -= 8
dsti += 5
}

self = data
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() }
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/ULID/ULID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import XCTest

import ULIDTests

var tests = [XCTestCaseEntry]()
tests += ULIDTests.allTests()
tests += ULIDTests.__allTests()

XCTMain(tests)
24 changes: 0 additions & 24 deletions Tests/ULIDTests/Data+Base32Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 36 additions & 25 deletions Tests/ULIDTests/ULIDTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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() {
Expand All @@ -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))
Expand Down Expand Up @@ -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 = """
Expand All @@ -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 {
Expand Down
Loading