#!/bin/bash
# core/timer - Duration Measuring Support

set -e

lib_load 'sys/tool/bc'


######
# Timer Value

# timer_now() - Prints the current time with nanoseconds.
timer_now() { date '+%s.%N'; }


######
# Timer Variables

# timer_var_running() - Prints name of variable that stores a boolean
# value indicating the running state of the given timer ($1).
timer_var_running() { echo "$1"; }

# timer_var_start() - Prints name of variable that stores the starting
# time of the given timer ($1).
timer_var_start() { echo "${1}_start"; }

# timer_var_final() - Prints name of variable that stores the final
# time of the given timer ($1).
timer_var_final() { echo "${1}_final"; }

# timer_var_marks() - Prints name of an array variable that stores
# marks for the given timer ($1).
timer_var_marks() { echo "${1}_marks"; }


######
# Timer Mark Interface
#
# A `timer mark` is defined as the time an event occurred while the
# timer is running.  This interface does not enforce any policy
# regarding the frequency, number, values, or ordering of marks.
# They simply must be valid time values formatted in the same manner as
# output by ``timer_now()``.  Their meaning is left to the user.

# timer_mark() - Saves a new mark ($2) for the given timer ($1).
# $1 - Name of timer.
# $2 - Mark time.  If `null`, obtains the current `Timer Value`_.
timer_mark() {
	min_max_args 1 2 "$@"
	local var when
	var=$(timer_var_marks "$1")
	when=${2:-$(timer_now)}
	[ "$when" ] || error "timer $1: unable to set mark"
	eval "$var+=( $when )"
}

# timer_mark_all() - Prints the list of marks for the given timer ($1).
# $1 - Name of timer.
timer_mark_all() {
	has_args 1 "$@"
	local marksvar=$(timer_var_marks "$1")
	eval "echo \"\${${marksvar}[*]}\""
}

# timer_mark_count() - Prints the number of marks for the given timer ($1).
# $1 - Name of timer.
timer_mark_count() {
	has_args 1 "$@"
	local marksvar
	marksvar=$(timer_var_marks "$1")
	eval "echo \$((\${#$marksvar[*]}))"
}

# timer_mark_first() - Prints the first mark for the given timer ($1).
# $1 - Name of timer.
timer_mark_first() {
	has_args 1 "$@"
	local marksvar
	marksvar=$(timer_var_marks "$1")
	eval "echo \"\$$marksvar\""
}

# timer_mark_get() - Prints the mark at the given index ($2) for the
# given timer ($1).
# $1 - Name of timer.
# $2 - Index into mark array.
timer_mark_get() {
	has_args 2 "$@"
	local marksvar
	marksvar=$(timer_var_marks "$1")
	eval "echo \"\${${marksvar}[$2]}\""
}

# timer_mark_measure() - Computes and prints the difference between
# two marks ($2 and $3) for the given timer ($1).
# $1 - Name of timer.
# $2 - First index into timer mark array.
# $3 - Second index into timer mark array.
timer_mark_measure() {
	has_args 3 "$@"
	local name=$1
	local idxstart=$2
	local idxfinal=$3

	local count
	count=$(timer_mark_count "$name")
	if ! [ $idxstart -lt $count -a $idxfinal -lt $count ]; then
		local msg="cannot measure from $idxstart to $idxfinal"
		error "timer $name: $msg with $count marks"
	fi

	local start final elapsed
	start=$(timer_mark_get $name $idxstart)
	final=$(timer_mark_get $name $idxfinal)
	bc_gen_sub $final $start | bc_run
}


######
# Timer Lap Interface
#
# A `timer lap` is defined as the elapsed interval between two succesive
# timer marks.  Thus, `n` saved marks will calculate `n - 1` lap intervals.

# timer_laps() - Prints the number of extant laps for the given timer ($1).
# $1 - Name of timer.
timer_laps() {
	has_args 1 "$@"
	local count
	count=$(timer_mark_count "$1")
	if [ $count -gt 1 ]; then
		echo $((count - 1))
	else
		echo 0
	fi
}

# timer_has_laps() - Checks the number of extant laps for the given timer ($1).
# $1 - Name of timer.
# Returns: Succcess if the timer has recorded at least one lap.
timer_has_laps() {
	has_args 1 "$@"
	local laps=$(timer_laps "$1")
	[ $laps -gt 0 ]
}

# timer_lap_measure() - Prints the elapsed time between two marks ($2
# and $3) for the given timer ($1).
# $1 - Name of timer.
# $2 - First index into timer mark array.
# $3 - Second index into timer mark array.
timer_lap_measure() {
	has_args 3 "$@"
	local name=$1
	if ! timer_has_laps "$name"; then
		warn "timer: $name: no laps recorded"
		return
	fi
	timer_mark_measure "$2" "$3"
}

# timer_lap_measure() - Prints the last lap interval for the given timer ($1).
# $1 - Name of timer
timer_lap_last() {
	has_args 1 "$@"
	timer_lap_measure "$1" -2 -1
}


######
# Timer Query

# timer_is_exists() - Checks to see if the given timer ($1) exists.
# $1 - Name of timer
# Returns: Success if the timer exists.
timer_exists() {
	local -n running=$(timer_var_running "$1")
	[ "$running" ]
}

# timer_is_running() - Checks to see if the given timer ($1) is running.
# $1 - Name of timer
# Returns: Success if the timer is running.
timer_is_running() {
	has_args 1 "$@"
	local -n running=$(timer_var_running "$1")
	[ "$running" ] && $running
}

# timer_running() - Sets the running state ($2)  for the given timer ($1).
# $1 - Name of timer
# $2 - New running state (``true`` or ``false``)
timer_running() {
	local -n running=$(timer_var_running "$1")
	running=$2
}


######
# Timer Control

# timer_start() - Stops the given timer ($1)
# $1 - Name of timer
timer_start() {
	has_args 1 "$@"
	local name=$1

	if timer_is_running "$name"; then
		warn "$name: timer already running"
		return
	fi

	timer_running true

	local -n start=$(timer_var_start "$name")
	start=$(timer_now)

	timer_mark $name $start
}

# timer_stop() - Stops the given timer ($1)
# $1 - Name of timer
timer_stop() {
	has_args 1 "$@"
	local name=$1

	local -n final=$(timer_var_final "$name")
	final=$(timer_now)

	timer_mark $name $final
	timer_running false
}


######
# Timer Reporting

# timer_elapsed() - Prints a human readable version of the elapsed time
# for the given timer ($1).
# $1 - Name of timer
timer_elapsed() {
	local elapsed
	elapsed=$(timer_elapsed_seconds "$@")
	local sec=${elapsed%.*}
	local nsec=${elapsed#*.}

	local hrs=$((sec / 3600))
	[ $hrs -gt 0 ] && echo "$hrs hours" || hrs=''

	secs=$((secs % 3600))

	local min=$((sec / 60))
	[ "$hrs" -o $min -gt 0 ] && echo "${hrs:+, }$min minutes" || min=''

	sec=$((sec % 60))

	echo "${min:+, }$sec.$nsec seconds"
}

# timer_elapsed_seconds() - Prints the total number of elapsed seconds
# with nanosecond resolution.
# $1 - Name of timer
timer_elapsed_seconds() {
	has_args 1 "$@"
	echo "$(timer_mark_measure "$1" 0 -1)"
}

View the Developer Guide Index

View the Reference Manual Index


Generated on Thu May 4 18:59:30 PDT 2017 by mcsh i7 v0.19.0.