<commit_before>#include "master.h"

/* Certain special objects in the image are known to the runtime */
void init_objects(F_HEADER *h)
{
	memcpy(userenv,h->userenv,sizeof(userenv));

	T = h->t;
	bignum_zero = h->bignum_zero;
	bignum_pos_one = h->bignum_pos_one;
	bignum_neg_one = h->bignum_neg_one;

	stage2 = (userenv[STAGE2_ENV] != F);
}

INLINE void load_data_heap(FILE *file, F_HEADER *h, F_PARAMETERS *p)
{
	CELL good_size = h->data_size + (1 << 20);

	if(good_size > p->tenured_size)
		p->tenured_size = good_size;

	init_data_heap(p->gen_count,
		p->young_size,
		p->aging_size,
		p->tenured_size,
		p->secure_gc);

	F_ZONE *tenured = &data_heap->generations[TENURED];

	long int bytes_read = fread((void*)tenured->start,1,h->data_size,file);

	if(bytes_read != h->data_size)
	{
		fprintf(stderr,"truncated image: %ld bytes read, %ld bytes expected\n",
			bytes_read,h->data_size);
		fatal_error("load_data_heap failed",0);
	}

	tenured->here = tenured->start + h->data_size;
	data_relocation_base = h->data_relocation_base;
}

INLINE void load_code_heap(FILE *file, F_HEADER *h, F_PARAMETERS *p)
{
	CELL good_size = h->code_size + (1 << 19);

	if(good_size > p->code_size)
		p->code_size = good_size;

	init_code_heap(p->code_size);

	if(h->code_size != 0)
	{
		long int bytes_read = fread(first_block(&code_heap),1,h->code_size,file);
		if(bytes_read != h->code_size)
		{
			fprintf(stderr,"truncated image: %ld bytes read, %ld bytes expected\n",
				bytes_read,h->code_size);
			fatal_error("load_code_heap failed",0);
		}
	}

	code_relocation_base = h->code_relocation_base;
	build_free_list(&code_heap,h->code_size);
}

/* Read an image file from disk, only done once during startup */
/* This function also initializes the data and code heaps */
void load_image(F_PARAMETERS *p)
{
	FILE *file = OPEN_READ(p->image);
	if(file == NULL)
	{
		FPRINTF(stderr,"Cannot open image file: %s\n",p->image);
		fprintf(stderr,"%s\n",strerror(errno));
		exit(1);
	}

	F_HEADER h;
	fread(&h,sizeof(F_HEADER),1,file);

	if(h.magic != IMAGE_MAGIC)
		fatal_error("Bad image: magic number check failed",h.magic);

	if(h.version != IMAGE_VERSION)
		fatal_error("Bad image: version number check failed",h.version);
	
	load_data_heap(file,&h,p);
	load_code_heap(file,&h,p);

	fclose(file);

	init_objects(&h);

	relocate_data();
	relocate_code();

	/* Store image path name */
	userenv[IMAGE_ENV] = tag_object(from_native_string(p->image));
}

/* Save the current image to disk */
void save_image(const F_CHAR *filename)
{
	FILE* file;
	F_HEADER h;

	FPRINTF(stderr,"*** Saving %s...\n",filename);

	file = OPEN_WRITE(filename);
	if(file == NULL)
	{
		fprintf(stderr,"Cannot open image file: %s\n",strerror(errno));
		return;
	}

	F_ZONE *tenured = &data_heap->generations[TENURED];

	h.magic = IMAGE_MAGIC;
	h.version = IMAGE_VERSION;
	h.data_relocation_base = tenured->start;
	h.data_size = tenured->here - tenured->start;
	h.code_relocation_base = code_heap.segment->start;
	h.code_size = heap_size(&code_heap);

	h.t = T;
	h.bignum_zero = bignum_zero;
	h.bignum_pos_one = bignum_pos_one;
	h.bignum_neg_one = bignum_neg_one;

	CELL i;
	for(i = 0; i < USER_ENV; i++)
	{
		if(i < FIRST_SAVE_ENV)
			h.userenv[i] = F;
		else
			h.userenv[i] = userenv[i];
	}

	fwrite(&h,sizeof(F_HEADER),1,file);

	if(fwrite((void*)tenured->start,h.data_size,1,file) != 1)
	{
		fprintf(stderr,"Save data heap failed: %s\n",strerror(errno));
		return;
	}

	if(fwrite(first_block(&code_heap),h.code_size,1,file) != 1)
	{
		fprintf(stderr,"Save code heap failed: %s\n",strerror(errno));
		return;
	}

	if(fclose(file))
	{
		fprintf(stderr,"Failed to close image file: %s\n",strerror(errno));
		return;
	}
}

DEFINE_PRIMITIVE(save_image)
{
	/* do a full GC to push everything into tenured space */
	gc();

	save_image(unbox_native_string());
}

DEFINE_PRIMITIVE(save_image_and_exit)
{
	F_CHAR *path = unbox_native_string();

	REGISTER_C_STRING(path);

	/* strip out userenv data which is set on startup anyway */
	CELL i;
	for(i = 0; i < FIRST_SAVE_ENV; i++)
		userenv[i] = F;

	for(i = LAST_SAVE_ENV + 1; i < USER_ENV; i++)
		userenv[i] = F;

	/* do a full GC + code heap compaction */
	compact_code_heap();

	UNREGISTER_C_STRING(path);

	/* Save the image */
	save_image(path);

	/* now exit; we cannot continue executing like this */
	exit(0);
}

void fixup_word(F_WORD *word)
{
	if(stage2)
	{
		code_fixup((CELL)&word->code);<commit_msg>Better error checking for image saves<commit_after>#include "master.h"

/* Certain special objects in the image are known to the runtime */
void init_objects(F_HEADER *h)
{
	memcpy(userenv,h->userenv,sizeof(userenv));

	T = h->t;
	bignum_zero = h->bignum_zero;
	bignum_pos_one = h->bignum_pos_one;
	bignum_neg_one = h->bignum_neg_one;

	stage2 = (userenv[STAGE2_ENV] != F);
}

INLINE void load_data_heap(FILE *file, F_HEADER *h, F_PARAMETERS *p)
{
	CELL good_size = h->data_size + (1 << 20);

	if(good_size > p->tenured_size)
		p->tenured_size = good_size;

	init_data_heap(p->gen_count,
		p->young_size,
		p->aging_size,
		p->tenured_size,
		p->secure_gc);

	F_ZONE *tenured = &data_heap->generations[TENURED];

	long int bytes_read = fread((void*)tenured->start,1,h->data_size,file);

	if(bytes_read != h->data_size)
	{
		fprintf(stderr,"truncated image: %ld bytes read, %ld bytes expected\n",
			bytes_read,h->data_size);
		fatal_error("load_data_heap failed",0);
	}

	tenured->here = tenured->start + h->data_size;
	data_relocation_base = h->data_relocation_base;
}

INLINE void load_code_heap(FILE *file, F_HEADER *h, F_PARAMETERS *p)
{
	CELL good_size = h->code_size + (1 << 19);

	if(good_size > p->code_size)
		p->code_size = good_size;

	init_code_heap(p->code_size);

	if(h->code_size != 0)
	{
		long int bytes_read = fread(first_block(&code_heap),1,h->code_size,file);
		if(bytes_read != h->code_size)
		{
			fprintf(stderr,"truncated image: %ld bytes read, %ld bytes expected\n",
				bytes_read,h->code_size);
			fatal_error("load_code_heap failed",0);
		}
	}

	code_relocation_base = h->code_relocation_base;
	build_free_list(&code_heap,h->code_size);
}

/* Read an image file from disk, only done once during startup */
/* This function also initializes the data and code heaps */
void load_image(F_PARAMETERS *p)
{
	FILE *file = OPEN_READ(p->image);
	if(file == NULL)
	{
		FPRINTF(stderr,"Cannot open image file: %s\n",p->image);
		fprintf(stderr,"%s\n",strerror(errno));
		exit(1);
	}

	F_HEADER h;
	fread(&h,sizeof(F_HEADER),1,file);

	if(h.magic != IMAGE_MAGIC)
		fatal_error("Bad image: magic number check failed",h.magic);

	if(h.version != IMAGE_VERSION)
		fatal_error("Bad image: version number check failed",h.version);
	
	load_data_heap(file,&h,p);
	load_code_heap(file,&h,p);

	fclose(file);

	init_objects(&h);

	relocate_data();
	relocate_code();

	/* Store image path name */
	userenv[IMAGE_ENV] = tag_object(from_native_string(p->image));
}

/* Save the current image to disk */
bool save_image(const F_CHAR *filename)
{
	FILE* file;
	F_HEADER h;

	FPRINTF(stderr,"*** Saving %s...\n",filename);

	file = OPEN_WRITE(filename);
	if(file == NULL)
	{
		fprintf(stderr,"Cannot open image file: %s\n",strerror(errno));
		return false;
	}

	F_ZONE *tenured = &data_heap->generations[TENURED];

	h.magic = IMAGE_MAGIC;
	h.version = IMAGE_VERSION;
	h.data_relocation_base = tenured->start;
	h.data_size = tenured->here - tenured->start;
	h.code_relocation_base = code_heap.segment->start;
	h.code_size = heap_size(&code_heap);

	h.t = T;
	h.bignum_zero = bignum_zero;
	h.bignum_pos_one = bignum_pos_one;
	h.bignum_neg_one = bignum_neg_one;

	CELL i;
	for(i = 0; i < USER_ENV; i++)
	{
		if(i < FIRST_SAVE_ENV)
			h.userenv[i] = F;
		else
			h.userenv[i] = userenv[i];
	}

	fwrite(&h,sizeof(F_HEADER),1,file);

	if(fwrite((void*)tenured->start,h.data_size,1,file) != 1)
	{
		fprintf(stderr,"Save data heap failed: %s\n",strerror(errno));
		return false;
	}

	if(fwrite(first_block(&code_heap),h.code_size,1,file) != 1)
	{
		fprintf(stderr,"Save code heap failed: %s\n",strerror(errno));
		return false;
	}

	if(fclose(file))
	{
		fprintf(stderr,"Failed to close image file: %s\n",strerror(errno));
		return false;
	}

	return true;
}

DEFINE_PRIMITIVE(save_image)
{
	/* do a full GC to push everything into tenured space */
	gc();

	save_image(unbox_native_string());
}

DEFINE_PRIMITIVE(save_image_and_exit)
{
	F_CHAR *path = unbox_native_string();

	REGISTER_C_STRING(path);

	/* strip out userenv data which is set on startup anyway */
	CELL i;
	for(i = 0; i < FIRST_SAVE_ENV; i++)
		userenv[i] = F;

	for(i = LAST_SAVE_ENV + 1; i < USER_ENV; i++)
		userenv[i] = F;

	/* do a full GC + code heap compaction */
	compact_code_heap();

	UNREGISTER_C_STRING(path);

	/* Save the image */
	if(save_image(path))
		exit(0);
	else
		exit(1);
}

void fixup_word(F_WORD *word)
{
	if(stage2)
	{
		code_fixup((CELL)&word->code);
{'fraction_non_alphanumeric': 0.1557238106000678, 'fraction_numerical': 0.005763363091874788, 'mean_word_length': 4.739299610894942, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 91, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3127183', 'n_tokens_mistral': 3813, 'n_tokens_neox': 3622, 'n_words': 691}