323 lines
6.4 KiB
Go
323 lines
6.4 KiB
Go
package stalecucumber
|
|
|
|
import "fmt"
|
|
import "math/big"
|
|
|
|
/**
|
|
Opcode: LONG1 (0x8a)
|
|
Long integer using one-byte length.
|
|
An arbitrary length integer encoded as a bytestring.
|
|
A single byte following the opcode indicates the length
|
|
of the bytestring
|
|
If the string length is zero, then the value is zero.
|
|
Otherwise the bytestring is 256-complement representation
|
|
of an integer in reverse.
|
|
**
|
|
Stack before: []
|
|
Stack after: [long]
|
|
**/
|
|
func (pm *PickleMachine) opcode_LONG1() error {
|
|
var l uint8
|
|
err := pm.readBinaryInto(&l, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if l == 0 {
|
|
pm.push(big.NewInt(0))
|
|
return nil
|
|
}
|
|
|
|
reversedData, err := pm.readFixedLengthRaw(int64(l))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//For no obvious reason, the python pickler
|
|
//always reverses the bytes. Reverse it here
|
|
var data [256]byte
|
|
{
|
|
var j int
|
|
for i := len(reversedData) - 1; i != -1; i-- {
|
|
data[j] = reversedData[i]
|
|
j++
|
|
}
|
|
}
|
|
|
|
v := new(big.Int)
|
|
v.SetBytes(data[:l])
|
|
|
|
invertIfNegative(data[0], v, int(l))
|
|
|
|
pm.push(v)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func invertIfNegative(first byte, v *big.Int, l int) {
|
|
var negative bool
|
|
//Check for negative number.
|
|
negative = (0x80 & first) != 0x0
|
|
|
|
if negative {
|
|
offset := big.NewInt(1)
|
|
offset.Lsh(offset, uint(l*8))
|
|
v.Sub(v, offset)
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
Opcode: LONG4 (0x8b)
|
|
Long integer using found-byte length.
|
|
|
|
An arbitrary length integer encoded as a bytestring.
|
|
A four-byte signed little-endian integer
|
|
following the opcode indicates the length of the bytestring.
|
|
If the string length is zero, then the value is zero.
|
|
Otherwise the bytestring is 256-complement representation
|
|
of an integer in reverse.**
|
|
Stack before: []
|
|
Stack after: [long]
|
|
**/
|
|
func (pm *PickleMachine) opcode_LONG4() error {
|
|
var l uint32
|
|
err := pm.readBinaryInto(&l, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if l == 0 {
|
|
pm.push(big.NewInt(0))
|
|
return nil
|
|
}
|
|
|
|
reversedData, err := pm.readFixedLengthRaw(int64(l))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//For no obvious reason, the python pickler
|
|
//always reverses the bytes. Reverse it here
|
|
data := make([]byte, len(reversedData))
|
|
{
|
|
var j int
|
|
for i := len(reversedData) - 1; i != -1; i-- {
|
|
data[j] = reversedData[i]
|
|
j++
|
|
}
|
|
}
|
|
|
|
v := new(big.Int)
|
|
v.SetBytes(data[:l])
|
|
|
|
invertIfNegative(data[0], v, len(data))
|
|
|
|
pm.push(v)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
/**
|
|
Opcode: NEWTRUE (0x88)
|
|
True.
|
|
|
|
Push True onto the stack.**
|
|
Stack before: []
|
|
Stack after: [bool]
|
|
**/
|
|
func (pm *PickleMachine) opcode_NEWTRUE() error {
|
|
pm.push(true)
|
|
return nil
|
|
}
|
|
|
|
/**
|
|
Opcode: NEWFALSE (0x89)
|
|
True.
|
|
|
|
Push False onto the stack.**
|
|
Stack before: []
|
|
Stack after: [bool]
|
|
**/
|
|
func (pm *PickleMachine) opcode_NEWFALSE() error {
|
|
pm.push(false)
|
|
return nil
|
|
}
|
|
|
|
/**
|
|
Opcode: TUPLE1 (0x85)
|
|
Build a one-tuple out of the topmost item on the stack.
|
|
|
|
This code pops one value off the stack and pushes a tuple of
|
|
length 1 whose one item is that value back onto it. In other
|
|
words:
|
|
|
|
stack[-1] = tuple(stack[-1:])
|
|
**
|
|
Stack before: [any]
|
|
Stack after: [tuple]
|
|
**/
|
|
func (pm *PickleMachine) opcode_TUPLE1() error {
|
|
v, err := pm.pop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pm.push([]interface{}{v})
|
|
return nil
|
|
}
|
|
|
|
/**
|
|
Opcode: TUPLE2 (0x86)
|
|
Build a two-tuple out of the top two items on the stack.
|
|
|
|
This code pops two values off the stack and pushes a tuple of
|
|
length 2 whose items are those values back onto it. In other
|
|
words:
|
|
|
|
stack[-2:] = [tuple(stack[-2:])]
|
|
**
|
|
Stack before: [any, any]
|
|
Stack after: [tuple]
|
|
**/
|
|
func (pm *PickleMachine) opcode_TUPLE2() error {
|
|
v := make([]interface{}, 2)
|
|
var err error
|
|
v[1], err = pm.pop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v[0], err = pm.pop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pm.push(v)
|
|
return nil
|
|
}
|
|
|
|
/**
|
|
Opcode: TUPLE3 (0x87)
|
|
Build a three-tuple out of the top three items on the stack.
|
|
|
|
This code pops three values off the stack and pushes a tuple of
|
|
length 3 whose items are those values back onto it. In other
|
|
words:
|
|
|
|
stack[-3:] = [tuple(stack[-3:])]
|
|
**
|
|
Stack before: [any, any, any]
|
|
Stack after: [tuple]
|
|
**/
|
|
func (pm *PickleMachine) opcode_TUPLE3() error {
|
|
v := make([]interface{}, 3)
|
|
var err error
|
|
v[2], err = pm.pop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v[1], err = pm.pop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v[0], err = pm.pop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pm.push(v)
|
|
return nil
|
|
}
|
|
|
|
/**
|
|
Opcode: EXT1 (0x82)
|
|
Extension code.
|
|
|
|
This code and the similar EXT2 and EXT4 allow using a registry
|
|
of popular objects that are pickled by name, typically classes.
|
|
It is envisioned that through a global negotiation and
|
|
registration process, third parties can set up a mapping between
|
|
ints and object names.
|
|
|
|
In order to guarantee pickle interchangeability, the extension
|
|
code registry ought to be global, although a range of codes may
|
|
be reserved for private use.
|
|
|
|
EXT1 has a 1-byte integer argument. This is used to index into the
|
|
extension registry, and the object at that index is pushed on the stack.
|
|
**
|
|
Stack before: []
|
|
Stack after: [any]
|
|
**/
|
|
func (pm *PickleMachine) opcode_EXT1() error {
|
|
return ErrOpcodeNotImplemented
|
|
}
|
|
|
|
/**
|
|
Opcode: EXT2 (0x83)
|
|
Extension code.
|
|
|
|
See EXT1. EXT2 has a two-byte integer argument.
|
|
**
|
|
Stack before: []
|
|
Stack after: [any]
|
|
**/
|
|
func (pm *PickleMachine) opcode_EXT2() error {
|
|
return ErrOpcodeNotImplemented
|
|
}
|
|
|
|
/**
|
|
Opcode: EXT4 (0x84)
|
|
Extension code.
|
|
|
|
See EXT1. EXT4 has a four-byte integer argument.
|
|
**
|
|
Stack before: []
|
|
Stack after: [any]
|
|
**/
|
|
func (pm *PickleMachine) opcode_EXT4() error {
|
|
return ErrOpcodeNotImplemented
|
|
}
|
|
|
|
/**
|
|
Opcode: NEWOBJ (0x81)
|
|
Build an object instance.
|
|
|
|
The stack before should be thought of as containing a class
|
|
object followed by an argument tuple (the tuple being the stack
|
|
top). Call these cls and args. They are popped off the stack,
|
|
and the value returned by cls.__new__(cls, *args) is pushed back
|
|
onto the stack.
|
|
**
|
|
Stack before: [any, any]
|
|
Stack after: [any]
|
|
**/
|
|
func (pm *PickleMachine) opcode_NEWOBJ() error {
|
|
return ErrOpcodeNotImplemented
|
|
}
|
|
|
|
/**
|
|
Opcode: PROTO (0x80)
|
|
Protocol version indicator.
|
|
|
|
For protocol 2 and above, a pickle must start with this opcode.
|
|
The argument is the protocol version, an int in range(2, 256).
|
|
**
|
|
Stack before: []
|
|
Stack after: []
|
|
**/
|
|
func (pm *PickleMachine) opcode_PROTO() error {
|
|
var version int8
|
|
err := pm.readBinaryInto(&version, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if version != 2 {
|
|
return fmt.Errorf("Unsupported version #%d detected", version)
|
|
}
|
|
|
|
return nil
|
|
}
|