diff --git a/container/gtype/bool.go b/container/gtype/bool.go index d2ee9c0dc..0c52d0352 100644 --- a/container/gtype/bool.go +++ b/container/gtype/bool.go @@ -59,3 +59,12 @@ func (v *Bool) Cas(old, new bool) bool { } return atomic.CompareAndSwapInt32(&v.value, oldInt32, newInt32) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Bool) MarshalJSON() ([]byte, error) { + if v.Val() { + return []byte("true"), nil + } else { + return []byte("false"), nil + } +} diff --git a/container/gtype/byte.go b/container/gtype/byte.go index bd61f8576..1a6c27baf 100644 --- a/container/gtype/byte.go +++ b/container/gtype/byte.go @@ -7,6 +7,8 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" + "strconv" "sync/atomic" ) @@ -49,3 +51,8 @@ func (v *Byte) Add(delta byte) (new byte) { func (v *Byte) Cas(old, new byte) bool { return atomic.CompareAndSwapInt32(&v.value, int32(old), int32(new)) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Byte) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.FormatUint(uint64(v.Val()), 10)), nil +} diff --git a/container/gtype/bytes.go b/container/gtype/bytes.go index 4476b3d85..997f16dc5 100644 --- a/container/gtype/bytes.go +++ b/container/gtype/bytes.go @@ -6,7 +6,11 @@ package gtype -import "sync/atomic" +import ( + "encoding/base64" + "github.com/gogf/gf/util/gconv" + "sync/atomic" +) type Bytes struct { value atomic.Value @@ -42,3 +46,11 @@ func (v *Bytes) Val() []byte { } return nil } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Bytes) MarshalJSON() ([]byte, error) { + val := v.Val() + dst := make([]byte, base64.StdEncoding.EncodedLen(len(val))) + base64.StdEncoding.Encode(dst, val) + return gconv.UnsafeStrToBytes(`"` + gconv.UnsafeBytesToStr(dst) + `"`), nil +} diff --git a/container/gtype/float32.go b/container/gtype/float32.go index 2b3759992..14045ff6a 100644 --- a/container/gtype/float32.go +++ b/container/gtype/float32.go @@ -7,7 +7,9 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" "math" + "strconv" "sync/atomic" "unsafe" ) @@ -62,3 +64,8 @@ func (v *Float32) Add(delta float32) (new float32) { func (v *Float32) Cas(old, new float32) bool { return atomic.CompareAndSwapUint32(&v.value, uint32(old), uint32(new)) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Float32) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.FormatFloat(float64(v.Val()), 'f', -1, 32)), nil +} diff --git a/container/gtype/float64.go b/container/gtype/float64.go index e720c5e59..aeb73976a 100644 --- a/container/gtype/float64.go +++ b/container/gtype/float64.go @@ -7,7 +7,9 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" "math" + "strconv" "sync/atomic" "unsafe" ) @@ -62,3 +64,8 @@ func (v *Float64) Add(delta float64) (new float64) { func (v *Float64) Cas(old, new float64) bool { return atomic.CompareAndSwapUint64(&v.value, uint64(old), uint64(new)) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Float64) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.FormatFloat(v.Val(), 'f', -1, 64)), nil +} diff --git a/container/gtype/gtype.go b/container/gtype/gtype.go index 0b5804ffb..795d36beb 100644 --- a/container/gtype/gtype.go +++ b/container/gtype/gtype.go @@ -7,8 +7,10 @@ // Package gtype provides kinds of high performance and concurrent-safe basic variable types. package gtype +// Type is alias of Interface. type Type = Interface +// New is alias of NewInterface. // See NewInterface. func New(value ...interface{}) *Type { return NewInterface(value...) diff --git a/container/gtype/int.go b/container/gtype/int.go index 984268b89..d6d6acad5 100644 --- a/container/gtype/int.go +++ b/container/gtype/int.go @@ -7,6 +7,8 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" + "strconv" "sync/atomic" ) @@ -49,3 +51,8 @@ func (v *Int) Add(delta int) (new int) { func (v *Int) Cas(old, new int) bool { return atomic.CompareAndSwapInt64(&v.value, int64(old), int64(new)) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Int) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.Itoa(v.Val())), nil +} diff --git a/container/gtype/int32.go b/container/gtype/int32.go index 9c6407625..e102f8cc3 100644 --- a/container/gtype/int32.go +++ b/container/gtype/int32.go @@ -7,6 +7,8 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" + "strconv" "sync/atomic" ) @@ -49,3 +51,8 @@ func (v *Int32) Add(delta int32) (new int32) { func (v *Int32) Cas(old, new int32) bool { return atomic.CompareAndSwapInt32(&v.value, old, new) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Int32) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.Itoa(int(v.Val()))), nil +} diff --git a/container/gtype/int64.go b/container/gtype/int64.go index 6c961efb1..1faaa9083 100644 --- a/container/gtype/int64.go +++ b/container/gtype/int64.go @@ -7,6 +7,8 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" + "strconv" "sync/atomic" ) @@ -49,3 +51,8 @@ func (v *Int64) Add(delta int64) (new int64) { func (v *Int64) Cas(old, new int64) bool { return atomic.CompareAndSwapInt64(&v.value, old, new) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Int64) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.FormatInt(v.Val(), 10)), nil +} diff --git a/container/gtype/interface.go b/container/gtype/interface.go index 92a5ce35f..e262ea846 100644 --- a/container/gtype/interface.go +++ b/container/gtype/interface.go @@ -7,6 +7,7 @@ package gtype import ( + "encoding/json" "sync/atomic" ) @@ -41,3 +42,8 @@ func (v *Interface) Set(value interface{}) (old interface{}) { func (v *Interface) Val() interface{} { return v.value.Load() } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Interface) MarshalJSON() ([]byte, error) { + return json.Marshal(v.Val()) +} diff --git a/container/gtype/string.go b/container/gtype/string.go index 7fe9986bd..b7dfd0da0 100644 --- a/container/gtype/string.go +++ b/container/gtype/string.go @@ -7,6 +7,7 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" "sync/atomic" ) @@ -44,3 +45,8 @@ func (v *String) Val() string { } return "" } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *String) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(`"` + v.Val() + `"`), nil +} diff --git a/container/gtype/uint.go b/container/gtype/uint.go index 3a07e0254..ad0ea65d8 100644 --- a/container/gtype/uint.go +++ b/container/gtype/uint.go @@ -7,6 +7,8 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" + "strconv" "sync/atomic" ) @@ -49,3 +51,8 @@ func (v *Uint) Add(delta uint) (new uint) { func (v *Uint) Cas(old, new uint) bool { return atomic.CompareAndSwapUint64(&v.value, uint64(old), uint64(new)) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Uint) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.FormatUint(uint64(v.Val()), 10)), nil +} diff --git a/container/gtype/uint32.go b/container/gtype/uint32.go index ee68dcf44..3cc109d32 100644 --- a/container/gtype/uint32.go +++ b/container/gtype/uint32.go @@ -7,6 +7,8 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" + "strconv" "sync/atomic" ) @@ -49,3 +51,8 @@ func (v *Uint32) Add(delta uint32) (new uint32) { func (v *Uint32) Cas(old, new uint32) bool { return atomic.CompareAndSwapUint32(&v.value, old, new) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Uint32) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.FormatUint(uint64(v.Val()), 10)), nil +} diff --git a/container/gtype/uint64.go b/container/gtype/uint64.go index bfb0c94bc..fca77c1c6 100644 --- a/container/gtype/uint64.go +++ b/container/gtype/uint64.go @@ -7,6 +7,8 @@ package gtype import ( + "github.com/gogf/gf/util/gconv" + "strconv" "sync/atomic" ) @@ -49,3 +51,8 @@ func (v *Uint64) Add(delta uint64) (new uint64) { func (v *Uint64) Cas(old, new uint64) bool { return atomic.CompareAndSwapUint64(&v.value, old, new) } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (v *Uint64) MarshalJSON() ([]byte, error) { + return gconv.UnsafeStrToBytes(strconv.FormatUint(v.Val(), 10)), nil +} diff --git a/container/gtype/z_bench_test.go b/container/gtype/z_bench_basic_test.go similarity index 92% rename from container/gtype/z_bench_test.go rename to container/gtype/z_bench_basic_test.go index d9570f631..22ac9abfc 100644 --- a/container/gtype/z_bench_test.go +++ b/container/gtype/z_bench_basic_test.go @@ -16,18 +16,19 @@ import ( "github.com/gogf/gf/encoding/gbinary" ) -var it = NewInt() -var it32 = NewInt32() -var it64 = NewInt64() -var uit = NewUint() -var uit32 = NewUint32() -var uit64 = NewUint64() -var bl = NewBool() -var bytes = NewBytes() -var str = NewString() -var inf = NewInterface() - -var at = atomic.Value{} +var ( + it = NewInt() + it32 = NewInt32() + it64 = NewInt64() + uit = NewUint() + uit32 = NewUint32() + uit64 = NewUint64() + bl = NewBool() + bytes = NewBytes() + str = NewString() + inf = NewInterface() + at = atomic.Value{} +) func BenchmarkInt_Set(b *testing.B) { for i := 0; i < b.N; i++ { diff --git a/container/gtype/z_bench_json_test.go b/container/gtype/z_bench_json_test.go new file mode 100644 index 000000000..db43ae7db --- /dev/null +++ b/container/gtype/z_bench_json_test.go @@ -0,0 +1,103 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".+\_Json" -benchmem + +package gtype_test + +import ( + "encoding/json" + "github.com/gogf/gf/container/gtype" + "testing" +) + +var ( + vBool = gtype.NewBool() + vByte = gtype.NewByte() + vBytes = gtype.NewBytes() + vFloat32 = gtype.NewFloat32() + vFloat64 = gtype.NewFloat64() + vInt = gtype.NewInt() + vInt32 = gtype.NewInt32() + vInt64 = gtype.NewInt64() + vInterface = gtype.NewInterface() + vString = gtype.NewString() + vUint = gtype.NewUint() + vUint32 = gtype.NewUint32() + vUint64 = gtype.NewUint64() +) + +func Benchmark_Bool_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vBool) + } +} + +func Benchmark_Byte_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vByte) + } +} + +func Benchmark_Bytes_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vBytes) + } +} + +func Benchmark_Float32_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vFloat32) + } +} + +func Benchmark_Float64_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vFloat64) + } +} + +func Benchmark_Int_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vInt) + } +} + +func Benchmark_Int32_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vInt32) + } +} + +func Benchmark_Int64_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vInt64) + } +} + +func Benchmark_Interface_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vInterface) + } +} + +func Benchmark_String_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vString) + } +} + +func Benchmark_Uint_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vUint) + } +} + +func Benchmark_Uint32_Json(b *testing.B) { + for i := 0; i < b.N; i++ { + json.Marshal(vUint64) + } +} diff --git a/container/gtype/z_unit_test.go b/container/gtype/z_unit_test.go index bd9675057..fc242d6db 100644 --- a/container/gtype/z_unit_test.go +++ b/container/gtype/z_unit_test.go @@ -7,6 +7,7 @@ package gtype_test import ( + "encoding/json" "sync" "testing" @@ -35,6 +36,24 @@ func Test_Bool(t *testing.T) { i2 := gtype.NewBool() gtest.AssertEQ(i2.Val(), false) }) + + gtest.Case(t, func() { + i := gtype.NewBool(true) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) + + gtest.Case(t, func() { + i := gtype.NewBool(false) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Byte(t *testing.T) { @@ -59,6 +78,14 @@ func Test_Byte(t *testing.T) { i1 := gtype.NewByte() gtest.AssertEQ(i1.Val(), byte(0)) }) + gtest.Case(t, func() { + i := gtype.NewByte(254) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Bytes(t *testing.T) { @@ -72,6 +99,14 @@ func Test_Bytes(t *testing.T) { i1 := gtype.NewBytes() gtest.AssertEQ(i1.Val(), nil) }) + gtest.Case(t, func() { + i := gtype.NewBytes([]byte("i love gf")) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_String(t *testing.T) { @@ -85,6 +120,14 @@ func Test_String(t *testing.T) { i1 := gtype.NewString() gtest.AssertEQ(i1.Val(), "") }) + gtest.Case(t, func() { + i := gtype.NewString("i love gf") + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Interface(t *testing.T) { @@ -100,6 +143,14 @@ func Test_Interface(t *testing.T) { i1 := gtype.New() gtest.AssertEQ(i1.Val(), nil) }) + gtest.Case(t, func() { + i := gtype.New("i love gf") + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Float32(t *testing.T) { @@ -125,6 +176,14 @@ func Test_Float32(t *testing.T) { i1 := gtype.NewFloat32() gtest.AssertEQ(i1.Val(), float32(0)) }) + gtest.Case(t, func() { + i := gtype.NewFloat32(3.333) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Float64(t *testing.T) { @@ -150,6 +209,14 @@ func Test_Float64(t *testing.T) { i1 := gtype.NewFloat64() gtest.AssertEQ(i1.Val(), float64(0)) }) + gtest.Case(t, func() { + i := gtype.NewFloat64(3.333) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Int(t *testing.T) { @@ -174,6 +241,14 @@ func Test_Int(t *testing.T) { i1 := gtype.NewInt() gtest.AssertEQ(i1.Val(), 0) }) + gtest.Case(t, func() { + i := gtype.NewInt(666) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Int32(t *testing.T) { @@ -198,6 +273,14 @@ func Test_Int32(t *testing.T) { i1 := gtype.NewInt32() gtest.AssertEQ(i1.Val(), int32(0)) }) + gtest.Case(t, func() { + i := gtype.NewInt32(666) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Int64(t *testing.T) { @@ -222,6 +305,14 @@ func Test_Int64(t *testing.T) { i1 := gtype.NewInt64() gtest.AssertEQ(i1.Val(), int64(0)) }) + gtest.Case(t, func() { + i := gtype.NewInt64(666) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Uint(t *testing.T) { @@ -246,6 +337,14 @@ func Test_Uint(t *testing.T) { i1 := gtype.NewUint() gtest.AssertEQ(i1.Val(), uint(0)) }) + gtest.Case(t, func() { + i := gtype.NewUint(666) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Uint32(t *testing.T) { @@ -270,6 +369,14 @@ func Test_Uint32(t *testing.T) { i1 := gtype.NewUint32() gtest.AssertEQ(i1.Val(), uint32(0)) }) + gtest.Case(t, func() { + i := gtype.NewUint32(666) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } func Test_Uint64(t *testing.T) { @@ -294,4 +401,12 @@ func Test_Uint64(t *testing.T) { i1 := gtype.NewUint64() gtest.AssertEQ(i1.Val(), uint64(0)) }) + gtest.Case(t, func() { + i := gtype.NewUint64(666) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + gtest.Assert(err1, nil) + gtest.Assert(err2, nil) + gtest.Assert(b1, b2) + }) } diff --git a/encoding/gbase64/gbase64.go b/encoding/gbase64/gbase64.go index 77db9cdb4..409780b66 100644 --- a/encoding/gbase64/gbase64.go +++ b/encoding/gbase64/gbase64.go @@ -9,6 +9,7 @@ package gbase64 import ( "encoding/base64" + "github.com/gogf/gf/util/gconv" ) // Encode encodes bytes with BASE64 algorithm. @@ -32,7 +33,7 @@ func EncodeString(src string) string { // EncodeToString encodes bytes to string with BASE64 algorithm. func EncodeToString(src []byte) string { - return string(Encode(src)) + return gconv.UnsafeBytesToStr(Encode(src)) } // DecodeString decodes string with BASE64 algorithm. @@ -43,5 +44,5 @@ func DecodeString(str string) ([]byte, error) { // DecodeString decodes string with BASE64 algorithm. func DecodeToString(str string) (string, error) { b, err := DecodeString(str) - return string(b), err + return gconv.UnsafeBytesToStr(b), err } diff --git a/encoding/gbinary/gbinary_le.go b/encoding/gbinary/gbinary_le.go index 2b236bfa9..07632b3ae 100644 --- a/encoding/gbinary/gbinary_le.go +++ b/encoding/gbinary/gbinary_le.go @@ -135,7 +135,7 @@ func LeEncodeInt8(i int8) []byte { } func LeEncodeUint8(i uint8) []byte { - return []byte{byte(i)} + return []byte{i} } func LeEncodeInt16(i int16) []byte { diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 4b8f4afac..84ebe7968 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -114,7 +114,7 @@ func Byte(i interface{}) byte { if v, ok := i.(byte); ok { return v } - return byte(Uint8(i)) + return Uint8(i) } // Bytes converts to []byte. @@ -155,7 +155,7 @@ func String(i interface{}) string { } switch value := i.(type) { case int: - return strconv.FormatInt(int64(value), 10) + return strconv.Itoa(value) case int8: return strconv.Itoa(int(value)) case int16: @@ -163,7 +163,7 @@ func String(i interface{}) string { case int32: return strconv.Itoa(int(value)) case int64: - return strconv.FormatInt(int64(value), 10) + return strconv.FormatInt(value, 10) case uint: return strconv.FormatUint(uint64(value), 10) case uint8: @@ -173,7 +173,7 @@ func String(i interface{}) string { case uint32: return strconv.FormatUint(uint64(value), 10) case uint64: - return strconv.FormatUint(uint64(value), 10) + return strconv.FormatUint(value, 10) case float32: return strconv.FormatFloat(float64(value), 'f', -1, 32) case float64: diff --git a/util/gconv/gconv_unsafe.go b/util/gconv/gconv_unsafe.go index 08f8b3c6e..f5d327481 100644 --- a/util/gconv/gconv_unsafe.go +++ b/util/gconv/gconv_unsafe.go @@ -9,11 +9,15 @@ package gconv import "unsafe" // UnsafeStrToBytes converts string to []byte without memory copy. +// Note that, if you completely sure you will never use in the feature, +// you can use this unsafe function to implement type conversion in high performance. func UnsafeStrToBytes(s string) []byte { return *(*[]byte)(unsafe.Pointer(&s)) } // UnsafeBytesToStr converts []byte to string without memory copy. +// Note that, if you completely sure you will never use in the feature, +// you can use this unsafe function to implement type conversion in high performance. func UnsafeBytesToStr(b []byte) string { return *(*string)(unsafe.Pointer(&b)) }