Skip to content

Commit

Permalink
Merge pull request #4 from YOCKOW/development
Browse files Browse the repository at this point in the history
v3.2.0
  • Loading branch information
YOCKOW authored May 24, 2020
2 parents 28d94af + 6513d36 commit 900e902
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 74 deletions.
60 changes: 60 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.DS_Store

.build/
.build
build/
y-build/

Expand Down
28 changes: 0 additions & 28 deletions .travis.yml

This file was deleted.

22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@ Its prototype is [YOCKOW's Gist](https://gist.github.com/YOCKOW/12d9607cb30f40b7


## Sample Code

### Measure

```Swift
import TimeSpecification

let duration = TimeSpecification.measure(repeatCount: 100) { doIt() }
print("It took \(duration) seconds.") // -> Processing time to execute `doIt` 100 times.

```

### With `Date`

```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 now = TimeSpecification(clock: .calendar)
let dateNow = Date(timeIntervalSince1970: now) // -> Almost same with Date(timeIntervalSince1970: Double(time(nil)))
```


Expand Down
45 changes: 45 additions & 0 deletions Sources/TimeSpecification/Date+TimeSpecification.swift
Original file line number Diff line number Diff line change
@@ -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)
}
}
123 changes: 88 additions & 35 deletions Sources/TimeSpecification/TimeSpecification.swift
Original file line number Diff line number Diff line change
@@ -1,60 +1,82 @@
/***************************************************************************************************
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

/// The representation for the time in nanoseconds.
public struct TimeSpecification {
public var seconds: Int64 = 0

private var _nanoseconds: Int32 = 0
}

extension TimeSpecification {
public var nanoseconds: Int32 {
get {
return self._nanoseconds
}
set {
self._nanoseconds = newValue
self._normalize()
}
}

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
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)
self.seconds += Int64(self._nanoseconds / 1_000_000_000) - 1
self._nanoseconds = self._nanoseconds % 1_000_000_000 + 1_000_000_000
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
}
}

public var nanoseconds: Int32 {
get {
return self._nanoseconds
}
set {
self._nanoseconds = newValue
self._normalize()
}
private init(_noNormalizationRequired time: (seconds: Int64, nanoseconds: Int32)) {
self.seconds = time.seconds
self._nanoseconds = time.nanoseconds
}
}

extension TimeSpecification {
public init(seconds:Int64, nanoseconds:Int32) {

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
Expand Down Expand Up @@ -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."
}
}

Expand Down Expand Up @@ -146,28 +169,58 @@ 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

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
}
}
Loading

0 comments on commit 900e902

Please sign in to comment.