<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>

<meta http-equiv="Content-Type" content="text/html;charset=US-ASCII">

<style type="text/css">

body { color: #000000; background-color: #FFFFFF; }
del { text-decoration: line-through; color: #8B0040; }
ins { text-decoration: underline; color: #005100; }

p.example { margin-left: 2em; }
pre.example { margin-left: 2em; }
div.example { margin-left: 2em; }

code.extract { background-color: #F5F6A2; }
pre.extract { margin-left: 2em; background-color: #F5F6A2;
  border: 1px solid #E1E28E; }

p.function { }
.attribute { margin-left: 2em; }
.attribute dt { float: left; font-style: italic;
  padding-right: 1ex; }
.attribute dd { margin-left: 0em; }

blockquote.std { color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.stddel { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5empadding-right: 0.5em; ; }

blockquote.stdins { text-decoration: underline;
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3; padding: 0.5em; }

table { border: 1px solid black; border-spacing: 0px;
  margin-left: auto; margin-right: auto; }
th { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }

</style>

<title>C++ Dynamic Arrays</title>
</head>
<body>
<h1>C++ Dynamic Arrays</h1>

<p>
ISO/IEC JTC1 SC22 WG21 N3662 - 2013-04-19
</p>

<address>
Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org
<br>
Matt Austern, austern@google.com
</address>

<p>
<a href="#Problem">Problem</a><br>
<a href="#Solution">Solution</a><br>
<a href="#Builtin">Builtin Arrays of Runtime Bound</a><br>
<a href="#Proposal">Proposal</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#containers">Chapter 23 Containers library [containers]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#sequence.reqmts">23.2.3 Sequence containers [sequence.reqmts]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#sequences">23.3 Sequence containers [sequences]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dynarray">23.3.8 Class template <code>dynarray</code> [dynarray]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dynarray.overview">23.3.8.1 Class template <code>dynarray</code> overview [dynarray.overview]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dynarray.cons">23.2.8.2 <code>dynarray</code> constructor and destructor [dynarray.cons]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dynarray.data">23.2.8.3 <code>dynarray::data</code> [dynarray.data]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dynarray.mutate">23.2.8.4 Mutating operations [dynarray.mutate]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dynarray.zero">23.2.8.5 Zero sized dynarrays [dynarray.zero]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dynarray.traits">23.3.8.6 Traits [dynarray.traits]</a><br>
<a href="#Revision">Revision History</a><br>
</p>

<h2><a name="Problem">Problem</a></h2>


<p>
Programs can become more efficient
when they can bind aspects of their execution
earlier in program development.
As an example,
the <code>std::unordered_map</code> container
provides more functionality than <code>std::vector</code>,
but <code>std::vector</code>
provides better performance
when the programmer can bind indexes
to a dense, but extensible, range near zero.
Going further, built-in arrays
provide even better performance
by binding the range end at compilation time.
</p>

<p>
Unfortunately, for some applications,
the range end is known at container construction
but not at compilation time.
So, built-in arrays are not applicable.
On the other hand,
<code>std::vector</code> is more general than needed,
as it permits an extensibility that is not required.
Ideally, we would like to be able to specify a container
where the index end is bound at construction,
but does not change thereafter.
</p>

<p>
The C programming language has such a container
in the form of <dfn>variable-length arrays</dfn>.
They are not general in that
they are limited to automatic variables,
but given that restriction
they are nearly as efficient as normal arrays,
requiring only mark/release stack allocation
and maintenance of a frame pointer.
(Maintaining a frame pointer is a good idea anyway.)
Unfortunately the detailed type semantics of C variable-length arrays
are probably not acceptable to C++,
so we cannot simply adopt them.
</p>

<p>
The <code>std::valarray</code> container is intermediate
between built-in arrays and <code>std::vector</code>,
but as it supports a <code>resize</code> method,
it cannot hold its size fixed for the lifetime of the variable.
Furthermore, <code>std::valarray</code>
supports compound member assignment operators
that imply such operators in the parameter type.
Such implications are workable only for types with "full interfaces",
not for general types.
</p>


<h2><a name="Solution">Solution</a></h2>

<p>
Instead of adopting C variable-length arrays,
we propose to define a new facility for arrays
where the number of elements is bound at construction.
We call these dynamic arrays, <code>dynarray</code>.
In keeping with C++ practice,
we wish to make <code>dynarray</code>s
usable with more than just automatic variables.
But to take advantage of the efficiency stack allocation,
we wish to make <code>dynarray</code> optimizable
when used as an automatic variable.
</p>

<p>
Therefore, we propose to define <code>dynarray</code>
so that compilers can recognize and implement
construction and destruction directly,
without appeal to any particular standard library implementation.
However, to minimize the necessary burden on compilers,
we propose that <code>dynarray</code> can be implemented as a pure library,
although with lost optimization opportunity.
</p>

<p>
We believe that the compilers can introduce the optimization
without impact on source or binary compatiblity.
There may be some change in code profiles and operator new calls
as a result of that optimization,
but such risks are common to compiler and library upgrades.
</p>

<p>
Syntactically, our proposal follows the lead of
<code>std::array</code> and <code>std::vector</code> containers.
Semantically, our proposal follows the lead of built-in arrays.
That is,
we do not require more out of <code>std::dynarray</code> element types
than we do of standard array element types.
</p>

<p>
The <code>dynarray</code> constructor has a parameter
indicating the number of elements in the container.
<code>Dynarray</code> requires an element type with a default constructor,
just as the built-in array requires.
Note that <code>dynarray</code> does not provide a default constructor,
because there is no reasonable default size,
and hence the <code>dynarray</code>
may not take a <code>dynarray</code> as an element.
</p>

<p>
<code>Dynarray</code> provides a copy constructor,
but use of the copy constructor requires that the element type
also have a copy constructor.
The presence of this constructor implies that
users cannot explicitly instantiate the <code>dynarray</code> template class
on a type that does not have a copy constructor.
This practice already exists in the standard library.
</p>

<p>
<code>Dynarray</code> provides random access iterators,
likely implemented as pointers.
The elements must be contiguously allocated,
to enable access via pointer arithmetic.
</p>

<p>
<code>Dynarray</code> also provides reverse iterators,
but these definitions imply that
the compiler implementation depends on the standard library implementation,
which is the reverse of the normal dependence.
</p>

<p>
<code>Dynarray</code> does not provide
any mechanism for determining whether heap or stack allocation was used.
</p>

<p>
<code>Dynarray</code> does not provide
a constructor from first and last forward iterators.
Such a constructor is possible, though,
as one can determine the size with <code>std::distance(first,last)</code>.
The technical consideration is that
determining the distance is only constant time for random access iterators.
</p>


<h2><a name="Builtin">Builtin Arrays of Runtime Bound</a></h2>

<p>
In <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3467.html">
N3497 Runtime-sized arrays with automatic storage duration</a>,
Jens Maurer proposes arrays with runtime bound.
These arrays are to <code>std::dynarray</code>
as normal fixed-size arrays are to <code>std::array</code>.
</p>

<p>
There are several similarities and differences.
</p>

<ul>

<li><p>
Both proposals permit allocation on the stack or on the heap,
at the discression of the implementation.
</p></li>

<li><p>
Arrays of runtime bound
can be used only for variables of automatic storage duration.
In contrast, <code>dynarray</code> can be used anywhere.
However, when <code>dynarray</code> is not used in a automatic variable,
use of the heap is necessarily required.
</p></li>

<li><p>
The types of arrays of runtime bound
are not inspectable (e.g. with <code>decltype</code>).
In contrast, <code>dynarray</code> has a normal template class type,
but the bound is not part of that type.
</p></li>

<li>
<p>
Arrays of runtime bound
throw <code>std::bad_array_length</code>
when the array size is unacceptable.
As currently defined,
<code>dynarray</code> also throws throw <code>std::bad_array_length</code>,
under the assumption that
migration between the two is easier if they throw the same exception.
The other choice would be to throw <code>std::length_error</code>
as does <code>std::vector::reserve()</code>.
Note, however, that the <code>std::vector::vector(size_type n)</code>
does not specify an exception.
The <code>operator new[]</code>
throws <code>std::bad_array_new_length</code>.
</p>

<p>
Because it may allocate memory,
<code>dynarray</code> may also throw <code>std::bad_alloc</code>.
</p>
</li>

<li><p>
Arrays of runtime bounds do no allow zero size;
<code>dynarray</code> does.
</p></li>

<li><p>
One can use initializer lists with arrays of runtime bound.
They are not currently supported with <code>dynarray</code>.
If <code>dynarray</code> were to support initializer lists,
it would derive its size from the initializer list.
This approach prevents a specification inconsistency
that is possible in arrays of runtime bound.
</p></li>

</ul>


<h2><a name="Proposal">Proposal</a></h2>

<p>
The <code>dynarray</code> container definition is as follows.
The section, paragraph, and table references
are based on those of
<cite>
<a href="../2012/n3485.pdf">
N3485</a> Working Draft, Standard for Programming Language C++</cite>,
Stefanus Du Toit, November 2012.
</p>


<h3><a name="containers">Chapter 23 Containers library [containers]</a></h3>

<p>
Add <code>&lt;dynarray&gt;</code> to table 87:
</p>

<blockquote class="std">
<table>
<caption>Table 87: Containers library summary</caption>
<tbody>
<tr><th>Subclause</th><th>Header(s)</th></tr>
<tr><td valign=top>23.2 Requirements</td>
<td></td></tr>
<tr><td valign=top>23.3 Sequence containers</td>
<td><code>&lt;array&gt;<br>
&lt;deque&gt;<br>
<ins>&lt;dynarray&gt;</ins><br>
&lt;forward_list&gt;<br>
&lt;list&gt;<br>
&lt;vector&gt;</code></td></tr>
<tr><td valign=top>23.4 Associative containers</td>
<td><code>&lt;map&gt;<br>
&lt;set&gt;</code></td></tr>
<tr><td valign=top>23.5 Unordered associative containers</td>
<td><code>&lt;unordered_map&gt;<br>
&lt;unordered_set&gt;</code></td></tr>
<tr><td valign=top>23.6 Container adaptors</td>
<td>&lt;queue&gt;<br>
&lt;stack&gt;</td></tr>
</tbody>
</table>
</blockquote>


<h3><a name="sequence.reqmts">23.2.3 Sequence containers [sequence.reqmts]</a></h3>

<p>
In table 101, Optional sequence container operations,
add <code>dynarray</code> to the list of containers
for operations
<code>front</code>,
<code>back</code>,
<code>a[n]</code>, and
<code>at(n)</code>.
</p>


<h3><a name="sequences">23.3 Sequence containers [sequences]</a></h3>

<p>
Add a new synopsis:
</p>

<blockquote class="stdins">
<p>
<b>Header <code>&lt;dynarray&gt;</code> synopsis</b>
</p>

<pre><code>

#include &lt;initializer_list&gt;

namespace std {

template&lt; class T &gt;
class dynarray;

template &lt;class Type, class Alloc>
struct uses_allocator&lt;dynarray&lt;Type&gt;, Alloc&gt;;

template &lt;class T, class Allocator&gt;
  bool operator==(const dynarray&lt;T&gt;&amp; x, const dynarray&lt;T&gt;&amp; y);
template &lt;class T, class Allocator&gt;
  bool operator&lt; (const dynarray&lt;T&gt;&amp; x, const dynarray&lt;T&gt;&amp; y);
template &lt;class T, class Allocator&gt;
  bool operator!=(const dynarray&lt;T&gt;&amp; x, const dynarray&lt;T&gt;&amp; y);
template &lt;class T, class Allocator&gt;
  bool operator&gt; (const dynarray&lt;T&gt;&amp; x, const dynarray&lt;T&gt;&amp; y);
template &lt;class T, class Allocator&gt;
  bool operator&gt;=(const dynarray&lt;T&gt;&amp; x, const dynarray&lt;T&gt;&amp; y);
template &lt;class T, class Allocator&gt;
  bool operator&lt;=(const dynarray&lt;T&gt;&amp; x, const dynarray&lt;T&gt;&amp; y);

} // namespace std
</code></pre>
</blockquote>


<h3><a name="dynarray">23.3.8 Class template <code>dynarray</code> [dynarray]</a></h3>

<p>
Add a new section.
</p>


<h3><a name="dynarray.overview">23.3.8.1 Class template <code>dynarray</code> overview [dynarray.overview]</a></h3>

<p>
Add a new section:
</p>

<blockquote class="stdins">

<p>
The header <code>&lt;dynarray&gt;</code>
defines a class template for storing sequences of objects
where the size is fixed at construction.
A <code>dynarray</code> supports random access iterators.
An instance of <code>dynarray&lt;T&gt;</code>
stores elements of type <code>T</code>.
The elements of a <code>dynarray</code> are stored contiguously,
meaning that if <code>d</code> is an <code>dynarray&lt;T&gt;</code>
then it obeys the identity
<code>&amp;d[n] == &amp;d[0] + n</code> for all <code>0 &lt;= n &lt; d.size()</code>.
</p>

<p>
Unless otherwise specified,
all dynarray operations have the same requirements and semantics
as specified in 23.2.
</p>

<p>
All operations except construction, destruction, and <code>fill</code>
shall have constant-time complexity.
</p>

<pre><code>
namespace std {
template&lt; typename T &gt;
class dynarray
{
    // types:
    typedef       T                               value_type;
    typedef       T&amp;                              reference;
    typedef const T&amp;                              const_reference;
    typedef       T*                              pointer;
    typedef const T*                              const_pointer;
    typedef       <var>implementation-defined</var>          iterator;
    typedef       <var>implementation-defined</var>          const_iterator;
    typedef reverse_iterator&lt;iterator&gt;            reverse_iterator;
    typedef reverse_iterator&lt;const_iterator&gt;      const_reverse_iterator;
    typedef size_t                size_type;
    typedef ptrdiff_t                difference_type;

public:
    // construct/copy/destroy:
    explicit dynarray(size_type <var>c</var>);
    template&lt; typename Alloc &gt;
      dynarray(size_type <var>c</var>, const Alloc&amp; alloc);
    dynarray(size_type <var>c</var>, const T&amp; <var>v</var>);
    template&lt; typename Alloc &gt;
      dynarray(size_type <var>c</var>, const T&amp; <var>v</var>, const Alloc&amp; alloc);
    dynarray(const dynarray&amp; <var>d</var>);
    template&lt; typename Alloc &gt;
      dynarray(const dynarray&amp; <var>d</var>, const Alloc&amp; alloc);
    dynarray(initializer_list&lt;T&gt;);
    template&lt; typename Alloc &gt;
      dynarray(initializer_list&lt;T&gt;, const Alloc&amp; alloc);
    dynarray&amp; operator=(const dynarray&amp;) = delete;
    ~dynarray();

    // iterators:
    iterator       begin()        noexcept;
    const_iterator begin()  const noexcept;
    const_iterator cbegin() const noexcept;
    iterator       end()          noexcept;
    const_iterator end()    const noexcept;
    const_iterator cend()   const noexcept;

    reverse_iterator       rbegin()        noexcept;
    const_reverse_iterator rbegin()  const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    reverse_iterator       rend()          noexcept;
    const_reverse_iterator rend()    const noexcept;
    const_reverse_iterator crend()   const noexcept;

    // capacity:
    size_type size()     const noexcept;
    size_type max_size() const noexcept;
    bool      empty()    const noexcept;

    // element access:
    reference       operator[](size_type <var>n</var>);
    const_reference operator[](size_type <var>n</var>) const;

    reference       front();
    const_reference front() const;
    reference       back();
    const_reference back()  const;

    const_reference at(size_type <var>n</var>) const;
    reference       at(size_type <var>n</var>);

    // data access:
    T*       data()       noexcept;
    const T* data() const noexcept;

    // mutating member functions:
    void fill(const T&amp; <var>v</var>);
};

} // namespace std
</code></pre>

</blockquote>


<h3><a name="dynarray.cons">23.2.8.2 <code>dynarray</code> constructor and destructor [dynarray.cons]</a></h3>

<p>
Add a new section:
</p>

<blockquote class="stdins">

<p>
<code>dynarray(size_type <var>c</var>);</code>
</p>

<blockquote>
<p>
<i>Effects:</i>
Allocates storage for <var>c</var> elements.
May or may not invoke the global <code>operator new</code>.
The <tt>c</tt> elements of the dynarray are default-initialized (8.5).
</p>

<p>
<i>Throws:</i>
<code>std::bad_array_length</code>
when the size requested is larger than implementable.
<code>std::bad_alloc</code>
when there is insufficient memory.
</p>

</blockquote>
<p>
<code>dynarray(size_type <var>c</var>, const T& <var>v</var>);</code>
</p>

<blockquote>

<p>
<i>Requires:</i>
<code>T</code> shall meet the CopyConstructible requirements.
</p>

<p>
<i>Effects:</i>
Allocates storage for <code><var>c</var></code> elements.
May or may not invoke the global <code>operator new</code>.
The <code><var>c</var></code> elements of the
dynarray are direct-initialized (8.5) with argument <code><var>v</var></code>.
</p>

<p>
<i>Throws:</i>
<code>std::bad_array_length</code>
when the size requested is larger than implementable.
<code>std::bad_alloc</code>
when there is insufficient memory.
</p>
</blockquote>

<p>
<code>dynarray(const dynarray&amp; <var>d</var>);</code>
</p>

<blockquote>

<p>
<i>Requires:</i>
<code>T</code> shall meet the CopyConstructible requirements.
</p>

<p>
<i>Throws:</i>
<code>std::bad_alloc</code>
when there is insufficient memory.
</p>
</blockquote>

<blockquote>
<p>
<i>Effects:</i>
Allocates storage for <code><var>d</var>.size()</code> elements.
The <code><var>d</var>.size()</code> elements of the dynarray
are direct-initialized (8.5)
with the corresponding elements of <code><var>d</var></code>.
May or may not invoke the global <code>operator new</code>.
</p>
</blockquote>


<pre>
<code>template&lt; typename Alloc &gt;
  dynarray(size_type <var>c</var>, const Alloc&amp; alloc);
template&lt; typename Alloc &gt;
  dynarray(size_type <var>c</var>, const T&amp; <var>v</var>, const Alloc&amp; alloc);
template&lt; typename Alloc &gt;
  dynarray(const dynarray&amp; <var>d</var>, const Alloc&amp; alloc);</code>
template&lt; typename Alloc &gt;
  dynarray(initializer_list&lt;T&gt;, const Alloc&amp; alloc);
</pre>

<blockquote>
<p>
<i>Requires:</i>
<code>Alloc</code> shall meet the requirements for an Allocator (17.6.3.5).
</p>

<p>
<i>Effects:</i>
Equivalent to the preceding constructors
except that each element is constructed
with uses-allocator construction (20.6.7.2).
</p>
</blockquote>

<p>
<code>~dynarray();</code>
</p>

<blockquote>
<p>
<i>Effects:</i>
Invokes the global <code>operator delete</code>
if and only if
the constructor invoked the global <code>operator new</code>.
</p>
</blockquote>

</blockquote>


<h3><a name="dynarray.data">23.2.8.3 <code>dynarray::data</code> [dynarray.data]</a></h3>

<p>
Add a new section:
</p>

<blockquote class="stdins">

<p>
<code>T* data() noexcept;</code><br>
<code>const T* data() const noexcept;</code>
</p>

<blockquote>
<p>
<i>Returns:</i>
A pointer to the contiguous storage containing the elements.
</p>
</blockquote>

</blockquote>


<h3><a name="dynarray.mutate">23.2.8.4 Mutating operations [dynarray.mutate]</a></h3>

<p>
Add a new section:
</p>

<blockquote class="stdins">

<p>
<code>void fill(const T&amp; <var>v</var>);</code>
</p>

<blockquote>
<p>
<i>Effects:</i>
<code>fill_n(begin(), size(), <var>v</var>);</code>
</p>
</blockquote>

</blockquote>


<h3><a name="dynarray.zero">23.2.8.5 Zero sized dynarrays [dynarray.zero]</a></h3>

<p>
Add a new section:
</p>

<blockquote class="stdins">

<p>
<code>dynarray</code> shall provide support
for the special case of construction with a size of zero.
In the case that the size is zero,
<code>begin() == end() ==</code> unique value.
The return value of <code>data()</code> is unspecified.
The effect of calling <code>front()</code> or <code>back()</code>
for a zero-sized <code>dynarray</code> is undefined.
</p>

</blockquote>

<h3><a name="dynarray.traits">23.3.8.6 Traits [dynarray.traits]</a></h3>

<p>
Add a new section.
</p>

<blockquote class="stdins">

<ul>
<dt><pre>
<code>template &lt;class Type, class Alloc>
  struct uses_allocator&lt;dynarray&lt;Type&gt;, Alloc&gt; : true_type { };</code></dt>
<dd>
<p>
<b>Requires:</b>
<code>Alloc</code> shall be an Allocator (17.6.3.5).
[<i>Note:</i>
Specialization of this trait informs other library components that
dynarray can be constructed with an allocator,
even though it does not have a nested <code>allocator_type</code>.
&mdash;<i>end note</i>]
</p>
</dd>
</blockquote>


<h2><a name="Revision">Revision History</a></h2>

<p>
This paper revises N3532 - 2013-03-12 as follows.
</p>

<ul>

<li><p>
Add types <code>pointer</code> and <code>const_pointer</code>.
</p></li>

<li><p>
Add a constructor with a value fill argument.
</p></li>

<li><p>
Add an initializer_list constructor.
</p></li>

<li><p>
Add constructors that apply allocators to element construction.
</p></li>

<li><p>
Add effect of allocating memory on constructors.
</p></li>

<li><p>
Add missing reference on deleted assignment return type.
</p></li>

<li><p>
Add <code>crbegin</code> and <code>crend</code>.
</p></li>

<li><p>
Add the <code>fill</code> member function.
</p></li>

<li><p>
Add the <code>swap</code> member function.
</p></li>

<li><p>
Add comparison functions.
</p></li>

<li><p>
Mark appropriate functions as <code>noexcept</code>.
</p></li>

<li><p>
Make various editorial corrections.
</p></li>

<li><p>
Remove the non-normative example implementation.
</p></li>

<li><p>
Remove the Presentation section.
</p></li>

<li><p>
Remove the Example section.
</p></li>


</ul>

<p>
N3532 revised N2648 = 08-0158 - 2008-05-16 as follows.
</p>

<ul>

<li><p>
Add a section discussing of the relationship to
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3497.html">
N3497 Runtime-sized arrays with automatic storage duration</a>.
</p></li>

<li><p>
Support zero-length <code>dynarray</code>s.
Add corresponding specifications
for <code>max_size</code> and <code>empty</code>.
Update implementation.
</p></li>

<li><p>
Throw <code>std::bad_array_length</code>
when the size parameter is too large.
Throw <code>std::bad_alloc</code>
when memory allocation fails.
</p></li>

<li><p>
Clarify that there is no support for determing
whether heap or stack allocation was used.
</p></li>

<li><p>
Note that a constructor from <code>initializer_list</code>
is not provided but technically feasible.
</p></li>

<li><p>
Note that a constructor from two forward iterators
is not provided but technically feasible.
</p></li>

<li><p>
Update the form of the HTML,
which affects the script for extracting the code.
</p></li>

<li><p>
Add a 'Revision History' section.
</p></li>

</ul>


</body>
</html>
