Supplement document of nbt API
This commit is contained in:
@ -14,6 +14,14 @@ func Unmarshal(data []byte, v interface{}) error {
|
||||
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) {
|
||||
val := reflect.ValueOf(v)
|
||||
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
|
||||
// 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 {
|
||||
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
|
||||
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)
|
||||
if u != nil {
|
||||
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:
|
||||
if vl := val.Len(); vl < int(listLen) {
|
||||
return fmt.Errorf(
|
||||
"TagList %s has len %d, but array %v only has len %d",
|
||||
tagName, listLen, val.Type(), vl)
|
||||
"TagList has len %d, but array %v only has len %d",
|
||||
listLen, val.Type(), vl)
|
||||
}
|
||||
buf = val
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -302,7 +310,7 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) err
|
||||
}
|
||||
field := tinfo.findIndexByName(tn)
|
||||
if field != -1 {
|
||||
err = d.unmarshal(val.Field(field), tt, tn)
|
||||
err = d.unmarshal(val.Field(field), tt)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
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)
|
||||
}
|
||||
val.SetMapIndex(reflect.ValueOf(tn), v.Elem())
|
||||
@ -344,7 +352,7 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) err
|
||||
break
|
||||
}
|
||||
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)
|
||||
}
|
||||
buf[tn] = value
|
||||
|
@ -11,6 +11,9 @@ import (
|
||||
"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) {
|
||||
var buf bytes.Buffer
|
||||
err := NewEncoder(&buf).Encode(v, "")
|
||||
@ -25,6 +28,10 @@ func NewEncoder(w io.Writer) *Encoder {
|
||||
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 {
|
||||
val := reflect.ValueOf(v)
|
||||
return e.marshal(val, getTagType(val), tagName)
|
||||
|
@ -5,35 +5,75 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//goland:noinspection SpellCheckingInspection
|
||||
func ExampleUnmarshal() {
|
||||
var data = []byte{
|
||||
0x08, 0x00, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x09,
|
||||
0x42, 0x61, 0x6e, 0x61, 0x6e, 0x72, 0x61, 0x6d, 0x61,
|
||||
func ExampleDecoder_Decode() {
|
||||
reader := bytes.NewReader([]byte{
|
||||
0x0a, // Start TagCompound("")
|
||||
0x00, 0x00,
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
decoder := NewDecoder(reader)
|
||||
_, err := decoder.Decode(&value)
|
||||
if err != nil {
|
||||
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:
|
||||
// 0a 00 00 08 00 04 6e 61 6d 65 00 04 54 6e 7a 65 00
|
||||
|
@ -2,10 +2,13 @@ package nbt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RawMessage stores the raw binary data of NBT.
|
||||
type RawMessage struct {
|
||||
Type byte
|
||||
Data []byte
|
||||
@ -49,3 +52,12 @@ func (m RawMessage) String() 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