blob: d024b39475af7e720e1e601d3cf2188761f49028 [file] [log] [blame]
/*
* 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 (
"container/list"
"container/vector"
"strconv"
)
/**
* Type constants in the Thrift protocol.
*/
type TType byte
const (
STOP = 0
VOID = 1
BOOL = 2
BYTE = 3
I08 = 3
DOUBLE = 4
I16 = 6
I32 = 8
I64 = 10
STRING = 11
UTF7 = 11
STRUCT = 12
MAP = 13
SET = 14
LIST = 15
ENUM = 16
UTF8 = 16
UTF16 = 17
GENERIC = 127
)
func (p TType) String() string {
switch p {
case STOP:
return "STOP"
case VOID:
return "VOID"
case BOOL:
return "BOOL"
case BYTE:
return "BYTE"
case DOUBLE:
return "DOUBLE"
case I16:
return "I16"
case I32:
return "I32"
case I64:
return "I64"
case STRING:
return "STRING"
case STRUCT:
return "STRUCT"
case MAP:
return "MAP"
case SET:
return "SET"
case LIST:
return "LIST"
case ENUM:
return "ENUM"
case UTF16:
return "UTF16"
case GENERIC:
return "GENERIC"
}
return "Unknown"
}
func (p TType) IsBaseType() bool {
switch p {
case BOOL, BYTE, DOUBLE, I16, I32, I64, STRING, UTF8, UTF16:
return true
default:
return false
}
return false
}
func (p TType) IsEmptyType() bool {
switch p {
case STOP, VOID:
return true
default:
return false
}
return false
}
func (p TType) IsEnum() bool {
switch p {
case ENUM:
return true
default:
return false
}
return false
}
func (p TType) IsNumericType() bool {
switch p {
case ENUM, BOOL, BYTE, DOUBLE, I16, I32, I64:
return true
default:
return false
}
return false
}
func (p TType) IsStringType() bool {
switch p {
case STRING, UTF8, UTF16:
return true
default:
return false
}
return false
}
func (p TType) IsContainer() bool {
switch p {
case MAP, SET, LIST:
return true
default:
return false
}
return false
}
func (p TType) IsStruct() bool {
switch p {
case STRUCT:
return true
default:
return false
}
return false
}
func (p TType) IsMap() bool {
switch p {
case MAP:
return true
default:
return false
}
return false
}
func (p TType) IsList() bool {
switch p {
case LIST:
return true
default:
return false
}
return false
}
func (p TType) IsSet() bool {
switch p {
case SET:
return true
default:
return false
}
return false
}
func (p TType) IsInt() bool {
switch p {
case BYTE, I16, I32, I64:
return true
default:
return false
}
return false
}
func (p TType) Coerce(other interface{}) TType {
if other == nil {
return TType(STOP)
}
switch b := other.(type) {
default:
return TType(STOP)
case nil:
return TType(STOP)
case TType:
return b
case byte:
return TType(b)
case int:
return TType(byte(b))
case int8:
return TType(byte(b))
case int32:
return TType(byte(b))
case int64:
return TType(byte(b))
case uint:
return TType(byte(b))
case uint32:
return TType(byte(b))
case uint64:
return TType(byte(b))
case float32:
return TType(byte(int(b)))
case float64:
return TType(byte(int(b)))
}
return TType(STOP)
}
func (p TType) LessType(other interface{}) bool {
return p < p.Coerce(other)
}
func (p TType) Less(i, j interface{}) bool {
cmp, ok := p.Compare(i, j)
return ok && cmp > 0
}
func (p TType) Compare(i, j interface{}) (int, bool) {
if i == j {
return 0, true
}
if i == nil {
if j == nil {
return 0, true
}
return -1, true
}
if j == nil {
return 1, true
}
ci, iok := p.CoerceData(i)
cj, jok := p.CoerceData(j)
if iok && !jok {
return 1, true
}
if !iok && jok {
return -1, true
}
// hopefully this doesn't happen as Compare() would continuously return 0, false
if !iok && !jok {
return 0, false
}
if ci == cj {
return 0, true
}
if ci == nil {
if cj == nil {
return 0, true
}
return -1, true
}
if cj == nil {
return 1, true
}
switch p {
case STOP, VOID:
// hopefully this doesn't happen as Compare() would continuously return 0, false
return 0, false
case BOOL:
vi := ci.(bool)
vj := cj.(bool)
if vi == vj {
return 0, true
}
if vi == false {
return -1, true
}
return 1, true
case BYTE:
vi := ci.(byte)
vj := cj.(byte)
if vi == vj {
return 0, true
}
if vi < vj {
return -1, true
}
return 1, true
case DOUBLE:
vi := ci.(float64)
vj := cj.(float64)
if vi == vj {
return 0, true
}
if vi < vj {
return -1, true
}
return 1, true
case I16:
vi := ci.(int16)
vj := cj.(int16)
if vi == vj {
return 0, true
}
if vi < vj {
return -1, true
}
return 1, true
case I32:
vi := ci.(int32)
vj := cj.(int32)
if vi == vj {
return 0, true
}
if vi < vj {
return -1, true
}
return 1, true
case I64:
vi := ci.(int64)
vj := cj.(int64)
if vi == vj {
return 0, true
}
if vi < vj {
return -1, true
}
return 1, true
case STRING, UTF8, UTF16:
vi := ci.(string)
vj := cj.(string)
if vi == vj {
return 0, true
}
if vi < vj {
return -1, true
}
return 1, true
case STRUCT:
si := ci.(TStruct)
sj := cj.(TStruct)
if cmp := CompareString(si.ThriftName(), sj.ThriftName()); cmp != 0 {
return cmp, true
}
if cmp, ok := si.TStructFields().CompareTo(sj.TStructFields()); !ok || cmp != 0 {
return cmp, ok
}
for field := range si.TStructFields().Iter() {
a := si.AttributeFromFieldId(field.Id())
b := sj.AttributeFromFieldId(field.Id())
if cmp, ok := field.TypeId().Compare(a, b); !ok || cmp != 0 {
return cmp, ok
}
}
return 0, true
case MAP:
mi := ci.(TMap)
mj := cj.(TMap)
ei := mi.KeyType()
if ej := mj.KeyType(); ei != ej {
return CompareInt(int(ei), int(ej)), true
}
if size := mi.Len(); size != mj.Len() {
return CompareInt(size, mj.Len()), true
}
if c, cok := ei.Compare(mi.Keys(), mj.Keys()); c != 0 || !cok {
return c, cok
}
return ei.Compare(mi.Values(), mj.Values())
case LIST:
li := ci.(TList)
lj := cj.(TList)
ei := li.ElemType()
ej := lj.ElemType()
if ei != ej {
return CompareInt(int(ei), int(ej)), true
}
size := li.Len()
if size != lj.Len() {
return CompareInt(size, lj.Len()), true
}
for k := 0; k < size; k++ {
vi := li.At(k)
vj := lj.At(k)
c, cok := ei.Compare(vi, vj)
if c != 0 || !cok {
return c, cok
}
}
return 0, true
case SET:
li := ci.(TSet)
lj := cj.(TSet)
ei := li.ElemType()
ej := lj.ElemType()
if ei != ej {
return CompareInt(int(ei), int(ej)), true
}
size := li.Len()
if size != lj.Len() {
return CompareInt(size, lj.Len()), true
}
return ei.Compare(li.Values(), lj.Values())
default:
panic("Invalid thrift type to coerce")
}
return 0, false
}
func (p TType) CompareValueArrays(li, lj []interface{}) (int, bool) {
size := len(li)
if cmp := CompareInt(size, len(lj)); cmp != 0 {
return cmp, true
}
for i := 0; i < size; i++ {
vi := li[i]
vj := lj[i]
c, cok := p.Compare(vi, vj)
if c != 0 || !cok {
return c, cok
}
}
return 0, true
}
func (p TType) Equals(other interface{}) bool {
return p == p.Coerce(other)
}
type Stringer interface {
String() string
}
type Enumer interface {
String() string
Value() int
IsEnum() bool
}
func TypeFromValue(data interface{}) TType {
switch i := data.(type) {
default:
return STOP
case nil:
return VOID
case bool:
return BOOL
case float32, float64:
return DOUBLE
case int, int32:
return I32
case byte:
return BYTE
case int8:
return I08
case int16:
return I16
case int64:
return I64
case string:
return STRING
case TStruct:
return STRUCT
case TMap:
return MAP
case TSet:
return SET
case []interface{}, *list.List, *vector.Vector, TList:
return LIST
}
return STOP
}
func (p TType) CoerceData(data interface{}) (interface{}, bool) {
if data == nil {
switch p {
case STOP:
return nil, true
case VOID:
return nil, true
case BOOL:
return false, true
case BYTE:
return byte(0), true
case DOUBLE:
return float64(0), true
case I16:
return int16(0), true
case I32:
return int32(0), true
case I64:
return int64(0), true
case STRING, UTF8, UTF16:
return "", true
case STRUCT:
return NewTStructEmpty(""), true
case MAP:
return NewTMapDefault(), true
case LIST:
return NewTListDefault(), true
case SET:
return NewTSetDefault(), true
default:
panic("Invalid thrift type to coerce")
}
}
switch p {
case STOP:
return nil, true
case VOID:
return nil, true
case BOOL:
switch b := data.(type) {
default:
return false, false
case bool:
return b, true
case Numeric:
return bool(b.Int() != 0), true
case int:
return b != 0, true
case byte:
return b != 0, true
case int8:
return b != 0, true
case int16:
return b != 0, true
case int32:
return b != 0, true
case int64:
return b != 0, true
case uint:
return b != 0, true
case uint16:
return b != 0, true
case uint32:
return b != 0, true
case uint64:
return b != 0, true
case float32:
return b != 0, true
case float64:
return b != 0, true
case Stringer:
v := b.String()
if v == "false" || v == "0" || len(v) == 0 {
return false, true
}
return true, true
case string:
if b == "false" || b == "0" || len(b) == 0 {
return false, true
}
return true, true
}
case BYTE:
if b, ok := data.(byte); ok {
return b, true
}
if b, ok := data.(Numeric); ok {
return b.Byte(), true
}
if b, ok := data.(bool); ok {
if b {
return byte(1), true
}
return byte(0), true
}
if b, ok := data.(int); ok {
return byte(b), true
}
if b, ok := data.(int8); ok {
return byte(b), true
}
if b, ok := data.(int16); ok {
return byte(b), true
}
if b, ok := data.(int32); ok {
return byte(b), true
}
if b, ok := data.(int64); ok {
return byte(b), true
}
if b, ok := data.(uint); ok {
return byte(b), true
}
if b, ok := data.(uint8); ok {
return byte(b), true
}
if b, ok := data.(uint16); ok {
return byte(b), true
}
if b, ok := data.(uint32); ok {
return byte(b), true
}
if b, ok := data.(uint64); ok {
return byte(b), true
}
if b, ok := data.(float32); ok {
return byte(int(b)), true
}
if b, ok := data.(float64); ok {
return byte(int(b)), true
}
if b, ok := data.(Stringer); ok {
data = b.String()
}
if b, ok := data.(string); ok {
i1, err := strconv.Atoi(b)
if err != nil {
return byte(int(i1)), true
}
}
return byte(0), false
case DOUBLE:
if b, ok := data.(float32); ok {
return float64(b), true
}
if b, ok := data.(float64); ok {
return b, true
}
if b, ok := data.(Numeric); ok {
return bool(b.Float64() != 0.0), true
}
if b, ok := data.(byte); ok {
return float64(b), true
}
if b, ok := data.(bool); ok {
if b {
return float64(1.0), true
}
return float64(0.0), true
}
if b, ok := data.(int); ok {
return float64(b), true
}
if b, ok := data.(int8); ok {
return float64(b), true
}
if b, ok := data.(int16); ok {
return float64(b), true
}
if b, ok := data.(int32); ok {
return float64(b), true
}
if b, ok := data.(int64); ok {
return float64(b), true
}
if b, ok := data.(uint); ok {
return float64(b), true
}
if b, ok := data.(uint8); ok {
return float64(b), true
}
if b, ok := data.(uint16); ok {
return float64(b), true
}
if b, ok := data.(uint32); ok {
return float64(b), true
}
if b, ok := data.(uint64); ok {
return float64(b), true
}
if b, ok := data.(Stringer); ok {
data = b.String()
}
if b, ok := data.(string); ok {
d1, err := strconv.Atof64(b)
if err != nil {
return d1, true
}
}
return float64(0), false
case I16:
if b, ok := data.(int16); ok {
return b, true
}
if b, ok := data.(int); ok {
return int16(b), true
}
if b, ok := data.(Numeric); ok {
return bool(b.Int16() != 0), true
}
if b, ok := data.(byte); ok {
return int16(b), true
}
if b, ok := data.(bool); ok {
if b {
return int16(1.0), true
}
return int16(0.0), true
}
if b, ok := data.(int8); ok {
return int16(b), true
}
if b, ok := data.(int32); ok {
return int16(b), true
}
if b, ok := data.(int64); ok {
return int16(b), true
}
if b, ok := data.(uint); ok {
return int16(b), true
}
if b, ok := data.(uint8); ok {
return int16(b), true
}
if b, ok := data.(uint16); ok {
return int16(b), true
}
if b, ok := data.(uint32); ok {
return int16(b), true
}
if b, ok := data.(uint64); ok {
return int16(b), true
}
if b, ok := data.(float32); ok {
return int16(int(b)), true
}
if b, ok := data.(float64); ok {
return int16(int(b)), true
}
if b, ok := data.(Stringer); ok {
data = b.String()
}
if b, ok := data.(string); ok {
i1, err := strconv.Atoi(b)
if err != nil {
return int16(i1), true
}
}
return int16(0), false
case I32:
if b, ok := data.(int32); ok {
return b, true
}
if b, ok := data.(int); ok {
return int32(b), true
}
if b, ok := data.(Numeric); ok {
return bool(b.Int32() != 0), true
}
if b, ok := data.(byte); ok {
return int32(b), true
}
if b, ok := data.(bool); ok {
if b {
return int32(1.0), true
}
return int32(0.0), true
}
if b, ok := data.(int8); ok {
return int32(b), true
}
if b, ok := data.(int16); ok {
return int32(b), true
}
if b, ok := data.(int64); ok {
return int32(b), true
}
if b, ok := data.(uint); ok {
return int32(b), true
}
if b, ok := data.(uint8); ok {
return int32(b), true
}
if b, ok := data.(uint16); ok {
return int32(b), true
}
if b, ok := data.(uint32); ok {
return int32(b), true
}
if b, ok := data.(uint64); ok {
return int32(b), true
}
if b, ok := data.(float32); ok {
return int32(int(b)), true
}
if b, ok := data.(float64); ok {
return int32(int(b)), true
}
if b, ok := data.(Stringer); ok {
data = b.String()
}
if b, ok := data.(string); ok {
i1, err := strconv.Atoi(b)
if err != nil {
return int32(i1), true
}
}
return int32(0), false
case I64:
if b, ok := data.(int64); ok {
return b, true
}
if b, ok := data.(int32); ok {
return int64(b), true
}
if b, ok := data.(int); ok {
return int64(b), true
}
if b, ok := data.(Numeric); ok {
return bool(b.Int64() != 0), true
}
if b, ok := data.(byte); ok {
return int64(b), true
}
if b, ok := data.(bool); ok {
if b {
return int64(1.0), true
}
return int64(0.0), true
}
if b, ok := data.(int8); ok {
return int64(b), true
}
if b, ok := data.(int16); ok {
return int64(b), true
}
if b, ok := data.(uint); ok {
return int64(b), true
}
if b, ok := data.(uint8); ok {
return int64(b), true
}
if b, ok := data.(uint16); ok {
return int64(b), true
}
if b, ok := data.(uint32); ok {
return int64(b), true
}
if b, ok := data.(uint64); ok {
return int64(b), true
}
if b, ok := data.(float32); ok {
return int64(b), true
}
if b, ok := data.(float64); ok {
return int64(b), true
}
if b, ok := data.(Stringer); ok {
data = b.String()
}
if b, ok := data.(string); ok {
i1, err := strconv.Atoi64(b)
if err != nil {
return i1, true
}
}
return int64(0), false
case STRING, UTF8, UTF16:
if b, ok := data.(Enumer); ok {
if i1, ok := data.(int); ok {
return i1, true
}
return b.String(), true
}
if b, ok := data.(Stringer); ok {
return b.String(), true
}
if b, ok := data.(string); ok {
return b, true
}
if b, ok := data.(int); ok {
return string(b), true
}
if b, ok := data.(byte); ok {
return string(b), true
}
if b, ok := data.(bool); ok {
if b {
return "true", true
}
return "false", true
}
if b, ok := data.(int8); ok {
return string(b), true
}
if b, ok := data.(int16); ok {
return string(b), true
}
if b, ok := data.(int32); ok {
return string(b), true
}
if b, ok := data.(int64); ok {
return string(b), true
}
if b, ok := data.(uint); ok {
return string(b), true
}
if b, ok := data.(uint8); ok {
return string(b), true
}
if b, ok := data.(uint16); ok {
return string(b), true
}
if b, ok := data.(uint32); ok {
return string(b), true
}
if b, ok := data.(uint64); ok {
return string(b), true
}
if b, ok := data.(float32); ok {
return strconv.Ftoa32(b, 'g', -1), true
}
if b, ok := data.(float64); ok {
return strconv.Ftoa64(b, 'g', -1), true
}
return "", false
case STRUCT:
if b, ok := data.(TStruct); ok {
return b, true
}
return NewTStructEmpty(""), true
case MAP:
if b, ok := data.(TMap); ok {
return b, true
}
return NewTMapDefault(), false
case LIST:
if b, ok := data.(TList); ok {
return b, true
}
return NewTListDefault(), false
case SET:
if b, ok := data.(TSet); ok {
return b, true
}
return NewTSetDefault(), false
default:
panic("Invalid thrift type to coerce")
}
return nil, false
}
type EqualsOtherInterface interface {
Equals(other interface{}) bool
}
type EqualsMap interface {
Equals(other TMap) bool
}
type EqualsSet interface {
Equals(other TSet) bool
}
type EqualsList interface {
Equals(other TList) bool
}
type EqualsStruct interface {
Equals(other TStruct) bool
}