1
0
mirror of https://github.com/uptrace/go-clickhouse.git synced 2025-06-08 23:26:11 +02:00

151 lines
2.3 KiB
Go
Raw Normal View History

2022-01-23 09:36:24 +02:00
package chschema
import (
"fmt"
"strconv"
"strings"
"sync"
)
var enumMap sync.Map
type enumInfo struct {
chType string
dec []string
enc map[string]int16
}
func (e *enumInfo) Encode(val string) (int16, bool) {
i, ok := e.enc[val]
return i, ok
}
func (e *enumInfo) Decode(i int16) string {
return e.dec[i]
}
func parseEnum(s string) *enumInfo {
if v, ok := enumMap.Load(s); ok {
return v.(*enumInfo)
}
enumInfo, err := _parseEnum(s)
if err != nil {
panic(err)
}
enumInfo.chType = s
enumMap.Store(s, enumInfo)
return enumInfo
}
func _parseEnum(chType string) (*enumInfo, error) {
2023-01-21 12:14:00 +02:00
s := chEnumType(chType)
2022-01-23 09:36:24 +02:00
if s == "" {
return nil, fmt.Errorf("can't parse enum type: %q", chType)
}
var dec []string
for s != "" {
var key, val string
var ok bool
s, key, ok = scanEnumKey(s)
if !ok {
return nil, fmt.Errorf("can't parse enum key: %q", s)
}
s, ok = scanEnumChar(s, '=')
if !ok {
return nil, fmt.Errorf("can't parse enum '=': %q", s)
}
s, val = scanEnumValue(s)
if val == "" {
return nil, fmt.Errorf("can't parse enum value: %q", s)
}
n, err := strconv.ParseInt(val, 10, 16)
if err != nil {
return nil, err
}
ln := int(n + 1)
if len(dec) < ln {
dec = append(dec, make([]string, ln-len(dec))...)
}
dec[n] = key
s, _ = scanEnumChar(s, ',')
}
enc := make(map[string]int16, len(dec))
for i, s := range dec {
enc[s] = int16(i)
}
return &enumInfo{
chType: chType,
dec: dec,
enc: enc,
}, nil
}
func scanEnumKey(s string) (string, string, bool) {
loop:
for i := 0; i < len(s); i++ {
c := s[i]
switch c {
case ' ':
// ignore
case '\'':
s = s[i+1:]
break loop
default:
return s, "", false
}
}
i := strings.IndexByte(s, '\'')
if i == -1 {
return s, "", false
}
key := s[:i]
s = s[i+1:]
return s, key, true
}
func scanEnumChar(s string, ch byte) (string, bool) {
var start int
loop:
for i := 0; i < len(s); i++ {
c := s[i]
switch c {
case ' ':
start = i + 1
case ch:
return s[i+1:], true
default:
break loop
}
}
return s[start:], false
}
func scanEnumValue(s string) (string, string) {
var start int
for i := 0; i < len(s); i++ {
c := s[i]
switch {
case c == ' ':
start = i + 1
case c >= '0' && c <= '9':
// continue
default:
return s[i:], s[start:i]
}
}
return "", s[start:]
}