58 lines
1.2 KiB
Go
58 lines
1.2 KiB
Go
|
package errors
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/getlantern/hidden"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
hiddenErrors = make([]hideableError, 100)
|
||
|
nextID = uint64(0)
|
||
|
hiddenMutex sync.RWMutex
|
||
|
)
|
||
|
|
||
|
type hideableError interface {
|
||
|
Error
|
||
|
id() uint64
|
||
|
setID(uint64)
|
||
|
setHiddenID(string)
|
||
|
}
|
||
|
|
||
|
// This trick saves the error to a ring buffer and embeds a non-printing
|
||
|
// hiddenID in the error's description, so that if the error is later wrapped
|
||
|
// by a standard error using something like
|
||
|
// fmt.Errorf("An error occurred: %v", thisError), we can subsequently extract
|
||
|
// the error simply using the hiddenID in the string.
|
||
|
func bufferError(e hideableError) {
|
||
|
hiddenMutex.Lock()
|
||
|
b := make([]byte, 8)
|
||
|
binary.BigEndian.PutUint64(b, nextID)
|
||
|
e.setID(nextID)
|
||
|
e.setHiddenID(hidden.ToString(b))
|
||
|
hiddenErrors[idxForID(nextID)] = e
|
||
|
nextID++
|
||
|
hiddenMutex.Unlock()
|
||
|
}
|
||
|
|
||
|
func get(hiddenID []byte) Error {
|
||
|
if len(hiddenID) != 8 {
|
||
|
return nil
|
||
|
}
|
||
|
id := binary.BigEndian.Uint64(hiddenID)
|
||
|
hiddenMutex.RLock()
|
||
|
err := hiddenErrors[idxForID(id)]
|
||
|
hiddenMutex.RUnlock()
|
||
|
if err != nil && err.id() == id {
|
||
|
// Found it!
|
||
|
return err
|
||
|
}
|
||
|
// buffer has rolled over
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func idxForID(id uint64) int {
|
||
|
return int(id % uint64(len(hiddenErrors)))
|
||
|
}
|