提交 f0a5eeb3 编写于 作者: jia zhang's avatar jia zhang

rune/libenclave & runectl: Move aesmd logic into libenclave/intelsgx package

Signed-off-by: jia zhang's avatarJia Zhang <zhang.jia@linux.alibaba.com>
上级 e6496ab8
......@@ -22,7 +22,8 @@ ifeq ($(DEBUG),1)
GCFLAGS=-gcflags "-N -l"
endif
PROTO_DIR := libenclave/proto
PROTO_DIR := libenclave/proto libenclave/intelsgx/proto
PROTOS := $(foreach dir,$(PROTO_DIR),$(patsubst %.proto,%.pb.go,$(wildcard $(dir)/*.proto)))
GO_BUILD := $(GO) build $(MOD_VENDOR) -buildmode=pie $(GCFLAGS) $(EXTRA_FLAGS) -tags "$(BUILDTAGS)" \
-ldflags "-X main.gitCommit=$(COMMIT) -X main.version=$(VERSION) $(EXTRA_LDFLAGS)"
......@@ -31,7 +32,7 @@ GO_BUILD_STATIC := CGO_ENABLED=1 $(GO) build $(MOD_VENDOR) $(GCFLAGS) $(EXTRA_FL
.DEFAULT: rune
rune: $(patsubst %.proto,%.pb.go,$(wildcard $(PROTO_DIR)/*.proto))
rune: $(PROTOS)
$(GO_BUILD) -o rune .
%.pb.go: %.proto
......@@ -124,7 +125,7 @@ clean:
rm -f contrib/cmd/recvtty/recvtty
rm -rf release
rm -rf man/man8
rm -f libenclave/proto/*.pb.go
rm -f $(PROTOS)
make -C libenclave/internal/runtime/pal/skeleton clean
validate:
......
......@@ -9,6 +9,7 @@ require (
github.com/coreos/go-systemd/v22 v22.0.0
github.com/cyphar/filepath-securejoin v0.2.2
github.com/docker/go-units v0.4.0
github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1
github.com/godbus/dbus/v5 v5.0.3
github.com/golang/protobuf v1.3.5
github.com/moby/sys/mountinfo v0.1.3
......
......@@ -11,10 +11,13 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1 h1:LoN2wx/aN8JPGebG+2DaUyk4M+xRcqJXfuIbs8AWHdE=
github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
......@@ -42,8 +45,10 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
......
package intelsgx // import "github.com/opencontainers/runc/libenclave/intelsgx"
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/go-restruct/restruct"
"github.com/golang/protobuf/proto"
pb "github.com/opencontainers/runc/libenclave/intelsgx/proto"
"github.com/sirupsen/logrus"
"net"
)
const (
aesmdSocket = "/var/run/aesmd/aesm.socket"
)
func dialAesmd() (*net.UnixConn, error) {
addr, err := net.ResolveUnixAddr("unix", aesmdSocket)
if err != nil {
return nil, err
}
conn, err := net.DialUnix("unix", nil, addr)
if err != nil {
return nil, err
}
return conn, nil
}
func GetToken(sig []byte) ([]byte, error) {
if len(sig) != SigStructLength {
return nil, fmt.Errorf("signature not match SIGSTRUCT")
}
s := &SigStruct{}
if err := restruct.Unpack(sig, binary.LittleEndian, &s); err != nil {
return nil, err
}
mrenclave := s.EnclaveHash[:]
modulus := s.Modulus[:]
attributes := s.Attributes[:]
logrus.Debugf("SIGSTRUCT:")
_ = s.Header[:]
logrus.Debugf(" Enclave Vendor: %#08x\n",
s.Vendor)
logrus.Debugf(" Enclave Build Date: %d-%d-%d\n",
s.BuildYear, s.BuildMonth, s.BuildDay)
logrus.Debugf(" Software Defined: %#08x\n",
s.SwDefined)
logrus.Debugf(" ISV assigned Product Family ID: 0x%v\n",
hex.EncodeToString(s.ISVFamilyId[:]))
logrus.Debugf(" ISV assigned Produdct ID: %#04x\n",
s.ISVProdId)
logrus.Debugf(" ISV assigned Extended Product ID: 0x%v\n",
hex.EncodeToString(s.ISVExtProdId[:]))
logrus.Debugf(" ISV assigned SVN: %d\n", s.ISVSvn)
logrus.Debugf(" Enclave Attributes: 0x%v\n",
hex.EncodeToString(attributes))
logrus.Debugf(" Enclave Attributes Mask: 0x%v\n",
hex.EncodeToString(s.AttributesMask[:]))
logrus.Debugf(" Enclave Misc Select: %#08x\n",
s.MiscSelect)
logrus.Debugf(" Enclave Misc Mask: %#08x\n",
s.MiscMask)
logrus.Debugf(" Enclave Hash: 0x%v\n",
hex.EncodeToString(mrenclave))
logrus.Debugf(" Modulus: 0x%v...\n",
hex.EncodeToString(modulus[:32]))
logrus.Debugf(" Exponent: %d\n",
s.Exponent)
logrus.Debugf(" Signature: 0x%v...\n",
hex.EncodeToString(s.Signature[:32]))
logrus.Debugf(" Q1: 0x%v...\n",
hex.EncodeToString(s.Q1[:32]))
logrus.Debugf(" Q2: 0x%v...\n",
hex.EncodeToString(s.Q2[:32]))
conn, err := dialAesmd()
if err != nil {
return nil, err
}
defer conn.Close()
req := pb.GetTokenRequestMessage{}
req.Req = &pb.GetTokenRequest{
Enclavehash: mrenclave,
Modulus: modulus,
Attributes: attributes,
Timeout: 10000,
}
var rdata []byte
rdata, err = proto.Marshal(&req)
if err != nil {
return nil, err
}
msgSize := uint32(len(rdata))
byteBuf := bytes.NewBuffer([]byte{})
binary.Write(byteBuf, binary.LittleEndian, &msgSize)
if _, err = conn.Write(byteBuf.Bytes()); err != nil {
return nil, err
}
if _, err = conn.Write(rdata); err != nil {
return nil, err
}
rdata = append(rdata[:4])
if _, err = conn.Read(rdata); err != nil {
return nil, err
}
byteBuf = bytes.NewBuffer(rdata)
if err = binary.Read(byteBuf, binary.LittleEndian, &msgSize); err != nil {
return nil, err
}
rdata = make([]byte, msgSize)
var msgSizeRead int
msgSizeRead, err = conn.Read(rdata)
if err != nil {
return nil, err
}
if msgSizeRead != int(msgSize) {
return nil, fmt.Errorf("invalid response size (returned %d, expected %d)",
msgSizeRead, msgSize)
}
resp := pb.GetTokenResponseMessage{}
resp.Resp = &pb.GetTokenResponse{}
if err := proto.Unmarshal(rdata, &resp); err != nil {
return nil, err
}
if resp.Resp.GetError() != 0 {
return nil, fmt.Errorf("failed to get EINITTOKEN (error code = %d)",
resp.Resp.GetError())
}
token := resp.Resp.GetToken()
if len(token) != EinittokenLength {
return nil, fmt.Errorf("invalid length of token: (returned %d, expected %d)",
len(resp.Resp.GetToken()), EinittokenLength)
}
tok := &Einittoken{}
if err := restruct.Unpack(token, binary.LittleEndian, &tok); err != nil {
return nil, err
}
logrus.Debugf("EINITTOKEN:\n")
logrus.Debugf(" Valid: %d\n",
tok.Valid)
logrus.Debugf(" Enclave Attributes: 0x%v\n",
hex.EncodeToString(tok.Attributes[:]))
logrus.Debugf(" Enclave Hash: 0x%v\n",
hex.EncodeToString(tok.MrEnclave[:]))
logrus.Debugf(" Enclave Signer: 0x%v\n",
hex.EncodeToString(tok.MrSigner[:]))
logrus.Debugf(" Launch Enclave's CPU SVN : 0x%v\n",
hex.EncodeToString(tok.CpuSvnLe[:]))
logrus.Debugf(" Launch Enclave's ISV assigned Product ID: %#04x\n",
tok.ISVProdIdLe)
logrus.Debugf(" Launch Enclave's ISV assigned SVN: %d\n",
tok.ISVSvnLe)
logrus.Debugf(" Launch Enclave's Masked Misc Select: %#08x\n",
tok.MaskedMiscSelectLe)
logrus.Debugf(" Launch Enclave's Masked Attributes: 0x%v\n",
hex.EncodeToString(tok.MaskedAttributesLe[:]))
logrus.Debugf(" Key ID: 0x%v\n",
hex.EncodeToString(tok.KeyId[:]))
logrus.Debugf(" MAC: 0x%v\n",
hex.EncodeToString(tok.Mac[:]))
return resp.Resp.GetToken(), nil
}
syntax = "proto3";
package aesm_service; // import "github.com/inclavare-containers/runectl/proto"
package aesm_service; // import "github.com/opencontainers/runc/libenclave/intelsgx/proto"
message GetTokenRequest {
bytes enclavehash = 1;
......
language: go
go:
- '1.7'
- '1.11'
- '1.12'
- '1.13'
- tip
script:
- go test -coverprofile=coverage.txt -covermode=atomic
- "if [[ $TRAVIS_GO_VERSION == 1.13 ]]; then bash <(curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh) -b $(go env GOPATH)/bin v1.20.0 && golangci-lint run; fi"
after_success:
- bash <(curl -s https://codecov.io/bash)
restruct is an open source project that anyone can contribute to. This file contains a list of all contributors up to this point. This list is obtained by running `git shortlog -s` and is listed in alphabetical order. If this file falls out of date and is missing a name, or an entry should be changed, please [file an issue](https://github.com/go-restruct/restruct/issues/new).
* [Dave Cheney](https://github.com/davecheney)
* [John Chadwick](https://github.com/jchv)
* [jlojosnegros](https://github.com/jlojosnegros)
ISC License
Copyright © 2015, John Chadwick <johnwchadwick@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
# restruct [![Build Status](https://travis-ci.org/go-restruct/restruct.svg)](https://travis-ci.org/go-restruct/restruct) [![codecov.io](http://codecov.io/github/go-restruct/restruct/coverage.svg?branch=master)](http://codecov.io/github/go-restruct/restruct?branch=master) [![godoc.org](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](https://godoc.org/github.com/go-restruct/restruct) [![Go Report Card](https://goreportcard.com/badge/github.com/go-restruct/restruct)](https://goreportcard.com/report/github.com/go-restruct/restruct)
`restruct` is a library for reading and writing binary data in Go. Similar to
lunixbochs `struc` and `encoding/binary`, this library reads data based on the
layout of structures and, like `struc`, based on what is contained in struct
tags.
To install Restruct, use the following command:
```
go get github.com/go-restruct/restruct
```
`restruct` aims to provide a clean, flexible, robust implementation of struct
packing. In the future, through fast-path optimizations and code generation, it
also aims to be quick, but it is currently very slow.
`restruct` currently requires Go 1.7+.
## Status
* As of writing, coverage is hovering around 95%, but more thorough testing
is always useful and desirable.
* Unpacking and packing are fully functional.
* More optimizations are probably possible.
## Example
```go
package main
import (
"encoding/binary"
"io/ioutil"
"os"
"github.com/go-restruct/restruct"
)
type Record struct {
Message string `struct:"[128]byte"`
}
type Container struct {
Version int `struct:"int32"`
NumRecord int `struct:"int32,sizeof=Records"`
Records []Record
}
func main() {
var c Container
file, _ := os.Open("records")
defer file.Close()
data, _ := ioutil.ReadAll(file)
restruct.Unpack(data, binary.LittleEndian, &c)
}
```
package restruct
// RegisterArrayType is deprecated; it is now a noop.
func RegisterArrayType(array interface{}) {
}
package restruct
import (
"encoding/binary"
"fmt"
"math"
"reflect"
"strings"
)
// Unpacker is a type capable of unpacking a binary representation of itself
// into a native representation. The Unpack function is expected to consume
// a number of bytes from the buffer, then return a slice of the remaining
// bytes in the buffer. You may use a pointer receiver even if the type is
// used by value.
type Unpacker interface {
Unpack(buf []byte, order binary.ByteOrder) ([]byte, error)
}
type decoder struct {
structstack
order binary.ByteOrder
sfields []field
bitCounter uint8
bitSize int
}
func putBit(buf []byte, bitSize int, bit int, val byte) {
bit = bitSize - 1 - bit
buf[len(buf)-bit/8-1] |= (val) << (uint(bit) % 8)
}
func (d *decoder) readBit() byte {
value := (d.buf[0] >> uint(7-d.bitCounter)) & 1
d.bitCounter++
if d.bitCounter >= 8 {
d.buf = d.buf[1:]
d.bitCounter -= 8
}
return value
}
func (d *decoder) readBits(f field, outBuf []byte) {
var decodedBits int
// Determine encoded size in bits.
if d.bitSize == 0 {
decodedBits = 8 * len(outBuf)
} else {
decodedBits = int(d.bitSize)
}
// Crop output buffer to relevant bytes only.
outBuf = outBuf[len(outBuf)-(decodedBits+7)/8:]
if d.bitCounter == 0 && decodedBits%8 == 0 {
// Fast path: we are fully byte-aligned.
copy(outBuf, d.buf)
d.buf = d.buf[len(outBuf):]
} else {
// Slow path: work bit-by-bit.
// TODO: This needs to be optimized in a way that can be easily
// understood; the previous optimized version was simply too hard to
// reason about.
for i := 0; i < decodedBits; i++ {
putBit(outBuf, decodedBits, i, d.readBit())
}
}
}
func (d *decoder) read8(f field) uint8 {
b := make([]byte, 1)
d.readBits(f, b)
return uint8(b[0])
}
func (d *decoder) read16(f field) uint16 {
b := make([]byte, 2)
d.readBits(f, b)
return d.order.Uint16(b)
}
func (d *decoder) read32(f field) uint32 {
b := make([]byte, 4)
d.readBits(f, b)
return d.order.Uint32(b)
}
func (d *decoder) read64(f field) uint64 {
b := make([]byte, 8)
d.readBits(f, b)
return d.order.Uint64(b)
}
func (d *decoder) readS8(f field) int8 { return int8(d.read8(f)) }
func (d *decoder) readS16(f field) int16 { return int16(d.read16(f)) }
func (d *decoder) readS32(f field) int32 { return int32(d.read32(f)) }
func (d *decoder) readS64(f field) int64 { return int64(d.read64(f)) }
func (d *decoder) readBytes(count int) []byte {
x := d.buf[0:count]
d.buf = d.buf[count:]
return x
}
func (d *decoder) skipBits(count int) {
d.bitCounter += uint8(count % 8)
if d.bitCounter > 8 {
d.bitCounter -= 8
count += 8
}
d.buf = d.buf[count/8:]
}
func (d *decoder) skip(f field, v reflect.Value) {
d.skipBits(d.fieldbits(f, v))
}
func (d *decoder) unpacker(v reflect.Value) (Unpacker, bool) {
if s, ok := v.Interface().(Unpacker); ok {
return s, true
}
if !v.CanAddr() {
return nil, false
}
if s, ok := v.Addr().Interface().(Unpacker); ok {
return s, true
}
return nil, false
}
func (d *decoder) setUint(f field, v reflect.Value, x uint64) {
switch v.Kind() {
case reflect.Bool:
b := x != 0
if f.Flags&InvertedBoolFlag == InvertedBoolFlag {
b = !b
}
v.SetBool(b)
default:
v.SetUint(x)
}
}
func (d *decoder) setInt(f field, v reflect.Value, x int64) {
switch v.Kind() {
case reflect.Bool:
b := x != 0
if f.Flags&InvertedBoolFlag == InvertedBoolFlag {
b = !b
}
v.SetBool(b)
default:
v.SetInt(x)
}
}
func (d *decoder) switc(f field, v reflect.Value, on interface{}) {
var def *switchcase
if v.Kind() != reflect.Struct {
panic(fmt.Errorf("%s: only switches on structs are valid", f.Name))
}
sfields := cachedFieldsFromStruct(f.BinaryType)
l := len(sfields)
// Zero out values for decoding.
for i := 0; i < l; i++ {
v := v.Field(f.Index)
v.Set(reflect.Zero(v.Type()))
}
for i := 0; i < l; i++ {
f := sfields[i]
v := v.Field(f.Index)
if f.Flags&DefaultFlag != 0 {
if def != nil {
panic(fmt.Errorf("%s: only one default case is allowed", f.Name))
}
def = &switchcase{f, v}
continue
}
if f.CaseExpr == nil {
panic(fmt.Errorf("%s: only cases are valid inside switches", f.Name))
}
if d.evalExpr(f.CaseExpr) == on {
d.read(f, v)
return
}
}
if def != nil {
d.read(def.f, def.v)
}
}
func (d *decoder) read(f field, v reflect.Value) {
if f.Flags&RootFlag == RootFlag {
d.setancestor(f, v, d.root())
return
}
if f.Flags&ParentFlag == ParentFlag {
for i := 1; i < len(d.stack); i++ {
if d.setancestor(f, v, d.ancestor(i)) {
break
}
}
return
}
if f.SwitchExpr != nil {
d.switc(f, v, d.evalExpr(f.SwitchExpr))
return
}
struc := d.ancestor(0)
if f.Name != "_" {
if s, ok := d.unpacker(v); ok {
var err error
d.buf, err = s.Unpack(d.buf, d.order)
if err != nil {
panic(err)
}
return
}
} else {
d.skipBits(d.fieldbits(f, v))
return
}
if !d.evalIf(f) {
return
}
sfields := d.sfields
order := d.order
if f.Order != nil {
d.order = f.Order
defer func() { d.order = order }()
}
if f.Skip != 0 {
d.skipBits(f.Skip * 8)
}
d.bitSize = d.evalBits(f)
alen := d.evalSize(f)
if alen == 0 && f.SIndex != -1 {
sv := struc.Field(f.SIndex)
l := len(sfields)
for i := 0; i < l; i++ {
if sfields[i].Index != f.SIndex {
continue
}
sf := sfields[i]
// Must use different codepath for signed/unsigned.
switch sf.BinaryType.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
alen = int(sv.Int())
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
alen = int(sv.Uint())
default:
panic(fmt.Errorf("unsupported size type %s: %s", sf.BinaryType.String(), sf.Name))
}
break
}
}
switch f.BinaryType.Kind() {
case reflect.Array:
l := f.BinaryType.Len()
// If the underlying value is a slice, initialize it.
if f.NativeType.Kind() == reflect.Slice {
v.Set(reflect.MakeSlice(reflect.SliceOf(f.NativeType.Elem()), l, l))
}
switch f.NativeType.Kind() {
case reflect.String:
// When using strings, treat as C string.
str := string(d.readBytes(d.fieldbytes(f, v)))
nul := strings.IndexByte(str, 0)
if nul != -1 {
str = str[0:nul]
}
v.SetString(str)
case reflect.Slice, reflect.Array:
ef := f.Elem()
for i := 0; i < l; i++ {
d.read(ef, v.Index(i))
}
default:
panic(fmt.Errorf("invalid array cast type: %s", f.NativeType.String()))
}
case reflect.Struct:
d.push(v)
d.sfields = cachedFieldsFromStruct(f.BinaryType)
l := len(d.sfields)
for i := 0; i < l; i++ {
f := d.sfields[i]
v := v.Field(f.Index)
if v.CanSet() {
d.read(f, v)
} else {
d.skip(f, v)
}
}
d.sfields = sfields
d.pop(v)
case reflect.Ptr:
v.Set(reflect.New(v.Type().Elem()))
d.read(f.Elem(), v.Elem())
case reflect.Slice, reflect.String:
fixed := func() {
switch f.NativeType.Elem().Kind() {
case reflect.Uint8:
v.SetBytes(d.readBytes(d.fieldbytes(f, v)))
default:
ef := f.Elem()
for i := 0; i < alen; i++ {
d.read(ef, v.Index(i))
}
}
}
switch f.NativeType.Kind() {
case reflect.String:
v.SetString(string(d.readBytes(alen)))
case reflect.Array:
if f.WhileExpr != nil {
i := 0
ef := f.Elem()
for d.evalWhile(f) {
d.read(ef, v.Index(i))
i++
}
} else {
fixed()
}
case reflect.Slice:
if f.WhileExpr != nil {
switch f.NativeType.Kind() {
case reflect.Slice:
ef := f.Elem()
for d.evalWhile(f) {
nv := reflect.New(ef.NativeType).Elem()
d.read(ef, nv)
v.Set(reflect.Append(v, nv))
}
}
} else {
v.Set(reflect.MakeSlice(f.NativeType, alen, alen))
fixed()
}
default:
panic(fmt.Errorf("invalid array cast type: %s", f.NativeType.String()))
}
case reflect.Int8:
d.setInt(f, v, int64(d.readS8(f)))
case reflect.Int16:
d.setInt(f, v, int64(d.readS16(f)))
case reflect.Int32:
d.setInt(f, v, int64(d.readS32(f)))
case reflect.Int64:
d.setInt(f, v, d.readS64(f))
case reflect.Uint8, reflect.Bool:
d.setUint(f, v, uint64(d.read8(f)))
case reflect.Uint16:
d.setUint(f, v, uint64(d.read16(f)))
case reflect.Uint32:
d.setUint(f, v, uint64(d.read32(f)))
case reflect.Uint64:
d.setUint(f, v, d.read64(f))
case reflect.Float32:
v.SetFloat(float64(math.Float32frombits(d.read32(f))))
case reflect.Float64:
v.SetFloat(math.Float64frombits(d.read64(f)))
case reflect.Complex64:
v.SetComplex(complex(
float64(math.Float32frombits(d.read32(f))),
float64(math.Float32frombits(d.read32(f))),
))
case reflect.Complex128:
v.SetComplex(complex(
math.Float64frombits(d.read64(f)),
math.Float64frombits(d.read64(f)),
))
}
if f.InExpr != nil {
v.Set(reflect.ValueOf(d.evalExpr(f.InExpr)))
}
}
package restruct
import (
"encoding/binary"
"fmt"
"math"
"reflect"
)
// Packer is a type capable of packing a native value into a binary
// representation. The Pack function is expected to overwrite a number of
// bytes in buf then return a slice of the remaining buffer. Note that you
// must also implement SizeOf, and returning an incorrect SizeOf will cause
// the encoder to crash. The SizeOf should be equal to the number of bytes
// consumed from the buffer slice in Pack. You may use a pointer receiver even
// if the type is used by value.
type Packer interface {
Sizer
Pack(buf []byte, order binary.ByteOrder) ([]byte, error)
}
type encoder struct {
structstack
order binary.ByteOrder
sfields []field
bitCounter int
bitSize int
}
func getBit(buf []byte, bitSize int, bit int) byte {
bit = bitSize - 1 - bit
return (buf[len(buf)-bit/8-1] >> (uint(bit) % 8)) & 1
}
func (e *encoder) writeBit(value byte) {
e.buf[0] |= (value & 1) << uint(7-e.bitCounter)
e.bitCounter++
if e.bitCounter >= 8 {
e.buf = e.buf[1:]
e.bitCounter -= 8
}
}
func (e *encoder) writeBits(f field, inBuf []byte) {
var encodedBits int
// Determine encoded size in bits.
if e.bitSize == 0 {
encodedBits = 8 * len(inBuf)
} else {
encodedBits = int(e.bitSize)
}
// Crop input buffer to relevant bytes only.
inBuf = inBuf[len(inBuf)-(encodedBits+7)/8:]
if e.bitCounter == 0 && encodedBits%8 == 0 {
// Fast path: we are fully byte-aligned.
copy(e.buf, inBuf)
e.buf = e.buf[len(inBuf):]
} else {
// Slow path: work bit-by-bit.
// TODO: This needs to be optimized in a way that can be easily
// understood; the previous optimized version was simply too hard to
// reason about.
for i := 0; i < encodedBits; i++ {
e.writeBit(getBit(inBuf, encodedBits, i))
}
}
}
func (e *encoder) write8(f field, x uint8) {
b := make([]byte, 1)
b[0] = x
e.writeBits(f, b)
}
func (e *encoder) write16(f field, x uint16) {
b := make([]byte, 2)
e.order.PutUint16(b, x)
e.writeBits(f, b)
}
func (e *encoder) write32(f field, x uint32) {
b := make([]byte, 4)
e.order.PutUint32(b, x)
e.writeBits(f, b)
}
func (e *encoder) write64(f field, x uint64) {
b := make([]byte, 8)
e.order.PutUint64(b, x)
e.writeBits(f, b)
}
func (e *encoder) writeS8(f field, x int8) { e.write8(f, uint8(x)) }
func (e *encoder) writeS16(f field, x int16) { e.write16(f, uint16(x)) }
func (e *encoder) writeS32(f field, x int32) { e.write32(f, uint32(x)) }
func (e *encoder) writeS64(f field, x int64) { e.write64(f, uint64(x)) }
func (e *encoder) skipBits(count int) {
e.bitCounter += count % 8
if e.bitCounter > 8 {
e.bitCounter -= 8
count += 8
}
e.buf = e.buf[count/8:]
}
func (e *encoder) skip(f field, v reflect.Value) {
e.skipBits(e.fieldbits(f, v))
}
func (e *encoder) packer(v reflect.Value) (Packer, bool) {
if s, ok := v.Interface().(Packer); ok {
return s, true
}
if !v.CanAddr() {
return nil, false
}
if s, ok := v.Addr().Interface().(Packer); ok {
return s, true
}
return nil, false
}
func (e *encoder) intFromField(f field, v reflect.Value) int64 {
switch v.Kind() {
case reflect.Bool:
b := v.Bool()
if f.Flags&InvertedBoolFlag == InvertedBoolFlag {
b = !b
}
if b {
if f.Flags&VariantBoolFlag == VariantBoolFlag {
return -1
}
return 1
}
return 0
default:
return v.Int()
}
}
func (e *encoder) uintFromField(f field, v reflect.Value) uint64 {
switch v.Kind() {
case reflect.Bool:
b := v.Bool()
if f.Flags&InvertedBoolFlag == InvertedBoolFlag {
b = !b
}
if b {
if f.Flags&VariantBoolFlag == VariantBoolFlag {
return ^uint64(0)
}
return 1
}
return 0
default:
return v.Uint()
}
}
func (e *encoder) switc(f field, v reflect.Value, on interface{}) {
var def *switchcase
if v.Kind() != reflect.Struct {
panic(fmt.Errorf("%s: only switches on structs are valid", f.Name))
}
sfields := cachedFieldsFromStruct(f.BinaryType)
l := len(sfields)
for i := 0; i < l; i++ {
f := sfields[i]
v := v.Field(f.Index)
if f.Flags&DefaultFlag != 0 {
if def != nil {
panic(fmt.Errorf("%s: only one default case is allowed", f.Name))
}
def = &switchcase{f, v}
continue
}
if f.CaseExpr == nil {
panic(fmt.Errorf("%s: only cases are valid inside switches", f.Name))
}
if e.evalExpr(f.CaseExpr) == on {
e.write(f, v)
return
}
}
if def != nil {
e.write(def.f, def.v)
}
}
func (e *encoder) write(f field, v reflect.Value) {
if f.Flags&RootFlag == RootFlag {
e.setancestor(f, v, e.root())
return
}
if f.Flags&ParentFlag == ParentFlag {
for i := 1; i < len(e.stack); i++ {
if e.setancestor(f, v, e.ancestor(i)) {
break
}
}
return
}
if f.SwitchExpr != nil {
e.switc(f, v, e.evalExpr(f.SwitchExpr))
return
}
struc := e.ancestor(0)
if f.Name != "_" {
if s, ok := e.packer(v); ok {
var err error
e.buf, err = s.Pack(e.buf, e.order)
if err != nil {
panic(err)
}
return
}
} else {
e.skipBits(e.fieldbits(f, v))
return
}
if !e.evalIf(f) {
return
}
sfields := e.sfields
order := e.order
if f.Order != nil {
e.order = f.Order
defer func() { e.order = order }()
}
if f.Skip != 0 {
e.skipBits(f.Skip * 8)
}
e.bitSize = e.evalBits(f)
// If this is a sizeof field, pull the current slice length into it.
if f.TIndex != -1 {
sv := struc.Field(f.TIndex)
switch f.BinaryType.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v.SetInt(int64(sv.Len()))
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v.SetUint(uint64(sv.Len()))
default:
panic(fmt.Errorf("unsupported size type %s: %s", f.BinaryType.String(), f.Name))
}
}
ov := v
if f.OutExpr != nil {
ov = reflect.ValueOf(e.evalExpr(f.OutExpr))
}
switch f.BinaryType.Kind() {
case reflect.Ptr:
// Skip if pointer is nil.
if v.IsNil() {
return
}
e.write(f.Elem(), v.Elem())
case reflect.Array, reflect.Slice, reflect.String:
switch f.NativeType.Kind() {
case reflect.Slice, reflect.String:
if f.SizeExpr != nil {
if l := e.evalSize(f); l != ov.Len() {
panic(fmt.Errorf("length does not match size expression (%d != %d)", ov.Len(), l))
}
}
fallthrough
case reflect.Array:
ef := f.Elem()
len := ov.Len()
cap := len
if f.BinaryType.Kind() == reflect.Array {
cap = f.BinaryType.Len()
}
for i := 0; i < len; i++ {
e.write(ef, ov.Index(i))
}
for i := len; i < cap; i++ {
e.write(ef, reflect.New(f.BinaryType.Elem()).Elem())
}
default:
panic(fmt.Errorf("invalid array cast type: %s", f.NativeType.String()))
}
case reflect.Struct:
e.push(ov)
e.sfields = cachedFieldsFromStruct(f.BinaryType)
l := len(e.sfields)
for i := 0; i < l; i++ {
sf := e.sfields[i]
sv := ov.Field(sf.Index)
if sv.CanSet() {
e.write(sf, sv)
} else {
e.skip(sf, sv)
}
}
e.sfields = sfields
e.pop(ov)
case reflect.Int8:
e.writeS8(f, int8(e.intFromField(f, ov)))
case reflect.Int16:
e.writeS16(f, int16(e.intFromField(f, ov)))
case reflect.Int32:
e.writeS32(f, int32(e.intFromField(f, ov)))
case reflect.Int64:
e.writeS64(f, int64(e.intFromField(f, ov)))
case reflect.Uint8, reflect.Bool:
e.write8(f, uint8(e.uintFromField(f, ov)))
case reflect.Uint16:
e.write16(f, uint16(e.uintFromField(f, ov)))
case reflect.Uint32:
e.write32(f, uint32(e.uintFromField(f, ov)))
case reflect.Uint64:
e.write64(f, uint64(e.uintFromField(f, ov)))
case reflect.Float32:
e.write32(f, math.Float32bits(float32(ov.Float())))
case reflect.Float64:
e.write64(f, math.Float64bits(float64(ov.Float())))
case reflect.Complex64:
x := ov.Complex()
e.write32(f, math.Float32bits(float32(real(x))))
e.write32(f, math.Float32bits(float32(imag(x))))
case reflect.Complex128:
x := ov.Complex()
e.write64(f, math.Float64bits(float64(real(x))))
e.write64(f, math.Float64bits(float64(imag(x))))
}
}
package restruct
import (
"github.com/go-restruct/restruct/expr"
)
var (
expressionsEnabled = false
stdLibResolver = expr.NewMapResolver(exprStdLib)
)
// EnableExprBeta enables you to use restruct expr while it is still in beta.
// Use at your own risk. Functionality may change in unforeseen, incompatible
// ways at any time.
func EnableExprBeta() {
expressionsEnabled = true
}
package expr
import (
"fmt"
"strconv"
)
type node interface {
source() string
}
type unaryop int
const (
unaryplus unaryop = iota
unarynegate
unarynot
unarybitnot
unaryderef
unaryref
)
type binaryop int
const (
binarylogicalor binaryop = iota
binarylogicaland
binaryequal
binarynotequal
binarylesser
binarylesserequal
binarygreater
binarygreaterequal
binaryadd
binarysub
binaryor
binaryxor
binarymul
binarydiv
binaryrem
binarylsh
binaryrsh
binaryand
binaryandnot
binarymember
binarycall
binarysubscript
binarygroup
)
// Identifier node.
type identnode struct {
pos int
ident string
}
func newidentnode(t token) identnode {
return identnode{t.pos, t.sval}
}
func (n identnode) source() string {
return n.ident
}
// Integer literal node.
type intnode struct {
pos int
uval uint64
ival int64
sign bool
}
func newintnode(t token) intnode {
return intnode{pos: t.pos, uval: t.uval, ival: t.ival, sign: t.sign}
}
func (n intnode) source() string {
if n.sign {
return strconv.FormatInt(n.ival, 10)
}
return strconv.FormatUint(n.uval, 10)
}
// Float literal node.
type floatnode struct {
pos int
fval float64
}
func newfloatnode(t token) floatnode {
return floatnode{pos: t.pos, fval: t.fval}
}
func (n floatnode) source() string {
return strconv.FormatFloat(n.fval, 'f', -1, 64)
}
// Bool literal node.
type boolnode struct {
pos int
val bool
}
func newboolnode(t token) boolnode {
return boolnode{t.pos, t.bval}
}
func (n boolnode) source() string {
if n.val {
return "true"
}
return "false"
}
// String literal node.
type strnode struct {
pos int
val string
}
func newstrnode(t token) strnode {
return strnode{t.pos, t.sval}
}
func (n strnode) source() string {
return fmt.Sprintf("%q", n.val)
}
// Rune literal node.
type runenode struct {
pos int
val rune
}
func newrunenode(t token) runenode {
return runenode{t.pos, rune(t.ival)}
}
func (n runenode) source() string {
return fmt.Sprintf("%q", n.val)
}
// Nil node.
type nilnode struct {
pos int
}
func newnilnode(t token) nilnode {
return nilnode{t.pos}
}
func (nilnode) source() string {
return "nil"
}
// Unary expression node.
type unaryexpr struct {
op unaryop
n node
}
func (n unaryexpr) source() string {
operand := n.n.source()
switch n.op {
case unaryplus:
return "+" + operand
case unarynegate:
return "-" + operand
case unarynot:
return "!" + operand
case unarybitnot:
return "^" + operand
case unaryderef:
return "*" + operand
case unaryref:
return "&" + operand
}
panic("invalid unary expr?")
}
// Binary expression node.
type binaryexpr struct {
op binaryop
a, b node
}
func (n binaryexpr) source() string {
a, b := n.a.source(), n.b.source()
switch n.op {
case binarylogicalor:
return a + " || " + b
case binarylogicaland:
return a + " && " + b
case binaryequal:
return a + " == " + b
case binarynotequal:
return a + " != " + b
case binarylesser:
return a + " < " + b
case binarylesserequal:
return a + " <= " + b
case binarygreater:
return a + " > " + b
case binarygreaterequal:
return a + " >= " + b
case binaryadd:
return a + " + " + b
case binarysub:
return a + " - " + b
case binaryor:
return a + " | " + b
case binaryxor:
return a + " ^ " + b
case binarymul:
return a + " * " + b
case binarydiv:
return a + " / " + b
case binaryrem:
return a + " % " + b
case binarylsh:
return a + " << " + b
case binaryrsh:
return a + " >> " + b
case binaryand:
return a + " & " + b
case binaryandnot:
return a + " &^ " + b
case binarymember:
return a + "." + b
case binarycall:
return a + "(" + b + ")"
case binarysubscript:
return a + "[" + b + "]"
case binarygroup:
return a + ", " + b
}
panic("invalid binary expr?")
}
// Ternary expression node.
type ternaryexpr struct {
a, b, c node
}
func (n ternaryexpr) source() string {
return n.a.source() + " ? " + n.b.source() + " : " + n.c.source()
}
package expr
import "reflect"
// Assertions.
var (
_ = TypeResolver(&TypeResolverAdapter{})
_ = TypeResolver(&MetaTypeResolver{})
_ = Resolver(&MetaResolver{})
_ = TypeResolver(&StructTypeResolver{})
_ = Resolver(&StructResolver{})
_ = TypeResolver(&MapTypeResolver{})
_ = Resolver(&MapResolver{})
)
// TypeResolver resolves types.
type TypeResolver interface {
TypeResolve(ident string) Type
}
// Resolver resolves runtime values.
type Resolver interface {
Resolve(ident string) Value
}
// TypeResolverAdapter adapts a runtime resolver to a type resolver by taking
// types of values retrieve from Resolve.
type TypeResolverAdapter struct {
Resolver
}
// NewTypeResolverAdapter creates a new TypeResolverAdapter from a resolver.
func NewTypeResolverAdapter(r Resolver) *TypeResolverAdapter {
return &TypeResolverAdapter{r}
}
// TypeResolve implements TypeResolver.
func (r *TypeResolverAdapter) TypeResolve(ident string) Type {
return r.Resolve(ident).Type()
}
// MetaTypeResolver runs multiple type resolvers serially.
type MetaTypeResolver struct {
resolvers []TypeResolver
}
// NewMetaTypeResolver creates a new meta type resolver.
func NewMetaTypeResolver() *MetaTypeResolver {
return &MetaTypeResolver{}
}
// AddResolver adds a new resolver below other resolvers.
func (r *MetaTypeResolver) AddResolver(n TypeResolver) {
r.resolvers = append(r.resolvers, n)
}
// TypeResolve implements TypeResolver.
func (r *MetaTypeResolver) TypeResolve(ident string) Type {
for _, resolver := range r.resolvers {
if t := resolver.TypeResolve(ident); t != nil {
return t
}
}
return nil
}
// MetaResolver runs multiple resolvers serially.
type MetaResolver struct {
resolvers []Resolver
}
// NewMetaResolver creates a new meta resolver.
func NewMetaResolver() *MetaResolver {
return &MetaResolver{}
}
// AddResolver adds a new resolver below other resolvers.
func (r *MetaResolver) AddResolver(n Resolver) {
r.resolvers = append(r.resolvers, n)
}
// Resolve implements Resolver.
func (r *MetaResolver) Resolve(ident string) Value {
for _, resolver := range r.resolvers {
if t := resolver.Resolve(ident); t != nil {
return t
}
}
return nil
}
// StructTypeResolver resolves types of struct fields.
type StructTypeResolver struct {
struc *StructType
}
// NewStructTypeResolver creates a new struct type resolver.
func NewStructTypeResolver(s interface{}) *StructTypeResolver {
return &StructTypeResolver{TypeOf(s).(*StructType)}
}
// TypeResolve implements TypeResolver.
func (r *StructTypeResolver) TypeResolve(ident string) Type {
if f, ok := r.struc.FieldByName(ident); ok {
return f.Type
}
return nil
}
// StructResolver resolves struct fields.
type StructResolver struct {
struc reflect.Value
}
// NewStructResolver creates a new struct resolver.
func NewStructResolver(s reflect.Value) *StructResolver {
return &StructResolver{s}
}
// Resolve implements Resolver.
func (r *StructResolver) Resolve(ident string) Value {
if sv := r.struc.FieldByName(ident); sv.IsValid() {
return ValueOf(sv.Interface())
}
return nil
}
// MapTypeResolver resolves map keys.
type MapTypeResolver struct {
m map[string]Type
}
// NewMapTypeResolver creates a new struct resolver.
func NewMapTypeResolver(m map[string]Type) *MapTypeResolver {
return &MapTypeResolver{m}
}
// TypeResolve implements TypeResolver.
func (r *MapTypeResolver) TypeResolve(ident string) Type {
if t, ok := r.m[ident]; ok {
return t
}
return nil
}
// MapResolver resolves map keys.
type MapResolver struct {
m map[string]Value
}
// NewMapResolver creates a new struct resolver.
func NewMapResolver(m map[string]Value) *MapResolver {
return &MapResolver{m}
}
// Resolve implements Resolver.
func (r *MapResolver) Resolve(ident string) Value {
if v, ok := r.m[ident]; ok {
return v
}
return nil
}
package expr
import (
"fmt"
)
// EvalProgram returns the result of executing the program with the given resolver.
func EvalProgram(resolver Resolver, program *Program) (v interface{}, err error) {
defer func() {
if r := recover(); r != nil {
if rerr, ok := r.(error); ok {
err = rerr
} else {
panic(r)
}
}
}()
v = evalnode(resolver, program.root).RawValue()
return
}
// Eval returns the result of evaluating the provided expression.
func Eval(resolver Resolver, expr string) (interface{}, error) {
return EvalProgram(resolver, ParseString(expr))
}
func evalnode(resolver Resolver, node node) Value {
switch n := node.(type) {
case identnode:
v := resolver.Resolve(n.ident)
if v == nil {
panic(fmt.Errorf("unresolved name %s", n.ident))
}
return v
case intnode:
if n.sign {
return literalintval(n.ival)
}
return literaluintval(n.uval)
case floatnode:
return literalfloatval(n.fval)
case boolnode:
return literalboolval(n.val)
case strnode:
return literalstrval(n.val)
case runenode:
return literalintval(int64(n.val))
case nilnode:
return literalnilval()
case unaryexpr:
return evalunary(resolver, n)
case binaryexpr:
return evalbinary(resolver, n)
case ternaryexpr:
return evalternary(resolver, n)
default:
panic("invalid node")
}
}
func evalunary(resolver Resolver, node unaryexpr) Value {
n := evalnode(resolver, node.n)
switch node.op {
case unaryplus:
return n
case unarynegate:
return n.Negate()
case unarynot:
return n.Not()
case unarybitnot:
return n.BitNot()
case unaryderef:
return n.Deref()
case unaryref:
return n.Ref()
default:
panic("invalid unary expression")
}
}
func flattengroup(n node) []node {
if n, ok := n.(binaryexpr); ok {
if n.op == binarygroup {
return append(flattengroup(n.a), flattengroup(n.b)...)
}
}
return []node{n}
}
func evalbinary(resolver Resolver, node binaryexpr) Value {
a := evalnode(resolver, node.a)
switch node.op {
case binarymember:
if id, ok := node.b.(identnode); ok {
return a.Dot(id.ident)
}
panic(fmt.Errorf("expected ident node, got %T", node.b))
case binarycall:
in := []Value{}
for _, n := range flattengroup(node.b) {
in = append(in, evalnode(resolver, n))
}
return a.Call(in)
case binarygroup:
return evalnode(resolver, node.b)
}
b := evalnode(resolver, node.b)
switch node.op {
case binarylogicalor:
return a.LogicalOr(b)
case binarylogicaland:
return a.LogicalAnd(b)
case binaryequal:
return a.Equal(b)
case binarynotequal:
return a.NotEqual(b)
case binarylesser:
return a.Lesser(b)
case binarylesserequal:
return a.LesserEqual(b)
case binarygreater:
return a.Greater(b)
case binarygreaterequal:
return a.GreaterEqual(b)
case binaryadd:
return a.Add(b)
case binarysub:
return a.Sub(b)
case binaryor:
return a.Or(b)
case binaryxor:
return a.Xor(b)
case binarymul:
return a.Mul(b)
case binarydiv:
return a.Div(b)
case binaryrem:
return a.Rem(b)
case binarylsh:
return a.Lsh(b)
case binaryrsh:
return a.Rsh(b)
case binaryand:
return a.And(b)
case binaryandnot:
return a.AndNot(b)
case binarysubscript:
return a.Index(b)
default:
panic("invalid binary expression")
}
}
func evalternary(resolver Resolver, node ternaryexpr) Value {
a := evalnode(resolver, node.a).Value().Interface()
cond, ok := a.(bool)
if !ok {
panic(fmt.Errorf("unexpected type %T for ternary", cond))
}
if cond {
return evalnode(resolver, node.b)
}
return evalnode(resolver, node.c)
}
package expr
//go:generate stringer -type=tokenkind
import (
"fmt"
"io"
"strconv"
"unicode"
"unicode/utf8"
)
func lower(ch rune) rune {
return ('a' - 'A') | ch
}
func isdecimal(ch rune) bool {
return '0' <= ch && ch <= '9'
}
func isoctal(ch rune) bool {
return '0' <= ch && ch <= '7'
}
func ishex(ch rune) bool {
return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f'
}
func isletter(c rune) bool {
return 'a' <= lower(c) && lower(c) <= 'z' || c == '_' || c >= utf8.RuneSelf && unicode.IsLetter(c)
}
func isdigit(c rune) bool {
return isdecimal(c) || c >= utf8.RuneSelf && unicode.IsDigit(c)
}
func isnumber(c rune) bool {
return isdigit(c) || ishex(c) || c == '.' || lower(c) == 'x'
}
func isident(c rune) bool {
return isletter(c) || isdigit(c)
}
func iswhitespace(c rune) bool {
return c == ' ' || c == '\t'
}
// tokenkind is an enumeration of different kinds of tokens.
type tokenkind int
// This is a definition of all possible token kinds.
const (
niltoken tokenkind = iota
errtoken
eoftoken
identtoken
inttoken
floattoken
booltoken
strtoken
runetoken
addtoken
subtoken
multoken
quotoken
remtoken
andtoken
nottoken
ortoken
xortoken
shltoken
shrtoken
andnottoken
logicalandtoken
logicalortoken
equaltoken
lessertoken
greatertoken
notequaltoken
lesserequaltoken
greaterequaltoken
leftparentoken
leftbrackettoken
commatoken
periodtoken
rightparentoken
rightbrackettoken
colontoken
ternarytoken
boolkeyword
bytekeyword
float32keyword
float64keyword
intkeyword
int8keyword
int16keyword
int32keyword
int64keyword
uintkeyword
uint8keyword
uint16keyword
uint32keyword
uint64keyword
uintptrkeyword
nilkeyword
)
var keywordmap = map[string]tokenkind{
"bool": boolkeyword,
"byte": bytekeyword,
"float32": float32keyword,
"float64": float64keyword,
"int": intkeyword,
"int8": int8keyword,
"int16": int16keyword,
"int32": int32keyword,
"int64": int64keyword,
"uint": uintkeyword,
"uint8": uint8keyword,
"uint16": uint16keyword,
"uint32": uint32keyword,
"uint64": uint64keyword,
"uintptr": uintptrkeyword,
"nil": nilkeyword,
}
const eof = utf8.MaxRune + 0x0001
// token contains information for a single lexical token.
type token struct {
kind tokenkind
pos int
sval string
ival int64
uval uint64
fval float64
bval bool
sign bool
eval error
}
// scanner scans lexical tokens from the expression.
type scanner struct {
r io.RuneScanner
p int
eof bool
}
func newscanner(r io.RuneScanner) *scanner {
return &scanner{r: r}
}
func (s *scanner) readrune() rune {
if s.eof {
return eof
}
c, _, err := s.r.ReadRune()
if err == io.EOF {
s.eof = true
return eof
} else if err != nil {
panic(err)
}
s.p++
return c
}
func (s *scanner) unreadrune() {
if s.eof {
return
}
if err := s.r.UnreadRune(); err != nil {
panic(err)
}
s.p--
}
func (s *scanner) skipws() {
for {
c := s.readrune()
if !iswhitespace(c) {
s.unreadrune()
return
}
}
}
func (s *scanner) accept(c rune) bool {
if s.readrune() == c {
return true
}
s.unreadrune()
return false
}
func (s *scanner) expect(c rune) {
r := s.readrune()
if r != c {
panic(fmt.Errorf("expected %c", r))
}
}
func (s *scanner) peekmatch(f func(rune) bool) bool {
c := s.readrune()
s.unreadrune()
return f(c)
}
func (s *scanner) acceptfn(f func(rune) bool) (rune, bool) {
r := s.readrune()
if f(r) {
return r, true
}
s.unreadrune()
return r, false
}
func (s *scanner) expectfn(f func(rune) bool) rune {
r, ok := s.acceptfn(f)
if !ok {
panic(fmt.Errorf("unexpected %c", r))
}
return r
}
func (s *scanner) tokensym(k tokenkind, src string) token {
return token{kind: k, sval: src}
}
func (s *scanner) errsymf(format string, a ...interface{}) token {
return token{kind: errtoken, eval: fmt.Errorf(format, a...)}
}
func (s *scanner) scanident() token {
t := token{kind: identtoken}
if r, ok := s.acceptfn(isletter); ok {
t.sval = string(r)
} else {
return s.errsymf("unexpected ident start token: %c", r)
}
for {
if r, ok := s.acceptfn(isident); ok {
t.sval += string(r)
continue
}
break
}
// Handle boolean constant.
if t.sval == "true" {
t.kind = booltoken
t.bval = true
}
if t.sval == "false" {
t.kind = booltoken
t.bval = false
}
// Handle keywords.
if k, ok := keywordmap[t.sval]; ok {
t.kind = k
}
return t
}
func (s *scanner) scannumber(t token) token {
if r, ok := s.acceptfn(isnumber); ok {
t.sval += string(r)
} else {
return s.errsymf("unexpected int start token: %c", r)
}
for {
if r, ok := s.acceptfn(isnumber); ok {
t.sval += string(r)
continue
}
break
}
var err error
if t.uval, err = strconv.ParseUint(t.sval, 0, 64); err == nil {
t.ival = int64(t.uval)
t.fval = float64(t.ival)
t.kind = inttoken
t.sign = false
} else if t.ival, err = strconv.ParseInt(t.sval, 0, 64); err == nil {
t.uval = uint64(t.ival)
t.fval = float64(t.ival)
t.kind = inttoken
t.sign = true
} else if t.fval, err = strconv.ParseFloat(t.sval, 64); err == nil {
t.ival = int64(t.fval)
t.uval = uint64(t.fval)
t.kind = floattoken
} else {
return token{kind: errtoken, eval: err}
}
return t
}
func runebytes(r rune) []byte {
runebuf := [4]byte{}
l := utf8.EncodeRune(runebuf[:], rune(r))
return runebuf[:l]
}
func (s *scanner) scanescape(quote byte) []byte {
switch {
case s.accept('a'):
return []byte{'\a'}
case s.accept('b'):
return []byte{'\b'}
case s.accept('f'):
return []byte{'\f'}
case s.accept('n'):
return []byte{'\n'}
case s.accept('r'):
return []byte{'\r'}
case s.accept('t'):
return []byte{'\t'}
case s.accept('v'):
return []byte{'\v'}
case s.accept('\\'):
return []byte{'\\'}
case s.accept(rune(quote)):
return []byte{quote}
case s.peekmatch(isoctal):
octal := string([]rune{s.expectfn(isoctal), s.expectfn(isoctal), s.expectfn(isoctal)})
code, err := strconv.ParseUint(octal, 8, 8)
if err != nil {
panic(err)
}
return []byte{byte(code)}
case s.accept('x'):
hex := string([]rune{s.expectfn(ishex), s.expectfn(ishex)})
code, err := strconv.ParseUint(hex, 16, 8)
if err != nil {
panic(err)
}
return []byte{byte(code)}
case s.accept('u'):
hex := string([]rune{
s.expectfn(ishex), s.expectfn(ishex), s.expectfn(ishex), s.expectfn(ishex),
})
code, err := strconv.ParseUint(hex, 16, 16)
if err != nil {
panic(err)
}
return runebytes(rune(code))
case s.accept('U'):
hex := string([]rune{
s.expectfn(ishex), s.expectfn(ishex), s.expectfn(ishex), s.expectfn(ishex),
s.expectfn(ishex), s.expectfn(ishex), s.expectfn(ishex), s.expectfn(ishex),
})
code, err := strconv.ParseUint(hex, 16, 32)
if err != nil {
panic(err)
}
return runebytes(rune(code))
default:
panic(fmt.Errorf("unexpected escape code %c", s.readrune()))
}
}
func (s *scanner) scanstring(quote byte) token {
str := []byte{}
for {
switch {
case s.accept(rune(quote)):
return token{kind: strtoken, sval: string(str)}
case s.accept('\\'):
str = append(str, s.scanescape(quote)...)
default:
str = append(str, runebytes(s.readrune())...)
}
}
}
func (s *scanner) scanrune() token {
defer s.expect('\'')
switch {
case s.accept('\\'):
r := []rune(string(s.scanescape('\'')))
return token{kind: runetoken, ival: int64(r[0])}
default:
return token{kind: runetoken, ival: int64(s.readrune())}
}
}
func (s *scanner) scan() (t token) {
defer func() {
if r := recover(); r != nil {
if err, ok := r.(error); ok {
t = token{kind: errtoken, pos: s.p, eval: err}
} else {
panic(r)
}
}
}()
s.skipws()
p := s.p
defer func() {
t.pos = p
}()
switch {
case s.accept(eof):
p = s.p
return token{kind: eoftoken}
case s.peekmatch(isletter):
return s.scanident()
case s.peekmatch(isdigit):
return s.scannumber(token{})
case s.accept('"'):
return s.scanstring('"')
case s.accept('\''):
return s.scanrune()
case s.accept('$'):
s.expect('\'')
return s.scanstring('\'')
case s.accept('+'):
return s.tokensym(addtoken, "+")
case s.accept('-'):
switch {
default:
return s.tokensym(subtoken, "-")
case s.peekmatch(isdigit):
return s.scannumber(token{sval: "-"})
}
case s.accept('*'):
return s.tokensym(multoken, "*")
case s.accept('/'):
return s.tokensym(quotoken, "/")
case s.accept('%'):
return s.tokensym(remtoken, "%")
case s.accept('&'):
switch {
default:
return s.tokensym(andtoken, "&")
case s.accept('&'):
return s.tokensym(logicalandtoken, "&&")
case s.accept('^'):
return s.tokensym(andnottoken, "&^")
}
case s.accept('|'):
switch {
default:
return s.tokensym(ortoken, "|")
case s.accept('|'):
return s.tokensym(logicalortoken, "||")
}
case s.accept('^'):
switch {
default:
return s.tokensym(xortoken, "^")
}
case s.accept('<'):
switch {
default:
return s.tokensym(lessertoken, "<")
case s.accept('='):
return s.tokensym(lesserequaltoken, "<=")
case s.accept('<'):
return s.tokensym(shltoken, "<<")
}
case s.accept('>'):
switch {
default:
return s.tokensym(greatertoken, ">")
case s.accept('='):
return s.tokensym(greaterequaltoken, ">=")
case s.accept('>'):
return s.tokensym(shrtoken, ">>")
}
case s.accept('='):
switch {
case s.accept('='):
return s.tokensym(equaltoken, "==")
default:
return s.errsymf("unexpected rune %c", s.readrune())
}
case s.accept('!'):
switch {
case s.accept('='):
return s.tokensym(notequaltoken, "!=")
default:
return s.tokensym(nottoken, "!")
}
case s.accept('('):
return s.tokensym(leftparentoken, "(")
case s.accept('['):
return s.tokensym(leftbrackettoken, "[")
case s.accept(','):
return s.tokensym(commatoken, ",")
case s.accept('.'):
switch {
default:
return s.tokensym(periodtoken, ".")
case s.peekmatch(isdigit):
return s.scannumber(token{sval: "."})
}
case s.accept(')'):
return s.tokensym(rightparentoken, ")")
case s.accept(']'):
return s.tokensym(rightbrackettoken, "]")
case s.accept(':'):
return s.tokensym(colontoken, ":")
case s.accept('?'):
return s.tokensym(ternarytoken, "?")
default:
return s.errsymf("unexpected rune %c", s.readrune())
}
}
package expr
// Package represents a package value.
type Package struct {
types map[string]Type
symbols map[string]Value
}
// NewPackage creates a new package.
func NewPackage(symbols map[string]Value) Package {
pkg := Package{symbols: symbols, types: map[string]Type{}}
for key := range symbols {
pkg.types[key] = pkg.symbols[key].Type()
}
return pkg
}
// Symbol returns a symbol, or nil if the symbol doesn't exist.
func (p Package) Symbol(ident string) Value {
if symbol, ok := p.symbols[ident]; ok {
return symbol
}
return nil
}
// Type returns the type for this package.
func (p Package) Type() Type {
return NewPackageType(p.types)
}
package expr
import (
"bytes"
"fmt"
"io"
)
// Program represents a parsed expression.
type Program struct {
root node
}
// Parse parses an expression into a program.
func Parse(r io.RuneScanner) *Program {
return &Program{newparser(newscanner(r)).parse()}
}
// ParseString parses an expression from a string.
func ParseString(s string) *Program {
return Parse(bytes.NewBufferString(s))
}
type parser struct {
s *scanner
a bool
t token
}
func newparser(s *scanner) *parser {
return &parser{s: s}
}
func (p *parser) readtoken() *token {
if !p.a {
p.a = true
p.t = p.s.scan()
}
return &p.t
}
func (p *parser) consume() {
p.a = false
}
func (p *parser) accept(k tokenkind) bool {
if p.readtoken().kind == k {
p.consume()
return true
}
return false
}
func (p *parser) expect(k tokenkind) {
if p.readtoken().kind != k {
panic(fmt.Errorf("expected %s token, got %s", k, p.t.kind))
}
p.consume()
}
// This parser is strongly based on byuu's modified recursive-descent algorithm
// (particularly the 'depth' parameter.)
// https://github.com/byuu/bsnes/blob/master/nall/string/eval/parser.hpp
func (p *parser) parseexpr(depth int) node {
var n node
unary := func(op unaryop, depth int) {
n = unaryexpr{op: op, n: p.parseexpr(depth)}
}
binary := func(op binaryop, depth int) {
if n == nil {
panic("unexpected binary op")
}
n = binaryexpr{op: op, a: n, b: p.parseexpr(depth)}
}
ternary := func(depth int) {
t := ternaryexpr{}
t.a = n
t.b = p.parseexpr(depth)
p.expect(colontoken)
t.c = p.parseexpr(depth)
n = t
}
switch {
case p.accept(identtoken):
n = newidentnode(p.t)
case p.accept(inttoken):
n = newintnode(p.t)
case p.accept(floattoken):
n = newfloatnode(p.t)
case p.accept(booltoken):
n = newboolnode(p.t)
case p.accept(strtoken):
n = newstrnode(p.t)
case p.accept(runetoken):
n = newrunenode(p.t)
case p.accept(nilkeyword):
n = newnilnode(p.t)
case p.accept(leftparentoken):
n = p.parseexpr(1)
default:
}
for {
if depth >= 8 {
break
}
if n != nil && p.accept(periodtoken) {
binary(binarymember, 8)
continue
}
if n != nil && p.accept(leftparentoken) {
binary(binarycall, 1)
continue
}
if n != nil && p.accept(leftbrackettoken) {
binary(binarysubscript, 1)
continue
}
if n == nil && p.accept(addtoken) {
unary(unaryplus, 7)
}
if n == nil && p.accept(subtoken) {
unary(unarynegate, 7)
}
if n == nil && p.accept(nottoken) {
unary(unarynot, 7)
}
if n == nil && p.accept(xortoken) {
unary(unarybitnot, 7)
}
if n == nil && p.accept(multoken) {
unary(unaryderef, 7)
}
if n == nil && p.accept(andtoken) {
unary(unaryref, 7)
}
if depth >= 7 {
break
}
if p.accept(multoken) {
binary(binarymul, 7)
continue
}
if p.accept(quotoken) {
binary(binarydiv, 7)
continue
}
if p.accept(remtoken) {
binary(binaryrem, 7)
continue
}
if p.accept(shltoken) {
binary(binarylsh, 7)
continue
}
if p.accept(shrtoken) {
binary(binaryrsh, 7)
continue
}
if p.accept(andtoken) {
binary(binaryand, 7)
continue
}
if depth >= 6 {
break
}
if p.accept(addtoken) {
binary(binaryadd, 6)
continue
}
if p.accept(subtoken) {
binary(binarysub, 6)
continue
}
if p.accept(ortoken) {
binary(binaryor, 6)
continue
}
if p.accept(xortoken) {
binary(binaryxor, 6)
continue
}
if depth >= 5 {
break
}
if p.accept(equaltoken) {
binary(binaryequal, 5)
continue
}
if p.accept(notequaltoken) {
binary(binarynotequal, 5)
continue
}
if p.accept(lessertoken) {
binary(binarylesser, 5)
continue
}
if p.accept(lesserequaltoken) {
binary(binarylesserequal, 5)
continue
}
if p.accept(greatertoken) {
binary(binarygreater, 5)
continue
}
if p.accept(greaterequaltoken) {
binary(binarygreaterequal, 5)
continue
}
if depth >= 4 {
break
}
if p.accept(logicalandtoken) {
binary(binarylogicaland, 4)
continue
}
if depth >= 3 {
break
}
if p.accept(logicalortoken) {
binary(binarylogicalor, 3)
continue
}
if p.accept(ternarytoken) {
ternary(3)
continue
}
if depth >= 2 {
break
}
if p.accept(commatoken) {
binary(binarygroup, 2)
continue
}
if depth >= 1 && (p.accept(rightparentoken) || p.accept(rightbrackettoken)) {
break
}
p.expect(eoftoken)
break
}
return n
}
func (p *parser) parse() node {
return p.parseexpr(0)
}
// Code generated by "stringer -type=tokenkind"; DO NOT EDIT.
package expr
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[niltoken-0]
_ = x[errtoken-1]
_ = x[eoftoken-2]
_ = x[identtoken-3]
_ = x[inttoken-4]
_ = x[floattoken-5]
_ = x[booltoken-6]
_ = x[strtoken-7]
_ = x[runetoken-8]
_ = x[addtoken-9]
_ = x[subtoken-10]
_ = x[multoken-11]
_ = x[quotoken-12]
_ = x[remtoken-13]
_ = x[andtoken-14]
_ = x[nottoken-15]
_ = x[ortoken-16]
_ = x[xortoken-17]
_ = x[shltoken-18]
_ = x[shrtoken-19]
_ = x[andnottoken-20]
_ = x[logicalandtoken-21]
_ = x[logicalortoken-22]
_ = x[equaltoken-23]
_ = x[lessertoken-24]
_ = x[greatertoken-25]
_ = x[notequaltoken-26]
_ = x[lesserequaltoken-27]
_ = x[greaterequaltoken-28]
_ = x[leftparentoken-29]
_ = x[leftbrackettoken-30]
_ = x[commatoken-31]
_ = x[periodtoken-32]
_ = x[rightparentoken-33]
_ = x[rightbrackettoken-34]
_ = x[colontoken-35]
_ = x[ternarytoken-36]
_ = x[boolkeyword-37]
_ = x[bytekeyword-38]
_ = x[float32keyword-39]
_ = x[float64keyword-40]
_ = x[intkeyword-41]
_ = x[int8keyword-42]
_ = x[int16keyword-43]
_ = x[int32keyword-44]
_ = x[int64keyword-45]
_ = x[uintkeyword-46]
_ = x[uint8keyword-47]
_ = x[uint16keyword-48]
_ = x[uint32keyword-49]
_ = x[uint64keyword-50]
_ = x[uintptrkeyword-51]
_ = x[nilkeyword-52]
}
const _tokenkind_name = "niltokenerrtokeneoftokenidenttokeninttokenfloattokenbooltokenstrtokenrunetokenaddtokensubtokenmultokenquotokenremtokenandtokennottokenortokenxortokenshltokenshrtokenandnottokenlogicalandtokenlogicalortokenequaltokenlessertokengreatertokennotequaltokenlesserequaltokengreaterequaltokenleftparentokenleftbrackettokencommatokenperiodtokenrightparentokenrightbrackettokencolontokenternarytokenboolkeywordbytekeywordfloat32keywordfloat64keywordintkeywordint8keywordint16keywordint32keywordint64keyworduintkeyworduint8keyworduint16keyworduint32keyworduint64keyworduintptrkeywordnilkeyword"
var _tokenkind_index = [...]uint16{0, 8, 16, 24, 34, 42, 52, 61, 69, 78, 86, 94, 102, 110, 118, 126, 134, 141, 149, 157, 165, 176, 191, 205, 215, 226, 238, 251, 267, 284, 298, 314, 324, 335, 350, 367, 377, 389, 400, 411, 425, 439, 449, 460, 472, 484, 496, 507, 519, 532, 545, 558, 572, 582}
func (i tokenkind) String() string {
if i < 0 || i >= tokenkind(len(_tokenkind_index)-1) {
return "tokenkind(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _tokenkind_name[_tokenkind_index[i]:_tokenkind_index[i+1]]
}
package expr
import (
"errors"
"fmt"
"reflect"
"sync"
)
var (
primType = map[reflect.Kind]Type{
reflect.Bool: NewPrimitiveType(Bool),
reflect.Int: NewPrimitiveType(Int),
reflect.Int8: NewPrimitiveType(Int8),
reflect.Int16: NewPrimitiveType(Int16),
reflect.Int32: NewPrimitiveType(Int32),
reflect.Int64: NewPrimitiveType(Int64),
reflect.Uint: NewPrimitiveType(Uint),
reflect.Uint8: NewPrimitiveType(Uint8),
reflect.Uint16: NewPrimitiveType(Uint16),
reflect.Uint32: NewPrimitiveType(Uint32),
reflect.Uint64: NewPrimitiveType(Uint64),
reflect.Uintptr: NewPrimitiveType(Uintptr),
reflect.Float32: NewPrimitiveType(Float32),
reflect.Float64: NewPrimitiveType(Float64),
reflect.String: NewPrimitiveType(String),
}
primRType = map[Kind]reflect.Type{
Bool: reflect.TypeOf(bool(false)),
Int: reflect.TypeOf(int(0)),
Int8: reflect.TypeOf(int8(0)),
Int16: reflect.TypeOf(int16(0)),
Int32: reflect.TypeOf(int32(0)),
Int64: reflect.TypeOf(int64(0)),
Uint: reflect.TypeOf(uint(0)),
Uint8: reflect.TypeOf(uint8(0)),
Uint16: reflect.TypeOf(uint16(0)),
Uint32: reflect.TypeOf(uint32(0)),
Uint64: reflect.TypeOf(uint64(0)),
Uintptr: reflect.TypeOf(uintptr(0)),
Float32: reflect.TypeOf(float32(0)),
Float64: reflect.TypeOf(float64(0)),
String: reflect.TypeOf(string("")),
}
// ErrInvalidKind occurs when you call an inappropriate method for a given kind.
ErrInvalidKind = errors.New("invalid kind")
// ErrNotRepresentable occurs when a type is encountered that is not supported by the language.
ErrNotRepresentable = errors.New("type cannot be represented")
// ErrUntypedNil occurs when an untyped nil is used inappropriately.
ErrUntypedNil = errors.New("untyped nil value")
)
// NoSuchFieldError is returned when an unknown field is accessed.
type NoSuchFieldError struct {
field string
}
func (err NoSuchFieldError) Error() string {
return fmt.Sprintf("no such field: %s", err.field)
}
// Kind is the most basic type descriptor.
type Kind int
// Enumeration of valid kinds of types.
const (
Invalid Kind = iota
// Primitives
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
String
// Untyped constants
UntypedBool
UntypedInt
UntypedFloat
UntypedNil
// Composite types
Array
Slice
Struct
Map
Ptr
Func
Pkg
)
// Type is the representation of an expr type.
type Type interface {
Kind() Kind
String() string
}
// PrimitiveType is the type of primitives.
type PrimitiveType struct {
kind Kind
}
// NewPrimitiveType returns a new primitive type.
func NewPrimitiveType(k Kind) Type {
if k < Bool || k > String {
panic("not a primitive kind")
}
return &PrimitiveType{kind: k}
}
// String implements Type.
func (t PrimitiveType) String() string {
switch t.kind {
case Bool:
return "bool"
case Int:
return "int"
case Int8:
return "int8"
case Int16:
return "int16"
case Int32:
return "int32"
case Int64:
return "int64"
case Uint:
return "uint"
case Uint8:
return "uint8"
case Uint16:
return "uint16"
case Uint32:
return "uint32"
case Uint64:
return "uint64"
case Uintptr:
return "uintptr"
case Float32:
return "float32"
case Float64:
return "float64"
case String:
return "string"
default:
return ""
}
}
// Kind implements Type.
func (t PrimitiveType) Kind() Kind {
return t.kind
}
// littype is the type of literals.
type littype struct {
kind Kind
}
// NewLiteralType returns a new primitive type.
func NewLiteralType(k Kind) Type {
if k < UntypedBool || k > UntypedNil {
panic("not a primitive kind")
}
return &littype{kind: k}
}
// String implements Type.
func (t littype) String() string {
switch t.kind {
case UntypedBool:
return "untyped bool constant"
case UntypedInt:
return "untyped int constant"
case UntypedFloat:
return "untyped float constant"
case UntypedNil:
return "untyped nil value"
default:
return ""
}
}
func (t littype) Kind() Kind { return t.kind }
// PackageType is the type of a package.
type PackageType struct {
symbols map[string]Type
}
// NewPackageType returns a new package with the given symbols.
func NewPackageType(symbols map[string]Type) *PackageType {
return &PackageType{symbols}
}
// String implements Type.
func (PackageType) String() string {
return "package"
}
// Kind implements Type.
func (PackageType) Kind() Kind {
return Pkg
}
// Symbol returns a symbol by the given name, or nil if none could be found.
func (t PackageType) Symbol(ident string) Type {
if s, ok := t.symbols[ident]; ok {
return s
}
return nil
}
// ArrayType is the type of array-like values.
type ArrayType struct {
count int
elem Type
}
// NewArrayType returns a new array type.
func NewArrayType(count int, elem Type) *ArrayType {
return &ArrayType{count: count, elem: elem}
}
// String implements Type.
func (t ArrayType) String() string {
return fmt.Sprintf("[%d]%s", t.count, t.elem.String())
}
// Kind implements Type.
func (ArrayType) Kind() Kind {
return Array
}
// Elem is the type of element in the array.
func (t ArrayType) Elem() Type {
return t.elem
}
// Len is the length of the array.
func (t ArrayType) Len() int {
return t.count
}
// SliceType is the type of array-like values.
type SliceType struct {
elem Type
}
// NewSliceType returns a new array type.
func NewSliceType(elem Type) *SliceType {
return &SliceType{elem: elem}
}
// String implements Type.
func (t SliceType) String() string {
return "[]" + t.elem.String()
}
// Kind implements Type.
func (SliceType) Kind() Kind {
return Slice
}
// Elem is the type of element in the slice.
func (t SliceType) Elem() Type {
return t.elem
}
// MapType is the type of maps.
type MapType struct {
key, val Type
}
// NewMapType returns a new map type.
func NewMapType(key Type, val Type) Type {
return MapType{key: key, val: val}
}
// String implements Type.
func (t MapType) String() string {
return "map[" + t.key.String() + "]" + t.val.String()
}
// Kind implements Type.
func (MapType) Kind() Kind {
return Map
}
// Key is the type of the map's keys.
func (t MapType) Key() Type {
return t.key
}
// Value is the type of the map's values.
func (t MapType) Value() Type {
return t.val
}
// Field represents a struct field.
type Field struct {
Name string
Type Type
}
// StructType is the type of struct values.
type StructType struct {
fields []Field
fieldMap map[string]Field
}
// NewStructType returns a new struct type.
func NewStructType(fields []Field) *StructType {
fieldMap := map[string]Field{}
for _, field := range fields {
fieldMap[field.Name] = field
}
return &StructType{fields: fields, fieldMap: fieldMap}
}
// String implements Type.
func (t StructType) String() string {
return "struct"
}
// Kind implements Type.
func (StructType) Kind() Kind {
return Struct
}
// NumFields returns the number of fields in the struct.
func (t StructType) NumFields() int {
return len(t.fields)
}
// Field returns the nth field in the struct.
func (t StructType) Field(i int) Field {
return t.fields[i]
}
// FieldByName returns the field with the given name.
func (t StructType) FieldByName(name string) (Field, bool) {
f, ok := t.fieldMap[name]
return f, ok
}
// PtrType is the type of pointers.
type PtrType struct {
elem Type
}
// NewPtrType returns a new pointer type.
func NewPtrType(elem Type) *PtrType {
return &PtrType{elem: elem}
}
// String implements Type.
func (t PtrType) String() string {
return "*" + t.elem.String()
}
// Kind implements Type.
func (PtrType) Kind() Kind {
return Ptr
}
// Elem returns the element being pointed to by the pointer.
func (t PtrType) Elem() Type {
return t.elem
}
// FuncType is the type of function values.
type FuncType struct {
in []Type
out []Type
variadic bool
}
// NewFuncType returns a new function type.
func NewFuncType(in []Type, out []Type, variadic bool) *FuncType {
return &FuncType{in: in, out: out, variadic: variadic}
}
// String implements Type.
func (t FuncType) String() string {
return "func"
}
// Kind implements Type.
func (FuncType) Kind() Kind {
return Func
}
// NumIn returns the number of input parameters.
func (t FuncType) NumIn() int {
return len(t.in)
}
// In gets the nth input parameter.
func (t FuncType) In(i int) Type {
return t.in[i]
}
// IsVariadic returns true for variadic functions.
func (t FuncType) IsVariadic() bool {
return t.variadic
}
// NumOut returns the number of output parameters.
func (t FuncType) NumOut() int {
return len(t.out)
}
// Out gets the nth output parameter.
func (t FuncType) Out(i int) Type {
return t.out[i]
}
// TypeEqual returns true if the two types are equal.
func TypeEqual(a, b Type) bool {
// TODO: this could be a bit more precise.
return reflect.DeepEqual(a, b)
}
// toreflecttype converts an expr type into a runtime type.
func toreflecttype(t Type) reflect.Type {
switch t := t.(type) {
case *PrimitiveType:
return primRType[t.Kind()]
case *ArrayType:
return reflect.ArrayOf(t.Len(), toreflecttype(t.Elem()))
case *SliceType:
return reflect.SliceOf(toreflecttype(t.Elem()))
case *StructType:
fields := make([]reflect.StructField, 0, t.NumFields())
for i := 0; i < t.NumFields(); i++ {
field := t.Field(i)
fields = append(fields, reflect.StructField{
Name: field.Name,
Type: toreflecttype(field.Type),
})
}
return reflect.StructOf(fields)
case *MapType:
return reflect.MapOf(toreflecttype(t.Key()), toreflecttype(t.Value()))
case *PtrType:
return reflect.PtrTo(toreflecttype(t.Elem()))
case *FuncType:
nin := t.NumIn()
in := make([]reflect.Type, 0, nin)
for i := 0; i < nin; i++ {
in = append(in, toreflecttype(t.In(i)))
}
nout := t.NumOut()
out := make([]reflect.Type, 0, nout)
for i := 0; i < nout; i++ {
out = append(out, toreflecttype(t.Out(i)))
}
return reflect.FuncOf(in, out, t.IsVariadic())
default:
panic(ErrNotRepresentable)
}
}
var typemap = map[reflect.Type]Type{}
var typemutex = sync.Mutex{}
func savetype(reflect reflect.Type, expr Type) Type {
typemutex.Lock()
defer typemutex.Unlock()
typemap[reflect] = expr
return expr
}
func loadtype(reflect reflect.Type) (Type, bool) {
typemutex.Lock()
defer typemutex.Unlock()
if expr, ok := typemap[reflect]; ok {
return expr, true
}
return nil, false
}
// fromreflecttype converts a runtime type into an expr type.
func fromreflecttype(t reflect.Type) Type {
if et, ok := loadtype(t); ok {
return et
}
switch t.Kind() {
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64,
reflect.String:
return primType[t.Kind()]
case reflect.Array:
return NewArrayType(t.Len(), fromreflecttype(t.Elem()))
case reflect.Func:
nin := t.NumIn()
in := make([]Type, 0, nin)
for i := 0; i < nin; i++ {
in = append(in, fromreflecttype(t.In(i)))
}
nout := t.NumOut()
out := make([]Type, 0, nout)
for i := 0; i < nout; i++ {
out = append(out, fromreflecttype(t.Out(i)))
}
return NewFuncType(in, out, t.IsVariadic())
case reflect.Map:
return NewMapType(fromreflecttype(t.Key()), fromreflecttype(t.Elem()))
case reflect.Ptr:
et := &PtrType{}
savetype(t, et)
*et = *NewPtrType(fromreflecttype(t.Elem()))
return et
case reflect.Slice:
et := &SliceType{}
savetype(t, et)
*et = *NewSliceType(fromreflecttype(t.Elem()))
return et
case reflect.Struct:
fields := make([]Field, 0, t.NumField())
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fields = append(fields, Field{
Name: field.Name,
Type: fromreflecttype(field.Type),
})
}
return NewStructType(fields)
default:
panic(ErrNotRepresentable)
}
}
func assignable(from Type, to Type) bool {
if TypeEqual(from, to) {
return true
}
switch from.Kind() {
case UntypedNil:
switch to.Kind() {
case Ptr, Func, Slice, Map:
return true
}
case UntypedBool:
switch to.Kind() {
case Bool:
return true
}
case UntypedInt:
// TODO: Range and overflow checks.
switch to.Kind() {
case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return true
case Float32, Float64:
return true
}
case UntypedFloat:
switch to.Kind() {
case Float32, Float64:
return true
}
}
return false
}
// TypeOf returns the type of a runtime value.
func TypeOf(i interface{}) Type {
if pkg, ok := i.(Package); ok {
return pkg.Type()
}
return fromreflecttype(reflect.TypeOf(i))
}
// +build !go1.9
package restruct
import (
"github.com/go-restruct/restruct/expr"
)
var exprStdLib = map[string]expr.Value{}
// +build go1.9
package restruct
import (
"math/bits"
"github.com/go-restruct/restruct/expr"
)
var exprStdLib = map[string]expr.Value{
"bits": expr.ValueOf(expr.NewPackage(map[string]expr.Value{
"LeadingZeros": expr.ValueOf(bits.LeadingZeros),
"LeadingZeros8": expr.ValueOf(bits.LeadingZeros8),
"LeadingZeros16": expr.ValueOf(bits.LeadingZeros16),
"LeadingZeros32": expr.ValueOf(bits.LeadingZeros32),
"LeadingZeros64": expr.ValueOf(bits.LeadingZeros64),
"Len": expr.ValueOf(bits.Len),
"Len8": expr.ValueOf(bits.Len8),
"Len16": expr.ValueOf(bits.Len16),
"Len32": expr.ValueOf(bits.Len32),
"Len64": expr.ValueOf(bits.Len64),
"OnesCount": expr.ValueOf(bits.OnesCount),
"OnesCount8": expr.ValueOf(bits.OnesCount8),
"OnesCount16": expr.ValueOf(bits.OnesCount16),
"OnesCount32": expr.ValueOf(bits.OnesCount32),
"OnesCount64": expr.ValueOf(bits.OnesCount64),
"Reverse": expr.ValueOf(bits.Reverse),
"Reverse8": expr.ValueOf(bits.Reverse8),
"Reverse16": expr.ValueOf(bits.Reverse16),
"Reverse32": expr.ValueOf(bits.Reverse32),
"Reverse64": expr.ValueOf(bits.Reverse64),
"ReverseBytes": expr.ValueOf(bits.ReverseBytes),
"ReverseBytes16": expr.ValueOf(bits.ReverseBytes16),
"ReverseBytes32": expr.ValueOf(bits.ReverseBytes32),
"ReverseBytes64": expr.ValueOf(bits.ReverseBytes64),
"RotateLeft": expr.ValueOf(bits.RotateLeft),
"RotateLeft8": expr.ValueOf(bits.RotateLeft8),
"RotateLeft16": expr.ValueOf(bits.RotateLeft16),
"RotateLeft32": expr.ValueOf(bits.RotateLeft32),
"RotateLeft64": expr.ValueOf(bits.RotateLeft64),
"TrailingZeros": expr.ValueOf(bits.TrailingZeros),
"TrailingZeros8": expr.ValueOf(bits.TrailingZeros8),
"TrailingZeros16": expr.ValueOf(bits.TrailingZeros16),
"TrailingZeros32": expr.ValueOf(bits.TrailingZeros32),
"TrailingZeros64": expr.ValueOf(bits.TrailingZeros64),
})),
}
package restruct
import (
"encoding/binary"
"errors"
"fmt"
"reflect"
"sync"
"github.com/go-restruct/restruct/expr"
)
// ErrInvalidSize is returned when sizefrom is used on an invalid type.
var ErrInvalidSize = errors.New("size specified on fixed size type")
// ErrInvalidSizeOf is returned when sizefrom is used on an invalid type.
var ErrInvalidSizeOf = errors.New("sizeof specified on fixed size type")
// ErrInvalidSizeFrom is returned when sizefrom is used on an invalid type.
var ErrInvalidSizeFrom = errors.New("sizefrom specified on fixed size type")
// ErrInvalidBits is returned when bits is used on an invalid type.
var ErrInvalidBits = errors.New("bits specified on non-bitwise type")
// FieldFlags is a type for flags that can be applied to fields individually.
type FieldFlags uint64
const (
// VariantBoolFlag causes the true value of a boolean to be ~0 instead of
// just 1 (all bits are set.) This emulates the behavior of VARIANT_BOOL.
VariantBoolFlag FieldFlags = 1 << iota
// InvertedBoolFlag causes the true and false states of a boolean to be
// flipped in binary.
InvertedBoolFlag
// RootFlag is set when the field points to the root struct.
RootFlag
// ParentFlag is set when the field points to the parent struct.
ParentFlag
// DefaultFlag is set when the field is designated as a switch case default.
DefaultFlag
)
// Sizer is a type which has a defined size in binary. The SizeOf function
// returns how many bytes the type will consume in memory. This is used during
// encoding for allocation and therefore must equal the exact number of bytes
// the encoded form needs. You may use a pointer receiver even if the type is
// used by value.
type Sizer interface {
SizeOf() int
}
// BitSizer is an interface for types that need to specify their own size in
// bit-level granularity. It has the same effect as Sizer.
type BitSizer interface {
BitSize() int
}
// field represents a structure field, similar to reflect.StructField.
type field struct {
Name string
Index int
BinaryType reflect.Type
NativeType reflect.Type
Order binary.ByteOrder
SIndex int // Index of size field for a slice/string.
TIndex int // Index of target of sizeof field.
Skip int
Trivial bool
BitSize uint8
Flags FieldFlags
IsRoot bool
IsParent bool
IfExpr *expr.Program
SizeExpr *expr.Program
BitsExpr *expr.Program
InExpr *expr.Program
OutExpr *expr.Program
WhileExpr *expr.Program
SwitchExpr *expr.Program
CaseExpr *expr.Program
}
// fields represents a structure.
type fields []field
var fieldCache = map[reflect.Type][]field{}
var cacheMutex = sync.RWMutex{}
// Elem constructs a transient field representing an element of an array, slice,
// or pointer.
func (f *field) Elem() field {
// Special cases for string types, grumble grumble.
t := f.BinaryType
if t.Kind() == reflect.String {
t = reflect.TypeOf([]byte{})
}
dt := f.NativeType
if dt.Kind() == reflect.String {
dt = reflect.TypeOf([]byte{})
}
return field{
Name: "*" + f.Name,
Index: -1,
BinaryType: t.Elem(),
NativeType: dt.Elem(),
Order: f.Order,
TIndex: -1,
SIndex: -1,
Skip: 0,
Trivial: isTypeTrivial(t.Elem()),
}
}
// fieldFromType returns a field from a reflected type.
func fieldFromType(typ reflect.Type) field {
return field{
Index: -1,
BinaryType: typ,
NativeType: typ,
Order: nil,
TIndex: -1,
SIndex: -1,
Skip: 0,
Trivial: isTypeTrivial(typ),
}
}
func validBitType(typ reflect.Type) bool {
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Float32,
reflect.Complex64, reflect.Complex128:
return true
default:
return false
}
}
func validSizeType(typ reflect.Type) bool {
switch typ.Kind() {
case reflect.Slice, reflect.String:
return true
default:
return false
}
}
func parseExpr(sources ...string) *expr.Program {
for _, s := range sources {
if s != "" {
return expr.ParseString(s)
}
}
return nil
}
// fieldsFromStruct returns a slice of fields for binary packing and unpacking.
func fieldsFromStruct(typ reflect.Type) (result fields) {
if typ.Kind() != reflect.Struct {
panic(fmt.Errorf("tried to get fields from non-struct type %s", typ.Kind().String()))
}
count := typ.NumField()
sizeOfMap := map[string]int{}
for i := 0; i < count; i++ {
val := typ.Field(i)
// Skip unexported names (except _)
if val.PkgPath != "" && val.Name != "_" {
continue
}
// Parse struct tag
opts := mustParseTag(val.Tag.Get("struct"))
if opts.Ignore {
continue
}
if opts.RootFlag {
result = append(result, field{
Name: val.Name,
Index: i,
Flags: RootFlag,
})
continue
}
if opts.ParentFlag {
result = append(result, field{
Name: val.Name,
Index: i,
Flags: ParentFlag,
})
continue
}
// Derive type
ftyp := val.Type
if opts.Type != nil {
ftyp = opts.Type
}
// SizeOf
sindex := -1
tindex := -1
if j, ok := sizeOfMap[val.Name]; ok {
if !validSizeType(val.Type) {
panic(ErrInvalidSizeOf)
}
sindex = j
result[sindex].TIndex = i
delete(sizeOfMap, val.Name)
} else if opts.SizeOf != "" {
sizeOfMap[opts.SizeOf] = i
}
// SizeFrom
if opts.SizeFrom != "" {
if !validSizeType(val.Type) {
panic(ErrInvalidSizeFrom)
}
for j := 0; j < i; j++ {
val := result[j]
if opts.SizeFrom == val.Name {
sindex = j
result[sindex].TIndex = i
}
}
if sindex == -1 {
panic(fmt.Errorf("couldn't find SizeFrom field %s", opts.SizeFrom))
}
}
// Expr
ifExpr := parseExpr(opts.IfExpr, val.Tag.Get("struct-if"))
sizeExpr := parseExpr(opts.SizeExpr, val.Tag.Get("struct-size"))
bitsExpr := parseExpr(opts.BitsExpr, val.Tag.Get("struct-bits"))
inExpr := parseExpr(opts.InExpr, val.Tag.Get("struct-in"))
outExpr := parseExpr(opts.OutExpr, val.Tag.Get("struct-out"))
whileExpr := parseExpr(opts.WhileExpr, val.Tag.Get("struct-while"))
switchExpr := parseExpr(opts.SwitchExpr, val.Tag.Get("struct-switch"))
caseExpr := parseExpr(opts.CaseExpr, val.Tag.Get("struct-case"))
if sizeExpr != nil && !validSizeType(val.Type) {
panic(ErrInvalidSize)
}
if bitsExpr != nil && !validBitType(ftyp) {
panic(ErrInvalidBits)
}
// Flags
flags := FieldFlags(0)
if opts.VariantBoolFlag {
flags |= VariantBoolFlag
}
if opts.InvertedBoolFlag {
flags |= InvertedBoolFlag
}
if opts.DefaultFlag {
flags |= DefaultFlag
}
result = append(result, field{
Name: val.Name,
Index: i,
BinaryType: ftyp,
NativeType: val.Type,
Order: opts.Order,
SIndex: sindex,
TIndex: tindex,
Skip: opts.Skip,
Trivial: isTypeTrivial(ftyp),
BitSize: opts.BitSize,
Flags: flags,
IfExpr: ifExpr,
SizeExpr: sizeExpr,
BitsExpr: bitsExpr,
InExpr: inExpr,
OutExpr: outExpr,
WhileExpr: whileExpr,
SwitchExpr: switchExpr,
CaseExpr: caseExpr,
})
}
for fieldName := range sizeOfMap {
panic(fmt.Errorf("couldn't find SizeOf field %s", fieldName))
}
return
}
func cachedFieldsFromStruct(typ reflect.Type) (result fields) {
cacheMutex.RLock()
result, ok := fieldCache[typ]
cacheMutex.RUnlock()
if ok {
return
}
result = fieldsFromStruct(typ)
cacheMutex.Lock()
fieldCache[typ] = result
cacheMutex.Unlock()
return
}
// isTypeTrivial determines if a given type is constant-size.
func isTypeTrivial(typ reflect.Type) bool {
if typ == nil {
return false
}
switch typ.Kind() {
case reflect.Bool,
reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Uintptr,
reflect.Float32,
reflect.Float64,
reflect.Complex64,
reflect.Complex128:
return true
case reflect.Array, reflect.Ptr:
return isTypeTrivial(typ.Elem())
case reflect.Struct:
for _, field := range cachedFieldsFromStruct(typ) {
if !isTypeTrivial(field.BinaryType) {
return false
}
}
return true
default:
return false
}
}
func (f *field) sizer(v reflect.Value) (Sizer, bool) {
if s, ok := v.Interface().(Sizer); ok {
return s, true
}
if !v.CanAddr() {
return nil, false
}
if s, ok := v.Addr().Interface().(Sizer); ok {
return s, true
}
return nil, false
}
func (f *field) bitSizer(v reflect.Value) (BitSizer, bool) {
if s, ok := v.Interface().(BitSizer); ok {
return s, true
}
if !v.CanAddr() {
return nil, false
}
if s, ok := v.Addr().Interface().(BitSizer); ok {
return s, true
}
return nil, false
}
func (f *field) bitSizeUsingInterface(val reflect.Value) (int, bool) {
if s, ok := f.bitSizer(val); ok {
return s.BitSize(), true
}
if s, ok := f.sizer(val); ok {
return s.SizeOf() * 8, true
}
return 0, false
}
module github.com/go-restruct/restruct
go 1.12
require (
github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.4.0
)
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
/*
Package restruct implements packing and unpacking of raw binary formats.
Structures can be created with struct tags annotating the on-disk or in-memory
layout of the structure, using the "struct" struct tag, like so:
struct {
Length int `struct:"int32,sizeof=Packets"`
Packets []struct{
Source string `struct:"[16]byte"`
Timestamp int `struct:"int32,big"`
Data [256]byte `struct:"skip=8"`
}
}
To unpack data in memory to this structure, simply use Unpack with a byte slice:
msg := Message{}
restruct.Unpack(data, binary.LittleEndian, &msg)
*/
package restruct
import (
"encoding/binary"
"reflect"
)
func fieldFromIntf(v interface{}) (field, reflect.Value) {
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
f := fieldFromType(val.Type())
return f, val
}
/*
Unpack reads data from a byteslice into a value.
Two types of values are directly supported here: Unpackers and structs. You can
pass them by value or by pointer, although it is an error if Restruct is
unable to set a value because it is unaddressable.
For structs, each field will be read sequentially based on a straightforward
interpretation of the type. For example, an int32 will be read as a 32-bit
signed integer, taking 4 bytes of memory. Structures and arrays are laid out
flat with no padding or metadata.
Unexported fields are ignored, except for fields named _ - those fields will
be treated purely as padding. Padding will not be preserved through packing
and unpacking.
The behavior of deserialization can be customized using struct tags. The
following struct tag syntax is supported:
`struct:"[flags...]"`
Flags are comma-separated keys. The following are available:
type A bare type name, e.g. int32 or []string. For integer
types, it is possible to specify the number of bits,
allowing the definition of bitfields, by appending a
colon followed by the number of bits. For example,
uint32:20 would specify a field that is 20 bits long.
sizeof=[Field] Specifies that the field should be treated as a count of
the number of elements in Field.
sizefrom=[Field] Specifies that the field should determine the number of
elements in itself by reading the counter in Field.
skip=[Count] Skips Count bytes before the field. You can use this to
e.g. emulate C structure alignment.
big,msb Specifies big endian byte order. When applied to
structs, this will apply to all fields under the struct.
little,lsb Specifies little endian byte order. When applied to
structs, this will apply to all fields under the struct.
variantbool Specifies that the boolean `true` value should be
encoded as -1 instead of 1.
invertedbool Specifies that the `true` and `false` encodings for
boolean should be swapped.
*/
func Unpack(data []byte, order binary.ByteOrder, v interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
if err, ok = r.(error); !ok {
panic(err)
}
}
}()
f, val := fieldFromIntf(v)
ss := structstack{allowexpr: expressionsEnabled, buf: data}
d := decoder{structstack: ss, order: order}
d.read(f, val)
return
}
/*
SizeOf returns the binary encoded size of the given value, in bytes.
*/
func SizeOf(v interface{}) (size int, err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
ss := structstack{allowexpr: expressionsEnabled}
f, val := fieldFromIntf(v)
return ss.fieldbytes(f, val), nil
}
/*
BitSize returns the binary encoded size of the given value, in bits.
*/
func BitSize(v interface{}) (size int, err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
ss := structstack{allowexpr: expressionsEnabled}
f, val := fieldFromIntf(v)
return ss.fieldbits(f, val), nil
}
/*
Pack writes data from a datastructure into a byteslice.
Two types of values are directly supported here: Packers and structs. You can
pass them by value or by pointer.
Each structure is serialized in the same way it would be deserialized with
Unpack. See Unpack documentation for the struct tag format.
*/
func Pack(order binary.ByteOrder, v interface{}) (data []byte, err error) {
defer func() {
if r := recover(); r != nil {
data = nil
err = r.(error)
}
}()
ss := structstack{allowexpr: expressionsEnabled, buf: []byte{}}
f, val := fieldFromIntf(v)
data = make([]byte, ss.fieldbytes(f, val))
ss.buf = data
e := encoder{structstack: ss, order: order}
e.write(f, val)
return
}
此差异已折叠。
package restruct
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"reflect"
"strconv"
"github.com/pkg/errors"
)
// typeMap maps identifiers to reflect.Types.
var typeMap = map[string]reflect.Type{
"bool": reflect.TypeOf(bool(false)),
"uint8": reflect.TypeOf(uint8(0)),
"uint16": reflect.TypeOf(uint16(0)),
"uint32": reflect.TypeOf(uint32(0)),
"uint64": reflect.TypeOf(uint64(0)),
"int8": reflect.TypeOf(int8(0)),
"int16": reflect.TypeOf(int16(0)),
"int32": reflect.TypeOf(int32(0)),
"int64": reflect.TypeOf(int64(0)),
"float32": reflect.TypeOf(float32(0)),
"float64": reflect.TypeOf(float64(0)),
"complex64": reflect.TypeOf(complex64(0)),
"complex128": reflect.TypeOf(complex128(0)),
"byte": reflect.TypeOf(uint8(0)),
"rune": reflect.TypeOf(int32(0)),
"uint": reflect.TypeOf(uint(0)),
"int": reflect.TypeOf(int(0)),
"uintptr": reflect.TypeOf(uintptr(0)),
"string": reflect.SliceOf(reflect.TypeOf(uint8(0))),
}
// typeOfExpr gets a type corresponding to an expression.
func typeOfExpr(expr ast.Expr) (reflect.Type, error) {
switch expr := expr.(type) {
default:
return nil, fmt.Errorf("unexpected expression: %T", expr)
case *ast.ArrayType:
switch expr.Len {
case ast.Expr(nil):
// Slice
sub, err := typeOfExpr(expr.Elt)
if err != nil {
return nil, err
}
return reflect.SliceOf(sub), nil
default:
// Parse length expression
lexpr, ok := expr.Len.(*ast.BasicLit)
if !ok {
return nil, fmt.Errorf("invalid array size expression")
}
if lexpr.Kind != token.INT {
return nil, fmt.Errorf("invalid array size type")
}
len, err := strconv.Atoi(lexpr.Value)
if err != nil {
return nil, err
}
// Parse elem type expression
sub, err := typeOfExpr(expr.Elt)
if err != nil {
return nil, err
}
return reflect.ArrayOf(len, sub), nil
}
case *ast.Ident:
// Primitive types
typ, ok := typeMap[expr.Name]
if !ok {
return nil, fmt.Errorf("unknown type %s", expr.Name)
}
return typ, nil
case *ast.StarExpr:
// Pointer
sub, err := typeOfExpr(expr.X)
if err != nil {
return nil, err
}
return reflect.PtrTo(sub), nil
case *ast.ChanType:
return nil, fmt.Errorf("channel type not allowed")
case *ast.MapType:
return nil, fmt.Errorf("map type not allowed")
}
}
// parseType parses a Golang type string and returns a reflect.Type.
func parseType(typ string) (reflect.Type, error) {
expr, err := parser.ParseExpr(typ)
if err != nil {
return nil, errors.Wrap(err, "parsing error")
}
return typeOfExpr(expr)
}
......@@ -23,6 +23,10 @@ github.com/cyphar/filepath-securejoin
# github.com/docker/go-units v0.4.0
## explicit
github.com/docker/go-units
# github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1
## explicit
github.com/go-restruct/restruct
github.com/go-restruct/restruct/expr
# github.com/godbus/dbus/v5 v5.0.3
## explicit
github.com/godbus/dbus/v5
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册