mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 07:06:54 +08:00
unit: make printing of units concurrency-safe and unicode-aware
This commit is contained in:
@@ -103,8 +103,8 @@ func TestFormat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGoStringFormat(t *testing.T) {
|
func TestGoStringFormat(t *testing.T) {
|
||||||
expect1 := `&unit.Unit{dimensions:unit.Dimensions{4:2, 7:-1}, formatted:"", value:6.62606957e-34}`
|
expect1 := `&unit.Unit{dimensions:unit.Dimensions{4:2, 7:-1}, value:6.62606957e-34}`
|
||||||
expect2 := `&unit.Unit{dimensions:unit.Dimensions{7:-1, 4:2}, formatted:"", value:6.62606957e-34}`
|
expect2 := `&unit.Unit{dimensions:unit.Dimensions{7:-1, 4:2}, value:6.62606957e-34}`
|
||||||
if r := fmt.Sprintf("%#v", New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1})); r != expect1 && r != expect2 {
|
if r := fmt.Sprintf("%#v", New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1})); r != expect1 && r != expect2 {
|
||||||
t.Errorf("Format %q: got: %q expected: %q", "%#v", r, expect1)
|
t.Errorf("Format %q: got: %q expected: %q", "%#v", r, expect1)
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Uniter is a type that can be converted to a Unit.
|
// Uniter is a type that can be converted to a Unit.
|
||||||
@@ -20,16 +22,48 @@ type Uniter interface {
|
|||||||
// function, typically within an init function.
|
// function, typically within an init function.
|
||||||
type Dimension int
|
type Dimension int
|
||||||
|
|
||||||
|
// NewDimension creates a new orthogonal dimension with the given symbol, and
|
||||||
|
// returns the value of that dimension. The input symbol must not overlap with
|
||||||
|
// any of the any of the SI base units or other symbols of common use in SI ("kg",
|
||||||
|
// "J", etc.), and must not overlap with any other dimensions created by calls
|
||||||
|
// to NewDimension. The SymbolExists function can check if the symbol exists.
|
||||||
|
// NewDimension will panic if the input symbol matches an existing symbol.
|
||||||
|
//
|
||||||
|
// NewDimension should only be called for unit types that are actually orthogonal
|
||||||
|
// to the base dimensions defined in this package. See the package-level
|
||||||
|
// documentation for further explanation.
|
||||||
|
func NewDimension(symbol string) Dimension {
|
||||||
|
defer mu.Unlock()
|
||||||
|
mu.Lock()
|
||||||
|
_, ok := dimensions[symbol]
|
||||||
|
if ok {
|
||||||
|
panic("unit: dimension string \"" + symbol + "\" already used")
|
||||||
|
}
|
||||||
|
d := Dimension(len(symbols))
|
||||||
|
symbols = append(symbols, symbol)
|
||||||
|
dimensions[symbol] = d
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the string for the dimension.
|
// String returns the string for the dimension.
|
||||||
func (d Dimension) String() string {
|
func (d Dimension) String() string {
|
||||||
switch {
|
if d == reserved {
|
||||||
case d == reserved:
|
|
||||||
return "reserved"
|
return "reserved"
|
||||||
case d < Dimension(len(symbols)):
|
|
||||||
return symbols[d]
|
|
||||||
default:
|
|
||||||
panic("unit: illegal dimension")
|
|
||||||
}
|
}
|
||||||
|
defer mu.RUnlock()
|
||||||
|
mu.RLock()
|
||||||
|
if int(d) < len(symbols) {
|
||||||
|
return symbols[d]
|
||||||
|
}
|
||||||
|
panic("unit: illegal dimension")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SymbolExists returns whether the given symbol is already in use.
|
||||||
|
func SymbolExists(symbol string) bool {
|
||||||
|
mu.RLock()
|
||||||
|
_, ok := dimensions[symbol]
|
||||||
|
mu.RUnlock()
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -47,6 +81,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// mu protects symbols and dimensions for concurrent use.
|
||||||
|
mu sync.RWMutex
|
||||||
symbols = []string{
|
symbols = []string{
|
||||||
CurrentDim: "A",
|
CurrentDim: "A",
|
||||||
LengthDim: "m",
|
LengthDim: "m",
|
||||||
@@ -178,33 +214,6 @@ func (u unitPrinters) Swap(i, j int) {
|
|||||||
u[i], u[j] = u[j], u[i]
|
u[i], u[j] = u[j], u[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDimension creates a new orthogonal dimension with the given symbol, and
|
|
||||||
// returns the value of that dimension. The input symbol must not overlap with
|
|
||||||
// any of the any of the SI base units or other symbols of common use in SI ("kg",
|
|
||||||
// "J", etc.), and must not overlap with any other dimensions created by calls
|
|
||||||
// to NewDimension. The SymbolExists function can check if the symbol exists.
|
|
||||||
// NewDimension will panic if the input symbol matches an existing symbol.
|
|
||||||
//
|
|
||||||
// NewDimension should only be called for unit types that are actually orthogonal
|
|
||||||
// to the base dimensions defined in this package. See the package-level
|
|
||||||
// documentation for further explanation.
|
|
||||||
func NewDimension(symbol string) Dimension {
|
|
||||||
_, ok := dimensions[symbol]
|
|
||||||
if ok {
|
|
||||||
panic("unit: dimension string \"" + symbol + "\" already used")
|
|
||||||
}
|
|
||||||
d := Dimension(len(symbols))
|
|
||||||
symbols = append(symbols, symbol)
|
|
||||||
dimensions[symbol] = d
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// SymoblExists returns whether the given symbol is already in use.
|
|
||||||
func SymbolExists(symbol string) bool {
|
|
||||||
_, ok := dimensions[symbol]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unit represents a dimensional value. The dimensions will typically be in SI
|
// Unit represents a dimensional value. The dimensions will typically be in SI
|
||||||
// units, but can also include dimensions created with NewDimension. The Unit type
|
// units, but can also include dimensions created with NewDimension. The Unit type
|
||||||
// is most useful for ensuring dimensional consistency when manipulating types
|
// is most useful for ensuring dimensional consistency when manipulating types
|
||||||
@@ -212,7 +221,6 @@ func SymbolExists(symbol string) bool {
|
|||||||
// mass to get a force. See the package documentation for further explanation.
|
// mass to get a force. See the package documentation for further explanation.
|
||||||
type Unit struct {
|
type Unit struct {
|
||||||
dimensions Dimensions
|
dimensions Dimensions
|
||||||
formatted string
|
|
||||||
value float64
|
value float64
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +282,6 @@ func (u *Unit) Mul(uniter Uniter) *Unit {
|
|||||||
u.dimensions[key] = d + val
|
u.dimensions[key] = d + val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u.formatted = ""
|
|
||||||
u.value *= a.value
|
u.value *= a.value
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
@@ -291,7 +298,6 @@ func (u *Unit) Div(uniter Uniter) *Unit {
|
|||||||
u.dimensions[key] = d - val
|
u.dimensions[key] = d - val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u.formatted = ""
|
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,20 +328,18 @@ func (u *Unit) Format(fs fmt.State, c rune) {
|
|||||||
case 'e', 'E', 'f', 'F', 'g', 'G':
|
case 'e', 'E', 'f', 'F', 'g', 'G':
|
||||||
p, pOk := fs.Precision()
|
p, pOk := fs.Precision()
|
||||||
w, wOk := fs.Width()
|
w, wOk := fs.Width()
|
||||||
if u.formatted == "" && len(u.dimensions) > 0 {
|
units := u.dimensions.String()
|
||||||
u.formatted = u.dimensions.String()
|
|
||||||
}
|
|
||||||
switch {
|
switch {
|
||||||
case pOk && wOk:
|
case pOk && wOk:
|
||||||
fmt.Fprintf(fs, "%*.*"+string(c), pos(w-len(u.formatted)-1), p, u.value)
|
fmt.Fprintf(fs, "%*.*"+string(c), pos(w-utf8.RuneCount([]byte(units))-1), p, u.value)
|
||||||
case pOk:
|
case pOk:
|
||||||
fmt.Fprintf(fs, "%.*"+string(c), p, u.value)
|
fmt.Fprintf(fs, "%.*"+string(c), p, u.value)
|
||||||
case wOk:
|
case wOk:
|
||||||
fmt.Fprintf(fs, "%*"+string(c), pos(w-len(u.formatted)-1), u.value)
|
fmt.Fprintf(fs, "%*"+string(c), pos(w-utf8.RuneCount([]byte(units))-1), u.value)
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(fs, "%"+string(c), u.value)
|
fmt.Fprintf(fs, "%"+string(c), u.value)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(fs, " %s", u.formatted)
|
fmt.Fprintf(fs, " %s", units)
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(fs, "%%!%c(*Unit=%g)", c, u)
|
fmt.Fprintf(fs, "%%!%c(*Unit=%g)", c, u)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user