227 lines
7.2 KiB
Zig
227 lines
7.2 KiB
Zig
|
|
const std = @import("std");
|
||
|
|
|
||
|
|
/// A decoded Pickle operation in its natural state.
|
||
|
|
pub const PickleOp = union(RawPickleOp) {
|
||
|
|
mark,
|
||
|
|
stop,
|
||
|
|
pop,
|
||
|
|
pop_mark,
|
||
|
|
dup,
|
||
|
|
float: []u8,
|
||
|
|
int: []u8,
|
||
|
|
binint: i32,
|
||
|
|
binint1: u8,
|
||
|
|
long: []u8,
|
||
|
|
binint2: u16,
|
||
|
|
none,
|
||
|
|
persid: []u8,
|
||
|
|
binpersid,
|
||
|
|
reduce,
|
||
|
|
string: []u8,
|
||
|
|
binstring: []u8,
|
||
|
|
short_binstring: []u8,
|
||
|
|
unicode: []u8,
|
||
|
|
binunicode: []u8,
|
||
|
|
append,
|
||
|
|
build,
|
||
|
|
global: [2][]u8,
|
||
|
|
dict,
|
||
|
|
empty_dict,
|
||
|
|
appends,
|
||
|
|
get: []u8,
|
||
|
|
binget: u8,
|
||
|
|
inst: [2][]u8,
|
||
|
|
long_binget: u32,
|
||
|
|
list,
|
||
|
|
empty_list,
|
||
|
|
obj,
|
||
|
|
put: []u8,
|
||
|
|
binput: u8,
|
||
|
|
long_binput: u32,
|
||
|
|
setitem,
|
||
|
|
tuple,
|
||
|
|
empty_tuple,
|
||
|
|
setitems,
|
||
|
|
binfloat: f64,
|
||
|
|
proto: u8,
|
||
|
|
newobj,
|
||
|
|
ext1: u8,
|
||
|
|
ext2: i16,
|
||
|
|
ext4: i32,
|
||
|
|
tuple1,
|
||
|
|
tuple2,
|
||
|
|
tuple3,
|
||
|
|
newtrue,
|
||
|
|
newfalse,
|
||
|
|
long1: []u8,
|
||
|
|
long4: []u8,
|
||
|
|
binbytes: []u8,
|
||
|
|
short_binbytes: []u8,
|
||
|
|
short_binunicode: []u8,
|
||
|
|
binunicode8: []u8,
|
||
|
|
binbytes8: []u8,
|
||
|
|
empty_set,
|
||
|
|
additems,
|
||
|
|
frozenset,
|
||
|
|
newobj_ex,
|
||
|
|
stack_global,
|
||
|
|
memoize,
|
||
|
|
frame: u64,
|
||
|
|
bytearray8: []u8,
|
||
|
|
next_buffer,
|
||
|
|
readonly_buffer,
|
||
|
|
|
||
|
|
pub fn deinit(self: PickleOp, allocator: std.mem.Allocator) void {
|
||
|
|
switch (self) {
|
||
|
|
.float,
|
||
|
|
.int,
|
||
|
|
.long,
|
||
|
|
.persid,
|
||
|
|
.string,
|
||
|
|
.binstring,
|
||
|
|
.short_binstring,
|
||
|
|
.unicode,
|
||
|
|
.binunicode,
|
||
|
|
.get,
|
||
|
|
.put,
|
||
|
|
.long1,
|
||
|
|
.long4,
|
||
|
|
.binbytes,
|
||
|
|
.short_binbytes,
|
||
|
|
.short_binunicode,
|
||
|
|
.binunicode8,
|
||
|
|
.binbytes8,
|
||
|
|
.bytearray8,
|
||
|
|
=> |v| allocator.free(v),
|
||
|
|
.global, .inst => |fields| {
|
||
|
|
inline for (fields) |field| {
|
||
|
|
allocator.free(field);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
else => {},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn clone(self: PickleOp, allocator: std.mem.Allocator) !PickleOp {
|
||
|
|
var res = self;
|
||
|
|
return switch (self) {
|
||
|
|
inline .float,
|
||
|
|
.int,
|
||
|
|
.long,
|
||
|
|
.persid,
|
||
|
|
.string,
|
||
|
|
.binstring,
|
||
|
|
.short_binstring,
|
||
|
|
.unicode,
|
||
|
|
.binunicode,
|
||
|
|
.get,
|
||
|
|
.put,
|
||
|
|
.long1,
|
||
|
|
.long4,
|
||
|
|
.binbytes,
|
||
|
|
.short_binbytes,
|
||
|
|
.short_binunicode,
|
||
|
|
.binunicode8,
|
||
|
|
.binbytes8,
|
||
|
|
.bytearray8,
|
||
|
|
=> |v, tag| {
|
||
|
|
const cloned = try allocator.alloc(u8, v.len);
|
||
|
|
@memcpy(cloned, v);
|
||
|
|
@field(res, @tagName(tag)) = cloned;
|
||
|
|
return res;
|
||
|
|
},
|
||
|
|
inline .global, .inst => |v, tag| {
|
||
|
|
var out: std.meta.Tuple(&.{ []u8, []u8 }) = undefined;
|
||
|
|
inline for (0..2) |i| {
|
||
|
|
out[i] = try allocator.alloc(u8, v[i].len);
|
||
|
|
@memcpy(out[i], v[i]);
|
||
|
|
}
|
||
|
|
@field(res, @tagName(tag)) = out;
|
||
|
|
return res;
|
||
|
|
},
|
||
|
|
else => self,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/// The values for the possible opcodes are in this enum.
|
||
|
|
pub const RawPickleOp = enum(u8) {
|
||
|
|
mark = '(', // push special markobject on stack
|
||
|
|
stop = '.', // every pickle ends with stop
|
||
|
|
pop = '0', // discard topmost stack item
|
||
|
|
pop_mark = '1', // discard stack top through topmost markobject
|
||
|
|
dup = '2', // duplicate top stack item
|
||
|
|
float = 'F', // push float object; decimal string argument
|
||
|
|
int = 'I', // push integer or bool; decimal string argument
|
||
|
|
binint = 'J', // push four-byte signed int
|
||
|
|
binint1 = 'K', // push 1-byte unsigned int
|
||
|
|
long = 'L', // push long; decimal string argument
|
||
|
|
binint2 = 'M', // push 2-byte unsigned int
|
||
|
|
none = 'N', // push None
|
||
|
|
persid = 'P', // push persistent object; id is taken from string arg
|
||
|
|
binpersid = 'Q', // " " " ; " " " " stack
|
||
|
|
reduce = 'R', // apply callable to argtuple, both on stack
|
||
|
|
string = 'S', // push string; NL-terminated string argument
|
||
|
|
binstring = 'T', // push string; counted binary string argument
|
||
|
|
short_binstring = 'U', // " " ; " " " " < 256 bytes
|
||
|
|
unicode = 'V', // push Unicode string; raw-unicode-escaped'd argument
|
||
|
|
binunicode = 'X', // " " " ; counted UTF-8 string argument
|
||
|
|
append = 'a', // append stack top to list below it
|
||
|
|
build = 'b', // call __setstate__ or __dict__.update()
|
||
|
|
global = 'c', // push self.find_class(modname, name); 2 string args
|
||
|
|
dict = 'd', // build a dict from stack items
|
||
|
|
empty_dict = '}', // push empty dict
|
||
|
|
appends = 'e', // extend list on stack by topmost stack slice
|
||
|
|
get = 'g', // push item from memo on stack; index is string arg
|
||
|
|
binget = 'h', // " " " " " " ; " " 1-byte arg
|
||
|
|
inst = 'i', // build & push class instance
|
||
|
|
long_binget = 'j', // push item from memo on stack; index is 4-byte arg
|
||
|
|
list = 'l', // build list from topmost stack items
|
||
|
|
empty_list = ']', // push empty list
|
||
|
|
obj = 'o', // build & push class instance
|
||
|
|
put = 'p', // store stack top in memo; index is string arg
|
||
|
|
binput = 'q', // " " " " " ; " " 1-byte arg
|
||
|
|
long_binput = 'r', // " " " " " ; " " 4-byte arg
|
||
|
|
setitem = 's', // add key+value pair to dict
|
||
|
|
tuple = 't', // build tuple from topmost stack items
|
||
|
|
empty_tuple = ')', // push empty tuple
|
||
|
|
setitems = 'u', // modify dict by adding topmost key+value pairs
|
||
|
|
binfloat = 'G', // push float; arg is 8-byte float encoding
|
||
|
|
|
||
|
|
// Protocol 2
|
||
|
|
proto = '\x80', // identify pickle protocol
|
||
|
|
newobj = '\x81', // build object by applying cls.__new__ to argtuple
|
||
|
|
ext1 = '\x82', // push object from extension registry; 1-byte index
|
||
|
|
ext2 = '\x83', // ditto, but 2-byte index
|
||
|
|
ext4 = '\x84', // ditto, but 4-byte index
|
||
|
|
tuple1 = '\x85', // build 1-tuple from stack top
|
||
|
|
tuple2 = '\x86', // build 2-tuple from two topmost stack items
|
||
|
|
tuple3 = '\x87', // build 3-tuple from three topmost stack items
|
||
|
|
newtrue = '\x88', // push True
|
||
|
|
newfalse = '\x89', // push False
|
||
|
|
long1 = '\x8a', // push long from < 256 bytes
|
||
|
|
long4 = '\x8b', // push really big long
|
||
|
|
|
||
|
|
// Protocol 3
|
||
|
|
binbytes = 'B', // push bytes; counted binary string argument
|
||
|
|
short_binbytes = 'C', // " " ; " " " " < 256 bytes
|
||
|
|
|
||
|
|
// Protocol 4
|
||
|
|
short_binunicode = '\x8c', // push short string; UTF-8 length < 256 bytes
|
||
|
|
binunicode8 = '\x8d', // push very long string
|
||
|
|
binbytes8 = '\x8e', // push very long bytes string
|
||
|
|
empty_set = '\x8f', // push empty set on the stack
|
||
|
|
additems = '\x90', // modify set by adding topmost stack items
|
||
|
|
frozenset = '\x91', // build frozenset from topmost stack items
|
||
|
|
newobj_ex = '\x92', // like newobj but work with keyword only arguments
|
||
|
|
stack_global = '\x93', // same as GLOBAL but using names on the stacks
|
||
|
|
memoize = '\x94', // store top of the stack in memo
|
||
|
|
frame = '\x95', // indicate the beginning of a new frame
|
||
|
|
|
||
|
|
// Protocol 5
|
||
|
|
bytearray8 = '\x96', // push bytearray
|
||
|
|
next_buffer = '\x97', // push next out-of-band buffer
|
||
|
|
readonly_buffer = '\x98', // make top of stack readonly
|
||
|
|
_,
|
||
|
|
};
|