2022-01-30 15:32:38 +02:00
|
|
|
[InterWarehouse::] The Warehouse.
|
2019-07-22 02:01:18 +03:00
|
|
|
|
|
|
|
To manage the memory storage of inter code.
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@h Introduction.
|
|
|
|
The data structure in this section may bring to mind the title of the Metallica
|
2022-02-21 15:05:50 +02:00
|
|
|
song "The Thing That Should Not Be", but it works. It's also better than it used
|
|
|
|
to be (which is more than you can say for Metallica).
|
2022-01-30 15:32:38 +02:00
|
|
|
|
|
|
|
Each inter tree needs to store data outside of its own //inter_tree_node// structures,
|
|
|
|
data which falls into two categories:
|
|
|
|
|
|
|
|
(*) Bytecode for instructions, each having a unique address.
|
|
|
|
(*) Resources such as texts and symbols tables, each having a unique ID number.
|
|
|
|
|
|
|
|
This data is held in the //inter_warehouse// connected to the tree. Each //inter_tree//
|
|
|
|
contains a pointer to its warehouse.
|
2019-07-22 02:01:18 +03:00
|
|
|
|
|
|
|
=
|
|
|
|
typedef struct inter_warehouse {
|
2022-01-30 15:32:38 +02:00
|
|
|
/* bytecode storage */
|
2019-07-22 02:01:18 +03:00
|
|
|
struct inter_warehouse_room *first_room;
|
2022-01-30 15:32:38 +02:00
|
|
|
struct inter_warehouse_room *last_room;
|
|
|
|
|
|
|
|
/* resource storage */
|
|
|
|
inter_ti next_free_resource_ID;
|
|
|
|
struct inter_warehouse_resource *stored_resources;
|
|
|
|
inter_ti resources_capacity;
|
|
|
|
|
2020-05-09 15:07:39 +03:00
|
|
|
CLASS_DEFINITION
|
2019-07-22 02:01:18 +03:00
|
|
|
} inter_warehouse;
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@ An implementation secret, though, is that (in the present implementation, anyway)
|
|
|
|
there is only one warehouse. If there are multiple trees, they all share the use
|
|
|
|
of a single warehouse, though they don't know it. This would cause thread-safety
|
|
|
|
issues if Inter had any aspiration to be threaded, but it does not have. And this
|
|
|
|
single-warehouse design makes it much faster to carry out "transmigration" -- the
|
|
|
|
movement of branches of Inter from one tree to another -- since the associated
|
|
|
|
bytecode and resources do not need to be copied from one warehouse to another.
|
2019-07-23 22:07:01 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
In this description we continue to talk about "a" warehouse, regardless of the
|
|
|
|
secret fact that there is only one.
|
2019-07-23 22:07:01 +03:00
|
|
|
|
|
|
|
=
|
2019-07-23 02:13:47 +03:00
|
|
|
inter_warehouse *the_only_warehouse = NULL;
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
inter_warehouse *InterWarehouse::new(void) {
|
2019-07-23 02:13:47 +03:00
|
|
|
if (the_only_warehouse == NULL) {
|
|
|
|
inter_warehouse *warehouse = CREATE(inter_warehouse);
|
2022-01-30 15:32:38 +02:00
|
|
|
|
|
|
|
warehouse->first_room = NULL;
|
|
|
|
warehouse->last_room = NULL;
|
|
|
|
|
|
|
|
warehouse->next_free_resource_ID = 1;
|
2019-07-23 02:13:47 +03:00
|
|
|
warehouse->stored_resources = NULL;
|
2022-01-30 15:32:38 +02:00
|
|
|
warehouse->resources_capacity = 0;
|
|
|
|
|
2019-07-23 02:13:47 +03:00
|
|
|
the_only_warehouse = warehouse;
|
|
|
|
}
|
|
|
|
return the_only_warehouse;
|
2019-07-22 02:01:18 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@h The resources.
|
|
|
|
The warehouse provides an array associating ID numbers with "resources": for
|
|
|
|
example, texts, or symbols tables. An ID is an unsigned integer with the lowest
|
|
|
|
legal ID being 1, so that 0 can safely be used to mean "no resource". Once
|
|
|
|
assigned, an ID is never reused.
|
|
|
|
|
|
|
|
Lookup needs tp be fast, so we store the list of resources as a flat array
|
|
|
|
which doubles in size each time its capacity is exceeded. This typically happens
|
|
|
|
about 10 times in a run of //inform7//, for example, to hold on the order of
|
|
|
|
100,000 resources.
|
|
|
|
|
|
|
|
=
|
|
|
|
typedef struct inter_warehouse_resource {
|
|
|
|
struct general_pointer res;
|
|
|
|
struct inter_package *resource_owner;
|
|
|
|
} inter_warehouse_resource;
|
|
|
|
|
|
|
|
@ =
|
|
|
|
inter_ti InterWarehouse::create_resource(inter_warehouse *warehouse) {
|
|
|
|
if (warehouse->next_free_resource_ID >= warehouse->resources_capacity)
|
|
|
|
@<Double the resource list capacity@>;
|
|
|
|
inter_ti n = warehouse->next_free_resource_ID++;
|
|
|
|
warehouse->stored_resources[n].res = NULL_GENERAL_POINTER;
|
|
|
|
warehouse->stored_resources[n].resource_owner = NULL;
|
|
|
|
return n;
|
2019-07-22 02:01:18 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@<Double the resource list capacity@> =
|
|
|
|
inter_ti new_size = 128;
|
|
|
|
while (new_size < 2*warehouse->resources_capacity) new_size = 2*new_size;
|
2022-01-28 01:38:14 +02:00
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
LOGIF(INTER_MEMORY, "Giving warehouse %d resource list of size %d (up from %d)\n",
|
2022-01-30 15:32:38 +02:00
|
|
|
warehouse->allocation_id, new_size, warehouse->resources_capacity);
|
2019-07-24 17:19:38 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
inter_warehouse_resource *storage = (inter_warehouse_resource *)
|
|
|
|
Memory::calloc((int) new_size, sizeof(inter_warehouse_resource), INTER_LINKS_MREASON);
|
|
|
|
inter_warehouse_resource *old = warehouse->stored_resources;
|
|
|
|
for (inter_ti i=0; i<warehouse->resources_capacity; i++) storage[i] = old[i];
|
|
|
|
if (warehouse->resources_capacity > 0)
|
|
|
|
Memory::I7_free(old, INTER_LINKS_MREASON, (int) warehouse->resources_capacity);
|
|
|
|
warehouse->stored_resources = storage;
|
|
|
|
warehouse->resources_capacity = new_size;
|
2019-07-22 02:01:18 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@ Every resource is tied to a package which owns it. This is only in fact used to
|
|
|
|
determine the tree which owns it -- but we store the package, not the tree, because
|
|
|
|
when material transmigrates from one tree to another, the owning package can then
|
2022-01-31 01:49:12 +02:00
|
|
|
stay the same: it is just that //InterPackage::tree// will return a different
|
2022-01-30 15:32:38 +02:00
|
|
|
tree after the movement has taken place.
|
2019-07-24 17:19:38 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
The following conveniently loops through all valid resource IDs for a given tree:
|
2019-07-22 02:01:18 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@d LOOP_OVER_RESOURCE_IDS(n, I)
|
|
|
|
for (inter_ti n = 1; n < I->housed->next_free_resource_ID; n++)
|
|
|
|
if ((I->housed->stored_resources[n].resource_owner == NULL) ||
|
2022-01-31 01:49:12 +02:00
|
|
|
(InterPackage::tree(I->housed->stored_resources[n].resource_owner) == I))
|
2019-07-22 02:01:18 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@ So what can a resource be? The following types are supported, and can be
|
|
|
|
deduced by looking at what class is stored in the general pointer |res|; this
|
|
|
|
saves redundantly storing a type field in //inter_warehouse_resource//.
|
2019-07-22 02:01:18 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@e TEXT_IRSRC from 1
|
|
|
|
@e SYMBOLS_TABLE_IRSRC
|
|
|
|
@e NODE_LIST_IRSRC
|
|
|
|
@e PACKAGE_REF_IRSRC
|
2019-07-22 02:01:18 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
=
|
|
|
|
inter_ti InterWarehouse::resource_type_code(inter_warehouse *warehouse, inter_ti n) {
|
|
|
|
if ((n == 0) || (n >= warehouse->next_free_resource_ID)) internal_error("bad resource ID");
|
|
|
|
switch (warehouse->stored_resources[n].res.run_time_type_code) {
|
|
|
|
case text_stream_CLASS: return TEXT_IRSRC;
|
|
|
|
case inter_symbols_table_CLASS: return SYMBOLS_TABLE_IRSRC;
|
|
|
|
case inter_node_list_CLASS: return NODE_LIST_IRSRC;
|
|
|
|
case inter_package_CLASS: return PACKAGE_REF_IRSRC;
|
2019-07-22 02:01:18 +03:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-01 02:41:22 +02:00
|
|
|
int InterWarehouse::known_type_code(inter_warehouse *warehouse, inter_ti n) {
|
|
|
|
if ((n == 0) || (n >= warehouse->next_free_resource_ID)) return 0;
|
|
|
|
switch (warehouse->stored_resources[n].res.run_time_type_code) {
|
|
|
|
case text_stream_CLASS: return TEXT_IRSRC;
|
|
|
|
case inter_symbols_table_CLASS: return SYMBOLS_TABLE_IRSRC;
|
|
|
|
case inter_node_list_CLASS: return NODE_LIST_IRSRC;
|
|
|
|
case inter_package_CLASS: return PACKAGE_REF_IRSRC;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@ First, it can be a text:
|
|
|
|
|
|
|
|
=
|
|
|
|
inter_ti InterWarehouse::create_text(inter_warehouse *warehouse, inter_package *owner) {
|
|
|
|
return InterWarehouse::create_ref(warehouse, STORE_POINTER_text_stream(Str::new()), owner);
|
2019-07-22 02:01:18 +03:00
|
|
|
}
|
|
|
|
|
2022-03-02 02:04:54 +02:00
|
|
|
inter_ti InterWarehouse::create_text_at(inter_bookmark *IBM, text_stream *text) {
|
|
|
|
inter_warehouse *warehouse = InterBookmark::warehouse(IBM);
|
|
|
|
inter_ti text_ID = InterWarehouse::create_text(warehouse, InterBookmark::package(IBM));
|
|
|
|
text_stream *to = InterWarehouse::get_text(warehouse, text_ID);
|
|
|
|
Str::copy(to, text);
|
|
|
|
return text_ID;
|
2022-03-01 02:41:22 +02:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
text_stream *InterWarehouse::get_text(inter_warehouse *warehouse, inter_ti n) {
|
|
|
|
general_pointer gp = InterWarehouse::get_ref(warehouse, n);
|
|
|
|
if (gp.run_time_type_code != text_stream_CLASS) return NULL;
|
|
|
|
return RETRIEVE_POINTER_text_stream(gp);
|
2019-07-23 02:13:47 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@ Second, a symbols table. Uniquely, this one does not specify an owning package
|
|
|
|
when the resource is created: that's because a package can only be made when a
|
|
|
|
symbols table for it already exists, so we need the table first. The owner is
|
|
|
|
therefore initially not set, and must be explicitly set after the package has
|
|
|
|
been created, using //InterWarehouse::set_symbols_table_owner//.
|
|
|
|
|
|
|
|
=
|
|
|
|
inter_ti InterWarehouse::create_symbols_table(inter_warehouse *warehouse) {
|
|
|
|
inter_ti n = InterWarehouse::create_resource(warehouse);
|
2022-02-03 17:51:44 +02:00
|
|
|
inter_symbols_table *new_table = InterSymbolsTable::new(n);
|
2022-01-30 15:32:38 +02:00
|
|
|
return InterWarehouse::create_ref_at(warehouse, n,
|
|
|
|
STORE_POINTER_inter_symbols_table(new_table), NULL);
|
2019-07-23 22:07:01 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
void InterWarehouse::set_symbols_table_owner(inter_warehouse *warehouse, inter_ti n,
|
|
|
|
inter_package *owner) {
|
|
|
|
if (InterWarehouse::get_symbols_table(warehouse, n) == NULL)
|
|
|
|
internal_error("not a symbols table");
|
|
|
|
warehouse->stored_resources[n].resource_owner = owner;
|
2019-07-23 22:07:01 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
inter_symbols_table *InterWarehouse::get_symbols_table(inter_warehouse *warehouse,
|
|
|
|
inter_ti n) {
|
|
|
|
general_pointer gp = InterWarehouse::get_ref(warehouse, n);
|
|
|
|
if (gp.run_time_type_code != inter_symbols_table_CLASS) return NULL;
|
|
|
|
return RETRIEVE_POINTER_inter_symbols_table(gp);
|
2019-07-23 02:13:47 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@ Third, a resource can be a package -- or, really, a pointer to a package, a
|
|
|
|
form of resource which allows bytecode to contain cross-references to packages.
|
|
|
|
|
|
|
|
This may as well be its own owner, since it can be valid only if the package
|
|
|
|
pointed to is in the same tree as the bytecode instruction using the resource.
|
|
|
|
|
|
|
|
=
|
|
|
|
inter_ti InterWarehouse::create_package(inter_warehouse *warehouse, inter_tree *I) {
|
|
|
|
inter_ti n = InterWarehouse::create_resource(warehouse);
|
2022-01-31 01:49:12 +02:00
|
|
|
inter_package *new_pack = InterPackage::new(I, n);
|
2022-01-30 15:32:38 +02:00
|
|
|
return InterWarehouse::create_ref_at(warehouse, n, STORE_POINTER_inter_package(new_pack),
|
|
|
|
new_pack);
|
2019-07-23 22:07:01 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
inter_package *InterWarehouse::get_package(inter_warehouse *warehouse, inter_ti n) {
|
|
|
|
general_pointer gp = InterWarehouse::get_ref(warehouse, n);
|
|
|
|
if (gp.run_time_type_code != inter_package_CLASS) return NULL;
|
|
|
|
return RETRIEVE_POINTER_inter_package(gp);
|
2019-07-23 02:13:47 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@ Finally, a node list.
|
|
|
|
|
|
|
|
=
|
|
|
|
inter_ti InterWarehouse::create_node_list(inter_warehouse *warehouse, inter_package *owner) {
|
|
|
|
return InterWarehouse::create_ref(warehouse,
|
2022-02-03 01:35:38 +02:00
|
|
|
STORE_POINTER_inter_node_list(InterNodeList::new()), owner);
|
2019-07-23 22:07:01 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
inter_node_list *InterWarehouse::get_node_list(inter_warehouse *warehouse, inter_ti n) {
|
|
|
|
general_pointer gp = InterWarehouse::get_ref(warehouse, n);
|
|
|
|
if (gp.run_time_type_code != inter_node_list_CLASS) return NULL;
|
|
|
|
return RETRIEVE_POINTER_inter_node_list(gp);
|
2019-07-23 02:13:47 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@ All of which use the following:
|
|
|
|
|
|
|
|
=
|
|
|
|
general_pointer InterWarehouse::get_ref(inter_warehouse *warehouse, inter_ti n) {
|
|
|
|
if ((n == 0) || (n >= warehouse->next_free_resource_ID)) return NULL_GENERAL_POINTER;
|
|
|
|
return warehouse->stored_resources[n].res;
|
2019-07-23 22:07:01 +03:00
|
|
|
}
|
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
inter_ti InterWarehouse::create_ref(inter_warehouse *warehouse,
|
|
|
|
general_pointer ref, inter_package *owner) {
|
|
|
|
inter_ti n = InterWarehouse::create_resource(warehouse);
|
|
|
|
return InterWarehouse::create_ref_at(warehouse, n, ref, owner);
|
2019-07-23 02:13:47 +03:00
|
|
|
}
|
2019-07-23 22:07:01 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
inter_ti InterWarehouse::create_ref_at(inter_warehouse *warehouse, inter_ti n,
|
|
|
|
general_pointer ref, inter_package *owner) {
|
|
|
|
warehouse->stored_resources[n].res = ref;
|
|
|
|
warehouse->stored_resources[n].resource_owner = owner;
|
2019-07-23 22:07:01 +03:00
|
|
|
return n;
|
|
|
|
}
|
2019-07-24 17:19:38 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@h The bytecode storage.
|
|
|
|
Conceptually, a warehouse stores bytecode at (word) addresses which begin from 0.
|
|
|
|
Each instruction occupies a contiguous run of addresses. That all sounds like
|
|
|
|
a typical machine-code arrangement, but:
|
|
|
|
|
|
|
|
(a) the instructions themselves contain neither absolute addresses nor address
|
|
|
|
offsets -- they are oblivious to where they are stored, and refer to code
|
|
|
|
positions using labels instead;
|
|
|
|
|
|
|
|
(b) some storage may remain unused, and the addresses of instructions do not
|
|
|
|
correspond to their order in the code.
|
|
|
|
|
|
|
|
=
|
|
|
|
typedef struct inter_warehouse_room {
|
|
|
|
struct inter_warehouse *owning_warehouse;
|
|
|
|
int room_usage;
|
|
|
|
int room_capacity;
|
|
|
|
inter_ti *bytecode;
|
|
|
|
struct inter_warehouse_room *next_room;
|
|
|
|
CLASS_DEFINITION
|
|
|
|
} inter_warehouse_room;
|
|
|
|
|
|
|
|
@ The warehouse is divided into a series of rooms of steadily telescoping sizes.
|
|
|
|
Unless an improbably large demand is made for a very long single instruction,
|
|
|
|
they will typically look like:
|
|
|
|
= (text)
|
|
|
|
room 0 addresses 0x000000 to 0x000fff (4K words)
|
|
|
|
room 1 addresses 0x001000 to 0x002fff (8K words)
|
|
|
|
room 2 addresses 0x003000 to 0x006fff (16K words)
|
|
|
|
room 3 addresses 0x007000 to 0x00efff (32K words)
|
|
|
|
...
|
|
|
|
=
|
|
|
|
though probably not with boundaries as neat as that, since there will be a
|
|
|
|
few words of unused space at the end of each room.
|
|
|
|
|
|
|
|
In a typical //inform7// run, we reach about the 9th room, so that the address
|
|
|
|
space amounts to around 2 million words.
|
|
|
|
|
|
|
|
@ A single instruction will occupy a contiguous run of addresses, and will
|
|
|
|
consist of a preframe (always |PREFRAME_SIZE| words) and then a frame (of
|
|
|
|
a variable size, though always at least 2 words). This will always lie
|
|
|
|
inside a single room: this is why, if we ask for an instruction with a
|
|
|
|
50000-word frame, we would force larger rooms to be created.
|
|
|
|
|
|
|
|
The following represents where an instruction is stored. The address of the
|
|
|
|
preframe will be |index| plus the sum of |room_usage| for previous rooms; the address
|
|
|
|
of the frame will be |PREFRAME_SIZE| more than that, since the frame always
|
|
|
|
immediately follows the preframe. |instruction| points to the first word of
|
|
|
|
the frame, and the |extent| is the size of the frame, so the size of the whole
|
|
|
|
instruction is |extent + PREFRAME_SIZE|.
|
|
|
|
|
|
|
|
Note that |instruction| and |extent| are both in principle redundant in this
|
|
|
|
structure. If you know |in_room| and |index| you know everything, because:
|
|
|
|
= (text as InC)
|
|
|
|
W.instruction == W.in_room->bytecode + W.index + PREFRAME_SIZE
|
|
|
|
W.extent == W.in_room->bytecode[W.index + PREFRAME_SKIP_AMOUNT] - PREFRAME_SIZE
|
|
|
|
=
|
|
|
|
But speed of access is so important that we store these two fields redundantly
|
|
|
|
in order to cache the results of those two calculations.
|
|
|
|
|
|
|
|
=
|
|
|
|
typedef struct warehouse_floor_space {
|
|
|
|
struct inter_warehouse_room *in_room;
|
|
|
|
int index;
|
|
|
|
inter_ti *instruction;
|
|
|
|
int extent;
|
|
|
|
} warehouse_floor_space;
|
|
|
|
|
|
|
|
@ We provide an API of just two functions to handle all this. Firstly,
|
|
|
|
//InterWarehouse::make_floor_space// makes room for an instruction of |n| words.
|
|
|
|
(This is the frame extent, and does not include the |PREFRAME_SIZE|.)
|
|
|
|
|
|
|
|
Note that this function always succeeds, because an internal error is thrown
|
|
|
|
if the system is out of memory.
|
|
|
|
|
|
|
|
=
|
|
|
|
warehouse_floor_space InterWarehouse::make_floor_space(inter_warehouse *warehouse, int n) {
|
|
|
|
int total_extent = n + PREFRAME_SIZE;
|
|
|
|
@<Make a new and bigger room if necessary@>;
|
|
|
|
|
|
|
|
inter_warehouse_room *IS = warehouse->last_room;
|
|
|
|
int at = IS->room_usage;
|
|
|
|
for (int i=0; i<total_extent; i++) IS->bytecode[at + i] = 0;
|
|
|
|
IS->bytecode[at + PREFRAME_SKIP_AMOUNT] = (inter_ti) total_extent;
|
|
|
|
IS->room_usage += total_extent;
|
|
|
|
|
|
|
|
warehouse_floor_space W;
|
|
|
|
W.in_room = IS; W.index = at;
|
|
|
|
if ((IS) && (at >= 0) && (at < IS->room_usage)) {
|
|
|
|
W.instruction = &(IS->bytecode[at + PREFRAME_SIZE]);
|
|
|
|
W.extent = ((int) IS->bytecode[at + PREFRAME_SKIP_AMOUNT]) - PREFRAME_SIZE;
|
|
|
|
} else {
|
|
|
|
W.instruction = NULL; W.extent = 0;
|
|
|
|
}
|
|
|
|
return W;
|
2019-07-24 17:19:38 +03:00
|
|
|
}
|
2019-09-07 03:28:18 +03:00
|
|
|
|
2022-01-30 15:32:38 +02:00
|
|
|
@<Make a new and bigger room if necessary@> =
|
|
|
|
inter_warehouse_room *IS = warehouse->last_room;
|
|
|
|
if ((IS == NULL) || (IS->room_usage + total_extent > IS->room_capacity)) {
|
|
|
|
if (IS) IS->room_capacity = IS->room_usage;
|
|
|
|
|
|
|
|
int new_capacity = 4096;
|
|
|
|
if (IS) new_capacity = 2*IS->room_capacity;
|
|
|
|
while (total_extent >= new_capacity) new_capacity = 2*new_capacity;
|
|
|
|
|
|
|
|
inter_warehouse_room *new_room = CREATE(inter_warehouse_room);
|
|
|
|
new_room->owning_warehouse = warehouse;
|
|
|
|
new_room->room_usage = 0;
|
|
|
|
new_room->room_capacity = new_capacity;
|
|
|
|
new_room->bytecode = (inter_ti *)
|
|
|
|
Memory::calloc(new_capacity, sizeof(inter_ti), INTER_BYTECODE_MREASON);
|
|
|
|
LOGIF(INTER_MEMORY, "Created warehouse room %d with capacity %08x\n",
|
|
|
|
new_room->allocation_id, new_room->room_capacity);
|
|
|
|
if (IS) IS->next_room = new_room;
|
|
|
|
else warehouse->first_room = new_room;
|
|
|
|
warehouse->last_room = new_room;
|
2019-09-07 03:28:18 +03:00
|
|
|
}
|
2022-01-30 15:32:38 +02:00
|
|
|
|
|
|
|
@ Secondly, //InterWarehouse::enlarge_floor_space// adds extra length to an
|
|
|
|
existing instruction -- which may involve moving its bytecode to a bigger room,
|
|
|
|
in the worst case, but is more typically very fast. Note that this function
|
|
|
|
may only be called on the floor space for the most-recently creates instruction,
|
|
|
|
so the floor space is guaranteed to be at the end of the space used in the
|
|
|
|
current room.
|
|
|
|
|
|
|
|
|by| cannot be negative (it is unsigned). The instruction therefore always
|
|
|
|
extends, not contracts. The values of the new words added are undefined:
|
|
|
|
they will very likely be 0 but do not rely upon this.
|
|
|
|
|
|
|
|
Note that this function always succeeds, because an internal error is thrown
|
|
|
|
if the system is out of memory.
|
|
|
|
|
|
|
|
=
|
|
|
|
warehouse_floor_space InterWarehouse::enlarge_floor_space(warehouse_floor_space FW,
|
|
|
|
inter_ti by) {
|
|
|
|
/* Have we already moved on to building further instructions? */
|
|
|
|
if (FW.in_room->next_room) internal_error("too late to extend instruction");
|
|
|
|
|
|
|
|
if (FW.in_room->room_usage + (int) by <= FW.in_room->room_capacity)
|
|
|
|
@<The existing room already has sufficient capacity to accommodate the extra@>
|
|
|
|
else
|
|
|
|
@<The existing room is not big enough@>;
|
|
|
|
|
|
|
|
return FW;
|
2019-09-07 03:28:18 +03:00
|
|
|
}
|
2022-01-30 15:32:38 +02:00
|
|
|
|
|
|
|
@<The existing room already has sufficient capacity to accommodate the extra@> =
|
|
|
|
FW.in_room->bytecode[FW.index + PREFRAME_SKIP_AMOUNT] += by;
|
|
|
|
FW.in_room->room_usage += by;
|
|
|
|
FW.extent += by;
|
|
|
|
|
|
|
|
@<The existing room is not big enough@> =
|
|
|
|
warehouse_floor_space new_W =
|
|
|
|
InterWarehouse::make_floor_space(FW.in_room->owning_warehouse, FW.extent + (int) by);
|
|
|
|
|
|
|
|
FW.in_room->room_usage = FW.index;
|
|
|
|
FW.in_room->room_capacity = FW.index;
|
|
|
|
|
|
|
|
@<Copy the bytecode for the preframe and frame to its new home@>;
|
|
|
|
|
|
|
|
@<Copy the bytecode for the preframe and frame to its new home@> =
|
|
|
|
for (int i=0; i<PREFRAME_SIZE; i++)
|
|
|
|
if (i != PREFRAME_SKIP_AMOUNT)
|
|
|
|
new_W.in_room->bytecode[new_W.index + i] = FW.in_room->bytecode[FW.index + i];
|
|
|
|
|
|
|
|
for (int i=0; i<new_W.extent; i++)
|
|
|
|
if (i < FW.extent)
|
|
|
|
new_W.instruction[i] = FW.instruction[i];
|
|
|
|
else
|
|
|
|
new_W.instruction[i] = 0;
|
|
|
|
|
|
|
|
FW = new_W;
|