commit
900e9028f6
|
@ -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 }}
|
|
@ -1,6 +1,6 @@
|
|||
.DS_Store
|
||||
|
||||
.build/
|
||||
.build
|
||||
build/
|
||||
y-build/
|
||||
|
||||
|
|
28
.travis.yml
28
.travis.yml
|
@ -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}
|
22
README.md
22
README.md
|
@ -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)))
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue