Merge pull request #242 from wongoo/loop_encode_decode

support clean encoder/decoder, discard decode buffer
diff --git a/date.go b/date.go
index f3db553..e1af345 100644
--- a/date.go
+++ b/date.go
@@ -73,7 +73,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 	}
 
 	switch {
diff --git a/decode.go b/decode.go
index f10456c..35de60e 100644
--- a/decode.go
+++ b/decode.go
@@ -79,6 +79,14 @@
 	return &Decoder{reader: bufio.NewReader(bytes.NewReader(b)), isSkip: true}
 }
 
+// Clean clean the Decoder (room) for a new object decoding.
+// Notice it won't reset reader buffer and will continue to read data from it.
+func (d *Decoder) Clean() {
+	d.typeRefs = &TypeRefs{records: map[string]bool{}}
+	d.refs = nil
+	d.classInfoList = nil
+}
+
 /////////////////////////////////////////
 // utilities
 /////////////////////////////////////////
@@ -86,15 +94,7 @@
 func (d *Decoder) Reset(b []byte) *Decoder {
 	// reuse reader buf, avoid allocate
 	d.reader.Reset(bytes.NewReader(b))
-	d.typeRefs = &TypeRefs{records: map[string]bool{}}
-
-	if d.refs != nil {
-		d.refs = nil
-	}
-	if d.classInfoList != nil {
-		d.classInfoList = nil
-	}
-
+	d.Clean()
 	return d
 }
 
@@ -109,11 +109,16 @@
 	return d.reader.Buffered()
 }
 
-// read a byte from Decoder, advance the ptr
-func (d *Decoder) readByte() (byte, error) {
+// ReadByte read a byte from Decoder, advance the ptr
+func (d *Decoder) ReadByte() (byte, error) {
 	return d.reader.ReadByte()
 }
 
+// Discard skips the next n bytes
+func (d *Decoder) Discard(n int) (int, error) {
+	return d.reader.Discard(n)
+}
+
 // unread a byte
 func (d *Decoder) unreadByte() error {
 	return d.reader.UnreadByte()
@@ -198,7 +203,7 @@
 		tag byte
 	)
 
-	tag, err = d.readByte()
+	tag, err = d.ReadByte()
 	if err == io.EOF {
 		return nil, err
 	}
diff --git a/double.go b/double.go
index 376a1f4..751af91 100644
--- a/double.go
+++ b/double.go
@@ -115,7 +115,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 	}
 	switch tag {
 	case BC_LONG_INT:
diff --git a/encode.go b/encode.go
index 2ae6791..b0344d1 100644
--- a/encode.go
+++ b/encode.go
@@ -48,6 +48,14 @@
 	}
 }
 
+// Clean clean the Encoder (room) for a new object encoding.
+func (e *Encoder) Clean() {
+	var buffer = make([]byte, 64)
+	e.classInfoList = nil
+	e.buffer = buffer[:0]
+	e.refMap = make(map[unsafe.Pointer]_refElem, 7)
+}
+
 // Buffer returns byte buffer
 func (e *Encoder) Buffer() []byte {
 	return e.buffer[:]
diff --git a/hessian_demo/loop_encode_decode_test.go b/hessian_demo/loop_encode_decode_test.go
new file mode 100644
index 0000000..f6acc0d
--- /dev/null
+++ b/hessian_demo/loop_encode_decode_test.go
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hessian_demo
+
+import (
+	"reflect"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+)
+
+type User struct {
+	Name string
+	Age  int
+}
+
+func (User) JavaClassName() string {
+	return "hessian.demo.User"
+}
+
+type Location struct {
+	Address  string
+	Postcode string
+}
+
+func (Location) JavaClassName() string {
+	return "hessian.demo.Location"
+}
+
+func TestLoopEncodeDecode(t *testing.T) {
+	u := &User{
+		Name: "wongoo",
+		Age:  18,
+	}
+	loc := &Location{
+		Address:  "xiamen",
+		Postcode: "361000",
+	}
+
+	// demo a bytes buffer from client to server
+	var bytes []byte
+	e := hessian.NewEncoder()
+
+	// -------- encode 1
+	bytes = append(bytes, encodeWithFlagAndLength(e, 'D', u)...)
+
+	// -------- encode 2
+	bytes = append(bytes, encodeWithFlagAndLength(e, 'S', 12345)...)
+
+	// -------- encode 3
+	bytes = append(bytes, encodeWithFlagAndLength(e, 'D', loc)...)
+
+	// -------- encode 4
+	bytes = append(bytes, encodeWithFlagAndLength(e, 'D', "hello")...)
+
+	// demo a decoder to decode buffer from client
+	d := hessian.NewDecoder(bytes)
+
+	// -------- decode 1
+	decodeFlagAndLengthAndData(t, d, u)
+
+	// -------- decode 2
+	decodeFlagAndLengthAndData(t, d, 12345)
+
+	// -------- decode 3
+	decodeFlagAndLengthAndData(t, d, loc)
+
+	// -------- decode 4
+	decodeFlagAndLengthAndData(t, d, "hello")
+}
+
+// encode format: [flag][length][binary data]
+func encodeWithFlagAndLength(e *hessian.Encoder, flag byte, obj interface{}) []byte {
+	var bytes []byte
+	bytes = append(bytes, flag)
+
+	e.Clean()
+	_ = e.Encode(obj)
+	dataBytes := e.Buffer()
+
+	length := len(dataBytes)
+
+	e.Clean()
+	_ = e.Encode(length)
+	lengthBytes := e.Buffer()
+
+	bytes = append(bytes, lengthBytes...)
+	bytes = append(bytes, dataBytes...)
+
+	return bytes
+}
+
+func decodeFlagAndLengthAndData(t *testing.T, d *hessian.Decoder, expect interface{}) {
+	// decode flag
+	d.Clean()
+	flag, _ := d.ReadByte()
+
+	// decode length
+	d.Clean()
+	lengthObj, _ := d.Decode()
+	length := lengthObj.(int64)
+
+	// skip data when flag='S'
+	if flag == 'S' {
+		_, _ = d.Discard(int(length))
+		return
+	}
+
+	// decode data
+	d.Clean()
+	res, _ := d.Decode()
+
+	// check
+	assert.True(t, reflect.DeepEqual(expect, res))
+}
diff --git a/int.go b/int.go
index 0581170..dd06d11 100644
--- a/int.go
+++ b/int.go
@@ -66,7 +66,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 	}
 
 	switch {
diff --git a/java_sql_time.go b/java_sql_time.go
index e6861bb..a359e8b 100644
--- a/java_sql_time.go
+++ b/java_sql_time.go
@@ -126,7 +126,7 @@
 	// add pointer ref so that ref the same object
 	d.appendRefs(vRef.Interface())
 
-	tag, err := d.readByte()
+	tag, err := d.ReadByte()
 	if err == io.EOF {
 		return nil, err
 	}
diff --git a/list.go b/list.go
index 0eed874..d8ed019 100644
--- a/list.go
+++ b/list.go
@@ -269,7 +269,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, err = d.readByte()
+		tag, err = d.ReadByte()
 		if err != nil {
 			return nil, perrors.WithStack(err)
 		}
diff --git a/long.go b/long.go
index d8d2005..e170700 100644
--- a/long.go
+++ b/long.go
@@ -70,7 +70,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 	}
 
 	switch {
@@ -102,7 +102,7 @@
 		return int64(tag-BC_INT_SHORT_ZERO)<<16 + int64(buf[0])<<8 + int64(buf[1]), nil
 
 	case tag == BC_DOUBLE_BYTE:
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 		return int64(tag), nil
 
 	case tag == BC_DOUBLE_SHORT:
diff --git a/map.go b/map.go
index dc7a21b..c6e3044 100644
--- a/map.go
+++ b/map.go
@@ -159,7 +159,7 @@
 	)
 
 	//tag, _ = d.readBufByte()
-	tag, err = d.readByte()
+	tag, err = d.ReadByte()
 	// check error
 	if err != nil {
 		return perrors.WithStack(err)
@@ -236,7 +236,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 	}
 
 	switch {
@@ -277,7 +277,7 @@
 					}
 				}
 			}
-			_, err = d.readByte()
+			_, err = d.ReadByte()
 			if err != nil {
 				return nil, perrors.WithStack(err)
 			}
@@ -299,7 +299,7 @@
 				}
 				m[k] = v
 			}
-			_, err = d.readByte()
+			_, err = d.ReadByte()
 			if err != nil {
 				return nil, perrors.WithStack(err)
 			}
@@ -320,7 +320,7 @@
 			}
 			m[k] = v
 		}
-		_, err = d.readByte()
+		_, err = d.ReadByte()
 		if err != nil {
 			return nil, perrors.WithStack(err)
 		}
diff --git a/object.go b/object.go
index 033db94..bec2de4 100644
--- a/object.go
+++ b/object.go
@@ -619,7 +619,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 	}
 
 	switch {
diff --git a/ref.go b/ref.go
index 9987325..9900a7a 100644
--- a/ref.go
+++ b/ref.go
@@ -157,7 +157,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 	}
 
 	switch {
diff --git a/string.go b/string.go
index a2a24ce..2581585 100644
--- a/string.go
+++ b/string.go
@@ -225,7 +225,7 @@
 		return int(tag - 0x00), nil
 
 	case tag >= 0x30 && tag <= 0x33:
-		b, err := d.readByte()
+		b, err := d.ReadByte()
 		if err != nil {
 			return -1, perrors.WithStack(err)
 		}
@@ -234,12 +234,12 @@
 		return length, nil
 
 	case tag == BC_STRING_CHUNK || tag == BC_STRING:
-		b0, err := d.readByte()
+		b0, err := d.ReadByte()
 		if err != nil {
 			return -1, perrors.WithStack(err)
 		}
 
-		b1, err := d.readByte()
+		b1, err := d.ReadByte()
 		if err != nil {
 			return -1, perrors.WithStack(err)
 		}
@@ -262,7 +262,7 @@
 	if flag != TAG_READ {
 		tag = byte(flag)
 	} else {
-		tag, _ = d.readByte()
+		tag, _ = d.ReadByte()
 	}
 
 	switch {
@@ -325,7 +325,7 @@
 					return *(*string)(unsafe.Pointer(&b)), nil
 				}
 
-				b, _ := d.readByte()
+				b, _ := d.ReadByte()
 				switch {
 				case (tag >= BC_STRING_DIRECT && tag <= STRING_DIRECT_MAX) ||
 					(tag >= 0x30 && tag <= 0x33) ||
@@ -484,7 +484,7 @@
 
 				if remain := offset - prev - nread; remain > 0 {
 					if remain == 1 {
-						ch, err := d.readByte()
+						ch, err := d.ReadByte()
 						if err != nil {
 							return s, perrors.WithStack(err)
 						}
@@ -514,7 +514,7 @@
 			}
 
 			// decode byte
-			ch, err := d.readByte()
+			ch, err := d.ReadByte()
 			if err != nil {
 				if err == io.EOF {
 					break
@@ -526,7 +526,7 @@
 				bytesBuf[offset] = ch
 				offset++
 			} else if (ch & 0xe0) == 0xc0 {
-				ch1, err := d.readByte()
+				ch1, err := d.ReadByte()
 				if err != nil {
 					return s, perrors.WithStack(err)
 				}