Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/development/std/time/duration.nct

duration.nct

//! Duration representation, normalization, and checked arithmetic.

see ./index.nct

const NANOSECONDS_PER_SECOND: u64 = 1000000000
const MILLISECONDS_PER_SECOND: u64 = 1000
const MICROSECONDS_PER_SECOND: u64 = 1000000
const NANOSECONDS_PER_MILLISECOND: u64 = 1000000
const NANOSECONDS_PER_MICROSECOND: u64 = 1000
const MAX_SECONDS: u64 = 18446744073709551615

copy struct Duration {
    seconds: u64
    nanoseconds: u64
}

noalloc func normalized_duration(seconds: u64, nanoseconds: u64): Duration {
    return Duration { seconds: seconds, nanoseconds: nanoseconds }
}

noalloc func duration_from_counter_delta(delta: u64, frequency: u64): Duration {
    let seconds = delta / frequency
    let remainder = delta % frequency
    // ARM64 CNTFRQ_EL0 is a non-zero 32-bit frequency. Splitting whole seconds first makes this
    // product fit in u64 while preserving floor rounding.
    let nanoseconds = (remainder * NANOSECONDS_PER_SECOND) / frequency
    return normalized_duration(seconds, nanoseconds)
}

construct Duration {
    noalloc func from_seconds(value: u64): Self {
        return normalized_duration(value, 0)
    }

    noalloc func from_milliseconds(value: u64): Self {
        return normalized_duration(
            value / MILLISECONDS_PER_SECOND,
            (value % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND,
        )
    }

    noalloc func from_microseconds(value: u64): Self {
        return normalized_duration(
            value / MICROSECONDS_PER_SECOND,
            (value % MICROSECONDS_PER_SECOND) * NANOSECONDS_PER_MICROSECOND,
        )
    }

    noalloc func from_nanoseconds(value: u64): Self {
        return normalized_duration(
            value / NANOSECONDS_PER_SECOND,
            value % NANOSECONDS_PER_SECOND,
        )
    }
}

instance Duration {
    noalloc method &self.whole_seconds(): u64 {
        return self.seconds
    }

    noalloc method &self.subsecond_nanoseconds(): u64 {
        return self.nanoseconds
    }

    noalloc method &self.is_zero(): bool {
        return self.seconds == 0 && self.nanoseconds == 0
    }

    noalloc method &self.checked_add(other: &Self): Self? {
        let nanosecond_sum = self.nanoseconds + other.nanoseconds
        let carry: u64 = if nanosecond_sum >= NANOSECONDS_PER_SECOND { 1 } else { 0 }
        let nanoseconds = if carry == 1 {
            nanosecond_sum - NANOSECONDS_PER_SECOND
        } else {
            nanosecond_sum
        }
        if other.seconds > MAX_SECONDS - self.seconds { return none }
        var seconds = self.seconds + other.seconds
        if carry == 1 {
            if seconds == MAX_SECONDS { return none }
            seconds += 1
        }
        return normalized_duration(seconds, nanoseconds)
    }

    noalloc method &self.checked_sub(other: &Self): Self? {
        if self < other { return none }
        if self.nanoseconds >= other.nanoseconds {
            return normalized_duration(
                self.seconds - other.seconds,
                self.nanoseconds - other.nanoseconds,
            )
        }
        return normalized_duration(
            self.seconds - other.seconds - 1,
            NANOSECONDS_PER_SECOND + self.nanoseconds - other.nanoseconds,
        )
    }

    noalloc operator (&self == other: &Self): bool {
        return self.seconds == other.seconds && self.nanoseconds == other.nanoseconds
    }

    noalloc operator (&self < other: &Self): bool {
        if self.seconds < other.seconds { return true }
        if other.seconds < self.seconds { return false }
        return self.nanoseconds < other.nanoseconds
    }
}