39#define PTH_PICKLE_MAX (64u * 1024u * 1024u)
41#define PTH_STORAGE_MAX ((size_t)4 * 1024u * 1024u * 1024u)
44#define PTH_OBJECTS_MAX 4000000u
118 PK_OBJ** grown = (
PK_OBJ**)realloc((
void*)vm->
all, cap *
sizeof(*grown));
120 if (!grown)
return NULL;
124 o = (
PK_OBJ*)calloc(1,
sizeof(*o));
136 if (o->
n == o->
cap) {
137 size_t cap = o->
cap ? o->
cap * 2 : 4;
141 grown = (
PK_OBJ**)realloc((
void*)o->
items, cap *
sizeof(*grown));
142 if (!grown)
return -1;
161 grown = (
PK_OBJ**)realloc((
void*)vm->
stack, cap *
sizeof(*grown));
162 if (!grown)
return -1;
201 o->
s = (
char*)malloc(len + 1);
202 if (!o->
s)
return NULL;
203 memcpy(o->
s, p, len);
216 if (index >= vm->
memo_n) {
217 size_t n = index + 1 > vm->
memo_n * 2 ? index + 1 : vm->
memo_n * 2;
220 if (!grown)
return -1;
221 memset((
void*)(grown + vm->
memo_n), 0, (n - vm->
memo_n) *
sizeof(*grown));
233static uint64_t
le(
const unsigned char* p,
int n) {
237 for (k = n - 1; k >= 0; k--) v = (v << 8) | p[k];
246 static const struct {
250 } known[] = {{
"torch FloatStorage",
"float32", 4},
251 {
"torch HalfStorage",
"float16", 2},
252 {
"torch BFloat16Storage",
"bfloat16", 2},
253 {
"torch DoubleStorage",
"float64", 8},
257 for (i = 0; known[i].global; i++) {
258 if (strcmp(name, known[i].global) == 0) {
259 *dtype = known[i].dtype;
260 return known[i].size;
274 if (callable->
type ==
PK_GLOBAL && (strcmp(callable->
s,
"torch._utils _rebuild_tensor_v2") == 0 ||
275 strcmp(callable->
s,
"torch._utils _rebuild_tensor") == 0)) {
288 if (callable->
type ==
PK_GLOBAL && strcmp(callable->
s,
"torch._utils _rebuild_parameter") == 0 &&
290 return args->
items[0];
303 if (m < 0)
return NULL;
306 for (k = (
size_t)m + 1; k < vm->
sp; k++)
321#define LEFT (len - at)
323 unsigned char op = p[at++];
324 PK_OBJ *a = NULL, *b = NULL, *c = NULL;
328 if (
LEFT < 1)
return NULL;
332 if (
LEFT < 8)
return NULL;
341 if (!vm->
sp)
return NULL;
347 if (m < 0)
return NULL;
369 size_t n = (size_t)(op - 0x84), k = 0;
371 if (vm->
sp < n)
return NULL;
374 for (k = vm->
sp - n; k < vm->sp; k++) {
378 if (
pk_push(vm, a))
return NULL;
390 if (m < 1 || vm->stack[m - 1]->
type !=
PK_LIST)
return NULL;
391 for (k = (
size_t)m + 1; k < vm->
sp; k++)
400 if (m < 0 || (vm->
sp - (
size_t)m - 1) % 2)
return NULL;
403 for (k = (
size_t)m + 1; k < vm->
sp; k++)
406 if (
pk_push(vm, a))
return NULL;
418 if (m < 1 || (vm->
sp - (
size_t)m - 1) % 2)
return NULL;
422 for (
size_t k = (
size_t)m + 1; k < vm->
sp; k++)
432 a->
i = (int32_t)(uint32_t)
le(p + at, 4);
434 if (
pk_push(vm, a))
return NULL;
439 if (
pk_push(vm, a))
return NULL;
443 a->
i = (int64_t)
le(p + at, 2);
445 if (
pk_push(vm, a))
return NULL;
450 if (
LEFT < 1)
return NULL;
453 a->
i = n ? (int64_t)
le(p + at, (
int)n) : 0;
455 if (n && n < 8 && (p[at + n - 1] & 0x80)) a->
i -= (int64_t)1 << (8 * n);
457 if (
pk_push(vm, a))
return NULL;
465 for (k = 0; k < 8; k++) bits = (bits << 8) | p[at + (size_t)k];
466 memcpy(&a->
f, &bits,
sizeof(a->
f));
468 if (
pk_push(vm, a))
return NULL;
478 if (
pk_push(vm, a))
return NULL;
488 int width = (op == 0x8c || op ==
'U' || op ==
'C') ? 1 : (op == 0x8d || op == 0x8e) ? 8
492 if (
LEFT < (
size_t)width)
return NULL;
493 n =
le(p + at, width);
495 if (n >
LEFT)
return NULL;
503 const unsigned char* first = memchr(p + at,
'\n',
LEFT);
504 const unsigned char* second = first ? memchr(first + 1,
'\n', (
size_t)(p + len - first - 1)) : NULL;
506 if (!first || !second)
return NULL;
509 a->
s[first - (p + at)] =
' ';
510 at = (size_t)(second - p) + 1;
511 if (
pk_push(vm, a))
return NULL;
520 n = strlen(a->
s) + strlen(b->s) + 2;
522 if (!c || !(c->s = (
char*)malloc(n)))
return NULL;
523 snprintf(c->s, n,
"%s %s", a->
s, b->s);
524 if (
pk_push(vm, c))
return NULL;
537 if (!vm->
sp)
return NULL;
542 for (k = vm->
memo_n; k > 0 && !vm->
memo[k - 1]; k--) {
551 if (
LEFT < (op ==
'h' ? 1u : 4u))
return NULL;
552 index = op ==
'h' ? p[at] : (size_t)
le(p + at, 4);
553 at += op ==
'h' ? 1 : 4;
558 const char* dtype = NULL;
569 b->s = (
char*)malloc(strlen(a->
items[2]->
s) + 1);
570 if (!b->s)
return NULL;
571 memcpy(b->s, a->
items[2]->
s, strlen(a->
items[2]->
s) + 1);
572 if (
pk_push(vm, b))
return NULL;
583 if (!
pk_pop(vm) || !vm->
sp)
return NULL;
586 n_log(
LOG_ERR,
"pth: pickle opcode 0x%02x at %zu is not read", op, at - 1);
599static float element_value(
const unsigned char* p,
int element,
const char* dtype) {
604 bits = (uint32_t)
le(p, 4);
605 }
else if (element == 8) {
606 uint64_t b64 =
le(p, 8);
609 memcpy(&d, &b64,
sizeof(d));
611 }
else if (strcmp(dtype,
"bfloat16") == 0) {
612 bits = (uint32_t)
le(p, 2) << 16;
615 uint32_t h = (uint32_t)
le(p, 2), sign = (h & 0x8000u) << 16, exp = (h >> 10) & 0x1fu, man = h & 0x3ffu;
622 while (!(man & 0x400u)) {
626 bits = sign | (exp << 23) | ((man & 0x3ffu) << 13);
628 }
else if (exp == 31) {
629 bits = sign | 0x7f800000u | (man << 13);
631 bits = sign | ((exp - 15 + 127) << 23) | (man << 13);
634 memcpy(&f, &bits,
sizeof(f));
648 unsigned char* raw = NULL;
649 size_t raw_len = 0, i = 0, count = 1, span = 0;
658 out->
dims = (int)size->
n;
659 for (d = 0; d < out->
dims; d++) {
664 if (out->
shape[d] && count > (SIZE_MAX /
sizeof(
float)) / (
size_t)out->
shape[d])
return -1;
665 count *= (size_t)out->
shape[d];
666 if (out->
shape[d]) span += (size_t)(out->
shape[d] - 1) * (size_t)stride->
items[d]->
i;
669 if (count && (
size_t)t->
i + span >= (
size_t)storage->
i)
return -1;
670 if (snprintf(member,
sizeof(member),
"%sdata/%s", dir, storage->
s) >= (
int)
sizeof(member))
return -1;
672 if (raw_len < (
size_t)storage->
i * (
size_t)storage->
element) {
677 out->
data = (
float*)malloc((count ? count : 1) *
sizeof(
float));
684 for (i = 0; i < count; i++) {
685 size_t rest = i, at = (size_t)t->
i;
687 for (d = out->
dims - 1; d >= 0; d--) {
688 size_t n = (size_t)out->
shape[d];
690 at += (
rest % n) * (
size_t)stride->
items[d]->
i;
707static int collect(
const char* path,
const char* dir,
const PK_OBJ* o,
const char* prefix,
N_PTH* pth,
int depth) {
710 if (depth > 32)
return -1;
715 if (!grown)
return -1;
718 memset(t, 0,
sizeof(*t));
719 t->
name = (
char*)malloc(strlen(prefix) + 1);
720 if (!t->
name)
return -1;
721 memcpy(t->
name, prefix, strlen(prefix) + 1);
726 for (k = 0; k + 1 < o->
n; k += 2) {
731 if (snprintf(name,
sizeof(name),
"%s%s%s", prefix, prefix[0] ?
"." :
"",
key->s) >= (
int)
sizeof(name))
733 if (
collect(path, dir, o->
items[k + 1], name, pth, depth + 1) != 0)
return -1;
741 unsigned char* pickle = NULL;
742 size_t pickle_len = 0, k = 0;
743 char member[512], dir[512];
745 const char* slash = NULL;
747 if (!path)
return NULL;
749 n_log(
LOG_ERR,
"pth: %s is not a checkpoint this reads (no data.pkl)", path);
753 slash = strrchr(member,
'/');
754 snprintf(dir,
sizeof(dir),
"%.*s", slash ? (
int)(slash - member + 1) : 0, member);
756 memset(&vm, 0,
sizeof(vm));
757 top =
pk_run(&vm, pickle, pickle_len);
760 pth = (
N_PTH*)calloc(1,
sizeof(*pth));
763 n_log(
LOG_ERR,
"pth: the pickle in %s could not be read", path);
765 for (k = 0; k < vm.
all_n; k++) {
771 free((
void*)vm.
stack);
772 free((
void*)vm.
memo);
779 for (k = 0; pth && name && k < pth->
count; k++)
787 if (!pth || !*pth)
return;
788 for (k = 0; k < (*pth)->count; k++) {
789 free((*pth)->tensors[k].name);
790 free((*pth)->tensors[k].data);
792 free((*pth)->tensors);
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
volatile char key[227]
TRUE while the key is held.
void rest(unsigned int ms)
sleep ms milliseconds
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
int dims
how many dimensions
char * name
its path, the dictionary keys joined by dots; owned
N_PTH_TENSOR * tensors
the tensors, in the order the file lists them
int64_t shape[8]
their sizes
float * data
its values, contiguous, row major; owned
void n_pth_free(N_PTH **pth)
release a checkpoint's tensors
const N_PTH_TENSOR * n_pth_find(const N_PTH *pth, const char *name)
find a tensor by its path
N_PTH * n_pth_load(const char *path)
read every tensor of a checkpoint
#define N_PTH_MAX_DIMS
Most dimensions a tensor read may have.
One tensor of a checkpoint.
int n_zip_find_ending(const char *path, const char *ending, char *name, size_t name_len)
find the first member whose name ends with a given text, as a PyTorch checkpoint's "archive/data....
int n_zip_read(const char *path, const char *name, size_t max_size, unsigned char **data, size_t *len)
read a member into memory, checking its CRC-32
int64_t i
an integer's value, a storage's element count, a tensor's offset
PK_OBJ ** all
every value made, freed together
static PK_OBJ * pk_string(PK_VM *vm, int type, const unsigned char *p, size_t len)
make a string value from bytes
const char * dtype
a storage's type name, for the log
static PK_OBJ * pk_run(PK_VM *vm, const unsigned char *p, size_t len)
run a pickle
static int pk_add(PK_OBJ *o, PK_OBJ *item)
add an item to a tuple, list or dictionary
static PK_OBJ * pk_pop(PK_VM *vm)
pop a value
#define PTH_PICKLE_MAX
Largest data.pkl read: a state dictionary's is a few kilobytes, a large model's a few hundred.
static PK_OBJ * pk_from_mark(PK_VM *vm, int type)
pop the values above the top mark into a new container
static PK_OBJ * pk_reduce(PK_VM *vm, const PK_OBJ *callable, PK_OBJ *args)
the call a REDUCE or NEWOBJ makes, done as data
#define PTH_STORAGE_MAX
Largest single storage read.
static int collect(const char *path, const char *dir, const PK_OBJ *o, const char *prefix, N_PTH *pth, int depth)
collect the tensors under a dictionary, depth first
static int pk_push(PK_VM *vm, PK_OBJ *o)
push a value
char * s
a string, a global's "module name", a storage's key; owned
static int storage_element(const char *name, const char **dtype)
a storage type's element size, from its global's name
PK_TYPE
What a pickle value is.
struct PK_OBJ ** items
items of a tuple, list or dictionary; a tensor's storage, then its size and stride tuples
static float element_value(const unsigned char *p, int element, const char *dtype)
convert one stored element to a float
#define PTH_OBJECTS_MAX
Most values the interpreter makes, and most memo entries: a guard, far above any real checkpoint's.
static int tensor_read(const char *path, const char *dir, const PK_OBJ *t, N_PTH_TENSOR *out)
read a tensor's values out of its storage file
static PK_OBJ * pk_new(PK_VM *vm, int type)
make a value, owned by the interpreter
static long pk_mark(const PK_VM *vm)
where the top mark is on the stack
static uint64_t le(const unsigned char *p, int n)
read a little endian unsigned value
int element
a storage's element size in bytes
static int pk_memo_put(PK_VM *vm, size_t index, PK_OBJ *o)
store a value in the memo
Reading the tensors of a PyTorch checkpoint (.pth, .pt) without Python.
Reading members out of a ZIP archive, to a file or into memory.