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 }