Skip to content

Commit 7a1518f

Browse files
committed
MarshalMsgpack()/UnmarshalMsgPack() [TO SQUASH]
1 parent a78624c commit 7a1518f

File tree

2 files changed

+70
-62
lines changed

2 files changed

+70
-62
lines changed

datetime/datetime.go

+43-42
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ package datetime
1111

1212
import (
1313
"fmt"
14-
"io"
15-
"reflect"
1614
"time"
1715

1816
"encoding/binary"
@@ -70,10 +68,32 @@ const (
7068
tzOffsetSize = 2
7169
)
7270

73-
func encodeDatetime(e *msgpack.Encoder, v reflect.Value) error {
74-
var dt datetime
71+
const maxSize = secondsSize + nsecSize + tzIndexSize + tzOffsetSize
72+
73+
type DateTime struct {
74+
time time.Time
75+
}
76+
77+
// NewDateTime returns a pointer to a new datetime.DateTime that contains a
78+
// specified time.Time.
79+
func NewDateTime(t time.Time) DateTime {
80+
dt := new(DateTime)
81+
dt.time = t
82+
return *dt
83+
}
84+
85+
// ToTime returns a time.Time that DateTime contains.
86+
func (dtime *DateTime) ToTime() time.Time {
87+
return dtime.time
88+
}
89+
90+
var _ msgpack.Marshaler = (*DateTime)(nil)
91+
var _ msgpack.Unmarshaler = (*DateTime)(nil)
92+
93+
func (dtime *DateTime) MarshalMsgpack() ([]byte, error) {
94+
tm := dtime.ToTime()
7595

76-
tm := v.Interface().(time.Time)
96+
var dt datetime
7797
dt.seconds = tm.Unix()
7898
dt.nsec = int32(tm.Nanosecond())
7999
dt.tzIndex = 0 // It is not implemented, see gh-163.
@@ -85,56 +105,37 @@ func encodeDatetime(e *msgpack.Encoder, v reflect.Value) error {
85105
}
86106

87107
buf := make([]byte, bytesSize)
88-
binary.LittleEndian.PutUint64(buf[0:], uint64(dt.seconds))
89-
if bytesSize == 16 {
108+
binary.LittleEndian.PutUint64(buf, uint64(dt.seconds))
109+
if bytesSize == maxSize {
90110
binary.LittleEndian.PutUint32(buf[secondsSize:], uint32(dt.nsec))
91111
binary.LittleEndian.PutUint16(buf[secondsSize+nsecSize:], uint16(dt.tzOffset))
92112
binary.LittleEndian.PutUint16(buf[secondsSize+nsecSize+tzOffsetSize:], uint16(dt.tzIndex))
93113
}
94114

95-
_, err := e.Writer().Write(buf)
96-
if err != nil {
97-
return fmt.Errorf("msgpack: can't write bytes to encoder writer: %w", err)
98-
}
99-
100-
return nil
115+
return buf, nil
101116
}
102117

103-
func decodeDatetime(d *msgpack.Decoder, v reflect.Value) error {
104-
var dt datetime
105-
secondsBytes := make([]byte, secondsSize)
106-
n, err := d.Buffered().Read(secondsBytes)
107-
if err != nil {
108-
return fmt.Errorf("msgpack: can't read bytes on datetime's seconds decode: %w", err)
109-
}
110-
if n < secondsSize {
111-
return fmt.Errorf("msgpack: unexpected end of stream after %d datetime bytes", n)
112-
}
113-
dt.seconds = int64(binary.LittleEndian.Uint64(secondsBytes))
114-
tailSize := nsecSize + tzOffsetSize + tzIndexSize
115-
tailBytes := make([]byte, tailSize)
116-
n, err = d.Buffered().Read(tailBytes)
117-
// Part with nanoseconds, tzoffset and tzindex is optional, so we don't
118-
// need to handle an error here.
119-
if err != nil && err != io.EOF {
120-
return fmt.Errorf("msgpack: can't read bytes on datetime's tail decode: %w", err)
118+
func (tm *DateTime) UnmarshalMsgpack(b []byte) error {
119+
l := len(b)
120+
if l != maxSize && l != secondsSize {
121+
return fmt.Errorf("invalid data length: got %d, wanted %d or %d", len(b), secondsSize, maxSize)
121122
}
123+
124+
var dt datetime
125+
sec := binary.LittleEndian.Uint64(b)
126+
dt.seconds = int64(sec)
122127
dt.nsec = 0
123-
if err == nil {
124-
if n < tailSize {
125-
return fmt.Errorf("msgpack: can't read bytes on datetime's tail decode: %w", err)
126-
}
127-
dt.nsec = int32(binary.LittleEndian.Uint32(tailBytes[0:]))
128-
dt.tzOffset = int16(binary.LittleEndian.Uint16(tailBytes[nsecSize:]))
129-
dt.tzIndex = int16(binary.LittleEndian.Uint16(tailBytes[nsecSize+tzOffsetSize:]))
128+
if l == maxSize {
129+
dt.nsec = int32(binary.LittleEndian.Uint32(b[secondsSize:]))
130+
dt.tzOffset = int16(binary.LittleEndian.Uint16(b[secondsSize+nsecSize:]))
131+
dt.tzIndex = int16(binary.LittleEndian.Uint16(b[secondsSize+nsecSize+tzOffsetSize:]))
130132
}
131-
t := time.Unix(dt.seconds, int64(dt.nsec)).UTC()
132-
v.Set(reflect.ValueOf(t))
133+
tt := time.Unix(dt.seconds, int64(dt.nsec)).UTC()
134+
*tm = NewDateTime(tt)
133135

134136
return nil
135137
}
136138

137139
func init() {
138-
msgpack.Register(reflect.TypeOf((*time.Time)(nil)).Elem(), encodeDatetime, decodeDatetime)
139-
msgpack.RegisterExt(datetime_extId, (*time.Time)(nil))
140+
msgpack.RegisterExt(datetime_extId, &DateTime{})
140141
}

datetime/datetime_test.go

+27-20
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
. "github.com/tarantool/go-tarantool"
13+
. "github.com/tarantool/go-tarantool/datetime"
1314
"github.com/tarantool/go-tarantool/test_helpers"
1415
"gopkg.in/vmihailenco/msgpack.v2"
1516
)
@@ -57,25 +58,27 @@ func assertDatetimeIsEqual(t *testing.T, tuples []interface{}, tm time.Time) {
5758
if len(tpl) != 1 {
5859
t.Fatalf("Unexpected return value body (tuple len = %d)", len(tpl))
5960
}
60-
if val, ok := tpl[dtIndex].(time.Time); !ok || !val.Equal(tm) {
61-
fmt.Println("Tuple: ", val)
61+
if val, ok := tpl[dtIndex].(DateTime); !ok || !val.ToTime().Equal(tm) {
62+
fmt.Println("Tuple: ", val.ToTime())
6263
fmt.Println("Expected:", tm)
63-
t.Fatalf("Unexpected return value body (tuple %d field)", dtIndex)
64+
t.Fatalf("Unexpected return value body (tuple %d field) %+v", dtIndex, val)
6465
}
6566
}
6667
}
6768

6869
func tupleInsertSelectDelete(t *testing.T, conn *Connection, tm time.Time) {
70+
dt := NewDateTime(tm)
71+
6972
// Insert tuple with datetime.
70-
_, err := conn.Insert(space, []interface{}{tm})
73+
_, err := conn.Insert(space, []interface{}{&dt})
7174
if err != nil {
7275
t.Fatalf("Datetime insert failed: %s", err.Error())
7376
}
7477

7578
// Select tuple with datetime.
7679
var offset uint32 = 0
7780
var limit uint32 = 1
78-
resp, err := conn.Select(space, index, offset, limit, IterEq, []interface{}{tm})
81+
resp, err := conn.Select(space, index, offset, limit, IterEq, []interface{}{&dt})
7982
if err != nil {
8083
t.Fatalf("Datetime select failed: %s", err.Error())
8184
}
@@ -85,7 +88,7 @@ func tupleInsertSelectDelete(t *testing.T, conn *Connection, tm time.Time) {
8588
assertDatetimeIsEqual(t, resp.Data, tm)
8689

8790
// Delete tuple with datetime.
88-
resp, err = conn.Delete(space, index, []interface{}{tm})
91+
resp, err = conn.Delete(space, index, []interface{}{&dt})
8992
if err != nil {
9093
t.Fatalf("Datetime delete failed: %s", err.Error())
9194
}
@@ -210,7 +213,8 @@ func TestDatetimeReplace(t *testing.T) {
210213
t.Fatalf("Time parse failed: %s", err)
211214
}
212215

213-
resp, err := conn.Replace(space, []interface{}{tm})
216+
dt := NewDateTime(tm)
217+
resp, err := conn.Replace(space, []interface{}{&dt})
214218
if err != nil {
215219
t.Fatalf("Datetime replace failed: %s", err)
216220
}
@@ -219,7 +223,7 @@ func TestDatetimeReplace(t *testing.T) {
219223
}
220224
assertDatetimeIsEqual(t, resp.Data, tm)
221225

222-
resp, err = conn.Select(space, index, 0, 1, IterEq, []interface{}{tm})
226+
resp, err = conn.Select(space, index, 0, 1, IterEq, []interface{}{&dt})
223227
if err != nil {
224228
t.Fatalf("Datetime select failed: %s", err)
225229
}
@@ -229,7 +233,7 @@ func TestDatetimeReplace(t *testing.T) {
229233
assertDatetimeIsEqual(t, resp.Data, tm)
230234

231235
// Delete tuple with datetime.
232-
_, err = conn.Delete(space, index, []interface{}{tm})
236+
_, err = conn.Delete(space, index, []interface{}{&dt})
233237
if err != nil {
234238
t.Fatalf("Datetime delete failed: %s", err.Error())
235239
}
@@ -403,19 +407,20 @@ func TestCustomEncodeDecodeTuple2(t *testing.T) {
403407

404408
// Setup: insert a value.
405409
tm := time.Unix(0, 0)
406-
_, err := conn.Insert(space, []interface{}{tm})
410+
dt := NewDateTime(tm)
411+
_, err := conn.Insert(space, []interface{}{&dt})
407412
if err != nil {
408413
t.Fatalf("Datetime insert failed: %s", err.Error())
409414
}
410415

411416
var tuples []Tuple
412-
err = conn.SelectTyped("testDatetime_2", index, 0, 1, IterEq, []interface{}{1, tm}, &tuples)
417+
err = conn.SelectTyped("testDatetime_2", index, 0, 1, IterEq, []interface{}{1, &dt}, &tuples)
413418
if err != nil {
414419
t.Fatalf("Failed to SelectTyped(): %s", err.Error())
415420
}
416421

417422
// Teardown: delete a value.
418-
_, err = conn.Delete(space, index, []interface{}{tm})
423+
_, err = conn.Delete(space, index, []interface{}{&dt})
419424
if err != nil {
420425
t.Fatalf("Datetime delete failed: %s", err.Error())
421426
}
@@ -431,20 +436,21 @@ func TestCustomEncodeDecodeTuple3(t *testing.T) {
431436

432437
// Setup: insert a value.
433438
tm := time.Unix(0, 0)
434-
_, err := conn.Insert(space, []interface{}{tm})
439+
dt := NewDateTime(tm)
440+
_, err := conn.Insert(space, []interface{}{&dt})
435441
if err != nil {
436442
t.Fatalf("Datetime insert failed: %s", err.Error())
437443
}
438444

439445
var tuples2 []Tuple2
440-
err = conn.SelectTyped("testDatetime_2", index, 0, 1, IterEq, []interface{}{1, tm}, &tuples2)
446+
err = conn.SelectTyped("testDatetime_2", index, 0, 1, IterEq, []interface{}{1, &dt}, &tuples2)
441447
if err != nil {
442448
t.Fatalf("Failed to SelectTyped: %s", err.Error())
443449
return
444450
}
445451

446452
// Teardown: delete a value.
447-
_, err = conn.Delete(space, index, []interface{}{tm})
453+
_, err = conn.Delete(space, index, []interface{}{&dt})
448454
if err != nil {
449455
t.Fatalf("Datetime delete failed: %s", err.Error())
450456
}
@@ -481,26 +487,27 @@ func TestCustomEncodeDecodeTuple5(t *testing.T) {
481487
conn := connectWithValidation(t)
482488
defer conn.Close()
483489

484-
dt := time.Unix(500, 1000)
485-
_, err := conn.Insert(space, []interface{}{dt})
490+
tm := time.Unix(500, 1000)
491+
dt := NewDateTime(tm)
492+
_, err := conn.Insert(space, []interface{}{&dt})
486493
if err != nil {
487494
t.Fatalf("Datetime insert failed: %s", err.Error())
488495
}
489496

490-
resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{dt})
497+
resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{&dt})
491498
if errSel != nil {
492499
t.Errorf("Failed to Select: %s", errSel.Error())
493500
}
494501
if tpl, ok := resp.Data[0].([]interface{}); !ok {
495502
t.Errorf("Unexpected body of Select")
496503
} else {
497-
if val, ok := tpl[0].(time.Time); !ok || !val.Equal(dt) {
504+
if val, ok := tpl[0].(DateTime); !ok || !val.ToTime().Equal(tm) {
498505
t.Fatalf("Unexpected body of Select")
499506
}
500507
}
501508

502509
// Teardown: delete a value.
503-
_, err = conn.Delete(space, index, []interface{}{dt})
510+
_, err = conn.Delete(space, index, []interface{}{&dt})
504511
if err != nil {
505512
t.Fatalf("Datetime delete failed: %s", err.Error())
506513
}

0 commit comments

Comments
 (0)