mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-23 19:39:05 +00:00
813 lines
18 KiB
Go
813 lines
18 KiB
Go
// Copyright 2014 The lldb Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Utilities to encode/decode and collate Go predeclared scalar types (and the
|
|
// typeless nil and []byte). The encoding format is a variation of the one
|
|
// used by the "encoding/gob" package.
|
|
|
|
package lldb
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/cznic/mathutil"
|
|
)
|
|
|
|
const (
|
|
gbNull = iota // 0x00
|
|
gbFalse // 0x01
|
|
gbTrue // 0x02
|
|
gbFloat0 // 0x03
|
|
gbFloat1 // 0x04
|
|
gbFloat2 // 0x05
|
|
gbFloat3 // 0x06
|
|
gbFloat4 // 0x07
|
|
gbFloat5 // 0x08
|
|
gbFloat6 // 0x09
|
|
gbFloat7 // 0x0a
|
|
gbFloat8 // 0x0b
|
|
gbComplex0 // 0x0c
|
|
gbComplex1 // 0x0d
|
|
gbComplex2 // 0x0e
|
|
gbComplex3 // 0x0f
|
|
gbComplex4 // 0x10
|
|
gbComplex5 // 0x11
|
|
gbComplex6 // 0x12
|
|
gbComplex7 // 0x13
|
|
gbComplex8 // 0x14
|
|
gbBytes00 // 0x15
|
|
gbBytes01 // 0x16
|
|
gbBytes02 // 0x17
|
|
gbBytes03 // 0x18
|
|
gbBytes04 // 0x19
|
|
gbBytes05 // 0x1a
|
|
gbBytes06 // 0x1b
|
|
gbBytes07 // 0x1c
|
|
gbBytes08 // 0x1d
|
|
gbBytes09 // 0x1e
|
|
gbBytes10 // 0x1f
|
|
gbBytes11 // 0x20
|
|
gbBytes12 // 0x21
|
|
gbBytes13 // 0x22
|
|
gbBytes14 // 0x23
|
|
gbBytes15 // 0x24
|
|
gbBytes16 // 0x25
|
|
gbBytes17 // Ox26
|
|
gbBytes1 // 0x27
|
|
gbBytes2 // 0x28: Offset by one to allow 64kB sized []byte.
|
|
gbString00 // 0x29
|
|
gbString01 // 0x2a
|
|
gbString02 // 0x2b
|
|
gbString03 // 0x2c
|
|
gbString04 // 0x2d
|
|
gbString05 // 0x2e
|
|
gbString06 // 0x2f
|
|
gbString07 // 0x30
|
|
gbString08 // 0x31
|
|
gbString09 // 0x32
|
|
gbString10 // 0x33
|
|
gbString11 // 0x34
|
|
gbString12 // 0x35
|
|
gbString13 // 0x36
|
|
gbString14 // 0x37
|
|
gbString15 // 0x38
|
|
gbString16 // 0x39
|
|
gbString17 // 0x3a
|
|
gbString1 // 0x3b
|
|
gbString2 // 0x3c
|
|
gbUintP1 // 0x3d
|
|
gbUintP2 // 0x3e
|
|
gbUintP3 // 0x3f
|
|
gbUintP4 // 0x40
|
|
gbUintP5 // 0x41
|
|
gbUintP6 // 0x42
|
|
gbUintP7 // 0x43
|
|
gbUintP8 // 0x44
|
|
gbIntM8 // 0x45
|
|
gbIntM7 // 0x46
|
|
gbIntM6 // 0x47
|
|
gbIntM5 // 0x48
|
|
gbIntM4 // 0x49
|
|
gbIntM3 // 0x4a
|
|
gbIntM2 // 0x4b
|
|
gbIntM1 // 0x4c
|
|
gbIntP1 // 0x4d
|
|
gbIntP2 // 0x4e
|
|
gbIntP3 // 0x4f
|
|
gbIntP4 // 0x50
|
|
gbIntP5 // 0x51
|
|
gbIntP6 // 0x52
|
|
gbIntP7 // 0x53
|
|
gbIntP8 // 0x54
|
|
gbInt0 // 0x55
|
|
|
|
gbIntMax = 255 - gbInt0 // 0xff == 170
|
|
)
|
|
|
|
// EncodeScalars encodes a vector of predeclared scalar type values to a
|
|
// []byte, making it suitable to store it as a "record" in a DB or to use it as
|
|
// a key of a BTree.
|
|
func EncodeScalars(scalars ...interface{}) (b []byte, err error) {
|
|
for _, scalar := range scalars {
|
|
switch x := scalar.(type) {
|
|
default:
|
|
return nil, &ErrINVAL{"EncodeScalars: unsupported type", fmt.Sprintf("%T in `%#v`", x, scalars)}
|
|
|
|
case nil:
|
|
b = append(b, gbNull)
|
|
|
|
case bool:
|
|
switch x {
|
|
case false:
|
|
b = append(b, gbFalse)
|
|
case true:
|
|
b = append(b, gbTrue)
|
|
}
|
|
|
|
case float32:
|
|
encFloat(float64(x), &b)
|
|
case float64:
|
|
encFloat(x, &b)
|
|
|
|
case complex64:
|
|
encComplex(complex128(x), &b)
|
|
case complex128:
|
|
encComplex(x, &b)
|
|
|
|
case string:
|
|
n := len(x)
|
|
if n <= 17 {
|
|
b = append(b, byte(gbString00+n))
|
|
b = append(b, []byte(x)...)
|
|
break
|
|
}
|
|
|
|
if n > 65535 {
|
|
return nil, fmt.Errorf("EncodeScalars: cannot encode string of length %d (limit 65536)", n)
|
|
}
|
|
|
|
pref := byte(gbString1)
|
|
if n > 255 {
|
|
pref++
|
|
}
|
|
b = append(b, pref)
|
|
encUint0(uint64(n), &b)
|
|
b = append(b, []byte(x)...)
|
|
|
|
case int8:
|
|
encInt(int64(x), &b)
|
|
case int16:
|
|
encInt(int64(x), &b)
|
|
case int32:
|
|
encInt(int64(x), &b)
|
|
case int64:
|
|
encInt(x, &b)
|
|
case int:
|
|
encInt(int64(x), &b)
|
|
|
|
case uint8:
|
|
encUint(uint64(x), &b)
|
|
case uint16:
|
|
encUint(uint64(x), &b)
|
|
case uint32:
|
|
encUint(uint64(x), &b)
|
|
case uint64:
|
|
encUint(x, &b)
|
|
case uint:
|
|
encUint(uint64(x), &b)
|
|
case []byte:
|
|
n := len(x)
|
|
if n <= 17 {
|
|
b = append(b, byte(gbBytes00+n))
|
|
b = append(b, []byte(x)...)
|
|
break
|
|
}
|
|
|
|
if n > 655356 {
|
|
return nil, fmt.Errorf("EncodeScalars: cannot encode []byte of length %d (limit 65536)", n)
|
|
}
|
|
|
|
pref := byte(gbBytes1)
|
|
if n > 255 {
|
|
pref++
|
|
}
|
|
b = append(b, pref)
|
|
if n <= 255 {
|
|
b = append(b, byte(n))
|
|
} else {
|
|
n--
|
|
b = append(b, byte(n>>8), byte(n))
|
|
}
|
|
b = append(b, x...)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func encComplex(f complex128, b *[]byte) {
|
|
encFloatPrefix(gbComplex0, real(f), b)
|
|
encFloatPrefix(gbComplex0, imag(f), b)
|
|
}
|
|
|
|
func encFloatPrefix(prefix byte, f float64, b *[]byte) {
|
|
u := math.Float64bits(f)
|
|
var n uint64
|
|
for i := 0; i < 8; i++ {
|
|
n <<= 8
|
|
n |= u & 0xFF
|
|
u >>= 8
|
|
}
|
|
bits := mathutil.BitLenUint64(n)
|
|
if bits == 0 {
|
|
*b = append(*b, prefix)
|
|
return
|
|
}
|
|
|
|
// 0 1 2 3 4 5 6 7 8 9
|
|
// . 1 1 1 1 1 1 1 1 2
|
|
encUintPrefix(prefix+1+byte((bits-1)>>3), n, b)
|
|
}
|
|
|
|
func encFloat(f float64, b *[]byte) {
|
|
encFloatPrefix(gbFloat0, f, b)
|
|
}
|
|
|
|
func encUint0(n uint64, b *[]byte) {
|
|
switch {
|
|
case n <= 0xff:
|
|
*b = append(*b, byte(n))
|
|
case n <= 0xffff:
|
|
*b = append(*b, byte(n>>8), byte(n))
|
|
case n <= 0xffffff:
|
|
*b = append(*b, byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0xffffffff:
|
|
*b = append(*b, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0xffffffffff:
|
|
*b = append(*b, byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0xffffffffffff:
|
|
*b = append(*b, byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0xffffffffffffff:
|
|
*b = append(*b, byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= math.MaxUint64:
|
|
*b = append(*b, byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
}
|
|
}
|
|
|
|
func encUintPrefix(prefix byte, n uint64, b *[]byte) {
|
|
*b = append(*b, prefix)
|
|
encUint0(n, b)
|
|
}
|
|
|
|
func encUint(n uint64, b *[]byte) {
|
|
bits := mathutil.Max(1, mathutil.BitLenUint64(n))
|
|
encUintPrefix(gbUintP1+byte((bits-1)>>3), n, b)
|
|
}
|
|
|
|
func encInt(n int64, b *[]byte) {
|
|
switch {
|
|
case n < -0x100000000000000:
|
|
*b = append(*b, byte(gbIntM8), byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n < -0x1000000000000:
|
|
*b = append(*b, byte(gbIntM7), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n < -0x10000000000:
|
|
*b = append(*b, byte(gbIntM6), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n < -0x100000000:
|
|
*b = append(*b, byte(gbIntM5), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n < -0x1000000:
|
|
*b = append(*b, byte(gbIntM4), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n < -0x10000:
|
|
*b = append(*b, byte(gbIntM3), byte(n>>16), byte(n>>8), byte(n))
|
|
case n < -0x100:
|
|
*b = append(*b, byte(gbIntM2), byte(n>>8), byte(n))
|
|
case n < 0:
|
|
*b = append(*b, byte(gbIntM1), byte(n))
|
|
case n <= gbIntMax:
|
|
*b = append(*b, byte(gbInt0+n))
|
|
case n <= 0xff:
|
|
*b = append(*b, gbIntP1, byte(n))
|
|
case n <= 0xffff:
|
|
*b = append(*b, gbIntP2, byte(n>>8), byte(n))
|
|
case n <= 0xffffff:
|
|
*b = append(*b, gbIntP3, byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0xffffffff:
|
|
*b = append(*b, gbIntP4, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0xffffffffff:
|
|
*b = append(*b, gbIntP5, byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0xffffffffffff:
|
|
*b = append(*b, gbIntP6, byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0xffffffffffffff:
|
|
*b = append(*b, gbIntP7, byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
case n <= 0x7fffffffffffffff:
|
|
*b = append(*b, gbIntP8, byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
}
|
|
}
|
|
|
|
func decodeFloat(b []byte) float64 {
|
|
var u uint64
|
|
for i, v := range b {
|
|
u |= uint64(v) << uint((i+8-len(b))*8)
|
|
}
|
|
return math.Float64frombits(u)
|
|
}
|
|
|
|
// DecodeScalars decodes a []byte produced by EncodeScalars.
|
|
func DecodeScalars(b []byte) (scalars []interface{}, err error) {
|
|
b0 := b
|
|
for len(b) != 0 {
|
|
switch tag := b[0]; tag {
|
|
//default:
|
|
//return nil, fmt.Errorf("tag %d(%#x) not supported", b[0], b[0])
|
|
case gbNull:
|
|
scalars = append(scalars, nil)
|
|
b = b[1:]
|
|
case gbFalse:
|
|
scalars = append(scalars, false)
|
|
b = b[1:]
|
|
case gbTrue:
|
|
scalars = append(scalars, true)
|
|
b = b[1:]
|
|
case gbFloat0:
|
|
scalars = append(scalars, 0.0)
|
|
b = b[1:]
|
|
case gbFloat1, gbFloat2, gbFloat3, gbFloat4, gbFloat5, gbFloat6, gbFloat7, gbFloat8:
|
|
n := 1 + int(tag) - gbFloat0
|
|
if len(b) < n-1 {
|
|
goto corrupted
|
|
}
|
|
|
|
scalars = append(scalars, decodeFloat(b[1:n]))
|
|
b = b[n:]
|
|
case gbComplex0, gbComplex1, gbComplex2, gbComplex3, gbComplex4, gbComplex5, gbComplex6, gbComplex7, gbComplex8:
|
|
n := 1 + int(tag) - gbComplex0
|
|
if len(b) < n-1 {
|
|
goto corrupted
|
|
}
|
|
|
|
re := decodeFloat(b[1:n])
|
|
b = b[n:]
|
|
|
|
if len(b) == 0 {
|
|
goto corrupted
|
|
}
|
|
|
|
tag = b[0]
|
|
if tag < gbComplex0 || tag > gbComplex8 {
|
|
goto corrupted
|
|
}
|
|
|
|
n = 1 + int(tag) - gbComplex0
|
|
if len(b) < n-1 {
|
|
goto corrupted
|
|
}
|
|
|
|
scalars = append(scalars, complex(re, decodeFloat(b[1:n])))
|
|
b = b[n:]
|
|
case gbBytes00, gbBytes01, gbBytes02, gbBytes03, gbBytes04,
|
|
gbBytes05, gbBytes06, gbBytes07, gbBytes08, gbBytes09,
|
|
gbBytes10, gbBytes11, gbBytes12, gbBytes13, gbBytes14,
|
|
gbBytes15, gbBytes16, gbBytes17:
|
|
n := int(tag - gbBytes00)
|
|
if len(b) < n+1 {
|
|
goto corrupted
|
|
}
|
|
|
|
scalars = append(scalars, append([]byte(nil), b[1:n+1]...))
|
|
b = b[n+1:]
|
|
case gbBytes1:
|
|
if len(b) < 2 {
|
|
goto corrupted
|
|
}
|
|
|
|
n := int(b[1])
|
|
b = b[2:]
|
|
if len(b) < n {
|
|
goto corrupted
|
|
}
|
|
|
|
scalars = append(scalars, append([]byte(nil), b[:n]...))
|
|
b = b[n:]
|
|
case gbBytes2:
|
|
if len(b) < 3 {
|
|
goto corrupted
|
|
}
|
|
|
|
n := int(b[1])<<8 | int(b[2]) + 1
|
|
b = b[3:]
|
|
if len(b) < n {
|
|
goto corrupted
|
|
}
|
|
|
|
scalars = append(scalars, append([]byte(nil), b[:n]...))
|
|
b = b[n:]
|
|
case gbString00, gbString01, gbString02, gbString03, gbString04,
|
|
gbString05, gbString06, gbString07, gbString08, gbString09,
|
|
gbString10, gbString11, gbString12, gbString13, gbString14,
|
|
gbString15, gbString16, gbString17:
|
|
n := int(tag - gbString00)
|
|
if len(b) < n+1 {
|
|
goto corrupted
|
|
}
|
|
|
|
scalars = append(scalars, string(b[1:n+1]))
|
|
b = b[n+1:]
|
|
case gbString1:
|
|
if len(b) < 2 {
|
|
goto corrupted
|
|
}
|
|
|
|
n := int(b[1])
|
|
b = b[2:]
|
|
if len(b) < n {
|
|
goto corrupted
|
|
}
|
|
|
|
scalars = append(scalars, string(b[:n]))
|
|
b = b[n:]
|
|
case gbString2:
|
|
if len(b) < 3 {
|
|
goto corrupted
|
|
}
|
|
|
|
n := int(b[1])<<8 | int(b[2])
|
|
b = b[3:]
|
|
if len(b) < n {
|
|
goto corrupted
|
|
}
|
|
|
|
scalars = append(scalars, string(b[:n]))
|
|
b = b[n:]
|
|
case gbUintP1, gbUintP2, gbUintP3, gbUintP4, gbUintP5, gbUintP6, gbUintP7, gbUintP8:
|
|
b = b[1:]
|
|
n := 1 + int(tag) - gbUintP1
|
|
if len(b) < n {
|
|
goto corrupted
|
|
}
|
|
|
|
var u uint64
|
|
for _, v := range b[:n] {
|
|
u = u<<8 | uint64(v)
|
|
}
|
|
scalars = append(scalars, u)
|
|
b = b[n:]
|
|
case gbIntM8, gbIntM7, gbIntM6, gbIntM5, gbIntM4, gbIntM3, gbIntM2, gbIntM1:
|
|
b = b[1:]
|
|
n := 8 - (int(tag) - gbIntM8)
|
|
if len(b) < n {
|
|
goto corrupted
|
|
}
|
|
u := uint64(math.MaxUint64)
|
|
for _, v := range b[:n] {
|
|
u = u<<8 | uint64(v)
|
|
}
|
|
scalars = append(scalars, int64(u))
|
|
b = b[n:]
|
|
case gbIntP1, gbIntP2, gbIntP3, gbIntP4, gbIntP5, gbIntP6, gbIntP7, gbIntP8:
|
|
b = b[1:]
|
|
n := 1 + int(tag) - gbIntP1
|
|
if len(b) < n {
|
|
goto corrupted
|
|
}
|
|
|
|
i := int64(0)
|
|
for _, v := range b[:n] {
|
|
i = i<<8 | int64(v)
|
|
}
|
|
scalars = append(scalars, i)
|
|
b = b[n:]
|
|
default:
|
|
scalars = append(scalars, int64(b[0])-gbInt0)
|
|
b = b[1:]
|
|
}
|
|
}
|
|
return append([]interface{}(nil), scalars...), nil
|
|
|
|
corrupted:
|
|
return nil, &ErrDecodeScalars{append([]byte(nil), b0...), len(b0) - len(b)}
|
|
}
|
|
|
|
func collateComplex(x, y complex128) int {
|
|
switch rx, ry := real(x), real(y); {
|
|
case rx < ry:
|
|
return -1
|
|
case rx == ry:
|
|
switch ix, iy := imag(x), imag(y); {
|
|
case ix < iy:
|
|
return -1
|
|
case ix == iy:
|
|
return 0
|
|
case ix > iy:
|
|
return 1
|
|
}
|
|
}
|
|
//case rx > ry:
|
|
return 1
|
|
}
|
|
|
|
func collateFloat(x, y float64) int {
|
|
switch {
|
|
case x < y:
|
|
return -1
|
|
case x == y:
|
|
return 0
|
|
}
|
|
//case x > y:
|
|
return 1
|
|
}
|
|
|
|
func collateInt(x, y int64) int {
|
|
switch {
|
|
case x < y:
|
|
return -1
|
|
case x == y:
|
|
return 0
|
|
}
|
|
//case x > y:
|
|
return 1
|
|
}
|
|
|
|
func collateUint(x, y uint64) int {
|
|
switch {
|
|
case x < y:
|
|
return -1
|
|
case x == y:
|
|
return 0
|
|
}
|
|
//case x > y:
|
|
return 1
|
|
}
|
|
|
|
func collateIntUint(x int64, y uint64) int {
|
|
if y > math.MaxInt64 {
|
|
return -1
|
|
}
|
|
|
|
return collateInt(x, int64(y))
|
|
}
|
|
|
|
func collateUintInt(x uint64, y int64) int {
|
|
return -collateIntUint(y, x)
|
|
}
|
|
|
|
func collateType(i interface{}) (r interface{}, err error) {
|
|
switch x := i.(type) {
|
|
default:
|
|
return nil, fmt.Errorf("invalid collate type %T", x)
|
|
case nil:
|
|
return i, nil
|
|
case bool:
|
|
return i, nil
|
|
case int8:
|
|
return int64(x), nil
|
|
case int16:
|
|
return int64(x), nil
|
|
case int32:
|
|
return int64(x), nil
|
|
case int64:
|
|
return i, nil
|
|
case int:
|
|
return int64(x), nil
|
|
case uint8:
|
|
return uint64(x), nil
|
|
case uint16:
|
|
return uint64(x), nil
|
|
case uint32:
|
|
return uint64(x), nil
|
|
case uint64:
|
|
return i, nil
|
|
case uint:
|
|
return uint64(x), nil
|
|
case float32:
|
|
return float64(x), nil
|
|
case float64:
|
|
return i, nil
|
|
case complex64:
|
|
return complex128(x), nil
|
|
case complex128:
|
|
return i, nil
|
|
case []byte:
|
|
return i, nil
|
|
case string:
|
|
return i, nil
|
|
}
|
|
}
|
|
|
|
// Collate collates two arrays of Go predeclared scalar types (and the typeless
|
|
// nil or []byte). If any other type appears in x or y, Collate will return a
|
|
// non nil error. String items are collated using strCollate or lexically
|
|
// byte-wise (as when using Go comparison operators) when strCollate is nil.
|
|
// []byte items are collated using bytes.Compare.
|
|
//
|
|
// Collate returns:
|
|
//
|
|
// -1 if x < y
|
|
// 0 if x == y
|
|
// +1 if x > y
|
|
//
|
|
// The same value as defined above must be returned from strCollate.
|
|
//
|
|
// The "outer" ordering is: nil, bool, number, []byte, string. IOW, nil is
|
|
// "smaller" than anything else except other nil, numbers collate before
|
|
// []byte, []byte collate before strings, etc.
|
|
//
|
|
// Integers and real numbers collate as expected in math. However, complex
|
|
// numbers are not ordered in Go. Here the ordering is defined: Complex numbers
|
|
// are in comparison considered first only by their real part. Iff the result
|
|
// is equality then the imaginary part is used to determine the ordering. In
|
|
// this "second order" comparing, integers and real numbers are considered as
|
|
// complex numbers with a zero imaginary part.
|
|
func Collate(x, y []interface{}, strCollate func(string, string) int) (r int, err error) {
|
|
nx, ny := len(x), len(y)
|
|
|
|
switch {
|
|
case nx == 0 && ny != 0:
|
|
return -1, nil
|
|
case nx == 0 && ny == 0:
|
|
return 0, nil
|
|
case nx != 0 && ny == 0:
|
|
return 1, nil
|
|
}
|
|
|
|
r = 1
|
|
if nx > ny {
|
|
x, y, r = y, x, -r
|
|
}
|
|
|
|
var c int
|
|
for i, xi0 := range x {
|
|
yi0 := y[i]
|
|
xi, err := collateType(xi0)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
yi, err := collateType(yi0)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
switch x := xi.(type) {
|
|
default:
|
|
panic(fmt.Errorf("internal error: %T", x))
|
|
|
|
case nil:
|
|
switch yi.(type) {
|
|
case nil:
|
|
// nop
|
|
default:
|
|
return -r, nil
|
|
}
|
|
|
|
case bool:
|
|
switch y := yi.(type) {
|
|
case nil:
|
|
return r, nil
|
|
case bool:
|
|
switch {
|
|
case !x && y:
|
|
return -r, nil
|
|
case x == y:
|
|
// nop
|
|
case x && !y:
|
|
return r, nil
|
|
}
|
|
default:
|
|
return -r, nil
|
|
}
|
|
|
|
case int64:
|
|
switch y := yi.(type) {
|
|
case nil, bool:
|
|
return r, nil
|
|
case int64:
|
|
c = collateInt(x, y)
|
|
case uint64:
|
|
c = collateIntUint(x, y)
|
|
case float64:
|
|
c = collateFloat(float64(x), y)
|
|
case complex128:
|
|
c = collateComplex(complex(float64(x), 0), y)
|
|
case []byte:
|
|
return -r, nil
|
|
case string:
|
|
return -r, nil
|
|
}
|
|
|
|
if c != 0 {
|
|
return c * r, nil
|
|
}
|
|
|
|
case uint64:
|
|
switch y := yi.(type) {
|
|
case nil, bool:
|
|
return r, nil
|
|
case int64:
|
|
c = collateUintInt(x, y)
|
|
case uint64:
|
|
c = collateUint(x, y)
|
|
case float64:
|
|
c = collateFloat(float64(x), y)
|
|
case complex128:
|
|
c = collateComplex(complex(float64(x), 0), y)
|
|
case []byte:
|
|
return -r, nil
|
|
case string:
|
|
return -r, nil
|
|
}
|
|
|
|
if c != 0 {
|
|
return c * r, nil
|
|
}
|
|
|
|
case float64:
|
|
switch y := yi.(type) {
|
|
case nil, bool:
|
|
return r, nil
|
|
case int64:
|
|
c = collateFloat(x, float64(y))
|
|
case uint64:
|
|
c = collateFloat(x, float64(y))
|
|
case float64:
|
|
c = collateFloat(x, y)
|
|
case complex128:
|
|
c = collateComplex(complex(x, 0), y)
|
|
case []byte:
|
|
return -r, nil
|
|
case string:
|
|
return -r, nil
|
|
}
|
|
|
|
if c != 0 {
|
|
return c * r, nil
|
|
}
|
|
|
|
case complex128:
|
|
switch y := yi.(type) {
|
|
case nil, bool:
|
|
return r, nil
|
|
case int64:
|
|
c = collateComplex(x, complex(float64(y), 0))
|
|
case uint64:
|
|
c = collateComplex(x, complex(float64(y), 0))
|
|
case float64:
|
|
c = collateComplex(x, complex(y, 0))
|
|
case complex128:
|
|
c = collateComplex(x, y)
|
|
case []byte:
|
|
return -r, nil
|
|
case string:
|
|
return -r, nil
|
|
}
|
|
|
|
if c != 0 {
|
|
return c * r, nil
|
|
}
|
|
|
|
case []byte:
|
|
switch y := yi.(type) {
|
|
case nil, bool, int64, uint64, float64, complex128:
|
|
return r, nil
|
|
case []byte:
|
|
c = bytes.Compare(x, y)
|
|
case string:
|
|
return -r, nil
|
|
}
|
|
|
|
if c != 0 {
|
|
return c * r, nil
|
|
}
|
|
|
|
case string:
|
|
switch y := yi.(type) {
|
|
case nil, bool, int64, uint64, float64, complex128:
|
|
return r, nil
|
|
case []byte:
|
|
return r, nil
|
|
case string:
|
|
switch {
|
|
case strCollate != nil:
|
|
c = strCollate(x, y)
|
|
case x < y:
|
|
return -r, nil
|
|
case x == y:
|
|
c = 0
|
|
case x > y:
|
|
return r, nil
|
|
}
|
|
}
|
|
|
|
if c != 0 {
|
|
return c * r, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
if nx == ny {
|
|
return 0, nil
|
|
}
|
|
|
|
return -r, nil
|
|
}
|