package expvar
import (
"encoding/json"
"internal/godebug"
"log"
"math"
"net/http"
"os"
"runtime"
"slices"
"strconv"
"sync"
"sync/atomic"
"unicode/utf8"
)
type Var interface {
String () string
}
type jsonVar interface {
appendJSON(b []byte ) []byte
}
type Int struct {
i atomic .Int64
}
func (v *Int ) Value () int64 {
return v .i .Load ()
}
func (v *Int ) String () string {
return string (v .appendJSON (nil ))
}
func (v *Int ) appendJSON (b []byte ) []byte {
return strconv .AppendInt (b , v .i .Load (), 10 )
}
func (v *Int ) Add (delta int64 ) {
v .i .Add (delta )
}
func (v *Int ) Set (value int64 ) {
v .i .Store (value )
}
type Float struct {
f atomic .Uint64
}
func (v *Float ) Value () float64 {
return math .Float64frombits (v .f .Load ())
}
func (v *Float ) String () string {
return string (v .appendJSON (nil ))
}
func (v *Float ) appendJSON (b []byte ) []byte {
return strconv .AppendFloat (b , math .Float64frombits (v .f .Load ()), 'g' , -1 , 64 )
}
func (v *Float ) Add (delta float64 ) {
for {
cur := v .f .Load ()
curVal := math .Float64frombits (cur )
nxtVal := curVal + delta
nxt := math .Float64bits (nxtVal )
if v .f .CompareAndSwap (cur , nxt ) {
return
}
}
}
func (v *Float ) Set (value float64 ) {
v .f .Store (math .Float64bits (value ))
}
type Map struct {
m sync .Map
keysMu sync .RWMutex
keys []string
}
type KeyValue struct {
Key string
Value Var
}
func (v *Map ) String () string {
return string (v .appendJSON (nil ))
}
func (v *Map ) appendJSON (b []byte ) []byte {
return v .appendJSONMayExpand (b , false )
}
func (v *Map ) appendJSONMayExpand (b []byte , expand bool ) []byte {
afterCommaDelim := byte (' ' )
mayAppendNewline := func (b []byte ) []byte { return b }
if expand {
afterCommaDelim = '\n'
mayAppendNewline = func (b []byte ) []byte { return append (b , '\n' ) }
}
b = append (b , '{' )
b = mayAppendNewline (b )
first := true
v .Do (func (kv KeyValue ) {
if !first {
b = append (b , ',' , afterCommaDelim )
}
first = false
b = appendJSONQuote (b , kv .Key )
b = append (b , ':' , ' ' )
switch v := kv .Value .(type ) {
case nil :
b = append (b , "null" ...)
case jsonVar :
b = v .appendJSON (b )
default :
b = append (b , v .String ()...)
}
})
b = mayAppendNewline (b )
b = append (b , '}' )
b = mayAppendNewline (b )
return b
}
func (v *Map ) Init () *Map {
v .keysMu .Lock ()
defer v .keysMu .Unlock ()
v .keys = v .keys [:0 ]
v .m .Clear ()
return v
}
func (v *Map ) addKey (key string ) {
v .keysMu .Lock ()
defer v .keysMu .Unlock ()
i , found := slices .BinarySearch (v .keys , key )
if found {
return
}
v .keys = slices .Insert (v .keys , i , key )
}
func (v *Map ) Get (key string ) Var {
i , _ := v .m .Load (key )
av , _ := i .(Var )
return av
}
func (v *Map ) Set (key string , av Var ) {
if _ , ok := v .m .Load (key ); !ok {
if _ , dup := v .m .LoadOrStore (key , av ); !dup {
v .addKey (key )
return
}
}
v .m .Store (key , av )
}
func (v *Map ) Add (key string , delta int64 ) {
i , ok := v .m .Load (key )
if !ok {
var dup bool
i , dup = v .m .LoadOrStore (key , new (Int ))
if !dup {
v .addKey (key )
}
}
if iv , ok := i .(*Int ); ok {
iv .Add (delta )
}
}
func (v *Map ) AddFloat (key string , delta float64 ) {
i , ok := v .m .Load (key )
if !ok {
var dup bool
i , dup = v .m .LoadOrStore (key , new (Float ))
if !dup {
v .addKey (key )
}
}
if iv , ok := i .(*Float ); ok {
iv .Add (delta )
}
}
func (v *Map ) Delete (key string ) {
v .keysMu .Lock ()
defer v .keysMu .Unlock ()
i , found := slices .BinarySearch (v .keys , key )
if found {
v .keys = slices .Delete (v .keys , i , i +1 )
v .m .Delete (key )
}
}
func (v *Map ) Do (f func (KeyValue )) {
v .keysMu .RLock ()
defer v .keysMu .RUnlock ()
for _ , k := range v .keys {
i , _ := v .m .Load (k )
val , _ := i .(Var )
f (KeyValue {k , val })
}
}
type String struct {
s atomic .Value
}
func (v *String ) Value () string {
p , _ := v .s .Load ().(string )
return p
}
func (v *String ) String () string {
return string (v .appendJSON (nil ))
}
func (v *String ) appendJSON (b []byte ) []byte {
return appendJSONQuote (b , v .Value ())
}
func (v *String ) Set (value string ) {
v .s .Store (value )
}
type Func func () any
func (f Func ) Value () any {
return f ()
}
func (f Func ) String () string {
v , _ := json .Marshal (f ())
return string (v )
}
var vars Map
func Publish (name string , v Var ) {
if _ , dup := vars .m .LoadOrStore (name , v ); dup {
log .Panicln ("Reuse of exported var name:" , name )
}
vars .keysMu .Lock ()
defer vars .keysMu .Unlock ()
vars .keys = append (vars .keys , name )
slices .Sort (vars .keys )
}
func Get (name string ) Var {
return vars .Get (name )
}
func NewInt (name string ) *Int {
v := new (Int )
Publish (name , v )
return v
}
func NewFloat (name string ) *Float {
v := new (Float )
Publish (name , v )
return v
}
func NewMap (name string ) *Map {
v := new (Map ).Init ()
Publish (name , v )
return v
}
func NewString (name string ) *String {
v := new (String )
Publish (name , v )
return v
}
func Do (f func (KeyValue )) {
vars .Do (f )
}
func expvarHandler(w http .ResponseWriter , r *http .Request ) {
w .Header ().Set ("Content-Type" , "application/json; charset=utf-8" )
w .Write (vars .appendJSONMayExpand (nil , true ))
}
func Handler () http .Handler {
return http .HandlerFunc (expvarHandler )
}
func cmdline() any {
return os .Args
}
func memstats() any {
stats := new (runtime .MemStats )
runtime .ReadMemStats (stats )
return *stats
}
func init() {
if godebug .New ("httpmuxgo121" ).Value () == "1" {
http .HandleFunc ("/debug/vars" , expvarHandler )
} else {
http .HandleFunc ("GET /debug/vars" , expvarHandler )
}
Publish ("cmdline" , Func (cmdline ))
Publish ("memstats" , Func (memstats ))
}
func appendJSONQuote(b []byte , s string ) []byte {
const hex = "0123456789abcdef"
b = append (b , '"' )
for _ , r := range s {
switch {
case r < ' ' || r == '\\' || r == '"' || r == '<' || r == '>' || r == '&' || r == '\u2028' || r == '\u2029' :
switch r {
case '\\' , '"' :
b = append (b , '\\' , byte (r ))
case '\n' :
b = append (b , '\\' , 'n' )
case '\r' :
b = append (b , '\\' , 'r' )
case '\t' :
b = append (b , '\\' , 't' )
default :
b = append (b , '\\' , 'u' , hex [(r >>12 )&0xf ], hex [(r >>8 )&0xf ], hex [(r >>4 )&0xf ], hex [(r >>0 )&0xf ])
}
case r < utf8 .RuneSelf :
b = append (b , byte (r ))
default :
b = utf8 .AppendRune (b , r )
}
}
b = append (b , '"' )
return b
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .