Skip to content

Commit 5775854

Browse files
committed
MarshalMsgpack()/UnmarshalMsgPack() [WIP]
1 parent 2e10679 commit 5775854

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
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
. "github.com/tarantool/go-tarantool"
11+
. "github.com/tarantool/go-tarantool/datetime"
1112
"github.com/tarantool/go-tarantool/test_helpers"
1213
"gopkg.in/vmihailenco/msgpack.v2"
1314
)
@@ -55,25 +56,27 @@ func assertDatetimeIsEqual(t *testing.T, tuples []interface{}, tm time.Time) {
5556
if len(tpl) != 1 {
5657
t.Fatalf("Unexpected return value body (tuple len = %d)", len(tpl))
5758
}
58-
if val, ok := tpl[dtIndex].(time.Time); !ok || !val.Equal(tm) {
59-
fmt.Println("Tuple: ", val)
59+
if val, ok := tpl[dtIndex].(DateTime); !ok || !val.ToTime().Equal(tm) {
60+
fmt.Println("Tuple: ", val.ToTime())
6061
fmt.Println("Expected:", tm)
61-
t.Fatalf("Unexpected return value body (tuple %d field)", dtIndex)
62+
t.Fatalf("Unexpected return value body (tuple %d field) %+v", dtIndex, val)
6263
}
6364
}
6465
}
6566

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

7376
// Select tuple with datetime.
7477
var offset uint32 = 0
7578
var limit uint32 = 1
76-
resp, err := conn.Select(space, index, offset, limit, IterEq, []interface{}{tm})
79+
resp, err := conn.Select(space, index, offset, limit, IterEq, []interface{}{&dt})
7780
if err != nil {
7881
t.Fatalf("Datetime select failed: %s", err.Error())
7982
}
@@ -83,7 +86,7 @@ func tupleInsertSelectDelete(t *testing.T, conn *Connection, tm time.Time) {
8386
assertDatetimeIsEqual(t, resp.Data, tm)
8487

8588
// Delete tuple with datetime.
86-
resp, err = conn.Delete(space, index, []interface{}{tm})
89+
resp, err = conn.Delete(space, index, []interface{}{&dt})
8790
if err != nil {
8891
t.Fatalf("Datetime delete failed: %s", err.Error())
8992
}
@@ -205,7 +208,8 @@ func TestDatetimeReplace(t *testing.T) {
205208
t.Fatalf("Time parse failed: %s", err)
206209
}
207210

208-
resp, err := conn.Replace(space, []interface{}{tm})
211+
dt := NewDateTime(tm)
212+
resp, err := conn.Replace(space, []interface{}{&dt})
209213
if err != nil {
210214
t.Fatalf("Datetime replace failed: %s", err)
211215
}
@@ -214,7 +218,7 @@ func TestDatetimeReplace(t *testing.T) {
214218
}
215219
assertDatetimeIsEqual(t, resp.Data, tm)
216220

217-
resp, err = conn.Select(space, index, 0, 1, IterEq, []interface{}{tm})
221+
resp, err = conn.Select(space, index, 0, 1, IterEq, []interface{}{&dt})
218222
if err != nil {
219223
t.Fatalf("Datetime select failed: %s", err)
220224
}
@@ -224,7 +228,7 @@ func TestDatetimeReplace(t *testing.T) {
224228
assertDatetimeIsEqual(t, resp.Data, tm)
225229

226230
// Delete tuple with datetime.
227-
_, err = conn.Delete(space, index, []interface{}{tm})
231+
_, err = conn.Delete(space, index, []interface{}{&dt})
228232
if err != nil {
229233
t.Fatalf("Datetime delete failed: %s", err.Error())
230234
}
@@ -398,19 +402,20 @@ func TestCustomEncodeDecodeTuple2(t *testing.T) {
398402

399403
// Setup: insert a value.
400404
tm := time.Unix(0, 0)
401-
_, err := conn.Insert(space, []interface{}{tm})
405+
dt := NewDateTime(tm)
406+
_, err := conn.Insert(space, []interface{}{&dt})
402407
if err != nil {
403408
t.Fatalf("Datetime insert failed: %s", err.Error())
404409
}
405410

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

412417
// Teardown: delete a value.
413-
_, err = conn.Delete(space, index, []interface{}{tm})
418+
_, err = conn.Delete(space, index, []interface{}{&dt})
414419
if err != nil {
415420
t.Fatalf("Datetime delete failed: %s", err.Error())
416421
}
@@ -426,20 +431,21 @@ func TestCustomEncodeDecodeTuple3(t *testing.T) {
426431

427432
// Setup: insert a value.
428433
tm := time.Unix(0, 0)
429-
_, err := conn.Insert(space, []interface{}{tm})
434+
dt := NewDateTime(tm)
435+
_, err := conn.Insert(space, []interface{}{&dt})
430436
if err != nil {
431437
t.Fatalf("Datetime insert failed: %s", err.Error())
432438
}
433439

434440
var tuples2 []Tuple2
435-
err = conn.SelectTyped("testDatetime_2", index, 0, 1, IterEq, []interface{}{1, tm}, &tuples2)
441+
err = conn.SelectTyped("testDatetime_2", index, 0, 1, IterEq, []interface{}{1, &dt}, &tuples2)
436442
if err != nil {
437443
t.Fatalf("Failed to SelectTyped: %s", err.Error())
438444
return
439445
}
440446

441447
// Teardown: delete a value.
442-
_, err = conn.Delete(space, index, []interface{}{tm})
448+
_, err = conn.Delete(space, index, []interface{}{&dt})
443449
if err != nil {
444450
t.Fatalf("Datetime delete failed: %s", err.Error())
445451
}
@@ -476,26 +482,27 @@ func TestCustomEncodeDecodeTuple5(t *testing.T) {
476482
conn := connectWithValidation(t)
477483
defer conn.Close()
478484

479-
dt := time.Unix(500, 1000)
480-
_, err := conn.Insert(space, []interface{}{dt})
485+
tm := time.Unix(500, 1000)
486+
dt := NewDateTime(tm)
487+
_, err := conn.Insert(space, []interface{}{&dt})
481488
if err != nil {
482489
t.Fatalf("Datetime insert failed: %s", err.Error())
483490
}
484491

485-
resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{dt})
492+
resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{&dt})
486493
if errSel != nil {
487494
t.Errorf("Failed to Select: %s", errSel.Error())
488495
}
489496
if tpl, ok := resp.Data[0].([]interface{}); !ok {
490497
t.Errorf("Unexpected body of Select")
491498
} else {
492-
if val, ok := tpl[0].(time.Time); !ok || !val.Equal(dt) {
499+
if val, ok := tpl[0].(DateTime); !ok || !val.ToTime().Equal(tm) {
493500
t.Fatalf("Unexpected body of Select")
494501
}
495502
}
496503

497504
// Teardown: delete a value.
498-
_, err = conn.Delete(space, index, []interface{}{dt})
505+
_, err = conn.Delete(space, index, []interface{}{&dt})
499506
if err != nil {
500507
t.Fatalf("Datetime delete failed: %s", err.Error())
501508
}

0 commit comments

Comments
 (0)