Difference between revisions of "V4 Design: epicsTypes"

From EPICSWIKI
m (switch capacity and elementSize arguments for EpicsArray constructors)
 
(15 intermediate revisions by 2 users not shown)
Line 1: Line 1:
= EPICS: epicsTypes =
__TOC__


May 31 2005
Marty Kraimer and Andrew Johnson


<center>
June 14, 2005
 
= Overview =
 
This document describes the fundamental C++ data types which will be used internally by iocCore and hence must be supported in EPICS Version 4.0.  We do not cover composite (<tt>struct</tt>) types here, as a generic interface to such types requires introspection information which is only provided at a higher level.


== Overview ==
All data that is sent to or received from any EPICS record will be composed out of the fundamental data types described here.


</center>
The data types are:


This document describes the C++ definitions for storing
* Primitive Types
data that can be accessed without pre-complied code, i.e.
** epicsBoolean
it has self describing features.
** epicsOctet
** epicsInt16
** epicsInt32
** epicsInt64
** epicsFloat32
** epicsFloat64
* Aggregate Types
** EpicsString
** EpicsArray
** EpicsMDArray
** EpicsEnum


== Comments on Type Safety ==


epicsTypes can be used by any code that stores data accessable via V4 Channel Access.
<tt>EpicsArray</tt> and <tt>EpicsMDArray</tt> as presented below are not type-safe, in that they take and return pointers to an <tt>epicsOctet</tt> which the user must cast to the appropriate element type. They also will not be able to default-construct or destruct their array elements, so cannot safely be used for complex C++ data types. This will not be an issue when creating an array of strings as long as we ensure that a buffer is zero-filled at allocation.
Some examples are:
# IOC records - Most data accessable from outside record support is stored as an epicsType.
# Channel Access Gateway - Can be used to store and transfer data.
# Channel Access Clients - Can be used to get/receive data from CA server.


epicsTypes is not a replacement for dataAccess.
We could make versions of these classes that are templated on the element type, which default-construct and destruct their elements and even use the element type's assignment operator for copying operations. However since Version 4.0 record types can contain array fields whose data types are not known until the database is loaded at runtime, we will still need to provide a non type-safe version of the array types. For the moment we're going to work with these definitions.
Standard support can be provided to access, via dataAccess, epicsType data.
For example standard support can be provided to move IOC record data between
record instances and a Channel Access server.


<tt>epicsType</tt> is an <tt>enum</tt> that defines the following:
== Locking Issues ==
* epicsUnknownT - Type is unknown
* epicsBooleanT, ..., epicsFloat64T - C++ primitive types
* epicsStringT - EpicsString which contains a UTF-8 Encoded Character String
* epicsArrayT - EpicsArray which describes type and storage for a one dim array. The element type can be any epicsType.
* epicsStructT - EpicsStruct which describes and provides storage for a set of fields each of some epicsType
* epicsMDArrayT - EpicsMDArray which describes type and provides storage for a multidimensional array. The element type can be a primitive type or epicsStringT.


These fundamental EPICS datatypes do not provide facilities for preventing
simultaneous access by multiple threads, which is especially important for the
aggregate types. For example the class definitions for <tt>EpicsString</tt>,
<tt>EpicsArray</tt> and <tt>EpicsMDArray</tt> all have methods which return
pointers into their internal data buffers, for efficiency reasons. In order to
make the use of these types thread-safe, suitable access rules and mutual
exclusion protection must be established at some higher level. The particular
locking scheme used must be appropriate to the application, so cannot be defined
in this document.


The actual types associated with the <tt>epicsType</tt>s are:
   
    typedef bool              epicsBoolean;
    typedef char              epicsOctet;
    typedef short              epicsInt16;
    typedef unsigned short    epicsUInt16;
    typedef int                epicsInt32;
    typedef unsigned int      epicsUInt32;
    typedef long long          epicsInt64;
    typedef unsigned long long epicsUInt64;
    typedef float              epicsFloat32;
    typedef double            epicsFloat64;


    /*EpicsString holds UTF-8 characters*/
----
    class EpicsString  {
    public:
        EpicsUTF_8Buffer    *pbuffer;
    };
    class EpicsArray {
    public:
        epicsType        type;
        EpicsArrayBuffer *pbuffer;
    };
    class EpicsStruct{
    public:
        EpicsStructDef *pstructDef;
        void    *pstorage;
    };
    class EpicsMDArray {
    public:
        epicsType type;
        EpicsMDArrayDescription *pdescription;
        EpicsMDArrayBuffer *pbuffer;
    };


The types are :
* <tt>epicsBoolean</tt>, ... <tt>epicsFloat64</tt> each has an associated C++ fundamental type.  An instance of a primitive type has only storage associated with it.
* <b><tt>epicsOctet</tt></b> is an 8 bit byte that just contains data.  It is not used as an integer type.
* <tt>EpicsString</tt> is a UTF-8 encoded character string. Multiple string buffer management implementation are supported. Both contiguous and segmented buffers are available.
* <tt>EpicsArray</tt> is an array of any type except unknown. Multiple array buffer management implementations are supported. Both contiguous and segmented implementations are available.
* <tt>EpicsStruct</tt> is a container with fields each of which is any epicsType
* <tt>EpicsMDArray</tt> describes a multi-dimensional array of a primitive or string type.


<center>
= Primitive Types =
== epicsTypes ==
</center>


<tt>epicsTypes.h</tt> contains the following:
<tt>epicsTypes.h</tt> contains the following typedefs, which are called the primitive types:


    /* The following may require OSD definitions*/
     typedef bool              epicsBoolean;
     typedef bool              epicsBoolean;
     typedef char              epicsOctet;
     typedef char              epicsOctet;
     typedef short              epicsInt16;
     typedef short              epicsInt16;
    typedef unsigned short    epicsUInt16;
     typedef int                epicsInt32;
     typedef int                epicsInt32;
    typedef unsigned int      epicsUInt32;
     typedef long long          epicsInt64;
     typedef long long          epicsInt64;
    typedef unsigned long long epicsUInt64;
     typedef float              epicsFloat32;
     typedef float              epicsFloat32;
     typedef double            epicsFloat64;
     typedef double            epicsFloat64;
   
    enum epicsType {
        epicsUnknownT,
        epicsBooleanT,    // epicsBoolean
        epicsOctetT,      // epicsOctet
        epicsInt16T,      // epicsInt16
        epicsUInt16T,      // epicsUInt16
        epicsInt32T,      // epicsInt32
        epicsUInt32T,      // epicsUInt32
        epicsInt64T,      // epicsInt64
        epicsUInt64T,      // epicsUInt64
        epicsFloat32T,    // epicsFloat32
        epicsFloat64T,    // epicsFloat64
        epicsStringT,      // EpicsString
        epicsArrayT,      // EpicsArray
        epicsStructT,      // EpicsStruct
        epicsMDArrayT      // EpicsMDArray
    };


It may be necessary to provide operating system dependent definitions for some of the types. For example on some architectures an <tt>epicsInt64</tt> may have to be defined as a <tt>long</tt> rather than a <tt>long long</tt>.


epicsTypes provides classes for describing data that can be introspected
and can be passed between different platforms.
All data that is sent to or received from EPICS records will be composed
of epicsTypes.


The types <tt>epicsBooleanT</tt>, ..., <tt>epicsFloat64T</tt> all map to a C++
----
standard type.
It may be necessary to provide operating system dependent definitions for some of the types. For example on some architectures a epicsInt64 may have to be defined as a <tt>long</tt> rather than a <tt>long long</tt>.


The unsigned integer types should only be used for data that represents
masks. If they are used for representing large numbers, e.g. an epicsUInt16 that
is greater than 32767, then Java clients/servers will not work properly.


<b>Java Notes</b>
= Aggregate Types =
* epicsOctet is a Java <tt>byte</tt>
* Java does not support unsigned integers. It should, however, accept each unsigned type as the corresponding signed type.


<tt>epicsUnknownT</tt> is provided in case something expected to produce
The aggregate types must have a fixed size but still be able to manage memory in various ways which will be different for different purposes. Therefor they are all based on a standard interface to a memory buffer which can have different implementations for different memory management requirements.
an epicsType fails.


The types <tt>epicsStringT</tt>, <tt>epicsArrayT</tt>, <tt>epicsStructT</tt>,
<center>
and <tt>epicsMDArrayT</tt> are designed so that the data they contain
== EpicsBuffer ==
can be described, introspected, and passed over a network.
</center>
Each is described in the following sections.


<center>
<tt>epicsBuffer.h</tt> defines a generic interface <tt>EpicsBuffer</tt> to a buffer of octet data, for which there may be several different implementations.
== Global comments about non-primitive types ==
</center>


=== Method Expose ===
A buffer factory <tt>EpicsBufferFactory</tt> provides a central registry of <tt>EpicsBufferCreator</tt> objects and uses these to create instances of a particular buffer type on demand.


The class definitions for non-primitive types,
    class EpicsBuffer { // interface
i.e. <tt>epicsStringT</tt>, <tt>epicsArrayT</tt>, <tt>epicsStructT</tt>,
    protected:
and <tt>epicsMDArrayT</tt>, all provide
        virtual ~EpicsBuffer() = 0;
a method <tt>expose</tt> which returns the address of data.
    public:
In order to make expose safe, some rules must be established.
        virtual EpicsBufferCreator *creator() const = 0;
The application that uses epicsTypes must set the rules since
        virtual void reserve(epicsInt32 capacity) = 0;
a generic solution may not be desirable.
        virtual epicsInt32 capacity() const = 0;
        virtual void resize(epicsInt32 newsize) = 0;
        virtual epicsInt32 size() const = 0;
        virtual epicsInt32 maxSize() const = 0;
        virtual bool mutable() const = 0;
        virtual bool isEqual(const EpicsBuffer &cmp) const = 0;
        virtual bool isEqual(epicsInt32 offset, epicsInt32 len,
                              const epicsOctet *pdata) const = 0;
        virtual void expose(epicsInt32 offset, epicsInt32 &len,
                            epicsOctet *&pdata) = 0;
        virtual void expose(epicsInt32 offset, epicsInt32 &len,
                            const epicsOctet *&pdata) const = 0;
   
    friend class EpicsBufferCreator;
    };


As an example of rules, a lock can be associated with each object
An <tt>EpicsBuffer</tt> is a container for epicsOctet data values, and may be used to store things such as character strings containing UTF-8 characters or arrays of some other data type.
that supports expose.
Code can only call expose and access the exposed data while it holds the lock.


The rules are <b>not</b> specified by epicsTypes since
Multiple implementations of <tt>EpicsBuffer</tt> are needed with different characteristics, but all buffers will be accessed using the same <tt>EpicsBuffer</tt> interface. At least the following buffer types will be provided:
they are application dependent,


=== Buffer Implementations ===
* readonly - A contiguous array that already contains the desired data is provided at buffer initialization time, and cannot be modified. This buffer is for use with literal character strings, and probably won't be registered with the <tt>EpicsBufferFactory</tt>.
* contiguous - The data is stored in a contiguous array of octets.
* segmented - The data is stored in fixed-size chunks. This form should be used for buffers that are frequently created and released, or which often need to change in size.


The class definitions for
Additional implementations may be provided for other purposes such as managing network buffers. There might be a need for a contiguous zero-terminated buffer type for code that regularly talks to C string handling routines.
<tt>epicsStringT</tt>, <tt>epicsArrayT</tt>, and <tt>epicsMDArrayT</tt>,
all manage storage for the associated type.
Multiple implementations are provided.
Implementations are provided for contiguous and segmented storage.


The contiguous implementation is appropriate for a string that is allocated
In addition to storing the data, the buffer keeps the following information:
at initialization and is only rarely modified.
At least two contiguous implementations are provided: mutable and non-mutable.
The mutable implementation allows the storage to be modified.
The non-mutable implementation does not allow the storage to be changed
after it has been given an initial value.


The segmented implementation is appropriate for storage that is frequently
;capacity
modified or is transient, e.g. data being transfered over the network.
: The number of octets of storage allocated for the buffer.
;size
: The number of octets of data currently held in the buffer.


Multiple segmented implementation may be necessary: An implementation
Implementations of <tt>EpicsBuffer</tt> must provide the following methods, the names of which have been designed (where possible) to match the names of the equivalent methods in the C++ standard template library's container classes:
is required that is appropriate for fields of ioc databases.
Other implementations may be required for transfering data to/from network
buffers.


<center>
;<tt>~EpicsBuffer()</tt>
== epicsString ==
: All data storage allocated for the buffer is reclaimed by the destructor.
</center>
;<tt>EpicsBufferCreator *creator() const</tt>
: An <tt>EpicsBuffer</tt> must know its own creator.
;<tt>void reserve(epicsInt32 capacity)</tt>
: Allocate space to store <tt>capacity</tt> octets. Some implementations may impose a <tt>max_size()</tt> smaller than the limits of memory. If either limit is exceeded by the request, an exception will be thrown. This method is used to request an increase or decrease in the amount of storage alloted for the buffer and may cause the data to move in memory, but does not change the data stored or its size. In particular it cannot reduce the capacity below the amount of data currently stored in the buffer, so to release all the data storage an application would call <tt>resize(0)</tt> then <tt>reserve(0)</tt>.
;<tt>epicsInt32 capacity() const</tt>
: Returns the allocated capacity of the buffer.
;<tt>void resize(epicsInt32 newsize)</tt>
: Sets the number of octets currently stored, up to the current capacity.
;<tt>epicsInt32 size() const</tt>
: Returns the number of octets currently stored in the buffer.
;<tt>epicsInt32 maxSize() const</tt>
: Returns the size of the largest buffer that may be allocated. An attempt to allocate more than this amount of space will fail for certain, but there is no guarantee that this amount of memory is available. This method provides a way for application code to discover any limits that may be imposed by the particular buffer type.
;<tt>bool mutable() const</tt>
: Returns true if the buffer data can be modified, i.e. are puts allowed.
;<tt>isEqual(const EpicsBuffer &cmp)</tt>
: Compares the contents of this buffer with the contents of the buffer <tt>cmp</tt>, and returns true if the data is identical.
;<tt>isEqual(epicsInt32 offset, epicsInt32 len, const epicsOctet *pdata)</tt>
: Compares <tt>len</tt> octets of data starting <tt>offset</tt> octets into this buffer with the octet array <tt>pdata</tt> supplied by the caller. This method is mainly provided to simplify the implementation of the other <tt>isEqual</tt> method.
;<tt>void expose(epicsInt32 offset, epicsInt32 &len, epicsOctet *&pdata)</tt>
: A request for the address of <tt>len</tt> octets of buffer data starting <tt>offset</tt> octets into the buffer. If the buffer implementation uses segmented memory the maximum number of contiguous octets exposed is the segment size, which may be less than the length requested. In this case the value of <tt>len</tt> will be reduced before the method returns. The caller must process the data provided and call <tt>expose</tt> again with an increased <tt>offset</tt> to retrieve or modify subsequent segments.


<tt>epicsString.h</tt> contains the following:
Two versions of the <tt>expose</tt> method are provided, one for use when modifying the buffer and one where the buffer (and the returned data pointer) is const.
    class EpicsUTF_8Buffer;
* The const <tt>expose()</tt> method will only present data up to the current limit as reported by the <tt>size()</tt> method.
   
* The non-const <tt>expose()</tt> method will present data up to the current capacity of the buffer, so application code that is reading as well as modifying the buffer's contents must keep track of the current value of <tt>size()</tt> and stop reading when this is reached (or use the both methods, the const one for reading and the other for writing).
    /*EpicsString holds UTF-8 characters*/
* The non-const version will throw an exception if called for a buffer that is immutable.
    class EpicsString  {
The design of the <tt>expose</tt> method is intended to increase the efficiency of data access. The caller must follow these conventions:
    public:
* Must call <tt>resize(n)</tt> if the data length is to be changed.
        EpicsUTF_8Buffer    *pbuffer;
* Must not access storage outside the length returned by expose. The caller may have to make multiple expose calls to read or write a complete string.
    };


    class EpicsUTF_8Buffer {
Class methods that allocate or deallocate memory must be thread-safe; buffers could be resized, created or destroyed at any time from any thread.  However synchronizing access to the data in the buffer is not the responsibility of this class, thus a buffer should probably not contain a mutex/lock, but its creator might.
    public:
        virtual epicsInt32 allocate(epicsInt32 capacity) = 0;
        virtual void release(bool onlyStorage) = 0;
        virtual epicsInt32 capacity() = 0;
        virtual epicsInt32 limit() = 0;
        virtual void limit(epicsInt32 newLimit) = 0;
        virtual epicsInt32 get(epicsOctet *pto,
                                epicsInt32 offset, epicsInt32 limit) = 0;
        virtual epicsInt32 put(const epicsOctet *pfrom,
                                epicsInt32 offset, epicsInt32 limit) = 0;
        virtual bool isEqual(const EpicsUTF_8Buffer *pbuffer) = 0;
        virtual bool isEqual(const epicsOctet *pstring, epicsInt32 len) = 0;
        virtual void expose(epicsInt32 offset, epicsInt32 limitRequest,
                            epicsOctet *pdata, epicsInt32 *limit);
        virtual epicsUint32 hash(epicsInt16 nBitsHashIndex) = 0;
    };


=== EpicsBufferCreator ===


    typedef EpicsUTF_8Buffer *(EpicsUTF_8BufferAllocate)();
     class EpicsBufferCreator { // interface
     class EpicsUTF_8BufferFactory {
     public:
     public:
         static epicsUint16 typeToTypeID(const char *type);
         virtual const char *bufferType() const = 0;
         static EpicsUTF_8Buffer *allocate(epicsUint16 typeId);
         virtual EpicsBuffer *create(epicsInt32 elementSize = 1) = 0;
         static void register(const char *type,
         virtual void destroy(EpicsBuffer *&pbuffer) = 0;
                          EpicsUTF_8BufferAllocate allocater);
     };
     };


An <tt>EpicsBufferCreator</tt> is an interface implemented by something that knows how to create a particular type of <tt>EpicsBuffer</tt>. How the creator manages the memory involved is up to the implementation - buffers and their data segments may be stored on a freelist by some implementations, and obtained from system memory by others.  Three methods must be implemented:


An <tt>EpicsString</tt> is an instance of an <tt>epicsStringT</tt>.
;<tt>const char *bufferType() const</tt>
It contains a UTF-8 encoded string, i.e. multiple bytes may be needed to store
: Returns some well-known name of the buffer type created.
a single character.
;<tt>EpicsBuffer *create(epicsInt32 elementSize = 1)</tt>
As long as it does not assume that each character is a single byte,
: Instanciates an <tt>EpicsBuffer</tt> which is appropriate for storing arrays of size <tt>elementSize</tt>. Array elements can never cross a segment boundary, thus a buffer segment can never be smaller than an individual element. This method may throw an exception if it is unable to create the buffer.
most code can just ignore the fact that the string is UTF-8 encoded.
;<tt>void destroy(EpicsBuffer *&pbuffer)</tt>
If the local computer has been internationalized for the particular UTF-8
: Executes <tt>pbuffer</tt>'s destructor to reclaim the buffer storage, reclaims the <tt>pbuffer</tt> object itself, and sets pbuffer to NULL.
encoding then the string can be printed via the printf family of methods.


EpicsString has the following fields:
The way to deallocate an <tt>EpicsBuffer *pbuffer</tt> is like this:
; <tt>pbuffer</tt>
    if (pbuffer) pbuffer->creator()->destroy(pbuffer);
: The address of a <tt>EpicsUTF_8Buffer</tt>, which is a class that manages the string storage.
or use the convenience function provided by <tt>EpicsBufferFactory</tt> which does exactly the same thing:
    EpicsBufferFactory::destroy(pbuffer);


Implementations of this class must be thread-safe; buffers could be created or destroyed at any time from any thread.


=== EpicsUTF_8Buffer ===
=== EpicsBufferFactory ===


A string buffer is a container for a UTF_8 encoded character string.
    class EpicsBufferFactory {
 
    public:
Multiple string buffer implementations are available. At least the following
        static void register(EpicsBufferCreator *creator);
are available:
        static EpicsBufferCreator *creator(const char *type);
* contiguous - The string is stored in a contiguous set of bytes.
        static EpicsBuffer *create(const char *type,
** mutable - The string value can be modified after an initial value is assigned.
                                    epicsInt32 elementSize = 1) {
** non-mutable - After the original assignment, the string becomes read only.
            return EpicsBufferFactory::creator(type)->create(elementSize);
* segmented - The string is stored in chunks. This form should be used for strings that are constantly modified.
        }
 
        static void destroy(EpicsBuffer *&pbuffer) {
A string buffer can only be accessed via interface <tt>EpicsUTF_8Buffer</tt>.
            if (pbuffer) pbuffer->creator()->destroy(pbuffer);
In addition to holding storage for a string,
        }
a string buffer keeps the following information.
    };
 
; capacity
: The number of octets allocated. Note that this is <b>NOT</b> the number of characters since a UTF-8 character may require several bytes.
; limit
: The current size, i.e. the index of the first octet that can not hold data. Data can not be read from or written into a buffer beyond limit. When data is being written to a buffer, limit is normally equal to capacity. When data is being read from a buffer, limit is normally less than capacity and indicates the end of valid data.


<b>Implementation Note</b> Code must always set limit so that it is at the
The <tt>EpicsBufferFactory</tt> provides a registry of <tt>EpicsBuffer</tt> implementations, each defined by an <tt>EpicsBufferCreator</tt>.  While each <tt>EpicsBufferCreator</tt> is ultimately responsible for creating all <tt>EpicsBuffer</tt>s of its particular type, the <tt>EpicsBufferFactory</tt> provides a convenience function that looks up an <tt>EpicsBufferCreator</tt> by name and calls its <tt>create()</tt> function directly. Use of the factory to create buffers is optional, since the registry can also be queried to find an <tt>EpicsBufferCreator</tt> by name.
end of a complete character.


<tt>EpicsUTF_8Buffer</tt> has the following methods:
;<tt>void register(EpicsBufferCreator *creator)</tt>
; <tt>allocate</tt>
: Adds <tt>creator</tt> to the list of known buffer types. It will throw an exception if the same name is already registered at a different address.
: This allocates space for up to <tt>capacity</tt> octets. The number of octets allocated is returned. An implementation attempts to allocate the requested capacity but some implementations, e.g. network buffers, may impose a maximum size. If capacity is not zero when this is called and new storage is allocated then the old storage is freed or reused and the octets spanned by position, limit appear in the newly allocated storage.
;<tt>EpicsBufferCreator *creator(const char *type)</tt>
: Looks up the <tt>EpicsBufferCreator</tt> associated with <tt>type</tt> and returns a pointer to it. '''Return NULL, or throw if not found?'''
;<tt>EpicsBuffer *create(const char *type, epicsInt32 elementSize = 1)</tt>
: Looks up the <tt>EpicsBufferCreator</tt> associated with <tt>type</tt>, passes <tt>elementSize</tt> to its <tt>create()</tt> method and returns the resulting <tt>EpicsBuffer</tt> pointer. '''Return NULL, or throw if not found?'''
;<tt>void destroy(EpicsBuffer *&pbuffer)</tt>
: Asks <tt>pbuffer</tt>'s creator destroy the buffer, reclaiming its associated memory and set pbuffer to NULL.


; <tt>release</tt>
It should be clear that creating a buffer is not the same as allocating storage for the data held in that buffer. The former is an <tt>EpicsBufferCreator</tt>'s job, while the latter is the responsibility of the <tt>EpicsBuffer</tt> itself. However an <tt>EpicsBufferCreator</tt> may provide additional services to its <tt>EpicsBuffer</tt> instances such as free lists.
: Storage for the string is released. If <tt>onlyStorage</tt> is <tt>true</tt> then storage for the buffer is also released.
; <tt>capacity</tt>
: returns the capacity
; <tt>limit</tt>
: Two methods are available, one to get the current limit and one to set the limit.
; <tt>get</tt>
: copies characters to pto and returns the number of octets transfered.


; <tt>put</tt>
: copies characters from pfrom, puts them into the buffer, and returns the number of octets transfered. limit is set equal to the number of characters written.
; <tt>isEqual(EpicsUTF_8Buffer *)</tt>
: Compares the string stored in the buffer with a string stored in a different buffer. This is normally called by code that uses an EpicsUTF_8Buffer.
; <tt>isEqual(epicsOctet *pstring, epicsInt32 len)</tt>
: Compares the string stored in the buffer with a string supplied by the caller. This is normally called by EpicsUTF_8Buffer itself to compare it's string with the string stored in another buffer.
; <tt>expose</tt>
: A request to return the address of actual bytes of storage. Since a buffer implementation may used segmented memory the number of bytes exposed may be less than the amount requested.
; <tt>hash</tt>
: implement a hash on the octets stored in the buffer.


----


=== EpicsUTF_8BufferFactory ===
This is a class for allocating an EpicsUTF_8Buffer
and also for registering EpicsUTF_8Buffer implementations.
Note that allocating a buffer is different than allocating storage for
the data that is stored in the buffer.


<center>
<center>
== epicsArray ==
== epicsString ==
</center>
</center>


<tt>epicsArray.h</tt> contains the following:
<tt>epicsString.h</tt> defines a string class that uses an <tt>EpicsBuffer</tt> to hold its data.  Within the core software all strings should be encoded using Unicode/UTF-8, but there is nothing specific to that encoding in the <tt>EpicsString</tt> interface, so other encodings may be stored if desired. EPICS will not provide software to translate between encodings.


     class EpicsArrayBuffer;
     class EpicsString {
 
    class EpicsArray {
     public:
     public:
         epicsType        type;  
         EpicsString();
         EpicsArrayBuffer *pbuffer;
         EpicsString(const char *literal);
        EpicsString(const char *bufferType, epicsInt32 capacity);
        EpicsString(EpicsBufferCreator *creator, epicsInt32 capacity);
        virtual ~EpicsString();
        EpicsString& operator=(const EpicsString &rhs);
        void createBuffer(const char *bufferType, epicsInt32 capacity = 0)
        void createBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        EpicsBufferCreator *bufferCreator() const;
        void destroyBuffer();
        epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                        epicsOctet *pto) const;
        epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                        const epicsOctet *pfrom);
        epicsInt32 hash(epicsInt16 nBitsHashIndex) const;
       
        // These routines are as described for EpicsBuffer
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        bool mutable() const;
        bool isEqual(const EpicsBuffer &cmp) const;
        bool isEqual(epicsInt32 offset, epicsInt32 len,
                      const epicsOctet *pdata) const;
        bool expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        bool expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    private:
        EpicsBuffer *pbuffer;
    private:  // Prevent compiler-implemented methods
        EpicsString(const EpicsString &str);  // No copy constructor
     };
     };
   
    epicsBoolean operator==(const EpicsString &lhs, const EpicsString &rhs);
    epicsBoolean operator!=(const EpicsString &lhs, const EpicsString &rhs);


    class EpicsArrayBuffer {
In Unicode/UTF-8 encoded strings, multiple octets may be needed to store a
    public:
single character. However most code should be able to ignore the character
        virtual epicsUInt32 allocate(
encoding as long as it does not assume that any character can be stored in a
                    epicsUInt32 capacity,epicsUint16 elementSize) = 0;
single octet. As long as the final output device (be it a terminal window, a
        virtual void release(bool onlyStorage) = 0;
printer or some other software package) is expecting Unicode/UTF-8 characters, a
        virtual epicsUInt32 capacity() = 0;
string can be output using the printf family of methods.
        virtual epicsUInt32 elementSize() = 0;
        virtual epicsUInt32 limit() = 0;
        virtual void limit(epicsUInt32 newLimit) = 0;
        virtual epicsUInt32 position() = 0;
        virtual void position(epicsUInt32 newPosition) = 0;
        virtual epicsInt32 get(void *pto,
                                epicsInt32 offset, epicsInt32 limit) = 0;
        virtual epicsInt32 put(const void *pfrom,
                                epicsInt32 offset, epicsInt32 limit) = 0;
        virtual void expose(epicsUInt32 offset, epicsUInt32 limitRequest,
                                    void *pdata, epicsUInt32 *limit);
    }
 


    typedef EpicsArrayBuffer *(EpicsArrayBufferAllocate)();
The <tt>EpicsString</tt> data is ''not'' guaranteed to be null-terminated and in most cases will not be, so applications using the string must take precautions when interfacing with routines that expect the string to end with a zero byte. We may provide a type of contiguous <tt>EpicsBuffer</tt> that does guarantee a null terminator.
    class EpicsArrayBufferFactory {
    public:
        static epicsUint16 typeToTypeID(const char *type);
        static EpicsArrayBuffer *allocate(epicsUint16 typeId);
        static void register(const char *type,
                          EpicsArrayBufferAllocate allocater);
    };
    // type : At least "Contiguous" and "Segmented" are implemented


<tt>EpicsString</tt> provides the following methods:
;<tt>EpicsString()</tt>
: If a string is default constructed, one of the <tt>createBuffer()</tt> methods must be called to set the underlying buffer type before any data can be stored.
;<tt>EpicsString(const char *literal)</tt>
: Use of this constructor causes the string to have a readonly buffer that holds just the literal string given, without copying. This is intended to be efficient as it is likely to be commonly used.
;<tt>EpicsString(const char *bufferType, epicsInt32 capacity)</tt>
;<tt>EpicsString(picsInt16 bufferTypeId, epicsInt32 capacity)</tt>
: Here a buffer type is specified, either by name or by identifier, so the selected buffer type will be created using the <tt>EpicsBufferFactory</tt>, and if <tt>capacity</tt> is non-zero the space will be reserved for at least that number of octets of data.
;<tt>~EpicsString()</tt>
: The destructor will destroy the <tt>EpicsBuffer</tt> if one has been created.
;<tt>void createBuffer(const char *bufferType, epicsInt32 capacity)</tt>
;<tt>void createBuffer(EpicsBufferCreator *creator, epicsInt32 capacity)</tt>
: These methods create an <tt>EpicsBuffer</tt> to hold the string data, and if <tt>capacity</tt> is non-zero the space will be reserved for at least that number of octets of data.
: If a buffer type has already been selected for the string, this method will throw an exception.  (However this definition may change if it is found to be too onerous; the alternative would be to create a new buffer, copy the data to it and then destroy the old buffer)
;<tt>EpicsBufferCreator *bufferCreator() const</tt>
: Returns the <tt>EpicsBufferCreator</tt> used for the underlying buffer storage, or NULL if none has been set yet.
;<tt>void destroyBuffer()</tt>
: Causes the underlying buffer storage to be destroyed, after which one of the <tt>createBuffer()</tt> methods must be used before data can be stored in the string again.
;<tt>epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)</tt>
: Copies up to <tt>len</tt> octets starting at <tt>offset</tt> from the string buffer to <tt>pto</tt>, and returns the number of octets transfered. The return value will be less than <tt>len</tt> if <tt>offset+len > size()</tt>.
;<tt>epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)</tt>
: Copies up to <tt>len</tt> octets from <tt>pfrom</tt> into the buffer starting <tt>offset</tt> octets from the beginning, and returns the number of octets copied. The return value will be less than <tt>len</tt> if <tt>offset+len > capacity()</tt>. The string size will be updated if this call extends the string beyond its original size. However on entry <tt>offset</tt> must not be greater than <tt>size()</tt> or an exception will be thrown.
;<tt>epicsInt32 hash(epicsInt16 nBitsHashIndex) const</tt>
: Calculates an n-bit hash of the octets stored in the string buffer.


An <tt>EpicsArray</tt> can contain data of any epicsType excerpt unknown.
<tt>EpicsString</tt> also implements all of the routines in the <tt>EpicsBuffer</tt> interface. If an <tt>EpicsBuffer</tt> has been created these calls are forwarded to the underlying  <tt>EpicsBuffer</tt> method. If an <tt>EpicsBuffer</tt> has not been created however, all methods except for <tt>createBuffer()</tt> will throw an exception.
It has the following fields:
; <tt>type</tt>
: Any epicsType except unknown
; <tt>pbuffer</tt>
: The address of an <tt>EpicsArrayBuffer</tt> that provides access to the array.


=== EpicsArrayBuffer  ===


Multiple array buffer implementations are available.
----
An implementation can provide the following semantics:
* contiguous - The array  is stored in a contiguous set of bytes.
** mutable - The array can be modified after an initial value is assigned.
** non-mutable - After the original assignment, the array becomes read only.
* segmented - The array is stored in chunks. This form should be used for strings that are constantly modified.


Since the fields can be of type epicsArrayT arrays of arrays are supported.
This is analogous to multidimensional arrays in Java.


An array buffer can only be accessed via interface <tt>EpicsArrayBuffer</tt>.
<center>
In addition to holding storage for the array
== epicsArray ==
an array buffer keeps the following information.
</center>


; capacity
<tt>epicsArray.h</tt> defines an array class that uses an <tt>EpicsBuffer</tt> to hold its data, which can be of any datatype that has a fixed size.
: The number of elements for which storage is allocated.
; element size
: the number of bytes for each element
; limit
: The current size, i.e. the index of the first element that can not hold data. Data can not be read from or written into a array beyond limit. When data is being written to a buffer limit is normally equal to capacity. When data is being read from a buffer limit is normally less than capacity and indicates the end of valid data.
; position
: The index of the first element that has data.


<tt>EpicsArrayBuffer</tt> has the following methods:
    class EpicsArray {
; <tt>allocate</tt>
    public:
: This allocates space for up to <tt>capacity</tt> elements. The number of elements allocated is returned. An implementation attempts to allocate the requested capacity but some implemenations, e.g. network buffers, may impose a maximum size. If capacity is not zero when this is called and new storage is allocated then the old storage is freed or reused and the elements spanned by position, limit appear in the newly allocated storage.
        EpicsArray(epicsInt32 elementSize = 0);
        EpicsArray(const char *bufferType, epicsInt32 elementSize = 0,
                    epicsInt32 capacity = 0);
        EpicsArray(EpicsBufferCreator *creator, epicsInt32 elementSize = 0,
                    epicsInt32 capacity = 0);
        virtual ~EpicsArray();
        EpicsArray& operator=(const EpicsArray &);
        void createBuffer(const char *bufferType, epicsInt32 capacity = 0);
        void createBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        EpicsBufferCreator *bufferCreator() const;
        void destroyBuffer();
        epicsOctet * element(epicsInt32 index);
        const epicsOctet * element(epicsInt32 index) const;
        epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                        epicsOctet *pto) const;
        epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                        const epicsOctet *pfrom);
       
        // Like EpicsBuffer, but units are elements not octets
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        bool mutable() const;
        void expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        void expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    protected:
        void setElementSize(epicsInt32 elementSize);
        epicsInt32 getElementSize() const;
    private:
        epicsInt32 elementSize;
        EpicsBuffer *pbuffer;
    private:  // Prevent compiler-implemented methods
        EpicsArray(const EpicsArray &);            // No copy constructor
    };


; <tt>release</tt>
An <tt>EpicsArray</tt> holds an array of elements of constant size.
: Storage for the array is released. If <tt>onlyStorage</tt> is <tt>true</tt> then the storage for the buffer itself is also released.
; <tt>capacity</tt>
: return the capacity
; <tt>limit</tt>
: Two methods are available, one to get the current limit and one to set the limit.
; <tt>position</tt>
: Two methods are available, one to get the current position and one to set the position.
; <tt>get</tt>
: copies characters to pto and returns the number of octets transfered.
; <tt>put</tt>
: copies characters from pfrom, puts them into the buffer, and returns the number of octets transfered. 
; <tt>expose</tt>
: A request to return the address of actual storage. Since a buffer implementation may used segmented memory the amount of storage exposed may be less than the amount requested.


=== EpicsArrayBufferFactory ===
<tt>EpicsArray</tt> has the following methods:
;<tt>EpicsArray(epicsInt32 elementSize = 0)</tt>
: If an array is default constructed, one of the <tt>createBuffer()</tt> methods must be called to set the underlying buffer type before any storage can be reserved for the array elements. The array must also be told its element size before buffer space can be reserved.
;<tt>EpicsArray(const char *bufferType, epicsInt32 capacity = 0, epicsInt32 elementSize = 0)</tt>
;<tt>EpicsArray(EpicsBufferCreator *creator, epicsInt32 capacity = 0, epicsInt32 elementSize = 0)</tt>
: Here a buffer type is specified, either by name or by identifier, so the selected butter type will be created using the <tt>EpicsBufferFactory</tt>, and if both <tt>capacity</tt> and <tt>elementSize</tt> are non-zero the space will be reserved for at least that number of array elements.
;<tt>~EpicsArray</tt>
: The destructor will destroy the <tt>EpicsBuffer</tt> if one has been created.
;<tt>EpicsArray& operator=(const EpicsArray &)</tt>
: Assignment is possible between arrays (implemented by bit copying), providing the element sizes match exactly and the target array has enough storage to hold all of the data in the source array - call <tt>reserve()</tt> on the target array first if necessary. Think very carefully before assigning arrays of strings, structs or other aggregate types that store pointers, as this will result in duplicate pointers to the same objects. This method will throw an exception if the element sizes differ, and will truncate the array data if the target capacity is insufficient. Calls <tt>resize()</tt> as appropriate.
;<tt>void createBuffer</tt>
: These methods create an <tt>EpicsBuffer</tt> to hold the array data, and if <tt>capacity</tt> and <tt>elementSize</tt> are non-zero the space will be reserved for at least that number of array elements.
: If a buffer type has already been selected for the array, this method will throw an exception.  (However this definition may change if it is found to be too onerous; the alternative would be to create a new buffer, copy the data to it and then destroy the old buffer)
;<tt>EpicsBufferCreator *bufferCreator() const</tt>
: Returns the <tt>EpicsBufferCreator</tt> used for the underlying buffer storage, or NULL if none has been set yet.
;<tt>destroyBuffer()</tt>
: Causes the underlying buffer storage to be destroyed, after which one of the <tt>createBuffer()</tt> methods must be used before data can be stored in the array again.
;<tt>epicsOctet * element(epicsInt32 index)</tt>
: Returns a pointer giving direct access to the <tt>index</tt>'th element of the array, allowing this element to be modified. The pointer returned will have to be cast to an appropriate element type before use. Access to either adjacent array element ''must not'' be made by adjusting the pointer since the underlying buffer storage may be segmented - instead call <tt>element()</tt> again with the appropriately modified index.
;<tt>const epicsOctet * element(epicsInt32 index) const</tt>
: Returns a pointer giving const (read-only) access to the <tt>index</tt>'th element of the array, allowing this element to be read but not modified. If the underlying buffer type is not mutable, this method will throw an exception. The pointer returned will have to be cast to an appropriate element type before use. Access to either adjacent array element ''must not'' be made by adjusting the pointer since the underlying buffer storage may be segmented - instead call <tt>element()</tt> again with the appropriately modified index.
;<tt>epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)</tt>
: Copies up to <tt>len</tt> elements starting at <tt>offset</tt> from the array buffer to <tt>pto</tt>, and returns the number of elements transfered. The return value will be less than <tt>len</tt> if <tt>offset+len > size()</tt>.
;<tt>epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)</tt>
: Copies up to <tt>len</tt> elements from <tt>pfrom</tt> into the buffer starting <tt>offset</tt> elements from the beginning, and returns the number of elements copied. The return value will be less than <tt>len</tt> if <tt>offset+len > capacity()</tt>. The array size will be updated if this call extends the array beyond its original size. However on entry <tt>offset</tt> must not be greater than <tt>size()</tt> or an exception will be thrown.
;<tt>void setElementSize(epicsInt32 elementSize)</tt>
: This sets the size (in octets) of the element type to be stored in the array. If a buffer has already been used for previous array data, this will be released and the buffer capacity set to zero.
;<tt>epicsInt32 getElementSize()</tt>
: This returns the element size currently stored.


This is a class for allocating an EpicsArrayBuffer
<tt>EpicsArray</tt> also implements the access routines from the <tt>EpicsBuffer</tt> interface, translating capacity, size and offset values from element counts into octets and passing them to the underlying <tt>EpicsBuffer</tt>. If an <tt>EpicsBuffer</tt> has not been created however, all these methods will throw an exception. The ability to change the element size is restricted to derived classes only.
and also for registering EpicsArrayBuffer implementations.
Note that allocating a buffer is different than allocating storage for
the data that is stored in the buffer.


<center>
== epicsStruct ==
</center>


<tt>epicsStruct.h</tt> contains the following:
----


    class EpicsStructDef;
    class EpicsStructLifetime;


    class EpicsStruct{
<center>
    public:
== epicsMDArray ==
        EpicsStructDef *pstructDef;
</center>
        void    *pstorage;
    };


    class EpicsStructField {
<tt>epicsMDArray.h</tt> defines a multi-dimensional array class that uses two <tt>EpicsArray</tt>s, one for the sizes of each dimension and another for the array data, which can be of any datatype that has a fixed size.
    public:
        EpicsString name;
        epicsType  type;
    };
   
    class EpicsStructDef{
    public:
        EpicsString  name;
        EpicsStructLifetime *plifetime;
        epicsInt16    nfields;
        EpicsStructField *pfield[]; // ptr to array of ptr to EpicsStructField
    };


     class EpicsStructLifetime {
     class EpicsMDArray {
     public:
     public:
         virtual void allocate(EpicsStruct *pstruct) = 0;
         EpicsMDArray(epicsInt32 elementSize = 0);
         virtual void destroy(EpicsStruct *pstruct) = 0;
        EpicsMDArray(const char *bufferType, epicsInt32 capacity = 0,
         virtual void *exposeField(EpicsStruct *pstruct, epicsInt16 index) = 0;
                      epicsInt32 elementSize = 0);
        EpicsMDArray(EpicsBufferCreator *creator, epicsInt32 capacity = 0,
                      epicsInt32 elementSize = 0);
         virtual ~EpicsMDArray();
        EpicsMDArray& operator=(const EpicsMDArray &);
        void createBuffer(const char *bufferType, epicsInt32 capacity = 0);
        void createBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        EpicsBufferCreator *bufferCreator() const;
        void destroyBuffer();
         void setDimensions(const EpicsArray &newDims);
        const EpicsArray& getDimensions() const;
        epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                        epicsOctet *pto) const;
        epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                        const epicsOctet *pfrom);
       
        // Like EpicsBuffer, but units are elements not octets
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        void expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        void expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    protected:
        void setElementSize(epicsInt32 elementSize);
        epicsInt32 getElementSize() const;
        void resize(epicsInt32 newsize);
    private:
        EpicsArray dimensions;    // EpicsArray<epicsInt32>
        EpicsArray elements;
    private:  // Prevent compiler-implemented methods
        EpicsMDArray(const EpicsMDArray &);           // No copy constructor
     };
     };


An epicsStruct is a container with fields each
An <tt>EpicsMDArray</tt> is a multi-dimensional array of elements of constant size, using two <tt>EpicsArray</tt>s, one to hold the element data and another for the size in each dimension.
of which is any epicsType including unknown.
 
The following classes are involved with an epicsStruct:
* <tt>EpicsStruct</tt> - An instance of an epicsStruct
* <tt>EpicsStructDef</tt> - Describes an epicsStruct
* <tt>EpicsStructField</tt> - Describes a field of an epicsStruct
* <tt>EpicsStructLifetime</tt> - An interface that manages storage for an epicsStruct.
 
It is expected that each of these classes are extended. In fact if any
field of an epicsStruct has type <tt>epicsUnknownT</tt> then it is likely
that most will be overridden.
 
epicsTypes does not specify how to locate an EpicsStructLifetime or
an EpicsStructDef. This is application dependent.
 
An <tt>EpicsStruct</tt> contains two fields:
; <tt>pstructDef</tt>
: Address of a <tt>EpicsStructDef</tt> that describes the structure.
; <tt>pstorage</tt>
: Address of storage for the data contained in the structure.


=== EpicsStructDef ===
The <tt>get()</tt>, <tt>put()</tt> and <tt>expose()</tt> methods all take an <tt>epicsInt32 offset</tt> parameter that specifies an element count into the underlying one dimensional array element buffer.  Currently there are no methods provided for accessing elements of the array at some particular coordinate; such methods would be more appropriate for a derived class with a fixed number of dimensions.


<tt>EpicsStructDef</tt> has the fields:
<tt>EpicsMDArray</tt> has the following methods:
; <tt>name</tt>
;<tt>EpicsMDArray(epicsInt32 elementSize = 0)</tt>
: The structure name.
: If an array is default constructed, one of the <tt>createBuffer()</tt> methods must be called to set the underlying buffer type before any storage can be reserved for the array elements. The array must also be told its element size before buffer space can be reserved.
; <tt>plifetime</tt>
;<tt>EpicsMDArray(const char *bufferType, epicsInt32 capacity = 0, epicsInt32 elementSize = 0)</tt>
: Address of a <tt>EpicsStructLifetime</tt> interface. See below.
;<tt>EpicsMDArray(EpicsBufferCreator *creator, epicsInt32 capacity = 0, epicsInt32 elementSize = 0)</tt>
; <tt>nfields</tt>
: Here a buffer type is specified, either by name or by identifier, so the selected butter type will be created using the <tt>EpicsBufferFactory</tt>, and if both <tt>capacity</tt> and <tt>elementSize</tt> are non-zero the space will be reserved for at least that number of array elements.
: The number of fields in the structure.
;<tt>~EpicsMDArray</tt>
; <tt>pfield</tt>
: The destructor will destroy the <tt>EpicsBuffer</tt>s if any have been created.
: An array of pointers to <tt>EpicsStructField</tt>, one for each field.
;<tt>void createBuffer</tt>
: These methods create an <tt>EpicsBuffer</tt> to hold the array data, and if <tt>capacity</tt> and <tt>elementSize</tt> are non-zero the space will be reserved for at least that number of array elements.
: If a buffer type has already been selected for the array, this method will throw an exception.  (However this definition may change if it is found to be too onerous; the alternative would be to create a new buffer, copy the data to it and then destroy the old buffer)
;<tt>EpicsBufferCreator *bufferCreator() const</tt>
: Returns the <tt>EpicsBufferCreator</tt> used for the underlying buffer storage, or NULL if none has been set yet.
;<tt>destroyBuffer()</tt>
: Causes the underlying buffer storage to be destroyed, after which one of the <tt>createBuffer()</tt> methods must be used before data can be stored in the array again.
;<tt>void setDimensions(const EpicsArray &newDims)</tt>
: Changes the dimensionality of the array, setting the number of dimensions and the extent in each dimension to match <tt>newDims</tt>. This call may cause the capacity of the underlying buffers to be increased if necessary to hold an increased number of dimensions and/or element data. Call <tt>reserve()</tt> to free up any unneeded data storage if desired.
;<tt>const EpicsArray& getDimensions() const</tt>
: Returns a reference to the internal <tt>EpicsArray</tt> that holds the dimension information fot the element array.
;<tt>epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)</tt>
: Copies up to <tt>len</tt> elements starting at <tt>offset</tt> from the element array buffer to <tt>pto</tt>, and returns the number of elements transfered. The return value will be less than <tt>len</tt> if <tt>offset+len > size()</tt>.
;<tt>epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)</tt>
: Copies up to <tt>len</tt> elements from <tt>pfrom</tt> into the buffer starting <tt>offset</tt> elements from the beginning, and returns the number of elements copied. The return value will be less than <tt>len</tt> if <tt>offset+len > capacity()</tt>. The array size will be updated if this call extends the array beyond its original size. However on entry <tt>offset</tt> must not be greater than <tt>size()</tt> or an exception will be thrown.
;<tt>void setElementSize(epicsInt32 elementSize)</tt>
: This sets the size (in octets) of the element type to be stored in the array. If a buffer has already been used for previous array data, this will be released and the buffer capacity set to zero.
;<tt>epicsInt32 getElementSize()</tt>
: This returns the element size currently stored.


=== EpicsStructField ===
<tt>EpicsMDArray</tt> also implements the access routines from the <tt>EpicsBuffer</tt> interface, translating capacity, size and offset values from element counts into octets and passing them to the underlying <tt>EpicsBuffer</tt>. If an <tt>EpicsBuffer</tt> has not been created however, all these methods will throw an exception. The ability to change the element or array sizes is restricted to derived classes only.


<tt>EpicsStructField</tt> has the fields:
; <tt>name</tt>
: The field name.
; <tt>type</tt>
: The field type, which can be any epicsType.


=== EpicsStructLifetime ===
----


<tt>EpicsStructLifetime</tt> is an interface that has three methods:
; <tt>allocate</tt>
: This sets <tt>pstructDef</tt> to the definition for the associated structure and sets pstorage to the address of storage for the fields in the structure.
; <tt>destroy</tt>
: releases the storage for the structure
; <tt>exposeField</tt>
: This returns the address of the storage for the data associated with the field.


<center>
<center>
== epicsMDArray ==
== EpicsEnum ==
</center>
</center>


An <tt>EpicsMDArray</tt> is an instance of an <tt>epicsMDArrayT</tt>
An <tt>EpicsEnum</tt> is a 16-bit index value, which uses a standard interface to convert between choice strings and their index values. There may be more than one implementation of the interface.
It specifies the element type, has the address of the array description,
type and has an associated buffer
that holds the storage for the array.


epicsMDArrayT, i.e. multidimensional array data, is a supported type,
<tt>epicsEnum.h</tt> contains these declarations:
because collection and display of two and three dimensional images is a common
    const int EpicsEnumInvalid = -1;
requirement.
 
An <tt>EpicsMDArray</tt> can only hold primitive or string data.
Thus it can be any of the types <tt>epicsBooleanT</tt>,
..., <tt>epicsStringT</tt> but it can not be type <tt>epicsUnknownT</tt>,
<tt>epicsArrayT</tt>, <tt>epicsStructT</tt>, or <tt>epicsMDArrayT</tt>.
 
 
<tt>epicsMDArray.h</tt> contains the following:
      
      
     class EpicsMDArrayDescription;
     class EpicsEnumChoices {
    class EpicsMDArrayBuffer;
 
    class EpicsMDArray {
     public:
     public:
        EpicsMDArrayDescription *pdescription;
        virtual EpicsEnumChoices * duplicate() = 0;
        EpicsMDArrayBuffer *pbuffer;
        virtual void release() = 0;
        virtual epicsInt16 nChoices() const = 0;
        virtual epicsInt16 index(const EpicsString &choice) const = 0;
        virtual const EpicsString & choice(epicsInt16 index) const = 0;
     };
     };
 
      
    class EpicsMDArrayBounds {
     class EpicsEnum {
    public
        epicsUInt32 low;
        epicsUInt32 high;
    };
    class EpicsMDArrayDescription {
    public:
        epicsType  type;
        epicsInt16  ndim;     // number of dimensions
        EpicsMDArrayBounds bounds[]; // bounds[ndim]
    };
 
     class EpicsMDArrayBuffer {
     public:
     public:
         virtual epicsUInt32 allocate(
        EpicsEnum();
                    epicsUInt32 capacity,epicsUint16 elementSize) = 0;
        EpicsEnum(const EpicsEnum &rhs);
         virtual void release(bool onlyStorage) = 0;
        EpicsEnum(EpicsEnumChoices *choices);
         virtual epicsUInt32 capacity() = 0;
         virtual ~EpicsEnum();
         virtual epicsUInt32 elementSize() = 0;
        EpicsEnum& operator=(const EpicsEnum &rhs);
         virtual epicsUInt32 limit() = 0;
          
         virtual void limit(epicsUInt32 newLimit) = 0;
        void choices(EpicsEnumChoices *pchoices);
         virtual void expose(epicsUInt32 offset, epicsUInt32 limitRequest,
         EpicsEnumChoices *choices() const { return pchoices; }
                                    void *pdata, epicsUInt32 *limit);
         epicsInt16 nChoices() const { return pchoices ? pchoices->nChoices() : 0; }
         operator epicsInt16() const { return index; }
         operator const EpicsString &() const;
        EpicsEnum& operator=(epicsInt16 rhs);
         EpicsEnum& operator=(const EpicsString &rhs);
    protected:
        EpicsEnumChoices *pchoices;
        epicsInt16 index;
     };
     };


    typedef EpicsMDArrayBuffer *(EpicsMDArrayBufferAllocate)();
The <tt>EpicsEnumChoices</tt> interface provides information about the values and choices available that can be use in an <tt>EpicsEnum</tt>. The <tt>EpicsEnum</tt> class itself provides storage for an enum index and a pointer to the describing interface implementation, and provides a number of different access methods.
    class EpicsMDArrayBufferFactory {
    public:
        static epicsUint16 typeToTypeID(const char *type);
        static EpicsMDArrayBuffer *allocate(epicsUint16 typeId);
        static void register(const char *type,
                          EpicsMDArrayBufferAllocate allocater);
    };
    // type : At least "Contiguous" and "Segmented" are implemented
 
 
<tt>EpicsMDArray</tt> has the following fields:
; <tt>pdescription</tt>
: The address of an <tt>EpicsMDArrayDescription</tt> that describes the array
; <tt>pbuffer</tt>
: The address of an <tt>EpicsMDArrayBuffer</tt> that provides access to the array.
 
=== EpicsMDArrayDescription ===
 
<tt>EpicsMDArrayDescription</tt> has the following fields:
; <tt>type</tt>
: The element type which must be one of bool,...,string
; <tt>ndim</tt>
: The number of dimensions
; <tt>bounds</tt>
: An array(ndim) of EpicsMDArrayBounds
 
<tt>EpicsMDArrayBounds</tt> has the fields:
; <tt>low</tt>
:
; <tt>high</tt>
:
 
The definitions describe a "slice" of a multi-dimensional array.
 
=== EpicsMDArrayBuffer ===
 
Multiple buffer implementations are available.
An implementation can provide the following semantics:
* contiguous - The array  is stored in a contiguous set of bytes.
** mutable - The array can be modified after an initial value is assigned.
** non-mutable - After the original assignment, the array becomes read only.
* segmented - The array is stored in chunks. This form should be used for strings that are constantly modified.
 
A buffer can only be accessed via interface <tt>EpicsMDArrayBuffer</tt>.
In additions to holding storage it keeps the following information:
; capacity
: The number of elements for which storage is allocated.
; element size
: the number of bytes for each element
; limit
: The current size, i.e. the index of the first element that can not hold data. Data can not be read from or written into a array beyond limit. When data is being written to a buffer limit is normally equal to capacity. When data is being read from a buffer limit is normally less than capacity and indicates the end of valid data.
 
<tt>EpicsMDArrayBuffer</tt> has the following methods:
; <tt>allocate</tt>
: This allocates space for up to <tt>capacity</tt> elements. The number of elements allocated is returned. An implementation attempts to allocate the requested capacity but some implemenations, e.g. network buffers, may impose a maximum size. If capacity is not zero when this is called and new storage is allocated then the old storage is freed or reused and the elements spanned by position, limit appear in the newly allocated storage.
 
; <tt>release</tt>
: Storage for the array is released. If <tt>onlyStorage</tt> is <tt>true</tt> then the storage for the buffer itself is also released.
; <tt>capacity</tt>
: return the capacity
; <tt>limit</tt>
: Two methods are available, one to get the current limit and one to set the limit.
; <tt>expose</tt>
: A request to return the address of actual storage. Since a buffer implementation may used segmented memory the amount of storage exposed may be less than the amount requested.


=== EpicsMDArrayBufferFactory ===
The <tt>EpicsEnumChoices</tt> interface has five methods which must be implemented:
;<tt>EpicsEnumChoices * clone()</tt>
: Asks the interface for a pointer that another enum can use, permitting a copy to be made and returned or a reference count to be incremented.
;<tt>void release()</tt>
: Tells the interface that this enum is no longer referencing it, permitting a reference-counted implementation.
;<tt>epicsInt16 nChoices() const</tt>
: Returns the number of possible choices for this enumeration, which is also one more than the index number of the highest choice.
;<tt>epicsInt16 index(const EpicsString &choice) const</tt>
: Looks up and returns the index value associated with a particular choice string.  If no match is found, it returns <tt>EpicsEnumInvalid</tt>.
;<tt>const EpicsString & choice(epicsInt16 index) const</tt>
: Looks up and returns the choice string associated with a particular index value.  If the value is outside the legal range of choices, it throws an exception.


This is a class for allocating an EpicsMDArrayBuffer
The <tt>EpicsEnum</tt> class defines the following methods:
and also for registering EpicsMDArrayBuffer implementations.
;<tt>EpicsEnum()</tt>
Note that allocating a buffer is different than allocating storage for
: The default constructor initializes the enum to invalid. This cannot be changed until the choices to use has been set.
the data that is stored in the buffer.
;<tt>EpicsEnum(const EpicsEnum &rhs)</tt>
: The copy constructor clone's the choices from its input parameter and copies the index value.
;<tt>EpicsEnum(EpicsEnumChoices *choices)</tt>
: This constructor sets the enum's choices interface (does not call clone).
;<tt>~EpicsEnum()</tt>
: The destructor releases the choice interface.
;<tt>EpicsEnum& operator=(const EpicsEnum &rhs)</tt>
: Assignment from another enum checks if the same choices interface is used and if not it releases the old interface and clones the new one. Finally it copies the value.
;<tt>void choices(EpicsEnumChoices *pchoices)</tt>
: Releases the existing choices interface if one is already set, and initializes itself with the new interface (does not call clone). This also resets the index to <tt>EpicsEnumInvalid</tt>.
;<tt>EpicsEnumChoices *choices() const</tt>
: Returns the choices interface used by this enum.
;<tt>epicsInt16 nChoices() const</tt>
: Returns the number of possible choices for this enumeration, which is also one more than the index number of the highest choice.
;<tt>operator epicsInt16() const</tt>
: A conversion operator, returns the current choice as an integer.
;<tt>operator const EpicsString &() const</tt>
: A conversion operator, returns the current choice as a string. Throws an exception if the current choice is <tt>EpicsEnumInvalid</tt>.
;<tt>EpicsEnum& operator=(epicsInt16 rhs)</tt>
: Assignment with an integer sets the index value. Throws an exception if the index is outside the allwed range.
;<tt>EpicsEnum& operator=(const EpicsString &rhs)</tt>
: Assignment with a string looks up and sets the index value. Throws an exception if the string cannot be

Latest revision as of 19:19, 6 July 2005

Marty Kraimer and Andrew Johnson

June 14, 2005

Overview

This document describes the fundamental C++ data types which will be used internally by iocCore and hence must be supported in EPICS Version 4.0. We do not cover composite (struct) types here, as a generic interface to such types requires introspection information which is only provided at a higher level.

All data that is sent to or received from any EPICS record will be composed out of the fundamental data types described here.

The data types are:

  • Primitive Types
    • epicsBoolean
    • epicsOctet
    • epicsInt16
    • epicsInt32
    • epicsInt64
    • epicsFloat32
    • epicsFloat64
  • Aggregate Types
    • EpicsString
    • EpicsArray
    • EpicsMDArray
    • EpicsEnum

Comments on Type Safety

EpicsArray and EpicsMDArray as presented below are not type-safe, in that they take and return pointers to an epicsOctet which the user must cast to the appropriate element type. They also will not be able to default-construct or destruct their array elements, so cannot safely be used for complex C++ data types. This will not be an issue when creating an array of strings as long as we ensure that a buffer is zero-filled at allocation.

We could make versions of these classes that are templated on the element type, which default-construct and destruct their elements and even use the element type's assignment operator for copying operations. However since Version 4.0 record types can contain array fields whose data types are not known until the database is loaded at runtime, we will still need to provide a non type-safe version of the array types. For the moment we're going to work with these definitions.

Locking Issues

These fundamental EPICS datatypes do not provide facilities for preventing simultaneous access by multiple threads, which is especially important for the aggregate types. For example the class definitions for EpicsString, EpicsArray and EpicsMDArray all have methods which return pointers into their internal data buffers, for efficiency reasons. In order to make the use of these types thread-safe, suitable access rules and mutual exclusion protection must be established at some higher level. The particular locking scheme used must be appropriate to the application, so cannot be defined in this document.




Primitive Types

epicsTypes.h contains the following typedefs, which are called the primitive types:

    typedef bool               epicsBoolean;
    typedef char               epicsOctet;
    typedef short              epicsInt16;
    typedef int                epicsInt32;
    typedef long long          epicsInt64;
    typedef float              epicsFloat32;
    typedef double             epicsFloat64;

It may be necessary to provide operating system dependent definitions for some of the types. For example on some architectures an epicsInt64 may have to be defined as a long rather than a long long.




Aggregate Types

The aggregate types must have a fixed size but still be able to manage memory in various ways which will be different for different purposes. Therefor they are all based on a standard interface to a memory buffer which can have different implementations for different memory management requirements.

EpicsBuffer

epicsBuffer.h defines a generic interface EpicsBuffer to a buffer of octet data, for which there may be several different implementations.

A buffer factory EpicsBufferFactory provides a central registry of EpicsBufferCreator objects and uses these to create instances of a particular buffer type on demand.

    class EpicsBuffer { // interface
    protected:
        virtual ~EpicsBuffer() = 0;
    public:
        virtual EpicsBufferCreator *creator() const = 0;
        virtual void reserve(epicsInt32 capacity) = 0;
        virtual epicsInt32 capacity() const = 0;
        virtual void resize(epicsInt32 newsize) = 0;
        virtual epicsInt32 size() const = 0;
        virtual epicsInt32 maxSize() const = 0;
        virtual bool mutable() const = 0;
        virtual bool isEqual(const EpicsBuffer &cmp) const = 0;
        virtual bool isEqual(epicsInt32 offset, epicsInt32 len,
                             const epicsOctet *pdata) const = 0;
        virtual void expose(epicsInt32 offset, epicsInt32 &len,
                            epicsOctet *&pdata) = 0;
        virtual void expose(epicsInt32 offset, epicsInt32 &len,
                            const epicsOctet *&pdata) const = 0;
    
    friend class EpicsBufferCreator;
    };

An EpicsBuffer is a container for epicsOctet data values, and may be used to store things such as character strings containing UTF-8 characters or arrays of some other data type.

Multiple implementations of EpicsBuffer are needed with different characteristics, but all buffers will be accessed using the same EpicsBuffer interface. At least the following buffer types will be provided:

  • readonly - A contiguous array that already contains the desired data is provided at buffer initialization time, and cannot be modified. This buffer is for use with literal character strings, and probably won't be registered with the EpicsBufferFactory.
  • contiguous - The data is stored in a contiguous array of octets.
  • segmented - The data is stored in fixed-size chunks. This form should be used for buffers that are frequently created and released, or which often need to change in size.

Additional implementations may be provided for other purposes such as managing network buffers. There might be a need for a contiguous zero-terminated buffer type for code that regularly talks to C string handling routines.

In addition to storing the data, the buffer keeps the following information:

capacity
The number of octets of storage allocated for the buffer.
size
The number of octets of data currently held in the buffer.

Implementations of EpicsBuffer must provide the following methods, the names of which have been designed (where possible) to match the names of the equivalent methods in the C++ standard template library's container classes:

~EpicsBuffer()
All data storage allocated for the buffer is reclaimed by the destructor.
EpicsBufferCreator *creator() const
An EpicsBuffer must know its own creator.
void reserve(epicsInt32 capacity)
Allocate space to store capacity octets. Some implementations may impose a max_size() smaller than the limits of memory. If either limit is exceeded by the request, an exception will be thrown. This method is used to request an increase or decrease in the amount of storage alloted for the buffer and may cause the data to move in memory, but does not change the data stored or its size. In particular it cannot reduce the capacity below the amount of data currently stored in the buffer, so to release all the data storage an application would call resize(0) then reserve(0).
epicsInt32 capacity() const
Returns the allocated capacity of the buffer.
void resize(epicsInt32 newsize)
Sets the number of octets currently stored, up to the current capacity.
epicsInt32 size() const
Returns the number of octets currently stored in the buffer.
epicsInt32 maxSize() const
Returns the size of the largest buffer that may be allocated. An attempt to allocate more than this amount of space will fail for certain, but there is no guarantee that this amount of memory is available. This method provides a way for application code to discover any limits that may be imposed by the particular buffer type.
bool mutable() const
Returns true if the buffer data can be modified, i.e. are puts allowed.
isEqual(const EpicsBuffer &cmp)
Compares the contents of this buffer with the contents of the buffer cmp, and returns true if the data is identical.
isEqual(epicsInt32 offset, epicsInt32 len, const epicsOctet *pdata)
Compares len octets of data starting offset octets into this buffer with the octet array pdata supplied by the caller. This method is mainly provided to simplify the implementation of the other isEqual method.
void expose(epicsInt32 offset, epicsInt32 &len, epicsOctet *&pdata)
A request for the address of len octets of buffer data starting offset octets into the buffer. If the buffer implementation uses segmented memory the maximum number of contiguous octets exposed is the segment size, which may be less than the length requested. In this case the value of len will be reduced before the method returns. The caller must process the data provided and call expose again with an increased offset to retrieve or modify subsequent segments.

Two versions of the expose method are provided, one for use when modifying the buffer and one where the buffer (and the returned data pointer) is const.

  • The const expose() method will only present data up to the current limit as reported by the size() method.
  • The non-const expose() method will present data up to the current capacity of the buffer, so application code that is reading as well as modifying the buffer's contents must keep track of the current value of size() and stop reading when this is reached (or use the both methods, the const one for reading and the other for writing).
  • The non-const version will throw an exception if called for a buffer that is immutable.

The design of the expose method is intended to increase the efficiency of data access. The caller must follow these conventions:

  • Must call resize(n) if the data length is to be changed.
  • Must not access storage outside the length returned by expose. The caller may have to make multiple expose calls to read or write a complete string.

Class methods that allocate or deallocate memory must be thread-safe; buffers could be resized, created or destroyed at any time from any thread. However synchronizing access to the data in the buffer is not the responsibility of this class, thus a buffer should probably not contain a mutex/lock, but its creator might.

EpicsBufferCreator

    class EpicsBufferCreator { // interface
    public:
        virtual const char *bufferType() const = 0;
        virtual EpicsBuffer *create(epicsInt32 elementSize = 1) = 0;
        virtual void destroy(EpicsBuffer *&pbuffer) = 0;
    };

An EpicsBufferCreator is an interface implemented by something that knows how to create a particular type of EpicsBuffer. How the creator manages the memory involved is up to the implementation - buffers and their data segments may be stored on a freelist by some implementations, and obtained from system memory by others. Three methods must be implemented:

const char *bufferType() const
Returns some well-known name of the buffer type created.
EpicsBuffer *create(epicsInt32 elementSize = 1)
Instanciates an EpicsBuffer which is appropriate for storing arrays of size elementSize. Array elements can never cross a segment boundary, thus a buffer segment can never be smaller than an individual element. This method may throw an exception if it is unable to create the buffer.
void destroy(EpicsBuffer *&pbuffer)
Executes pbuffer's destructor to reclaim the buffer storage, reclaims the pbuffer object itself, and sets pbuffer to NULL.

The way to deallocate an EpicsBuffer *pbuffer is like this:

    if (pbuffer) pbuffer->creator()->destroy(pbuffer);

or use the convenience function provided by EpicsBufferFactory which does exactly the same thing:

    EpicsBufferFactory::destroy(pbuffer);

Implementations of this class must be thread-safe; buffers could be created or destroyed at any time from any thread.

EpicsBufferFactory

    class EpicsBufferFactory {
    public:
        static void register(EpicsBufferCreator *creator);
        static EpicsBufferCreator *creator(const char *type);
        static EpicsBuffer *create(const char *type,
                                   epicsInt32 elementSize = 1) {
            return EpicsBufferFactory::creator(type)->create(elementSize);
        }
        static void destroy(EpicsBuffer *&pbuffer) {
            if (pbuffer) pbuffer->creator()->destroy(pbuffer);
        }
    };

The EpicsBufferFactory provides a registry of EpicsBuffer implementations, each defined by an EpicsBufferCreator. While each EpicsBufferCreator is ultimately responsible for creating all EpicsBuffers of its particular type, the EpicsBufferFactory provides a convenience function that looks up an EpicsBufferCreator by name and calls its create() function directly. Use of the factory to create buffers is optional, since the registry can also be queried to find an EpicsBufferCreator by name.

void register(EpicsBufferCreator *creator)
Adds creator to the list of known buffer types. It will throw an exception if the same name is already registered at a different address.
EpicsBufferCreator *creator(const char *type)
Looks up the EpicsBufferCreator associated with type and returns a pointer to it. Return NULL, or throw if not found?
EpicsBuffer *create(const char *type, epicsInt32 elementSize = 1)
Looks up the EpicsBufferCreator associated with type, passes elementSize to its create() method and returns the resulting EpicsBuffer pointer. Return NULL, or throw if not found?
void destroy(EpicsBuffer *&pbuffer)
Asks pbuffer's creator destroy the buffer, reclaiming its associated memory and set pbuffer to NULL.

It should be clear that creating a buffer is not the same as allocating storage for the data held in that buffer. The former is an EpicsBufferCreator's job, while the latter is the responsibility of the EpicsBuffer itself. However an EpicsBufferCreator may provide additional services to its EpicsBuffer instances such as free lists.




epicsString

epicsString.h defines a string class that uses an EpicsBuffer to hold its data. Within the core software all strings should be encoded using Unicode/UTF-8, but there is nothing specific to that encoding in the EpicsString interface, so other encodings may be stored if desired. EPICS will not provide software to translate between encodings.

    class EpicsString {
    public:
        EpicsString();
        EpicsString(const char *literal);
        EpicsString(const char *bufferType, epicsInt32 capacity);
        EpicsString(EpicsBufferCreator *creator, epicsInt32 capacity);
        virtual ~EpicsString();
        EpicsString& operator=(const EpicsString &rhs);
        void createBuffer(const char *bufferType, epicsInt32 capacity = 0)
        void createBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        EpicsBufferCreator *bufferCreator() const;
        void destroyBuffer();
        epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                       epicsOctet *pto) const;
        epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                       const epicsOctet *pfrom);
        epicsInt32 hash(epicsInt16 nBitsHashIndex) const;
        
        // These routines are as described for EpicsBuffer
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        bool mutable() const;
        bool isEqual(const EpicsBuffer &cmp) const;
        bool isEqual(epicsInt32 offset, epicsInt32 len,
                     const epicsOctet *pdata) const;
        bool expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        bool expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    private:
        EpicsBuffer *pbuffer;
    private:   // Prevent compiler-implemented methods
        EpicsString(const EpicsString &str);   // No copy constructor
    };
    
    epicsBoolean operator==(const EpicsString &lhs, const EpicsString &rhs);
    epicsBoolean operator!=(const EpicsString &lhs, const EpicsString &rhs);

In Unicode/UTF-8 encoded strings, multiple octets may be needed to store a single character. However most code should be able to ignore the character encoding as long as it does not assume that any character can be stored in a single octet. As long as the final output device (be it a terminal window, a printer or some other software package) is expecting Unicode/UTF-8 characters, a string can be output using the printf family of methods.

The EpicsString data is not guaranteed to be null-terminated and in most cases will not be, so applications using the string must take precautions when interfacing with routines that expect the string to end with a zero byte. We may provide a type of contiguous EpicsBuffer that does guarantee a null terminator.

EpicsString provides the following methods:

EpicsString()
If a string is default constructed, one of the createBuffer() methods must be called to set the underlying buffer type before any data can be stored.
EpicsString(const char *literal)
Use of this constructor causes the string to have a readonly buffer that holds just the literal string given, without copying. This is intended to be efficient as it is likely to be commonly used.
EpicsString(const char *bufferType, epicsInt32 capacity)
EpicsString(picsInt16 bufferTypeId, epicsInt32 capacity)
Here a buffer type is specified, either by name or by identifier, so the selected buffer type will be created using the EpicsBufferFactory, and if capacity is non-zero the space will be reserved for at least that number of octets of data.
~EpicsString()
The destructor will destroy the EpicsBuffer if one has been created.
void createBuffer(const char *bufferType, epicsInt32 capacity)
void createBuffer(EpicsBufferCreator *creator, epicsInt32 capacity)
These methods create an EpicsBuffer to hold the string data, and if capacity is non-zero the space will be reserved for at least that number of octets of data.
If a buffer type has already been selected for the string, this method will throw an exception. (However this definition may change if it is found to be too onerous; the alternative would be to create a new buffer, copy the data to it and then destroy the old buffer)
EpicsBufferCreator *bufferCreator() const
Returns the EpicsBufferCreator used for the underlying buffer storage, or NULL if none has been set yet.
void destroyBuffer()
Causes the underlying buffer storage to be destroyed, after which one of the createBuffer() methods must be used before data can be stored in the string again.
epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)
Copies up to len octets starting at offset from the string buffer to pto, and returns the number of octets transfered. The return value will be less than len if offset+len > size().
epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)
Copies up to len octets from pfrom into the buffer starting offset octets from the beginning, and returns the number of octets copied. The return value will be less than len if offset+len > capacity(). The string size will be updated if this call extends the string beyond its original size. However on entry offset must not be greater than size() or an exception will be thrown.
epicsInt32 hash(epicsInt16 nBitsHashIndex) const
Calculates an n-bit hash of the octets stored in the string buffer.

EpicsString also implements all of the routines in the EpicsBuffer interface. If an EpicsBuffer has been created these calls are forwarded to the underlying EpicsBuffer method. If an EpicsBuffer has not been created however, all methods except for createBuffer() will throw an exception.




epicsArray

epicsArray.h defines an array class that uses an EpicsBuffer to hold its data, which can be of any datatype that has a fixed size.

    class EpicsArray {
    public:
        EpicsArray(epicsInt32 elementSize = 0);
        EpicsArray(const char *bufferType, epicsInt32 elementSize = 0,
                   epicsInt32 capacity = 0);
        EpicsArray(EpicsBufferCreator *creator, epicsInt32 elementSize = 0,
                   epicsInt32 capacity = 0);
        virtual ~EpicsArray();
        EpicsArray& operator=(const EpicsArray &);
        void createBuffer(const char *bufferType, epicsInt32 capacity = 0);
        void createBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        EpicsBufferCreator *bufferCreator() const;
        void destroyBuffer();
        epicsOctet * element(epicsInt32 index);
        const epicsOctet * element(epicsInt32 index) const;
        epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                       epicsOctet *pto) const;
        epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                       const epicsOctet *pfrom);
        
        // Like EpicsBuffer, but units are elements not octets
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        bool mutable() const;
        void expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        void expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    protected:
        void setElementSize(epicsInt32 elementSize);
        epicsInt32 getElementSize() const;
    private:
        epicsInt32 elementSize;
        EpicsBuffer *pbuffer;
    private:   // Prevent compiler-implemented methods
        EpicsArray(const EpicsArray &);            // No copy constructor
    };

An EpicsArray holds an array of elements of constant size.

EpicsArray has the following methods:

EpicsArray(epicsInt32 elementSize = 0)
If an array is default constructed, one of the createBuffer() methods must be called to set the underlying buffer type before any storage can be reserved for the array elements. The array must also be told its element size before buffer space can be reserved.
EpicsArray(const char *bufferType, epicsInt32 capacity = 0, epicsInt32 elementSize = 0)
EpicsArray(EpicsBufferCreator *creator, epicsInt32 capacity = 0, epicsInt32 elementSize = 0)
Here a buffer type is specified, either by name or by identifier, so the selected butter type will be created using the EpicsBufferFactory, and if both capacity and elementSize are non-zero the space will be reserved for at least that number of array elements.
~EpicsArray
The destructor will destroy the EpicsBuffer if one has been created.
EpicsArray& operator=(const EpicsArray &)
Assignment is possible between arrays (implemented by bit copying), providing the element sizes match exactly and the target array has enough storage to hold all of the data in the source array - call reserve() on the target array first if necessary. Think very carefully before assigning arrays of strings, structs or other aggregate types that store pointers, as this will result in duplicate pointers to the same objects. This method will throw an exception if the element sizes differ, and will truncate the array data if the target capacity is insufficient. Calls resize() as appropriate.
void createBuffer
These methods create an EpicsBuffer to hold the array data, and if capacity and elementSize are non-zero the space will be reserved for at least that number of array elements.
If a buffer type has already been selected for the array, this method will throw an exception. (However this definition may change if it is found to be too onerous; the alternative would be to create a new buffer, copy the data to it and then destroy the old buffer)
EpicsBufferCreator *bufferCreator() const
Returns the EpicsBufferCreator used for the underlying buffer storage, or NULL if none has been set yet.
destroyBuffer()
Causes the underlying buffer storage to be destroyed, after which one of the createBuffer() methods must be used before data can be stored in the array again.
epicsOctet * element(epicsInt32 index)
Returns a pointer giving direct access to the index'th element of the array, allowing this element to be modified. The pointer returned will have to be cast to an appropriate element type before use. Access to either adjacent array element must not be made by adjusting the pointer since the underlying buffer storage may be segmented - instead call element() again with the appropriately modified index.
const epicsOctet * element(epicsInt32 index) const
Returns a pointer giving const (read-only) access to the index'th element of the array, allowing this element to be read but not modified. If the underlying buffer type is not mutable, this method will throw an exception. The pointer returned will have to be cast to an appropriate element type before use. Access to either adjacent array element must not be made by adjusting the pointer since the underlying buffer storage may be segmented - instead call element() again with the appropriately modified index.
epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)
Copies up to len elements starting at offset from the array buffer to pto, and returns the number of elements transfered. The return value will be less than len if offset+len > size().
epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)
Copies up to len elements from pfrom into the buffer starting offset elements from the beginning, and returns the number of elements copied. The return value will be less than len if offset+len > capacity(). The array size will be updated if this call extends the array beyond its original size. However on entry offset must not be greater than size() or an exception will be thrown.
void setElementSize(epicsInt32 elementSize)
This sets the size (in octets) of the element type to be stored in the array. If a buffer has already been used for previous array data, this will be released and the buffer capacity set to zero.
epicsInt32 getElementSize()
This returns the element size currently stored.

EpicsArray also implements the access routines from the EpicsBuffer interface, translating capacity, size and offset values from element counts into octets and passing them to the underlying EpicsBuffer. If an EpicsBuffer has not been created however, all these methods will throw an exception. The ability to change the element size is restricted to derived classes only.




epicsMDArray

epicsMDArray.h defines a multi-dimensional array class that uses two EpicsArrays, one for the sizes of each dimension and another for the array data, which can be of any datatype that has a fixed size.

    class EpicsMDArray {
    public:
        EpicsMDArray(epicsInt32 elementSize = 0);
        EpicsMDArray(const char *bufferType, epicsInt32 capacity = 0,
                     epicsInt32 elementSize = 0);
        EpicsMDArray(EpicsBufferCreator *creator, epicsInt32 capacity = 0,
                     epicsInt32 elementSize = 0);
        virtual ~EpicsMDArray();
        EpicsMDArray& operator=(const EpicsMDArray &);
        void createBuffer(const char *bufferType, epicsInt32 capacity = 0);
        void createBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        EpicsBufferCreator *bufferCreator() const;
        void destroyBuffer();
        void setDimensions(const EpicsArray &newDims);
        const EpicsArray& getDimensions() const;
        epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                       epicsOctet *pto) const;
        epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                       const epicsOctet *pfrom);
        
        // Like EpicsBuffer, but units are elements not octets
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        void expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        void expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    protected:
        void setElementSize(epicsInt32 elementSize);
        epicsInt32 getElementSize() const;
        void resize(epicsInt32 newsize);
    private:
        EpicsArray dimensions;     // EpicsArray<epicsInt32>
        EpicsArray elements;
    private:   // Prevent compiler-implemented methods
        EpicsMDArray(const EpicsMDArray &);            // No copy constructor
    };

An EpicsMDArray is a multi-dimensional array of elements of constant size, using two EpicsArrays, one to hold the element data and another for the size in each dimension.

The get(), put() and expose() methods all take an epicsInt32 offset parameter that specifies an element count into the underlying one dimensional array element buffer. Currently there are no methods provided for accessing elements of the array at some particular coordinate; such methods would be more appropriate for a derived class with a fixed number of dimensions.

EpicsMDArray has the following methods:

EpicsMDArray(epicsInt32 elementSize = 0)
If an array is default constructed, one of the createBuffer() methods must be called to set the underlying buffer type before any storage can be reserved for the array elements. The array must also be told its element size before buffer space can be reserved.
EpicsMDArray(const char *bufferType, epicsInt32 capacity = 0, epicsInt32 elementSize = 0)
EpicsMDArray(EpicsBufferCreator *creator, epicsInt32 capacity = 0, epicsInt32 elementSize = 0)
Here a buffer type is specified, either by name or by identifier, so the selected butter type will be created using the EpicsBufferFactory, and if both capacity and elementSize are non-zero the space will be reserved for at least that number of array elements.
~EpicsMDArray
The destructor will destroy the EpicsBuffers if any have been created.
void createBuffer
These methods create an EpicsBuffer to hold the array data, and if capacity and elementSize are non-zero the space will be reserved for at least that number of array elements.
If a buffer type has already been selected for the array, this method will throw an exception. (However this definition may change if it is found to be too onerous; the alternative would be to create a new buffer, copy the data to it and then destroy the old buffer)
EpicsBufferCreator *bufferCreator() const
Returns the EpicsBufferCreator used for the underlying buffer storage, or NULL if none has been set yet.
destroyBuffer()
Causes the underlying buffer storage to be destroyed, after which one of the createBuffer() methods must be used before data can be stored in the array again.
void setDimensions(const EpicsArray &newDims)
Changes the dimensionality of the array, setting the number of dimensions and the extent in each dimension to match newDims. This call may cause the capacity of the underlying buffers to be increased if necessary to hold an increased number of dimensions and/or element data. Call reserve() to free up any unneeded data storage if desired.
const EpicsArray& getDimensions() const
Returns a reference to the internal EpicsArray that holds the dimension information fot the element array.
epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)
Copies up to len elements starting at offset from the element array buffer to pto, and returns the number of elements transfered. The return value will be less than len if offset+len > size().
epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)
Copies up to len elements from pfrom into the buffer starting offset elements from the beginning, and returns the number of elements copied. The return value will be less than len if offset+len > capacity(). The array size will be updated if this call extends the array beyond its original size. However on entry offset must not be greater than size() or an exception will be thrown.
void setElementSize(epicsInt32 elementSize)
This sets the size (in octets) of the element type to be stored in the array. If a buffer has already been used for previous array data, this will be released and the buffer capacity set to zero.
epicsInt32 getElementSize()
This returns the element size currently stored.

EpicsMDArray also implements the access routines from the EpicsBuffer interface, translating capacity, size and offset values from element counts into octets and passing them to the underlying EpicsBuffer. If an EpicsBuffer has not been created however, all these methods will throw an exception. The ability to change the element or array sizes is restricted to derived classes only.




EpicsEnum

An EpicsEnum is a 16-bit index value, which uses a standard interface to convert between choice strings and their index values. There may be more than one implementation of the interface.

epicsEnum.h contains these declarations:

    const int EpicsEnumInvalid = -1;
    
    class EpicsEnumChoices {
    public:
        virtual EpicsEnumChoices * duplicate() = 0;
        virtual void release() = 0;
        virtual epicsInt16 nChoices() const = 0;
        virtual epicsInt16 index(const EpicsString &choice) const = 0;
        virtual const EpicsString & choice(epicsInt16 index) const = 0;
    };
    
    class EpicsEnum {
    public:
        EpicsEnum();
        EpicsEnum(const EpicsEnum &rhs);
        EpicsEnum(EpicsEnumChoices *choices);
        virtual ~EpicsEnum();
        EpicsEnum& operator=(const EpicsEnum &rhs);
        
        void choices(EpicsEnumChoices *pchoices);
        EpicsEnumChoices *choices() const { return pchoices; }
        epicsInt16 nChoices() const { return pchoices ? pchoices->nChoices() : 0; }
        operator epicsInt16() const { return index; }
        operator const EpicsString &() const;
        EpicsEnum& operator=(epicsInt16 rhs);
        EpicsEnum& operator=(const EpicsString &rhs);
    protected:
        EpicsEnumChoices *pchoices;
        epicsInt16 index;
    };

The EpicsEnumChoices interface provides information about the values and choices available that can be use in an EpicsEnum. The EpicsEnum class itself provides storage for an enum index and a pointer to the describing interface implementation, and provides a number of different access methods.

The EpicsEnumChoices interface has five methods which must be implemented:

EpicsEnumChoices * clone()
Asks the interface for a pointer that another enum can use, permitting a copy to be made and returned or a reference count to be incremented.
void release()
Tells the interface that this enum is no longer referencing it, permitting a reference-counted implementation.
epicsInt16 nChoices() const
Returns the number of possible choices for this enumeration, which is also one more than the index number of the highest choice.
epicsInt16 index(const EpicsString &choice) const
Looks up and returns the index value associated with a particular choice string. If no match is found, it returns EpicsEnumInvalid.
const EpicsString & choice(epicsInt16 index) const
Looks up and returns the choice string associated with a particular index value. If the value is outside the legal range of choices, it throws an exception.

The EpicsEnum class defines the following methods:

EpicsEnum()
The default constructor initializes the enum to invalid. This cannot be changed until the choices to use has been set.
EpicsEnum(const EpicsEnum &rhs)
The copy constructor clone's the choices from its input parameter and copies the index value.
EpicsEnum(EpicsEnumChoices *choices)
This constructor sets the enum's choices interface (does not call clone).
~EpicsEnum()
The destructor releases the choice interface.
EpicsEnum& operator=(const EpicsEnum &rhs)
Assignment from another enum checks if the same choices interface is used and if not it releases the old interface and clones the new one. Finally it copies the value.
void choices(EpicsEnumChoices *pchoices)
Releases the existing choices interface if one is already set, and initializes itself with the new interface (does not call clone). This also resets the index to EpicsEnumInvalid.
EpicsEnumChoices *choices() const
Returns the choices interface used by this enum.
epicsInt16 nChoices() const
Returns the number of possible choices for this enumeration, which is also one more than the index number of the highest choice.
operator epicsInt16() const
A conversion operator, returns the current choice as an integer.
operator const EpicsString &() const
A conversion operator, returns the current choice as a string. Throws an exception if the current choice is EpicsEnumInvalid.
EpicsEnum& operator=(epicsInt16 rhs)
Assignment with an integer sets the index value. Throws an exception if the index is outside the allwed range.
EpicsEnum& operator=(const EpicsString &rhs)
Assignment with a string looks up and sets the index value. Throws an exception if the string cannot be