Supplement document of nbt API
This commit is contained in:
@ -14,6 +14,14 @@ func Unmarshal(data []byte, v interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode method decodes an NBT value from the reader underline the Decoder into v.
|
||||||
|
// Internally try to handle all possible v by reflection,
|
||||||
|
// but the type of v must matches the NBT value logically.
|
||||||
|
// For example, you can decode an NBT value which root tag is TagCompound(0x0a)
|
||||||
|
// into a struct or map, but not a string.
|
||||||
|
//
|
||||||
|
// This method also return tag name of the root tag.
|
||||||
|
// In real world, it is often empty, but the API should allows you to get it when ever you want.
|
||||||
func (d *Decoder) Decode(v interface{}) (string, error) {
|
func (d *Decoder) Decode(v interface{}) (string, error) {
|
||||||
val := reflect.ValueOf(v)
|
val := reflect.ValueOf(v)
|
||||||
if val.Kind() != reflect.Ptr {
|
if val.Kind() != reflect.Ptr {
|
||||||
@ -31,7 +39,7 @@ func (d *Decoder) Decode(v interface{}) (string, error) {
|
|||||||
|
|
||||||
// We decode val not val.Elem because the NBTDecoder interface
|
// We decode val not val.Elem because the NBTDecoder interface
|
||||||
// test must be applied at the top level of the value.
|
// test must be applied at the top level of the value.
|
||||||
err = d.unmarshal(val, tagType, tagName)
|
err = d.unmarshal(val, tagType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tagName, fmt.Errorf("nbt: fail to decode tag %q: %w", tagName, err)
|
return tagName, fmt.Errorf("nbt: fail to decode tag %q: %w", tagName, err)
|
||||||
}
|
}
|
||||||
@ -51,7 +59,7 @@ func (d *Decoder) checkCompressed(head byte) (compress string) {
|
|||||||
// ErrEND error will be returned when reading a NBT with only Tag_End
|
// ErrEND error will be returned when reading a NBT with only Tag_End
|
||||||
var ErrEND = errors.New("unexpected TAG_End")
|
var ErrEND = errors.New("unexpected TAG_End")
|
||||||
|
|
||||||
func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) error {
|
func (d *Decoder) unmarshal(val reflect.Value, tagType byte) error {
|
||||||
u, val := indirect(val, tagType == TagEnd)
|
u, val := indirect(val, tagType == TagEnd)
|
||||||
if u != nil {
|
if u != nil {
|
||||||
return u.Decode(tagType, d.r)
|
return u.Decode(tagType, d.r)
|
||||||
@ -271,13 +279,13 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) err
|
|||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
if vl := val.Len(); vl < int(listLen) {
|
if vl := val.Len(); vl < int(listLen) {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"TagList %s has len %d, but array %v only has len %d",
|
"TagList has len %d, but array %v only has len %d",
|
||||||
tagName, listLen, val.Type(), vl)
|
listLen, val.Type(), vl)
|
||||||
}
|
}
|
||||||
buf = val
|
buf = val
|
||||||
}
|
}
|
||||||
for i := 0; i < int(listLen); i++ {
|
for i := 0; i < int(listLen); i++ {
|
||||||
if err := d.unmarshal(buf.Index(i), listType, ""); err != nil {
|
if err := d.unmarshal(buf.Index(i), listType); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +310,7 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) err
|
|||||||
}
|
}
|
||||||
field := tinfo.findIndexByName(tn)
|
field := tinfo.findIndexByName(tn)
|
||||||
if field != -1 {
|
if field != -1 {
|
||||||
err = d.unmarshal(val.Field(field), tt, tn)
|
err = d.unmarshal(val.Field(field), tt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("fail to decode tag %q: %w", tn, err)
|
return fmt.Errorf("fail to decode tag %q: %w", tn, err)
|
||||||
}
|
}
|
||||||
@ -328,7 +336,7 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) err
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
v := reflect.New(val.Type().Elem())
|
v := reflect.New(val.Type().Elem())
|
||||||
if err = d.unmarshal(v.Elem(), tt, tn); err != nil {
|
if err = d.unmarshal(v.Elem(), tt); err != nil {
|
||||||
return fmt.Errorf("fail to decode tag %q: %w", tn, err)
|
return fmt.Errorf("fail to decode tag %q: %w", tn, err)
|
||||||
}
|
}
|
||||||
val.SetMapIndex(reflect.ValueOf(tn), v.Elem())
|
val.SetMapIndex(reflect.ValueOf(tn), v.Elem())
|
||||||
@ -344,7 +352,7 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) err
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
var value interface{}
|
var value interface{}
|
||||||
if err = d.unmarshal(reflect.ValueOf(&value).Elem(), tt, tn); err != nil {
|
if err = d.unmarshal(reflect.ValueOf(&value).Elem(), tt); err != nil {
|
||||||
return fmt.Errorf("fail to decode tag %q: %w", tn, err)
|
return fmt.Errorf("fail to decode tag %q: %w", tn, err)
|
||||||
}
|
}
|
||||||
buf[tn] = value
|
buf[tn] = value
|
||||||
|
@ -11,6 +11,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Marshal is the shortcut of NewEncoder().Encode() with empty tag name.
|
||||||
|
// Notices that repeatedly init buffers is low efficiency.
|
||||||
|
// Using Encoder and Reset the buffer in each times is recommended in that cases.
|
||||||
func Marshal(v interface{}) ([]byte, error) {
|
func Marshal(v interface{}) ([]byte, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err := NewEncoder(&buf).Encode(v, "")
|
err := NewEncoder(&buf).Encode(v, "")
|
||||||
@ -25,6 +28,10 @@ func NewEncoder(w io.Writer) *Encoder {
|
|||||||
return &Encoder{w: w}
|
return &Encoder{w: w}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encode encodes v into the writer inside Encoder with the root tag named tagName.
|
||||||
|
// In most cases, the root tag typed TagCompound and the tag name is empty string,
|
||||||
|
// but any other type is allowed just because there is valid technically. Once if
|
||||||
|
// you should pass an string into this, you should get a TagString.
|
||||||
func (e *Encoder) Encode(v interface{}, tagName string) error {
|
func (e *Encoder) Encode(v interface{}, tagName string) error {
|
||||||
val := reflect.ValueOf(v)
|
val := reflect.ValueOf(v)
|
||||||
return e.marshal(val, getTagType(val), tagName)
|
return e.marshal(val, getTagType(val), tagName)
|
||||||
|
@ -5,35 +5,75 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
//goland:noinspection SpellCheckingInspection
|
func ExampleDecoder_Decode() {
|
||||||
func ExampleUnmarshal() {
|
reader := bytes.NewReader([]byte{
|
||||||
var data = []byte{
|
0x0a, // Start TagCompound("")
|
||||||
0x08, 0x00, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x09,
|
0x00, 0x00,
|
||||||
0x42, 0x61, 0x6e, 0x61, 0x6e, 0x72, 0x61, 0x6d, 0x61,
|
|
||||||
|
0x08, // TagString("Author"): "Tnze"
|
||||||
|
0x00, 0x06, 'A', 'u', 't', 'h', 'o', 'r',
|
||||||
|
0x00, 0x04, 'T', 'n', 'z', 'e',
|
||||||
|
|
||||||
|
0x00, // End TagCompound
|
||||||
|
})
|
||||||
|
|
||||||
|
var value struct {
|
||||||
|
Author string
|
||||||
}
|
}
|
||||||
|
|
||||||
var Name string
|
decoder := NewDecoder(reader)
|
||||||
|
_, err := decoder.Decode(&value)
|
||||||
if err := Unmarshal(data, &Name); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(Name)
|
|
||||||
|
|
||||||
// Output: Bananrama
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleMarshal() {
|
|
||||||
var value = struct {
|
|
||||||
Name string `nbt:"name"`
|
|
||||||
}{"Tnze"}
|
|
||||||
|
|
||||||
data, err := Marshal(value)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("% 02x ", data)
|
fmt.Println(value)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {Tnze}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDecoder_Decode_singleTagString() {
|
||||||
|
reader := bytes.NewReader([]byte{
|
||||||
|
// TagString
|
||||||
|
0x08,
|
||||||
|
// TagName
|
||||||
|
0x00, 0x04,
|
||||||
|
0x6e, 0x61, 0x6d, 0x65,
|
||||||
|
// Content
|
||||||
|
0x00, 0x09,
|
||||||
|
0x42, 0x61, 0x6e, 0x61, 0x6e, 0x72, 0x61, 0x6d, 0x61,
|
||||||
|
})
|
||||||
|
|
||||||
|
var Name string
|
||||||
|
|
||||||
|
decoder := NewDecoder(reader)
|
||||||
|
tagName, err := decoder.Decode(&Name)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(tagName)
|
||||||
|
fmt.Println(Name)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// name
|
||||||
|
// Bananrama
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleEncoder_Encode_tagCompound() {
|
||||||
|
var value = struct {
|
||||||
|
Name string `nbt:"name"`
|
||||||
|
}{"Tnze"}
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
encoder := NewEncoder(&buff)
|
||||||
|
err := encoder.Encode(value, "")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("% 02x ", buff.Bytes())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// 0a 00 00 08 00 04 6e 61 6d 65 00 04 54 6e 7a 65 00
|
// 0a 00 00 08 00 04 6e 61 6d 65 00 04 54 6e 7a 65 00
|
||||||
|
@ -2,10 +2,13 @@ package nbt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RawMessage stores the raw binary data of NBT.
|
||||||
type RawMessage struct {
|
type RawMessage struct {
|
||||||
Type byte
|
Type byte
|
||||||
Data []byte
|
Data []byte
|
||||||
@ -49,3 +52,12 @@ func (m RawMessage) String() string {
|
|||||||
}
|
}
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m RawMessage) Unmarshal(v interface{}) error {
|
||||||
|
d := NewDecoder(bytes.NewReader(m.Data))
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
if val.Kind() != reflect.Ptr {
|
||||||
|
return errors.New("nbt: non-pointer passed to Decode")
|
||||||
|
}
|
||||||
|
return d.unmarshal(val, m.Type)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user