140 lines
3.6 KiB
Go
140 lines
3.6 KiB
Go
|
package column
|
||
|
|
||
|
import (
|
||
|
"encoding/hex"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
|
||
|
"github.com/kshvakov/clickhouse/lib/binary"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
UUIDLen = 16
|
||
|
NullUUID = "00000000-0000-0000-0000-000000000000"
|
||
|
)
|
||
|
|
||
|
var ErrInvalidUUIDFormat = errors.New("invalid UUID format")
|
||
|
|
||
|
type UUID struct {
|
||
|
base
|
||
|
scanType reflect.Type
|
||
|
}
|
||
|
|
||
|
func (*UUID) Read(decoder *binary.Decoder) (interface{}, error) {
|
||
|
src, err := decoder.Fixed(UUIDLen)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
src = swap(src)
|
||
|
|
||
|
var uuid [36]byte
|
||
|
{
|
||
|
hex.Encode(uuid[:], src[:4])
|
||
|
uuid[8] = '-'
|
||
|
hex.Encode(uuid[9:13], src[4:6])
|
||
|
uuid[13] = '-'
|
||
|
hex.Encode(uuid[14:18], src[6:8])
|
||
|
uuid[18] = '-'
|
||
|
hex.Encode(uuid[19:23], src[8:10])
|
||
|
uuid[23] = '-'
|
||
|
hex.Encode(uuid[24:], src[10:])
|
||
|
}
|
||
|
return string(uuid[:]), nil
|
||
|
}
|
||
|
|
||
|
func (u *UUID) Write(encoder *binary.Encoder, v interface{}) (err error) {
|
||
|
var uuid []byte
|
||
|
switch v := v.(type) {
|
||
|
case string:
|
||
|
if uuid, err = uuid2bytes(v); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
case []byte:
|
||
|
if len(v) != UUIDLen {
|
||
|
return fmt.Errorf("invalid raw UUID len '%s' (expected %d, got %d)", uuid, UUIDLen, len(uuid))
|
||
|
}
|
||
|
uuid = make([]byte, 16)
|
||
|
copy(uuid, v)
|
||
|
default:
|
||
|
return &ErrUnexpectedType{
|
||
|
T: v,
|
||
|
Column: u,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uuid = swap(uuid)
|
||
|
|
||
|
if _, err := encoder.Write(uuid); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func swap(src []byte) []byte {
|
||
|
_ = src[15]
|
||
|
src[0], src[7] = src[7], src[0]
|
||
|
src[1], src[6] = src[6], src[1]
|
||
|
src[2], src[5] = src[5], src[2]
|
||
|
src[3], src[4] = src[4], src[3]
|
||
|
src[8], src[15] = src[15], src[8]
|
||
|
src[9], src[14] = src[14], src[9]
|
||
|
src[10], src[13] = src[13], src[10]
|
||
|
src[11], src[12] = src[12], src[11]
|
||
|
return src
|
||
|
}
|
||
|
|
||
|
func uuid2bytes(str string) ([]byte, error) {
|
||
|
var uuid [16]byte
|
||
|
strLength := len(str)
|
||
|
if strLength == 0 {
|
||
|
str = NullUUID
|
||
|
} else if strLength != 36 {
|
||
|
return nil, ErrInvalidUUIDFormat
|
||
|
}
|
||
|
if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' {
|
||
|
return nil, ErrInvalidUUIDFormat
|
||
|
}
|
||
|
for i, x := range [16]int{
|
||
|
0, 2, 4, 6,
|
||
|
9, 11, 14, 16,
|
||
|
19, 21, 24, 26,
|
||
|
28, 30, 32, 34,
|
||
|
} {
|
||
|
if v, ok := xtob(str[x], str[x+1]); !ok {
|
||
|
return nil, ErrInvalidUUIDFormat
|
||
|
} else {
|
||
|
uuid[i] = v
|
||
|
}
|
||
|
}
|
||
|
return uuid[:], nil
|
||
|
}
|
||
|
|
||
|
// 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,
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
}
|