wangchuanjin преди 5 години
родител
ревизия
0b2aa120da

+ 6 - 4
src/github.com/gocql/gocql/go.mod

@@ -1,7 +1,9 @@
-module "github.com/gocql/gocql"
+module github.com/gocql/gocql
+
+go 1.14
 
 require (
-	"github.com/golang/snappy" v0.0.0-20170215233205-553a64147049
-	"github.com/hailocab/go-hostpool" v0.0.0-20160125115350-e80d13ce29ed
-	"gopkg.in/inf.v0" v1.9.1-gopkgin-v0.9.1
+	github.com/golang/snappy v0.0.0-20170215233205-553a64147049
+	github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed
+	gopkg.in/inf.v0 v0.9.1
 )

+ 28 - 0
src/gopkg.in/inf.v0/LICENSE

@@ -0,0 +1,28 @@
+Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go
+Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 210 - 0
src/gopkg.in/inf.v0/benchmark_test.go

@@ -0,0 +1,210 @@
+package inf
+
+import (
+	"fmt"
+	"math/big"
+	"math/rand"
+	"sync"
+	"testing"
+)
+
+const maxcap = 1024 * 1024
+const bits = 256
+const maxscale = 32
+
+var once sync.Once
+
+var decInput [][2]Dec
+var intInput [][2]big.Int
+
+var initBench = func() {
+	decInput = make([][2]Dec, maxcap)
+	intInput = make([][2]big.Int, maxcap)
+	max := new(big.Int).Lsh(big.NewInt(1), bits)
+	r := rand.New(rand.NewSource(0))
+	for i := 0; i < cap(decInput); i++ {
+		decInput[i][0].SetUnscaledBig(new(big.Int).Rand(r, max)).
+			SetScale(Scale(r.Int31n(int32(2*maxscale-1)) - int32(maxscale)))
+		decInput[i][1].SetUnscaledBig(new(big.Int).Rand(r, max)).
+			SetScale(Scale(r.Int31n(int32(2*maxscale-1)) - int32(maxscale)))
+	}
+	for i := 0; i < cap(intInput); i++ {
+		intInput[i][0].Rand(r, max)
+		intInput[i][1].Rand(r, max)
+	}
+}
+
+func doBenchmarkDec1(b *testing.B, f func(z *Dec)) {
+	once.Do(initBench)
+	b.ResetTimer()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		f(&decInput[i%maxcap][0])
+	}
+}
+
+func doBenchmarkDec2(b *testing.B, f func(x, y *Dec)) {
+	once.Do(initBench)
+	b.ResetTimer()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		f(&decInput[i%maxcap][0], &decInput[i%maxcap][1])
+	}
+}
+
+func doBenchmarkInt1(b *testing.B, f func(z *big.Int)) {
+	once.Do(initBench)
+	b.ResetTimer()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		f(&intInput[i%maxcap][0])
+	}
+}
+
+func doBenchmarkInt2(b *testing.B, f func(x, y *big.Int)) {
+	once.Do(initBench)
+	b.ResetTimer()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		f(&intInput[i%maxcap][0], &intInput[i%maxcap][1])
+	}
+}
+
+func Benchmark_Dec_String(b *testing.B) {
+	doBenchmarkDec1(b, func(x *Dec) {
+		x.String()
+	})
+}
+
+func Benchmark_Dec_StringScan(b *testing.B) {
+	doBenchmarkDec1(b, func(x *Dec) {
+		s := x.String()
+		d := new(Dec)
+		fmt.Sscan(s, d)
+	})
+}
+
+func Benchmark_Dec_GobEncode(b *testing.B) {
+	doBenchmarkDec1(b, func(x *Dec) {
+		x.GobEncode()
+	})
+}
+
+func Benchmark_Dec_GobEnDecode(b *testing.B) {
+	doBenchmarkDec1(b, func(x *Dec) {
+		g, _ := x.GobEncode()
+		new(Dec).GobDecode(g)
+	})
+}
+
+func Benchmark_Dec_Add(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		ys := y.Scale()
+		y.SetScale(x.Scale())
+		_ = new(Dec).Add(x, y)
+		y.SetScale(ys)
+	})
+}
+
+func Benchmark_Dec_AddMixed(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).Add(x, y)
+	})
+}
+
+func Benchmark_Dec_Sub(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		ys := y.Scale()
+		y.SetScale(x.Scale())
+		_ = new(Dec).Sub(x, y)
+		y.SetScale(ys)
+	})
+}
+
+func Benchmark_Dec_SubMixed(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).Sub(x, y)
+	})
+}
+
+func Benchmark_Dec_Mul(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).Mul(x, y)
+	})
+}
+
+func Benchmark_Dec_Mul_QuoExact(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		v := new(Dec).Mul(x, y)
+		_ = new(Dec).QuoExact(v, y)
+	})
+}
+
+func Benchmark_Dec_QuoRound_Fixed_Down(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).QuoRound(x, y, 0, RoundDown)
+	})
+}
+
+func Benchmark_Dec_QuoRound_Fixed_HalfUp(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).QuoRound(x, y, 0, RoundHalfUp)
+	})
+}
+
+func Benchmark_Int_String(b *testing.B) {
+	doBenchmarkInt1(b, func(x *big.Int) {
+		x.String()
+	})
+}
+
+func Benchmark_Int_StringScan(b *testing.B) {
+	doBenchmarkInt1(b, func(x *big.Int) {
+		s := x.String()
+		d := new(big.Int)
+		fmt.Sscan(s, d)
+	})
+}
+
+func Benchmark_Int_GobEncode(b *testing.B) {
+	doBenchmarkInt1(b, func(x *big.Int) {
+		x.GobEncode()
+	})
+}
+
+func Benchmark_Int_GobEnDecode(b *testing.B) {
+	doBenchmarkInt1(b, func(x *big.Int) {
+		g, _ := x.GobEncode()
+		new(big.Int).GobDecode(g)
+	})
+}
+
+func Benchmark_Int_Add(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_ = new(big.Int).Add(x, y)
+	})
+}
+
+func Benchmark_Int_Sub(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_ = new(big.Int).Sub(x, y)
+	})
+}
+
+func Benchmark_Int_Mul(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_ = new(big.Int).Mul(x, y)
+	})
+}
+
+func Benchmark_Int_Quo(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_ = new(big.Int).Quo(x, y)
+	})
+}
+
+func Benchmark_Int_QuoRem(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_, _ = new(big.Int).QuoRem(x, y, new(big.Int))
+	})
+}

+ 615 - 0
src/gopkg.in/inf.v0/dec.go

@@ -0,0 +1,615 @@
+// Package inf (type inf.Dec) implements "infinite-precision" decimal
+// arithmetic.
+// "Infinite precision" describes two characteristics: practically unlimited
+// precision for decimal number representation and no support for calculating
+// with any specific fixed precision.
+// (Although there is no practical limit on precision, inf.Dec can only
+// represent finite decimals.)
+//
+// This package is currently in experimental stage and the API may change.
+//
+// This package does NOT support:
+//  - rounding to specific precisions (as opposed to specific decimal positions)
+//  - the notion of context (each rounding must be explicit)
+//  - NaN and Inf values, and distinguishing between positive and negative zero
+//  - conversions to and from float32/64 types
+//
+// Features considered for possible addition:
+//  + formatting options
+//  + Exp method
+//  + combined operations such as AddRound/MulAdd etc
+//  + exchanging data in decimal32/64/128 formats
+//
+package inf // import "gopkg.in/inf.v0"
+
+// TODO:
+//  - avoid excessive deep copying (quo and rounders)
+
+import (
+	"fmt"
+	"io"
+	"math/big"
+	"strings"
+)
+
+// A Dec represents a signed arbitrary-precision decimal.
+// It is a combination of a sign, an arbitrary-precision integer coefficient
+// value, and a signed fixed-precision exponent value.
+// The sign and the coefficient value are handled together as a signed value
+// and referred to as the unscaled value.
+// (Positive and negative zero values are not distinguished.)
+// Since the exponent is most commonly non-positive, it is handled in negated
+// form and referred to as scale.
+//
+// The mathematical value of a Dec equals:
+//
+//  unscaled * 10**(-scale)
+//
+// Note that different Dec representations may have equal mathematical values.
+//
+//  unscaled  scale  String()
+//  -------------------------
+//         0      0    "0"
+//         0      2    "0.00"
+//         0     -2    "0"
+//         1      0    "1"
+//       100      2    "1.00"
+//        10      0   "10"
+//         1     -1   "10"
+//
+// The zero value for a Dec represents the value 0 with scale 0.
+//
+// Operations are typically performed through the *Dec type.
+// The semantics of the assignment operation "=" for "bare" Dec values is
+// undefined and should not be relied on.
+//
+// Methods are typically of the form:
+//
+//	func (z *Dec) Op(x, y *Dec) *Dec
+//
+// and implement operations z = x Op y with the result as receiver; if it
+// is one of the operands it may be overwritten (and its memory reused).
+// To enable chaining of operations, the result is also returned. Methods
+// returning a result other than *Dec take one of the operands as the receiver.
+//
+// A "bare" Quo method (quotient / division operation) is not provided, as the
+// result is not always a finite decimal and thus in general cannot be
+// represented as a Dec.
+// Instead, in the common case when rounding is (potentially) necessary,
+// QuoRound should be used with a Scale and a Rounder.
+// QuoExact or QuoRound with RoundExact can be used in the special cases when it
+// is known that the result is always a finite decimal.
+//
+type Dec struct {
+	unscaled big.Int
+	scale    Scale
+}
+
+// Scale represents the type used for the scale of a Dec.
+type Scale int32
+
+const scaleSize = 4 // bytes in a Scale value
+
+// Scaler represents a method for obtaining the scale to use for the result of
+// an operation on x and y.
+type scaler interface {
+	Scale(x *Dec, y *Dec) Scale
+}
+
+var bigInt = [...]*big.Int{
+	big.NewInt(0), big.NewInt(1), big.NewInt(2), big.NewInt(3), big.NewInt(4),
+	big.NewInt(5), big.NewInt(6), big.NewInt(7), big.NewInt(8), big.NewInt(9),
+	big.NewInt(10),
+}
+
+var exp10cache [64]big.Int = func() [64]big.Int {
+	e10, e10i := [64]big.Int{}, bigInt[1]
+	for i, _ := range e10 {
+		e10[i].Set(e10i)
+		e10i = new(big.Int).Mul(e10i, bigInt[10])
+	}
+	return e10
+}()
+
+// NewDec allocates and returns a new Dec set to the given int64 unscaled value
+// and scale.
+func NewDec(unscaled int64, scale Scale) *Dec {
+	return new(Dec).SetUnscaled(unscaled).SetScale(scale)
+}
+
+// NewDecBig allocates and returns a new Dec set to the given *big.Int unscaled
+// value and scale.
+func NewDecBig(unscaled *big.Int, scale Scale) *Dec {
+	return new(Dec).SetUnscaledBig(unscaled).SetScale(scale)
+}
+
+// Scale returns the scale of x.
+func (x *Dec) Scale() Scale {
+	return x.scale
+}
+
+// Unscaled returns the unscaled value of x for u and true for ok when the
+// unscaled value can be represented as int64; otherwise it returns an undefined
+// int64 value for u and false for ok. Use x.UnscaledBig().Int64() to avoid
+// checking the validity of the value when the check is known to be redundant.
+func (x *Dec) Unscaled() (u int64, ok bool) {
+	u = x.unscaled.Int64()
+	var i big.Int
+	ok = i.SetInt64(u).Cmp(&x.unscaled) == 0
+	return
+}
+
+// UnscaledBig returns the unscaled value of x as *big.Int.
+func (x *Dec) UnscaledBig() *big.Int {
+	return &x.unscaled
+}
+
+// SetScale sets the scale of z, with the unscaled value unchanged, and returns
+// z.
+// The mathematical value of the Dec changes as if it was multiplied by
+// 10**(oldscale-scale).
+func (z *Dec) SetScale(scale Scale) *Dec {
+	z.scale = scale
+	return z
+}
+
+// SetUnscaled sets the unscaled value of z, with the scale unchanged, and
+// returns z.
+func (z *Dec) SetUnscaled(unscaled int64) *Dec {
+	z.unscaled.SetInt64(unscaled)
+	return z
+}
+
+// SetUnscaledBig sets the unscaled value of z, with the scale unchanged, and
+// returns z.
+func (z *Dec) SetUnscaledBig(unscaled *big.Int) *Dec {
+	z.unscaled.Set(unscaled)
+	return z
+}
+
+// Set sets z to the value of x and returns z.
+// It does nothing if z == x.
+func (z *Dec) Set(x *Dec) *Dec {
+	if z != x {
+		z.SetUnscaledBig(x.UnscaledBig())
+		z.SetScale(x.Scale())
+	}
+	return z
+}
+
+// Sign returns:
+//
+//	-1 if x <  0
+//	 0 if x == 0
+//	+1 if x >  0
+//
+func (x *Dec) Sign() int {
+	return x.UnscaledBig().Sign()
+}
+
+// Neg sets z to -x and returns z.
+func (z *Dec) Neg(x *Dec) *Dec {
+	z.SetScale(x.Scale())
+	z.UnscaledBig().Neg(x.UnscaledBig())
+	return z
+}
+
+// Cmp compares x and y and returns:
+//
+//   -1 if x <  y
+//    0 if x == y
+//   +1 if x >  y
+//
+func (x *Dec) Cmp(y *Dec) int {
+	xx, yy := upscale(x, y)
+	return xx.UnscaledBig().Cmp(yy.UnscaledBig())
+}
+
+// Abs sets z to |x| (the absolute value of x) and returns z.
+func (z *Dec) Abs(x *Dec) *Dec {
+	z.SetScale(x.Scale())
+	z.UnscaledBig().Abs(x.UnscaledBig())
+	return z
+}
+
+// Add sets z to the sum x+y and returns z.
+// The scale of z is the greater of the scales of x and y.
+func (z *Dec) Add(x, y *Dec) *Dec {
+	xx, yy := upscale(x, y)
+	z.SetScale(xx.Scale())
+	z.UnscaledBig().Add(xx.UnscaledBig(), yy.UnscaledBig())
+	return z
+}
+
+// Sub sets z to the difference x-y and returns z.
+// The scale of z is the greater of the scales of x and y.
+func (z *Dec) Sub(x, y *Dec) *Dec {
+	xx, yy := upscale(x, y)
+	z.SetScale(xx.Scale())
+	z.UnscaledBig().Sub(xx.UnscaledBig(), yy.UnscaledBig())
+	return z
+}
+
+// Mul sets z to the product x*y and returns z.
+// The scale of z is the sum of the scales of x and y.
+func (z *Dec) Mul(x, y *Dec) *Dec {
+	z.SetScale(x.Scale() + y.Scale())
+	z.UnscaledBig().Mul(x.UnscaledBig(), y.UnscaledBig())
+	return z
+}
+
+// Round sets z to the value of x rounded to Scale s using Rounder r, and
+// returns z.
+func (z *Dec) Round(x *Dec, s Scale, r Rounder) *Dec {
+	return z.QuoRound(x, NewDec(1, 0), s, r)
+}
+
+// QuoRound sets z to the quotient x/y, rounded using the given Rounder to the
+// specified scale.
+//
+// If the rounder is RoundExact but the result can not be expressed exactly at
+// the specified scale, QuoRound returns nil, and the value of z is undefined.
+//
+// There is no corresponding Div method; the equivalent can be achieved through
+// the choice of Rounder used.
+//
+func (z *Dec) QuoRound(x, y *Dec, s Scale, r Rounder) *Dec {
+	return z.quo(x, y, sclr{s}, r)
+}
+
+func (z *Dec) quo(x, y *Dec, s scaler, r Rounder) *Dec {
+	scl := s.Scale(x, y)
+	var zzz *Dec
+	if r.UseRemainder() {
+		zz, rA, rB := new(Dec).quoRem(x, y, scl, true, new(big.Int), new(big.Int))
+		zzz = r.Round(new(Dec), zz, rA, rB)
+	} else {
+		zz, _, _ := new(Dec).quoRem(x, y, scl, false, nil, nil)
+		zzz = r.Round(new(Dec), zz, nil, nil)
+	}
+	if zzz == nil {
+		return nil
+	}
+	return z.Set(zzz)
+}
+
+// QuoExact sets z to the quotient x/y and returns z when x/y is a finite
+// decimal. Otherwise it returns nil and the value of z is undefined.
+//
+// The scale of a non-nil result is "x.Scale() - y.Scale()" or greater; it is
+// calculated so that the remainder will be zero whenever x/y is a finite
+// decimal.
+func (z *Dec) QuoExact(x, y *Dec) *Dec {
+	return z.quo(x, y, scaleQuoExact{}, RoundExact)
+}
+
+// quoRem sets z to the quotient x/y with the scale s, and if useRem is true,
+// it sets remNum and remDen to the numerator and denominator of the remainder.
+// It returns z, remNum and remDen.
+//
+// The remainder is normalized to the range -1 < r < 1 to simplify rounding;
+// that is, the results satisfy the following equation:
+//
+//  x / y = z + (remNum/remDen) * 10**(-z.Scale())
+//
+// See Rounder for more details about rounding.
+//
+func (z *Dec) quoRem(x, y *Dec, s Scale, useRem bool,
+	remNum, remDen *big.Int) (*Dec, *big.Int, *big.Int) {
+	// difference (required adjustment) compared to "canonical" result scale
+	shift := s - (x.Scale() - y.Scale())
+	// pointers to adjusted unscaled dividend and divisor
+	var ix, iy *big.Int
+	switch {
+	case shift > 0:
+		// increased scale: decimal-shift dividend left
+		ix = new(big.Int).Mul(x.UnscaledBig(), exp10(shift))
+		iy = y.UnscaledBig()
+	case shift < 0:
+		// decreased scale: decimal-shift divisor left
+		ix = x.UnscaledBig()
+		iy = new(big.Int).Mul(y.UnscaledBig(), exp10(-shift))
+	default:
+		ix = x.UnscaledBig()
+		iy = y.UnscaledBig()
+	}
+	// save a copy of iy in case it to be overwritten with the result
+	iy2 := iy
+	if iy == z.UnscaledBig() {
+		iy2 = new(big.Int).Set(iy)
+	}
+	// set scale
+	z.SetScale(s)
+	// set unscaled
+	if useRem {
+		// Int division
+		_, intr := z.UnscaledBig().QuoRem(ix, iy, new(big.Int))
+		// set remainder
+		remNum.Set(intr)
+		remDen.Set(iy2)
+	} else {
+		z.UnscaledBig().Quo(ix, iy)
+	}
+	return z, remNum, remDen
+}
+
+type sclr struct{ s Scale }
+
+func (s sclr) Scale(x, y *Dec) Scale {
+	return s.s
+}
+
+type scaleQuoExact struct{}
+
+func (sqe scaleQuoExact) Scale(x, y *Dec) Scale {
+	rem := new(big.Rat).SetFrac(x.UnscaledBig(), y.UnscaledBig())
+	f2, f5 := factor2(rem.Denom()), factor(rem.Denom(), bigInt[5])
+	var f10 Scale
+	if f2 > f5 {
+		f10 = Scale(f2)
+	} else {
+		f10 = Scale(f5)
+	}
+	return x.Scale() - y.Scale() + f10
+}
+
+func factor(n *big.Int, p *big.Int) int {
+	// could be improved for large factors
+	d, f := n, 0
+	for {
+		dd, dm := new(big.Int).DivMod(d, p, new(big.Int))
+		if dm.Sign() == 0 {
+			f++
+			d = dd
+		} else {
+			break
+		}
+	}
+	return f
+}
+
+func factor2(n *big.Int) int {
+	// could be improved for large factors
+	f := 0
+	for ; n.Bit(f) == 0; f++ {
+	}
+	return f
+}
+
+func upscale(a, b *Dec) (*Dec, *Dec) {
+	if a.Scale() == b.Scale() {
+		return a, b
+	}
+	if a.Scale() > b.Scale() {
+		bb := b.rescale(a.Scale())
+		return a, bb
+	}
+	aa := a.rescale(b.Scale())
+	return aa, b
+}
+
+func exp10(x Scale) *big.Int {
+	if int(x) < len(exp10cache) {
+		return &exp10cache[int(x)]
+	}
+	return new(big.Int).Exp(bigInt[10], big.NewInt(int64(x)), nil)
+}
+
+func (x *Dec) rescale(newScale Scale) *Dec {
+	shift := newScale - x.Scale()
+	switch {
+	case shift < 0:
+		e := exp10(-shift)
+		return NewDecBig(new(big.Int).Quo(x.UnscaledBig(), e), newScale)
+	case shift > 0:
+		e := exp10(shift)
+		return NewDecBig(new(big.Int).Mul(x.UnscaledBig(), e), newScale)
+	}
+	return x
+}
+
+var zeros = []byte("00000000000000000000000000000000" +
+	"00000000000000000000000000000000")
+var lzeros = Scale(len(zeros))
+
+func appendZeros(s []byte, n Scale) []byte {
+	for i := Scale(0); i < n; i += lzeros {
+		if n > i+lzeros {
+			s = append(s, zeros...)
+		} else {
+			s = append(s, zeros[0:n-i]...)
+		}
+	}
+	return s
+}
+
+func (x *Dec) String() string {
+	if x == nil {
+		return "<nil>"
+	}
+	scale := x.Scale()
+	s := []byte(x.UnscaledBig().String())
+	if scale <= 0 {
+		if scale != 0 && x.unscaled.Sign() != 0 {
+			s = appendZeros(s, -scale)
+		}
+		return string(s)
+	}
+	negbit := Scale(-((x.Sign() - 1) / 2))
+	// scale > 0
+	lens := Scale(len(s))
+	if lens-negbit <= scale {
+		ss := make([]byte, 0, scale+2)
+		if negbit == 1 {
+			ss = append(ss, '-')
+		}
+		ss = append(ss, '0', '.')
+		ss = appendZeros(ss, scale-lens+negbit)
+		ss = append(ss, s[negbit:]...)
+		return string(ss)
+	}
+	// lens > scale
+	ss := make([]byte, 0, lens+1)
+	ss = append(ss, s[:lens-scale]...)
+	ss = append(ss, '.')
+	ss = append(ss, s[lens-scale:]...)
+	return string(ss)
+}
+
+// Format is a support routine for fmt.Formatter. It accepts the decimal
+// formats 'd' and 'f', and handles both equivalently.
+// Width, precision, flags and bases 2, 8, 16 are not supported.
+func (x *Dec) Format(s fmt.State, ch rune) {
+	if ch != 'd' && ch != 'f' && ch != 'v' && ch != 's' {
+		fmt.Fprintf(s, "%%!%c(dec.Dec=%s)", ch, x.String())
+		return
+	}
+	fmt.Fprintf(s, x.String())
+}
+
+func (z *Dec) scan(r io.RuneScanner) (*Dec, error) {
+	unscaled := make([]byte, 0, 256) // collects chars of unscaled as bytes
+	dp, dg := -1, -1                 // indexes of decimal point, first digit
+loop:
+	for {
+		ch, _, err := r.ReadRune()
+		if err == io.EOF {
+			break loop
+		}
+		if err != nil {
+			return nil, err
+		}
+		switch {
+		case ch == '+' || ch == '-':
+			if len(unscaled) > 0 || dp >= 0 { // must be first character
+				r.UnreadRune()
+				break loop
+			}
+		case ch == '.':
+			if dp >= 0 {
+				r.UnreadRune()
+				break loop
+			}
+			dp = len(unscaled)
+			continue // don't add to unscaled
+		case ch >= '0' && ch <= '9':
+			if dg == -1 {
+				dg = len(unscaled)
+			}
+		default:
+			r.UnreadRune()
+			break loop
+		}
+		unscaled = append(unscaled, byte(ch))
+	}
+	if dg == -1 {
+		return nil, fmt.Errorf("no digits read")
+	}
+	if dp >= 0 {
+		z.SetScale(Scale(len(unscaled) - dp))
+	} else {
+		z.SetScale(0)
+	}
+	_, ok := z.UnscaledBig().SetString(string(unscaled), 10)
+	if !ok {
+		return nil, fmt.Errorf("invalid decimal: %s", string(unscaled))
+	}
+	return z, nil
+}
+
+// SetString sets z to the value of s, interpreted as a decimal (base 10),
+// and returns z and a boolean indicating success. The scale of z is the
+// number of digits after the decimal point (including any trailing 0s),
+// or 0 if there is no decimal point. If SetString fails, the value of z
+// is undefined but the returned value is nil.
+func (z *Dec) SetString(s string) (*Dec, bool) {
+	r := strings.NewReader(s)
+	_, err := z.scan(r)
+	if err != nil {
+		return nil, false
+	}
+	_, _, err = r.ReadRune()
+	if err != io.EOF {
+		return nil, false
+	}
+	// err == io.EOF => scan consumed all of s
+	return z, true
+}
+
+// Scan is a support routine for fmt.Scanner; it sets z to the value of
+// the scanned number. It accepts the decimal formats 'd' and 'f', and
+// handles both equivalently. Bases 2, 8, 16 are not supported.
+// The scale of z is the number of digits after the decimal point
+// (including any trailing 0s), or 0 if there is no decimal point.
+func (z *Dec) Scan(s fmt.ScanState, ch rune) error {
+	if ch != 'd' && ch != 'f' && ch != 's' && ch != 'v' {
+		return fmt.Errorf("Dec.Scan: invalid verb '%c'", ch)
+	}
+	s.SkipSpace()
+	_, err := z.scan(s)
+	return err
+}
+
+// Gob encoding version
+const decGobVersion byte = 1
+
+func scaleBytes(s Scale) []byte {
+	buf := make([]byte, scaleSize)
+	i := scaleSize
+	for j := 0; j < scaleSize; j++ {
+		i--
+		buf[i] = byte(s)
+		s >>= 8
+	}
+	return buf
+}
+
+func scale(b []byte) (s Scale) {
+	for j := 0; j < scaleSize; j++ {
+		s <<= 8
+		s |= Scale(b[j])
+	}
+	return
+}
+
+// GobEncode implements the gob.GobEncoder interface.
+func (x *Dec) GobEncode() ([]byte, error) {
+	buf, err := x.UnscaledBig().GobEncode()
+	if err != nil {
+		return nil, err
+	}
+	buf = append(append(buf, scaleBytes(x.Scale())...), decGobVersion)
+	return buf, nil
+}
+
+// GobDecode implements the gob.GobDecoder interface.
+func (z *Dec) GobDecode(buf []byte) error {
+	if len(buf) == 0 {
+		return fmt.Errorf("Dec.GobDecode: no data")
+	}
+	b := buf[len(buf)-1]
+	if b != decGobVersion {
+		return fmt.Errorf("Dec.GobDecode: encoding version %d not supported", b)
+	}
+	l := len(buf) - scaleSize - 1
+	err := z.UnscaledBig().GobDecode(buf[:l])
+	if err != nil {
+		return err
+	}
+	z.SetScale(scale(buf[l : l+scaleSize]))
+	return nil
+}
+
+// MarshalText implements the encoding.TextMarshaler interface.
+func (x *Dec) MarshalText() ([]byte, error) {
+	return []byte(x.String()), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (z *Dec) UnmarshalText(data []byte) error {
+	_, ok := z.SetString(string(data))
+	if !ok {
+		return fmt.Errorf("invalid inf.Dec")
+	}
+	return nil
+}

+ 33 - 0
src/gopkg.in/inf.v0/dec_go1_2_test.go

@@ -0,0 +1,33 @@
+// +build go1.2
+
+package inf
+
+import (
+	"encoding"
+	"encoding/json"
+	"testing"
+)
+
+var _ encoding.TextMarshaler = new(Dec)
+var _ encoding.TextUnmarshaler = new(Dec)
+
+type Obj struct {
+	Val *Dec
+}
+
+func TestDecJsonMarshalUnmarshal(t *testing.T) {
+	o := Obj{Val: NewDec(123, 2)}
+	js, err := json.Marshal(o)
+	if err != nil {
+		t.Fatalf("json.Marshal(%v): got %v, want ok", o, err)
+	}
+	o2 := &Obj{}
+	err = json.Unmarshal(js, o2)
+	if err != nil {
+		t.Fatalf("json.Unmarshal(%#q): got %v, want ok", js, err)
+	}
+	if o.Val.Scale() != o2.Val.Scale() ||
+		o.Val.UnscaledBig().Cmp(o2.Val.UnscaledBig()) != 0 {
+		t.Fatalf("json.Unmarshal(json.Marshal(%v)): want %v, got %v", o, o, o2)
+	}
+}

+ 40 - 0
src/gopkg.in/inf.v0/dec_internal_test.go

@@ -0,0 +1,40 @@
+package inf
+
+import (
+	"math/big"
+	"testing"
+)
+
+var decQuoRemZZZ = []struct {
+	z, x, y  *Dec
+	r        *big.Rat
+	srA, srB int
+}{
+	// basic examples
+	{NewDec(1, 0), NewDec(2, 0), NewDec(2, 0), big.NewRat(0, 1), 0, 1},
+	{NewDec(15, 1), NewDec(3, 0), NewDec(2, 0), big.NewRat(0, 1), 0, 1},
+	{NewDec(1, 1), NewDec(1, 0), NewDec(10, 0), big.NewRat(0, 1), 0, 1},
+	{NewDec(0, 0), NewDec(2, 0), NewDec(3, 0), big.NewRat(2, 3), 1, 1},
+	{NewDec(0, 0), NewDec(2, 0), NewDec(6, 0), big.NewRat(1, 3), 1, 1},
+	{NewDec(1, 1), NewDec(2, 0), NewDec(12, 0), big.NewRat(2, 3), 1, 1},
+
+	// examples from the Go Language Specification
+	{NewDec(1, 0), NewDec(5, 0), NewDec(3, 0), big.NewRat(2, 3), 1, 1},
+	{NewDec(-1, 0), NewDec(-5, 0), NewDec(3, 0), big.NewRat(-2, 3), -1, 1},
+	{NewDec(-1, 0), NewDec(5, 0), NewDec(-3, 0), big.NewRat(-2, 3), 1, -1},
+	{NewDec(1, 0), NewDec(-5, 0), NewDec(-3, 0), big.NewRat(2, 3), -1, -1},
+}
+
+func TestDecQuoRem(t *testing.T) {
+	for i, a := range decQuoRemZZZ {
+		z, rA, rB := new(Dec), new(big.Int), new(big.Int)
+		s := scaleQuoExact{}.Scale(a.x, a.y)
+		z.quoRem(a.x, a.y, s, true, rA, rB)
+		if a.z.Cmp(z) != 0 || a.r.Cmp(new(big.Rat).SetFrac(rA, rB)) != 0 {
+			t.Errorf("#%d QuoRemZZZ got %v, %v, %v; expected %v, %v", i, z, rA, rB, a.z, a.r)
+		}
+		if a.srA != rA.Sign() || a.srB != rB.Sign() {
+			t.Errorf("#%d QuoRemZZZ wrong signs, got %v, %v; expected %v, %v", i, rA.Sign(), rB.Sign(), a.srA, a.srB)
+		}
+	}
+}

+ 379 - 0
src/gopkg.in/inf.v0/dec_test.go

@@ -0,0 +1,379 @@
+package inf_test
+
+import (
+	"bytes"
+	"encoding/gob"
+	"fmt"
+	"math/big"
+	"strings"
+	"testing"
+
+	"gopkg.in/inf.v0"
+)
+
+type decFunZZ func(z, x, y *inf.Dec) *inf.Dec
+type decArgZZ struct {
+	z, x, y *inf.Dec
+}
+
+var decSumZZ = []decArgZZ{
+	{inf.NewDec(0, 0), inf.NewDec(0, 0), inf.NewDec(0, 0)},
+	{inf.NewDec(1, 0), inf.NewDec(1, 0), inf.NewDec(0, 0)},
+	{inf.NewDec(1111111110, 0), inf.NewDec(123456789, 0), inf.NewDec(987654321, 0)},
+	{inf.NewDec(-1, 0), inf.NewDec(-1, 0), inf.NewDec(0, 0)},
+	{inf.NewDec(864197532, 0), inf.NewDec(-123456789, 0), inf.NewDec(987654321, 0)},
+	{inf.NewDec(-1111111110, 0), inf.NewDec(-123456789, 0), inf.NewDec(-987654321, 0)},
+	{inf.NewDec(12, 2), inf.NewDec(1, 1), inf.NewDec(2, 2)},
+}
+
+var decProdZZ = []decArgZZ{
+	{inf.NewDec(0, 0), inf.NewDec(0, 0), inf.NewDec(0, 0)},
+	{inf.NewDec(0, 0), inf.NewDec(1, 0), inf.NewDec(0, 0)},
+	{inf.NewDec(1, 0), inf.NewDec(1, 0), inf.NewDec(1, 0)},
+	{inf.NewDec(-991*991, 0), inf.NewDec(991, 0), inf.NewDec(-991, 0)},
+	{inf.NewDec(2, 3), inf.NewDec(1, 1), inf.NewDec(2, 2)},
+	{inf.NewDec(2, -3), inf.NewDec(1, -1), inf.NewDec(2, -2)},
+	{inf.NewDec(2, 3), inf.NewDec(1, 1), inf.NewDec(2, 2)},
+}
+
+func TestDecSignZ(t *testing.T) {
+	var zero inf.Dec
+	for _, a := range decSumZZ {
+		s := a.z.Sign()
+		e := a.z.Cmp(&zero)
+		if s != e {
+			t.Errorf("got %d; want %d for z = %v", s, e, a.z)
+		}
+	}
+}
+
+func TestDecAbsZ(t *testing.T) {
+	var zero inf.Dec
+	for _, a := range decSumZZ {
+		var z inf.Dec
+		z.Abs(a.z)
+		var e inf.Dec
+		e.Set(a.z)
+		if e.Cmp(&zero) < 0 {
+			e.Sub(&zero, &e)
+		}
+		if z.Cmp(&e) != 0 {
+			t.Errorf("got z = %v; want %v", z, e)
+		}
+	}
+}
+
+func testDecFunZZ(t *testing.T, msg string, f decFunZZ, a decArgZZ) {
+	var z inf.Dec
+	f(&z, a.x, a.y)
+	if (&z).Cmp(a.z) != 0 {
+		t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z)
+	}
+}
+
+func TestDecSumZZ(t *testing.T) {
+	AddZZ := func(z, x, y *inf.Dec) *inf.Dec { return z.Add(x, y) }
+	SubZZ := func(z, x, y *inf.Dec) *inf.Dec { return z.Sub(x, y) }
+	for _, a := range decSumZZ {
+		arg := a
+		testDecFunZZ(t, "AddZZ", AddZZ, arg)
+
+		arg = decArgZZ{a.z, a.y, a.x}
+		testDecFunZZ(t, "AddZZ symmetric", AddZZ, arg)
+
+		arg = decArgZZ{a.x, a.z, a.y}
+		testDecFunZZ(t, "SubZZ", SubZZ, arg)
+
+		arg = decArgZZ{a.y, a.z, a.x}
+		testDecFunZZ(t, "SubZZ symmetric", SubZZ, arg)
+	}
+}
+
+func TestDecProdZZ(t *testing.T) {
+	MulZZ := func(z, x, y *inf.Dec) *inf.Dec { return z.Mul(x, y) }
+	for _, a := range decProdZZ {
+		arg := a
+		testDecFunZZ(t, "MulZZ", MulZZ, arg)
+
+		arg = decArgZZ{a.z, a.y, a.x}
+		testDecFunZZ(t, "MulZZ symmetric", MulZZ, arg)
+	}
+}
+
+var decUnscaledTests = []struct {
+	d  *inf.Dec
+	u  int64 // ignored when ok == false
+	ok bool
+}{
+	{new(inf.Dec), 0, true},
+	{inf.NewDec(-1<<63, 0), -1 << 63, true},
+	{inf.NewDec(-(-1<<63 + 1), 0), -(-1<<63 + 1), true},
+	{new(inf.Dec).Neg(inf.NewDec(-1<<63, 0)), 0, false},
+	{new(inf.Dec).Sub(inf.NewDec(-1<<63, 0), inf.NewDec(1, 0)), 0, false},
+	{inf.NewDecBig(new(big.Int).Lsh(big.NewInt(1), 64), 0), 0, false},
+}
+
+func TestDecUnscaled(t *testing.T) {
+	for i, tt := range decUnscaledTests {
+		u, ok := tt.d.Unscaled()
+		if ok != tt.ok {
+			t.Errorf("#%d Unscaled: got %v, expected %v", i, ok, tt.ok)
+		} else if ok && u != tt.u {
+			t.Errorf("#%d Unscaled: got %v, expected %v", i, u, tt.u)
+		}
+	}
+}
+
+var decRoundTests = [...]struct {
+	in  *inf.Dec
+	s   inf.Scale
+	r   inf.Rounder
+	exp *inf.Dec
+}{
+	{inf.NewDec(123424999999999993, 15), 2, inf.RoundHalfUp, inf.NewDec(12342, 2)},
+	{inf.NewDec(123425000000000001, 15), 2, inf.RoundHalfUp, inf.NewDec(12343, 2)},
+	{inf.NewDec(123424999999999993, 15), 15, inf.RoundHalfUp, inf.NewDec(123424999999999993, 15)},
+	{inf.NewDec(123424999999999993, 15), 16, inf.RoundHalfUp, inf.NewDec(1234249999999999930, 16)},
+	{inf.NewDecBig(new(big.Int).Lsh(big.NewInt(1), 64), 0), -1, inf.RoundHalfUp, inf.NewDec(1844674407370955162, -1)},
+	{inf.NewDecBig(new(big.Int).Lsh(big.NewInt(1), 64), 0), -2, inf.RoundHalfUp, inf.NewDec(184467440737095516, -2)},
+	{inf.NewDecBig(new(big.Int).Lsh(big.NewInt(1), 64), 0), -3, inf.RoundHalfUp, inf.NewDec(18446744073709552, -3)},
+	{inf.NewDecBig(new(big.Int).Lsh(big.NewInt(1), 64), 0), -4, inf.RoundHalfUp, inf.NewDec(1844674407370955, -4)},
+	{inf.NewDecBig(new(big.Int).Lsh(big.NewInt(1), 64), 0), -5, inf.RoundHalfUp, inf.NewDec(184467440737096, -5)},
+	{inf.NewDecBig(new(big.Int).Lsh(big.NewInt(1), 64), 0), -6, inf.RoundHalfUp, inf.NewDec(18446744073710, -6)},
+}
+
+func TestDecRound(t *testing.T) {
+	for i, tt := range decRoundTests {
+		z := new(inf.Dec).Round(tt.in, tt.s, tt.r)
+		if tt.exp.Cmp(z) != 0 {
+			t.Errorf("#%d Round got %v; expected %v", i, z, tt.exp)
+		}
+	}
+}
+
+var decStringTests = []struct {
+	in     string
+	out    string
+	val    int64
+	scale  inf.Scale // skip SetString if negative
+	ok     bool
+	scanOk bool
+}{
+	{in: "", ok: false, scanOk: false},
+	{in: "a", ok: false, scanOk: false},
+	{in: "z", ok: false, scanOk: false},
+	{in: "+", ok: false, scanOk: false},
+	{in: "-", ok: false, scanOk: false},
+	{in: "g", ok: false, scanOk: false},
+	{in: ".", ok: false, scanOk: false},
+	{in: ".-0", ok: false, scanOk: false},
+	{in: ".+0", ok: false, scanOk: false},
+	// Scannable but not SetStringable
+	{"0b", "ignored", 0, 0, false, true},
+	{"0x", "ignored", 0, 0, false, true},
+	{"0xg", "ignored", 0, 0, false, true},
+	{"0.0g", "ignored", 0, 1, false, true},
+	// examples from godoc for Dec
+	{"0", "0", 0, 0, true, true},
+	{"0.00", "0.00", 0, 2, true, true},
+	{"ignored", "0", 0, -2, true, false},
+	{"1", "1", 1, 0, true, true},
+	{"1.00", "1.00", 100, 2, true, true},
+	{"10", "10", 10, 0, true, true},
+	{"ignored", "10", 1, -1, true, false},
+	// other tests
+	{"+0", "0", 0, 0, true, true},
+	{"-0", "0", 0, 0, true, true},
+	{"0.0", "0.0", 0, 1, true, true},
+	{"0.1", "0.1", 1, 1, true, true},
+	{"0.", "0", 0, 0, true, true},
+	{"-10", "-10", -1, -1, true, true},
+	{"-1", "-1", -1, 0, true, true},
+	{"-0.1", "-0.1", -1, 1, true, true},
+	{"-0.01", "-0.01", -1, 2, true, true},
+	{"+0.", "0", 0, 0, true, true},
+	{"-0.", "0", 0, 0, true, true},
+	{".0", "0.0", 0, 1, true, true},
+	{"+.0", "0.0", 0, 1, true, true},
+	{"-.0", "0.0", 0, 1, true, true},
+	{"0.0000000000", "0.0000000000", 0, 10, true, true},
+	{"0.0000000001", "0.0000000001", 1, 10, true, true},
+	{"-0.0000000000", "0.0000000000", 0, 10, true, true},
+	{"-0.0000000001", "-0.0000000001", -1, 10, true, true},
+	{"-10", "-10", -10, 0, true, true},
+	{"+10", "10", 10, 0, true, true},
+	{"00", "0", 0, 0, true, true},
+	{"023", "23", 23, 0, true, true},      // decimal, not octal
+	{"-02.3", "-2.3", -23, 1, true, true}, // decimal, not octal
+}
+
+func TestDecGetString(t *testing.T) {
+	z := new(inf.Dec)
+	for i, test := range decStringTests {
+		if !test.ok {
+			continue
+		}
+		z.SetUnscaled(test.val)
+		z.SetScale(test.scale)
+
+		s := z.String()
+		if s != test.out {
+			t.Errorf("#%da got %s; want %s", i, s, test.out)
+		}
+
+		s = fmt.Sprintf("%d", z)
+		if s != test.out {
+			t.Errorf("#%db got %s; want %s", i, s, test.out)
+		}
+	}
+}
+
+func TestDecSetString(t *testing.T) {
+	tmp := new(inf.Dec)
+	for i, test := range decStringTests {
+		if test.scale < 0 {
+			// SetString only supports scale >= 0
+			continue
+		}
+		// initialize to a non-zero value so that issues with parsing
+		// 0 are detected
+		tmp.Set(inf.NewDec(1234567890, 123))
+		n1, ok1 := new(inf.Dec).SetString(test.in)
+		n2, ok2 := tmp.SetString(test.in)
+		expected := inf.NewDec(test.val, test.scale)
+		if ok1 != test.ok || ok2 != test.ok {
+			t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok)
+			continue
+		}
+		if !ok1 {
+			if n1 != nil {
+				t.Errorf("#%d (input '%s') n1 != nil", i, test.in)
+			}
+			continue
+		}
+		if !ok2 {
+			if n2 != nil {
+				t.Errorf("#%d (input '%s') n2 != nil", i, test.in)
+			}
+			continue
+		}
+
+		if n1.Cmp(expected) != 0 {
+			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val)
+		}
+		if n2.Cmp(expected) != 0 {
+			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val)
+		}
+	}
+}
+
+func TestDecScan(t *testing.T) {
+	tmp := new(inf.Dec)
+	for i, test := range decStringTests {
+		if test.scale < 0 {
+			// SetString only supports scale >= 0
+			continue
+		}
+		// initialize to a non-zero value so that issues with parsing
+		// 0 are detected
+		tmp.Set(inf.NewDec(1234567890, 123))
+		n1, n2 := new(inf.Dec), tmp
+		nn1, err1 := fmt.Sscan(test.in, n1)
+		nn2, err2 := fmt.Sscan(test.in, n2)
+		if !test.scanOk {
+			if err1 == nil || err2 == nil {
+				t.Errorf("#%d (input '%s') ok incorrect, should be %t", i, test.in, test.scanOk)
+			}
+			continue
+		}
+		expected := inf.NewDec(test.val, test.scale)
+		if nn1 != 1 || err1 != nil || nn2 != 1 || err2 != nil {
+			t.Errorf("#%d (input '%s') error %d %v, %d %v", i, test.in, nn1, err1, nn2, err2)
+			continue
+		}
+		if n1.Cmp(expected) != 0 {
+			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val)
+		}
+		if n2.Cmp(expected) != 0 {
+			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val)
+		}
+	}
+}
+
+var decScanNextTests = []struct {
+	in   string
+	ok   bool
+	next rune
+}{
+	{"", false, 0},
+	{"a", false, 'a'},
+	{"z", false, 'z'},
+	{"+", false, 0},
+	{"-", false, 0},
+	{"g", false, 'g'},
+	{".", false, 0},
+	{".-0", false, '-'},
+	{".+0", false, '+'},
+	{"0b", true, 'b'},
+	{"0x", true, 'x'},
+	{"0xg", true, 'x'},
+	{"0.0g", true, 'g'},
+}
+
+func TestDecScanNext(t *testing.T) {
+	for i, test := range decScanNextTests {
+		rdr := strings.NewReader(test.in)
+		n1 := new(inf.Dec)
+		nn1, _ := fmt.Fscan(rdr, n1)
+		if (test.ok && nn1 == 0) || (!test.ok && nn1 > 0) {
+			t.Errorf("#%d (input '%s') ok incorrect should be %t", i, test.in, test.ok)
+			continue
+		}
+		r := rune(0)
+		nn2, err := fmt.Fscanf(rdr, "%c", &r)
+		if test.next != r {
+			t.Errorf("#%d (input '%s') next incorrect, got %c should be %c, %d, %v", i, test.in, r, test.next, nn2, err)
+		}
+	}
+}
+
+var decGobEncodingTests = []string{
+	"0",
+	"1",
+	"2",
+	"10",
+	"42",
+	"1234567890",
+	"298472983472983471903246121093472394872319615612417471234712061",
+}
+
+func TestDecGobEncoding(t *testing.T) {
+	var medium bytes.Buffer
+	enc := gob.NewEncoder(&medium)
+	dec := gob.NewDecoder(&medium)
+	for i, test := range decGobEncodingTests {
+		for j := 0; j < 2; j++ {
+			for k := inf.Scale(-5); k <= 5; k++ {
+				medium.Reset() // empty buffer for each test case (in case of failures)
+				stest := test
+				if j != 0 {
+					// negative numbers
+					stest = "-" + test
+				}
+				var tx inf.Dec
+				tx.SetString(stest)
+				tx.SetScale(k) // test with positive, negative, and zero scale
+				if err := enc.Encode(&tx); err != nil {
+					t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err)
+				}
+				var rx inf.Dec
+				if err := dec.Decode(&rx); err != nil {
+					t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err)
+				}
+				if rx.Cmp(&tx) != 0 {
+					t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx)
+				}
+			}
+		}
+	}
+}

+ 62 - 0
src/gopkg.in/inf.v0/example_test.go

@@ -0,0 +1,62 @@
+package inf_test
+
+import (
+	"fmt"
+	"log"
+)
+
+import "gopkg.in/inf.v0"
+
+func ExampleDec_SetString() {
+	d := new(inf.Dec)
+	d.SetString("012345.67890") // decimal; leading 0 ignored; trailing 0 kept
+	fmt.Println(d)
+	// Output: 12345.67890
+}
+
+func ExampleDec_Scan() {
+	// The Scan function is rarely used directly;
+	// the fmt package recognizes it as an implementation of fmt.Scanner.
+	d := new(inf.Dec)
+	_, err := fmt.Sscan("184467440.73709551617", d)
+	if err != nil {
+		log.Println("error scanning value:", err)
+	} else {
+		fmt.Println(d)
+	}
+	// Output: 184467440.73709551617
+}
+
+func ExampleDec_QuoRound_scale2RoundDown() {
+	// 10 / 3 is an infinite decimal; it has no exact Dec representation
+	x, y := inf.NewDec(10, 0), inf.NewDec(3, 0)
+	// use 2 digits beyond the decimal point, round towards 0
+	z := new(inf.Dec).QuoRound(x, y, 2, inf.RoundDown)
+	fmt.Println(z)
+	// Output: 3.33
+}
+
+func ExampleDec_QuoRound_scale2RoundCeil() {
+	// -42 / 400 is an finite decimal with 3 digits beyond the decimal point
+	x, y := inf.NewDec(-42, 0), inf.NewDec(400, 0)
+	// use 2 digits beyond decimal point, round towards positive infinity
+	z := new(inf.Dec).QuoRound(x, y, 2, inf.RoundCeil)
+	fmt.Println(z)
+	// Output: -0.10
+}
+
+func ExampleDec_QuoExact_ok() {
+	// 1 / 25 is a finite decimal; it has exact Dec representation
+	x, y := inf.NewDec(1, 0), inf.NewDec(25, 0)
+	z := new(inf.Dec).QuoExact(x, y)
+	fmt.Println(z)
+	// Output: 0.04
+}
+
+func ExampleDec_QuoExact_fail() {
+	// 1 / 3 is an infinite decimal; it has no exact Dec representation
+	x, y := inf.NewDec(1, 0), inf.NewDec(3, 0)
+	z := new(inf.Dec).QuoExact(x, y)
+	fmt.Println(z)
+	// Output: <nil>
+}

+ 145 - 0
src/gopkg.in/inf.v0/rounder.go

@@ -0,0 +1,145 @@
+package inf
+
+import (
+	"math/big"
+)
+
+// Rounder represents a method for rounding the (possibly infinite decimal)
+// result of a division to a finite Dec. It is used by Dec.Round() and
+// Dec.Quo().
+//
+// See the Example for results of using each Rounder with some sample values.
+//
+type Rounder rounder
+
+// See http://speleotrove.com/decimal/damodel.html#refround for more detailed
+// definitions of these rounding modes.
+var (
+	RoundDown     Rounder // towards 0
+	RoundUp       Rounder // away from 0
+	RoundFloor    Rounder // towards -infinity
+	RoundCeil     Rounder // towards +infinity
+	RoundHalfDown Rounder // to nearest; towards 0 if same distance
+	RoundHalfUp   Rounder // to nearest; away from 0 if same distance
+	RoundHalfEven Rounder // to nearest; even last digit if same distance
+)
+
+// RoundExact is to be used in the case when rounding is not necessary.
+// When used with Quo or Round, it returns the result verbatim when it can be
+// expressed exactly with the given precision, and it returns nil otherwise.
+// QuoExact is a shorthand for using Quo with RoundExact.
+var RoundExact Rounder
+
+type rounder interface {
+
+	// When UseRemainder() returns true, the Round() method is passed the
+	// remainder of the division, expressed as the numerator and denominator of
+	// a rational.
+	UseRemainder() bool
+
+	// Round sets the rounded value of a quotient to z, and returns z.
+	// quo is rounded down (truncated towards zero) to the scale obtained from
+	// the Scaler in Quo().
+	//
+	// When the remainder is not used, remNum and remDen are nil.
+	// When used, the remainder is normalized between -1 and 1; that is:
+	//
+	//  -|remDen| < remNum < |remDen|
+	//
+	// remDen has the same sign as y, and remNum is zero or has the same sign
+	// as x.
+	Round(z, quo *Dec, remNum, remDen *big.Int) *Dec
+}
+
+type rndr struct {
+	useRem bool
+	round  func(z, quo *Dec, remNum, remDen *big.Int) *Dec
+}
+
+func (r rndr) UseRemainder() bool {
+	return r.useRem
+}
+
+func (r rndr) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec {
+	return r.round(z, quo, remNum, remDen)
+}
+
+var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)}
+
+func roundHalf(f func(c int, odd uint) (roundUp bool)) func(z, q *Dec, rA, rB *big.Int) *Dec {
+	return func(z, q *Dec, rA, rB *big.Int) *Dec {
+		z.Set(q)
+		brA, brB := rA.BitLen(), rB.BitLen()
+		if brA < brB-1 {
+			// brA < brB-1 => |rA| < |rB/2|
+			return z
+		}
+		roundUp := false
+		srA, srB := rA.Sign(), rB.Sign()
+		s := srA * srB
+		if brA == brB-1 {
+			rA2 := new(big.Int).Lsh(rA, 1)
+			if s < 0 {
+				rA2.Neg(rA2)
+			}
+			roundUp = f(rA2.Cmp(rB)*srB, z.UnscaledBig().Bit(0))
+		} else {
+			// brA > brB-1 => |rA| > |rB/2|
+			roundUp = true
+		}
+		if roundUp {
+			z.UnscaledBig().Add(z.UnscaledBig(), intSign[s+1])
+		}
+		return z
+	}
+}
+
+func init() {
+	RoundExact = rndr{true,
+		func(z, q *Dec, rA, rB *big.Int) *Dec {
+			if rA.Sign() != 0 {
+				return nil
+			}
+			return z.Set(q)
+		}}
+	RoundDown = rndr{false,
+		func(z, q *Dec, rA, rB *big.Int) *Dec {
+			return z.Set(q)
+		}}
+	RoundUp = rndr{true,
+		func(z, q *Dec, rA, rB *big.Int) *Dec {
+			z.Set(q)
+			if rA.Sign() != 0 {
+				z.UnscaledBig().Add(z.UnscaledBig(), intSign[rA.Sign()*rB.Sign()+1])
+			}
+			return z
+		}}
+	RoundFloor = rndr{true,
+		func(z, q *Dec, rA, rB *big.Int) *Dec {
+			z.Set(q)
+			if rA.Sign()*rB.Sign() < 0 {
+				z.UnscaledBig().Add(z.UnscaledBig(), intSign[0])
+			}
+			return z
+		}}
+	RoundCeil = rndr{true,
+		func(z, q *Dec, rA, rB *big.Int) *Dec {
+			z.Set(q)
+			if rA.Sign()*rB.Sign() > 0 {
+				z.UnscaledBig().Add(z.UnscaledBig(), intSign[2])
+			}
+			return z
+		}}
+	RoundHalfDown = rndr{true, roundHalf(
+		func(c int, odd uint) bool {
+			return c > 0
+		})}
+	RoundHalfUp = rndr{true, roundHalf(
+		func(c int, odd uint) bool {
+			return c >= 0
+		})}
+	RoundHalfEven = rndr{true, roundHalf(
+		func(c int, odd uint) bool {
+			return c > 0 || c == 0 && odd == 1
+		})}
+}

+ 72 - 0
src/gopkg.in/inf.v0/rounder_example_test.go

@@ -0,0 +1,72 @@
+package inf_test
+
+import (
+	"fmt"
+	"os"
+	"text/tabwriter"
+
+	"gopkg.in/inf.v0"
+)
+
+// This example displays the results of Dec.Round with each of the Rounders.
+//
+func ExampleRounder() {
+	var vals = []struct {
+		x string
+		s inf.Scale
+	}{
+		{"-0.18", 1}, {"-0.15", 1}, {"-0.12", 1}, {"-0.10", 1},
+		{"-0.08", 1}, {"-0.05", 1}, {"-0.02", 1}, {"0.00", 1},
+		{"0.02", 1}, {"0.05", 1}, {"0.08", 1}, {"0.10", 1},
+		{"0.12", 1}, {"0.15", 1}, {"0.18", 1},
+	}
+
+	var rounders = []struct {
+		name    string
+		rounder inf.Rounder
+	}{
+		{"RoundDown", inf.RoundDown}, {"RoundUp", inf.RoundUp},
+		{"RoundCeil", inf.RoundCeil}, {"RoundFloor", inf.RoundFloor},
+		{"RoundHalfDown", inf.RoundHalfDown}, {"RoundHalfUp", inf.RoundHalfUp},
+		{"RoundHalfEven", inf.RoundHalfEven}, {"RoundExact", inf.RoundExact},
+	}
+
+	fmt.Println("The results of new(inf.Dec).Round(x, s, inf.RoundXXX):\n")
+	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight)
+	fmt.Fprint(w, "x\ts\t|\t")
+	for _, r := range rounders {
+		fmt.Fprintf(w, "%s\t", r.name[5:])
+	}
+	fmt.Fprintln(w)
+	for _, v := range vals {
+		fmt.Fprintf(w, "%s\t%d\t|\t", v.x, v.s)
+		for _, r := range rounders {
+			x, _ := new(inf.Dec).SetString(v.x)
+			z := new(inf.Dec).Round(x, v.s, r.rounder)
+			fmt.Fprintf(w, "%d\t", z)
+		}
+		fmt.Fprintln(w)
+	}
+	w.Flush()
+
+	// Output:
+	// The results of new(inf.Dec).Round(x, s, inf.RoundXXX):
+	//
+	//      x s | Down   Up Ceil Floor HalfDown HalfUp HalfEven Exact
+	//  -0.18 1 | -0.1 -0.2 -0.1  -0.2     -0.2   -0.2     -0.2 <nil>
+	//  -0.15 1 | -0.1 -0.2 -0.1  -0.2     -0.1   -0.2     -0.2 <nil>
+	//  -0.12 1 | -0.1 -0.2 -0.1  -0.2     -0.1   -0.1     -0.1 <nil>
+	//  -0.10 1 | -0.1 -0.1 -0.1  -0.1     -0.1   -0.1     -0.1  -0.1
+	//  -0.08 1 |  0.0 -0.1  0.0  -0.1     -0.1   -0.1     -0.1 <nil>
+	//  -0.05 1 |  0.0 -0.1  0.0  -0.1      0.0   -0.1      0.0 <nil>
+	//  -0.02 1 |  0.0 -0.1  0.0  -0.1      0.0    0.0      0.0 <nil>
+	//   0.00 1 |  0.0  0.0  0.0   0.0      0.0    0.0      0.0   0.0
+	//   0.02 1 |  0.0  0.1  0.1   0.0      0.0    0.0      0.0 <nil>
+	//   0.05 1 |  0.0  0.1  0.1   0.0      0.0    0.1      0.0 <nil>
+	//   0.08 1 |  0.0  0.1  0.1   0.0      0.1    0.1      0.1 <nil>
+	//   0.10 1 |  0.1  0.1  0.1   0.1      0.1    0.1      0.1   0.1
+	//   0.12 1 |  0.1  0.2  0.2   0.1      0.1    0.1      0.1 <nil>
+	//   0.15 1 |  0.1  0.2  0.2   0.1      0.1    0.2      0.2 <nil>
+	//   0.18 1 |  0.1  0.2  0.2   0.1      0.2    0.2      0.2 <nil>
+
+}

+ 109 - 0
src/gopkg.in/inf.v0/rounder_test.go

@@ -0,0 +1,109 @@
+package inf_test
+
+import (
+	"math/big"
+	"testing"
+
+	"gopkg.in/inf.v0"
+)
+
+var decRounderInputs = [...]struct {
+	quo    *inf.Dec
+	rA, rB *big.Int
+}{
+	// examples from go language spec
+	{inf.NewDec(1, 0), big.NewInt(2), big.NewInt(3)},   //  5 /  3
+	{inf.NewDec(-1, 0), big.NewInt(-2), big.NewInt(3)}, // -5 /  3
+	{inf.NewDec(-1, 0), big.NewInt(2), big.NewInt(-3)}, //  5 / -3
+	{inf.NewDec(1, 0), big.NewInt(-2), big.NewInt(-3)}, // -5 / -3
+	// examples from godoc
+	{inf.NewDec(-1, 1), big.NewInt(-8), big.NewInt(10)},
+	{inf.NewDec(-1, 1), big.NewInt(-5), big.NewInt(10)},
+	{inf.NewDec(-1, 1), big.NewInt(-2), big.NewInt(10)},
+	{inf.NewDec(0, 1), big.NewInt(-8), big.NewInt(10)},
+	{inf.NewDec(0, 1), big.NewInt(-5), big.NewInt(10)},
+	{inf.NewDec(0, 1), big.NewInt(-2), big.NewInt(10)},
+	{inf.NewDec(0, 1), big.NewInt(0), big.NewInt(1)},
+	{inf.NewDec(0, 1), big.NewInt(2), big.NewInt(10)},
+	{inf.NewDec(0, 1), big.NewInt(5), big.NewInt(10)},
+	{inf.NewDec(0, 1), big.NewInt(8), big.NewInt(10)},
+	{inf.NewDec(1, 1), big.NewInt(2), big.NewInt(10)},
+	{inf.NewDec(1, 1), big.NewInt(5), big.NewInt(10)},
+	{inf.NewDec(1, 1), big.NewInt(8), big.NewInt(10)},
+}
+
+var decRounderResults = [...]struct {
+	rounder inf.Rounder
+	results [len(decRounderInputs)]*inf.Dec
+}{
+	{inf.RoundExact, [...]*inf.Dec{nil, nil, nil, nil,
+		nil, nil, nil, nil, nil, nil,
+		inf.NewDec(0, 1), nil, nil, nil, nil, nil, nil}},
+	{inf.RoundDown, [...]*inf.Dec{
+		inf.NewDec(1, 0), inf.NewDec(-1, 0), inf.NewDec(-1, 0), inf.NewDec(1, 0),
+		inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
+		inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
+		inf.NewDec(0, 1),
+		inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
+		inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1)}},
+	{inf.RoundUp, [...]*inf.Dec{
+		inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0),
+		inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-2, 1),
+		inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
+		inf.NewDec(0, 1),
+		inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1),
+		inf.NewDec(2, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}},
+	{inf.RoundHalfDown, [...]*inf.Dec{
+		inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0),
+		inf.NewDec(-2, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
+		inf.NewDec(-1, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
+		inf.NewDec(0, 1),
+		inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(1, 1),
+		inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(2, 1)}},
+	{inf.RoundHalfUp, [...]*inf.Dec{
+		inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0),
+		inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-1, 1),
+		inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(0, 1),
+		inf.NewDec(0, 1),
+		inf.NewDec(0, 1), inf.NewDec(1, 1), inf.NewDec(1, 1),
+		inf.NewDec(1, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}},
+	{inf.RoundHalfEven, [...]*inf.Dec{
+		inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0),
+		inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-1, 1),
+		inf.NewDec(-1, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
+		inf.NewDec(0, 1),
+		inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(1, 1),
+		inf.NewDec(1, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}},
+	{inf.RoundFloor, [...]*inf.Dec{
+		inf.NewDec(1, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(1, 0),
+		inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-2, 1),
+		inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
+		inf.NewDec(0, 1),
+		inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
+		inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1)}},
+	{inf.RoundCeil, [...]*inf.Dec{
+		inf.NewDec(2, 0), inf.NewDec(-1, 0), inf.NewDec(-1, 0), inf.NewDec(2, 0),
+		inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
+		inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
+		inf.NewDec(0, 1),
+		inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1),
+		inf.NewDec(2, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}},
+}
+
+func TestDecRounders(t *testing.T) {
+	for i, a := range decRounderResults {
+		for j, input := range decRounderInputs {
+			q := new(inf.Dec).Set(input.quo)
+			rA, rB := new(big.Int).Set(input.rA), new(big.Int).Set(input.rB)
+			res := a.rounder.Round(new(inf.Dec), q, rA, rB)
+			if a.results[j] == nil && res == nil {
+				continue
+			}
+			if (a.results[j] == nil && res != nil) ||
+				(a.results[j] != nil && res == nil) ||
+				a.results[j].Cmp(res) != 0 {
+				t.Errorf("#%d,%d Rounder got %v; expected %v", i, j, res, a.results[j])
+			}
+		}
+	}
+}

+ 0 - 20
src/qfw/util/common.go

@@ -22,7 +22,6 @@ import (
 	"time"
 
 	"github.com/dchest/captcha"
-	"gopkg.in/mgo.v2/bson"
 )
 
 const (
@@ -497,25 +496,6 @@ func InterfaceArrToint64Arr(arr []interface{}) []int64 {
 	return tmp
 }
 
-//根据bsonID转string
-func BsonIdToSId(uid interface{}) string {
-	if uid == nil {
-		return ""
-	} else if u, ok := uid.(string); ok {
-		return u
-	} else {
-		return fmt.Sprintf("%x", string(uid.(bson.ObjectId)))
-	}
-}
-
-func StringTOBsonId(id string) (bid bson.ObjectId) {
-	defer Catch()
-	if id != "" {
-		bid = bson.ObjectIdHex(id)
-	}
-	return
-}
-
 func GetSubDay(t1 int64) int {
 	tt1 := time.Unix(t1, 0)
 	tt2 := time.Now()

+ 9 - 8
src/qfw/util/jy/entnichepush.go

@@ -3,16 +3,16 @@ package jy
 import (
 	"encoding/json"
 	"fmt"
-	mg "jy/mongodb"
 	"log"
 	. "qfw/util"
 	"qfw/util/elastic"
+	. "qfw/util/mongodb"
 	"qfw/util/mysql"
 	"qfw/util/redis"
 	"strings"
 	"time"
 
-	"gopkg.in/mgo.v2/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
 )
 
 var EntnichePush = &entnichePush{}
@@ -46,7 +46,7 @@ func (e *entnichePush) todayKey(entId, userId int) string {
 	return fmt.Sprintf("entnichepush_%d_%d", entId, userId)
 }
 
-func (e *entnichePush) Datas(MQFW mg.MongodbSim, PushMysql *mysql.Mysql, entId, userId int, pageNum int, selectTime, area string) (hasNextPage bool, result []*SubPushList) {
+func (e *entnichePush) Datas(MQFW MongodbSim, PushMysql *mysql.Mysql, entId, userId int, pageNum int, selectTime, area string) (hasNextPage bool, result []*SubPushList) {
 	if pageNum < 1 {
 		pageNum = 1
 	}
@@ -98,7 +98,7 @@ func (e *entnichePush) Datas(MQFW mg.MongodbSim, PushMysql *mysql.Mysql, entId,
 	hasNextPage = len(result) >= pageSize
 	return
 }
-func (e *entnichePush) getDatasFromMysql(MQFW mg.MongodbSim, PushMysql *mysql.Mysql, entId, userId int, pageNum, myPageSize int, selectTime, area string, isLimit bool) (result []*SubPushList) {
+func (e *entnichePush) getDatasFromMysql(MQFW MongodbSim, PushMysql *mysql.Mysql, entId, userId int, pageNum, myPageSize int, selectTime, area string, isLimit bool) (result []*SubPushList) {
 	findSQL := "select id,date,infoid,isvisit,matchkeys,type,1 as isvip from pushentniche where entid=" + fmt.Sprint(entId) + " and userid=" + fmt.Sprint(userId)
 	findStr := ""
 	if selectTime != "" {
@@ -157,7 +157,7 @@ func (e *entnichePush) GetJyPushs(datas []map[string]interface{}) (pushCas []*Pu
 }
 
 //根据id取内容
-func (e *entnichePush) GetInfoByIds(MQFW mg.MongodbSim, pushCas []*PushCa) []*SubPushList {
+func (e *entnichePush) GetInfoByIds(MQFW MongodbSim, pushCas []*PushCa) []*SubPushList {
 	array := make([]*SubPushList, len(pushCas))
 	if len(pushCas) == 0 {
 		return array
@@ -197,10 +197,11 @@ func (e *entnichePush) GetInfoByIds(MQFW mg.MongodbSim, pushCas []*PushCa) []*Su
 		}
 	}
 	//mongodb bidding
-	mgo_ids := []bson.ObjectId{}
+	mgo_ids := []primitive.ObjectID{}
 	for _, v := range es_ids {
 		if infos[v] == nil {
-			mgo_ids = append(mgo_ids, bson.ObjectIdHex(v))
+			oid, _ := primitive.ObjectIDFromHex(v)
+			mgo_ids = append(mgo_ids, oid)
 		}
 	}
 	if len(mgo_ids) > 0 {
@@ -214,7 +215,7 @@ func (e *entnichePush) GetInfoByIds(MQFW mg.MongodbSim, pushCas []*PushCa) []*Su
 		}
 	}
 	//mongodb bidding_back
-	mgo_back_ids := []bson.ObjectId{}
+	mgo_back_ids := []primitive.ObjectID{}
 	for _, v := range mgo_ids {
 		if infos[BsonIdToSId(v)] == nil {
 			mgo_back_ids = append(mgo_back_ids, v)

+ 11 - 10
src/qfw/util/jy/subscribepush.go

@@ -3,16 +3,16 @@ package jy
 import (
 	"encoding/json"
 	"fmt"
-	mg "jy/mongodb"
 	"log"
 	. "qfw/util"
 	"qfw/util/elastic"
+	. "qfw/util/mongodb"
 	"qfw/util/mysql"
 	"qfw/util/redis"
 	"strings"
 	"time"
 
-	"gopkg.in/mgo.v2/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
 )
 
 const (
@@ -147,7 +147,7 @@ func (s *subscribePush) InfoFormat(p *PushCa, info *map[string]interface{}) *Sub
 	}
 }
 
-func (s *subscribePush) Datas(MQFW mg.MongodbSim, PushMysql *mysql.Mysql, userId string, pageNum int, selectTime, area string) (hasNextPage bool, result []*SubPushList) {
+func (s *subscribePush) Datas(MQFW MongodbSim, PushMysql *mysql.Mysql, userId string, pageNum int, selectTime, area string) (hasNextPage bool, result []*SubPushList) {
 	if userId == "" {
 		return
 	}
@@ -202,7 +202,7 @@ func (s *subscribePush) Datas(MQFW mg.MongodbSim, PushMysql *mysql.Mysql, userId
 	hasNextPage = len(result) >= pageSize
 	return
 }
-func (s *subscribePush) getDatasFromMysql(MQFW mg.MongodbSim, PushMysql *mysql.Mysql, userId string, pageNum, myPageSize int, selectTime, area string, isLimit bool) (result []*SubPushList) {
+func (s *subscribePush) getDatasFromMysql(MQFW MongodbSim, PushMysql *mysql.Mysql, userId string, pageNum, myPageSize int, selectTime, area string, isLimit bool) (result []*SubPushList) {
 	findSQL := "select id,date,infoid,isvisit,matchkeys,type,isvip from pushsubscribe where userid = '" + userId + "'"
 	findStr := ""
 	if selectTime != "" {
@@ -261,7 +261,7 @@ func (s *subscribePush) GetJyPushs(datas []map[string]interface{}) (pushCas []*P
 }
 
 //根据id取内容
-func (s *subscribePush) GetInfoByIds(MQFW mg.MongodbSim, pushCas []*PushCa) []*SubPushList {
+func (s *subscribePush) GetInfoByIds(MQFW MongodbSim, pushCas []*PushCa) []*SubPushList {
 	array := make([]*SubPushList, len(pushCas))
 	if len(pushCas) == 0 {
 		return array
@@ -301,10 +301,11 @@ func (s *subscribePush) GetInfoByIds(MQFW mg.MongodbSim, pushCas []*PushCa) []*S
 		}
 	}
 	//mongodb bidding
-	mgo_ids := []bson.ObjectId{}
+	mgo_ids := []primitive.ObjectID{}
 	for _, v := range es_ids {
 		if infos[v] == nil {
-			mgo_ids = append(mgo_ids, bson.ObjectIdHex(v))
+			oid, _ := primitive.ObjectIDFromHex(v)
+			mgo_ids = append(mgo_ids, oid)
 		}
 	}
 	if len(mgo_ids) > 0 {
@@ -318,7 +319,7 @@ func (s *subscribePush) GetInfoByIds(MQFW mg.MongodbSim, pushCas []*PushCa) []*S
 		}
 	}
 	//mongodb bidding_back
-	mgo_back_ids := []bson.ObjectId{}
+	mgo_back_ids := []primitive.ObjectID{}
 	for _, v := range mgo_ids {
 		if infos[BsonIdToSId(v)] == nil {
 			mgo_back_ids = append(mgo_back_ids, v)
@@ -346,7 +347,7 @@ func (s *subscribePush) GetInfoByIds(MQFW mg.MongodbSim, pushCas []*PushCa) []*S
 }
 
 //保存最近7天的数据到历史记录
-func (s *subscribePush) MakeHistoryDatas(MQFW mg.MongodbSim, PushMysql *mysql.Mysql, userId string, PushView func(userid, allquery, field string, pageNum, pageSize int) (keys []interface{}, list *[]map[string]interface{})) (bool, []*SubPushList) {
+func (s *subscribePush) MakeHistoryDatas(MQFW MongodbSim, PushMysql *mysql.Mysql, userId string, PushView func(userid, allquery, field string, pageNum, pageSize int) (keys []interface{}, list *[]map[string]interface{})) (bool, []*SubPushList) {
 	log.Println("匹配最近7天数据", userId)
 	field := `"_id","title","publishtime","toptype","subtype","type","area","city","s_subscopeclass","buyerclass","budget","bidamount"`
 	allquery := `{"range":{"publishtime":{"gt":%s}}}`
@@ -433,7 +434,7 @@ func (s *subscribePush) MakeHistoryDatas(MQFW mg.MongodbSim, PushMysql *mysql.My
 }
 
 //获取用户信息
-func (s *subscribePush) UserInfo(MQFW mg.MongodbSim, userId string) (*map[string]interface{}, int64) {
+func (s *subscribePush) UserInfo(MQFW MongodbSim, userId string) (*map[string]interface{}, int64) {
 	user, ok := MQFW.FindById("user", userId, `{"s_m_openid":1,"a_m_openid":1,"s_phone":1,"a_mergeorder":1,"o_jy":1,"l_firstpushtime":1,"i_vip_status":1,"l_vip_endtime":1,"o_vipjy":1}`)
 	if !ok || user == nil {
 		return nil, 0

+ 17 - 18
src/qfw/util/jy/user_merge.go

@@ -4,23 +4,22 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"jy/mongodb"
 	"log"
 	qutil "qfw/util"
+	. "qfw/util/mongodb"
 	"qfw/util/redis"
 	"regexp"
 	"strings"
 	"time"
 
 	"github.com/go-xweb/httpsession"
-	"gopkg.in/mgo.v2/bson"
 )
 
 var WxClientReg = regexp.MustCompile(`.*micromessenger.*`)
 var aboutTableRemove = []string{"pushspace", "pushspace_entniche", "pushspace_entniche_project", "pushspace_entniche_temp", "pushspace_entniche_wait", "pushspace_fail", "pushspace_project", "pushspace_statistic", "pushspace_temp", "pushspace_vip", "pushspace_fail"}
 
 //自动合并两个账户、其中一个为空(无订阅、无关注项目、无关注企业)
-func AutoMerge(mg mongodb.MongodbSim, sess *httpsession.Session) (bool, error) {
+func AutoMerge(mg MongodbSim, sess *httpsession.Session) (bool, error) {
 	userId := qutil.ObjToString(sess.Get("userId"))
 	if userId == "" {
 		return false, errors.New("未获取到userId")
@@ -36,12 +35,12 @@ func AutoMerge(mg mongodb.MongodbSim, sess *httpsession.Session) (bool, error) {
 	}
 	otherUser := &map[string]interface{}{}
 	if s_m_phone != "" {
-		otherUser, _ = mg.FindOneByField("user", bson.M{"i_appid": 2, "s_phone": s_m_phone, "s_m_phone": bson.M{"$ne": s_m_phone}}, nil)
+		otherUser, _ = mg.FindOneByField("user", map[string]interface{}{"i_appid": 2, "s_phone": s_m_phone, "s_m_phone": map[string]interface{}{"$ne": s_m_phone}}, nil)
 		if otherUser == nil || len(*otherUser) == 0 { //微信端合并 app端
 			return true, nil
 		}
 	} else {
-		otherUser, _ = mg.FindOneByField("user", bson.M{"i_appid": 2, "s_m_phone": s_phone, "s_phone": bson.M{"$ne": s_phone}}, nil)
+		otherUser, _ = mg.FindOneByField("user", map[string]interface{}{"i_appid": 2, "s_m_phone": s_phone, "s_phone": map[string]interface{}{"$ne": s_phone}}, nil)
 		if otherUser == nil || len(*otherUser) == 0 { //app端合并 微信端
 			return true, nil
 		}
@@ -59,21 +58,21 @@ func AutoMerge(mg mongodb.MongodbSim, sess *httpsession.Session) (bool, error) {
 			return
 		}
 		if t {
-			id := qutil.BsonIdToSId((*thisUser)["_id"])
-			if mg.Count("jylab_followent", bson.M{"s_userid": id}) != 0 {
+			id := BsonIdToSId((*thisUser)["_id"])
+			if mg.Count("jylab_followent", map[string]interface{}{"s_userid": id}) != 0 {
 				t = false
 			} else {
-				if mg.Count("follow_project", bson.M{"s_userid": id}) != 0 {
+				if mg.Count("follow_project", map[string]interface{}{"s_userid": id}) != 0 {
 					t = false
 				}
 			}
 		}
 		if o {
-			id := qutil.BsonIdToSId((*otherUser)["_id"])
-			if mg.Count("jylab_followent", bson.M{"s_userid": id}) != 0 {
+			id := BsonIdToSId((*otherUser)["_id"])
+			if mg.Count("jylab_followent", map[string]interface{}{"s_userid": id}) != 0 {
 				o = false
 			} else {
-				if mg.Count("follow_project", bson.M{"s_userid": id}) != 0 {
+				if mg.Count("follow_project", map[string]interface{}{"s_userid": id}) != 0 {
 					o = false
 				}
 			}
@@ -98,7 +97,7 @@ func AutoMerge(mg mongodb.MongodbSim, sess *httpsession.Session) (bool, error) {
 	if !MergeData(mg, finalUser, removeUser) {
 		return false, errors.New("数据库操作出错")
 	}
-	log.Printf("[%s,%s]账户自动合并 合并结果 %+v\n", qutil.BsonIdToSId((*finalUser)["_id"]), qutil.BsonIdToSId((*removeUser)["_id"]), finalUser)
+	log.Printf("[%s,%s]账户自动合并 合并结果 %+v\n", BsonIdToSId((*finalUser)["_id"]), BsonIdToSId((*removeUser)["_id"]), finalUser)
 	go MoveUser(mg, removeUser)
 	FlushSession(sess, finalUser, true)
 	return true, nil
@@ -115,7 +114,7 @@ func checNullkeyset(a_key interface{}) bool {
 	return false
 }
 
-func MergeData(mg mongodb.MongodbSim, saveRes, otherRes *map[string]interface{}) bool {
+func MergeData(mg MongodbSim, saveRes, otherRes *map[string]interface{}) bool {
 	saveResIsWx := qutil.ObjToString((*saveRes)["s_phone"]) == ""
 	for k, v := range *otherRes {
 		if _, ok := (*saveRes)[k]; !ok {
@@ -134,14 +133,14 @@ func MergeData(mg mongodb.MongodbSim, saveRes, otherRes *map[string]interface{})
 	}
 	(*saveRes)["i_ispush"] = 1
 	delete(*saveRes, "s_m_phone")
-	if !mg.UpdateById("user", qutil.BsonIdToSId((*saveRes)["_id"]), saveRes) {
+	if !mg.UpdateById("user", BsonIdToSId((*saveRes)["_id"]), saveRes) {
 		return false
 	}
 	return true
 }
 
 func FlushSession(sess *httpsession.Session, userData *map[string]interface{}, isWx bool) {
-	sess.Set("userId", qutil.BsonIdToSId((*userData)["_id"]))
+	sess.Set("userId", BsonIdToSId((*userData)["_id"]))
 	sess.Set("s_jpushid", (*userData)["s_jpushid"])
 	sess.Set("s_m_openid", (*userData)["s_m_openid"])
 	sess.Set("s_opushid", (*userData)["s_opushid"])
@@ -158,15 +157,15 @@ func FlushSession(sess *httpsession.Session, userData *map[string]interface{}, i
 	}
 }
 
-func MoveUser(mg mongodb.MongodbSim, otherRes *map[string]interface{}) {
+func MoveUser(mg MongodbSim, otherRes *map[string]interface{}) {
 	(*otherRes)["l_mergeData"] = time.Now().Unix()
-	userId := qutil.BsonIdToSId((*otherRes)["_id"])
+	userId := BsonIdToSId((*otherRes)["_id"])
 	(*otherRes)["s_userId"] = userId
 	if mg.Save("user_merge", otherRes) == "" {
 		log.Println("保存user_merge失败", otherRes)
 		return
 	}
-	if !mg.Del("user", map[string]interface{}{"_id": qutil.StringTOBsonId(userId)}) {
+	if !mg.Del("user", map[string]interface{}{"_id": StringTOBsonId(userId)}) {
 		log.Println("删除user信息失败", otherRes)
 		return
 	}

+ 1 - 1
src/qfw/util/mail/gmail.go

@@ -3,10 +3,10 @@ package mail
 import (
 	"crypto/tls"
 	"log"
+	"qfw/util"
 	"strings"
 	"sync"
 	"time"
-	"util"
 
 	"github.com/go-gomail/gomail"
 )

+ 41 - 0
src/qfw/util/rpc/credit.go

@@ -0,0 +1,41 @@
+// credit
+package rpc
+
+import (
+	"net/rpc"
+)
+
+//客户端远程调用封装
+type RpcCall struct {
+	Addr string //rpc服务地址
+}
+
+type CreditData struct {
+	Code       string //积分代码
+	Uid        string //用户ID
+	Umid       string //用户m_open_id
+	Num        int    //积分值
+	OtherParam map[string]interface{}
+}
+
+//增加积分
+func (rc *RpcCall) InCreadit(param *CreditData, replay *int) error {
+	client, err := rpc.DialHTTP("tcp", rc.Addr)
+	defer client.Close()
+	if err != nil {
+		return err
+	}
+	err = client.Call("CreditRpc.InCreadit", param, replay)
+	return err
+}
+
+//消费积分
+func (rc *RpcCall) OutCreadit(param *CreditData, replay *int) error {
+	client, err := rpc.DialHTTP("tcp", rc.Addr)
+	defer client.Close()
+	if err != nil {
+		return err
+	}
+	err = client.Call("CreditRpc.OutCreadit", param, replay)
+	return err
+}

+ 8 - 0
src/qfw/util/rpc/follow.go

@@ -0,0 +1,8 @@
+// follow
+package rpc
+
+type FollowData struct {
+	OpenId      string `json:"openid"`
+	Server      string `json:"server"`
+	Projectname string `json:"projectname"`
+}

+ 17 - 0
src/qfw/util/rpc/log.go

@@ -0,0 +1,17 @@
+// credit
+package rpc
+
+import (
+	"net/rpc"
+)
+
+//rpc日志采集
+func (rc *RpcCall) AccessLog(json string) error {
+	client, err := rpc.DialHTTP("tcp", rc.Addr)
+	defer client.Close()
+	if err != nil {
+		return err
+	}
+	err = client.Call("RpcAccessLog.Log", json, nil)
+	return err
+}

+ 102 - 0
src/qfw/util/rpc/options.go

@@ -0,0 +1,102 @@
+package rpc
+import (
+	"errors"
+	"math/rand"
+	"sync"
+	"time"
+)
+
+var (
+	errClosed   = errors.New("pool is closed")
+	errInvalid  = errors.New("invalid config")
+	errRejected = errors.New("connection is nil. rejecting")
+	errTargets  = errors.New("targets server is empty")
+)
+
+func init() {
+	rand.NewSource(time.Now().UnixNano())
+}
+
+//Options pool options
+type Options struct {
+	lock sync.RWMutex
+	//targets node
+	Targets *[]string
+	//targets channel
+	input chan *[]string
+
+	//InitTargets init targets
+	InitTargets []string
+	// init connection
+	InitCap int
+	// max connections
+	MaxCap       int
+	DialTimeout  time.Duration
+	IdleTimeout  time.Duration
+	ReadTimeout  time.Duration
+	WriteTimeout time.Duration
+}
+
+// Input is the input channel
+func (o *Options) Input() chan<- *[]string {
+	return o.input
+}
+
+// update targets
+func (o *Options) update() {
+	//init targets
+	o.Targets = &o.InitTargets
+
+	go func() {
+		for targets := range o.input {
+			if targets == nil {
+				continue
+			}
+
+			o.lock.Lock()
+			o.Targets = targets
+			o.lock.Unlock()
+		}
+	}()
+
+}
+
+// NewOptions returns a new newOptions instance with sane defaults.
+func NewOptions() *Options {
+	o := &Options{}
+	o.InitCap = 5
+	o.MaxCap = 100
+	o.DialTimeout = 5 * time.Second
+	o.ReadTimeout = 5 * time.Second
+	o.WriteTimeout = 5 * time.Second
+	o.IdleTimeout = 60 * time.Second
+	return o
+}
+
+// validate checks a Config instance.
+func (o *Options) validate() error {
+	if o.InitTargets == nil ||
+		o.InitCap <= 0 ||
+		o.MaxCap <= 0 ||
+		o.InitCap > o.MaxCap ||
+		o.DialTimeout == 0 ||
+		o.ReadTimeout == 0 ||
+		o.WriteTimeout == 0 {
+		return errInvalid
+	}
+	return nil
+}
+
+//nextTarget next target implement load balance
+func (o *Options) nextTarget() string {
+	o.lock.RLock()
+	defer o.lock.RUnlock()
+
+	tlen := len(*o.Targets)
+	if tlen <= 0 {
+		return ""
+	}
+
+	//rand server
+	return (*o.Targets)[rand.Int()%tlen]
+}

+ 44 - 0
src/qfw/util/rpc/order.go

@@ -0,0 +1,44 @@
+package rpc
+
+//数据导出线下支付
+type DateExportData struct {
+	ImgSrc    string `json:"imgSrc"`
+	OrderCode string `json:"orderCode"`
+}
+
+//创建订单
+type JyPayOrderParam struct {
+	Price       int                    `json:"price"`
+	ProductType string                 `json:"productType"`
+	Ip          string                 `json:"ip"`
+	Openid      string                 `json:"openid"`
+	PayWay      string                 `json:"payWay"`
+	Userid      string                 `json:"userid"`
+	Detail      map[string]interface{} `json:"detail"`
+}
+
+//修改订单
+type JyPayOrderChangeParam struct {
+	ProductType string `json:"productType"`
+	OrderCode   string `json:"orderCode"`
+	PayWay      string `json:"payWay"`
+	Openid      string `json:"openid"`
+	Ip          string `json:"ip"`
+}
+
+//关闭订单
+type JyPayOrderCloseParam struct {
+	ProductType string `json:"productType"`
+	OrderCode   string `json:"orderCode"`
+}
+
+/*
+code -1:失败
+err  错误信息
+*/
+type JyPayOrderResult struct {
+	Status    int    `json:"status"`
+	Err       string `json:"err"`
+	OrderCode string `json:"orderCode"`
+	PayStr    string `json:"payStr"`
+}

+ 23 - 0
src/qfw/util/rpc/push.go

@@ -0,0 +1,23 @@
+package rpc
+
+type PushData struct {
+	Mopenid  string
+	PushType map[string]string
+	Words    string
+}
+type FollowPush struct {
+	ProjectName string
+	ProjectCode string
+	InfoId      string
+	FollowId    string
+	UserId      string
+	OpenId      string
+	Flag        int
+}
+type FollowPushEnt struct {
+	Entname  string
+	InfoId   string
+	FollowId string
+	OpenId   string
+	Flag     int
+}

+ 28 - 0
src/qfw/util/rpc/rpc_test.go

@@ -0,0 +1,28 @@
+// rpc_test
+package rpc
+
+import (
+	"log"
+	"testing"
+)
+
+func Test_InCreadit(t *testing.T) {
+	rc := new(RpcCall)
+	rc.Addr = "127.0.0.1:8765"
+	otherParam := make(map[string]interface{})
+	otherParam["name"] = "zhangsan"
+	otherParam["age"] = 18
+	err := rc.InCreadit(&CreditData{Code: "1", Uid: "2", Num: 3, OtherParam: otherParam}, 0)
+	log.Println("err:", err)
+}
+
+func Test_OutCreadit(t *testing.T) {
+	rc := new(RpcCall)
+	rc.Addr = "127.0.0.1:8765"
+	otherParam := make(map[string]interface{})
+	otherParam["name"] = "zhangsan"
+	otherParam["age"] = 18
+	replay := 0
+	err := rc.OutCreadit(&CreditData{Code: "1", Uid: "2", Num: 3, OtherParam: otherParam}, &replay)
+	log.Println("err:", err)
+}

+ 71 - 0
src/qfw/util/rpc/rpccall.go

@@ -0,0 +1,71 @@
+package rpc
+
+import (
+	"encoding/json"
+	"log"
+	rpc "net/rpc"
+	"qfw/util"
+)
+
+/*通用的微信发模板消息方法
+ *成功:repl=="Y"
+ */
+func WxSendTmplMsg(address string, p *WxTmplMsg) (bool, error) {
+	defer util.Catch()
+	var repl RpcResult
+	client, err := rpc.DialHTTP("tcp", address)
+	if err != nil {
+		log.Println(p.OpenId, err)
+		return false, err
+	}
+	defer client.Close()
+	err = client.Call("WeiXinRpc.SendTmplMsg", p, &repl)
+	if err != nil {
+		log.Println(p.OpenId, err)
+		return false, err
+	}
+	return repl == "Y", nil
+}
+
+/*微信推送消息rpc接口
+ *成功:repl=="Y"
+ */
+func WxPush(address, serviceMethod string, p *NotifyMsg) (bool, string) {
+	defer util.Catch()
+	var repl RpcResult
+	client, err := rpc.DialHTTP("tcp", address)
+	if err != nil {
+		log.Println(p.Openid, err.Error())
+		return false, ""
+	}
+	defer client.Close()
+	err = client.Call(serviceMethod, p, &repl)
+	if err != nil {
+		log.Println(p.Openid, err.Error())
+		return false, ""
+	}
+	return repl == "Y", string(repl)
+}
+
+//app推送消息rpc接口
+func AppPush(address string, m map[string]interface{}) bool {
+	defer util.Catch()
+	var repl string
+	client, err := rpc.DialHTTP("tcp", address)
+	if err != nil {
+		log.Println(m, err.Error())
+		return false
+	}
+	defer client.Close()
+	b, err := json.Marshal(m)
+	if err != nil {
+		log.Println(m, err.Error())
+		return false
+	}
+	err = client.Call("Rpc.Push", b, &repl)
+	if err != nil {
+		log.Println(m, err.Error())
+		return false
+	}
+	return repl == "y"
+}

+ 214 - 0
src/qfw/util/rpc/rpcpool.go

@@ -0,0 +1,214 @@
+package rpc
+
+import (
+	"bufio"
+	"encoding/gob"
+	"fmt"
+	"io"
+	"net"
+	"net/rpc"
+	"sync"
+	"time"
+)
+
+//RPCPool pool info
+type RPCPool struct {
+	Mu          sync.Mutex
+	IdleTimeout time.Duration
+	conns       chan *rpcIdleConn
+	factory     func() (*rpc.Client, error)
+	close       func(*rpc.Client) error
+}
+
+type rpcIdleConn struct {
+	conn *rpc.Client
+	t    time.Time
+}
+
+//Get get from pool
+func (c *RPCPool) Get() (*rpc.Client, error) {
+	c.Mu.Lock()
+	conns := c.conns
+	c.Mu.Unlock()
+
+	if conns == nil {
+		return nil, errClosed
+	}
+	for {
+		select {
+		case wrapConn := <-conns:
+			if wrapConn == nil {
+				return nil, errClosed
+			}
+			//判断是否超时,超时则丢弃
+			if timeout := c.IdleTimeout; timeout > 0 {
+				if wrapConn.t.Add(timeout).Before(time.Now()) {
+					//丢弃并关闭该链接
+					c.close(wrapConn.conn)
+					continue
+				}
+			}
+			return wrapConn.conn, nil
+		case <-time.After(50 * time.Millisecond):
+			conn, err := c.factory()
+			if err != nil {
+				return nil, err
+			}
+			return conn, nil
+		}
+	}
+}
+
+//Put put back to pool
+func (c *RPCPool) Put(conn *rpc.Client) error {
+	if conn == nil {
+		return errRejected
+	}
+
+	c.Mu.Lock()
+	defer c.Mu.Unlock()
+
+	if c.conns == nil {
+		return c.close(conn)
+	}
+
+	select {
+	case c.conns <- &rpcIdleConn{conn: conn, t: time.Now()}:
+		return nil
+	default:
+		//连接池已满,直接关闭该链接
+		return c.close(conn)
+	}
+}
+
+//Close close all connection
+func (c *RPCPool) Close() {
+	c.Mu.Lock()
+	conns := c.conns
+	c.conns = nil
+	c.factory = nil
+	closeFun := c.close
+	c.close = nil
+	c.Mu.Unlock()
+
+	if conns == nil {
+		return
+	}
+
+	close(conns)
+	for wrapConn := range conns {
+		closeFun(wrapConn.conn)
+	}
+}
+
+//IdleCount idle connection count
+func (c *RPCPool) IdleCount() int {
+	c.Mu.Lock()
+	conns := c.conns
+	c.Mu.Unlock()
+	return len(conns)
+}
+
+//Codec ...
+type Codec struct {
+	Timeout time.Duration
+	Closer  io.ReadWriteCloser
+	Decoder *gob.Decoder
+	Encoder *gob.Encoder
+	EncBuf  *bufio.Writer
+}
+
+//WriteRequest ...
+func (c *Codec) WriteRequest(r *rpc.Request, body interface{}) (err error) {
+	if err = c.timeoutCoder(r, "write request"); err != nil {
+		return
+	}
+
+	if err = c.timeoutCoder(body, "write request body"); err != nil {
+		return
+	}
+
+	return c.EncBuf.Flush()
+}
+
+//ReadResponseHeader ...
+func (c *Codec) ReadResponseHeader(r *rpc.Response) error {
+	return c.Decoder.Decode(r)
+}
+
+//ReadResponseBody ...
+func (c *Codec) ReadResponseBody(body interface{}) error {
+	return c.Decoder.Decode(body)
+}
+
+//Close ...
+func (c *Codec) Close() error {
+	return c.Closer.Close()
+}
+
+func (c *Codec) timeoutCoder(e interface{}, msg string) error {
+	if c.Timeout < 0 {
+		c.Timeout = time.Second * 5
+	}
+
+	echan := make(chan error, 1)
+	go func() { echan <- c.Encoder.Encode(e) }()
+
+	select {
+	case e := <-echan:
+		return e
+	case <-time.After(c.Timeout):
+		return fmt.Errorf("Timeout %s", msg)
+	}
+}
+
+//NewRPCPool init rpc pool
+func NewRPCPool(o *Options) (*RPCPool, error) {
+	if err := o.validate(); err != nil {
+		return nil, err
+	}
+
+	//init pool
+	pool := &RPCPool{
+		conns: make(chan *rpcIdleConn, o.MaxCap),
+		factory: func() (*rpc.Client, error) {
+			target := o.nextTarget()
+			if target == "" {
+				return nil, errTargets
+			}
+
+			conn, err := net.DialTimeout("tcp", target, o.DialTimeout)
+			if err != nil {
+				return nil, err
+			}
+
+			encBuf := bufio.NewWriter(conn)
+			p := rpc.NewClientWithCodec(&Codec{
+				Closer:  conn,
+				Decoder: gob.NewDecoder(conn),
+				Encoder: gob.NewEncoder(encBuf),
+				EncBuf:  encBuf,
+				Timeout: o.WriteTimeout,
+			})
+
+			return p, err
+		},
+		close:       func(v *rpc.Client) error { return v.Close() },
+		IdleTimeout: o.IdleTimeout,
+	}
+
+	//danamic update targets
+	o.update()
+
+	//init make conns
+	for i := 0; i < o.InitCap; i++ {
+		conn, err := pool.factory()
+		if err != nil {
+			pool.Close()
+			return nil, err
+		}
+		pool.conns <- &rpcIdleConn{conn: conn, t: time.Now()}
+	}
+
+	return pool, nil
+}

+ 55 - 0
src/qfw/util/rpc/weixin.go

@@ -0,0 +1,55 @@
+package rpc
+
+type NotifyMsg struct {
+	Openid      string //用户id
+	Remark      string //消息内容,其他属性待加
+	Result      string //
+	Detail      string //
+	Title       string
+	Url         string
+	Date        string
+	Service     string
+	Color       string
+	DetailColor string
+	TplId       string
+}
+
+type RpcResult string
+
+//红包
+type BonusMsg struct {
+	Mchbillno   string `json:"mch_billno"`   //订单号
+	Sendname    string `json:"send_name"`    //红包发送者名称
+	Reopenid    string `json:"re_openid"`    //接受人openid
+	Totalamount int    `json:"total_amount"` //金额,单位分
+	Totalnum    int    `json:"total_num"`    //发放总人数
+	Wishing     string `json:"wishing"`      //祝福语128字以内
+	Actname     string `json:"act_name"`     //活动名称
+	Remark      string `json:"remark"`       //说明
+}
+type Article struct {
+	Title       string `json:"title"`
+	Description string `json:"description"`
+	PicUrl      string `json:"picurl"`
+	Url         string `json:"url"`
+}
+type News struct {
+	ToUser   string //用户id
+	Articles []Article
+}
+type CustomMsg struct {
+	Url  string
+	Data map[string]interface{}
+}
+
+type TmplItem struct {
+	Value string `json:"value,omitempty"`
+	Color string `json:"color,omitempty"`
+}
+
+type WxTmplMsg struct {
+	OpenId   string //用户id
+	Url      string
+	TplId    string
+	TmplData map[string]*TmplItem
+}