You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
8.0 KiB
289 lines
8.0 KiB
package hbaseutil |
|
|
|
import ( |
|
"encoding/binary" |
|
"fmt" |
|
"github.com/tsuna/gohbase/hrpc" |
|
"go-common/library/log" |
|
"reflect" |
|
"strconv" |
|
) |
|
|
|
/* |
|
add tag for struct fields: |
|
you can add tag: |
|
family: for hbase family, can be omitted, if omitted, the qualifier would be set at whatever famliy |
|
qualifier: for hbase qualifier |
|
if omitted, the fields must be map[string]int or map[string]string |
|
see parser_test.go for detail |
|
*/ |
|
|
|
type field struct { |
|
parser *Parser |
|
name string |
|
structField reflect.StructField |
|
fieldValue reflect.Value |
|
family string |
|
} |
|
|
|
func (f *field) isValid() bool { |
|
return f.fieldValue.IsValid() |
|
} |
|
|
|
func (f *field) setValue(c *hrpc.Cell) (err error) { |
|
if c == nil { |
|
return |
|
} |
|
|
|
if f.fieldValue.Kind() == reflect.Ptr { |
|
f.fieldValue.Set(reflect.New(f.fieldValue.Type().Elem())) |
|
f.fieldValue = f.fieldValue.Elem() |
|
} |
|
switch f.fieldValue.Kind() { |
|
case reflect.Map: |
|
err = f.setMap(c) |
|
default: |
|
err = setBasicValue(c.Value, f.fieldValue, f.name, f.parser.ParseIntFunc) |
|
} |
|
return |
|
} |
|
|
|
func setBasicValue(value []byte, rv reflect.Value, name string, parsefunc ParseIntFunc) (err error) { |
|
if rv.Kind() == reflect.Ptr { |
|
if rv.IsNil() { |
|
rv.Set(reflect.New(rv.Type().Elem())) |
|
} |
|
rv = rv.Elem() |
|
} |
|
switch rv.Kind() { |
|
case reflect.String: |
|
rv.Set(reflect.ValueOf(string(value))) |
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
|
var i, e = parsefunc(value, rv, name) |
|
if e != nil { |
|
err = fmt.Errorf("field=%s, fail to convert: %s", name, e) |
|
return |
|
} |
|
if rv.OverflowInt(int64(i)) { |
|
log.Warn("field overflow, field=%s, value=%d, field type=%s", name, i, rv.Type().Name()) |
|
} |
|
rv.SetInt(int64(i)) |
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
|
var i, e = parsefunc(value, rv, name) |
|
if e != nil { |
|
err = fmt.Errorf("field=%s, fail to convert: %s", name, e) |
|
return |
|
} |
|
if rv.OverflowUint(i) { |
|
log.Warn("field overflow, field=%s, value=%d, field type=%s", name, i, rv.Type().Name()) |
|
} |
|
rv.SetUint(i) |
|
default: |
|
err = fmt.Errorf("cannot convert type:%s, kind()=%v, field=%s", rv.Type().Name(), rv.Kind(), name) |
|
} |
|
return |
|
} |
|
|
|
func (f *field) setMap(c *hrpc.Cell) (err error) { |
|
var fieldType = f.fieldValue.Type() |
|
if f.fieldValue.IsNil() { |
|
f.fieldValue.Set(reflect.MakeMap(fieldType)) |
|
fieldType = f.fieldValue.Type() |
|
} |
|
var keyType = fieldType.Key() |
|
if keyType.Kind() != reflect.String { |
|
err = fmt.Errorf("cannot convert to map, only support map key: (string), but get type=%s", keyType.Name()) |
|
return |
|
} |
|
|
|
var val = reflect.Indirect(reflect.New(fieldType.Elem())) |
|
err = setBasicValue(c.Value, val, f.name, f.parser.ParseIntFunc) |
|
if err != nil { |
|
err = fmt.Errorf("cannot convert to map, only support map value: (integer, string), type=%s, err=%v", fieldType.Name(), err) |
|
return |
|
} |
|
|
|
var key = indirect(reflect.New(fieldType.Key())) |
|
key.SetString(string(c.Qualifier)) |
|
f.fieldValue.SetMapIndex(key, val) |
|
return |
|
} |
|
|
|
// ParseIntFunc function to parse []byte to uint64 |
|
// if not set, will assume []byte is big endian form of integer, length of 1/2/4/8 bytes |
|
type ParseIntFunc func(v []byte, rv reflect.Value, fieldname string) (result uint64, err error) |
|
|
|
//Parser parser for hbase cell |
|
type Parser struct { |
|
ParseIntFunc ParseIntFunc |
|
} |
|
|
|
func getOrCreateFieldMapByFamily(familyMap map[string]map[string]field, key string) (result map[string]field) { |
|
var ok bool |
|
if result, ok = familyMap[key]; !ok { |
|
result = make(map[string]field) |
|
familyMap[key] = result |
|
} |
|
return result |
|
} |
|
|
|
func getField(familyMap map[string]map[string]field, family string, qualifier string) (result field) { |
|
var ok bool |
|
var qualifierMap map[string]field |
|
if qualifierMap, ok = familyMap[family]; !ok { |
|
qualifierMap, ok = familyMap[""] |
|
if !ok { |
|
return |
|
} |
|
} |
|
|
|
if result, ok = qualifierMap[qualifier]; !ok { |
|
qualifierMap, ok = familyMap[""] |
|
if ok { |
|
result, ok = qualifierMap[qualifier] |
|
} |
|
if !ok { |
|
return |
|
} |
|
} |
|
return |
|
} |
|
|
|
//Parse parse cell to struct |
|
// supported type: |
|
// integer from 16 ~ 64 bit, the cell's value must be big endian form of the integer, length could be 2 or 4 or 8 bytes |
|
// string |
|
func (p *Parser) Parse(cell []*hrpc.Cell, ptr interface{}) (err error) { |
|
if len(cell) == 0 { |
|
log.Warn("cell length = 0, nothing to parse") |
|
return |
|
} |
|
var familyFieldMap = make(map[string]map[string]field) |
|
// field only have family, and type is map[string]{integer,string} |
|
var familyOnlyMap = make(map[string]field) |
|
//var noFamilyFieldMap = make(map[string]reflect.Value) |
|
|
|
var ptrType = reflect.TypeOf(ptr) |
|
// if it's ptr |
|
if ptrType.Kind() == reflect.Ptr { |
|
var value = reflect.ValueOf(ptr) |
|
value = indirect(value) |
|
var valueType = value.Type() |
|
var valueKind = valueType.Kind() |
|
if valueKind == reflect.Struct { |
|
for i := 0; i < value.NumField(); i++ { |
|
fieldInfo := valueType.Field(i) // a reflect.StructField |
|
tag := fieldInfo.Tag // a reflect.StructTag |
|
//fmt.Printf("tag for field: %s, tag: %s\n", fieldInfo.Name, tag) |
|
family := tag.Get("family") |
|
qualifier := tag.Get("qualifier") |
|
|
|
var field = field{ |
|
family: family, |
|
name: fieldInfo.Name, |
|
structField: fieldInfo, |
|
fieldValue: value.Field(i), |
|
parser: p, |
|
} |
|
// if no qualifier, or star, we create only family field |
|
if qualifier == "" || qualifier == "*" { |
|
if fieldInfo.Type.Kind() != reflect.Map { |
|
log.Warn("%s.%s, family-only field only support map, but get(%s)", ptrType.Name(), fieldInfo.Name, fieldInfo.Type.Name()) |
|
continue |
|
} |
|
familyOnlyMap[family] = field |
|
} else { |
|
// save field info |
|
var fieldMapForFamily = getOrCreateFieldMapByFamily(familyFieldMap, family) |
|
fieldMapForFamily[qualifier] = field |
|
} |
|
} |
|
} else { |
|
log.Warn("cannot decode, unsupport type(%s)", valueKind.String()) |
|
} |
|
} |
|
if p.ParseIntFunc == nil { |
|
p.ParseIntFunc = ByteBigEndianToUint64 |
|
} |
|
// parse |
|
for _, c := range cell { |
|
var family = string(c.Family) |
|
var qualifier = string(c.Qualifier) |
|
//log.Info("parse cell, family=%s, qualifier=%s", family, qualifier) |
|
var fieldValue = getField(familyFieldMap, family, qualifier) |
|
if !fieldValue.isValid() { |
|
fieldValue = familyOnlyMap[family] |
|
if !fieldValue.isValid() { |
|
//log.Warn("no field for cell, family=%s, qualifier=%s", family, qualifier) |
|
continue |
|
} |
|
} |
|
if e := fieldValue.setValue(c); e != nil { |
|
log.Warn("fail to set value, err=%v", e) |
|
continue |
|
} |
|
} |
|
|
|
return |
|
} |
|
|
|
// indirect returns the value pointed to by a pointer. |
|
// Pointers are followed until the value is not a pointer. |
|
// New values are allocated for each nil pointer. |
|
// |
|
// An exception to this rule is if the value satisfies an interface of |
|
// interest to us (like encoding.TextUnmarshaler). |
|
func indirect(v reflect.Value) reflect.Value { |
|
if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface { |
|
return v |
|
} |
|
if v.IsNil() { |
|
v.Set(reflect.New(v.Type().Elem())) |
|
} else if v.Kind() == reflect.Interface { |
|
v = v.Elem() |
|
} |
|
return indirect(reflect.Indirect(v)) |
|
} |
|
|
|
//StringToUint parse string to uint |
|
func StringToUint(value []byte, rv reflect.Value, fieldname string) (result uint64, err error) { |
|
if len(value) == 0 { |
|
return |
|
} |
|
if value[0] == '-' { |
|
i64, e := strconv.ParseInt(string(value), 10, 64) |
|
err = e |
|
result = uint64(i64) |
|
} else { |
|
result, err = strconv.ParseUint(string(value), 10, 64) |
|
} |
|
|
|
return |
|
} |
|
|
|
//ByteBigEndianToUint64 convert big endian to uint64 |
|
func ByteBigEndianToUint64(value []byte, rv reflect.Value, fieldname string) (result uint64, err error) { |
|
var length = len(value) |
|
switch length { |
|
case 4: |
|
result = uint64(binary.BigEndian.Uint32(value)) |
|
case 8: |
|
result = uint64(binary.BigEndian.Uint64(value)) |
|
case 2: |
|
result = uint64(binary.BigEndian.Uint16(value)) |
|
case 1: |
|
result = uint64(value[0]) |
|
default: |
|
err = fmt.Errorf("cannot decode to integer, byteslen=%d, only support (1,2,4,8)", length) |
|
} |
|
if err == nil { |
|
var vlen = len(value) |
|
var rvType = rv.Type() |
|
if rvType.Size() != uintptr(vlen) { |
|
log.Error("field=%s type=%s length=%d, cell length=%d, doesn't match, may yield wrong value!", |
|
fieldname, rvType.Name(), rvType.Size(), vlen) |
|
} |
|
} |
|
|
|
return |
|
}
|
|
|