add uuid support in go
diff --git a/go.sum b/go.sum
index cfde584..3f616cf 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
diff --git a/lib/go/thrift/binary_protocol.go b/lib/go/thrift/binary_protocol.go
index c6fae0f..adbd66f 100644
--- a/lib/go/thrift/binary_protocol.go
+++ b/lib/go/thrift/binary_protocol.go
@@ -242,6 +242,11 @@
return p.WriteI64(ctx, int64(math.Float64bits(value)))
}
+func (p *TBinaryProtocol) WriteUuid(ctx context.Context, value Uuid) error {
+ _, err := p.trans.Write(value[:])
+ return NewTProtocolException(err)
+}
+
func (p *TBinaryProtocol) WriteString(ctx context.Context, value string) error {
e := p.WriteI32(ctx, int32(len(value)))
if e != nil {
@@ -453,6 +458,12 @@
return value, err
}
+func (p *TBinaryProtocol) ReadUuid(ctx context.Context) (value Uuid, err error) {
+ uuid := Uuid{}
+ err = p.readAll(ctx, uuid[0:16])
+ return uuid, err
+}
+
func (p *TBinaryProtocol) ReadString(ctx context.Context) (value string, err error) {
size, e := p.ReadI32(ctx)
if e != nil {
diff --git a/lib/go/thrift/compact_protocol.go b/lib/go/thrift/compact_protocol.go
index dc86fe6..7b96800 100644
--- a/lib/go/thrift/compact_protocol.go
+++ b/lib/go/thrift/compact_protocol.go
@@ -328,6 +328,11 @@
return NewTProtocolException(err)
}
+func (p *TCompactProtocol) WriteUuid(ctx context.Context, value Uuid) error {
+ _, err := p.trans.Write(value[:])
+ return NewTProtocolException(err)
+}
+
// Write a string to the wire with a varint size preceding.
func (p *TCompactProtocol) WriteString(ctx context.Context, value string) error {
_, e := p.writeVarint32(int32(len(value)))
@@ -621,6 +626,16 @@
return string(buf), NewTProtocolException(e)
}
+func (p *TCompactProtocol) ReadUuid(ctx context.Context) (value Uuid, err error) {
+ if buf, e := safeReadBytes(16, p.trans); e != nil {
+ return Uuid{}, NewTProtocolException(e)
+ } else {
+ var uuid Uuid
+ copy(uuid[:], buf)
+ return uuid, nil
+ }
+}
+
// Read a []byte from the wire.
func (p *TCompactProtocol) ReadBinary(ctx context.Context) (value []byte, err error) {
length, e := p.readVarint32()
diff --git a/lib/go/thrift/debug_protocol.go b/lib/go/thrift/debug_protocol.go
index c1f4fab..be071f4 100644
--- a/lib/go/thrift/debug_protocol.go
+++ b/lib/go/thrift/debug_protocol.go
@@ -234,6 +234,16 @@
}
return err
}
+
+func (tdp *TDebugProtocol) WriteUuid(ctx context.Context, value Uuid) error {
+ err := tdp.Delegate.WriteUuid(ctx, value)
+ tdp.logf("%sWriteUuid(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ if tdp.DuplicateTo != nil {
+ tdp.DuplicateTo.WriteUuid(ctx, value)
+ }
+ return err
+}
+
func (tdp *TDebugProtocol) WriteDouble(ctx context.Context, value float64) error {
err := tdp.Delegate.WriteDouble(ctx, value)
tdp.logf("%sWriteDouble(value=%#v) => %#v", tdp.LogPrefix, value, err)
@@ -411,6 +421,16 @@
}
return
}
+
+func (tdp *TDebugProtocol) ReadUuid(ctx context.Context) (value Uuid, err error) {
+ value, err = tdp.Delegate.ReadUuid(ctx)
+ tdp.logf("%sReadUuid() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ if tdp.DuplicateTo != nil {
+ tdp.DuplicateTo.WriteUuid(ctx, value)
+ }
+ return
+}
+
func (tdp *TDebugProtocol) ReadBinary(ctx context.Context) (value []byte, err error) {
value, err = tdp.Delegate.ReadBinary(ctx)
tdp.logf("%sReadBinary() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
diff --git a/lib/go/thrift/header_protocol.go b/lib/go/thrift/header_protocol.go
index 878041f..9a31b52 100644
--- a/lib/go/thrift/header_protocol.go
+++ b/lib/go/thrift/header_protocol.go
@@ -213,6 +213,10 @@
return p.protocol.WriteDouble(ctx, value)
}
+func (p *THeaderProtocol) WriteUuid(ctx context.Context, value Uuid) error {
+ return p.protocol.WriteUuid(ctx, value)
+}
+
func (p *THeaderProtocol) WriteString(ctx context.Context, value string) error {
return p.protocol.WriteString(ctx, value)
}
@@ -326,6 +330,10 @@
return p.protocol.ReadDouble(ctx)
}
+func (p *THeaderProtocol) ReadUuid(ctx context.Context) (value Uuid, err error) {
+ return p.protocol.ReadUuid(ctx)
+}
+
func (p *THeaderProtocol) ReadString(ctx context.Context) (value string, err error) {
return p.protocol.ReadString(ctx)
}
diff --git a/lib/go/thrift/json_protocol.go b/lib/go/thrift/json_protocol.go
index d248ecf..0867f3b 100644
--- a/lib/go/thrift/json_protocol.go
+++ b/lib/go/thrift/json_protocol.go
@@ -33,7 +33,6 @@
// JSON protocol implementation for thrift.
// Utilizes Simple JSON protocol
-//
type TJSONProtocol struct {
*TSimpleJSONProtocol
}
@@ -189,6 +188,10 @@
return p.OutputF64(v)
}
+func (p *TJSONProtocol) WriteUuid(ctx context.Context, uuid Uuid) error {
+ return p.WriteString(ctx, uuid.String())
+}
+
func (p *TJSONProtocol) WriteString(ctx context.Context, v string) error {
return p.OutputString(v)
}
@@ -379,6 +382,14 @@
return v, err
}
+func (p *TJSONProtocol) ReadUuid(ctx context.Context) (Uuid, error) {
+ if s, err := p.ReadString(ctx); err != nil {
+ return Uuid{}, err
+ } else {
+ return NewUuidFromString(s)
+ }
+}
+
func (p *TJSONProtocol) ReadString(ctx context.Context) (string, error) {
var v string
if err := p.ParsePreValue(); err != nil {
diff --git a/lib/go/thrift/protocol.go b/lib/go/thrift/protocol.go
index 647c0bd..afe35d7 100644
--- a/lib/go/thrift/protocol.go
+++ b/lib/go/thrift/protocol.go
@@ -51,6 +51,7 @@
WriteI64(ctx context.Context, value int64) error
WriteDouble(ctx context.Context, value float64) error
WriteString(ctx context.Context, value string) error
+ WriteUuid(ctx context.Context, value Uuid) error
WriteBinary(ctx context.Context, value []byte) error
ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error)
@@ -71,6 +72,7 @@
ReadI32(ctx context.Context) (value int32, err error)
ReadI64(ctx context.Context) (value int64, err error)
ReadDouble(ctx context.Context) (value float64, err error)
+ ReadUuid(ctx context.Context) (value Uuid, err error)
ReadString(ctx context.Context) (value string, err error)
ReadBinary(ctx context.Context) (value []byte, err error)
diff --git a/lib/go/thrift/simple_json_protocol.go b/lib/go/thrift/simple_json_protocol.go
index 5cefb60..8bb31a9 100644
--- a/lib/go/thrift/simple_json_protocol.go
+++ b/lib/go/thrift/simple_json_protocol.go
@@ -93,7 +93,6 @@
// This protocol produces/consumes a simple output format
// suitable for parsing by scripting languages. It should not be
// confused with the full-featured TJSONProtocol.
-//
type TSimpleJSONProtocol struct {
trans TTransport
@@ -316,6 +315,10 @@
return p.OutputString(v)
}
+func (p *TSimpleJSONProtocol) WriteUuid(ctx context.Context, v Uuid) error {
+ return p.WriteString(ctx, v.String())
+}
+
func (p *TSimpleJSONProtocol) WriteBinary(ctx context.Context, v []byte) error {
// JSON library only takes in a string,
// not an arbitrary byte array, to ensure bytes are transmitted
@@ -534,6 +537,14 @@
return v, err
}
+func (p *TSimpleJSONProtocol) ReadUuid(ctx context.Context) (Uuid, error) {
+ if s, err := p.ReadString(ctx); err != nil {
+ return Uuid{}, err
+ } else {
+ return NewUuidFromString(s)
+ }
+}
+
func (p *TSimpleJSONProtocol) ReadString(ctx context.Context) (string, error) {
var v string
if err := p.ParsePreValue(); err != nil {
diff --git a/lib/go/thrift/type.go b/lib/go/thrift/type.go
index 4292ffc..8c9046d 100644
--- a/lib/go/thrift/type.go
+++ b/lib/go/thrift/type.go
@@ -39,8 +39,9 @@
SET = 14
LIST = 15
UTF8 = 16
- UTF16 = 17
- //BINARY = 18 wrong and unusued
+ UUID = 17
+ //UTF16 = 17 wrong and unused
+ //BINARY = 18 wrong and unused
)
var typeNames = map[int]string{
@@ -58,7 +59,7 @@
SET: "SET",
LIST: "LIST",
UTF8: "UTF8",
- UTF16: "UTF16",
+ UUID: "UUID",
}
func (p TType) String() string {
diff --git a/lib/go/thrift/uuid.go b/lib/go/thrift/uuid.go
new file mode 100644
index 0000000..2065ba8
--- /dev/null
+++ b/lib/go/thrift/uuid.go
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "encoding/hex"
+ "errors"
+)
+
+// most of the code is copied from https://github.com/google/uuid in order to reduce dependency on
+// external libraries
+
+type Uuid [16]byte
+
+// from https://github.com/google/uuid/blob/512b657a42880af87e9f0d863aa6dccf3540d4ba/util.go#L18
+// xvalues returns the value of a byte as a hexadecimal digit or 255.
+var xvalues = [256]byte{
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
+ 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+}
+
+// from https://github.com/google/uuid/blob/512b657a42880af87e9f0d863aa6dccf3540d4ba/util.go#L39
+// xtob converts hex characters x1 and x2 into a byte.
+func xtob(x1, x2 byte) (byte, bool) {
+ b1 := xvalues[x1]
+ b2 := xvalues[x2]
+ return (b1 << 4) | b2, b1 != 255 && b2 != 255
+}
+
+func (uuid *Uuid) String() string {
+ var buf [36]byte
+ encodeHex(buf[:], uuid)
+ return string(buf[:])
+}
+
+// copied from https://github.com/google/uuid/blob/44b5fee7c49cf3bcdf723f106b36d56ef13ccc88/uuid.go#L200
+func encodeHex(dst []byte, uuid *Uuid) {
+ hex.Encode(dst, uuid[:4])
+ dst[8] = '-'
+ hex.Encode(dst[9:13], uuid[4:6])
+ dst[13] = '-'
+ hex.Encode(dst[14:18], uuid[6:8])
+ dst[18] = '-'
+ hex.Encode(dst[19:23], uuid[8:10])
+ dst[23] = '-'
+ hex.Encode(dst[24:], uuid[10:])
+}
+
+// from https://github.com/google/uuid/blob/44b5fee7c49cf3bcdf723f106b36d56ef13ccc88/uuid.go#L64
+func NewUuidFromString(s string) (Uuid, error) {
+ var uuid Uuid
+ if len(s) != 36 {
+ return uuid, errors.New("invalid uuid string")
+ }
+ if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
+ return uuid, errors.New("invalid UUID format")
+ }
+ for i, x := range [16]int{
+ 0, 2, 4, 6,
+ 9, 11,
+ 14, 16,
+ 19, 21,
+ 24, 26, 28, 30, 32, 34} {
+ v, ok := xtob(s[x], s[x+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ uuid[i] = v
+ }
+ return uuid, nil
+}
diff --git a/lib/go/thrift/uuid_test.go b/lib/go/thrift/uuid_test.go
new file mode 100644
index 0000000..bef9b41
--- /dev/null
+++ b/lib/go/thrift/uuid_test.go
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import "testing"
+
+func TestUuidString(t *testing.T) {
+ uuid, err := NewUuidFromString("12345678-1234-1234-1234-123456789012")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if uuid.String() != "12345678-1234-1234-1234-123456789012" {
+ t.Fatal("String() returned wrong value")
+ }
+}
+
+func TestUuidFromString(t *testing.T) {
+ uuid, err := NewUuidFromString("12345678-1234-1234-1234-123456789012")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if uuid != [16]byte{0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12} {
+ t.Fatal("NewUuidFromString returned wrong value")
+ }
+}
+
+func TestUuidFromStringShortCase(t *testing.T) {
+ _, err := NewUuidFromString("12345678-1234-1234-1234-12345678901")
+ if err == nil {
+ t.Fatal("NewUuidFromString should have returned an error")
+ }
+}
+
+func TestUuidFromStringLongCase(t *testing.T) {
+ _, err := NewUuidFromString("12345678-1234-1234-1234-1234567890123")
+ if err == nil {
+ t.Fatal("NewUuidFromString should have returned an error")
+ }
+}