Merge pull request #4 from YOCKOW/development

v3.2.0
This commit is contained in:
YOCKOW 2020-05-24 14:27:37 +09:00 committed by GitHub
commit 900e9028f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 240 additions and 76 deletions

60
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,60 @@
name: CI
on:
push:
branches:
- '**'
tags-ignore:
- '**'
paths:
- '**/*.swift'
- '.github/workflows/*.yml'
pull_request:
paths:
- '**/*.swift'
- '.github/workflows/*.yml'
jobs:
test:
strategy:
matrix:
os:
- ubuntu-latest
- macOS-latest
swift-compat-ver:
- '5'
# - '4.2'
# - '4'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Use a cache for ".build" directory.
uses: actions/cache@v1
with:
path: .build
key: build-${{ github.workspace }}-${{ runner.os }}-${{ matrix.swift-compat-ver }}-${{ hashFiles('**/*.swift') }}
restore-keys: |
build-${{ github.workspace }}-${{ runner.os }}-${{ matrix.swift-compat-ver }}-
build-${{ github.workspace }}-${{ runner.os }}-
build-${{ github.workspace }}-
- uses: YOCKOW/Action-setup-swift@master
with:
swift-version: '5.2.2'
- name: Try to build products with debug mode.
run: |
swift build --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
if [ $? != 0 ]; then
echo "Failed to build products with debug mode."
rm -rf $(cd .build/debug && pwd -P)
fi
continue-on-error: true
- name: Test with debug mode.
run: swift test --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
- name: Try to build products with release mode.
run: |
swift build --configuration release -Xswiftc -enable-testing -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
if [ $? != 0 ]; then
echo "Failed to build products with release mode."
rm -rf $(cd .build/release && pwd -P)
fi
continue-on-error: true
- name: Test with release mode.
run: swift test --configuration release -Xswiftc -enable-testing -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
.DS_Store
.build/
.build
build/
y-build/

View File

@ -1,28 +0,0 @@
branches:
except:
- /^\d+\.\d+\.\d+(-\S*)?$/
os:
- linux
- osx
language: generic
sudo: required
osx_image: xcode10.2
dist: xenial
env:
- SWIFT_BUILD_OPTION="-Xswiftc -swift-version -Xswiftc 4"
- SWIFT_BUILD_OPTION="-Xswiftc -swift-version -Xswiftc 4.2"
- SWIFT_BUILD_OPTION="-Xswiftc -swift-version -Xswiftc 5"
before_install:
- |
if [ $(uname) = "Linux" ]; then
export SWIFT_ARCHIVE_URL="https://swift.org/builds/swift-5.0-release/ubuntu1604/swift-5.0-RELEASE/swift-5.0-RELEASE-ubuntu16.04.tar.gz"
curl ${SWIFT_ARCHIVE_URL} -o ../swift.tar.gz
mkdir ../swift
tar xzf ../swift.tar.gz -C ../swift --strip-components 2
export PATH="$(dirname $(pwd))/swift/bin:$PATH"
fi
script:
- swift build ${SWIFT_BUILD_OPTION}
- swift build --configuration release ${SWIFT_BUILD_OPTION}
- swift test ${SWIFT_BUILD_OPTION}

View File

@ -4,16 +4,24 @@ Its prototype is [YOCKOW's Gist](https://gist.github.com/YOCKOW/12d9607cb30f40b7
## Sample Code
### Measure
```Swift
import TimeSpecification
func time(_ body:() -> Void) {
let start = TimeSpecification(clock: .system)
body()
let end = TimeSpecification(clock: .system)
let duration = end - start
print("\(duration)")
}
let duration = TimeSpecification.measure(repeatCount: 100) { doIt() }
print("It took \(duration) seconds.") // -> Processing time to execute `doIt` 100 times.
```
### With `Date`
```Swift
import TimeSpecification
let now = TimeSpecification(clock: .calendar)
let dateNow = Date(timeIntervalSince1970: now) // -> Almost same with Date(timeIntervalSince1970: Double(time(nil)))
```

View File

@ -0,0 +1,45 @@
/* *************************************************************************************************
Date+TimeSpecification.swift
© 2020 YOCKOW.
Licensed under MIT License.
See "LICENSE.txt" for more information.
************************************************************************************************ */
import Foundation
extension TimeSpecification {
@inlinable
public var timeIntervalValue: TimeInterval {
return TimeInterval(self.doubleValue)
}
}
extension Date {
/// Creates a date value initialized relative to the current date and time
/// by a given number of nanoseconds.
@inlinable
public init(timeIntervalSinceNow: TimeSpecification) {
self.init(timeIntervalSinceNow: timeIntervalSinceNow.timeIntervalValue)
}
/// Creates a date value initialized relative to another given date
/// by a given number of nanoseconds.
@inlinable
public init(timeInterval: TimeSpecification, since date: Date) {
self.init(timeInterval: timeInterval.timeIntervalValue, since: date)
}
/// Creates a date value initialized relative to 00:00:00 UTC on 1 January 2001
/// by a given number of nanoseconds.
@inlinable
public init(timeIntervalSinceReferenceDate: TimeSpecification) {
self.init(timeIntervalSinceReferenceDate: timeIntervalSinceReferenceDate.timeIntervalValue)
}
/// Creates a date value initialized relative to 00:00:00 UTC on 1 January 1970
/// by a given number of nanoseconds.
@inlinable
public init(timeIntervalSince1970: TimeSpecification) {
self.init(timeIntervalSince1970: timeIntervalSince1970.timeIntervalValue)
}
}

View File

@ -1,17 +1,17 @@
/***************************************************************************************************
TimeSpecification.swift
© 2016-2019 YOCKOW.
© 2016-2020 YOCKOW.
Licensed under MIT License.
See "LICENSE.txt" for more information.
**************************************************************************************************/
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
#if canImport(Darwin)
import Darwin
private let mach_task_self:() -> mach_port_t = { return mach_task_self_ }
private typealias CTimeSpec = mach_timespec_t
private let mach_task_self:() -> mach_port_t = { mach_task_self_ }
private typealias _CTimeSpec = mach_timespec_t
#else
import Glibc
private typealias CTimeSpec = timespec
private typealias _CTimeSpec = timespec
#endif
import Foundation
@ -19,24 +19,8 @@ import Foundation
/// The representation for the time in nanoseconds.
public struct TimeSpecification {
public var seconds: Int64 = 0
private var _nanoseconds: Int32 = 0
}
extension TimeSpecification {
private mutating func _normalize() {
//`nanoseconds` must be always zero or positive value and less than 1_000_000_000
if self._nanoseconds >= 1_000_000_000 {
self.seconds += Int64(self._nanoseconds / 1_000_000_000)
self._nanoseconds = self._nanoseconds % 1_000_000_000
} else if self._nanoseconds < 0 {
// For example,
// (seconds:3, nanoseconds:-2_123_456_789)
// -> (seconds:0, nanoseconds:876_543_211)
self.seconds += Int64(self._nanoseconds / 1_000_000_000) - 1
self._nanoseconds = self._nanoseconds % 1_000_000_000 + 1_000_000_000
}
}
public var nanoseconds: Int32 {
get {
return self._nanoseconds
@ -46,15 +30,53 @@ extension TimeSpecification {
self._normalize()
}
}
}
extension TimeSpecification {
public init(seconds:Int64, nanoseconds:Int32) {
private mutating func _normalize() {
//`nanoseconds` must be always zero or positive value and less than 1_000_000_000
if self._nanoseconds >= 1_000_000_000 {
let quotRem: div_t = div(self._nanoseconds, 1_000_000_000)
self.seconds += Int64(quotRem.quot)
self._nanoseconds = quotRem.rem
} else if self._nanoseconds < 0 {
// For example,
// (seconds:3, nanoseconds:-2_123_456_789)
// -> (seconds:0, nanoseconds:876_543_211)
let quotRem: div_t = div(self._nanoseconds, 1_000_000_000)
self.seconds += Int64(quotRem.quot) - 1
self._nanoseconds = quotRem.rem + 1_000_000_000
}
}
private init(_noNormalizationRequired time: (seconds: Int64, nanoseconds: Int32)) {
self.seconds = time.seconds
self._nanoseconds = time.nanoseconds
}
public init(seconds: Int64, nanoseconds: Int32) {
self.seconds = seconds
self.nanoseconds = nanoseconds // will be normalized
}
}
extension TimeSpecification: Codable {
public enum CodingKeys: String, CodingKey {
case seconds, nanoseconds
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.seconds, forKey: .seconds)
try container.encode(self.nanoseconds, forKey: .nanoseconds)
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let seconds = try container.decode(Int64.self, forKey: .seconds)
let nanoseconds = try container.decode(Int32.self, forKey: .nanoseconds)
self.init(seconds: seconds, nanoseconds: nanoseconds)
}
}
extension TimeSpecification: Equatable {
public static func ==(lhs:TimeSpecification, rhs:TimeSpecification) -> Bool {
return lhs.seconds == rhs.seconds && lhs.nanoseconds == rhs.nanoseconds
@ -97,16 +119,17 @@ extension TimeSpecification {
public var integerValue: Int { return Int(self.seconds) }
/// Double representation of the time.
@inlinable
public var doubleValue: Double { return Double(self.nanoseconds) * 1.0E-9 + Double(self.seconds) }
}
extension TimeSpecification: CustomStringConvertible, CustomDebugStringConvertible {
public var description: String {
return String(format:"%.09f seconds", self.doubleValue)
return String(format:"\(self.seconds).%09d", self.nanoseconds)
}
public var debugDescription: String {
return self.description
return self.description + " seconds."
}
}
@ -146,24 +169,39 @@ extension TimeSpecification {
///
/// Note: This means `CLOCK_MONOTONIC` on Linux, `SYSTEM_CLOCK` on macOS.
case system
fileprivate var _clockID: CInt {
switch self {
case .calendar:
#if canImport(Darwin)
return CALENDAR_CLOCK
#else
return CLOCK_REALTIME
#endif
case .system:
#if canImport(Darwin)
return SYSTEM_CLOCK
#else
return CLOCK_MONOTONIC
#endif
}
}
}
private init(_ cts:CTimeSpec) {
self.init(seconds:Int64(cts.tv_sec), nanoseconds:Int32(cts.tv_nsec))
private init(_ cts: _CTimeSpec) {
self.init(_noNormalizationRequired: (seconds: Int64(cts.tv_sec),
nanoseconds: Int32(cts.tv_nsec)))
}
/// Initialze with an instance of `Clock`.
public init(clock:Clock) {
var c_timespec:CTimeSpec = CTimeSpec(tv_sec:0, tv_nsec:0)
let clock_id:CInt
public init(clock: Clock) {
var c_timespec: _CTimeSpec = _CTimeSpec(tv_sec:0, tv_nsec:0)
#if os(Linux)
clock_id = (clock == .calendar) ? CLOCK_REALTIME : CLOCK_MONOTONIC
_ = clock_gettime(clock_id, &c_timespec)
_ = clock_gettime(clock._clockID, &c_timespec)
#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
var clock_name: clock_serv_t = 0
clock_id = (clock == .calendar) ? CALENDAR_CLOCK : SYSTEM_CLOCK
_ = host_get_clock_service(mach_host_self(), clock_id, &clock_name)
_ = host_get_clock_service(mach_host_self(), clock._clockID, &clock_name)
_ = clock_get_time(clock_name, &c_timespec)
_ = mach_port_deallocate(mach_task_self(), clock_name)
#endif
@ -171,3 +209,18 @@ extension TimeSpecification {
self.init(c_timespec)
}
}
extension TimeSpecification {
/// Measure a processing time of the closure.
///
/// - parameters:
/// * repeatCount: Indicates the number of times to execute the closure. It must be greater than zero.
/// * body: The target closure.
public static func measure(repeatCount: Int = 1, _ body: () throws -> Void) rethrows -> TimeSpecification {
precondition(repeatCount > 0, "\(#function): `repeatCount` must be greater than zero.")
let start = TimeSpecification(clock: .system)
for _ in 0..<repeatCount { try body() }
let end = TimeSpecification(clock: .system)
return end - start
}
}

View File

@ -1,15 +1,15 @@
/***************************************************************************************************
SwiftTimeSpecificationTests.swift
© 2016-2019 YOCKOW.
© 2016-2020 YOCKOW.
Licensed under MIT License.
See "LICENSE.txt" for more information.
**************************************************************************************************/
// FIXME: These are irresponsible tests below.
import XCTest
@testable import TimeSpecification
import Foundation
class TimeSpecificationTests: XCTestCase {
func test_normalization() {
let N1 = TimeSpecification(seconds:0, nanoseconds:1_234_567_890)
@ -19,6 +19,18 @@ class TimeSpecificationTests: XCTestCase {
XCTAssertTrue(N2.seconds == -3 && N2.nanoseconds == 765_432_110, "Normalization Test 2")
}
func test_codable() throws {
let spec = TimeSpecification(seconds: 123, nanoseconds: 456_789)
let encoded = try JSONEncoder().encode(spec)
let encodedString = try XCTUnwrap(String(data: encoded, encoding: .utf8))
// https://github.com/YOCKOW/SwiftTimeSpecification/runs/703218186?check_suite_focus=true#step:6:18
XCTAssertTrue(encodedString == #"{"seconds":123,"nanoseconds":456789}"# ||
encodedString == #"{"nanoseconds":456789,"seconds":123}"#)
let decoded = try JSONDecoder().decode(TimeSpecification.self, from: encoded)
XCTAssertEqual(decoded, spec)
}
func test_comparison() {
let C1 = TimeSpecification(seconds:100, nanoseconds:100)
let C2 = TimeSpecification(seconds: 98, nanoseconds:2_000_000_100)
@ -47,4 +59,15 @@ class TimeSpecificationTests: XCTestCase {
XCTAssertEqual(L1 + R1, TimeSpecification(seconds:201, nanoseconds:111_111_110), "Sum Test 1")
XCTAssertEqual(L1 - R1, TimeSpecification(seconds:0, nanoseconds:-864_197_532), "Difference Test 1")
}
func test_description() {
let spec = TimeSpecification(seconds: 123, nanoseconds: 456_789)
XCTAssertEqual(spec.description, "123.000456789")
}
func test_date() {
let spec = TimeSpecification(seconds: 100, nanoseconds: 123_456_789)
XCTAssertEqual(Date(timeIntervalSinceReferenceDate: spec),
Date(timeIntervalSinceReferenceDate: 100.123456789))
}
}

View File

@ -6,7 +6,10 @@ extension TimeSpecificationTests {
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__TimeSpecificationTests = [
("test_codable", test_codable),
("test_comparison", test_comparison),
("test_date", test_date),
("test_description", test_description),
("test_floatLiteral", test_floatLiteral),
("test_integerLiteral", test_integerLiteral),
("test_normalization", test_normalization),