Commit 06476df7 authored by Larz Conwell's avatar Larz Conwell

Return errors rather than doing panics.

This makes it simpler to handle errors in code importing this package.
parent b555ed17
...@@ -8,6 +8,14 @@ import ( ...@@ -8,6 +8,14 @@ import (
"reflect" "reflect"
) )
type TypeError struct {
typ string
}
func (te *TypeError) Error() string {
return fmt.Sprintf("no support for type '%s'", te.typ)
}
// An Encoder encodes Go data structures into pickle byte stream // An Encoder encodes Go data structures into pickle byte stream
type Encoder struct { type Encoder struct {
w io.Writer w io.Writer
...@@ -79,7 +87,7 @@ func (e *Encoder) encode(rv reflect.Value) error { ...@@ -79,7 +87,7 @@ func (e *Encoder) encode(rv reflect.Value) error {
e.w.Write([]byte{opNone}) e.w.Write([]byte{opNone})
default: default:
panic(fmt.Sprintf("no support for type '%s'", rk.String())) return &TypeError{typ: rk.String()}
} }
return nil return nil
......
...@@ -80,6 +80,7 @@ const ( ...@@ -80,6 +80,7 @@ const (
var errNotImplemented = errors.New("unimplemented opcode") var errNotImplemented = errors.New("unimplemented opcode")
var ErrInvalidPickleVersion = errors.New("invalid pickle version") var ErrInvalidPickleVersion = errors.New("invalid pickle version")
var ErrNoMarker = errors.New("no marker in stack")
type OpcodeError struct { type OpcodeError struct {
Key byte Key byte
...@@ -243,15 +244,15 @@ func (d *Decoder) mark() { ...@@ -243,15 +244,15 @@ func (d *Decoder) mark() {
} }
// Return the position of the topmost marker // Return the position of the topmost marker
func (d *Decoder) marker() int { func (d *Decoder) marker() (int, error) {
m := mark{} m := mark{}
var k int var k int
for k = len(d.stack) - 1; d.stack[k] != m && k > 0; k-- { for k = len(d.stack) - 1; d.stack[k] != m && k > 0; k-- {
} }
if k >= 0 { if k >= 0 {
return k return k, nil
} }
panic("no marker in stack") return 0, ErrNoMarker
} }
// Append a new value // Append a new value
...@@ -553,7 +554,11 @@ func (d *Decoder) global() error { ...@@ -553,7 +554,11 @@ func (d *Decoder) global() error {
} }
func (d *Decoder) loadDict() error { func (d *Decoder) loadDict() error {
k := d.marker() k, err := d.marker()
if err != nil {
return err
}
m := make(map[interface{}]interface{}, 0) m := make(map[interface{}]interface{}, 0)
items := d.stack[k+1:] items := d.stack[k+1:]
for i := 0; i < len(items); i += 2 { for i := 0; i < len(items); i += 2 {
...@@ -570,7 +575,11 @@ func (d *Decoder) loadEmptyDict() error { ...@@ -570,7 +575,11 @@ func (d *Decoder) loadEmptyDict() error {
} }
func (d *Decoder) loadAppends() error { func (d *Decoder) loadAppends() error {
k := d.marker() k, err := d.marker()
if err != nil {
return err
}
l := d.stack[k-1] l := d.stack[k-1]
switch l.(type) { switch l.(type) {
case []interface{}: case []interface{}:
...@@ -621,14 +630,22 @@ func (d *Decoder) loadBool(b bool) error { ...@@ -621,14 +630,22 @@ func (d *Decoder) loadBool(b bool) error {
} }
func (d *Decoder) loadList() error { func (d *Decoder) loadList() error {
k := d.marker() k, err := d.marker()
if err != nil {
return err
}
v := append([]interface{}{}, d.stack[k+1:]...) v := append([]interface{}{}, d.stack[k+1:]...)
d.stack = append(d.stack[:k], v) d.stack = append(d.stack[:k], v)
return nil return nil
} }
func (d *Decoder) loadTuple() error { func (d *Decoder) loadTuple() error {
k := d.marker() k, err := d.marker()
if err != nil {
return err
}
v := append([]interface{}{}, d.stack[k+1:]...) v := append([]interface{}{}, d.stack[k+1:]...)
d.stack = append(d.stack[:k], v) d.stack = append(d.stack[:k], v)
return nil return nil
...@@ -700,7 +717,11 @@ func (d *Decoder) loadSetItem() error { ...@@ -700,7 +717,11 @@ func (d *Decoder) loadSetItem() error {
} }
func (d *Decoder) loadSetItems() error { func (d *Decoder) loadSetItems() error {
k := d.marker() k, err := d.marker()
if err != nil {
return err
}
l := d.stack[k-1] l := d.stack[k-1]
switch m := l.(type) { switch m := l.(type) {
case map[interface{}]interface{}: case map[interface{}]interface{}:
......
...@@ -18,7 +18,11 @@ func TestMarker(t *testing.T) { ...@@ -18,7 +18,11 @@ func TestMarker(t *testing.T) {
buf := bytes.Buffer{} buf := bytes.Buffer{}
dec := NewDecoder(&buf) dec := NewDecoder(&buf)
dec.mark() dec.mark()
if dec.marker() != 0 { k, err := dec.marker()
if err != nil {
t.Error(err)
}
if k != 0 {
t.Error("no marker found") t.Error("no marker found")
} }
} }
...@@ -50,7 +54,7 @@ func TestDecode(t *testing.T) { ...@@ -50,7 +54,7 @@ func TestDecode(t *testing.T) {
{"unicode", "V\\u65e5\\u672c\\u8a9e\np0\n.", string("日本語")}, {"unicode", "V\\u65e5\\u672c\\u8a9e\np0\n.", string("日本語")},
{"empty dict", "(dp0\n.", make(map[interface{}]interface{})}, {"empty dict", "(dp0\n.", make(map[interface{}]interface{})},
{"dict with strings", "(dp0\nS'a'\np1\nS'1'\np2\nsS'b'\np3\nS'2'\np4\ns.", map[interface{}]interface{}{"a": "1", "b": "2"}}, {"dict with strings", "(dp0\nS'a'\np1\nS'1'\np2\nsS'b'\np3\nS'2'\np4\ns.", map[interface{}]interface{}{"a": "1", "b": "2"}},
{"GLOBAL and REDUCE opcodes", "cfoo\nbar\nS'bing'\n\x85R.", Call{Callable: Class{Module: "foo", Name: "bar"}, Args: []interface{} {"bing"}}}, {"GLOBAL and REDUCE opcodes", "cfoo\nbar\nS'bing'\n\x85R.", Call{Callable: Class{Module: "foo", Name: "bar"}, Args: []interface{}{"bing"}}},
{"LONG_BINPUT opcode", "(lr0000I17\na.", []interface{}{int64(17)}}, {"LONG_BINPUT opcode", "(lr0000I17\na.", []interface{}{int64(17)}},
{"graphite message1", string(graphitePickle1), []interface{}{map[interface{}]interface{}{"values": []interface{}{float64(473), float64(497), float64(540), float64(1497), float64(1808), float64(1890), float64(2013), float64(1821), float64(1847), float64(2176), float64(2156), float64(1250), float64(2055), float64(1570), None{}, None{}}, "start": int64(1383782400), "step": int64(86400), "end": int64(1385164800), "name": "ZZZZ.UUUUUUUU.CCCCCCCC.MMMMMMMM.XXXXXXXXX.TTT"}}}, {"graphite message1", string(graphitePickle1), []interface{}{map[interface{}]interface{}{"values": []interface{}{float64(473), float64(497), float64(540), float64(1497), float64(1808), float64(1890), float64(2013), float64(1821), float64(1847), float64(2176), float64(2156), float64(1250), float64(2055), float64(1570), None{}, None{}}, "start": int64(1383782400), "step": int64(86400), "end": int64(1385164800), "name": "ZZZZ.UUUUUUUU.CCCCCCCC.MMMMMMMM.XXXXXXXXX.TTT"}}},
{"graphite message2", string(graphitePickle2), []interface{}{map[interface{}]interface{}{"values": []interface{}{float64(473), float64(497), float64(540), float64(1497), float64(1808), float64(1890), float64(2013), float64(1821), float64(1847), float64(2176), float64(2156), float64(1250), float64(2055), float64(1570), None{}, None{}}, "start": int64(1383782400), "step": int64(86400), "end": int64(1385164800), "name": "user.login.area.machine.metric.minute"}}}, {"graphite message2", string(graphitePickle2), []interface{}{map[interface{}]interface{}{"values": []interface{}{float64(473), float64(497), float64(540), float64(1497), float64(1808), float64(1890), float64(2013), float64(1821), float64(1847), float64(2176), float64(2156), float64(1250), float64(2055), float64(1570), None{}, None{}}, "start": int64(1383782400), "step": int64(86400), "end": int64(1385164800), "name": "user.login.area.machine.metric.minute"}}},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment