/*
 * E57Format.cpp - implementation of public functions of the E57 format
 *   Reference Implementation.
 *
 * Original work Copyright 2009 - 2010 Kevin Ackley (kackley@gwi.net)
 * Modified work Copyright 2018 - 2020 Andy Maloney <asmaloney@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

//! @file E57Format.cpp

#include "BlobNodeImpl.h"
#include "CompressedVectorNodeImpl.h"
#include "CompressedVectorReaderImpl.h"
#include "CompressedVectorWriterImpl.h"
#include "FloatNodeImpl.h"
#include "ImageFileImpl.h"
#include "IntegerNodeImpl.h"
#include "ScaledIntegerNodeImpl.h"
#include "SourceDestBufferImpl.h"
#include "StringNodeImpl.h"
#include "VectorNodeImpl.h"

using namespace e57;

/*!
@brief Check whether Node class invariant is true
@param   [in] doRecurse     If true, also check invariants of all children or
sub-objects recursively.
@param   [in] doDowncast    If true, also check any invariants of the actual
derived type in addition to the generic node invariants.
@details
This function checks at least the assertions in the documented class invariant
description (see class reference page for this object). Other internal
invariants that are implementation-dependent may also be checked. If any
invariant clause is violated, an E57Exception with errorCode of
E57_ERROR_INVARIANCE_VIOLATION is thrown.

Specifying doRecurse=true only makes sense if doDowncast=true is also specified
(the generic Node has no way to access any children). Checking the invariant
recursively may be expensive if the tree is large, so should be used
judiciously, in debug versions of the application.
@post    No visible state is modified.
@throw   ::E57_ERROR_INVARIANCE_VIOLATION or any other E57 ErrorCode
@see     Class Invariant section in Node,
IntegerNode::checkInvariant, ScaledIntegerNode::checkInvariant,
FloatNode::checkInvariant, BlobNode::checkInvariant,
StructureNode::checkInvariant, VectorNode::checkInvariant,
CompressedVectorNode::checkInvariant
*/
void Node::checkInvariant( bool doRecurse, bool doDowncast )
{
   ImageFile imf = destImageFile();

   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !imf.isOpen() )
   {
      return;
   }

   // Parent attachment state is same as this attachment state
   if ( isAttached() != parent().isAttached() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Parent destination ImageFile is same as this
   if ( imf != parent().destImageFile() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // If this is the ImageFile root node
   if ( *this == imf.root() )
   {
      // Must be attached
      if ( !isAttached() )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }

      // Must be is a root node
      if ( !isRoot() )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
   }

   // If this is a root node
   if ( isRoot() )
   {
      // Absolute pathName is "/"
      if ( pathName() != "/" )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }

      // parent() returns this node
      if ( *this != parent() )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
   }
   else
   {
      // Non-root can't be own parent
      if ( *this == parent() )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }

      // pathName is concatenation of parent pathName and this elementName
      if ( parent().isRoot() )
      {
         if ( pathName() != "/" + elementName() )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }
      }
      else
      {
         if ( pathName() != parent().pathName() + "/" + elementName() )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }
      }

      // Non-root nodes must be children of either a VectorNode or StructureNode
      if ( parent().type() == E57_VECTOR )
      {
         VectorNode v = static_cast<VectorNode>( parent() );

         // Must be defined in parent VectorNode with this elementName
         if ( !v.isDefined( elementName() ) )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }

         // Getting child of parent with this elementName must return this
         if ( v.get( elementName() ) != *this )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }
      }
      else if ( parent().type() == E57_STRUCTURE )
      {
         StructureNode s = static_cast<StructureNode>( parent() );

         // Must be defined in parent VectorNode with this elementName
         if ( !s.isDefined( elementName() ) )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }

         // Getting child of parent with this elementName must return this
         if ( s.get( elementName() ) != *this )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }
      }
      else
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
   }

   // If this is attached
   if ( isAttached() )
   {
      // Get root of this
      Node n = *this;
      while ( !n.isRoot() )
      {
         n = n.parent();
      }

      // If in tree of ImageFile (could be in a prototype instead)
      if ( n == imf.root() )
      {
         // pathName must be defined
         if ( !imf.root().isDefined( pathName() ) )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }

         // Getting by absolute pathName must be this
         if ( imf.root().get( pathName() ) != *this )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }
      }
   }

   // If requested, check invariants of derived types:
   if ( doDowncast )
   {
      switch ( type() )
      {
         case E57_STRUCTURE:
         {
            StructureNode s( *this );
            s.checkInvariant( doRecurse, false );
         }
         break;
         case E57_VECTOR:
         {
            VectorNode v( *this );
            v.checkInvariant( doRecurse, false );
         }
         break;
         case E57_COMPRESSED_VECTOR:
         {
            CompressedVectorNode cv( *this );
            cv.checkInvariant( doRecurse, false );
         }
         break;
         case E57_INTEGER:
         {
            IntegerNode i( *this );
            i.checkInvariant( doRecurse, false );
         }
         break;
         case E57_SCALED_INTEGER:
         {
            ScaledIntegerNode si( *this );
            si.checkInvariant( doRecurse, false );
         }
         break;
         case E57_FLOAT:
         {
            FloatNode f( *this );
            f.checkInvariant( doRecurse, false );
         }
         break;
         case E57_STRING:
         {
            StringNode s( *this );
            s.checkInvariant( doRecurse, false );
         }
         break;
         case E57_BLOB:
         {
            BlobNode b( *this );
            b.checkInvariant( doRecurse, false );
         }
         break;
         default:
            break;
      }
   }
}

/*!
@class Node
@brief   Generic handle to any of the 8 types of E57 element objects.
@details
A Node is a generic handle to an underlying object that is any of the eight type
of E57 element objects. Each of the eight node types support the all the
functions of the Node class. A Node is a vertex in a tree (acyclic graph), which
is a hierarchical organization of nodes. At the top of the hierarchy is a single
root Node. If a Node is a container type (StructureNode, VectorNode,
CompressedVectorNode) it may have child nodes. The following are non-container
type nodes (also known as terminal nodes): IntegerNode, ScaledIntegerNode,
FloatNode, StringNode, BlobNode. Terminal nodes store various types of values
and cannot have children. Each Node has an elementName, which is a string that
uniquely identifies it within the children of its parent. Children of a
StructureNode have elementNames that are explicitly given by the API user.
Children of a VectorNode or CompressedVectorNode have element names that are
string reorientations of the Node's positional index, starting at "0". A path
name is a sequence elementNames (divided by "/") that must be traversed to get
from a Node to one of its descendents.

Data is organized in an E57 format file (an ImageFile) hierarchically.
Each ImageFile has a predefined root node that other nodes can be attached to as
children (either directly or indirectly). A Node can exist temporarily without
being attached to an ImageFile, however the state will not be saved in the
associated file, and the state will be lost if the program exits.

A handle to a generic Node may be safely be converted to and from a handle to
the Node's true underlying type. Since an attempt to convert a generic Node to a
incorrect handle type will fail with an exception, the true type should be
interrogated beforehand.

Due to the set-once design of the Foundation API, terminal nodes are immutable
(i.e. their values and attributes can't change after creation). Once a
parent-child relationship has been established, it cannot be changed.

Only generic operations are available for a Node, to access more specific
operations (e.g. StructureNode::childCount) the generic handle must be converted
to the node type of the underlying object. This conversion is done in a
type-safe way using "downcasting" (see discussion below).

@section node_Downcasting Downcasting
The conversion from a general handle type to a specific handle type is called
"downcasting". Each of the 8 specific node types have a downcast function (see
IntegerNode::IntegerNode(const Node&) for example). If a downcast is requested
to an incorrect type (e.g. taking a Node handle that is actually a FloatNode and
trying to downcast it to a IntegerNode), an E57Exception is thrown with an
ErrorCode of E57_ERROR_BAD_NODE_DOWNCAST. Depending on the program design,
throwing a bad downcast exception might be acceptable, if an element must be a
specific type and no recovery is possible. If a standard requires an element be
one several types, then Node::type() should be used to interrogate the type in
an @c if or @c switch statement. Downcasting is "dangerous" (can fail with an
exception) so the API requires the programmer to explicitly call the downcast
functions rather than have the c++ compiler insert them automatically.

@section node_Upcasting Upcasting
The conversion of a specific node handle (e.g. IntegerNode) to a general Node
handle is called "upcasting". Each of the 8 specific node types have an upcast
function (see IntegerNode::operator Node() for example). Upcasting is "safe"
(can't cause an exception) so the API allows the c++ compiler to insert them
automatically. Upcasting is useful if you have a specific node handle and want
to call a function that takes a generic Node handle argument. In this case, the
function can be called with the specific handle and the compiler will
automatically insert the upcast conversion. This implicit conversion allows one
function, with an argument of type Node, to handle operations that apply to all
8 types of nodes (e.g. StructureNode::set()).

@section node_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Foundation.cpp
@skip begin Node::checkInvariant
@skip checkInvariant(
@until end Node::checkInvariant

@see StructureNode, VectorNode, CompressedVectorNode, IntegerNode,
ScaledIntegerNode, FloatNode, StringNode, BlobNode
*/

//! @brief Check whether StructureNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void StructureNode::checkInvariant( bool doRecurse, bool doUpcast )
{
   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !destImageFile().isOpen() )
   {
      return;
   }

   // If requested, call Node::checkInvariant
   if ( doUpcast )
   {
      static_cast<Node>( *this ).checkInvariant( false, false );
   }

   // Check each child
   for ( int64_t i = 0; i < childCount(); i++ )
   {
      Node child = get( i );

      // If requested, check children recursively
      if ( doRecurse )
      {
         child.checkInvariant( doRecurse, true );
      }

      // Child's parent must be this
      if ( static_cast<Node>( *this ) != child.parent() )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }

      // Child's elementName must be defined
      if ( !isDefined( child.elementName() ) )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }

      // Getting child by element name must yield same child
      Node n = get( child.elementName() );
      if ( n != child )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
   }
}

//! @brief Check whether VectorNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void VectorNode::checkInvariant( bool doRecurse, bool doUpcast )
{
   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !destImageFile().isOpen() )
   {
      return;
   }

   // If requested, call Node::checkInvariant
   if ( doUpcast )
   {
      static_cast<Node>( *this ).checkInvariant( false, false );
   }

   // Check each child
   for ( int64_t i = 0; i < childCount(); i++ )
   {
      Node child = get( i );

      // If requested, check children recursively
      if ( doRecurse )
      {
         child.checkInvariant( doRecurse, true );
      }

      // Child's parent must be this
      if ( static_cast<Node>( *this ) != child.parent() )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }

      // Child's elementName must be defined
      if ( !isDefined( child.elementName() ) )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }

      // Getting child by element name must yield same child
      Node n = get( child.elementName() );
      if ( n != child )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
   }
}

//! @brief Check whether CompressedVectorNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void CompressedVectorNode::checkInvariant( bool doRecurse, bool doUpcast )
{
   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !destImageFile().isOpen() )
   {
      return;
   }

   // If requested, call Node::checkInvariant
   if ( doUpcast )
   {
      static_cast<Node>( *this ).checkInvariant( false, false );
   }

   // Check prototype is good Node
   prototype().checkInvariant( doRecurse );

   // prototype attached state not same as this attached state
   if ( prototype().isAttached() != isAttached() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // prototype not root
   if ( !prototype().isRoot() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // prototype dest ImageFile not same as this dest ImageFile
   if ( prototype().destImageFile() != destImageFile() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Check codecs is good Node
   codecs().checkInvariant( doRecurse );

   // codecs attached state not same as this attached state
   if ( codecs().isAttached() != isAttached() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // codecs not root
   if ( !codecs().isRoot() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // codecs dest ImageFile not same as this dest ImageFile
   if ( codecs().destImageFile() != destImageFile() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
}
/*!
@brief Check whether IntegerNode class invariant is true
@param   [in] doRecurse   If true, also check invariants of all children or
sub-objects recursively.
@param   [in] doUpcast    If true, also check invariants of the generic Node
class.
@details
This function checks at least the assertions in the documented class invariant
description (see class reference page for this object). Other internal
invariants that are implementation-dependent may also be checked. If any
invariant clause is violated, an E57Exception with errorCode of
E57_ERROR_INVARIANCE_VIOLATION is thrown.

Checking the invariant recursively may be expensive if the tree is large, so
should be used judiciously, in debug versions of the application.
@post    No visible state is modified.
@throw   ::E57_ERROR_INVARIANCE_VIOLATION or any other E57 ErrorCode
*/
void IntegerNode::checkInvariant( bool /*doRecurse*/, bool doUpcast )
{
   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !destImageFile().isOpen() )
   {
      return;
   }

   // If requested, call Node::checkInvariant
   if ( doUpcast )
   {
      static_cast<Node>( *this ).checkInvariant( false, false );
   }

   if ( value() < minimum() || value() > maximum() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
}

//! @brief Check whether ScaledIntegerNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void ScaledIntegerNode::checkInvariant( bool /*doRecurse*/, bool doUpcast )
{
   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !destImageFile().isOpen() )
   {
      return;
   }

   // If requested, call Node::checkInvariant
   if ( doUpcast )
   {
      static_cast<Node>( *this ).checkInvariant( false, false );
   }

   // If value is out of bounds
   if ( rawValue() < minimum() || rawValue() > maximum() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // If scale is zero
   if ( scale() == 0 )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // If scaled value is not calculated correctly
   if ( scaledValue() != rawValue() * scale() + offset() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
}
//! @brief Check whether FloatNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void FloatNode::checkInvariant( bool /*doRecurse*/, bool doUpcast )
{
   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !destImageFile().isOpen() )
   {
      return;
   }

   // If requested, call Node::checkInvariant
   if ( doUpcast )
   {
      static_cast<Node>( *this ).checkInvariant( false, false );
   }

   if ( precision() == E57_SINGLE )
   {
      if ( static_cast<float>( minimum() ) < E57_FLOAT_MIN || static_cast<float>( maximum() ) > E57_FLOAT_MAX )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
   }

   // If value is out of bounds
   if ( value() < minimum() || value() > maximum() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
}
//! @brief Check whether StringNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void StringNode::checkInvariant( bool /*doRecurse*/, bool doUpcast )
{
   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !destImageFile().isOpen() )
   {
      return;
   }

   // If requested, call Node::checkInvariant
   if ( doUpcast )
   {
      static_cast<Node>( *this ).checkInvariant( false, false );
   }
   /// ? check legal UTF-8
}
//! @brief Check whether BlobNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void BlobNode::checkInvariant( bool /*doRecurse*/, bool doUpcast )
{
   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !destImageFile().isOpen() )
   {
      return;
   }

   // If requested, call Node::checkInvariant
   if ( doUpcast )
   {
      static_cast<Node>( *this ).checkInvariant( false, false );
   }

   if ( byteCount() < 0 )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
}
/*!
@brief Check whether CompressedVectorReader class invariant is true
@param   [in] doRecurse   If true, also check invariants of all children or
sub-objects recursively.
@details
This function checks at least the assertions in the documented class invariant
description (see class reference page for this object). Other internal
invariants that are implementation-dependent may also be checked. If any
invariant clause is violated, an E57Exception with errorCode of
E57_ERROR_INVARIANCE_VIOLATION is thrown.
@post    No visible state is modified.
*/
void CompressedVectorReader::checkInvariant( bool /*doRecurse*/ )
{
   // If this CompressedVectorReader is not open, can't test invariant (almost
   // every call would throw)
   if ( !isOpen() )
   {
      return;
   }

   CompressedVectorNode cv = compressedVectorNode();
   ImageFile imf = cv.destImageFile();

   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !imf.isOpen() )
   {
      return;
   }

   // Associated CompressedVectorNode must be attached to ImageFile
   if ( !cv.isAttached() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Dest ImageFile must have at least 1 reader (this one)
   if ( imf.readerCount() < 1 )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Dest ImageFile can't have any writers
   if ( imf.writerCount() != 0 )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
}

//! @brief Check whether CompressedVectorWriter class invariant is true
//! @copydetails CompressedVectorReader::checkInvariant
void CompressedVectorWriter::checkInvariant( bool /*doRecurse*/ )
{
   // If this CompressedVectorWriter is not open, can't test invariant (almost
   // every call would throw)
   if ( !isOpen() )
   {
      return;
   }

   CompressedVectorNode cv = compressedVectorNode();
   ImageFile imf = cv.destImageFile();

   // If destImageFile not open, can't test invariant (almost every call would
   // throw)
   if ( !imf.isOpen() )
   {
      return;
   }

   // Associated CompressedVectorNode must be attached to ImageFile
   if ( !cv.isAttached() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Dest ImageFile must be writable
   if ( !imf.isWritable() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Dest ImageFile must have exactly 1 writer (this one)
   if ( imf.writerCount() != 1 )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Dest ImageFile can't have any readers
   if ( imf.readerCount() != 0 )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
}

/*!
@brief Check whether ImageFile class invariant is true
@param   [in] doRecurse   If true, also check invariants of all children or
sub-objects recursively.
@details
This function checks at least the assertions in the documented class invariant
description (see class reference page for this object). Other internal
invariants that are implementation-dependent may also be checked. If any
invariant clause is violated, an E57Exception with errorCode of
E57_ERROR_INVARIANCE_VIOLATION is thrown.

Checking the invariant recursively may be expensive if the tree is large, so
should be used judiciously, in debug versions of the application.
@post    No visible state is modified.
@throw   ::E57_ERROR_INVARIANCE_VIOLATION or any other E57 ErrorCode
@see     Node::checkInvariant
*/
void ImageFile::checkInvariant( bool doRecurse ) const
{
   // If this ImageFile is not open, can't test invariant (almost every call
   // would throw)
   if ( !isOpen() )
   {
      return;
   }

   // root() node must be a root node
   if ( !root().isRoot() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Can't have empty fileName
   if ( fileName().empty() )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   int wCount = writerCount();
   int rCount = readerCount();

   // Can't have negative number of readers
   if ( rCount < 0 )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Can't have negative number of writers
   if ( wCount < 0 )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // Can't have more than one writer
   if ( 1 < wCount )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }

   // If have writer
   if ( wCount > 0 )
   {
      // Must be in write-mode
      if ( !isWritable() )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }

      // Can't have any readers
      if ( rCount > 0 )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
   }

   // Extension prefixes and URIs are unique
   const size_t eCount = extensionsCount();
   for ( size_t i = 0; i < eCount; i++ )
   {
      for ( size_t j = i + 1; j < eCount; j++ )
      {
         if ( extensionsPrefix( i ) == extensionsPrefix( j ) )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }
         if ( extensionsUri( i ) == extensionsUri( j ) )
         {
            throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
         }
      }
   }

   // Verify lookup functions are correct
   for ( size_t i = 0; i < eCount; i++ )
   {
      ustring goodPrefix = extensionsPrefix( i );
      ustring goodUri = extensionsUri( i );
      ustring prefix;
      ustring uri;
      if ( !extensionsLookupPrefix( goodPrefix, uri ) )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
      if ( uri != goodUri )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
      if ( !extensionsLookupUri( goodUri, prefix ) )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
      if ( prefix != goodPrefix )
      {
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
      }
   }

   // If requested, check all objects "below" this one
   if ( doRecurse )
   {
      root().checkInvariant( doRecurse );
   }
}

//! @brief Check whether SourceDestBuffer class invariant is true
void SourceDestBuffer::checkInvariant( bool /*doRecurse*/ ) const
{
   // Stride must be >= a memory type dependent value
   size_t min_stride = 0;
   switch ( memoryRepresentation() )
   {
      case E57_INT8:
      case E57_UINT8:
      case E57_BOOL:
         min_stride = 1;
         break;
      case E57_INT16:
      case E57_UINT16:
         min_stride = 2;
         break;
      case E57_INT32:
      case E57_UINT32:
      case E57_REAL32:
         min_stride = 4;
         break;
      case E57_INT64:
      case E57_REAL64:
         min_stride = 8;
         break;
      case E57_USTRING:
         min_stride = sizeof( ustring );
         break;
      default:
         throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
   if ( stride() < min_stride )
   {
      throw E57_EXCEPTION1( E57_ERROR_INVARIANCE_VIOLATION );
   }
}

/*!
@brief   Return the NodeType of a generic Node.
@details This function allows the actual node type to be interrogated before
upcasting the handle to the actual node type (see Upcasting and Dowcasting
section in Node).
@return  The NodeType of a generic Node, which may be one of the following
NodeType enumeration values:
::E57_STRUCTURE, ::E57_VECTOR, ::E57_COMPRESSED_VECTOR, ::E57_INTEGER,
::E57_SCALED_INTEGER,
::E57_FLOAT, ::E57_STRING, ::E57_BLOB.
@post    No visible state is modified.
@see     NodeType, upcast/dowcast discussion in Node
*/
NodeType Node::type() const
{
   return impl_->type();
}

/*!
@brief   Is this a root node.
@details A root node has itself as a parent (it is not a child of any node).
Newly constructed nodes (before they are inserted into an ImageFile tree) start
out as root nodes. It is possible to temporarily create small trees that are
unattached to any ImageFile. In these temporary trees, the top-most node will be
a root node. After the tree is attached to the ImageFile tree, the only root
node will be the pre-created one of the ImageTree (the one returned by
ImageFile::root). The concept of @em attachment is slightly larger than that of
the parent-child relationship (see Node::isAttached and
CompressedVectorNode::CompressedVectorNode for more details).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  true if this node is a root node.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Node::parent, Node::isAttached, CompressedVectorNode::CompressedVectorNode
*/
bool Node::isRoot() const
{
   return impl_->isRoot();
}

/*!
@brief   Return parent of node, or self if a root node.
@details Nodes are organized into trees (acyclic graphs) with a distinguished
node (the "top-most" node) called the root node. A parent-child relationship is
established between nodes to form a tree. Nodes can have zero or one parent.
Nodes with zero parents are called root nodes.
In the API, if a node has zero parents it is represented by having itself as a
parent. Due to the set-once design of the API, a parent-child relationship
cannot be modified once established. A child node can be any of the 8 node
types, but a parent node can only be one of the 3 container node types
(E57_STRUCTURE, E57_VECTOR, and E57_COMPRESSED_VECTOR). Each parent-child link
has a string name (the elementName) associated with it (See Node::elementName
for more details). More than one tree can be formed at any given time. Typically
small trees are temporarily constructed before attachment to an ImageFile so
that they will be written to the disk.

@b Warning: user algorithms that use this function to walk the tree must take
care to handle the case where a node is its own parent (it is a root node). Use
Node::isRoot to avoid infinite loops or infinite recursion.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  A smart Node handle referencing the parent node or this node if is a
root node.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Node::isRoot, Node::isAttached, CompressedVectorNode::CompressedVectorNode, Node::elementName
*/
Node Node::parent() const
{
   return Node( impl_->parent() );
}

/*!
@brief   Get absolute pathname of node.
@details
Nodes are organized into trees (acyclic graphs) by a parent-child relationship
between nodes. Each parent-child relationship has an associated elementName
string that is unique for a given parent. Any node in a given tree can be
identified by a sequence of elementNames of how to get to the node from the root
of the tree. An absolute pathname string that is formed by arranging this
sequence of elementNames separated by the "/" character with a leading "/"
prepended.

Some example absolute pathNames: "/data3D/0/points/153/cartesianX",
"/data3D/0/points",
"/cameraImages/1/pose/rotation/w", and "/". These examples have probably been
attached to an ImageFile. Here is an example absolute pathName of a node in a
pose tree that has not yet been attached to an ImageFile: "/pose/rotation/w".

A technical aside: the elementName of a root node does not appear in absolute
pathnames, since the "path" is between the staring node (the root) and the
ending node. By convention, in this API, a root node has the empty string ("")
as its elementName.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The absolute path name of the node.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Node::elementName, Node::parent, Node::isRoot
*/
ustring Node::pathName() const
{
   return impl_->pathName();
}

/*!
@brief   Get element name of node.
@details
The elementName is a string associated with each parent-child link between
nodes. For a given parent, the elementName uniquely identifies each of its
children. Thus, any node in a tree can be identified by a sequence of
elementNames that form a path from the tree's root node (see Node::pathName for
more details).

Three types of nodes (the container node types) can be parents: StructureNode,
VectorNode, and CompressedVectorNode. The children of a StructureNode are
explicitly given unique elementNames when they are attached to the parent (using
StructureNode::set). The children of VectorNode and CompressedVectorNode are
implicitly given elementNames based on their position in the list (starting at
"0"). In a CompressedVectorNode, the elementName can become quite large:
"1000000000" or more. However in a CompressedVectorNode, the elementName string
is not stored in the file and is deduced by the position of the child.

@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The element name of the node, or "" if a root node.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Node::pathName, Node::parent, Node::isRoot
*/
ustring Node::elementName() const
{
   return impl_->elementName();
}

/*!
@brief   Get the ImageFile that was declared as the destination for the node
when it was created.
@details The first argument of the constructors of each of the 8 types of nodes
is an ImageFile that indicates which ImageFile the node will eventually be
attached to. This function returns that constructor argument. It is an error to
attempt to attach the node to a different ImageFile. However it is not an error
to not attach the node to any ImageFile (it's just wasteful). Use
Node::isAttached to check if the node actually did get attached.
@post    No visible object state is modified.
@return  The ImageFile that was declared as the destination for the node when it
was created.
@see     Node::isAttached,
StructureNode::StructureNode(), VectorNode::VectorNode(),
CompressedVectorNode::CompressedVectorNode(), IntegerNode::IntegerNode(),
ScaledIntegerNode::ScaledIntegerNode(), FloatNode::FloatNode(),
StringNode::StringNode(), BlobNode::BlobNode()
*/
ImageFile Node::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

/*!
@brief   Has node been attached into the tree of an ImageFile.
@details Nodes are attached into an ImageFile tree by inserting them as children
(directly or indirectly) of the ImageFile's root node. Nodes can also be
attached to an ImageFile if they are used in the @c codecs or @c prototype trees
of an CompressedVectorNode that is attached. Attached nodes will be saved to
disk when the ImageFile is closed, and restored when the ImageFile is read back
in from disk. Unattached nodes will not be saved to disk. It is not recommended
to create nodes that are not eventually attached to the ImageFile.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible object state is modified.
@return  @c true if node is child of (or in codecs or prototype of a child
CompressedVectorNode of) the root node of an ImageFile.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Node::destImageFile, ImageFile::root
*/
bool Node::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Diagnostic function to print internal state of object to output stream
in an indented format.
@param   [in] indent    Number of spaces to indent all the printed lines of this
object.
@param   [in] os        Output stream to print on.
@details
All objects in the E57 Foundation API (with exception of E57Exception) support a
dump() function. These functions print out to the console a detailed listing of
the internal state of objects. The content of these printouts is not documented,
and is really of interest only to implementation developers/maintainers or the
really adventurous users. In implementations of the API other than the Reference
Implementation, the dump() functions may produce no output (although the
functions should still be defined). The output format may change from version to
version.
@post    No visible object state is modified.
@throw   No E57Exceptions
*/
#ifdef E57_DEBUG
void Node::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void Node::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Test if two node handles refer to the same underlying node
@param   [in] n2        The node to compare this node with
@post    No visible object state is modified.
@return  @c true if node handles refer to the same underlying node.
@throw   No E57Exceptions
*/
bool Node::operator==( Node n2 ) const
{
   return ( impl_ == n2.impl_ );
}

/*!
@brief   Test if two node handles refer to different underlying nodes
@param   [in] n2        The node to compare this node with
@post    No visible object state is modified.
@return  @c true if node handles refer to different underlying nodes.
@throw   No E57Exceptions
*/
bool Node::operator!=( Node n2 ) const
{
   return ( impl_ != n2.impl_ );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
Node::Node( NodeImplSharedPtr ni ) : impl_( ni )
{
}
//! @endcond

//=====================================================================================
/*!
@class StructureNode
@brief   An E57 element containing named child nodes.
@details
A StructureNode is a container of named child nodes, which may be any of the
eight node types. The children of a structure node must have unique
elementNames. Once a child node is set with a particular elementName, it may not
be modified.

See Node class discussion for discussion of the common functions that
StructureNode supports.
@section structurenode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin StructureNode::checkInvariant
@skip checkInvariant(
@until end StructureNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an empty StructureNode.
@param   [in] destImageFile   The ImageFile where the new node will eventually
be stored.
@details
A StructureNode is a container for collections of named E57 nodes.
The @a destImageFile indicates which ImageFile the StructureNode will eventually
be attached to. A node is attached to an ImageFile by adding it underneath the
predefined root of the ImageFile (gotten from ImageFile::root). It is not an
error to fail to attach the StructureNode to the @a destImageFile. It is an
error to attempt to attach the StructureNode to a different ImageFile.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@return  A smart StructureNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Node
*/
StructureNode::StructureNode( ImageFile destImageFile ) : impl_( new StructureNodeImpl( destImageFile.impl() ) )
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool StructureNode::isRoot() const
{
   return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node StructureNode::parent() const
{
   return Node( impl_->parent() );
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring StructureNode::pathName() const
{
   return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent.
//! @copydetails Node::elementName()
ustring StructureNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node
//! when it was created.
//! @copydetails Node::destImageFile()
ImageFile StructureNode::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool StructureNode::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Return number of child nodes contained by this StructureNode.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  Number of child nodes contained by this StructureNode.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureNode::get(int64_t) const,
StructureNode::set, VectorNode::childCount
*/
int64_t StructureNode::childCount() const
{
   return impl_->childCount();
}

/*!
@brief   Is the given pathName defined relative to this node.
@param   [in] pathName   The absolute pathname, or pathname relative to this
object, to check.
@details
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this
StructureNode. If this StructureNode is not attached to an ImageFile, the @a
pathName origin root will not the root node of an ImageFile.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  true if pathName is currently defined.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::root, VectorNode::isDefined
*/
bool StructureNode::isDefined( const ustring &pathName ) const
{
   return impl_->isDefined( pathName );
}

/*!
@brief   Get a child element by positional index.
@param   [in] index   The index of child element to get, starting at 0.
@details
The order of children is not specified, and may be different than order the
children were added to the StructureNode. The order of children may change if
more children are added to the StructureNode.
@pre     0 <= @a index < childCount()
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  A smart Node handle referencing the child node.
@throw   ::E57_ERROR_CHILD_INDEX_OUT_OF_BOUNDS
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureNode::childCount,
StructureNode::get(const ustring&) const, VectorNode::get
*/
Node StructureNode::get( int64_t index ) const
{
   return Node( impl_->get( index ) );
}

/*!
@brief   Get a child by path name.
@param   [in] pathName   The absolute pathname, or pathname relative to this
object, of the object to get. The @a pathName may be relative to this node, or
absolute (starting with a "/"). The origin of the absolute path name is the root
of the tree that contains this StructureNode. If this StructureNode is not
attached to an ImageFile, the @a pathName origin root will not the root node of
an ImageFile.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The @a pathName must be defined (i.e. isDefined(pathName)).
@post    No visible state is modified.
@return  A smart Node handle referencing the child node.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureNode::get(int64_t) const
*/
Node StructureNode::get( const ustring &pathName ) const
{
   return Node( impl_->get( pathName ) );
}

/*!
@brief   Add a new child at a given path
    @param   [in] pathName  The absolute pathname, or pathname relative to this
object, that the child object @a n will be given.
@param   [in] n         The node to be added to the tree with given @a pathName.
@details
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this
StructureNode. If this StructureNode is not attached to an ImageFile, the @a
pathName origin root will not the root node of an ImageFile.

The path name formed from all element names in @a pathName except the last must
exist. If the @a pathName identifies the child of a VectorNode, then the last
element name in @a pathName must be numeric, and be equal to the childCount of
that VectorNode (the request is equivalent to VectorNode::append). The
StructureNode must not be a descendent of a homogeneous VectorNode with more
than one child.

The element naming grammar specified by the ASTM E57 format standard are not
enforced in this function. This would be very difficult to do dynamically, as
some of the naming rules involve combinations of names.
@pre     The new child node @a n must be a root node (i.e. n.isRoot()).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The associated destImageFile must have been opened in write mode (i.e.
destImageFile().isWritable()).
@pre     The @a pathName must not already be defined (i.e.
!isDefined(pathName)).
@pre     The associated destImageFile of this StructureNode and of @a n must be
same (i.e. destImageFile() == n.destImageFile()).
@post    The @a pathName will be defined (i.e. isDefined(pathName)).
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_SET_TWICE
@throw   ::E57_ERROR_ALREADY_HAS_PARENT
@throw   ::E57_ERROR_DIFFERENT_DEST_IMAGEFILE
@throw   ::E57_ERROR_HOMOGENEOUS_VIOLATION
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorNode::append
*/
void StructureNode::set( const ustring &pathName, const Node &n )
{
   impl_->set( pathName, n.impl(), false );
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void StructureNode::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void StructureNode::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Upcast a StructureNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it
for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     explanation in Node, Node::type(), StructureNode(const Node&)
*/
StructureNode::operator Node() const
{
   /// Implicitly upcast from shared_ptr<StructureNodeImpl> to SharedNodeImplPtr
   /// and construct a Node object
   return Node( impl_ );
}

/*!
@brief   Downcast a generic Node handle to a StructureNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying StructureNode, otherwise an
exception is thrown. In designs that need to avoid the exception, use
Node::type() to determine the actual type of the @a n before downcasting. This
function must be explicitly called (c++ compiler cannot insert it
automatically).
@return  A smart StructureNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     Node::type(), StructureNode::operator Node()
*/
StructureNode::StructureNode( const Node &n )
{
   if ( n.type() != E57_STRUCTURE )
   {
      throw E57_EXCEPTION2( E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString( n.type() ) );
   }

   /// Set our shared_ptr to the downcast shared_ptr
   impl_ = std::static_pointer_cast<StructureNodeImpl>( n.impl() );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
StructureNode::StructureNode( std::weak_ptr<ImageFileImpl> fileParent ) : impl_( new StructureNodeImpl( fileParent ) )
{
}

StructureNode::StructureNode( std::shared_ptr<StructureNodeImpl> ni ) : impl_( ni )
{
}
//! @endcond

//=====================================================================================
/*!
@class VectorNode
@brief   An E57 element containing ordered vector of child nodes.
@details
A VectorNode is a container of ordered child nodes.
The child nodes are automatically assigned an elementName, which is a string
version of the positional index of the child starting at "0". Child nodes may
only be appended onto the end of a VectorNode.

A VectorNode that is created with a restriction that its children must have the
same type is called a "homogeneous VectorNode". A VectorNode without such a
restriction is called a "heterogeneous VectorNode".

See Node class discussion for discussion of the common functions that
StructureNode supports.
@section vectornode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin VectorNode::checkInvariant
@skip checkInvariant(
@until end VectorNode::checkInvariant

@see     Node
*/

/*!
@brief   Create a new empty Vector node.
@param   [in] destImageFile         The ImageFile where the new node will
eventually be stored.
@param   [in] allowHeteroChildren   Will child elements of differing types be
allowed in this VectorNode.
@details
A VectorNode is a ordered container of E57 nodes.
The @a destImageFile indicates which ImageFile the VectorNode will eventually be
attached to. A node is attached to an ImageFile by adding it underneath the
predefined root of the ImageFile (gotten from ImageFile::root). It is not an
error to fail to attach the VectorNode to the @a destImageFile. It is an error
to attempt to attach the VectorNode to a different ImageFile.

If @a allowHeteroChildren is false, then the children that are appended to the
VectorNode must be identical in every visible characteristic except the stored
values. These visible characteristics include number of children (for
StructureNode and VectorNode descendents), number of records/prototypes/codecs
(for CompressedVectorNode), minimum/maximum attributes (for IntegerNode,
ScaledIntegerNode, FloatNode), byteCount (for BlobNode), scale/offset (for
ScaledIntegerNode), and all elementNames. The enforcement of this homogeneity
rule begins when the second child is appended to the VectorNode, thus it is not
an error to modify a child of a homogeneous VectorNode containing only one
child.

If @a allowHeteroChildren is true, then the types of the children of the
VectorNode are completely unconstrained.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@return  A smart VectorNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Node, VectorNode::allowHeteroChildren, ::E57_ERROR_HOMOGENEOUS_VIOLATION
*/
VectorNode::VectorNode( ImageFile destImageFile, bool allowHeteroChildren ) :
   impl_( new VectorNodeImpl( destImageFile.impl(), allowHeteroChildren ) )
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool VectorNode::isRoot() const
{
   return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node VectorNode::parent() const
{
   return Node( impl_->parent() );
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring VectorNode::pathName() const
{
   return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring VectorNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node
//! when it was created.
//! @copydetails Node::destImageFile()
ImageFile VectorNode::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool VectorNode::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Get whether child elements are allowed to be different types?
@details
See the class discussion at bottom of VectorNode page for details of
homogeneous/heterogeneous VectorNode. The returned attribute is determined when
the VectorNode is created, and cannot be changed.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  True if child elements can be different types.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ::E57_ERROR_HOMOGENEOUS_VIOLATION
*/
bool VectorNode::allowHeteroChildren() const
{
   return impl_->allowHeteroChildren();
}

/*!
@brief   Get number of child elements in this VectorNode.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  Number of child elements in this VectorNode.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorNode::get(int64_t), VectorNode::append, StructureNode::childCount
*/
int64_t VectorNode::childCount() const
{
   return impl_->childCount();
}

/*!
@brief   Is the given pathName defined relative to this node.
@param   [in] pathName   The absolute pathname, or pathname relative to this
object, to check.
@details
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this
VectorNode. If this VectorNode is not attached to an ImageFile, the @a pathName
origin root will not the root node of an ImageFile.

The element names of child elements of VectorNodes are numbers, encoded as
strings, starting at "0".
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  true if pathName is currently defined.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureNode::isDefined
*/
bool VectorNode::isDefined( const ustring &pathName ) const
{
   return impl_->isDefined( pathName );
}

/*!
@brief   Get a child element by positional index.
@param   [in] index   The index of child element to get, starting at 0.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     0 <= @a index < childCount()
@post    No visible state is modified.
@return  A smart Node handle referencing the child node.
@throw   ::E57_ERROR_CHILD_INDEX_OUT_OF_BOUNDS
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorNode::childCount, VectorNode::append, StructureNode::get(int64_t) const
*/
Node VectorNode::get( int64_t index ) const
{
   return Node( impl_->get( index ) );
}

/*!
@brief   Get a child element by string path name
@param   [in] pathName   The pathname, either absolute or relative, of the
object to get.
@details
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this
VectorNode. If this VectorNode is not attached to an ImageFile, the @a pathName
origin root will not the root node of an ImageFile.

The element names of child elements of VectorNodes are numbers, encoded as
strings, starting at "0".
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The @a pathName must be defined (i.e. isDefined(pathName)).
@post    No visible state is modified.
@return  A smart Node handle referencing the child node.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorNode::childCount, VectorNode::append, StructureNode::get(int64_t) const
*/
Node VectorNode::get( const ustring &pathName ) const
{
   return Node( impl_->get( pathName ) );
}

/*!
@brief   Append a child element to end of VectorNode.
@param   [in] n   The node to be added as a child at end of the VectorNode.
@details
If the VectorNode is homogeneous and already has at least one child, then @a n
must be identical to the existing children in every visible characteristic
except the stored values. These visible characteristics include number of
children (for StructureNode and VectorNode descendents), number of
records/prototypes/codecs (for CompressedVectorNode), minimum/maximum attributes
(for IntegerNode, ScaledIntegerNode, FloatNode), byteCount (for BlobNode),
scale/offset (for ScaledIntegerNode), and all elementNames.

The VectorNode must not be a descendent of a homogeneous VectorNode with more
than one child.
@pre     The new child node @a n must be a root node (not already having a
parent).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The associated destImageFile must have been opened in write mode (i.e.
destImageFile().isWritable()).
@post    the childCount is incremented.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_HOMOGENEOUS_VIOLATION
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_ALREADY_HAS_PARENT
@throw   ::E57_ERROR_DIFFERENT_DEST_IMAGEFILE
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorNode::childCount, VectorNode::get(int64_t), StructureNode::set
*/
void VectorNode::append( const Node &n )
{
   impl_->append( n.impl() );
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void VectorNode::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void VectorNode::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Upcast a VectorNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it
for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     explanation in Node, Node::type(), VectorNode(const Node&)
*/
VectorNode::operator Node() const
{
   /// Implicitly upcast from shared_ptr<VectorNodeImpl> to SharedNodeImplPtr
   /// and construct a Node object
   return Node( impl_ );
}

/*!
@brief   Downcast a generic Node handle to a VectorNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying VectorNode, otherwise an
exception is thrown. In designs that need to avoid the exception, use
Node::type() to determine the actual type of the @a n before downcasting. This
function must be explicitly called (c++ compiler cannot insert it
automatically).
@return  A smart VectorNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     Node::type(), VectorNode::operator Node()
*/
VectorNode::VectorNode( const Node &n )
{
   if ( n.type() != E57_VECTOR )
   {
      throw E57_EXCEPTION2( E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString( n.type() ) );
   }

   /// Set our shared_ptr to the downcast shared_ptr
   impl_ = std::static_pointer_cast<VectorNodeImpl>( n.impl() );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
VectorNode::VectorNode( std::shared_ptr<VectorNodeImpl> ni ) : impl_( ni )
{
}
//! @endcond

//=====================================================================================
/*!
@class SourceDestBuffer
@brief   A memory buffer to transfer data to/from a CompressedVectorNode in a
block.
@details
The SourceDestBuffer is an encapsulation of a buffer in memory that will
transfer data to/from a field in a CompressedVectorNode. The API user is
responsible for creating the actual memory buffer, describing it correctly to
the API, making sure it exists during the transfer period, and destroying it
after the transfer is complete. Additionally, the SourceDestBuffer has
information that specifies the connection to the CompressedVectorNode field
(i.e. the field's path name in the prototype).

The type of buffer element may be an assortment of built-in C++ memory types.
There are all combinations of signed/unsigned and 8/16/32/64 bit integers
(except unsigned 64bit integer, which is not supported in the ASTM standard),
bool, float, double, as well as a vector of variable length unicode strings. The
compiler selects the appropriate constructor automatically based on the type of
the buffer array. However, the API user is responsible for reporting the correct
length and stride options (otherwise unspecified behavior can occur).

The connection of the SourceDestBuffer to a CompressedVectorNode field is
established by specifying the pathName. There are several options to this
connection: doConversion and doScaling, which are described in the constructor
documentation.

@section sourcedestbuffer_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin SourceDestBuffer::checkInvariant
@skip checkInvariant(
@until end SourceDestBuffer::checkInvariant

@see     Node
*/

/*!
@brief   Designate buffers to transfer data to/from a CompressedVectorNode in a
block.
@param   [in] destImageFile The ImageFile where the new node will eventually be
stored.
@param   [in] pathName      The pathname of the field in CompressedVectorNode
that will transfer data to/from.
@param   [in] b             The caller allocated memory buffer.
@param   [in] capacity      The total number of memory elements in buffer @a b.
@param   [in] doConversion  Will a conversion be attempted between memory and
ImageFile representations.
@param   [in] doScaling     In a ScaledInteger field, do memory elements hold
scaled values, if false they hold raw values.
@param   [in] stride        The number of bytes between memory elements.  If
zero, defaults to sizeof memory element.
@details
This overloaded form of the SourceDestBuffer constructor declares a buffer @a b
to be the source/destination of a transfer of values stored in a
CompressedVectorNode.\n\n

The @a pathName will be used to identify a Node in the prototype that will
get/receive data from this buffer. The @a pathName may be an absolute path name
(e.g. "/cartesianX") or a path name relative to the root of the prototype (i.e.
the absolute path name without the leading "/", for example: "cartesianX").\n\n

The type of @a b is used to determine the MemoryRepresentation of the
SourceDestBuffer. The buffer @a b may be used for multiple block transfers. See
discussions of operation of SourceDestBuffer attributes in
SourceDestBuffer::memoryRepresentation, SourceDestBuffer::capacity,
SourceDestBuffer::doConversion, and SourceDestBuffer::doScaling, and
SourceDestBuffer::stride.\n\n

The API user is responsible for ensuring that the lifetime of the @a b memory
buffer exceeds the time that it is used in transfers (i.e. the E57 Foundation
Implementation cannot detect that the buffer been destroyed).\n\n

The @a capacity must match the capacity of all other SourceDestBuffers that will
participate in a transfer with a CompressedVectorNode.

@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The stride must be >= sizeof(*b)
@return  A smart SourceDestBuffer handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_BAD_BUFFER
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::reader, ImageFile::writer,
CompressedVectorReader::read(std::vector<SourceDestBuffer>&),
CompressedVectorWriter::write(std::vector<SourceDestBuffer>&)
*/
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, int8_t *b, const size_t capacity,
                                    bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<int8_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, uint8_t *b, const size_t capacity,
                                    bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<uint8_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, int16_t *b, const size_t capacity,
                                    bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<int16_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, uint16_t *b,
                                    const size_t capacity, bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<uint16_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, int32_t *b, const size_t capacity,
                                    bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<int32_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, uint32_t *b,
                                    const size_t capacity, bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<uint32_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, int64_t *b, const size_t capacity,
                                    bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<int64_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, bool *b, const size_t capacity,
                                    bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<bool>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, float *b, const size_t capacity,
                                    bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<float>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode
//! in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const
//! ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, double *b, const size_t capacity,
                                    bool doConversion, bool doScaling, size_t stride ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, capacity, doConversion, doScaling ) )
{
   impl_->setTypeInfo<double>( b, stride );
}

/*!
@brief   Designate vector of strings to transfer data to/from a CompressedVector
as a block.
@param   [in] destImageFile The ImageFile where the new node will eventually be
stored.
@param   [in] pathName      The pathname of the field in CompressedVectorNode
that will transfer data to/from.
@param   [in] b             The caller created vector of ustrings to transfer
from/to.
@details
This overloaded form of the SourceDestBuffer constructor declares a
vector<ustring> to be the source/destination of a transfer of StringNode values
stored in a CompressedVectorNode.

The @a pathName will be used to identify a Node in the prototype that will
get/receive data from this buffer. The @a pathName may be an absolute path name
(e.g. "/cartesianX") or a path name relative to the root of the prototype (i.e.
the absolute path name without the leading "/", for example: "cartesianX").

The @a b->size() must match capacity of all other SourceDestBuffers that will
participate in a transfer with a CompressedVectorNode (string or any other type
of buffer). In a read into the SourceDestBuffer, the previous contents of the
strings in the vector are lost, and the memory space is potentially freed. The
@a b->size() of the vector will not be changed. It is an error to request a
read/write of more records that @a b->size() (just as it would be for buffers of
integer types). The API user is responsible for ensuring that the lifetime of
the @a b vector exceeds the time that it is used in transfers (i.e. the E57
Foundation Implementation cannot detect that the buffer been destroyed).

@pre     b.size() must be > 0.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@return  A smart SourceDestBuffer handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_BAD_BUFFER
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBuffer::doConversion for discussion on representations compatible with string SourceDestBuffers.
*/
SourceDestBuffer::SourceDestBuffer( ImageFile destImageFile, const ustring &pathName, StringList *b ) :
   impl_( new SourceDestBufferImpl( destImageFile.impl(), pathName, b ) )
{
}

/*!
@brief   Get path name in prototype that this SourceDestBuffer will transfer
data to/from.
@details
The prototype of a CompressedVectorNode describes the fields that are in each
record. This function returns the path name of the node in the prototype tree
that this SourceDestBuffer will write/read. The correctness of this path name is
checked when this SourceDestBuffer is associated with a CompressedVectorNode
(either in CompressedVectorNode::writer,
CompressedVectorWriter::write(std::vector<SourceDestBuffer>&, unsigned),
CompressedVectorNode::reader,
CompressedVectorReader::read(std::vector<SourceDestBuffer>&)).

@post    No visible state is modified.
@return  Path name in prototype that this SourceDestBuffer will transfer data
to/from.
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVector, CompressedVectorNode::prototype
*/
ustring SourceDestBuffer::pathName() const
{
   return impl_->pathName();
}

/*!
@brief   Get memory representation of the elements in this SourceDestBuffer.
@details
The memory representation is deduced from which overloaded SourceDestBuffer
constructor was used. The memory representation is independent of the type and
minimum/maximum bounds of the node in the prototype that the SourceDestBuffer
will transfer to/from. However, some combinations will result in an error if
doConversion is not requested (e.g. E57_INT16 and FloatNode).

Some combinations risk an error occurring during a write, if a value is too
large (e.g. writing a E57_INT16 memory representation to an IntegerNode with
minimum=-1024 maximum=1023). Some combinations risk an error occurring during a
read, if a value is too large (e.g. reading an IntegerNode with minimum=-1024
maximum=1023 int an E57_INT8 memory representation). Some combinations are never
possible (e.g. E57_INT16 and StringNode).
@post    No visible state is modified.
@return  Memory representation of the elements in buffer.
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     MemoryRepresentation, NodeType
*/
MemoryRepresentation SourceDestBuffer::memoryRepresentation() const
{
   return impl_->memoryRepresentation();
}

/*!
@brief   Get total capacity of buffer.
@details
The API programmer is responsible for correctly specifying the length of a
buffer. This function returns that declared length. If the length is incorrect
(in particular, too long) memory may be corrupted or erroneous values written.
@post    No visible state is modified.
@return  Total capacity of buffer.
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
*/
size_t SourceDestBuffer::capacity() const
{
   return impl_->capacity();
}

/*!
@brief   Get whether conversions will be performed to match the memory type of
buffer.
@details
The API user must explicitly request conversion between basic representation
groups in memory and on the disk. The four basic representation groups are:
integer, boolean, floating point, and string. There is no distinction between
integer and boolean groups on the disk (they both use IntegerNode). A explicit
request for conversion between single and double precision floating point
representations is not required.

The most useful conversion is between integer and floating point representation
groups. Conversion from integer to floating point representations cannot result
in an overflow, and is usually loss-less (except for extremely large integers).
Conversion from floating point to integer representations can result in an
overflow, and can be lossy.

Conversion between any of the integer, boolean, and floating point
representation groups is supported. No conversion from the string to any other
representation group is possible. Missing or unsupported conversions are
detected when the first transfer is attempted (i.e. not when the
CompressedVectorReader or CompressedVectorWriter is created).

@post    No visible state is modified.
@return  true if conversions will be performed to match the memory type of
buffer.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
*/
bool SourceDestBuffer::doConversion() const
{
   return impl_->doConversion();
}

/*!
@brief   Get whether scaling will be performed for ScaledIntegerNode transfers.
@details
The doScaling option only applies to ScaledIntegerNodes stored in a
CompressedVectorNode on the disk (it is ignored if a ScaledIntegerNode is not
involved).

As a convenience, an E57 Foundation Implementation can perform scaling of data
so that the API user can manipulate scaledValues rather than rawValues. For a
reader, the scaling process is: scaledValue = (rawValue * scale) + offset. For a
writer, the scaling process is reversed: rawValue = (scaledValue - offset) /
scale. The result performing a scaling in a reader (or "unscaling" in a writer)
is always a floating point number. This floating point number may have to be
converted to be compatible with the destination representation. If the
destination representation is not floating point, there is a risk of violating
declared min/max bounds. Because of this risk, it is recommended that scaling
only be requested for reading scaledValues from ScaledIntegerNodes into floating
point numbers in memory.

It is also possible (and perhaps safest of all) to never request that scaling be
performed, and always deal with rawValues outside the API. Note this does not
mean that ScaledIntegerNodes should be avoided. ScaledIntgerNodes are essential
for encoding numeric data with fractional parts in CompressedVectorNodes.
Because the ASTM E57 format recommends that SI units without prefix be used
(i.e. meters, not milli-meters or micro-furlongs), almost every measured value
will have a fractional part.

@post    No visible state is modified.
@return  true if scaling will be performed for ScaledInteger transfers.
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode
*/
bool SourceDestBuffer::doScaling() const
{
   return impl_->doScaling();
}

/*!
@brief   Get number of bytes between consecutive memory elements in buffer
@details
Elements in a memory buffer do not have to be consecutive.
They can also be spaced at regular intervals.
This allows a value to be picked out of an array of C++ structures (the stride
would be the size of the structure). In the case that the element values are
stored consecutively in memory, the stride equals the size of the memory
representation of the element.
@post    No visible state is modified.
@return  Number of bytes between consecutive memory elements in buffer
*/
size_t SourceDestBuffer::stride() const
{
   return impl_->stride();
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void SourceDestBuffer::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void SourceDestBuffer::dump( int indent, std::ostream &os ) const
{
}
#endif

//=====================================================================================
/*!
@class CompressedVectorReader
@brief   An iterator object keeping track of a read in progress from a
CompressedVectorNode.
@details
A CompressedVectorReader object is a block iterator that reads blocks of records
from a CompressedVectorNode and stores them in memory buffers
(SourceDestBuffers). Blocks of records are processed rather than a single
record-at-a-time for efficiency reasons. The CompressedVectorReader class
encapsulates all the state that must be saved in between the processing of one
record block and the next (e.g. partially read disk pages, or data decompression
state). New memory buffers can be used for each record block read, or the
previous buffers can be reused.

CompressedVectorReader objects have an open/closed state.
Initially a newly created CompressedVectorReader is in the open state.
After the API user calls CompressedVectorReader::close, the object will be in
the closed state and no more data transfers will be possible.

There is no CompressedVectorReader constructor in the API.
The function CompressedVectorNode::reader returns an already constructed
CompressedVectorReader object with given memory buffers (SourceDestBuffers)
already associated.

It is recommended to call CompressedVectorReader::close to gracefully end the
transfer. Unlike the CompressedVectorWriter, not all fields in the record of the
CompressedVectorNode are required to be read at one time.

@section CompressedVectorReader_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin CompressedVectorReader::checkInvariant
@skip checkInvariant(
@until end CompressedVectorReader::checkInvariant

@see     CompressedVectorNode, CompressedVectorWriter
*/

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
CompressedVectorReader::CompressedVectorReader( std::shared_ptr<CompressedVectorReaderImpl> ni ) : impl_( ni )
{
}
//! @endcond

/*!
@brief   Request transfer of blocks of data from CompressedVectorNode into
previously designated destination buffers.
@details
The SourceDestBuffers used are previously designated either in
CompressedVectorNode::reader where this object was created, or in the last call
to CompressedVectorReader::read(std::vector<SourceDestBuffer>&) where new
buffers were designated. The function will always return the full number of
records requested (the capacity of the SourceDestBuffers) unless it has reached
the end of the CompressedVectorNode, in which case it will return less than the
capacity of the SourceDestBuffers. Partial reads will store the records at the
beginning of the SourceDestBuffers. It is not an error to call this function
after all records in the CompressedVectorNode have been read (the function
returns 0).

If a conversion or bounds error occurs during the transfer, the
CompressedVectorReader is left in an undocumented state (it can't be used any
further). If a file I/O or checksum error occurs during the transfer, both the
CompressedVectorReader and the associated ImageFile are left in an undocumented
state (they can't be used any further).

The API user is responsible for ensuring that the underlying memory buffers
represented in the SourceDestBuffers still exist when this function is called.
The E57 Foundation Implementation cannot detect that a memory buffer been
destroyed.

@pre     The associated ImageFile must be open.
@pre     This CompressedVectorReader must be open (i.e isOpen())
@return  The number of records read.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_READER_NOT_OPEN
@throw   ::E57_ERROR_CONVERSION_REQUIRED            This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_VALUE_NOT_REPRESENTABLE        This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_REAL64_TOO_LARGE               This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_EXPECTING_NUMERIC              This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_EXPECTING_USTRING              This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_BAD_CV_PACKET      This CompressedVectorReader, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorReader, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorReader, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorReader, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorReader::read(std::vector<SourceDestBuffer>&),
CompressedVectorNode::reader, SourceDestBuffer,
CompressedVectorReader::read(std::vector<SourceDestBuffer>&)
*/
unsigned CompressedVectorReader::read()
{
   return impl_->read();
}

/*!
@brief   Request transfer of block of data from CompressedVectorNode into given
destination buffers.
@param   [in] dbufs     Vector of memory buffers that will receive data read
from a CompressedVectorNode.
@details
The @a dbufs must all have the same capacity.
The specified @a dbufs must have same number of elements as previously
designated SourceDestBuffer vector. The each SourceDestBuffer within @a dbufs
must be identical to the previously designated SourceDestBuffer except for
capacity and buffer address.

The @a dbufs locations are saved so that a later call to
CompressedVectorReader::read() can be used without having to re-specify the
SourceDestBuffers.

The function will always return the full number of records requested (the
capacity of the SourceDestBuffers) unless it has reached the end of the
CompressedVectorNode, in which case it will return less than the capacity of the
SourceDestBuffers. Partial reads will store the records at the beginning of the
SourceDestBuffers. It is not an error to call this function after all records in
the CompressedVectorNode have been read (the function returns 0).

If a conversion or bounds error occurs during the transfer, the
CompressedVectorReader is left in an undocumented state (it can't be used any
further). If a file I/O or checksum error occurs during the transfer, both the
CompressedVectorReader and the associated ImageFile are left in an undocumented
state (they can't be used any further).

The API user is responsible for ensuring that the underlying memory buffers
represented in the SourceDestBuffers still exist when this function is called.
The E57 Foundation Implementation cannot detect that a memory buffer been
destroyed.

@pre     The associated ImageFile must be open.
@pre     This CompressedVectorReader must be open (i.e isOpen())
@return  The number of records read.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_READER_NOT_OPEN
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_CONVERSION_REQUIRED            This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_VALUE_NOT_REPRESENTABLE        This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_REAL64_TOO_LARGE               This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_EXPECTING_NUMERIC              This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_EXPECTING_USTRING              This CompressedVectorReader
in undocumented state
@throw   ::E57_ERROR_BAD_CV_PACKET      This CompressedVectorReader, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorReader, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorReader, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorReader, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorReader::read(), CompressedVectorNode::reader, SourceDestBuffer
*/
unsigned CompressedVectorReader::read( std::vector<SourceDestBuffer> &dbufs )
{
   return impl_->read( dbufs );
}

/*!
@brief   Set record number of CompressedVectorNode where next read will start.
@param   [in] recordNumber   The index of record in ComressedVectorNode where
next read using this CompressedVectorReader will start.
@details
This function may be called at any time (as long as ImageFile and
CompressedVectorReader are open). The next read will start at the given
recordNumber. It is not an error to seek to recordNumber = childCount() (i.e. to
one record past end of CompressedVectorNode).

@pre     @a recordNumber <= childCount() of CompressedVectorNode.
@pre     The associated ImageFile must be open.
@pre     This CompressedVectorReader must be open (i.e isOpen())
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_READER_NOT_OPEN
@throw   ::E57_ERROR_BAD_CV_PACKET
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorNode::reader
*/
void CompressedVectorReader::seek( int64_t recordNumber )
{
   impl_->seek( recordNumber );
}

/*!
@brief   End the read operation.
@details
It is recommended that this function be called to gracefully end a transfer to a
CompressedVectorNode. It is not an error to call this function if the
CompressedVectorReader is already closed. This function will cause the
CompressedVectorReader to enter the closed state, and any further transfers
requests will fail.

@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorReader::isOpen, CompressedVectorNode::reader
*/
void CompressedVectorReader::close()
{
   impl_->close();
}

/*!
@brief   Test whether CompressedVectorReader is still open for reading.
@pre     The associated ImageFile must be open.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorReader::close, CompressedVectorNode::reader
*/
bool CompressedVectorReader::isOpen()
{
   return impl_->isOpen();
}

/*!
@brief   Return the CompressedVectorNode being read.
@details
It is not an error if this CompressedVectorReader is closed.
@pre     The associated ImageFile must be open.
@return  A smart CompressedVectorNode handle referencing the underlying object
being read from.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorReader::close, CompressedVectorNode::reader
*/
CompressedVectorNode CompressedVectorReader::compressedVectorNode() const
{
   return impl_->compressedVectorNode();
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void CompressedVectorReader::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void CompressedVectorReader::dump( int indent, std::ostream &os ) const
{
}
#endif

//=====================================================================================
/*!
@class CompressedVectorWriter
@brief   An iterator object keeping track of a write in progress to a
CompressedVectorNode.
@details
A CompressedVectorWriter object is a block iterator that reads blocks of records
from memory and stores them in a CompressedVectorNode. Blocks of records are
processed rather than a single record-at-a-time for efficiency reasons. The
CompressedVectorWriter class encapsulates all the state that must be saved in
between the processing of one record block and the next (e.g. partially written
disk pages, partially filled bytes in a bytestream, or data compression state).
New memory buffers can be used for each record block write, or the previous
buffers can be reused.

CompressedVectorWriter objects have an open/closed state.
Initially a newly created CompressedVectorWriter is in the open state.
After the API user calls CompressedVectorWriter::close, the object will be in
the closed state and no more data transfers will be possible.

There is no CompressedVectorWriter constructor in the API.
The function CompressedVectorNode::writer returns an already constructed
CompressedVectorWriter object with given memory buffers (SourceDestBuffers)
already associated. CompressedVectorWriter::close must explicitly be called to
safely and gracefully end the transfer.

@b Warning: If CompressedVectorWriter::close is not called before the
CompressedVectorWriter destructor is invoked, all writes to the
CompressedVectorNode will be lost (it will have zero children).

@section CompressedVectorWriter_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin CompressedVectorWriter::checkInvariant
@skip checkInvariant(
@until end CompressedVectorWriter::checkInvariant

@see     CompressedVectorNode, CompressedVectorReader
*/

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
CompressedVectorWriter::CompressedVectorWriter( std::shared_ptr<CompressedVectorWriterImpl> ni ) : impl_( ni )
{
}
//! @endcond

/*!
@brief   Request transfer of blocks of data to CompressedVectorNode from
previously designated source buffers.
@param   [in] recordCount   Number of records to write.
@details
The SourceDestBuffers used are previously designated either in
CompressedVectorNode::writer where this object was created, or in the last call
to CompressedVectorWriter::write(std::vector<SourceDestBuffer>&, unsigned) where
new buffers were designated.

If a conversion or bounds error occurs during the transfer, the
CompressedVectorWriter is left in an undocumented state (it can't be used any
further), and all previously written records are deleted from the associated
CompressedVectorNode which will then have zero children. If a file I/O or
checksum error occurs during the transfer, both this CompressedVectorWriter and
the associated ImageFile are left in an undocumented state (they can't be used
any further). If CompressedVectorWriter::close is not called before the
CompressedVectorWriter destructor is invoked, all writes to the
CompressedVectorNode will be lost (it will have zero children).

@pre     The associated ImageFile must be open.
@pre     This CompressedVectorWriter must be open (i.e isOpen())
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_WRITER_NOT_OPEN
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_NO_BUFFER_FOR_ELEMENT
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_CONVERSION_REQUIRED     This CompressedVectorWriter in
undocumented state, associated CompressedVectorNode modified but consistent,
associated ImageFile modified but consistent.
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS     This CompressedVectorWriter in
undocumented state, associated CompressedVectorNode modified but consistent,
associated ImageFile modified but consistent.
@throw   ::E57_ERROR_VALUE_NOT_REPRESENTABLE This CompressedVectorWriter in
undocumented state, associated CompressedVectorNode modified but consistent,
associated ImageFile modified but consistent.
@throw   ::E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE This CompressedVectorWriter
in undocumented state, associated CompressedVectorNode modified but consistent,
associated ImageFile modified but consistent.
@throw   ::E57_ERROR_REAL64_TOO_LARGE   This CompressedVectorWriter in
undocumented state, associated CompressedVectorNode modified but consistent,
associated ImageFile modified but consistent.
@throw   ::E57_ERROR_EXPECTING_NUMERIC  This CompressedVectorWriter in
undocumented state, associated CompressedVectorNode modified but consistent,
associated ImageFile modified but consistent.
@throw   ::E57_ERROR_EXPECTING_USTRING  This CompressedVectorWriter in
undocumented state, associated CompressedVectorNode modified but consistent,
associated ImageFile modified but consistent.
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_WRITE_FAILED       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorWriter::write(std::vector<SourceDestBuffer>&,unsigned),
CompressedVectorNode::writer, CompressedVectorWriter::close, SourceDestBuffer,
E57Exception
*/
void CompressedVectorWriter::write( const size_t recordCount )
{
   impl_->write( recordCount );
}

/*!
@brief   Request transfer of block of data to CompressedVectorNode from given
source buffers.
@param   [in] sbufs         Vector of memory buffers that hold data to be
written to a CompressedVectorNode.
@param   [in] recordCount   Number of records to write.
@details
The @a sbufs must all have the same capacity.
The @a sbufs capacity must be >= @a recordCount.
The specified @a sbufs must have same number of elements as previously
designated SourceDestBuffer vector. The each SourceDestBuffer within @a sbufs
must be identical to the previously designated SourceDestBuffer except for
capacity and buffer address.

The @a sbufs locations are saved so that a later call to
CompressedVectorWriter::write(unsigned) can be used without having to re-specify
the SourceDestBuffers.

If a conversion or bounds error occurs during the transfer, the
CompressedVectorWriter is left in an undocumented state (it can't be used any
further), and all previously written records are deleted from the the associated
CompressedVectorNode which will then have zero children. If a file I/O or
checksum error occurs during the transfer, both this CompressedVectorWriter and
the associated ImageFile are left in an undocumented state (they can't be used
any further).

@b Warning: If CompressedVectorWriter::close is not called before the
CompressedVectorWriter destructor is invoked, all writes to the
CompressedVectorNode will be lost (it will have zero children).

@pre     The associated ImageFile must be open.
@pre     This CompressedVectorWriter must be open (i.e isOpen())
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_WRITER_NOT_OPEN
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_NO_BUFFER_FOR_ELEMENT
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_CONVERSION_REQUIRED     This CompressedVectorWriter in
undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS     This CompressedVectorWriter in
undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_VALUE_NOT_REPRESENTABLE This CompressedVectorWriter in
undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE This CompressedVectorWriter
in undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_REAL64_TOO_LARGE   This CompressedVectorWriter in
undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_EXPECTING_NUMERIC  This CompressedVectorWriter in
undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_EXPECTING_USTRING  This CompressedVectorWriter in
undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_WRITE_FAILED       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorWriter::write(unsigned), CompressedVectorNode::writer,
CompressedVectorWriter::close, SourceDestBuffer, E57Exception
*/
void CompressedVectorWriter::write( std::vector<SourceDestBuffer> &sbufs, const size_t recordCount )
{
   impl_->write( sbufs, recordCount );
}

/*!
@brief   End the write operation.
@details
This function must be called to safely and gracefully end a transfer to a
CompressedVectorNode. This is required because errors cannot be communicated
from the CompressedVectorNode destructor (in C++ destructors can't throw
exceptions). It is not an error to call this function if the
CompressedVectorWriter is already closed. This function will cause the
CompressedVectorWriter to enter the closed state, and any further transfers
requests will fail.

@b Warning: If this function is not called before the CompressedVectorWriter
destructor is invoked, all writes to the CompressedVectorNode will be lost (it
will have zero children).
@pre     The associated ImageFile must be open.
@post    This CompressedVectorWriter is closed (i.e !isOpen())
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_WRITE_FAILED       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorWriter, associated
ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorWriter::isOpen
*/
void CompressedVectorWriter::close()
{
   impl_->close();
}

/*!
@brief   Test whether CompressedVectorWriter is still open for writing.
@pre     The associated ImageFile must be open.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorWriter::close, CompressedVectorNode::writer
*/
bool CompressedVectorWriter::isOpen()
{
   return impl_->isOpen();
}

/*!
@brief   Return the CompressedVectorNode being written to.
@pre     The associated ImageFile must be open.
@return  A smart CompressedVectorNode handle referencing the underlying object
being written to.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorNode::writer
*/
CompressedVectorNode CompressedVectorWriter::compressedVectorNode() const
{
   return impl_->compressedVectorNode();
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void CompressedVectorWriter::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void CompressedVectorWriter::dump( int indent, std::ostream &os ) const
{
}
#endif

//=====================================================================================
/*!
@class CompressedVectorNode
@brief   An E57 element containing ordered vector of child nodes, stored in an
efficient binary format.
@details
The CompressedVectorNode encodes very long sequences of identically typed
records. In an E57 file, the per-point information (coordinates, intensity,
color, time stamp etc.) are stored in a CompressedVectorNode. For time and space
efficiency, the CompressedVectorNode data is stored in a binary section of the
E57 file.

Conceptually, the CompressedVectorNode encodes a structure that looks very much
like a homogeneous VectorNode object. However because of the huge volume of data
(E57 files can store more than 10 billion points) within a CompressedVectorNode,
the functions for accessing the data are dramatically different.
CompressedVectorNode data is accessed in large blocks of records (100s to 1000s
at a time).

Two attributes are required to create a new CompressedVectorNode.
The first attribute describes the shape of the record that will be stored.
This record type description is called the @c prototype of the
CompressedVectorNode. Often the @c prototype will be a StructNode with a single
level of named child elements. However, the prototype can be a tree of any depth
consisting of the following node types: IntegerNode, ScaledIntegerNode,
FloatNode, StringNode, StructureNode, or VectorNode (i.e. CompressedVectorNode
and BlobNode are not allowed). Only the node types and attributes are used in
the prototype, the values stored are ignored. For example, if the prototype
contains an IntegerNode, with a value=0, minimum=0, maximum=1023, then this
means that each record will contain an integer that can take any value in the
interval [0,1023]. As a second example, if the prototype contains an
ScaledIntegerNode, with a value=0, minimum=0, maximum=1023, scale=.001, offset=0
then this means that each record will contain an integer that can take any value
in the interval [0,1023] and if a reader requests the scaledValue of the field,
the rawValue should be multiplied by 0.001.

The second attribute needed to describe a new CompressedVectorNode is the @c
codecs description of how the values of the records are to be represented on the
disk. The codec object is a VectorNode of a particular format that describes the
encoding for each field in the record, which codec will be used to transfer the
values to and from the disk. Currently only one codec is defined for E57 files,
the bitPackCodec, which copies the numbers from memory, removes any unused bit
positions, and stores the without additional spaces on the disk. The
bitPackCodec has no configuration options or parameters to tune. In the ASTM
standard, if no codec is specified, the bitPackCodec is assumed. So specifying
the @c codecs as an empty VectorNode is equivalent to requesting at all fields
in the record be encoded with the bitPackCodec.

Other than the @c prototype and @c codecs attributes, the only other state
directly accessible is the number of children (records) in the
CompressedVectorNode. The read/write access to the contents of the
CompressedVectorNode is coordinated by two other Foundation API objects:
CompressedVectorReader and CompressedVectorWriter.

@section CompressedVectorNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin CompressedVectorNode::checkInvariant
@skip checkInvariant(
@until end CompressedVectorNode::checkInvariant

@see     CompressedVectorReader, CompressedVectorWriter, Node
*/

/*!
@brief   Create an empty CompressedVectorNode, for writing, that will store
records specified by the prototype.
@param   [in] destImageFile The ImageFile where the new node will eventually be
stored.
@param   [in] prototype     A tree that describes the fields in each record of
the CompressedVectorNode.
@param   [in] codecs        A VectorNode describing which codecs should be used
for each field described in the prototype.
@details
The @a destImageFile indicates which ImageFile the CompressedVectorNode will
eventually be attached to. A node is attached to an ImageFile by adding it
underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the CompressedVectorNode to the
@a destImageFile. It is an error to attempt to attach the CompressedVectorNode
to a different ImageFile. The CompressedVectorNode may not be written to until
it is attached to the destImageFile tree.

The @a prototype may be any tree consisting of only the following node types:
IntegerNode, ScaledIntegerNode, FloatNode, StringNode, StructureNode, or
VectorNode (i.e. CompressedVectorNode and BlobNode are not allowed). See
CompressedVectorNode for discussion about the @a prototype argument.

The @a codecs must be a heterogeneous VectorNode with children as specified in
the ASTM E57 data format standard. Since currently only one codec is supported
(bitPackCodec), and it is the default, passing an empty VectorNode will specify
that all record fields will be encoded with bitPackCodec.

@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@pre     @a prototype must be an unattached root node (i.e.
!prototype.isAttached() && prototype.isRoot())
@pre     @a prototype cannot contain BlobNodes or CompressedVectorNodes.
@pre     @a codecs must be an unattached root node (i.e. !codecs.isAttached() &&
codecs.isRoot())
@post    prototype.isAttached()
@post    codecs.isAttached()
@return  A smart CompressedVectorNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_BAD_PROTOTYPE
@throw   ::E57_ERROR_BAD_CODECS
@throw   ::E57_ERROR_ALREADY_HAS_PARENT
@throw   ::E57_ERROR_DIFFERENT_DEST_IMAGEFILE
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBuffer, Node, CompressedVectorNode::reader, CompressedVectorNode::writer
*/
CompressedVectorNode::CompressedVectorNode( ImageFile destImageFile, const Node &prototype, const VectorNode &codecs ) :
   impl_( new CompressedVectorNodeImpl( destImageFile.impl() ) )
{
   /// Because of shared_ptr quirks, can't set prototype,codecs in
   /// CompressedVectorNodeImpl(), so set it afterwards
   impl_->setPrototype( prototype.impl() );
   impl_->setCodecs( codecs.impl() );
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool CompressedVectorNode::isRoot() const
{
   return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node CompressedVectorNode::parent() const
{
   return Node( impl_->parent() );
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring CompressedVectorNode::pathName() const
{
   return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring CompressedVectorNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node
//! when it was created.
//! @copydetails Node::destImageFile()
ImageFile CompressedVectorNode::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool CompressedVectorNode::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Get current number of records in a CompressedVectorNode.
@details
For a CompressedVectorNode with an active CompressedVectorWriter, the returned
number will reflect any writes completed.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  Current number of records in CompressedVectorNode.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorNode::reader, CompressedVectorNode::writer
*/
int64_t CompressedVectorNode::childCount() const
{
   return impl_->childCount();
}

/*!
@brief   Get the prototype tree that describes the types in the record.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  A smart Node handle referencing the root of the prototype tree.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorNode::CompressedVectorNode, SourceDestBuffer,
CompressedVectorNode::reader, CompressedVectorNode::writer
*/
Node CompressedVectorNode::prototype() const
{
   return Node( impl_->getPrototype() );
}

/*!
@brief   Get the codecs tree that describes the encoder/decoder configuration of
the CompressedVectorNode.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  A smart VectorNode handle referencing the root of the codecs tree.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorNode::CompressedVectorNode, SourceDestBuffer,
CompressedVectorNode::reader, CompressedVectorNode::writer
*/
VectorNode CompressedVectorNode::codecs() const
{
   return VectorNode( impl_->getCodecs() );
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void CompressedVectorNode::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void CompressedVectorNode::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Upcast a CompressedVectorNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it
for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     explanation in Node, Node::type(), CompressedVectorNode(const Node&)
*/
CompressedVectorNode::operator Node() const
{
   /// Implicitly upcast from shared_ptr<CompressedVectorNodeImpl> to
   /// SharedNodeImplPtr and construct a Node object
   return Node( impl_ );
}

/*!
@brief   Downcast a generic Node handle to a CompressedVectorNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying CompressedVectorNode,
otherwise an exception is thrown. In designs that need to avoid the exception,
use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it
automatically).
@return  A smart CompressedVectorNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     Node::type(), CompressedVectorNode::operator, Node()
*/
CompressedVectorNode::CompressedVectorNode( const Node &n )
{
   if ( n.type() != E57_COMPRESSED_VECTOR )
   {
      throw E57_EXCEPTION2( E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString( n.type() ) );
   }

   /// Set our shared_ptr to the downcast shared_ptr
   impl_ = std::static_pointer_cast<CompressedVectorNodeImpl>( n.impl() );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
CompressedVectorNode::CompressedVectorNode( std::shared_ptr<CompressedVectorNodeImpl> ni ) : impl_( ni )
{
}
//! @endcond

/*!
@brief   Create an iterator object for writing a series of blocks of data to a
CompressedVectorNode.
@param   [in] sbufs         Vector of memory buffers that will hold data to be
written to a CompressedVectorNode.
@details
See CompressedVectorWriter::write(std::vector<SourceDestBuffer>&, unsigned) for
discussion about restrictions on @a sbufs.

The pathNames in the @a sbufs must match one-to-one with the terminal nodes
(i.e. nodes that can have no children: IntegerNode, ScaledIntegerNode,
FloatNode, StringNode) in this CompressedVectorNode's prototype. It is an error
for two SourceDestBuffers in @a dbufs to identify the same terminal node in the
prototype.


It is an error to call this function if the CompressedVectorNode already has any
records (i.e. a CompressedVectorNode cannot be set twice).

@pre     @a sbufs can't be empty (i.e. sbufs.length() > 0).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable()).
@pre     The destination ImageFile can't have any readers or writers open
(destImageFile().readerCount()==0 && destImageFile().writerCount()==0)
@pre     This CompressedVectorNode must be attached (i.e. isAttached()).
@pre     This CompressedVectorNode must have no records (i.e. childCount() ==
0).
@return  A smart CompressedVectorWriter handle referencing the underlying
iterator object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_SET_TWICE
@throw   ::E57_ERROR_TOO_MANY_WRITERS
@throw   ::E57_ERROR_TOO_MANY_READERS
@throw   ::E57_ERROR_NODE_UNATTACHED
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_NO_BUFFER_FOR_ELEMENT
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorWriter, SourceDestBuffer, CompressedVectorNode::CompressedVectorNode,
CompressedVectorNode::prototype
*/
CompressedVectorWriter CompressedVectorNode::writer( std::vector<SourceDestBuffer> &sbufs )
{
   return CompressedVectorWriter( impl_->writer( sbufs ) );
}

/*!
@brief   Create an iterator object for reading a series of blocks of data from a
CompressedVectorNode.
@param   [in] dbufs     Vector of memory buffers that will receive data read
from a CompressedVectorNode.
@details
The pathNames in the @a dbufs must identify terminal nodes (i.e. node that can
have no children: IntegerNode, ScaledIntegerNode, FloatNode, StringNode) in this
CompressedVectorNode's prototype. It is an error for two SourceDestBuffers in @a
dbufs to identify the same terminal node in the prototype. It is not an error to
create a CompressedVectorReader for an empty CompressedVectorNode.

@pre     @a dbufs can't be empty
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The destination ImageFile can't have any writers open
(destImageFile().writerCount()==0)
@pre     This CompressedVectorNode must be attached (i.e. isAttached()).
@return  A smart CompressedVectorReader handle referencing the underlying
iterator object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_TOO_MANY_WRITERS
@throw   ::E57_ERROR_NODE_UNATTACHED
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_BAD_CV_HEADER
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorReader, SourceDestBuffer, CompressedVectorNode::CompressedVectorNode,
CompressedVectorNode::prototype
*/
CompressedVectorReader CompressedVectorNode::reader( const std::vector<SourceDestBuffer> &dbufs )
{
   return CompressedVectorReader( impl_->reader( dbufs ) );
}

//=====================================================================================
/*!
@class IntegerNode
@brief   An E57 element encoding an integer value.
@details
An IntegerNode is a terminal node (i.e. having no children) that holds an
integer value, and minimum/maximum bounds. Once the IntegerNode value and
attributes are set at creation, they may not be modified.

The minimum attribute may be a number in the interval [-2^63, 2^63).
The maximum attribute may be a number in the interval [minimum, 2^63).
The value may be a number in the interval [minimum, maximum].

See Node class discussion for discussion of the common functions that
StructureNode supports.

@section IntegerNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin IntegerNode::checkInvariant
@skip checkInvariant(
@until end IntegerNode::checkInvariant

@see     Node, CompressedVector
*/

/*!
@brief   Create an E57 element for storing a integer value.
@param   [in] destImageFile   The ImageFile where the new node will eventually
be stored.
@param   [in] value     The integer value of the element.
@param   [in] minimum   The smallest value that the element may take.
@param   [in] maximum   The largest value that the element may take.
@details
An IntegerNode stores an integer value, and a lower and upper bound.
The IntegerNode class corresponds to the ASTM E57 standard Integer element.
See the class discussion at bottom of IntegerNode page for more details.

The @a destImageFile indicates which ImageFile the IntegerNode will eventually
be attached to. A node is attached to an ImageFile by adding it underneath the
predefined root of the ImageFile (gotten from ImageFile::root). It is not an
error to fail to attach the IntegerNode to the @a destImageFile. It is an error
to attempt to attach the IntegerNode to a different ImageFile.

@b Warning: it is an error to give an @a value outside the @a minimum / @a
maximum bounds, even if the IntegerNode is destined to be used in a
CompressedVectorNode prototype (where the @a value will be ignored). If the
IntegerNode is to be used in a prototype, it is recommended to specify a @a
value = 0 if 0 is within bounds, or a @a value = @a minimum if 0 is not within
bounds.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@pre     minimum <= value <= maximum
@return  A smart IntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerNode::value, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
IntegerNode::IntegerNode( ImageFile destImageFile, int64_t value, int64_t minimum, int64_t maximum ) :
   impl_( new IntegerNodeImpl( destImageFile.impl(), value, minimum, maximum ) )
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool IntegerNode::isRoot() const
{
   return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node IntegerNode::parent() const
{
   return Node( impl_->parent() );
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring IntegerNode::pathName() const
{
   return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring IntegerNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node
//! when it was created.
//! @copydetails Node::destImageFile()
ImageFile IntegerNode::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool IntegerNode::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Get integer value stored.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  integer value stored.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerNode::minimum, IntegerNode::maximum
*/
int64_t IntegerNode::value() const
{
   return impl_->value();
}

/*!
@brief   Get the declared minimum that the value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared minimum that the value may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerCreate.cpp example, IntegerNode::value
*/
int64_t IntegerNode::minimum() const
{
   return impl_->minimum();
}

/*!
@brief   Get the declared maximum that the value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared maximum that the value may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerNode::value
*/
int64_t IntegerNode::maximum() const
{
   return impl_->maximum();
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void IntegerNode::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void IntegerNode::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Upcast a IntegerNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it
for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     explanation in Node, Node::type(), IntegerNode(const Node&)
*/
IntegerNode::operator Node() const
{
   /// Upcast from shared_ptr<IntegerNodeImpl> to SharedNodeImplPtr and
   /// construct a Node object
   return Node( impl_ );
}

/*!
@brief   Downcast a generic Node handle to a IntegerNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying IntegerNode, otherwise an
exception is thrown. In designs that need to avoid the exception, use
Node::type() to determine the actual type of the @a n before downcasting. This
function must be explicitly called (c++ compiler cannot insert it
automatically).
@return  A smart IntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     Node::type(), IntegerNode::operator Node()
*/
IntegerNode::IntegerNode( const Node &n )
{
   if ( n.type() != E57_INTEGER )
   {
      throw E57_EXCEPTION2( E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString( n.type() ) );
   }

   /// Set our shared_ptr to the downcast shared_ptr
   impl_ = std::static_pointer_cast<IntegerNodeImpl>( n.impl() );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
IntegerNode::IntegerNode( std::shared_ptr<IntegerNodeImpl> ni ) : impl_( ni )
{
}
//! @endcond

//=====================================================================================
/*!
@class ScaledIntegerNode
@brief   An E57 element encoding a fixed point number.
@details
An ScaledIntegerNode is a terminal node (i.e. having no children) that holds a
fixed point number encoded by an integer @c rawValue, a double precision
floating point @c scale, an double precision floating point @c offset, and
integer minimum/maximum bounds.

The @c minimum attribute may be a number in the interval [-2^63, 2^63).
The @c maximum attribute may be a number in the interval [minimum, 2^63).
The @c rawValue may be a number in the interval [minimum, maximum].
The @c scaledValue is a calculated double precision floating point number
derived from: scaledValue = rawValue*scale + offset.

See Node class discussion for discussion of the common functions that
StructureNode supports.

@section ScaledIntegerNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin ScaledIntegerNode::checkInvariant
@skip checkInvariant(
@until end ScaledIntegerNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an E57 element for storing a fixed point number.
@param   [in] destImageFile   The ImageFile where the new node will eventually
be stored.
@param   [in] rawValue  The raw integer value of the element.
@param   [in] minimum   The smallest rawValue that the element may take.
@param   [in] maximum   The largest rawValue that the element may take.
@param   [in] scale     The scaling factor used to compute scaledValue from
rawValue.
@param   [in] offset    The offset factor used to compute scaledValue from
rawValue.
@details
An ScaledIntegerNode stores an integer value, a lower and upper bound, and two
conversion factors. The ScaledIntegerNode class corresponds to the ASTM E57
standard ScaledInteger element. See the class discussion at bottom of
ScaledIntegerNode page for more details.

The @a destImageFile indicates which ImageFile the ScaledIntegerNode will
eventually be attached to. A node is attached to an ImageFile by adding it
underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the ScaledIntegerNode to the @a
destImageFile. It is an error to attempt to attach the ScaledIntegerNode to a
different ImageFile.

@b Warning: it is an error to give an @a rawValue outside the @a minimum / @a
maximum bounds, even if the ScaledIntegerNode is destined to be used in a
CompressedVectorNode prototype (where the @a rawValue will be ignored). If the
ScaledIntegerNode is to be used in a prototype, it is recommended to specify a
@a rawValue = 0 if 0 is within bounds, or a @a rawValue = @a minimum if 0 is not
within bounds.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@pre     minimum <= rawValue <= maximum
@pre     scale != 0
@return  A smart ScaledIntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::rawValue, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
ScaledIntegerNode::ScaledIntegerNode( ImageFile destImageFile, int64_t rawValue, int64_t minimum, int64_t maximum,
                                      double scale, double offset ) :
   impl_( new ScaledIntegerNodeImpl( destImageFile.impl(), rawValue, minimum, maximum, scale, offset ) )
{
}
ScaledIntegerNode::ScaledIntegerNode( ImageFile destImageFile, int rawValue, int64_t minimum, int64_t maximum,
                                      double scale, double offset ) :
   impl_( new ScaledIntegerNodeImpl( destImageFile.impl(), static_cast<int64_t>( rawValue ), minimum, maximum, scale,
                                     offset ) )
{
}
ScaledIntegerNode::ScaledIntegerNode( ImageFile destImageFile, int rawValue, int minimum, int maximum, double scale,
                                      double offset ) :
   impl_( new ScaledIntegerNodeImpl( destImageFile.impl(), static_cast<int64_t>( rawValue ),
                                     static_cast<int64_t>( minimum ), static_cast<int64_t>( maximum ), scale, offset ) )
{
}
/*!
@brief   This second constructor create an E57 element for storing a fixed point
number but does the scaling for you.
@param   [in] destImageFile   The ImageFile where the new node will eventually
be stored.
@param   [in] scaledValue     The scaled integer value of the element.
@param   [in] scaledMinimum   The smallest scaledValue that the element may
take.
@param   [in] scaledMaximum   The largest scaledValue that the element may take.
@param   [in] scale     The scaling factor used to compute scaledValue from
rawValue.
@param   [in] offset    The offset factor used to compute scaledValue from
rawValue.
@details
An ScaledIntegerNode stores an integer value, a lower and upper bound, and two
conversion factors. This ScaledIntegerNode constructor calculates the rawValue,
minimum, and maximum by doing the floor((scaledValue - offset)/scale + .5) on
each scaled parameters.
@b Warning: it is an error to give an @a rawValue outside the @a minimum / @a
maximum bounds, even if the ScaledIntegerNode is destined to be used in a
CompressedVectorNode prototype (where the @a rawValue will be ignored). If the
ScaledIntegerNode is to be used in a prototype, it is recommended to specify a
@a rawValue = 0 if 0 is within bounds, or a @a rawValue = @a minimum if 0 is not
within bounds.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@pre     scaledMinimum <= scaledValue <= scaledMaximum
@pre     scale != 0
@return  A smart ScaledIntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::scaledValue, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
ScaledIntegerNode::ScaledIntegerNode( ImageFile destImageFile, double scaledValue, double scaledMinimum,
                                      double scaledMaximum, double scale, double offset ) :
   impl_( new ScaledIntegerNodeImpl( destImageFile.impl(), scaledValue, scaledMinimum, scaledMaximum, scale, offset ) )
{
}
//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool ScaledIntegerNode::isRoot() const
{
   return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node ScaledIntegerNode::parent() const
{
   return Node( impl_->parent() );
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring ScaledIntegerNode::pathName() const
{
   return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring ScaledIntegerNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node
//! when it was created.
//! @copydetails Node::destImageFile()
ImageFile ScaledIntegerNode::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool ScaledIntegerNode::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Get raw unscaled integer value of element.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The raw unscaled integer value stored.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::scaledValue, ScaledIntegerNode::minimum, ScaledIntegerNode::maximum
*/
int64_t ScaledIntegerNode::rawValue() const
{
   return impl_->rawValue();
}

/*!
@brief   Get scaled value of element.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The scaled value (rawValue*scale + offset) calculated from the rawValue
stored.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::rawValue
*/
double ScaledIntegerNode::scaledValue() const
{
   return impl_->scaledValue();
}

/*!
@brief   Get the declared minimum that the raw value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared minimum that the rawValue may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::maximum, ScaledIntegerNode::rawValue
*/
int64_t ScaledIntegerNode::minimum() const
{
   return impl_->minimum();
}
/*!
@brief   Get the declared scaled minimum that the scaled value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared minimum that the rawValue may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::scaledMaximum, ScaledIntegerNode::scaledValue
*/
double ScaledIntegerNode::scaledMinimum() const
{
   return impl_->scaledMinimum();
}
/*!
@brief   Get the declared maximum that the raw value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared maximum that the rawValue may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::minimum, ScaledIntegerNode::rawValue
*/
int64_t ScaledIntegerNode::maximum() const
{
   return impl_->maximum();
}
/*!
@brief   Get the declared scaled maximum that the scaled value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared maximum that the rawValue may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::scaledMinimum, ScaledIntegerNode::scaledValue
*/
double ScaledIntegerNode::scaledMaximum() const // Added by SC
{
   return impl_->scaledMaximum();
}
/*!
@brief   Get declared scaling factor.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The scaling factor used to compute scaledValue from rawValue.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::scaledValue
*/
double ScaledIntegerNode::scale() const
{
   return impl_->scale();
}

/*!
@brief   Get declared offset.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The offset used to compute scaledValue from rawValue.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerNode::scaledValue
*/
double ScaledIntegerNode::offset() const
{
   return impl_->offset();
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void ScaledIntegerNode::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void ScaledIntegerNode::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Upcast a ScaledIntegerNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it
for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     Explanation in Node, Node::type(), ScaledIntegerNode(const Node&)
*/
ScaledIntegerNode::operator Node() const
{
   /// Upcast from shared_ptr<ScaledIntegerNodeImpl> to SharedNodeImplPtr and
   /// construct a Node object
   return Node( impl_ );
}

/*!
@brief   Downcast a generic Node handle to an ScaledIntegerNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying ScaledIntegerNode, otherwise
an exception is thrown. In designs that need to avoid the exception, use
Node::type() to determine the actual type of the @a n before downcasting. This
function must be explicitly called (c++ compiler cannot insert it
automatically).
@return  A smart ScaledIntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     Node::type(), ScaledIntegerNode::operator, Node()
*/
ScaledIntegerNode::ScaledIntegerNode( const Node &n )
{
   if ( n.type() != E57_SCALED_INTEGER )
   {
      throw E57_EXCEPTION2( E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString( n.type() ) );
   }

   /// Set our shared_ptr to the downcast shared_ptr
   impl_ = std::static_pointer_cast<ScaledIntegerNodeImpl>( n.impl() );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
ScaledIntegerNode::ScaledIntegerNode( std::shared_ptr<ScaledIntegerNodeImpl> ni ) : impl_( ni )
{
}
//! @endcond

//=====================================================================================
/*!
@class FloatNode
@brief   An E57 element encoding a single or double precision IEEE floating
point number.
@details
An FloatNode is a terminal node (i.e. having no children) that holds an IEEE
floating point value, and minimum/maximum bounds. The precision of the floating
point value and attributes may be either single or double precision. Once the
FloatNode value and attributes are set at creation, they may not be modified.

If the precision option of the FloatNode is E57_SINGLE:
The minimum attribute may be a number in the interval
[-3.402823466e+38, 3.402823466e+38]. The maximum attribute may be a number in
the interval [maximum, 3.402823466e+38]. The value may be a number in the
interval [minimum, maximum].

If the precision option of the FloatNode is E57_DOUBLE:
The minimum attribute may be a number in the interval
[-1.7976931348623158e+308, 1.7976931348623158e+308]. The maximum attribute may
be a number in the interval [maximum, 1.7976931348623158e+308]. The value may be
a number in the interval [minimum, maximum].

See Node class discussion for discussion of the common functions that
StructureNode supports.

@section FloatNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin FloatNode::checkInvariant
@skip checkInvariant(
@until end FloatNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an E57 element for storing an double precision IEEE floating
point number.
@param   [in] destImageFile   The ImageFile where the new node will eventually
be stored.
@param   [in] value     The double precision IEEE floating point value of the
element.
@param   [in] precision The precision of IEEE floating point to use. May be
E57_SINGLE or E57_DOUBLE.
@param   [in] minimum   The smallest value that the value may take.
@param   [in] maximum   The largest value that the value may take.
@details
An FloatNode stores an IEEE floating point number and a lower and upper bound.
The FloatNode class corresponds to the ASTM E57 standard Float element.
See the class discussion at bottom of FloatNode page for more details.

The @a destImageFile indicates which ImageFile the FloatNode will eventually be
attached to. A node is attached to an ImageFile by adding it underneath the
predefined root of the ImageFile (gotten from ImageFile::root). It is not an
error to fail to attach the FloatNode to the @a destImageFile. It is an error to
attempt to attach the FloatNode to a different ImageFile.

There is only one FloatNode constructor that handles both E57_SINGLE and
E57_DOUBLE precision cases. If @a precision = E57_SINGLE, then the object will
silently round the double precision @a value to the nearest representable single
precision value. In this case, the lower bits will be lost, and if the value is
outside the representable range of a single precision number, the exponent may
be changed. The same is true for the @a minimum and @a maximum arguments.

@b Warning: it is an error to give an @a value outside the @a minimum / @a
maximum bounds, even if the FloatNode is destined to be used in a
CompressedVectorNode prototype (where the @a value will be ignored). If the
FloatNode is to be used in a prototype, it is recommended to specify a @a value
= 0 if 0 is within bounds, or a @a value = @a minimum if 0 is not within bounds.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@pre     minimum <= value <= maximum
@return  A smart FloatNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatPrecision, FloatNode::value, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
FloatNode::FloatNode( ImageFile destImageFile, double value, FloatPrecision precision, double minimum,
                      double maximum ) :
   impl_( new FloatNodeImpl( destImageFile.impl(), value, precision, minimum, maximum ) )
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool FloatNode::isRoot() const
{
   return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node FloatNode::parent() const
{
   return Node( impl_->parent() );
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring FloatNode::pathName() const
{
   return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring FloatNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node
//! when it was created.
//! @copydetails Node::destImageFile()
ImageFile FloatNode::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool FloatNode::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Get IEEE floating point value stored.
@details
If precision is E57_SINGLE, the single precision value is returned as a double.
If precision is E57_DOUBLE, the double precision value is returned as a double.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The IEEE floating point value stored, represented as a double.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatNode::minimum, FloatNode::maximum
*/
double FloatNode::value() const
{
   return impl_->value();
}

/*!
@brief   Get declared precision of the floating point number.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared precision of the floating point number, either
::E57_SINGLE or ::E57_DOUBLE.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatPrecision
*/
FloatPrecision FloatNode::precision() const
{
   return impl_->precision();
}

/*!
@brief   Get the declared minimum that the value may take.
@details
If precision is E57_SINGLE, the single precision minimum is returned as a
double. If precision is E57_DOUBLE, the double precision minimum is returned as
a double.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared minimum that the value may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatNode::maximum, FloatNode::value
*/
double FloatNode::minimum() const
{
   return impl_->minimum();
}

/*!
@brief   Get the declared maximum that the value may take.
@details
If precision is E57_SINGLE, the single precision maximum is returned as a
double. If precision is E57_DOUBLE, the double precision maximum is returned as
a double.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared maximum that the value may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatNode::minimum, FloatNode::value
*/
double FloatNode::maximum() const
{
   return impl_->maximum();
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void FloatNode::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void FloatNode::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Upcast a FloatNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it
for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     Explanation in Node, Node::type()
*/
FloatNode::operator Node() const
{
   /// Upcast from shared_ptr<FloatNodeImpl> to SharedNodeImplPtr and construct
   /// a Node object
   return Node( impl_ );
}

/*!
@brief   Downcast a generic Node handle to a FloatNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying FloatNode, otherwise an
exception is thrown. In designs that need to avoid the exception, use
Node::type() to determine the actual type of the @a n before downcasting. This
function must be explicitly called (c++ compiler cannot insert it
automatically).
@return  A smart FloatNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     Node::type(), FloatNode::operator Node()
*/
FloatNode::FloatNode( const Node &n )
{
   if ( n.type() != E57_FLOAT )
   {
      throw E57_EXCEPTION2( E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString( n.type() ) );
   }

   /// Set our shared_ptr to the downcast shared_ptr
   impl_ = std::static_pointer_cast<FloatNodeImpl>( n.impl() );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
FloatNode::FloatNode( std::shared_ptr<FloatNodeImpl> ni ) : impl_( ni )
{
}
//! @endcond

//=====================================================================================
/*!
@class StringNode
@brief   An E57 element encoding a Unicode character string value.
@details
A StringNode is a terminal node (i.e. having no children) that holds an Unicode
character string encoded in UTF-8. Once the StringNode value is set at creation,
it may not be modified.

See Node class discussion for discussion of the common functions that
StructureNode supports.

@section StringNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin StringNode::checkInvariant
@skip checkInvariant(
@until end StringNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an element storing a Unicode character string.
@param   [in] destImageFile The ImageFile where the new node will eventually be
stored.
@param   [in] value         The Unicode character string value of the element,
in UTF-8 encoding.
@details
The StringNode class corresponds to the ASTM E57 standard String element.
See the class discussion at bottom of StringNode page for more details.

The @a destImageFile indicates which ImageFile the StringNode will eventually be
attached to. A node is attached to an ImageFile by adding it underneath the
predefined root of the ImageFile (gotten from ImageFile::root). It is not an
error to fail to attach the StringNode to the @a destImageFile. It is an error
to attempt to attach the StringNode to a different ImageFile.

If the StringNode is to be used in a CompressedVectorNode prototype, it is
recommended to specify a
@a value = "" (the default value).
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@return  A smart StringNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StringNode::value, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
StringNode::StringNode( ImageFile destImageFile, const ustring &value ) :
   impl_( new StringNodeImpl( destImageFile.impl(), value ) )
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool StringNode::isRoot() const
{
   return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node StringNode::parent() const
{
   return Node( impl_->parent() );
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring StringNode::pathName() const
{
   return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring StringNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node
//! when it was created.
//! @copydetails Node::destImageFile()
ImageFile StringNode::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool StringNode::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Get Unicode character string value stored.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The Unicode character string value stored.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
*/
ustring StringNode::value() const
{
   return impl_->value();
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void StringNode::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void StringNode::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Upcast a StringNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it
for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     Explanation in Node, Node::type(), StringNode(const Node&)
*/
StringNode::operator Node() const
{
   /// Upcast from shared_ptr<StringNodeImpl> to SharedNodeImplPtr and construct
   /// a Node object
   return Node( impl_ );
}

/*!
@brief   Downcast a generic Node handle to a StringNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying StringNode, otherwise an
exception is thrown. In designs that need to avoid the exception, use
Node::type() to determine the actual type of the @a n before downcasting. This
function must be explicitly called (c++ compiler cannot insert it
automatically).
@return  A smart StringNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     Node::type(), StringNode::operator Node()
*/
StringNode::StringNode( const Node &n )
{
   if ( n.type() != E57_STRING )
   {
      throw E57_EXCEPTION2( E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString( n.type() ) );
   }

   /// Set our shared_ptr to the downcast shared_ptr
   impl_ = std::static_pointer_cast<StringNodeImpl>( n.impl() );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
StringNode::StringNode( std::shared_ptr<StringNodeImpl> ni ) : impl_( ni )
{
}
//! @endcond

//=====================================================================================
/*!
@class BlobNode
@brief   An E57 element encoding an fixed-length sequence of bytes with an
opaque format.
@details
A BlobNode is a terminal node (i.e. having no children) that holds an opaque,
fixed-length sequence of bytes. The number of bytes in the BlobNode is declared
at creation time. The content of the blob is stored within the E57 file in an
efficient binary format (but not compressed). The BlobNode cannot grow after it
is created. There is no ordering constraints on how content of a BlobNode may be
accessed (i.e. it is random access). BlobNodes in an ImageFile opened for
reading are read-only.

There are two categories of BlobNodes, distinguished by their usage: private
BlobNodes and public BlobNodes. In a private BlobNode, the format of its content
bytes is not published. This is useful for storing proprietary data that a
writer does not wish to share with all readers. Rather than put this information
in a separate file, the writer can embed the file inside the E57 file so it
cannot be lost.

In a public BlobNode, the format is published or follows some industry standard
format (e.g. JPEG). Rather than reinvent the wheel in applications that are
already well-served by an existing format standard, an E57 file writer can just
embed an existing file as an "attachment" in a BlobNode. The internal format of
a public BlobNode is not enforced by the Foundation API. It is recommended that
there be some mechanism for a reader to know ahead of time which format the
BlobNode content adheres to (either specified by a document, or encoded by some
scheme in the E57 Element tree).

The BlobNode is the one node type where the set-once policy is not strictly
enforced. It is possible to write the same byte location in a BlobNode several
times. However it is not recommended.

See Node class discussion for discussion of the common functions that
StructureNode supports.

@section BlobNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or, can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin BlobNode::checkInvariant
@skip checkInvariant(
@until end BlobNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an element for storing a sequence of bytes with an opaque
format.
@param   [in] destImageFile The ImageFile where the new node will eventually be
stored.
@param   [in] byteCount     The number of bytes reserved in the ImageFile for
holding the blob.
@details
The BlobNode class corresponds to the ASTM E57 standard Blob element.
See the class discussion at bottom of BlobNode page for more details.

The E57 Foundation Implementation may pre-allocate disk space in the ImageFile
to store the declared length of the blob. The disk must have enough free space
to store @a byteCount bytes of data. The data of a newly created BlobNode is
initialized to zero.

The @a destImageFile indicates which ImageFile the BlobNode will eventually be
attached to. A node is attached to an ImageFile by adding it underneath the
predefined root of the ImageFile (gotten from ImageFile::root). It is not an
error to fail to attach the BlobNode to the @a destImageFile. It is an error to
attempt to attach the BlobNode to a different ImageFile.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be
true).
@pre     The @a destImageFile must have been opened in write mode (i.e.
destImageFile.isWritable() must be true).
@pre     byteCount >= 0
@return  A smart BlobNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Node, BlobNode::read, BlobNode::write
*/
BlobNode::BlobNode( ImageFile destImageFile, int64_t byteCount ) :
   impl_( new BlobNodeImpl( destImageFile.impl(), byteCount ) )
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool BlobNode::isRoot() const
{
   return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node BlobNode::parent() const
{
   return Node( impl_->parent() );
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring BlobNode::pathName() const
{
   return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring BlobNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node
//! when it was created.
//! @copydetails Node::destImageFile()
ImageFile BlobNode::destImageFile() const
{
   return ImageFile( impl_->destImageFile() );
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool BlobNode::isAttached() const
{
   return impl_->isAttached();
}

/*!
@brief   Get size of blob declared when it was created.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared size of the blob when it was created.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     BlobNode::read, BlobNode::write
*/
int64_t BlobNode::byteCount() const
{
   return impl_->byteCount();
}

/*!
@brief   Read a buffer of bytes from a blob.
@param   [in] buf   A memory buffer to store bytes read from the blob.
@param   [in] start The index of the first byte in blob to read.
@param   [in] count The number of bytes to read.
@details
The memory buffer @a buf must be able to store at least @a count bytes.
The data is stored in a binary section of the ImageFile with checksum
protection, so undetected corruption is very unlikely. It is an error to attempt
to read outside the declared size of the Blob. The format of the data read is
opaque (unspecified by the ASTM E57 data format standard). Since @a buf is a
byte buffer, byte ordering is irrelevant (it will come out in the same order
that it went in). There is no constraint on the ordering of reads. Any part of
the Blob data can be read zero or more times.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     buf != NULL
@pre     0 <= @a start < byteCount()
@pre     0 <= count
@pre     (@a start + @a count) < byteCount()
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     BlobNode::byteCount, BlobNode::write
*/
void BlobNode::read( uint8_t *buf, int64_t start, size_t count )
{
   impl_->read( buf, start, count );
}

/*!
@brief   Write a buffer of bytes to a blob.
@param   [in] buf   A memory buffer of bytes to write to the blob.
@param   [in] start The index of the first byte in blob to write to.
@param   [in] count The number of bytes to write.
@details
The memory buffer @a buf must store at least @a count bytes.
The data is stored in a binary section of the ImageFile with checksum
protection, so undetected corruption is very unlikely. It is an error to attempt
to write outside the declared size of the Blob. The format of the data written
is opaque (unspecified by the ASTM E57 data format standard). Since @a buf is a
byte buffer, byte ordering is irrelevant (it will come out in the same order
that it went in). There is no constraint on the ordering of writes. It is not an
error to write a portion of the BlobNode data more than once, or not at all.
Initially all the BlobNode data is zero, so if a portion is not written, it will
remain zero. The BlobNode is one of the two node types that must be attached to
the root of a write mode ImageFile before write operations can be performed (the
other type is CompressedVectorNode).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The associated destImageFile must have been opened in write mode (i.e.
destImageFile().isWritable()).
@pre     The BlobNode must be attached to an ImageFile (i.e. isAttached()).
@pre     buf != NULL
@pre     0 <= @a start < byteCount()
@pre     0 <= count
@pre     (@a start + @a count) < byteCount()
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_NODE_UNATTACHED
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_WRITE_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     BlobNode::byteCount, BlobNode::read
*/
void BlobNode::write( uint8_t *buf, int64_t start, size_t count )
{
   impl_->write( buf, start, count );
}

//! @brief   Diagnostic function to print internal state of object to output
//! stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void BlobNode::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void BlobNode::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Upcast a BlobNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it
for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     Explanation in Node, Node::type(), BlobNode(const Node&)
*/
BlobNode::operator Node() const
{
   /// Upcast from shared_ptr<StringNodeImpl> to SharedNodeImplPtr and construct
   /// a Node object
   return Node( impl_ );
}

/*!
@brief   Downcast a generic Node handle to a BlobNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying BlobNode, otherwise an
exception is thrown. In designs that need to avoid the exception, use
Node::type() to determine the actual type of the @a n before downcasting. This
function must be explicitly called (c++ compiler cannot insert it
automatically).
@return  A smart BlobNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     Node::type(), BlobNode::operator Node()
*/
BlobNode::BlobNode( const Node &n )
{
   if ( n.type() != E57_BLOB )
   {
      throw E57_EXCEPTION2( E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString( n.type() ) );
   }

   /// Set our shared_ptr to the downcast shared_ptr
   impl_ = std::static_pointer_cast<BlobNodeImpl>( n.impl() );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
BlobNode::BlobNode( ImageFile destImageFile, int64_t fileOffset, int64_t length ) :
   impl_( new BlobNodeImpl( destImageFile.impl(), fileOffset, length ) )
{
}

BlobNode::BlobNode( std::shared_ptr<BlobNodeImpl> ni ) : impl_( ni )
{
}
//! @endcond

//=====================================================================================
/*!
@class ImageFile
@brief   An ASTM E57 3D format file object.
@details
@section imagefile_ClassOverview Class overview
The ImageFile class represents the state of an ASTM E57 format data file.
An ImageFile may be created from an E57 file on the disk (read mode).
An new ImageFile may be created to write an E57 file to disk (write mode).

E57 files are organized in a tree structure.
Each ImageFile object has a predefined root node (of type StructureNode).
In a write mode ImageFile, the root node is initially empty.
In a read mode ImageFile, the root node is populated by the tree stored in the
.e57 file on disk.

@section imagefile_OpenClose The open/close state
An ImageFile object, opened in either mode (read/write), can be in one of two
states: open or closed. An ImageFile in the open state is ready to perform
transfers of data and to be interrogated. An ImageFile in the closed state
cannot perform any further transfers, and has very limited ability to be
interrogated. Note entering the closed state is different than destroying the
ImageFile object. An ImageFile object can still exist and be in the closed
state. When created, the ImageFile is initially open.

The ImageFile state can transition to the closed state in two ways.
The programmer can call ImageFile::close after all required processing has
completed. The programmer can call ImageFile::cancel if it is determined that
the ImageFile is no longer needed.

@section imagefile_Extensions Extensions

Basically in an E57 file, "extension = namespace + rules + meaning".
The "namespace" ensures that element names don't collide.
The "rules" may be written on paper, or partly codified in a computer grammar.
The "meaning" is a definition of what was measured, what the numbers in the file
mean.

Extensions are identified by URIs.
Extensions are not identified by prefixes.
Prefixes are a shorthand, used in a particular file, to make the element names
more palatable for humans. When thinking about a prefixed element name, in your
mind you should immediately substitute the URI for the prefix. For example,
think "http://www.example.com/DemoExtension:extra2" rather than "demo:extra2",
if the prefix "demo" is declared in the file to be a shorthand for the URI
"http://www.example.com/DemoExtension".

The rules are statements of: what is valid, what element names are possible,
what values are possible. The rules establish the answer to the following yes/no
question: "Is this extended E57 file valid?". The rules divide all possible
files into two sets: valid files and invalid files.

The "meanings" part of the above equation defines what the files in the first
set, the valid files, actually mean. This definition usually comes in the form
of documentation of the content of each new element in the format and how they
relate to the other elements.

An element name in an E57 file is a member of exactly one namespace (either the
default namespace defined in the ASTM standard, or an extension namespace).
Rules about the structure of an E57 extension (what element names can appear
where), are implicitly assumed only to govern the element names within the
namespace of the extension. Element names in other namespaces are unconstrained.
This is because a reader is required to ignore elements in namespaces that are
unfamiliar (to treat them as if they didn't exist). This enables a writer to
"tack on" new elements into pre-defined structures (e.g. structures defined in
the ASTM standard), without fear that it will confuse a reader that is only
familiar with the old format. This allows an extension designer to communicate
to two sets of readers: the old readers that will understand the information in
the old base format, and the new-fangled readers that will be able to read the
base format and the extra information stored in element names in the extended
namespace.

@section ImageFile_invariant Class Invariant
A class invariant is a list of statements about an object that are always true
before and after any operation on the object. An invariant is useful for testing
correct operation of an implementation. Statements in an invariant can involve
only externally visible state, or can refer to internal implementation-specific
state that is not visible to the API user. The following C++ code checks
externally visible state for consistency and throws an exception if the
invariant is violated:
@dontinclude E57Format.cpp
@skip begin ImageFile::checkInvariant
@skip checkInvariant(
@until end ImageFile::checkInvariant
*/

/*!
@brief   Open an ASTM E57 imaging data file for reading/writing.
@param   [in] fname File name to open.
Support of '\' as a directory separating character is system dependent.
For maximum portability, it is recommended that '/' be used as a directory
separator in file names. Special device file name support are implementation
dependent (e.g. "\\.\PhysicalDrive3" or
"/dev/hd3"). It is recommended that files that meet all of the requirements for
a legal ASTM E57 file format use the extension @c ".e57". It is recommended that
files that utilize the low-level E57 element data types, but do not have all the
required element names required by ASTM E57 file format standard use the file
extension @c "._e57".
@param   [in] mode Either "w" for writing or "r" for reading.
@param   [in] checksumPolicy The percentage of checksums we compute and verify
as an int. Clamped to 0-100.
@details

@par Write Mode
In write mode, the file cannot be already open.
A file with name given by @a fname is immediately created on the disk.
This file may grow as a result of operations on the ImageFile.
Which API functions write data to the file are implementation dependent.
Thus any API operation that stores data may fail as a result of insufficient
free disk space. Read API operations are legal for an ImageFile opened in write
mode.

@par Read Mode
Read mode files may be shared.
Write API operations are not legal for an ImageFile opened in read mode (i.e.
the ImageFile is read-only). There is no API support for appending data onto an
existing E57 data file.

@post    Resulting ImageFile is in @c open state if constructor succeeds (no
exception thrown).
@return  A smart ImageFile handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_OPEN_FAILED
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_WRITE_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_BAD_FILE_SIGNATURE
@throw   ::E57_ERROR_UNKNOWN_FILE_VERSION
@throw   ::E57_ERROR_BAD_FILE_LENGTH
@throw   ::E57_ERROR_XML_PARSER_INIT
@throw   ::E57_ERROR_XML_PARSER
@throw   ::E57_ERROR_BAD_XML_FORMAT
@throw   ::E57_ERROR_BAD_CONFIGURATION
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerNode, ScaledIntegerNode, FloatNode,
StringNode, BlobNode, StructureNode, VectorNode, CompressedVectorNode,
E57Exception, E57Utilities::E57Utilities
*/
ImageFile::ImageFile( const ustring &fname, const ustring &mode, ReadChecksumPolicy checksumPolicy ) :
   impl_( new ImageFileImpl( checksumPolicy ) )
{
   /// Do second phase of construction, now that ImageFile object is complete.
   impl_->construct2( fname, mode );
}

ImageFile::ImageFile( const char *input, const uint64_t size, ReadChecksumPolicy checksumPolicy ) :
   impl_( new ImageFileImpl( checksumPolicy ) )
{
   impl_->construct2( input, size );
}

/*!
@brief   Get the pre-established root StructureNode of the E57 ImageFile.
@details The root node of an ImageFile always exists and is always type
StructureNode. The root node is empty in a newly created write mode ImageFile.
@pre     This ImageFile must be open (i.e. isOpen()).
@return  A smart StructureNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureNode.
*/
StructureNode ImageFile::root() const
{
   return StructureNode( impl_->root() );
}

/*!
@brief   Complete any write operations on an ImageFile, and close the file on
the disk.
@details
Completes the writing of the state of the ImageFile to the disk.
Some API implementations may store significant portions of the state of the
ImageFile in memory. This state is moved into the disk file before it is closed.
Any errors in finishing the writing are reported by throwing an exception.
If an exception is thrown, depending on the error code, the ImageFile may enter
the closed state. If no exception is thrown, then the file on disk will be an
accurate representation of the ImageFile.

@b Warning: if the ImageFile::close function is not called, and the ImageFile
destructor is invoked with the ImageFile in the open state, the associated disk
file will be deleted and the ImageFile will @em not be saved to the disk (the
same outcome as calling ImageFile::cancel). The reason for this is that any
error conditions can't be reported from a destructor, so the user can't be
assured that the destruction/close completed successfully. It is strongly
recommended that this close function be called before the ImageFile is
destroyed.

It is not an error if ImageFile is already closed.
@post    ImageFile is in @c closed state.
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_WRITE_FAILED
@throw   ::E57_ERROR_CLOSE_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::cancel, ImageFile::isOpen
*/
void ImageFile::close()
{
   impl_->close();
}

/*!
@brief   Stop I/O operations and delete a partially written ImageFile on the
disk.
@details
If the ImageFile is write mode, the associated file on the disk is closed and
deleted, and the ImageFile goes to the closed state. If the ImageFile is read
mode, the behavior is same as calling ImageFile::close, but no exceptions are
thrown. It is not an error if ImageFile is already closed.
@post    ImageFile is in @c closed state.
@throw   No E57Exceptions.
@see     ImageFile::ImageFile, ImageFile::close, ImageFile::isOpen
*/
void ImageFile::cancel()
{
   impl_->cancel();
}

/*!
@brief   Test whether ImageFile is still open for accessing.
@post    No visible state is modified.
@return  true if ImageFile is in @c open state.
@throw   No E57Exceptions.
@see     ImageFile::ImageFile, ImageFile::close
*/
bool ImageFile::isOpen() const
{
   return impl_->isOpen();
}

/*!
@brief   Test whether ImageFile was opened in write mode.
@post    No visible state is modified.
@return  true if ImageFile was opened in write mode.
@throw   No E57Exceptions.
@see     ImageFile::ImageFile, ImageFile::isOpen
*/
bool ImageFile::isWritable() const
{
   return impl_->isWriter();
}

/*!
@brief   Get the file name the ImageFile was created with.
@post    No visible state is modified.
@return  The file name the ImageFile was created with.
@throw   No E57Exceptions.
@see     Cancel.cpp example, ImageFile::ImageFile
*/
ustring ImageFile::fileName() const
{
   return impl_->fileName();
}

/*!
@brief   Get current number of open CompressedVectorWriter objects writing to
ImageFile.
@details
CompressedVectorWriter objects that still exist, but are in the closed state
aren't counted. CompressedVectorWriter objects are created by the
CompressedVectorNode::writer function.
@pre     This ImageFile must be open (i.e. isOpen()).
@post    No visible state is modified.
@return  The current number of open CompressedVectorWriter objects writing to
ImageFile.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorNode::writer, CompressedVectorWriter
*/
int ImageFile::writerCount() const
{
   return impl_->writerCount();
}

/*!
@brief   Get current number of open CompressedVectorReader objects reading from
ImageFile.
@details
CompressedVectorReader objects that still exist, but are in the closed state
aren't counted. CompressedVectorReader objects are created by the
CompressedVectorNode::reader function.
@pre     This ImageFile must be open (i.e. isOpen()).
@post    No visible state is modified.
@return  The current number of open CompressedVectorReader objects reading from
ImageFile.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorNode::reader, CompressedVectorReader
*/
int ImageFile::readerCount() const
{
   return impl_->readerCount();
}

/*!
@brief   Declare the use of an E57 extension in an ImageFile being written.
@param   [in] prefix    The shorthand name of the extension to use in element
names.
@param   [in] uri       The Uniform Resource Identifier string to associate with
the prefix in the ImageFile.
@details
The (@a prefix, @a uri) pair is registered in the known extensions of the
ImageFile. Both @a prefix and @a uri must be unique in the ImageFile. It is not
legal to declare a URI associated with the default namespace (@a prefix = "").
It is not an error to declare a namespace and not use it in an element name.
It is an error to use a namespace prefix in an element name that is not declared
beforehand.

A writer is free to "hard code" the prefix names in the element name strings
that it uses (since it established the prefix declarations in the file). A
reader cannot assume that any given prefix is always mapped to the same URI or
vice versa. A reader might check an ImageFile, and if the prefixes aren't the
way it likes, the reader could give up.

A better scheme would be to lookup the URI that the reader is familiar with, and
store the prefix that the particular file uses in a variable. Then every time
the reader needs to form a prefixed element name, it can assemble the full
element name from the stored prefix variable and the constant documented base
name string. This is less convenient than using a single "hard coded" string
constant for an element name, but it is robust against any choice of prefix/URI
combination.

See the class discussion at bottom of ImageFile page for more details about
namespaces.
@pre     This ImageFile must be open (i.e. isOpen()).
@pre     ImageFile must have been opened in write mode (i.e. isWritable()).
@pre     prefix != ""
@pre     uri != ""
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_DUPLICATE_NAMESPACE_PREFIX
@throw   ::E57_ERROR_DUPLICATE_NAMESPACE_URI
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::extensionsCount, ImageFile::extensionsLookupPrefix, ImageFile::extensionsLookupUri
*/
void ImageFile::extensionsAdd( const ustring &prefix, const ustring &uri )
{
   impl_->extensionsAdd( prefix, uri );
}

/*!
@brief   Get URI associated with an E57 extension prefix in the ImageFile.
@param   [in] prefix    The shorthand name of the extension to look up.
@param   [out] uri      The URI that was associated with the given @a prefix.
@details
If @a prefix = "", then @a uri is set to the default namespace URI, and the
function returns true. if @a prefix is declared in the ImageFile, then @a uri is
set the corresponding URI, and the function returns true. It is an error if @a
prefix contains an illegal character combination for E57 namespace prefixes. It
is not an error if @a prefix is well-formed, but not defined in the ImageFile
(the function just returns false).
@pre     This ImageFile must be open (i.e. isOpen()).
@post    No visible state is modified.
@return  true if prefix is declared in the ImageFile.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::extensionsLookupUri
*/
bool ImageFile::extensionsLookupPrefix( const ustring &prefix, ustring &uri ) const
{
   return impl_->extensionsLookupPrefix( prefix, uri );
}

/*!
@brief   Get an E57 extension prefix associated with a URI in the ImageFile.
@param   [in] uri       The URI of the extension to look up.
@param   [out] prefix   The shorthand prefix that was associated with the given
@a uri.
@details
If @a uri is declared in the ImageFile, then @a prefix is set the corresponding
prefix, and the function returns true. It is an error if @a uri contains an
illegal character combination for E57 namespace URIs. It is not an error if @a
uri is well-formed, but not defined in the ImageFile (the function just returns
false).
@pre     This ImageFile must be open (i.e. isOpen()).
@pre     uri != ""
@post    No visible state is modified.
@return  true if URI is declared in the ImageFile.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::extensionsLookupPrefix
*/
bool ImageFile::extensionsLookupUri( const ustring &uri, ustring &prefix ) const
{
   return impl_->extensionsLookupUri( uri, prefix );
}

/*!
@brief   Get number of E57 extensions declared in the ImageFile.
@details
The default E57 namespace does not count as an extension.
@pre     This ImageFile must be open (i.e. isOpen()).
@post    No visible state is modified.
@return  The number of E57 extensions defined in the ImageFile.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::extensionsPrefix, ImageFile::extensionsUri
*/
size_t ImageFile::extensionsCount() const
{
   return impl_->extensionsCount();
}

/*!
@brief   Get an E57 extension prefix declared in an ImageFile by index.
@param   [in] index The index of the prefix to get, starting at 0.
@details
The order that the prefixes are stored in is not necessarily the same as the
order they were created. However the prefix order will correspond to the URI
order. The default E57 namespace is not counted as an extension.
@pre     This ImageFile must be open (i.e. isOpen()).
@pre     0 <= index < extensionsCount()
@post    No visible state is modified.
@return  The E57 extension prefix at the given index.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::extensionsCount, ImageFile::extensionsUri
*/
ustring ImageFile::extensionsPrefix( const size_t index ) const
{
   return impl_->extensionsPrefix( index );
}

/*!
@brief   Get an E57 extension URI declared in an ImageFile by index.
@param   [in] index The index of the URI to get, starting at 0.
@details
The order that the URIs are stored is not necessarily the same as the order they
were created. However the URI order will correspond to the prefix order. The
default E57 namespace is not counted as an extension.
@pre     This ImageFile must be open (i.e. isOpen()).
@pre     0 <= index < extensionsCount()
@post    No visible state is modified.
@return  The E57 extension URI at the given index.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::extensionsCount, ImageFile::extensionsPrefix
*/
ustring ImageFile::extensionsUri( const size_t index ) const
{
   return impl_->extensionsUri( index );
}

/*!
@brief   Test whether an E57 element name has an extension prefix.
@details
The element name has a prefix if the function
elementNameParse(elementName,prefix,dummy) would succeed, and returned prefix !=
"".
@param   [in] elementName   The string element name to test.
@post    No visible state is modified.
@return  True if the E57 element name has an extension prefix.
@throw   No E57Exceptions.
*/
bool ImageFile::isElementNameExtended( const ustring &elementName ) const
{
   return impl_->isElementNameExtended( elementName );
}

/*!
@brief   Parse element name into prefix and localPart substrings.
@param   [in] elementName   The string element name to parse into prefix and
local parts.
@param   [out] prefix       The prefix (if any) in the @a elementName.
@param   [out] localPart    The part of the element name after the prefix.
@details
A legal element name may be in prefixed (ID:ID) or unprefixed (ID) form,
where ID is a string whose first character is in {a-z,A-Z,_} followed by zero or
more characters in {a-z,A-Z,_,0-9,-,.}. If in prefixed form, the prefix does not
have to be declared in the ImageFile.
@post    No visible state is modified.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ImageFile::isElementNameExtended
*/
void ImageFile::elementNameParse( const ustring &elementName, ustring &prefix, ustring &localPart ) const
{
   impl_->elementNameParse( elementName, prefix, localPart );
}

/*!
@brief   Diagnostic function to print internal state of object to output stream
in an indented format.
@copydetails Node::dump()
*/
#ifdef E57_DEBUG
void ImageFile::dump( int indent, std::ostream &os ) const
{
   impl_->dump( indent, os );
}
#else
void ImageFile::dump( int indent, std::ostream &os ) const
{
}
#endif

/*!
@brief   Test if two ImageFile handles refer to the same underlying ImageFile
@param   [in] imf2        The ImageFile to compare this ImageFile with
@post    No visible object state is modified.
@return  @c true if ImageFile handles refer to the same underlying ImageFile.
@throw   No E57Exceptions
*/
bool ImageFile::operator==( ImageFile imf2 ) const
{
   return ( impl_ == imf2.impl_ );
}

/*!
@brief   Test if two ImageFile handles refer to different underlying ImageFile
@param   [in] imf2        The ImageFile to compare this ImageFile with
@post    No visible object state is modified.
@return  @c true if ImageFile handles refer to different underlying ImageFiles.
@throw   No E57Exceptions
*/
bool ImageFile::operator!=( ImageFile imf2 ) const
{
   return ( impl_ != imf2.impl_ );
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't
//! documented.
ImageFile::ImageFile( ImageFileImplSharedPtr imfi ) : impl_( imfi )
{
}
//! @endcond
