Add int4range
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type BoundType byte
|
||||
|
||||
const (
|
||||
Inclusive = BoundType('i')
|
||||
Exclusive = BoundType('e')
|
||||
Unbounded = BoundType('U')
|
||||
Empty = BoundType('E')
|
||||
)
|
||||
|
||||
type UntypedTextRange struct {
|
||||
Lower string
|
||||
Upper string
|
||||
LowerType BoundType
|
||||
UpperType BoundType
|
||||
}
|
||||
|
||||
func ParseUntypedTextRange(src string) (*UntypedTextRange, error) {
|
||||
utr := &UntypedTextRange{}
|
||||
if src == "empty" {
|
||||
utr.LowerType = 'E'
|
||||
utr.UpperType = 'E'
|
||||
return utr, nil
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(src)
|
||||
|
||||
skipWhitespace(buf)
|
||||
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid lower bound: %v", err)
|
||||
}
|
||||
switch r {
|
||||
case '(':
|
||||
utr.LowerType = Exclusive
|
||||
case '[':
|
||||
utr.LowerType = Inclusive
|
||||
default:
|
||||
return nil, fmt.Errorf("missing lower bound, instead got: %v", string(r))
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid lower value: %v", err)
|
||||
}
|
||||
buf.UnreadRune()
|
||||
|
||||
if r == ',' {
|
||||
utr.LowerType = Unbounded
|
||||
} else {
|
||||
utr.Lower, err = rangeParseValue(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid lower value: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("missing range separator: %v", err)
|
||||
}
|
||||
if r != ',' {
|
||||
return nil, fmt.Errorf("missing range separator: %v", r)
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid upper value: %v", err)
|
||||
}
|
||||
buf.UnreadRune()
|
||||
|
||||
if r == ')' || r == ']' {
|
||||
utr.UpperType = Unbounded
|
||||
} else {
|
||||
utr.Upper, err = rangeParseValue(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid upper value: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("missing upper bound: %v", err)
|
||||
}
|
||||
switch r {
|
||||
case ')':
|
||||
utr.UpperType = Exclusive
|
||||
case ']':
|
||||
utr.UpperType = Inclusive
|
||||
default:
|
||||
return nil, fmt.Errorf("missing upper bound, instead got: %v", string(r))
|
||||
}
|
||||
|
||||
skipWhitespace(buf)
|
||||
|
||||
if buf.Len() > 0 {
|
||||
return nil, fmt.Errorf("unexpected trailing data: %v", buf.String())
|
||||
}
|
||||
|
||||
return utr, nil
|
||||
}
|
||||
|
||||
func rangeParseValue(buf *bytes.Buffer) (string, error) {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r == '"' {
|
||||
return rangeParseQuotedValue(buf)
|
||||
}
|
||||
buf.UnreadRune()
|
||||
|
||||
s := &bytes.Buffer{}
|
||||
|
||||
for {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '\\':
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
case ',', '[', ']', '(', ')':
|
||||
buf.UnreadRune()
|
||||
return s.String(), nil
|
||||
}
|
||||
|
||||
s.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
func rangeParseQuotedValue(buf *bytes.Buffer) (string, error) {
|
||||
s := &bytes.Buffer{}
|
||||
|
||||
for {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '\\':
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
case '"':
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r != '"' {
|
||||
buf.UnreadRune()
|
||||
return s.String(), nil
|
||||
}
|
||||
}
|
||||
s.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
type UntypedBinaryRange struct {
|
||||
Lower []byte
|
||||
Upper []byte
|
||||
LowerType BoundType
|
||||
UpperType BoundType
|
||||
}
|
||||
|
||||
// 0 = () = 00000
|
||||
// 1 = empty = 00001
|
||||
// 2 = [) = 00010
|
||||
// 4 = (] = 00100
|
||||
// 6 = [] = 00110
|
||||
// 8 = ) = 01000
|
||||
// 12 = ] = 01100
|
||||
// 16 = ( = 10000
|
||||
// 18 = [ = 10010
|
||||
// 24 = = 11000
|
||||
|
||||
const emptyMask = 1
|
||||
const lowerInclusiveMask = 2
|
||||
const upperInclusiveMask = 4
|
||||
const lowerUnboundedMask = 8
|
||||
const upperUnboundedMask = 16
|
||||
|
||||
func ParseUntypedBinaryRange(src []byte) (*UntypedBinaryRange, error) {
|
||||
ubr := &UntypedBinaryRange{}
|
||||
|
||||
if len(src) == 0 {
|
||||
return nil, fmt.Errorf("range too short: %v", len(src))
|
||||
}
|
||||
|
||||
rangeType := src[0]
|
||||
rp := 1
|
||||
|
||||
if rangeType&emptyMask > 0 {
|
||||
if len(src[rp:]) > 0 {
|
||||
return nil, fmt.Errorf("unexpected trailing bytes parsing empty range: %v", len(src[rp:]))
|
||||
}
|
||||
ubr.LowerType = Empty
|
||||
ubr.UpperType = Empty
|
||||
return ubr, nil
|
||||
}
|
||||
|
||||
if rangeType&lowerInclusiveMask > 0 {
|
||||
ubr.LowerType = Inclusive
|
||||
} else if rangeType&lowerUnboundedMask > 0 {
|
||||
ubr.LowerType = Unbounded
|
||||
} else {
|
||||
ubr.LowerType = Exclusive
|
||||
}
|
||||
|
||||
if rangeType&upperInclusiveMask > 0 {
|
||||
ubr.UpperType = Inclusive
|
||||
} else if rangeType&upperUnboundedMask > 0 {
|
||||
ubr.UpperType = Unbounded
|
||||
} else {
|
||||
ubr.UpperType = Exclusive
|
||||
}
|
||||
|
||||
if ubr.LowerType == Unbounded && ubr.UpperType == Unbounded {
|
||||
if len(src[rp:]) > 0 {
|
||||
return nil, fmt.Errorf("unexpected trailing bytes parsing unbounded range: %v", len(src[rp:]))
|
||||
}
|
||||
return ubr, nil
|
||||
}
|
||||
|
||||
if len(src[rp:]) < 4 {
|
||||
return nil, fmt.Errorf("too few bytes for size: %v", src[rp:])
|
||||
}
|
||||
valueLen := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
val := src[rp : rp+valueLen]
|
||||
rp += valueLen
|
||||
|
||||
if ubr.LowerType != Unbounded {
|
||||
ubr.Lower = val
|
||||
} else {
|
||||
ubr.Upper = val
|
||||
if len(src[rp:]) > 0 {
|
||||
return nil, fmt.Errorf("unexpected trailing bytes parsing range: %v", len(src[rp:]))
|
||||
}
|
||||
return ubr, nil
|
||||
}
|
||||
|
||||
if ubr.UpperType != Unbounded {
|
||||
if len(src[rp:]) < 4 {
|
||||
return nil, fmt.Errorf("too few bytes for size: %v", src[rp:])
|
||||
}
|
||||
valueLen := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
ubr.Upper = src[rp : rp+valueLen]
|
||||
rp += valueLen
|
||||
}
|
||||
|
||||
if len(src[rp:]) > 0 {
|
||||
return nil, fmt.Errorf("unexpected trailing bytes parsing range: %v", len(src[rp:]))
|
||||
}
|
||||
|
||||
return ubr, nil
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user