go mod vendor
This commit is contained in:
parent
1ac41e91c1
commit
9d45604f27
|
@ -0,0 +1,213 @@
|
|||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
||||
package codec
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NoopHandle returns a no-op handle. It basically does nothing.
|
||||
// It is only useful for benchmarking, as it gives an idea of the
|
||||
// overhead from the codec framework.
|
||||
//
|
||||
// LIBRARY USERS: *** DO NOT USE ***
|
||||
func NoopHandle(slen int) *noopHandle {
|
||||
h := noopHandle{}
|
||||
h.rand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
h.B = make([][]byte, slen)
|
||||
h.S = make([]string, slen)
|
||||
for i := 0; i < len(h.S); i++ {
|
||||
b := make([]byte, i+1)
|
||||
for j := 0; j < len(b); j++ {
|
||||
b[j] = 'a' + byte(i)
|
||||
}
|
||||
h.B[i] = b
|
||||
h.S[i] = string(b)
|
||||
}
|
||||
return &h
|
||||
}
|
||||
|
||||
// noopHandle does nothing.
|
||||
// It is used to simulate the overhead of the codec framework.
|
||||
type noopHandle struct {
|
||||
BasicHandle
|
||||
binaryEncodingType
|
||||
noopDrv // noopDrv is unexported here, so we can get a copy of it when needed.
|
||||
}
|
||||
|
||||
type noopDrv struct {
|
||||
d *Decoder
|
||||
e *Encoder
|
||||
i int
|
||||
S []string
|
||||
B [][]byte
|
||||
mks []bool // stack. if map (true), else if array (false)
|
||||
mk bool // top of stack. what container are we on? map or array?
|
||||
ct valueType // last response for IsContainerType.
|
||||
cb int // counter for ContainerType
|
||||
rand *rand.Rand
|
||||
}
|
||||
|
||||
func (h *noopDrv) r(v int) int { return h.rand.Intn(v) }
|
||||
func (h *noopDrv) m(v int) int { h.i++; return h.i % v }
|
||||
|
||||
func (h *noopDrv) newEncDriver(e *Encoder) encDriver { h.e = e; return h }
|
||||
func (h *noopDrv) newDecDriver(d *Decoder) decDriver { h.d = d; return h }
|
||||
|
||||
func (h *noopDrv) reset() {}
|
||||
func (h *noopDrv) uncacheRead() {}
|
||||
|
||||
// --- encDriver
|
||||
|
||||
// stack functions (for map and array)
|
||||
func (h *noopDrv) start(b bool) {
|
||||
// println("start", len(h.mks)+1)
|
||||
h.mks = append(h.mks, b)
|
||||
h.mk = b
|
||||
}
|
||||
func (h *noopDrv) end() {
|
||||
// println("end: ", len(h.mks)-1)
|
||||
h.mks = h.mks[:len(h.mks)-1]
|
||||
if len(h.mks) > 0 {
|
||||
h.mk = h.mks[len(h.mks)-1]
|
||||
} else {
|
||||
h.mk = false
|
||||
}
|
||||
}
|
||||
|
||||
func (h *noopDrv) EncodeBuiltin(rt uintptr, v interface{}) {}
|
||||
func (h *noopDrv) EncodeNil() {}
|
||||
func (h *noopDrv) EncodeInt(i int64) {}
|
||||
func (h *noopDrv) EncodeUint(i uint64) {}
|
||||
func (h *noopDrv) EncodeBool(b bool) {}
|
||||
func (h *noopDrv) EncodeFloat32(f float32) {}
|
||||
func (h *noopDrv) EncodeFloat64(f float64) {}
|
||||
func (h *noopDrv) EncodeRawExt(re *RawExt, e *Encoder) {}
|
||||
func (h *noopDrv) EncodeArrayStart(length int) { h.start(true) }
|
||||
func (h *noopDrv) EncodeMapStart(length int) { h.start(false) }
|
||||
func (h *noopDrv) EncodeEnd() { h.end() }
|
||||
|
||||
func (h *noopDrv) EncodeString(c charEncoding, v string) {}
|
||||
func (h *noopDrv) EncodeSymbol(v string) {}
|
||||
func (h *noopDrv) EncodeStringBytes(c charEncoding, v []byte) {}
|
||||
|
||||
func (h *noopDrv) EncodeExt(rv interface{}, xtag uint64, ext Ext, e *Encoder) {}
|
||||
|
||||
// ---- decDriver
|
||||
func (h *noopDrv) initReadNext() {}
|
||||
func (h *noopDrv) CheckBreak() bool { return false }
|
||||
func (h *noopDrv) IsBuiltinType(rt uintptr) bool { return false }
|
||||
func (h *noopDrv) DecodeBuiltin(rt uintptr, v interface{}) {}
|
||||
func (h *noopDrv) DecodeInt(bitsize uint8) (i int64) { return int64(h.m(15)) }
|
||||
func (h *noopDrv) DecodeUint(bitsize uint8) (ui uint64) { return uint64(h.m(35)) }
|
||||
func (h *noopDrv) DecodeFloat(chkOverflow32 bool) (f float64) { return float64(h.m(95)) }
|
||||
func (h *noopDrv) DecodeBool() (b bool) { return h.m(2) == 0 }
|
||||
func (h *noopDrv) DecodeString() (s string) { return h.S[h.m(8)] }
|
||||
|
||||
// func (h *noopDrv) DecodeStringAsBytes(bs []byte) []byte { return h.DecodeBytes(bs) }
|
||||
|
||||
func (h *noopDrv) DecodeBytes(bs []byte, isstring, zerocopy bool) []byte { return h.B[h.m(len(h.B))] }
|
||||
|
||||
func (h *noopDrv) ReadEnd() { h.end() }
|
||||
|
||||
// toggle map/slice
|
||||
func (h *noopDrv) ReadMapStart() int { h.start(true); return h.m(10) }
|
||||
func (h *noopDrv) ReadArrayStart() int { h.start(false); return h.m(10) }
|
||||
|
||||
func (h *noopDrv) ContainerType() (vt valueType) {
|
||||
// return h.m(2) == 0
|
||||
// handle kStruct, which will bomb is it calls this and doesn't get back a map or array.
|
||||
// consequently, if the return value is not map or array, reset it to one of them based on h.m(7) % 2
|
||||
// for kstruct: at least one out of every 2 times, return one of valueTypeMap or Array (else kstruct bombs)
|
||||
// however, every 10th time it is called, we just return something else.
|
||||
var vals = [...]valueType{valueTypeArray, valueTypeMap}
|
||||
// ------------ TAKE ------------
|
||||
// if h.cb%2 == 0 {
|
||||
// if h.ct == valueTypeMap || h.ct == valueTypeArray {
|
||||
// } else {
|
||||
// h.ct = vals[h.m(2)]
|
||||
// }
|
||||
// } else if h.cb%5 == 0 {
|
||||
// h.ct = valueType(h.m(8))
|
||||
// } else {
|
||||
// h.ct = vals[h.m(2)]
|
||||
// }
|
||||
// ------------ TAKE ------------
|
||||
// if h.cb%16 == 0 {
|
||||
// h.ct = valueType(h.cb % 8)
|
||||
// } else {
|
||||
// h.ct = vals[h.cb%2]
|
||||
// }
|
||||
h.ct = vals[h.cb%2]
|
||||
h.cb++
|
||||
return h.ct
|
||||
|
||||
// if h.ct == valueTypeNil || h.ct == valueTypeString || h.ct == valueTypeBytes {
|
||||
// return h.ct
|
||||
// }
|
||||
// return valueTypeUnset
|
||||
// TODO: may need to tweak this so it works.
|
||||
// if h.ct == valueTypeMap && vt == valueTypeArray || h.ct == valueTypeArray && vt == valueTypeMap {
|
||||
// h.cb = !h.cb
|
||||
// h.ct = vt
|
||||
// return h.cb
|
||||
// }
|
||||
// // go in a loop and check it.
|
||||
// h.ct = vt
|
||||
// h.cb = h.m(7) == 0
|
||||
// return h.cb
|
||||
}
|
||||
func (h *noopDrv) TryDecodeAsNil() bool {
|
||||
if h.mk {
|
||||
return false
|
||||
} else {
|
||||
return h.m(8) == 0
|
||||
}
|
||||
}
|
||||
func (h *noopDrv) DecodeExt(rv interface{}, xtag uint64, ext Ext) uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (h *noopDrv) DecodeNaked() {
|
||||
// use h.r (random) not h.m() because h.m() could cause the same value to be given.
|
||||
var sk int
|
||||
if h.mk {
|
||||
// if mapkey, do not support values of nil OR bytes, array, map or rawext
|
||||
sk = h.r(7) + 1
|
||||
} else {
|
||||
sk = h.r(12)
|
||||
}
|
||||
n := &h.d.n
|
||||
switch sk {
|
||||
case 0:
|
||||
n.v = valueTypeNil
|
||||
case 1:
|
||||
n.v, n.b = valueTypeBool, false
|
||||
case 2:
|
||||
n.v, n.b = valueTypeBool, true
|
||||
case 3:
|
||||
n.v, n.i = valueTypeInt, h.DecodeInt(64)
|
||||
case 4:
|
||||
n.v, n.u = valueTypeUint, h.DecodeUint(64)
|
||||
case 5:
|
||||
n.v, n.f = valueTypeFloat, h.DecodeFloat(true)
|
||||
case 6:
|
||||
n.v, n.f = valueTypeFloat, h.DecodeFloat(false)
|
||||
case 7:
|
||||
n.v, n.s = valueTypeString, h.DecodeString()
|
||||
case 8:
|
||||
n.v, n.l = valueTypeBytes, h.B[h.m(len(h.B))]
|
||||
case 9:
|
||||
n.v = valueTypeArray
|
||||
case 10:
|
||||
n.v = valueTypeMap
|
||||
default:
|
||||
n.v = valueTypeExt
|
||||
n.u = h.DecodeUint(64)
|
||||
n.l = h.B[h.m(len(h.B))]
|
||||
}
|
||||
h.ct = n.v
|
||||
return
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package codec
|
||||
|
||||
//go:generate bash prebuild.sh
|
|
@ -0,0 +1,199 @@
|
|||
#!/bin/bash
|
||||
|
||||
# _needgen is a helper function to tell if we need to generate files for msgp, codecgen.
|
||||
_needgen() {
|
||||
local a="$1"
|
||||
zneedgen=0
|
||||
if [[ ! -e "$a" ]]
|
||||
then
|
||||
zneedgen=1
|
||||
echo 1
|
||||
return 0
|
||||
fi
|
||||
for i in `ls -1 *.go.tmpl gen.go values_test.go`
|
||||
do
|
||||
if [[ "$a" -ot "$i" ]]
|
||||
then
|
||||
zneedgen=1
|
||||
echo 1
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo 0
|
||||
}
|
||||
|
||||
# _build generates fast-path.go and gen-helper.go.
|
||||
#
|
||||
# It is needed because there is some dependency between the generated code
|
||||
# and the other classes. Consequently, we have to totally remove the
|
||||
# generated files and put stubs in place, before calling "go run" again
|
||||
# to recreate them.
|
||||
_build() {
|
||||
if ! [[ "${zforce}" == "1" ||
|
||||
"1" == $( _needgen "fast-path.generated.go" ) ||
|
||||
"1" == $( _needgen "gen-helper.generated.go" ) ||
|
||||
"1" == $( _needgen "gen.generated.go" ) ||
|
||||
1 == 0 ]]
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# echo "Running prebuild"
|
||||
if [ "${zbak}" == "1" ]
|
||||
then
|
||||
# echo "Backing up old generated files"
|
||||
_zts=`date '+%m%d%Y_%H%M%S'`
|
||||
_gg=".generated.go"
|
||||
[ -e "gen-helper${_gg}" ] && mv gen-helper${_gg} gen-helper${_gg}__${_zts}.bak
|
||||
[ -e "fast-path${_gg}" ] && mv fast-path${_gg} fast-path${_gg}__${_zts}.bak
|
||||
# [ -e "safe${_gg}" ] && mv safe${_gg} safe${_gg}__${_zts}.bak
|
||||
# [ -e "unsafe${_gg}" ] && mv unsafe${_gg} unsafe${_gg}__${_zts}.bak
|
||||
else
|
||||
rm -f fast-path.generated.go gen.generated.go gen-helper.generated.go \
|
||||
*safe.generated.go *_generated_test.go *.generated_ffjson_expose.go
|
||||
fi
|
||||
|
||||
cat > gen.generated.go <<EOF
|
||||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
||||
package codec
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS AUTO-GENERATED FROM gen-dec-(map|array).go.tmpl
|
||||
|
||||
const genDecMapTmpl = \`
|
||||
EOF
|
||||
|
||||
cat >> gen.generated.go < gen-dec-map.go.tmpl
|
||||
|
||||
cat >> gen.generated.go <<EOF
|
||||
\`
|
||||
|
||||
const genDecListTmpl = \`
|
||||
EOF
|
||||
|
||||
cat >> gen.generated.go < gen-dec-array.go.tmpl
|
||||
|
||||
cat >> gen.generated.go <<EOF
|
||||
\`
|
||||
|
||||
EOF
|
||||
|
||||
cat > gen-from-tmpl.codec.generated.go <<EOF
|
||||
package codec
|
||||
import "io"
|
||||
func GenInternalGoFile(r io.Reader, w io.Writer, safe bool) error {
|
||||
return genInternalGoFile(r, w, safe)
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > gen-from-tmpl.generated.go <<EOF
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
//import "flag"
|
||||
import "ugorji.net/codec"
|
||||
import "os"
|
||||
|
||||
func run(fnameIn, fnameOut string, safe bool) {
|
||||
fin, err := os.Open(fnameIn)
|
||||
if err != nil { panic(err) }
|
||||
defer fin.Close()
|
||||
fout, err := os.Create(fnameOut)
|
||||
if err != nil { panic(err) }
|
||||
defer fout.Close()
|
||||
err = codec.GenInternalGoFile(fin, fout, safe)
|
||||
if err != nil { panic(err) }
|
||||
}
|
||||
|
||||
func main() {
|
||||
// do not make safe/unsafe variants.
|
||||
// Instead, depend on escape analysis, and place string creation and usage appropriately.
|
||||
// run("unsafe.go.tmpl", "safe.generated.go", true)
|
||||
// run("unsafe.go.tmpl", "unsafe.generated.go", false)
|
||||
run("fast-path.go.tmpl", "fast-path.generated.go", false)
|
||||
run("gen-helper.go.tmpl", "gen-helper.generated.go", false)
|
||||
}
|
||||
|
||||
EOF
|
||||
go run -tags=notfastpath gen-from-tmpl.generated.go && \
|
||||
rm -f gen-from-tmpl.*generated.go
|
||||
}
|
||||
|
||||
_codegenerators() {
|
||||
if [[ $zforce == "1" ||
|
||||
"1" == $( _needgen "values_codecgen${zsfx}" ) ||
|
||||
"1" == $( _needgen "values_msgp${zsfx}" ) ||
|
||||
"1" == $( _needgen "values_ffjson${zsfx}" ) ||
|
||||
1 == 0 ]]
|
||||
then
|
||||
# codecgen creates some temporary files in the directory (main, pkg).
|
||||
# Consequently, we should start msgp and ffjson first, and also put a small time latency before
|
||||
# starting codecgen.
|
||||
# Without this, ffjson chokes on one of the temporary files from codecgen.
|
||||
if [[ $zexternal == "1" ]]
|
||||
then
|
||||
echo "ffjson ... " && \
|
||||
ffjson -w values_ffjson${zsfx} $zfin &
|
||||
zzzIdFF=$!
|
||||
echo "msgp ... " && \
|
||||
msgp -tests=false -o=values_msgp${zsfx} -file=$zfin &
|
||||
zzzIdMsgp=$!
|
||||
|
||||
sleep 1 # give ffjson and msgp some buffer time. see note above.
|
||||
fi
|
||||
|
||||
echo "codecgen - !unsafe ... " && \
|
||||
codecgen -rt codecgen -t 'x,codecgen,!unsafe' -o values_codecgen${zsfx} -d 19780 $zfin &
|
||||
zzzIdC=$!
|
||||
echo "codecgen - unsafe ... " && \
|
||||
codecgen -u -rt codecgen -t 'x,codecgen,unsafe' -o values_codecgen_unsafe${zsfx} -d 19781 $zfin &
|
||||
zzzIdCU=$!
|
||||
wait $zzzIdC $zzzIdCU $zzzIdMsgp $zzzIdFF && \
|
||||
# remove (M|Unm)arshalJSON implementations, so they don't conflict with encoding/json bench \
|
||||
if [[ $zexternal == "1" ]]
|
||||
then
|
||||
sed -i 's+ MarshalJSON(+ _MarshalJSON(+g' values_ffjson${zsfx} && \
|
||||
sed -i 's+ UnmarshalJSON(+ _UnmarshalJSON(+g' values_ffjson${zsfx}
|
||||
fi && \
|
||||
echo "generators done!" && \
|
||||
true
|
||||
fi
|
||||
}
|
||||
|
||||
# _init reads the arguments and sets up the flags
|
||||
_init() {
|
||||
OPTIND=1
|
||||
while getopts "fbx" flag
|
||||
do
|
||||
case "x$flag" in
|
||||
'xf') zforce=1;;
|
||||
'xb') zbak=1;;
|
||||
'xx') zexternal=1;;
|
||||
*) echo "prebuild.sh accepts [-fbx] only"; return 1;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
OPTIND=1
|
||||
}
|
||||
|
||||
# main script.
|
||||
# First ensure that this is being run from the basedir (i.e. dirname of script is .)
|
||||
if [ "." = `dirname $0` ]
|
||||
then
|
||||
zmydir=`pwd`
|
||||
zfin="test_values.generated.go"
|
||||
zsfx="_generated_test.go"
|
||||
# rm -f *_generated_test.go
|
||||
rm -f codecgen-*.go && \
|
||||
_init "$@" && \
|
||||
_build && \
|
||||
cp $zmydir/values_test.go $zmydir/$zfin && \
|
||||
_codegenerators && \
|
||||
echo prebuild done successfully
|
||||
rm -f $zmydir/$zfin
|
||||
else
|
||||
echo "Script must be run from the directory it resides in"
|
||||
fi
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Run all the different permutations of all the tests.
|
||||
# This helps ensure that nothing gets broken.
|
||||
|
||||
_run() {
|
||||
# 1. VARIATIONS: regular (t), canonical (c), IO R/W (i),
|
||||
# binc-nosymbols (n), struct2array (s), intern string (e),
|
||||
# json-indent (d), circular (l)
|
||||
# 2. MODE: reflection (r), external (x), codecgen (g), unsafe (u), notfastpath (f)
|
||||
# 3. OPTIONS: verbose (v), reset (z), must (m),
|
||||
#
|
||||
# Use combinations of mode to get exactly what you want,
|
||||
# and then pass the variations you need.
|
||||
|
||||
ztags=""
|
||||
zargs=""
|
||||
local OPTIND
|
||||
OPTIND=1
|
||||
while getopts "_xurtcinsvgzmefdl" flag
|
||||
do
|
||||
case "x$flag" in
|
||||
'xr') ;;
|
||||
'xf') ztags="$ztags notfastpath" ;;
|
||||
'xg') ztags="$ztags codecgen" ;;
|
||||
'xx') ztags="$ztags x" ;;
|
||||
'xu') ztags="$ztags unsafe" ;;
|
||||
'xv') zargs="$zargs -tv" ;;
|
||||
'xz') zargs="$zargs -tr" ;;
|
||||
'xm') zargs="$zargs -tm" ;;
|
||||
'xl') zargs="$zargs -tl" ;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
# shift $((OPTIND-1))
|
||||
printf '............. TAGS: %s .............\n' "$ztags"
|
||||
# echo ">>>>>>> TAGS: $ztags"
|
||||
|
||||
OPTIND=1
|
||||
while getopts "_xurtcinsvgzmefdl" flag
|
||||
do
|
||||
case "x$flag" in
|
||||
'xt') printf ">>>>>>> REGULAR : "; go test "-tags=$ztags" $zargs ; sleep 2 ;;
|
||||
'xc') printf ">>>>>>> CANONICAL : "; go test "-tags=$ztags" $zargs -tc; sleep 2 ;;
|
||||
'xi') printf ">>>>>>> I/O : "; go test "-tags=$ztags" $zargs -ti; sleep 2 ;;
|
||||
'xn') printf ">>>>>>> NO_SYMBOLS : "; go test "-tags=$ztags" -run=Binc $zargs -tn; sleep 2 ;;
|
||||
'xs') printf ">>>>>>> TO_ARRAY : "; go test "-tags=$ztags" $zargs -ts; sleep 2 ;;
|
||||
'xe') printf ">>>>>>> INTERN : "; go test "-tags=$ztags" $zargs -te; sleep 2 ;;
|
||||
'xd') printf ">>>>>>> INDENT : ";
|
||||
go test "-tags=$ztags" -run=JsonCodecsTable -td=-1 $zargs;
|
||||
go test "-tags=$ztags" -run=JsonCodecsTable -td=8 $zargs;
|
||||
sleep 2 ;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
OPTIND=1
|
||||
}
|
||||
|
||||
# echo ">>>>>>> RUNNING VARIATIONS OF TESTS"
|
||||
if [[ "x$@" = "x" ]]; then
|
||||
# All: r, x, g, gu
|
||||
_run "-_tcinsed_ml" # regular
|
||||
_run "-_tcinsed_ml_z" # regular with reset
|
||||
_run "-_tcinsed_ml_f" # regular with no fastpath (notfastpath)
|
||||
_run "-x_tcinsed_ml" # external
|
||||
_run "-gx_tcinsed_ml" # codecgen: requires external
|
||||
_run "-gxu_tcinsed_ml" # codecgen + unsafe
|
||||
elif [[ "x$@" = "x-Z" ]]; then
|
||||
# Regular
|
||||
_run "-_tcinsed_ml" # regular
|
||||
_run "-_tcinsed_ml_z" # regular with reset
|
||||
elif [[ "x$@" = "x-F" ]]; then
|
||||
# regular with notfastpath
|
||||
_run "-_tcinsed_ml_f" # regular
|
||||
_run "-_tcinsed_ml_zf" # regular with reset
|
||||
else
|
||||
_run "$@"
|
||||
fi
|
|
@ -0,0 +1,233 @@
|
|||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
||||
package codec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
|
||||
timeExtEncFn = func(rv reflect.Value) (bs []byte, err error) {
|
||||
defer panicToErr(&err)
|
||||
bs = timeExt{}.WriteExt(rv.Interface())
|
||||
return
|
||||
}
|
||||
timeExtDecFn = func(rv reflect.Value, bs []byte) (err error) {
|
||||
defer panicToErr(&err)
|
||||
timeExt{}.ReadExt(rv.Interface(), bs)
|
||||
return
|
||||
}
|
||||
)
|
||||
|
||||
type timeExt struct{}
|
||||
|
||||
func (x timeExt) WriteExt(v interface{}) (bs []byte) {
|
||||
switch v2 := v.(type) {
|
||||
case time.Time:
|
||||
bs = encodeTime(v2)
|
||||
case *time.Time:
|
||||
bs = encodeTime(*v2)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported format for time conversion: expecting time.Time; got %T", v2))
|
||||
}
|
||||
return
|
||||
}
|
||||
func (x timeExt) ReadExt(v interface{}, bs []byte) {
|
||||
tt, err := decodeTime(bs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*(v.(*time.Time)) = tt
|
||||
}
|
||||
|
||||
func (x timeExt) ConvertExt(v interface{}) interface{} {
|
||||
return x.WriteExt(v)
|
||||
}
|
||||
func (x timeExt) UpdateExt(v interface{}, src interface{}) {
|
||||
x.ReadExt(v, src.([]byte))
|
||||
}
|
||||
|
||||
// EncodeTime encodes a time.Time as a []byte, including
|
||||
// information on the instant in time and UTC offset.
|
||||
//
|
||||
// Format Description
|
||||
//
|
||||
// A timestamp is composed of 3 components:
|
||||
//
|
||||
// - secs: signed integer representing seconds since unix epoch
|
||||
// - nsces: unsigned integer representing fractional seconds as a
|
||||
// nanosecond offset within secs, in the range 0 <= nsecs < 1e9
|
||||
// - tz: signed integer representing timezone offset in minutes east of UTC,
|
||||
// and a dst (daylight savings time) flag
|
||||
//
|
||||
// When encoding a timestamp, the first byte is the descriptor, which
|
||||
// defines which components are encoded and how many bytes are used to
|
||||
// encode secs and nsecs components. *If secs/nsecs is 0 or tz is UTC, it
|
||||
// is not encoded in the byte array explicitly*.
|
||||
//
|
||||
// Descriptor 8 bits are of the form `A B C DDD EE`:
|
||||
// A: Is secs component encoded? 1 = true
|
||||
// B: Is nsecs component encoded? 1 = true
|
||||
// C: Is tz component encoded? 1 = true
|
||||
// DDD: Number of extra bytes for secs (range 0-7).
|
||||
// If A = 1, secs encoded in DDD+1 bytes.
|
||||
// If A = 0, secs is not encoded, and is assumed to be 0.
|
||||
// If A = 1, then we need at least 1 byte to encode secs.
|
||||
// DDD says the number of extra bytes beyond that 1.
|
||||
// E.g. if DDD=0, then secs is represented in 1 byte.
|
||||
// if DDD=2, then secs is represented in 3 bytes.
|
||||
// EE: Number of extra bytes for nsecs (range 0-3).
|
||||
// If B = 1, nsecs encoded in EE+1 bytes (similar to secs/DDD above)
|
||||
//
|
||||
// Following the descriptor bytes, subsequent bytes are:
|
||||
//
|
||||
// secs component encoded in `DDD + 1` bytes (if A == 1)
|
||||
// nsecs component encoded in `EE + 1` bytes (if B == 1)
|
||||
// tz component encoded in 2 bytes (if C == 1)
|
||||
//
|
||||
// secs and nsecs components are integers encoded in a BigEndian
|
||||
// 2-complement encoding format.
|
||||
//
|
||||
// tz component is encoded as 2 bytes (16 bits). Most significant bit 15 to
|
||||
// Least significant bit 0 are described below:
|
||||
//
|
||||
// Timezone offset has a range of -12:00 to +14:00 (ie -720 to +840 minutes).
|
||||
// Bit 15 = have\_dst: set to 1 if we set the dst flag.
|
||||
// Bit 14 = dst\_on: set to 1 if dst is in effect at the time, or 0 if not.
|
||||
// Bits 13..0 = timezone offset in minutes. It is a signed integer in Big Endian format.
|
||||
//
|
||||
func encodeTime(t time.Time) []byte {
|
||||
//t := rv.Interface().(time.Time)
|
||||
tsecs, tnsecs := t.Unix(), t.Nanosecond()
|
||||
var (
|
||||
bd byte
|
||||
btmp [8]byte
|
||||
bs [16]byte
|
||||
i int = 1
|
||||
)
|
||||
l := t.Location()
|
||||
if l == time.UTC {
|
||||
l = nil
|
||||
}
|
||||
if tsecs != 0 {
|
||||
bd = bd | 0x80
|
||||
bigen.PutUint64(btmp[:], uint64(tsecs))
|
||||
f := pruneSignExt(btmp[:], tsecs >= 0)
|
||||
bd = bd | (byte(7-f) << 2)
|
||||
copy(bs[i:], btmp[f:])
|
||||
i = i + (8 - f)
|
||||
}
|
||||
if tnsecs != 0 {
|
||||
bd = bd | 0x40
|
||||
bigen.PutUint32(btmp[:4], uint32(tnsecs))
|
||||
f := pruneSignExt(btmp[:4], true)
|
||||
bd = bd | byte(3-f)
|
||||
copy(bs[i:], btmp[f:4])
|
||||
i = i + (4 - f)
|
||||
}
|
||||
if l != nil {
|
||||
bd = bd | 0x20
|
||||
// Note that Go Libs do not give access to dst flag.
|
||||
_, zoneOffset := t.Zone()
|
||||
//zoneName, zoneOffset := t.Zone()
|
||||
zoneOffset /= 60
|
||||
z := uint16(zoneOffset)
|
||||
bigen.PutUint16(btmp[:2], z)
|
||||
// clear dst flags
|
||||
bs[i] = btmp[0] & 0x3f
|
||||
bs[i+1] = btmp[1]
|
||||
i = i + 2
|
||||
}
|
||||
bs[0] = bd
|
||||
return bs[0:i]
|
||||
}
|
||||
|
||||
// DecodeTime decodes a []byte into a time.Time.
|
||||
func decodeTime(bs []byte) (tt time.Time, err error) {
|
||||
bd := bs[0]
|
||||
var (
|
||||
tsec int64
|
||||
tnsec uint32
|
||||
tz uint16
|
||||
i byte = 1
|
||||
i2 byte
|
||||
n byte
|
||||
)
|
||||
if bd&(1<<7) != 0 {
|
||||
var btmp [8]byte
|
||||
n = ((bd >> 2) & 0x7) + 1
|
||||
i2 = i + n
|
||||
copy(btmp[8-n:], bs[i:i2])
|
||||
//if first bit of bs[i] is set, then fill btmp[0..8-n] with 0xff (ie sign extend it)
|
||||
if bs[i]&(1<<7) != 0 {
|
||||
copy(btmp[0:8-n], bsAll0xff)
|
||||
//for j,k := byte(0), 8-n; j < k; j++ { btmp[j] = 0xff }
|
||||
}
|
||||
i = i2
|
||||
tsec = int64(bigen.Uint64(btmp[:]))
|
||||
}
|
||||
if bd&(1<<6) != 0 {
|
||||
var btmp [4]byte
|
||||
n = (bd & 0x3) + 1
|
||||
i2 = i + n
|
||||
copy(btmp[4-n:], bs[i:i2])
|
||||
i = i2
|
||||
tnsec = bigen.Uint32(btmp[:])
|
||||
}
|
||||
if bd&(1<<5) == 0 {
|
||||
tt = time.Unix(tsec, int64(tnsec)).UTC()
|
||||
return
|
||||
}
|
||||
// In stdlib time.Parse, when a date is parsed without a zone name, it uses "" as zone name.
|
||||
// However, we need name here, so it can be shown when time is printed.
|
||||
// Zone name is in form: UTC-08:00.
|
||||
// Note that Go Libs do not give access to dst flag, so we ignore dst bits
|
||||
|
||||
i2 = i + 2
|
||||
tz = bigen.Uint16(bs[i:i2])
|
||||
i = i2
|
||||
// sign extend sign bit into top 2 MSB (which were dst bits):
|
||||
if tz&(1<<13) == 0 { // positive
|
||||
tz = tz & 0x3fff //clear 2 MSBs: dst bits
|
||||
} else { // negative
|
||||
tz = tz | 0xc000 //set 2 MSBs: dst bits
|
||||
//tzname[3] = '-' (TODO: verify. this works here)
|
||||
}
|
||||
tzint := int16(tz)
|
||||
if tzint == 0 {
|
||||
tt = time.Unix(tsec, int64(tnsec)).UTC()
|
||||
} else {
|
||||
// For Go Time, do not use a descriptive timezone.
|
||||
// It's unnecessary, and makes it harder to do a reflect.DeepEqual.
|
||||
// The Offset already tells what the offset should be, if not on UTC and unknown zone name.
|
||||
// var zoneName = timeLocUTCName(tzint)
|
||||
tt = time.Unix(tsec, int64(tnsec)).In(time.FixedZone("", int(tzint)*60))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func timeLocUTCName(tzint int16) string {
|
||||
if tzint == 0 {
|
||||
return "UTC"
|
||||
}
|
||||
var tzname = []byte("UTC+00:00")
|
||||
//tzname := fmt.Sprintf("UTC%s%02d:%02d", tzsign, tz/60, tz%60) //perf issue using Sprintf. inline below.
|
||||
//tzhr, tzmin := tz/60, tz%60 //faster if u convert to int first
|
||||
var tzhr, tzmin int16
|
||||
if tzint < 0 {
|
||||
tzname[3] = '-' // (TODO: verify. this works here)
|
||||
tzhr, tzmin = -tzint/60, (-tzint)%60
|
||||
} else {
|
||||
tzhr, tzmin = tzint/60, tzint%60
|
||||
}
|
||||
tzname[4] = timeDigits[tzhr/10]
|
||||
tzname[5] = timeDigits[tzhr%10]
|
||||
tzname[7] = timeDigits[tzmin/10]
|
||||
tzname[8] = timeDigits[tzmin%10]
|
||||
return string(tzname)
|
||||
//return time.FixedZone(string(tzname), int(tzint)*60)
|
||||
}
|
Loading…
Reference in New Issue