inweb-bootstrap/docs/foundation-module/2-mmr.html

1335 lines
152 KiB
HTML
Raw Normal View History

2019-02-04 22:26:45 +00:00
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
2020-04-08 22:41:00 +00:00
<title>Memory</title>
<meta name="viewport" content="width=device-width initial-scale=1">
2019-02-04 22:26:45 +00:00
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Language" content="en-gb">
<link href="../inweb.css" rel="stylesheet" rev="stylesheet" type="text/css">
2020-04-10 20:29:28 +00:00
2019-02-04 22:26:45 +00:00
</head>
<body>
<nav role="navigation">
<h1><a href="../webs.html">Sources</a></h1>
<ul>
<li><a href="../inweb/index.html">inweb</a></li>
</ul>
<h2>Foundation</h2>
<ul>
<li><a href="../foundation-module/index.html">foundation-module</a></li>
<li><a href="../foundation-test/index.html">foundation-test</a></li>
</ul>
</nav>
<main role="main">
2020-04-08 22:41:00 +00:00
<!--Weave of 'Memory' generated by 7-->
<ul class="crumbs"><li><a href="../webs.html">Source</a></li><li><a href="index.html">foundation</a></li><li><a href="index.html#2">Chapter 2: Memory, Streams and Collections</a></li><li><b>Memory</b></li></ul><p class="purpose">To allocate memory suitable for the dynamic creation of objects of different sizes, placing some larger objects automatically into doubly linked lists and assigning each a unique allocation ID number.</p>
2019-02-04 22:26:45 +00:00
<ul class="toc"><li><a href="#SP1">&#167;1. Memory manager</a></li><li><a href="#SP6">&#167;6. Architecture</a></li><li><a href="#SP7">&#167;7. Level 1: memory blocks</a></li><li><a href="#SP13">&#167;13. Level 2: memory frames and integrity checking</a></li><li><a href="#SP17">&#167;17. Level 3: managing linked lists of allocated objects</a></li><li><a href="#SP19">&#167;19. Allocator functions created by macros</a></li><li><a href="#SP21">&#167;21. Expanding many macros</a></li><li><a href="#SP22">&#167;22. Simple memory allocations</a></li><li><a href="#SP28">&#167;28. Text storage</a></li><li><a href="#SP32">&#167;32. Memory usage report</a></li><li><a href="#SP36">&#167;36. Run-time pointer type checking</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. Memory manager. </b>This allocates memory as needed to store the numerous "objects" of different
sizes, all C structures. There's no garbage collection because nothing is ever
destroyed. Each type has its own doubly-linked list, and in each type the
objects created are given unique IDs (within that type) counting upwards
from 0. These IDs will be unique across all threads.
</p>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. </b>Before going much further, we will need to anticipate what the memory
manager wants. An "object" is a copy in memory of a C <code class="display"><span class="extract">struct</span></code>; thus,
a plain <code class="display"><span class="extract">int</span></code> is not an object. The memory manager can only deal with
a given type of <code class="display"><span class="extract">struct</span></code> if it contains three special elements, and we
define those using a macro. Thus, if the user wants to allocate larger
structures of type <code class="display"><span class="extract">thingummy</span></code>, then it needs to be defined like so:
</p>
<pre class="display">
2020-04-07 22:04:32 +00:00
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="identifier">thingummy</span><span class="plain"> {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">whatsit</span><span class="plain">;</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">doobrey</span><span class="plain">;</span>
<span class="plain">...</span>
<span class="constant">MEMORY_MANAGEMENT</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph">The caveat about "larger structures" is that smaller objects can instead be
stored in arrays, to reduce memory and speed overheads. Their structure
declarations do not include the following macro; they do not have unique
IDs; and they cannot be iterated over.
</p>
<pre class="definitions">
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">define</span> <span class="constant">MEMORY_MANAGEMENT</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">allocation_id</span><span class="plain">; </span><span class="comment"> Numbered from 0 upwards in creation order</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">next_structure</span><span class="plain">; </span><span class="comment"> Next object in double-linked list</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">prev_structure</span><span class="plain">; </span><span class="comment"> Previous object in double-linked list</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. </b>It is also necessary to define a constant in the following enumeration
family: for <code class="display"><span class="extract">thingummy</span></code>, it would be <code class="display"><span class="extract">thingummy_MT</span></code>. Had it been a smaller
object, it would have been <code class="display"><span class="extract">thingummy_array_MT</span></code> instead.
</p>
<p class="inwebparagraph">There is no significance to the order in which structures are registered
with the memory system. The ones here are those needed by Foundation.
</p>
<pre class="definitions">
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">enum</span> <span class="constant">filename_MT</span><span class="definitionkeyword"> from </span><span class="constant">0</span>
<span class="definitionkeyword">enum</span> <span class="constant">pathname_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">string_storage_area_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">scan_directory_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">ebook_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">ebook_datum_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">ebook_volume_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">ebook_chapter_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">ebook_page_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">ebook_image_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">HTML_file_state_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">HTML_tag_array_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">text_stream_array_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">command_line_switch_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">dictionary_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">dict_entry_array_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">debugging_aspect_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">linked_list_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">linked_list_item_array_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">match_avinue_array_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">match_trie_array_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">method_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">method_set_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">ebook_mark_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">semantic_version_number_holder_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">semver_range_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">web_md_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">chapter_md_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">section_md_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">web_bibliographic_datum_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">module_MT</span>
<span class="definitionkeyword">enum</span> <span class="constant">module_search_MT</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. </b>For each type of object to be allocated, a single structure of the
following design is maintained. Types which are allocated individually,
like world objects, have <code class="display"><span class="extract">no_allocated_together</span></code> set to 1, and the doubly
linked list is of the objects themselves. For types allocated in small
arrays (typically of 100 objects at a time), <code class="display"><span class="extract">no_allocated_together</span></code> is set
to the number of objects in each completed array (so, typically 100) and
the doubly linked list is of the arrays.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">allocation_status_structure</span><span class="plain"> {</span>
<span class="comment"> actually needed for allocation purposes:</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">objects_allocated</span><span class="plain">; </span><span class="comment"> total number of objects (or arrays) ever allocated</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">first_in_memory</span><span class="plain">; </span><span class="comment"> head of doubly linked list</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">last_in_memory</span><span class="plain">; </span><span class="comment"> tail of doubly linked list</span>
<span class="comment"> used only to provide statistics for the debugging log:</span>
<span class="reserved">char</span><span class="plain"> *</span><span class="identifier">name_of_type</span><span class="plain">; </span><span class="comment"> e.g., <code class="display"><span class="extract">"lexicon_entry_MT"</span></code></span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">bytes_allocated</span><span class="plain">; </span><span class="comment"> total allocation for this type of object, not counting overhead</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">objects_count</span><span class="plain">; </span><span class="comment"> total number currently in existence (i.e., undeleted)</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">no_allocated_together</span><span class="plain">; </span><span class="comment"> number of objects in each array of this type of object</span>
2020-04-06 11:26:10 +00:00
<span class="plain">} </span><span class="reserved">allocation_status_structure</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure allocation_status_structure is private to this section.</p>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. </b>The memory allocator itself needs some memory, but only a fixed-size and
fairly small array of the structures defined above. The allocator can safely
begin as soon as this is initialised.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">allocation_status_structure</span><span class="plain"> </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">NO_DEFINED_MT_VALUES</span><span class="plain">];</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::start</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">NO_DEFINED_MT_VALUES</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++) {</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">first_in_memory</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">last_in_memory</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_count</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">bytes_allocated</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">no_allocated_together</span><span class="plain"> = </span><span class="constant">1</span><span class="plain">;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">name_of_type</span><span class="plain"> = </span><span class="string">"unused"</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="functiontext">Memory::name_fundamental_reasons</span><span class="plain">();</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
2020-04-09 17:32:37 +00:00
<p class="endnote">The function Memory::start is used in 1/fm (<a href="1-fm.html#SP8">&#167;8</a>).</p>
2019-02-04 22:26:45 +00:00
<p class="inwebparagraph"><a id="SP6"></a><b>&#167;6. Architecture. </b>The memory manager is built in three levels, with its interface to the user
being entirely at level 3 (except that when it shuts down it calls a level 1
routine to free everything). Each level uses the one below it.
</p>
<p class="inwebparagraph"></p>
<ul class="items"><li>(3) Managing linked lists of large objects, within which objects can be
created at any point, and from which objects can be deleted; and providing
a way to create new small objects of any given type.
</li><li>(2) Allocating some thousands of memory frames, each holding one large object
or an array of small objects.
</li><li>(1) Allocating and freeing a few dozen large blocks of contiguous memory.
</li></ul>
2019-03-12 23:32:12 +00:00
<p class="inwebparagraph"><a id="SP7"></a><b>&#167;7. Level 1: memory blocks. </b>Memory is allocated in blocks within which objects are allocated as
2019-02-04 22:26:45 +00:00
needed. The "safety margin" is the number of spare bytes left blank at the
end of each object: this is done because we want to be paranoid about
compilers on different architectures aligning structures to different
boundaries (multiples of 4, 8, 16, etc.). Each block also ends with a
firebreak of zeroes, which ought never to be touched: we want to minimise the
chance of a mistake causing a memory exception which crashes the compiler,
because if that happens it will be difficult to recover the circumstances from
the debugging log.
</p>
<pre class="definitions">
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">define</span> <span class="constant">SAFETY_MARGIN</span><span class="plain"> </span><span class="constant">128</span>
<span class="definitionkeyword">define</span> <span class="constant">BLANK_END_SIZE</span><span class="plain"> </span><span class="constant">256</span>
2019-02-04 22:26:45 +00:00
</pre>
2019-03-12 23:32:12 +00:00
<p class="inwebparagraph"><a id="SP8"></a><b>&#167;8. </b>At present <code class="display"><span class="extract">MEMORY_GRANULARITY</span></code> is 800K. This is the quantity of memory
2019-02-04 22:26:45 +00:00
allocated by each individual <code class="display"><span class="extract">malloc</span></code> call.
</p>
<p class="inwebparagraph">After <code class="display"><span class="extract">MAX_BLOCKS_ALLOWED</span></code> blocks, we throw in the towel: we must have
fallen into an endless loop which creates endless new objects somewhere.
(If this ever happens, it would be a bug: the point of this mechanism is to
be able to recover. Without this safety measure, OS X in particular would
grind slowly to a halt, never refusing a <code class="display"><span class="extract">malloc</span></code>, until the user was
unable to get the GUI responsive enough to kill the process.)
</p>
<pre class="definitions">
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">define</span> <span class="constant">MAX_BLOCKS_ALLOWED</span><span class="plain"> </span><span class="constant">15000</span>
<span class="definitionkeyword">define</span> <span class="constant">MEMORY_GRANULARITY</span><span class="plain"> </span><span class="constant">100</span><span class="plain">*1024*8 </span><span class="comment"> which must be divisible by 1024</span>
2019-02-04 22:26:45 +00:00
</pre>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">no_blocks_allocated</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">total_objects_allocated</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span><span class="comment"> a potentially larger number, used only for the debugging log</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP9"></a><b>&#167;9. </b>Memory blocks are stored in a linked list, and we keep track of the
size of the current block: that is, the block at the tail of the list.
Each memory block consists of a header structure, followed by <code class="display"><span class="extract">SAFETY_MARGIN</span></code>
null bytes, followed by actual data.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">memblock_header</span><span class="plain"> {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">block_number</span><span class="plain">;</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">memblock_header</span><span class="plain"> *</span><span class="identifier">next</span><span class="plain">;</span>
<span class="reserved">char</span><span class="plain"> *</span><span class="identifier">the_memory</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">memblock_header</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure memblock_header is accessed in 4/taa and here.</p>
<p class="inwebparagraph"><a id="SP10"></a><b>&#167;10. </b></p>
<pre class="display">
<span class="reserved">memblock_header</span><span class="plain"> *</span><span class="identifier">first_memblock_header</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">; </span><span class="comment"> head of list of memory blocks</span>
<span class="reserved">memblock_header</span><span class="plain"> *</span><span class="identifier">current_memblock_header</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">; </span><span class="comment"> tail of list of memory blocks</span>
2019-02-04 22:26:45 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">used_in_current_memblock</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span><span class="comment"> number of bytes so far used in the tail memory block</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP11"></a><b>&#167;11. </b>The actual allocation and deallocation is performed by the following
pair of routines.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::allocate_another_block</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">cp</span><span class="plain">;</span>
<span class="reserved">memblock_header</span><span class="plain"> *</span><span class="identifier">mh</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
&lt;<span class="cwebmacro">Allocate and zero out a block of memory, making cp point to it</span> <span class="cwebmacronumber">11.1</span>&gt;<span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">mh</span><span class="plain"> = (</span><span class="reserved">memblock_header</span><span class="plain"> *) </span><span class="identifier">cp</span><span class="plain">;</span>
<span class="identifier">used_in_current_memblock</span><span class="plain"> = </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="reserved">memblock_header</span><span class="plain">) + </span><span class="constant">SAFETY_MARGIN</span><span class="plain">;</span>
<span class="identifier">mh</span><span class="plain">-&gt;</span><span class="element">the_memory</span><span class="plain"> = (</span><span class="reserved">void</span><span class="plain"> *) (</span><span class="identifier">cp</span><span class="plain"> + </span><span class="identifier">used_in_current_memblock</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
&lt;<span class="cwebmacro">Add new block to the tail of the list of memory blocks</span> <span class="cwebmacronumber">11.2</span>&gt;<span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::allocate_another_block is used in <a href="#SP16_1">&#167;16.1</a>.</p>
<p class="inwebparagraph"><a id="SP11_1"></a><b>&#167;11.1. </b>Note that <code class="display"><span class="extract">cp</span></code> and <code class="display"><span class="extract">mh</span></code> are set to the same value: they merely have different
pointer types as far as the C compiler is concerned.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Allocate and zero out a block of memory, making cp point to it</span> <span class="cwebmacronumber">11.1</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">no_blocks_allocated</span><span class="plain">++ &gt;= </span><span class="constant">MAX_BLOCKS_ALLOWED</span><span class="plain">)</span>
<span class="functiontext">Errors::fatal</span><span class="plain">(</span>
2019-02-04 22:26:45 +00:00
<span class="string">"the memory manager has halted inweb, which seems to be generating "</span>
<span class="string">"endless structures. Presumably it is trapped in a loop"</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="functiontext">Memory::check_memory_integrity</span><span class="plain">();</span>
<span class="identifier">cp</span><span class="plain"> = (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain"> *) (</span><span class="functiontext">Memory::paranoid_calloc</span><span class="plain">(</span><span class="constant">MEMORY_GRANULARITY</span><span class="plain">, </span><span class="constant">1</span><span class="plain">));</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">cp</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="functiontext">Errors::fatal</span><span class="plain">(</span><span class="string">"Run out of memory: malloc failed"</span><span class="plain">);</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="constant">MEMORY_GRANULARITY</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++) </span><span class="identifier">cp</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = </span><span class="constant">0</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP11">&#167;11</a>.</p>
<p class="inwebparagraph"><a id="SP11_2"></a><b>&#167;11.2. </b>As can be seen, memory block numbers count upwards from 0 in order of
their allocation.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Add new block to the tail of the list of memory blocks</span> <span class="cwebmacronumber">11.2</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">current_memblock_header</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="identifier">mh</span><span class="plain">-&gt;</span><span class="element">block_number</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">first_memblock_header</span><span class="plain"> = </span><span class="identifier">mh</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">mh</span><span class="plain">-&gt;</span><span class="element">block_number</span><span class="plain"> = </span><span class="identifier">current_memblock_header</span><span class="plain">-&gt;</span><span class="element">block_number</span><span class="plain"> + </span><span class="constant">1</span><span class="plain">;</span>
<span class="identifier">current_memblock_header</span><span class="plain">-&gt;</span><span class="element">next</span><span class="plain"> = </span><span class="identifier">mh</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
<span class="identifier">current_memblock_header</span><span class="plain"> = </span><span class="identifier">mh</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP11">&#167;11</a>.</p>
<p class="inwebparagraph"><a id="SP12"></a><b>&#167;12. </b>Freeing all this memory again is just a matter of freeing each block
in turn, but of course being careful to avoid following links in a just-freed
block.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::free</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="functiontext">Memory::free_ssas</span><span class="plain">();</span>
<span class="reserved">memblock_header</span><span class="plain"> *</span><span class="identifier">mh</span><span class="plain"> = </span><span class="identifier">first_memblock_header</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">mh</span><span class="plain"> != </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="reserved">memblock_header</span><span class="plain"> *</span><span class="identifier">next_mh</span><span class="plain"> = </span><span class="identifier">mh</span><span class="plain">-&gt;</span><span class="element">next</span><span class="plain">;</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">p</span><span class="plain"> = (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">mh</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">free</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">);</span>
<span class="identifier">mh</span><span class="plain"> = </span><span class="identifier">next_mh</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
2020-04-09 17:32:37 +00:00
<p class="endnote">The function Memory::free is used in 1/fm (<a href="1-fm.html#SP9">&#167;9</a>).</p>
2019-02-04 22:26:45 +00:00
<p class="inwebparagraph"><a id="SP13"></a><b>&#167;13. Level 2: memory frames and integrity checking. </b>Within these extensive blocks of contiguous memory, we place the actual
objects in between "memory frames", which are only used at present to police
the integrity of memory: again, finding obscure and irritating memory-corruption
bugs is more important to us than saving bytes. Each memory frame wraps either
a single large object, or a single array of small objects.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">INTEGRITY_NUMBER</span><span class="plain"> </span><span class="constant">0x12345678</span><span class="plain"> </span><span class="comment"> a value unlikely to be in memory just by chance</span>
2019-02-04 22:26:45 +00:00
</pre>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">memory_frame</span><span class="plain"> {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">integrity_check</span><span class="plain">; </span><span class="comment"> this should always contain the <code class="display"><span class="extract">INTEGRITY_NUMBER</span></code></span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">memory_frame</span><span class="plain"> *</span><span class="identifier">next_frame</span><span class="plain">; </span><span class="comment"> next frame in the list of memory frames</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">mem_type</span><span class="plain">; </span><span class="comment"> type of object stored in this frame</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">allocation_id</span><span class="plain">; </span><span class="comment"> allocation ID number of object stored in this frame</span>
2020-04-06 11:26:10 +00:00
<span class="plain">} </span><span class="reserved">memory_frame</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure memory_frame is private to this section.</p>
<p class="inwebparagraph"><a id="SP14"></a><b>&#167;14. </b>There is a single linked list of all the memory frames, perhaps of about
10000 entries in length, beginning here. (These frames live in different memory
blocks, but we don't need to worry about that.)
</p>
<pre class="display">
<span class="reserved">memory_frame</span><span class="plain"> *</span><span class="identifier">first_memory_frame</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">; </span><span class="comment"> earliest memory frame ever allocated</span>
<span class="reserved">memory_frame</span><span class="plain"> *</span><span class="identifier">last_memory_frame</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">; </span><span class="comment"> most recent memory frame allocated</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP15"></a><b>&#167;15. </b>If the integrity numbers of every frame are still intact, then it is pretty
unlikely that any bug has caused memory to overwrite one frame into another.
<code class="display"><span class="extract">Memory::check_memory_integrity</span></code> might on very large runs be run often, if we didn't
prevent this: since the number of calls would be roughly proportional to
2020-04-10 20:29:28 +00:00
memory usage, we would implicitly have an \(O(n^2)\) running time in the
amount of storage \(n\) allocated.
2019-02-04 22:26:45 +00:00
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">calls_to_cmi</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::check_memory_integrity</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">c</span><span class="plain">;</span>
<span class="reserved">memory_frame</span><span class="plain"> *</span><span class="identifier">mf</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">c</span><span class="plain"> = </span><span class="identifier">calls_to_cmi</span><span class="plain">++;</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (!((</span><span class="identifier">c</span><span class="plain">&lt;10) || (</span><span class="identifier">c</span><span class="plain"> == </span><span class="constant">100</span><span class="plain">) || (</span><span class="identifier">c</span><span class="plain"> == </span><span class="constant">1000</span><span class="plain">) || (</span><span class="identifier">c</span><span class="plain"> == </span><span class="constant">10000</span><span class="plain">))) </span><span class="reserved">return</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
2020-04-06 11:26:10 +00:00
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">, </span><span class="identifier">mf</span><span class="plain"> = </span><span class="identifier">first_memory_frame</span><span class="plain">; </span><span class="identifier">mf</span><span class="plain">; </span><span class="identifier">c</span><span class="plain">++, </span><span class="identifier">mf</span><span class="plain"> = </span><span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">next_frame</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">integrity_check</span><span class="plain"> != </span><span class="constant">INTEGRITY_NUMBER</span><span class="plain">)</span>
<span class="functiontext">Errors::fatal</span><span class="plain">(</span><span class="string">"Memory manager failed integrity check"</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::debug_memory_frames</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">from</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">to</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">c</span><span class="plain">;</span>
<span class="reserved">memory_frame</span><span class="plain"> *</span><span class="identifier">mf</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">, </span><span class="identifier">mf</span><span class="plain"> = </span><span class="identifier">first_memory_frame</span><span class="plain">; (</span><span class="identifier">mf</span><span class="plain">) &amp;&amp; (</span><span class="identifier">c</span><span class="plain"> &lt;= </span><span class="identifier">to</span><span class="plain">); </span><span class="identifier">c</span><span class="plain">++, </span><span class="identifier">mf</span><span class="plain"> = </span><span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">next_frame</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> &gt;= </span><span class="identifier">from</span><span class="plain">) {</span>
<span class="reserved">char</span><span class="plain"> *</span><span class="identifier">desc</span><span class="plain"> = </span><span class="string">"corrupt"</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">integrity_check</span><span class="plain"> == </span><span class="constant">INTEGRITY_NUMBER</span><span class="plain">)</span>
<span class="identifier">desc</span><span class="plain"> = </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">mem_type</span><span class="plain">].</span><span class="element">name_of_type</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::check_memory_integrity is used in <a href="#SP11_1">&#167;11.1</a>.</p>
<p class="endnote">The function Memory::debug_memory_frames appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP16"></a><b>&#167;16. </b>We have seen how memory is allocated in large blocks, and that a linked
list of memory frames will live inside those blocks; we have seen how the
list is checked for integrity; but we not seen how it is built. Every
memory frame is created by the following function:
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> *</span><span class="functiontext">Memory::allocate</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">mem_type</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">extent</span><span class="plain">) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">cp</span><span class="plain">;</span>
<span class="reserved">memory_frame</span><span class="plain"> *</span><span class="identifier">mf</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">bytes_free_in_current_memblock</span><span class="plain">, </span><span class="identifier">extent_without_overheads</span><span class="plain"> = </span><span class="identifier">extent</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">extent</span><span class="plain"> += </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="reserved">memory_frame</span><span class="plain">); </span><span class="comment"> each allocation is preceded by a memory frame</span>
<span class="identifier">extent</span><span class="plain"> += </span><span class="constant">SAFETY_MARGIN</span><span class="plain">; </span><span class="comment"> each allocation is followed by <code class="display"><span class="extract">SAFETY_MARGIN</span></code> null bytes</span>
2019-02-04 22:26:45 +00:00
&lt;<span class="cwebmacro">Ensure that the current memory block has room for this many bytes</span> <span class="cwebmacronumber">16.1</span>&gt;<span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">cp</span><span class="plain"> = ((</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain"> *) (</span><span class="identifier">current_memblock_header</span><span class="plain">-&gt;</span><span class="element">the_memory</span><span class="plain">)) + </span><span class="identifier">used_in_current_memblock</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">used_in_current_memblock</span><span class="plain"> += </span><span class="identifier">extent</span><span class="plain">;</span>
<span class="identifier">mf</span><span class="plain"> = (</span><span class="reserved">memory_frame</span><span class="plain"> *) </span><span class="identifier">cp</span><span class="plain">; </span><span class="comment"> the new memory frame,</span>
<span class="identifier">cp</span><span class="plain"> = </span><span class="identifier">cp</span><span class="plain"> + </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="reserved">memory_frame</span><span class="plain">); </span><span class="comment"> following which is the actual allocated data</span>
2019-02-04 22:26:45 +00:00
2020-04-06 11:26:10 +00:00
<span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">integrity_check</span><span class="plain"> = </span><span class="constant">INTEGRITY_NUMBER</span><span class="plain">;</span>
<span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">allocation_id</span><span class="plain"> = </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">mem_type</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain">;</span>
<span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">mem_type</span><span class="plain"> = </span><span class="identifier">mem_type</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
&lt;<span class="cwebmacro">Add the new memory frame to the big linked list of all frames</span> <span class="cwebmacronumber">16.2</span>&gt;<span class="plain">;</span>
&lt;<span class="cwebmacro">Update the allocation status for this type of object</span> <span class="cwebmacronumber">16.3</span>&gt;<span class="plain">;</span>
<span class="identifier">total_objects_allocated</span><span class="plain">++;</span>
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">cp</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::allocate is used in <a href="#SP19">&#167;19</a>.</p>
<p class="inwebparagraph"><a id="SP16_1"></a><b>&#167;16.1. </b>The granularity error below will be triggered the first time a particular
object type is allocated. So this is not a potential time-bomb just waiting
for a user with a particularly long and involved source text to discover.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Ensure that the current memory block has room for this many bytes</span> <span class="cwebmacronumber">16.1</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">current_memblock_header</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="functiontext">Memory::allocate_another_block</span><span class="plain">();</span>
<span class="identifier">bytes_free_in_current_memblock</span><span class="plain"> = </span><span class="constant">MEMORY_GRANULARITY</span><span class="plain"> - (</span><span class="identifier">used_in_current_memblock</span><span class="plain"> + </span><span class="identifier">extent</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">bytes_free_in_current_memblock</span><span class="plain"> &lt; </span><span class="constant">BLANK_END_SIZE</span><span class="plain">) {</span>
<span class="functiontext">Memory::allocate_another_block</span><span class="plain">();</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">extent</span><span class="plain">+</span><span class="constant">BLANK_END_SIZE</span><span class="plain"> &gt;= </span><span class="constant">MEMORY_GRANULARITY</span><span class="plain">)</span>
<span class="functiontext">Errors::fatal</span><span class="plain">(</span><span class="string">"Memory manager failed because granularity too low"</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP16">&#167;16</a>.</p>
<p class="inwebparagraph"><a id="SP16_2"></a><b>&#167;16.2. </b>New memory frames are added to the tail of the list:
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Add the new memory frame to the big linked list of all frames</span> <span class="cwebmacronumber">16.2</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="identifier">mf</span><span class="plain">-&gt;</span><span class="element">next_frame</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">first_memory_frame</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">first_memory_frame</span><span class="plain"> = </span><span class="identifier">mf</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="identifier">last_memory_frame</span><span class="plain">-&gt;</span><span class="element">next_frame</span><span class="plain"> = </span><span class="identifier">mf</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">last_memory_frame</span><span class="plain"> = </span><span class="identifier">mf</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP16">&#167;16</a>.</p>
<p class="inwebparagraph"><a id="SP16_3"></a><b>&#167;16.3. </b>See the definition of <code class="display"><span class="extract">alloc_status</span></code> above.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Update the allocation status for this type of object</span> <span class="cwebmacronumber">16.3</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">mem_type</span><span class="plain">].</span><span class="element">first_in_memory</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">)</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">mem_type</span><span class="plain">].</span><span class="element">first_in_memory</span><span class="plain"> = (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">cp</span><span class="plain">;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">mem_type</span><span class="plain">].</span><span class="element">last_in_memory</span><span class="plain"> = (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">cp</span><span class="plain">;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">mem_type</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain">++;</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">mem_type</span><span class="plain">].</span><span class="element">bytes_allocated</span><span class="plain"> += </span><span class="identifier">extent_without_overheads</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP16">&#167;16</a>.</p>
<p class="inwebparagraph"><a id="SP17"></a><b>&#167;17. Level 3: managing linked lists of allocated objects. </b>We define macros which look as if they are functions, but for which one
argument is the name of a type: expanding these macros provides suitable C
functions to handle each possible type. These macros provide the interface
through which all other sections allocate and leaf through memory.
</p>
<p class="inwebparagraph">Note that Inweb allows multi-line macro definitions without backslashes
to continue them, unlike ordinary C. Otherwise these are "standard"
macros, though this was my first brush with the <code class="display"><span class="extract">##</span></code> concatenation
operator: basically <code class="display"><span class="extract">CREATE(thing)</span></code> expands into <code class="display"><span class="extract">(allocate_thing())</span></code>
because of the <code class="display"><span class="extract">##</span></code>. (See Kernighan and Ritchie, section 4.11.2.)
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">CREATE</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">) (</span><span class="identifier">allocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">())</span>
<span class="definitionkeyword">define</span> <span class="identifier">COPY</span><span class="plain">(</span><span class="identifier">to</span><span class="plain">, </span><span class="identifier">from</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">) (</span><span class="identifier">copy_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="identifier">to</span><span class="plain">, </span><span class="identifier">from</span><span class="plain">))</span>
<span class="definitionkeyword">define</span> <span class="identifier">CREATE_BEFORE</span><span class="plain">(</span><span class="identifier">existing</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">) (</span><span class="identifier">allocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_before</span><span class="plain">(</span><span class="identifier">existing</span><span class="plain">))</span>
<span class="definitionkeyword">define</span> <span class="identifier">DESTROY</span><span class="plain">(</span><span class="identifier">this</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">) (</span><span class="identifier">deallocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="identifier">this</span><span class="plain">))</span>
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">define</span> <span class="identifier">FIRST_OBJECT</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">) ((</span><span class="identifier">type_name</span><span class="plain"> *) </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">first_in_memory</span><span class="plain">)</span>
<span class="definitionkeyword">define</span> <span class="identifier">LAST_OBJECT</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">) ((</span><span class="identifier">type_name</span><span class="plain"> *) </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">last_in_memory</span><span class="plain">)</span>
2019-02-04 22:26:45 +00:00
<span class="definitionkeyword">define</span> <span class="identifier">NEXT_OBJECT</span><span class="plain">(</span><span class="identifier">this</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">) ((</span><span class="identifier">type_name</span><span class="plain"> *) (</span><span class="identifier">this</span><span class="plain">-&gt;</span><span class="identifier">next_structure</span><span class="plain">))</span>
<span class="definitionkeyword">define</span> <span class="identifier">PREV_OBJECT</span><span class="plain">(</span><span class="identifier">this</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">) ((</span><span class="identifier">type_name</span><span class="plain"> *) (</span><span class="identifier">this</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain">))</span>
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">define</span> <span class="identifier">NUMBER_CREATED</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">) (</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">objects_count</span><span class="plain">)</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"><a id="SP18"></a><b>&#167;18. </b>The following macros are widely used (well, the first one is, anyway)
for looking through the double linked list of existing objects of a
given type.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">LOOP_OVER</span><span class="plain">(</span><span class="identifier">var</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">)</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">var</span><span class="plain">=</span><span class="identifier">FIRST_OBJECT</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">); </span><span class="identifier">var</span><span class="plain"> != </span><span class="identifier">NULL</span><span class="plain">; </span><span class="identifier">var</span><span class="plain"> = </span><span class="identifier">NEXT_OBJECT</span><span class="plain">(</span><span class="identifier">var</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">))</span>
2019-02-04 22:26:45 +00:00
<span class="definitionkeyword">define</span> <span class="identifier">LOOP_BACKWARDS_OVER</span><span class="plain">(</span><span class="identifier">var</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">)</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">var</span><span class="plain">=</span><span class="identifier">LAST_OBJECT</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">); </span><span class="identifier">var</span><span class="plain"> != </span><span class="identifier">NULL</span><span class="plain">; </span><span class="identifier">var</span><span class="plain"> = </span><span class="identifier">PREV_OBJECT</span><span class="plain">(</span><span class="identifier">var</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">))</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"><a id="SP19"></a><b>&#167;19. Allocator functions created by macros. </b>The following macros generate a family of systematically named functions.
For instance, we shall shortly expand <code class="display"><span class="extract">ALLOCATE_INDIVIDUALLY(parse_node)</span></code>,
which will expand to three functions: <code class="display"><span class="extract">allocate_parse_node</span></code>,
<code class="display"><span class="extract">deallocate_parse_node</span></code> and <code class="display"><span class="extract">allocate_parse_node_before</span></code>.
</p>
<p class="inwebparagraph">Quaintly, <code class="display"><span class="extract">#type_name</span></code> expands into the value of <code class="display"><span class="extract">type_name</span></code> put within
double-quotes.
</p>
<pre class="definitions">
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">define</span> <span class="identifier">NEW_OBJECT</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">) ((</span><span class="identifier">type_name</span><span class="plain"> *) </span><span class="functiontext">Memory::allocate</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">, </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">)))</span>
2019-02-04 22:26:45 +00:00
<span class="definitionkeyword">define</span> <span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">)</span>
<span class="identifier">MAKE_REFERENCE_ROUTINES</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">)</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">allocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">name_of_type</span><span class="plain"> = #</span><span class="identifier">type_name</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">prev_obj</span><span class="plain"> = </span><span class="identifier">LAST_OBJECT</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">);</span>
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">new_obj</span><span class="plain"> = </span><span class="identifier">NEW_OBJECT</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">new_obj</span><span class="plain">-&gt;</span><span class="element">allocation_id</span><span class="plain"> = </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain">-1;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">new_obj</span><span class="plain">-&gt;</span><span class="identifier">next_structure</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">prev_obj</span><span class="plain"> != </span><span class="identifier">NULL</span><span class="plain">)</span>
<span class="identifier">prev_obj</span><span class="plain">-&gt;</span><span class="identifier">next_structure</span><span class="plain"> = (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">new_obj</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">new_obj</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain"> = </span><span class="identifier">prev_obj</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">objects_count</span><span class="plain">++;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">new_obj</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="identifier">deallocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">kill_me</span><span class="plain">) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">prev_obj</span><span class="plain"> = </span><span class="identifier">PREV_OBJECT</span><span class="plain">(</span><span class="identifier">kill_me</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">);</span>
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">next_obj</span><span class="plain"> = </span><span class="identifier">NEXT_OBJECT</span><span class="plain">(</span><span class="identifier">kill_me</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">prev_obj</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">first_in_memory</span><span class="plain"> = </span><span class="identifier">next_obj</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">prev_obj</span><span class="plain">-&gt;</span><span class="identifier">next_structure</span><span class="plain"> = </span><span class="identifier">next_obj</span><span class="plain">;</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">next_obj</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">last_in_memory</span><span class="plain"> = </span><span class="identifier">prev_obj</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">next_obj</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain"> = </span><span class="identifier">prev_obj</span><span class="plain">;</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">objects_count</span><span class="plain">--;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">allocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_before</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">existing</span><span class="plain">) {</span>
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">new_obj</span><span class="plain"> = </span><span class="identifier">allocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">();</span>
<span class="identifier">deallocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="identifier">new_obj</span><span class="plain">);</span>
<span class="identifier">new_obj</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain"> = </span><span class="identifier">existing</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">existing</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain"> != </span><span class="identifier">NULL</span><span class="plain">)</span>
2019-02-04 22:26:45 +00:00
<span class="plain">((</span><span class="identifier">type_name</span><span class="plain"> *) </span><span class="identifier">existing</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain">)-&gt;</span><span class="identifier">next_structure</span><span class="plain"> = </span><span class="identifier">new_obj</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">else</span><span class="plain"> </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">first_in_memory</span><span class="plain"> = (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">new_obj</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">new_obj</span><span class="plain">-&gt;</span><span class="identifier">next_structure</span><span class="plain"> = </span><span class="identifier">existing</span><span class="plain">;</span>
<span class="identifier">existing</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain"> = </span><span class="identifier">new_obj</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_MT</span><span class="plain">].</span><span class="element">objects_count</span><span class="plain">++;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">new_obj</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="identifier">copy_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">to</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">from</span><span class="plain">) {</span>
2019-03-12 23:32:12 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">prev_obj</span><span class="plain"> = </span><span class="identifier">to</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain">;</span>
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">next_obj</span><span class="plain"> = </span><span class="identifier">to</span><span class="plain">-&gt;</span><span class="identifier">next_structure</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">aid</span><span class="plain"> = </span><span class="identifier">to</span><span class="plain">-&gt;</span><span class="element">allocation_id</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">*</span><span class="identifier">to</span><span class="plain"> = *</span><span class="identifier">from</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">to</span><span class="plain">-&gt;</span><span class="element">allocation_id</span><span class="plain"> = </span><span class="identifier">aid</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">to</span><span class="plain">-&gt;</span><span class="identifier">next_structure</span><span class="plain"> = </span><span class="identifier">next_obj</span><span class="plain">;</span>
<span class="identifier">to</span><span class="plain">-&gt;</span><span class="identifier">prev_structure</span><span class="plain"> = </span><span class="identifier">prev_obj</span><span class="plain">;</span>
2019-03-12 23:32:12 +00:00
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"><a id="SP20"></a><b>&#167;20. </b><code class="display"><span class="extract">ALLOCATE_IN_ARRAYS</span></code> is still more obfuscated. When we
<code class="display"><span class="extract">ALLOCATE_IN_ARRAYS(X, 100)</span></code>, the result will be definitions of a new type
<code class="display"><span class="extract">X_block</span></code> and functions <code class="display"><span class="extract">allocate_X</span></code>, <code class="display"><span class="extract">allocate_X_block</span></code>,
<code class="display"><span class="extract">deallocate_X_block</span></code> and <code class="display"><span class="extract">allocate_X_block_before</span></code> (though the last is not
destined ever to be used). Note that we are not provided with the means to
deallocate individual objects this time: that's the trade-off for
allocating in blocks.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">ALLOCATE_IN_ARRAYS</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">, </span><span class="identifier">NO_TO_ALLOCATE_TOGETHER</span><span class="plain">)</span>
<span class="identifier">MAKE_REFERENCE_ROUTINES</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">, </span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array_MT</span><span class="plain">)</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain"> {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">used</span><span class="plain">;</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="identifier">type_name</span><span class="plain"> </span><span class="identifier">array</span><span class="plain">[</span><span class="identifier">NO_TO_ALLOCATE_TOGETHER</span><span class="plain">];</span>
<span class="constant">MEMORY_MANAGEMENT</span>
2019-02-04 22:26:45 +00:00
<span class="plain">} </span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain">;</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain">)</span>
<span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain"> *</span><span class="identifier">next_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">struct</span><span class="plain"> </span><span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">allocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">next_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) ||</span>
2019-02-04 22:26:45 +00:00
<span class="plain">(</span><span class="identifier">next_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain">-&gt;</span><span class="identifier">used</span><span class="plain"> &gt;= </span><span class="identifier">NO_TO_ALLOCATE_TOGETHER</span><span class="plain">)) {</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array_MT</span><span class="plain">].</span><span class="element">no_allocated_together</span><span class="plain"> = </span><span class="identifier">NO_TO_ALLOCATE_TOGETHER</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">next_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain"> = </span><span class="identifier">allocate_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain">();</span>
2020-04-04 19:46:43 +00:00
<span class="identifier">next_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain">-&gt;</span><span class="identifier">used</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> &amp;(</span><span class="identifier">next_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain">-&gt;</span><span class="identifier">array</span><span class="plain">[</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">next_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">##</span><span class="identifier">_array</span><span class="plain">-&gt;</span><span class="identifier">used</span><span class="plain">++]);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"><a id="SP21"></a><b>&#167;21. Expanding many macros. </b>Each given structure must have a typedef name, say <code class="display"><span class="extract">marvel</span></code>, and can be
used in one of two ways. Either way, we can obtain a new one with the macro
<code class="display"><span class="extract">CREATE(marvel)</span></code>.
</p>
<p class="inwebparagraph">Either (a) it will be individually allocated. In this case <code class="display"><span class="extract">marvel_MT</span></code>
should be defined with a new MT (memory type) number, and the macro
<code class="display"><span class="extract">ALLOCATE_INDIVIDUALLY(marvel)</span></code> should be expanded. The first and last
objects created will be <code class="display"><span class="extract">FIRST_OBJECT(marvel)</span></code> and <code class="display"><span class="extract">LAST_OBJECT(marvel)</span></code>,
and we can proceed either way through a double linked list of them with
<code class="display"><span class="extract">PREV_OBJECT(mv, marvel)</span></code> and <code class="display"><span class="extract">NEXT_OBJECT(mv, marvel)</span></code>. For convenience,
we can loop through marvels, in creation order, using <code class="display"><span class="extract">LOOP_OVER(var,
</span></code>marvel)<code class="display"><span class="extract">, which expands to a </span></code>for<code class="display"><span class="extract"> loop in which the variable </span></code>var<code class="display"><span class="extract"> runs
</span></code>through each created marvel in turn; or equally we can run backwards
through using <code class="display"><span class="extract">LOOP_BACKWARDS_OVER(var, marvel)</span></code>. In addition, there are
corruption checks to protect the memory from overrunning accidents, and the
structure can be used as a value in the symbols table. Good for large
structures with significant semantic content.
</p>
<p class="inwebparagraph">Or (b) it will be allocated in arrays. Once again we can obtain new marvels
with <code class="display"><span class="extract">CREATE(marvel)</span></code>. This is more efficient both in speed and memory
usage, but we lose the ability to loop through the objects. For this
arrangement, define <code class="display"><span class="extract">marvel_array_MT</span></code> with a new MT number and expand the
macro <code class="display"><span class="extract">ALLOCATE_IN_ARRAYS(marvel, 100)</span></code>, where 100 (or what may you) is the
number of objects allocated jointly as a block. Good for small structures
used in the lower levels.
</p>
<p class="inwebparagraph">Here goes, then.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">pathname</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">string_storage_area</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">scan_directory</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">ebook</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">ebook_datum</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">ebook_volume</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">ebook_chapter</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">ebook_page</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">ebook_image</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">HTML_file_state</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">command_line_switch</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">dictionary</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">debugging_aspect</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">linked_list</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">method</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">method_set</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">ebook_mark</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">semantic_version_number_holder</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">semver_range</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">web_bibliographic_datum</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">web_md</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">chapter_md</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">section_md</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">module</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_INDIVIDUALLY</span><span class="plain">(</span><span class="reserved">module_search</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_IN_ARRAYS</span><span class="plain">(</span><span class="reserved">dict_entry</span><span class="plain">, </span><span class="constant">100</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_IN_ARRAYS</span><span class="plain">(</span><span class="reserved">HTML_tag</span><span class="plain">, </span><span class="constant">1000</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_IN_ARRAYS</span><span class="plain">(</span><span class="reserved">linked_list_item</span><span class="plain">, </span><span class="constant">1000</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_IN_ARRAYS</span><span class="plain">(</span><span class="reserved">match_avinue</span><span class="plain">, </span><span class="constant">1000</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_IN_ARRAYS</span><span class="plain">(</span><span class="reserved">match_trie</span><span class="plain">, </span><span class="constant">1000</span><span class="plain">)</span>
<span class="identifier">ALLOCATE_IN_ARRAYS</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain">, </span><span class="constant">100</span><span class="plain">)</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP22"></a><b>&#167;22. Simple memory allocations. </b>Not all of our memory will be claimed in the form of structures: now and then
we need to use the equivalent of traditional <code class="display"><span class="extract">malloc</span></code> and <code class="display"><span class="extract">calloc</span></code> routines.
</p>
<pre class="definitions">
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">enum</span> <span class="constant">STREAM_MREASON</span><span class="definitionkeyword"> from </span><span class="constant">0</span>
<span class="definitionkeyword">enum</span> <span class="constant">FILENAME_STORAGE_MREASON</span>
<span class="definitionkeyword">enum</span> <span class="constant">STRING_STORAGE_MREASON</span>
<span class="definitionkeyword">enum</span> <span class="constant">DICTIONARY_MREASON</span>
<span class="definitionkeyword">enum</span> <span class="constant">CLS_SORTING_MREASON</span>
2019-02-04 22:26:45 +00:00
</pre>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::name_fundamental_reasons</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="functiontext">Memory::reason_name</span><span class="plain">(</span><span class="constant">STREAM_MREASON</span><span class="plain">, </span><span class="string">"text stream storage"</span><span class="plain">);</span>
<span class="functiontext">Memory::reason_name</span><span class="plain">(</span><span class="constant">FILENAME_STORAGE_MREASON</span><span class="plain">, </span><span class="string">"filename/pathname storage"</span><span class="plain">);</span>
<span class="functiontext">Memory::reason_name</span><span class="plain">(</span><span class="constant">STRING_STORAGE_MREASON</span><span class="plain">, </span><span class="string">"string storage"</span><span class="plain">);</span>
<span class="functiontext">Memory::reason_name</span><span class="plain">(</span><span class="constant">DICTIONARY_MREASON</span><span class="plain">, </span><span class="string">"dictionary storage"</span><span class="plain">);</span>
<span class="functiontext">Memory::reason_name</span><span class="plain">(</span><span class="constant">CLS_SORTING_MREASON</span><span class="plain">, </span><span class="string">"sorting"</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::name_fundamental_reasons is used in <a href="#SP5">&#167;5</a>.</p>
<p class="inwebparagraph"><a id="SP23"></a><b>&#167;23. </b>And here is the (very simple) implementation.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">char</span><span class="plain"> *</span><span class="identifier">memory_needs</span><span class="plain">[</span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">];</span>
2019-02-04 22:26:45 +00:00
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::reason_name</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">r</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">reason</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">r</span><span class="plain"> &lt; </span><span class="constant">0</span><span class="plain">) || (</span><span class="identifier">r</span><span class="plain"> &gt;= </span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">)) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"MR out of range"</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">memory_needs</span><span class="plain">[</span><span class="identifier">r</span><span class="plain">] = </span><span class="identifier">reason</span><span class="plain">;</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">char</span><span class="plain"> *</span><span class="functiontext">Memory::description_of_reason</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">r</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">r</span><span class="plain"> &lt; </span><span class="constant">0</span><span class="plain">) || (</span><span class="identifier">r</span><span class="plain"> &gt;= </span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">)) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"MR out of range"</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">memory_needs</span><span class="plain">[</span><span class="identifier">r</span><span class="plain">];</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::reason_name is used in <a href="#SP22">&#167;22</a>.</p>
<p class="endnote">The function Memory::description_of_reason is used in <a href="#SP26_1">&#167;26.1</a>, <a href="#SP31">&#167;31</a>.</p>
<p class="inwebparagraph"><a id="SP24"></a><b>&#167;24. </b>We keep some statistics on this. The value for "memory claimed" is the
net amount of memory currently owned, which is increased when we allocate
it and decreased when we free it. Whether the host OS is able to make
efficient use of the memory we free, we can't know, but it probably is, and
therefore the best estimate of how well we're doing is the "maximum memory
claimed" &mdash; the highest recorded net usage count over the run.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">max_memory_at_once_for_each_need</span><span class="plain">[</span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">],</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">memory_claimed_for_each_need</span><span class="plain">[</span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">],</span>
<span class="identifier">number_of_claims_for_each_need</span><span class="plain">[</span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">];</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">total_claimed_simply</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP25"></a><b>&#167;25. </b>Our allocation routines behave just like the standard C library's <code class="display"><span class="extract">malloc</span></code>
and <code class="display"><span class="extract">calloc</span></code>, but where a third argument supplies a reason why the memory is
needed, and where any failure to allocate memory is tidily dealt with. We will
exit on any such failure, so that the caller can be certain that the return
values of these functions are always non-<code class="display"><span class="extract">NULL</span></code> pointers.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> *</span><span class="functiontext">Memory::I7_calloc</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">how_many</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">size_in_bytes</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">reason</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Memory::I7_alloc</span><span class="plain">(</span><span class="identifier">how_many</span><span class="plain">, </span><span class="identifier">size_in_bytes</span><span class="plain">, </span><span class="identifier">reason</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> *</span><span class="functiontext">Memory::I7_malloc</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">size_in_bytes</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">reason</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Memory::I7_alloc</span><span class="plain">(-1, </span><span class="identifier">size_in_bytes</span><span class="plain">, </span><span class="identifier">reason</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::I7_calloc is used in 2/str (<a href="2-str.html#SP26">&#167;26</a>), 2/dct (<a href="2-dct.html#SP2">&#167;2</a>), 3/cla (<a href="3-cla.html#SP14">&#167;14</a>).</p>
2019-02-04 22:26:45 +00:00
<p class="endnote">The function Memory::I7_malloc is used in <a href="#SP29">&#167;29</a>, 2/str (<a href="2-str.html#SP35_3">&#167;35.3</a>).</p>
<p class="inwebparagraph"><a id="SP26"></a><b>&#167;26. </b>And this, then, is the joint routine implementing both.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> *</span><span class="functiontext">Memory::I7_alloc</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">N</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">R</span><span class="plain">) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">pointer</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">bytes_needed</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">R</span><span class="plain"> &lt; </span><span class="constant">0</span><span class="plain">) || (</span><span class="identifier">R</span><span class="plain"> &gt;= </span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">)) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"no such memory reason"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">total_claimed_simply</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) </span>&lt;<span class="cwebmacro">Zero out the statistics on simple memory allocations</span> <span class="cwebmacronumber">26.2</span>&gt;<span class="plain">;</span>
2019-02-04 22:26:45 +00:00
&lt;<span class="cwebmacro">Claim the memory using malloc or calloc as appropriate</span> <span class="cwebmacronumber">26.1</span>&gt;<span class="plain">;</span>
&lt;<span class="cwebmacro">Update the statistics on simple memory allocations</span> <span class="cwebmacronumber">26.3</span>&gt;<span class="plain">;</span>
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">pointer</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::I7_alloc is used in <a href="#SP25">&#167;25</a>.</p>
<p class="inwebparagraph"><a id="SP26_1"></a><b>&#167;26.1. </b>I am nervous about assuming that <code class="display"><span class="extract">calloc(0, X)</span></code> returns a non-<code class="display"><span class="extract">NULL</span></code> pointer
in all implementations of the standard C library, so the case when <code class="display"><span class="extract">N</span></code> is zero
allocates a tiny but positive amount of memory, just to be safe.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Claim the memory using malloc or calloc as appropriate</span> <span class="cwebmacronumber">26.1</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">N</span><span class="plain"> &gt; </span><span class="constant">0</span><span class="plain">) {</span>
<span class="identifier">pointer</span><span class="plain"> = </span><span class="functiontext">Memory::paranoid_calloc</span><span class="plain">((</span><span class="identifier">size_t</span><span class="plain">) </span><span class="identifier">N</span><span class="plain">, (</span><span class="identifier">size_t</span><span class="plain">) </span><span class="identifier">S</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">bytes_needed</span><span class="plain"> = </span><span class="identifier">N</span><span class="plain">*</span><span class="identifier">S</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">pointer</span><span class="plain"> = </span><span class="functiontext">Memory::paranoid_calloc</span><span class="plain">(1, (</span><span class="identifier">size_t</span><span class="plain">) </span><span class="identifier">S</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">bytes_needed</span><span class="plain"> = </span><span class="identifier">S</span><span class="plain">;</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">pointer</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="functiontext">Errors::fatal_with_C_string</span><span class="plain">(</span><span class="string">"Out of memory for %s"</span><span class="plain">, </span><span class="functiontext">Memory::description_of_reason</span><span class="plain">(</span><span class="identifier">R</span><span class="plain">));</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP26">&#167;26</a>.</p>
<p class="inwebparagraph"><a id="SP26_2"></a><b>&#167;26.2. </b>These statistics have no function except to improve the diagnostics in the
debugging log, but they are very cheap to keep, since <code class="display"><span class="extract">Memory::I7_alloc</span></code> is called only
rarely and to allocate large blocks of memory.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Zero out the statistics on simple memory allocations</span> <span class="cwebmacronumber">26.2</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++) {</span>
2020-04-04 19:46:43 +00:00
<span class="identifier">max_memory_at_once_for_each_need</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">memory_claimed_for_each_need</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">number_of_claims_for_each_need</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = </span><span class="constant">0</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP26">&#167;26</a>.</p>
<p class="inwebparagraph"><a id="SP26_3"></a><b>&#167;26.3. </b><code class="display">
&lt;<span class="cwebmacrodefn">Update the statistics on simple memory allocations</span> <span class="cwebmacronumber">26.3</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="identifier">memory_claimed_for_each_need</span><span class="plain">[</span><span class="identifier">R</span><span class="plain">] += </span><span class="identifier">bytes_needed</span><span class="plain">;</span>
<span class="identifier">total_claimed_simply</span><span class="plain"> += </span><span class="identifier">bytes_needed</span><span class="plain">;</span>
<span class="identifier">number_of_claims_for_each_need</span><span class="plain">[</span><span class="identifier">R</span><span class="plain">]++;</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">memory_claimed_for_each_need</span><span class="plain">[</span><span class="identifier">R</span><span class="plain">] &gt; </span><span class="identifier">max_memory_at_once_for_each_need</span><span class="plain">[</span><span class="identifier">R</span><span class="plain">])</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">max_memory_at_once_for_each_need</span><span class="plain">[</span><span class="identifier">R</span><span class="plain">] = </span><span class="identifier">memory_claimed_for_each_need</span><span class="plain">[</span><span class="identifier">R</span><span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP26">&#167;26</a>.</p>
<p class="inwebparagraph"><a id="SP27"></a><b>&#167;27. </b>We also provide our own wrapper for <code class="display"><span class="extract">free</span></code>:
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::I7_free</span><span class="plain">(</span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">pointer</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">R</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">bytes_freed</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">R</span><span class="plain"> &lt; </span><span class="constant">0</span><span class="plain">) || (</span><span class="identifier">R</span><span class="plain"> &gt;= </span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">)) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"no such memory reason"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">pointer</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"can't free NULL memory"</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">memory_claimed_for_each_need</span><span class="plain">[</span><span class="identifier">R</span><span class="plain">] -= </span><span class="identifier">bytes_freed</span><span class="plain">;</span>
<span class="identifier">free</span><span class="plain">(</span><span class="identifier">pointer</span><span class="plain">);</span>
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::I7_array_free</span><span class="plain">(</span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">pointer</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">R</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">num_cells</span><span class="plain">, </span><span class="identifier">size_t</span><span class="plain"> </span><span class="identifier">cell_size</span><span class="plain">) {</span>
<span class="functiontext">Memory::I7_free</span><span class="plain">(</span><span class="identifier">pointer</span><span class="plain">, </span><span class="identifier">R</span><span class="plain">, </span><span class="identifier">num_cells</span><span class="plain">*((</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">cell_size</span><span class="plain">));</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::I7_free is used in <a href="#SP30">&#167;30</a>, 2/str (<a href="2-str.html#SP34_2">&#167;34.2</a>), 2/dct (<a href="2-dct.html#SP7_2">&#167;7.2</a>, <a href="2-dct.html#SP11">&#167;11</a>), 3/cla (<a href="3-cla.html#SP14">&#167;14</a>).</p>
2019-02-04 22:26:45 +00:00
<p class="endnote">The function Memory::I7_array_free appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP28"></a><b>&#167;28. Text storage. </b>We will also use much simpler memory areas for text, in 64K chunks:
</p>
<pre class="definitions">
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">define</span> <span class="constant">SSA_CAPACITY</span><span class="plain"> </span><span class="constant">64</span><span class="plain">*1024</span>
2019-02-04 22:26:45 +00:00
</pre>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">string_storage_area</span><span class="plain"> {</span>
<span class="reserved">char</span><span class="plain"> *</span><span class="identifier">storage_at</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">first_free_byte</span><span class="plain">;</span>
<span class="constant">MEMORY_MANAGEMENT</span>
<span class="plain">} </span><span class="reserved">string_storage_area</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
2020-04-06 11:26:10 +00:00
<span class="reserved">string_storage_area</span><span class="plain"> *</span><span class="identifier">current_ssa</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure string_storage_area is private to this section.</p>
<p class="inwebparagraph"><a id="SP29"></a><b>&#167;29. </b>The following is ideal for parking a read-only string of text somewhere
safe in memory. Since the length can't be extended, it's usually unsafe
to write to the result.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">char</span><span class="plain"> *</span><span class="functiontext">Memory::new_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">from</span><span class="plain">) {</span>
2019-03-12 23:32:12 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">length_needed</span><span class="plain"> = (</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">strlen</span><span class="plain">(</span><span class="identifier">from</span><span class="plain">) + </span><span class="constant">1</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (!((</span><span class="identifier">current_ssa</span><span class="plain">) &amp;&amp;</span>
<span class="plain">(</span><span class="identifier">current_ssa</span><span class="plain">-&gt;</span><span class="element">first_free_byte</span><span class="plain"> + </span><span class="identifier">length_needed</span><span class="plain"> &lt; </span><span class="constant">SSA_CAPACITY</span><span class="plain">))) {</span>
<span class="identifier">current_ssa</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">string_storage_area</span><span class="plain">);</span>
<span class="identifier">current_ssa</span><span class="plain">-&gt;</span><span class="element">storage_at</span><span class="plain"> = </span><span class="functiontext">Memory::I7_malloc</span><span class="plain">(</span><span class="constant">SSA_CAPACITY</span><span class="plain">, </span><span class="constant">STRING_STORAGE_MREASON</span><span class="plain">);</span>
<span class="identifier">current_ssa</span><span class="plain">-&gt;</span><span class="element">first_free_byte</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">char</span><span class="plain"> *</span><span class="identifier">rp</span><span class="plain"> = </span><span class="identifier">current_ssa</span><span class="plain">-&gt;</span><span class="element">storage_at</span><span class="plain"> + </span><span class="identifier">current_ssa</span><span class="plain">-&gt;</span><span class="element">first_free_byte</span><span class="plain">;</span>
<span class="identifier">current_ssa</span><span class="plain">-&gt;</span><span class="element">first_free_byte</span><span class="plain"> += </span><span class="identifier">length_needed</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">strcpy</span><span class="plain">(</span><span class="identifier">rp</span><span class="plain">, </span><span class="identifier">from</span><span class="plain">);</span>
2019-03-12 23:32:12 +00:00
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">rp</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::new_string appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP30"></a><b>&#167;30. </b>And here we free any SSAs needed in the course of the run.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::free_ssas</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">string_storage_area</span><span class="plain"> *</span><span class="identifier">ssa</span><span class="plain">;</span>
<span class="identifier">LOOP_OVER</span><span class="plain">(</span><span class="identifier">ssa</span><span class="plain">, </span><span class="reserved">string_storage_area</span><span class="plain">)</span>
<span class="functiontext">Memory::I7_free</span><span class="plain">(</span><span class="identifier">ssa</span><span class="plain">-&gt;</span><span class="element">storage_at</span><span class="plain">, </span><span class="constant">STRING_STORAGE_MREASON</span><span class="plain">, </span><span class="constant">SSA_CAPACITY</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::free_ssas is used in <a href="#SP12">&#167;12</a>.</p>
<p class="inwebparagraph"><a id="SP31"></a><b>&#167;31. </b>And the following provides statistics, and a mini-report, for the memory
report in the debugging log (for which, see below).
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Memory::log_usage</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">total</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">total_claimed_simply</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">, </span><span class="identifier">t</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">NO_DEFINED_MREASON_VALUES</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">t</span><span class="plain"> += </span><span class="identifier">max_memory_at_once_for_each_need</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">];</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">total</span><span class="plain"> &gt; </span><span class="constant">0</span><span class="plain">)</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"0.%03d: %s - %d bytes in %d claim(s)\n"</span><span class="plain">,</span>
<span class="functiontext">Memory::proportion</span><span class="plain">(</span><span class="identifier">max_memory_at_once_for_each_need</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">], </span><span class="identifier">total</span><span class="plain">),</span>
<span class="functiontext">Memory::description_of_reason</span><span class="plain">(</span><span class="identifier">i</span><span class="plain">),</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">max_memory_at_once_for_each_need</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">],</span>
<span class="identifier">number_of_claims_for_each_need</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">]);</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">t</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::log_usage is used in <a href="#SP32">&#167;32</a>, <a href="#SP32_3">&#167;32.3</a>.</p>
<p class="inwebparagraph"><a id="SP32"></a><b>&#167;32. Memory usage report. </b>A small utility routine to help keep track of our unquestioned profligacy.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Memory::log_statistics</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">total_for_objects</span><span class="plain"> = </span><span class="constant">MEMORY_GRANULARITY</span><span class="plain">*</span><span class="identifier">no_blocks_allocated</span><span class="plain">; </span><span class="comment"> usage in bytes</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">total_for_SMAs</span><span class="plain"> = </span><span class="functiontext">Memory::log_usage</span><span class="plain">(0); </span><span class="comment"> usage in bytes</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">sorted_usage</span><span class="plain">[</span><span class="identifier">NO_DEFINED_MT_VALUES</span><span class="plain">]; </span><span class="comment"> memory type numbers, in usage order</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">total</span><span class="plain"> = (</span><span class="identifier">total_for_objects</span><span class="plain"> + </span><span class="identifier">total_for_SMAs</span><span class="plain">)/1024; </span><span class="comment"> total memory usage in KB</span>
2019-02-04 22:26:45 +00:00
&lt;<span class="cwebmacro">Sort the table of memory type usages into decreasing size order</span> <span class="cwebmacronumber">32.2</span>&gt;<span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">total_for_objects_used</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span><span class="comment"> out of the <code class="display"><span class="extract">total_for_objects</span></code>, the bytes used</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">total_objects</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
&lt;<span class="cwebmacro">Calculate the memory usage for objects</span> <span class="cwebmacronumber">32.1</span>&gt;<span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">overhead_for_objects</span><span class="plain"> = </span><span class="identifier">total_for_objects</span><span class="plain"> - </span><span class="identifier">total_for_objects_used</span><span class="plain">; </span><span class="comment"> bytes wasted</span>
2019-02-04 22:26:45 +00:00
&lt;<span class="cwebmacro">Print the report to the debugging log</span> <span class="cwebmacronumber">32.3</span>&gt;<span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
2020-04-09 17:32:37 +00:00
<p class="endnote">The function Memory::log_statistics is used in 1/fm (<a href="1-fm.html#SP9">&#167;9</a>).</p>
2019-02-04 22:26:45 +00:00
<p class="inwebparagraph"><a id="SP32_1"></a><b>&#167;32.1. </b><code class="display">
&lt;<span class="cwebmacrodefn">Calculate the memory usage for objects</span> <span class="cwebmacronumber">32.1</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">j</span><span class="plain">=0; </span><span class="identifier">j</span><span class="plain">&lt;</span><span class="identifier">NO_DEFINED_MT_VALUES</span><span class="plain">; </span><span class="identifier">j</span><span class="plain">++) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">i</span><span class="plain"> = </span><span class="identifier">sorted_usage</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">];</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain"> != </span><span class="constant">0</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">no_allocated_together</span><span class="plain"> == </span><span class="constant">1</span><span class="plain">)</span>
2020-04-04 19:46:43 +00:00
<span class="identifier">total_objects</span><span class="plain"> += </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="identifier">objects_allocated</span><span class="plain">;</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">else</span>
2020-04-04 19:46:43 +00:00
<span class="identifier">total_objects</span><span class="plain"> += </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="identifier">objects_allocated</span><span class="plain">*</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">no_allocated_together</span><span class="plain">;</span>
<span class="identifier">total_for_objects_used</span><span class="plain"> += </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">bytes_allocated</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP32">&#167;32</a>.</p>
<p class="inwebparagraph"><a id="SP32_2"></a><b>&#167;32.2. </b>This is the criterion for sorting memory types in the report: descending
order of total number of bytes allocated.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Sort the table of memory type usages into decreasing size order</span> <span class="cwebmacronumber">32.2</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">NO_DEFINED_MT_VALUES</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++) </span><span class="identifier">sorted_usage</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = </span><span class="identifier">i</span><span class="plain">;</span>
<span class="identifier">qsort</span><span class="plain">(</span><span class="identifier">sorted_usage</span><span class="plain">, (</span><span class="identifier">size_t</span><span class="plain">) </span><span class="identifier">NO_DEFINED_MT_VALUES</span><span class="plain">, </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="reserved">int</span><span class="plain">), </span><span class="functiontext">Memory::compare_usage</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP32">&#167;32</a>.</p>
<p class="inwebparagraph"><a id="SP32_3"></a><b>&#167;32.3. </b>And here is the actual report:
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Print the report to the debugging log</span> <span class="cwebmacronumber">32.3</span>&gt; =
</code></p>
<pre class="displaydefn">
2020-04-06 11:26:10 +00:00
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"\nReport by memory manager:\n\n"</span><span class="plain">);</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"Total consumption was %dK = %dMB, divided up in the following proportions:\n"</span><span class="plain">,</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">total</span><span class="plain">, (</span><span class="identifier">total</span><span class="plain">+512)/1024);</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"0.%03d: %d objects in %d frames in %d memory blocks (of %dK each):\n"</span><span class="plain">,</span>
<span class="functiontext">Memory::proportion</span><span class="plain">(</span><span class="identifier">total_for_objects</span><span class="plain">, </span><span class="identifier">total</span><span class="plain">),</span>
<span class="identifier">total_objects</span><span class="plain">, </span><span class="identifier">total_objects_allocated</span><span class="plain">, </span><span class="identifier">no_blocks_allocated</span><span class="plain">, </span><span class="constant">MEMORY_GRANULARITY</span><span class="plain">/1024);</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">" 0.%03d: memory manager overhead - %d bytes\n"</span><span class="plain">,</span>
<span class="functiontext">Memory::proportion</span><span class="plain">(</span><span class="identifier">overhead_for_objects</span><span class="plain">, </span><span class="identifier">total</span><span class="plain">), </span><span class="identifier">overhead_for_objects</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">j</span><span class="plain">=0; </span><span class="identifier">j</span><span class="plain">&lt;</span><span class="identifier">NO_DEFINED_MT_VALUES</span><span class="plain">; </span><span class="identifier">j</span><span class="plain">++) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">i</span><span class="plain"> = </span><span class="identifier">sorted_usage</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">];</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain"> != </span><span class="constant">0</span><span class="plain">) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">" 0.%03d: %s - "</span><span class="plain">,</span>
2020-04-06 11:26:10 +00:00
<span class="functiontext">Memory::proportion</span><span class="plain">(</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">bytes_allocated</span><span class="plain">, </span><span class="identifier">total</span><span class="plain">),</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">name_of_type</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">no_allocated_together</span><span class="plain"> == </span><span class="constant">1</span><span class="plain">) {</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"%d "</span><span class="plain">, </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_count</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_count</span><span class="plain"> != </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain">)</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"(+%d deleted) "</span><span class="plain">,</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain"> - </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_count</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="identifier">LOG</span><span class="plain">(</span><span class="string">"%d blocks of %d = %d "</span><span class="plain">,</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain">, </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">no_allocated_together</span><span class="plain">,</span>
<span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">objects_allocated</span><span class="plain">*</span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">no_allocated_together</span><span class="plain">);</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"objects, %d bytes\n"</span><span class="plain">, </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">].</span><span class="element">bytes_allocated</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="functiontext">Memory::log_usage</span><span class="plain">(</span><span class="identifier">total</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP32">&#167;32</a>.</p>
<p class="inwebparagraph"><a id="SP33"></a><b>&#167;33. </b></p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Memory::compare_usage</span><span class="plain">(</span><span class="reserved">const</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">ent1</span><span class="plain">, </span><span class="reserved">const</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">ent2</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">ix1</span><span class="plain"> = *((</span><span class="reserved">const</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> *) </span><span class="identifier">ent1</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">ix2</span><span class="plain"> = *((</span><span class="reserved">const</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> *) </span><span class="identifier">ent2</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">ix2</span><span class="plain">].</span><span class="element">bytes_allocated</span><span class="plain"> - </span><span class="identifier">alloc_status</span><span class="plain">[</span><span class="identifier">ix1</span><span class="plain">].</span><span class="element">bytes_allocated</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::compare_usage is used in <a href="#SP32_2">&#167;32.2</a>.</p>
<p class="inwebparagraph"><a id="SP34"></a><b>&#167;34. </b>Finally, a little routine to compute the proportions of memory for each
usage. Recall that <code class="display"><span class="extract">bytes</span></code> is measured in bytes, but <code class="display"><span class="extract">total</span></code> in kilobytes.
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Memory::proportion</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">bytes</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">total</span><span class="plain">) {</span>
<span class="reserved">float</span><span class="plain"> </span><span class="identifier">B</span><span class="plain"> = (</span><span class="reserved">float</span><span class="plain">) </span><span class="identifier">bytes</span><span class="plain">, </span><span class="identifier">T</span><span class="plain"> = (</span><span class="reserved">float</span><span class="plain">) </span><span class="identifier">total</span><span class="plain">;</span>
<span class="reserved">float</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = (1000*</span><span class="identifier">B</span><span class="plain">)/(1024*</span><span class="identifier">T</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">P</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::proportion is used in <a href="#SP31">&#167;31</a>, <a href="#SP32_3">&#167;32.3</a>.</p>
<p class="inwebparagraph"><a id="SP35"></a><b>&#167;35. </b></p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> *</span><span class="functiontext">Memory::paranoid_calloc</span><span class="plain">(</span><span class="identifier">size_t</span><span class="plain"> </span><span class="identifier">N</span><span class="plain">, </span><span class="identifier">size_t</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">) {</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">P</span><span class="plain"> = </span><span class="identifier">calloc</span><span class="plain">(</span><span class="identifier">N</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::paranoid_calloc is used in <a href="#SP11_1">&#167;11.1</a>, <a href="#SP26_1">&#167;26.1</a>.</p>
<p class="inwebparagraph"><a id="SP36"></a><b>&#167;36. Run-time pointer type checking. </b>In several places Inform needs to store pointers of type <code class="display"><span class="extract">void *</span></code>, that is,
pointers which have no indication of what type of data they point to.
This is not type-safe and therefore offers plenty of opportunity for
blunders. The following provides run-time type checking to ensure that
each time we dereference a typeless pointer, it does indeed point to
a structure of the type we think it should.
</p>
<p class="inwebparagraph">The structure <code class="display"><span class="extract">general_pointer</span></code> holds a <code class="display"><span class="extract">void *</span></code> pointer to any one of the
following:
</p>
<p class="inwebparagraph"></p>
2020-04-10 20:29:28 +00:00
<ul class="items"><li>(a) <code class="display"><span class="extract">NULL</span></code>, to which we assign ID number \(-1\);
2019-02-04 22:26:45 +00:00
</li><li>(b) <code class="display"><span class="extract">char</span></code>, to which we assign ID number 1000;
</li><li>(c) any individually allocated structure of the types listed above, to
which we assign the ID numbers used above: for instance, <code class="display"><span class="extract">blorb_figure_MT</span></code>
is the ID number for a <code class="display"><span class="extract">general_pointer</span></code> which points to a <code class="display"><span class="extract">blorb_figure</span></code>
structure.
</li></ul>
<pre class="definitions">
2020-04-06 11:26:10 +00:00
<span class="definitionkeyword">define</span> <span class="constant">NULL_GENERAL_POINTER</span><span class="plain"> (</span><span class="functiontext">Memory::store_gp_null</span><span class="plain">())</span>
<span class="definitionkeyword">define</span> <span class="identifier">GENERAL_POINTER_IS_NULL</span><span class="plain">(</span><span class="identifier">gp</span><span class="plain">) (</span><span class="functiontext">Memory::test_gp_null</span><span class="plain">(</span><span class="identifier">gp</span><span class="plain">))</span>
2019-02-04 22:26:45 +00:00
</pre>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">general_pointer</span><span class="plain"> {</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">pointer_to_data</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">run_time_type_code</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">general_pointer</span><span class="plain">;</span>
<span class="reserved">general_pointer</span><span class="plain"> </span><span class="functiontext">Memory::store_gp_null</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">general_pointer</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">;</span>
<span class="identifier">gp</span><span class="plain">.</span><span class="element">pointer_to_data</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">gp</span><span class="plain">.</span><span class="element">run_time_type_code</span><span class="plain"> = -1; </span><span class="comment"> guaranteed to differ from all <code class="display"><span class="extract">_MT</span></code> values</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Memory::test_gp_null</span><span class="plain">(</span><span class="reserved">general_pointer</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">gp</span><span class="plain">.</span><span class="element">run_time_type_code</span><span class="plain"> == -1) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Memory::store_gp_null appears nowhere else.</p>
<p class="endnote">The function Memory::test_gp_null appears nowhere else.</p>
<p class="endnote">The structure general_pointer is private to this section.</p>
<p class="inwebparagraph"><a id="SP37"></a><b>&#167;37. </b>The symbols tables need to look at pointer values directly without knowing
their types, but only to test equality, so we abstract that thus. And the
debugging log also shows actual hexadecimal addresses to distinguish nameless
objects and to help with interpreting output from GDB, so we abstract that too.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">COMPARE_GENERAL_POINTERS</span><span class="plain">(</span><span class="identifier">gp1</span><span class="plain">, </span><span class="identifier">gp2</span><span class="plain">)</span>
2020-04-06 11:26:10 +00:00
<span class="plain">(</span><span class="identifier">gp1</span><span class="plain">.</span><span class="element">pointer_to_data</span><span class="plain"> == </span><span class="identifier">gp2</span><span class="plain">.</span><span class="element">pointer_to_data</span><span class="plain">)</span>
2019-02-04 22:26:45 +00:00
<span class="definitionkeyword">define</span> <span class="identifier">GENERAL_POINTER_AS_INT</span><span class="plain">(</span><span class="identifier">gp</span><span class="plain">)</span>
2020-04-06 11:26:10 +00:00
<span class="plain">((</span><span class="identifier">pointer_sized_int</span><span class="plain">) </span><span class="identifier">gp</span><span class="plain">.</span><span class="element">pointer_to_data</span><span class="plain">)</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"><a id="SP38"></a><b>&#167;38. </b>If we have a pointer to <code class="display"><span class="extract">circus</span></code> (say) then <code class="display"><span class="extract">g=STORE_POINTER_circus(p)</span></code>
returns a <code class="display"><span class="extract">general_pointer</span></code> with <code class="display"><span class="extract">p</span></code> as the actual pointer, but will not
compile unless <code class="display"><span class="extract">p</span></code> is indeed of type <code class="display"><span class="extract">circus *</span></code>. When we later
<code class="display"><span class="extract">RETRIEVE_POINTER_circus(g)</span></code>, an internal error is thrown if <code class="display"><span class="extract">g</span></code> contains a pointer
which is other than <code class="display"><span class="extract">void *</span></code>, or which has never been referenced.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">MAKE_REFERENCE_ROUTINES</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain">, </span><span class="identifier">id_code</span><span class="plain">)</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">general_pointer</span><span class="plain"> </span><span class="identifier">STORE_POINTER_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">data</span><span class="plain">) {</span>
<span class="reserved">general_pointer</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">;</span>
<span class="identifier">gp</span><span class="plain">.</span><span class="element">pointer_to_data</span><span class="plain"> = (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">data</span><span class="plain">;</span>
<span class="identifier">gp</span><span class="plain">.</span><span class="element">run_time_type_code</span><span class="plain"> = </span><span class="identifier">id_code</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="identifier">type_name</span><span class="plain"> *</span><span class="identifier">RETRIEVE_POINTER_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="reserved">general_pointer</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">gp</span><span class="plain">.</span><span class="element">run_time_type_code</span><span class="plain"> != </span><span class="identifier">id_code</span><span class="plain">) {</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"Wanted ID code %d, found %d\n"</span><span class="plain">, </span><span class="identifier">id_code</span><span class="plain">, </span><span class="identifier">gp</span><span class="plain">.</span><span class="element">run_time_type_code</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"attempt to retrieve wrong pointer type as "</span><span class="plain"> #</span><span class="identifier">type_name</span><span class="plain">);</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> (</span><span class="identifier">type_name</span><span class="plain"> *) </span><span class="identifier">gp</span><span class="plain">.</span><span class="element">pointer_to_data</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">general_pointer</span><span class="plain"> </span><span class="identifier">PASS_POINTER_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="reserved">general_pointer</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">gp</span><span class="plain">.</span><span class="element">run_time_type_code</span><span class="plain"> != </span><span class="identifier">id_code</span><span class="plain">) {</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"Wanted ID code %d, found %d\n"</span><span class="plain">, </span><span class="identifier">id_code</span><span class="plain">, </span><span class="identifier">gp</span><span class="plain">.</span><span class="element">run_time_type_code</span><span class="plain">);</span>
2019-02-04 22:26:45 +00:00
<span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"attempt to pass wrong pointer type as "</span><span class="plain"> #</span><span class="identifier">type_name</span><span class="plain">);</span>
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
2020-04-06 11:26:10 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">VALID_POINTER_</span><span class="plain">##</span><span class="identifier">type_name</span><span class="plain">(</span><span class="reserved">general_pointer</span><span class="plain"> </span><span class="identifier">gp</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">gp</span><span class="plain">.</span><span class="element">run_time_type_code</span><span class="plain"> == </span><span class="identifier">id_code</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"><a id="SP39"></a><b>&#167;39. </b>Suitable <code class="display"><span class="extract">MAKE_REFERENCE_ROUTINES</span></code> were expanded for all of the memory
allocated objects above; so that leaves only humble <code class="display"><span class="extract">char *</span></code> pointers:
</p>
<pre class="display">
2020-04-06 11:26:10 +00:00
<span class="identifier">MAKE_REFERENCE_ROUTINES</span><span class="plain">(</span><span class="reserved">char</span><span class="plain">, </span><span class="constant">1000</span><span class="plain">)</span>
2019-02-04 22:26:45 +00:00
</pre>
<p class="inwebparagraph"></p>
2019-03-12 23:32:12 +00:00
<hr class="tocbar">
<ul class="toc"><li><a href="2-dl.html">Back to 'Debugging Log'</a></li><li><a href="2-str.html">Continue with 'Streams'</a></li></ul><hr class="tocbar">
<!--End of weave-->
2020-04-10 20:29:28 +00:00
<script>
MathJax = {
tex: {
inlineMath: <b>'$', '$'], ['\\(', '\\)'</b>
},
svg: {
fontCache: 'global'
}
};
</script>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js">
</script>
</main>
2019-02-04 22:26:45 +00:00
</body>
</html>