| package msgp |
| |
| import ( |
| "fmt" |
| "reflect" |
| ) |
| |
| const resumableDefault = false |
| |
| var ( |
| // ErrShortBytes is returned when the |
| // slice being decoded is too short to |
| // contain the contents of the message |
| ErrShortBytes error = errShort{} |
| |
| // this error is only returned |
| // if we reach code that should |
| // be unreachable |
| fatal error = errFatal{} |
| ) |
| |
| // Error is the interface satisfied |
| // by all of the errors that originate |
| // from this package. |
| type Error interface { |
| error |
| |
| // Resumable returns whether |
| // or not the error means that |
| // the stream of data is malformed |
| // and the information is unrecoverable. |
| Resumable() bool |
| } |
| |
| // contextError allows msgp Error instances to be enhanced with additional |
| // context about their origin. |
| type contextError interface { |
| Error |
| |
| // withContext must not modify the error instance - it must clone and |
| // return a new error with the context added. |
| withContext(ctx string) error |
| } |
| |
| // Cause returns the underlying cause of an error that has been wrapped |
| // with additional context. |
| func Cause(e error) error { |
| out := e |
| if e, ok := e.(errWrapped); ok && e.cause != nil { |
| out = e.cause |
| } |
| return out |
| } |
| |
| // Resumable returns whether or not the error means that the stream of data is |
| // malformed and the information is unrecoverable. |
| func Resumable(e error) bool { |
| if e, ok := e.(Error); ok { |
| return e.Resumable() |
| } |
| return resumableDefault |
| } |
| |
| // WrapError wraps an error with additional context that allows the part of the |
| // serialized type that caused the problem to be identified. Underlying errors |
| // can be retrieved using Cause() |
| // |
| // The input error is not modified - a new error should be returned. |
| // |
| // ErrShortBytes is not wrapped with any context due to backward compatibility |
| // issues with the public API. |
| // |
| func WrapError(err error, ctx ...interface{}) error { |
| switch e := err.(type) { |
| case errShort: |
| return e |
| case contextError: |
| return e.withContext(ctxString(ctx)) |
| default: |
| return errWrapped{cause: err, ctx: ctxString(ctx)} |
| } |
| } |
| |
| // ctxString converts the incoming interface{} slice into a single string. |
| func ctxString(ctx []interface{}) string { |
| out := "" |
| for idx, cv := range ctx { |
| if idx > 0 { |
| out += "/" |
| } |
| out += fmt.Sprintf("%v", cv) |
| } |
| return out |
| } |
| |
| func addCtx(ctx, add string) string { |
| if ctx != "" { |
| return add + "/" + ctx |
| } else { |
| return add |
| } |
| } |
| |
| // errWrapped allows arbitrary errors passed to WrapError to be enhanced with |
| // context and unwrapped with Cause() |
| type errWrapped struct { |
| cause error |
| ctx string |
| } |
| |
| func (e errWrapped) Error() string { |
| if e.ctx != "" { |
| return fmt.Sprintf("%s at %s", e.cause, e.ctx) |
| } else { |
| return e.cause.Error() |
| } |
| } |
| |
| func (e errWrapped) Resumable() bool { |
| if e, ok := e.cause.(Error); ok { |
| return e.Resumable() |
| } |
| return resumableDefault |
| } |
| |
| type errShort struct{} |
| |
| func (e errShort) Error() string { return "msgp: too few bytes left to read object" } |
| func (e errShort) Resumable() bool { return false } |
| |
| type errFatal struct { |
| ctx string |
| } |
| |
| func (f errFatal) Error() string { |
| out := "msgp: fatal decoding error (unreachable code)" |
| if f.ctx != "" { |
| out += " at " + f.ctx |
| } |
| return out |
| } |
| |
| func (f errFatal) Resumable() bool { return false } |
| |
| func (f errFatal) withContext(ctx string) error { f.ctx = addCtx(f.ctx, ctx); return f } |
| |
| // ArrayError is an error returned |
| // when decoding a fix-sized array |
| // of the wrong size |
| type ArrayError struct { |
| Wanted uint32 |
| Got uint32 |
| ctx string |
| } |
| |
| // Error implements the error interface |
| func (a ArrayError) Error() string { |
| out := fmt.Sprintf("msgp: wanted array of size %d; got %d", a.Wanted, a.Got) |
| if a.ctx != "" { |
| out += " at " + a.ctx |
| } |
| return out |
| } |
| |
| // Resumable is always 'true' for ArrayErrors |
| func (a ArrayError) Resumable() bool { return true } |
| |
| func (a ArrayError) withContext(ctx string) error { a.ctx = addCtx(a.ctx, ctx); return a } |
| |
| // IntOverflow is returned when a call |
| // would downcast an integer to a type |
| // with too few bits to hold its value. |
| type IntOverflow struct { |
| Value int64 // the value of the integer |
| FailedBitsize int // the bit size that the int64 could not fit into |
| ctx string |
| } |
| |
| // Error implements the error interface |
| func (i IntOverflow) Error() string { |
| str := fmt.Sprintf("msgp: %d overflows int%d", i.Value, i.FailedBitsize) |
| if i.ctx != "" { |
| str += " at " + i.ctx |
| } |
| return str |
| } |
| |
| // Resumable is always 'true' for overflows |
| func (i IntOverflow) Resumable() bool { return true } |
| |
| func (i IntOverflow) withContext(ctx string) error { i.ctx = addCtx(i.ctx, ctx); return i } |
| |
| // UintOverflow is returned when a call |
| // would downcast an unsigned integer to a type |
| // with too few bits to hold its value |
| type UintOverflow struct { |
| Value uint64 // value of the uint |
| FailedBitsize int // the bit size that couldn't fit the value |
| ctx string |
| } |
| |
| // Error implements the error interface |
| func (u UintOverflow) Error() string { |
| str := fmt.Sprintf("msgp: %d overflows uint%d", u.Value, u.FailedBitsize) |
| if u.ctx != "" { |
| str += " at " + u.ctx |
| } |
| return str |
| } |
| |
| // Resumable is always 'true' for overflows |
| func (u UintOverflow) Resumable() bool { return true } |
| |
| func (u UintOverflow) withContext(ctx string) error { u.ctx = addCtx(u.ctx, ctx); return u } |
| |
| // UintBelowZero is returned when a call |
| // would cast a signed integer below zero |
| // to an unsigned integer. |
| type UintBelowZero struct { |
| Value int64 // value of the incoming int |
| ctx string |
| } |
| |
| // Error implements the error interface |
| func (u UintBelowZero) Error() string { |
| str := fmt.Sprintf("msgp: attempted to cast int %d to unsigned", u.Value) |
| if u.ctx != "" { |
| str += " at " + u.ctx |
| } |
| return str |
| } |
| |
| // Resumable is always 'true' for overflows |
| func (u UintBelowZero) Resumable() bool { return true } |
| |
| func (u UintBelowZero) withContext(ctx string) error { |
| u.ctx = ctx |
| return u |
| } |
| |
| // A TypeError is returned when a particular |
| // decoding method is unsuitable for decoding |
| // a particular MessagePack value. |
| type TypeError struct { |
| Method Type // Type expected by method |
| Encoded Type // Type actually encoded |
| |
| ctx string |
| } |
| |
| // Error implements the error interface |
| func (t TypeError) Error() string { |
| out := fmt.Sprintf("msgp: attempted to decode type %q with method for %q", t.Encoded, t.Method) |
| if t.ctx != "" { |
| out += " at " + t.ctx |
| } |
| return out |
| } |
| |
| // Resumable returns 'true' for TypeErrors |
| func (t TypeError) Resumable() bool { return true } |
| |
| func (t TypeError) withContext(ctx string) error { t.ctx = addCtx(t.ctx, ctx); return t } |
| |
| // returns either InvalidPrefixError or |
| // TypeError depending on whether or not |
| // the prefix is recognized |
| func badPrefix(want Type, lead byte) error { |
| t := sizes[lead].typ |
| if t == InvalidType { |
| return InvalidPrefixError(lead) |
| } |
| return TypeError{Method: want, Encoded: t} |
| } |
| |
| // InvalidPrefixError is returned when a bad encoding |
| // uses a prefix that is not recognized in the MessagePack standard. |
| // This kind of error is unrecoverable. |
| type InvalidPrefixError byte |
| |
| // Error implements the error interface |
| func (i InvalidPrefixError) Error() string { |
| return fmt.Sprintf("msgp: unrecognized type prefix 0x%x", byte(i)) |
| } |
| |
| // Resumable returns 'false' for InvalidPrefixErrors |
| func (i InvalidPrefixError) Resumable() bool { return false } |
| |
| // ErrUnsupportedType is returned |
| // when a bad argument is supplied |
| // to a function that takes `interface{}`. |
| type ErrUnsupportedType struct { |
| T reflect.Type |
| |
| ctx string |
| } |
| |
| // Error implements error |
| func (e *ErrUnsupportedType) Error() string { |
| out := fmt.Sprintf("msgp: type %q not supported", e.T) |
| if e.ctx != "" { |
| out += " at " + e.ctx |
| } |
| return out |
| } |
| |
| // Resumable returns 'true' for ErrUnsupportedType |
| func (e *ErrUnsupportedType) Resumable() bool { return true } |
| |
| func (e *ErrUnsupportedType) withContext(ctx string) error { |
| o := *e |
| o.ctx = addCtx(o.ctx, ctx) |
| return &o |
| } |