Difference between revisions of "V4 Design: epicsTypes"

From EPICSWIKI
(Major edits to epicsString)
(Major changes to EpicsArray)
Line 1: Line 1:
= EPICS: epicsTypes =
= EPICS: epicsTypes =


June 3 2005
June 7, 2005


<center>
<center>
== Overview ==
== Overview ==
 
</center>
</center>


This document describes the C++ definitions for storing
This document describes the C++ definitions for storing
Line 37: Line 35:
* epicsTypeMDArray - Multidimensional array of type epicsTypeBoolean,..epicsTypeFloat64, or epicsTypeString.
* epicsTypeMDArray - Multidimensional array of type epicsTypeBoolean,..epicsTypeFloat64, or epicsTypeString.
* epicsTypeEnum - An index and associated set of choice strings.
* epicsTypeEnum - An index and associated set of choice strings.
* epicsTypeBitmask - A bit mask
* epicsTypeStruct - A structure with fields each of which is any epicsType
* epicsTypeStruct - A structure with fields each of which is any epicsType


Line 59: Line 56:


     class EpicsEnum; // see below for details;
     class EpicsEnum; // see below for details;
    class EpicsBitmask; // see velow for details


     class EpicsStruct; // see below for details;
     class EpicsStruct; // see below for details;
Line 69: Line 64:
<center>
<center>
== epicsTypes ==
== epicsTypes ==
</center>
</center>


<tt>epicsTypes.h</tt> contains the following:
<tt>epicsTypes.h</tt> contains the following:
Line 118: Line 113:
<center>
<center>
== Locking Issues ==
== Locking Issues ==
</center>
</center>


epicsTypes does not provide any facilities for preventing simultaneous
epicsTypes does not provide any facilities for preventing simultaneous
Line 128: Line 123:
In order to make expose safe, some rules must be established.
In order to make expose safe, some rules must be established.


As an example of rules, a lock can be associated with each object
As an example of rules, a lock can be associated with each object that supports expose. Code can only call expose and access the exposed data while it holds the lock.
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
The rules are <b>not</b> specified by epicsTypes since
Line 138: Line 131:


<center>
<center>
== epicsString ==
== EpicsBuffer ==
</center>
</center>


<tt>epicsString.h</tt> defines a generic interface to a string buffer <tt>EpicsBuffer</tt>, and then uses this in the definition of the <tt>EpicsString</tt> itself.
<tt>epicsBuffer.h</tt> defines a generic interface <tt>EpicsBuffer</tt> to a buffer of octet data, and a buffer factory <tt>EpicsBufferFactory</tt> that provides a central registry of different implementations of <tt>EpicsBuffer</tt> and uses this to create instances of a particular buffer type on demand.
 
=== EpicsBuffer ===


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


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.
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.


Multiple implementations of EpicsBuffer are needed with different characteristics. At least the following buffer types will be provided:
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:


* readonly - A contiguous array is provided at buffer initialization time, and cannot be modified. This is intended for use with literal character strings.
* 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.
* 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 allocated and released, or which often need to change in size.
* segmented - The data is stored in fixed-size chunks. This form should be used for buffers that are frequently allocated and released, or which often need to change in size.


Additional implementations may be provided for other purposes such as managing network buffers.
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.


A buffer will usually be accessed via the interface <tt>EpicsBuffer</tt>. In addition to storing the data, the buffer keeps the following information:
In addition to storing the data, the buffer keeps the following information:


;capacity
;capacity
Line 180: Line 171:
: The number of octets of data currently held in the buffer.
: The number of octets of data currently held in the buffer.


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 string class:
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:


;<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>
;<tt>epicsInt32 size() const</tt>
: Returns the number of octets currently stored in the buffer.
: Returns the number of octets currently stored in the buffer.
;<tt>epicsInt32 max_size() const</tt>
;<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.
: 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>void resize(epicsInt32 newsize)</tt>
: Sets the number of octets currently stored, up to the current capacity.
;<tt>epicsInt32 capacity() const</tt>
: Returns the allocated capacity of the buffer.
;<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 requests an increase or decrease in the amount of storage for the buffer and may cause the data to move in memory as a result, but must never cause the capacity to reduce below the amount of data currently stored in the buffer.
;<tt>void destruct()</tt>
;<tt>void destruct()</tt>
: All storage for both the data and the buffer class itself is released.
: All storage for both the data and the buffer class itself is released.
Line 198: Line 189:
;<tt>isEqual(const EpicsBuffer &cmp)</tt>
;<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.
: 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(const epicsOctet *pdata, epicsInt32 len, epicsInt32 offset = 0)</tt>
;<tt>isEqual(epicsInt32 offset, epicsInt32 len, const epicsOctet *pdata)</tt>
: Compares the <tt>len</tt> octets of data <tt>offset</tt> items 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.
: 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>bool expose(epicsInt32 &len, epicsInt32 offset, epicsOctet &*pdata)</tt>
;<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.
: 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.
: 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. The non-const version will throw an exception if called for a buffer that is immutable.
 
: The const <tt>expose()</tt> method will only expose data up to the current limit as reported by the <tt>size()</tt> method.
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.
: The non-const <tt>expose()</tt> method will continue to expose 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.
* The const <tt>expose()</tt> method will only present data up to the current limit as reported by the <tt>size()</tt> method.
: The design of the <tt>expose</tt> method is intended to permit increased efficiency. The caller must follow these conventions:
* 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).
:* Must call <tt>resize(n)</tt> if the data length is to be changed.
* The non-const version will throw an exception if called for a buffer that is immutable.
:* 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.
The design of the <tt>expose</tt> method is intended to increase the efficiency of data access. The caller must follow these conventions:
* Must call <tt>resize(n)</tt> 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.


=== EpicsBufferFactory ===
=== EpicsBufferFactory ===
Line 222: Line 215:
     };
     };


This is a class for creating EpicsBuffers and also for registering EpicsBuffer implementations. Note that creating a buffer is not the same as allocating storage for the data that is to be stored in that buffer. The former is performed by EpicsBufferFactory, while the latter is the responsibilty of the EpicsBuffer implementation.
This class provides a central registry of different <tt>EpicsBuffer</tt> implementations and is also responsible for creating an <tt>EpicsBuffer</tt> of any of these types on demand. Note that creating a buffer is not the same as allocating storage for the data that is to be stored in that buffer. The former is performed by <tt>EpicsBufferFactory</tt>, while the latter is the responsibilty of the <tt>EpicsBuffer</tt> implementation.


=== EpicsString ===
 
<center>
== epicsString ==
</center>
 
<tt>epicsString.h</tt> defines a string class that uses an <tt>EpicsBuffer</tt> to hold its data.


     class EpicsString : public EpicsBuffer {
     class EpicsString : public EpicsBuffer {
Line 232: Line 230:
         EpicsString(const char *bufferType, epicsInt32 capacity);
         EpicsString(const char *bufferType, epicsInt32 capacity);
         EpicsString(epicsInt16 bufferTypeId, epicsInt32 capacity);
         EpicsString(epicsInt16 bufferTypeId, epicsInt32 capacity);
        EpicsString(const EpicsString &str);
         virtual ~EpicsString();
         virtual ~EpicsString();
         EpicsString& operator=(const EpicsString &rhs);
         EpicsString& operator=(const EpicsString &rhs);
         void useBuffer(const char *bufferType, epicsInt32 capacity = 0)
         void useBuffer(const char *bufferType, epicsInt32 capacity = 0)
         void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity = 0);
         void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity = 0);
         epicsInt32 get(epicsOctet *pto, epicsInt32 noctets,
         epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                         epicsInt32 offset = 0) const;
                         epicsOctet *pto) const;
         epicsInt32 put(const epicsOctet *pfrom, epicsInt32 noctets,
         epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                         epicsInt32 offset = 0);
                         const epicsOctet *pfrom);
         epicsInt32 hash(epicsInt16 nBitsHashIndex) const;
         epicsInt32 hash(epicsInt16 nBitsHashIndex) const;
         ... // Routines inherited from EpicsBuffer
          
        // These routines are as described for EpicsBuffer
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        void destruct();
        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;
     protected:
     protected:
         EpicsBuffer *pbuffer;
         EpicsBuffer *pbuffer;
    private:
        EpicsString(const EpicsString &str);  // No copy constructor
     };
     };
      
      
     epicsBoolean operator==(const EpicsString &lhs, const EpicsString &rhs);
     epicsBoolean operator==(const EpicsString &lhs, const EpicsString &rhs);
     epicsBoolean operator!=(const EpicsString &lhs, const EpicsString &rhs);
     epicsBoolean operator!=(const EpicsString &lhs, const EpicsString &rhs);


An <tt>EpicsString</tt> is a datatype represented by the epicsType enumeration value <tt>epicsTypeString</tt>.
An <tt>EpicsString</tt> is a datatype represented by the epicsType enumeration value <tt>epicsTypeString</tt>.
Line 258: Line 271:
encoding then a UTF-8 string can be printed via the printf family of methods.
encoding then a UTF-8 string can be printed via 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.
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.
: '''Maybe the contiguous buffer type should have a variant that does guarantee a null terminator?'''


EpicsString provides the following methods:
<tt>EpicsString</tt> provides the following methods:
;<tt>EpicsString()</tt>
;<tt>EpicsString()</tt>
: If a string is default constructed, one of the <tt>useBuffer()</tt> methods must be called to set the underlying buffer type before any data can be stored.
: If a string is default constructed, one of the <tt>useBuffer()</tt> methods must be called to set the underlying buffer type before any data can be stored.
Line 268: Line 280:
;<tt>EpicsString(const char *bufferType, epicsInt32 capacity)</tt>
;<tt>EpicsString(const char *bufferType, epicsInt32 capacity)</tt>
;<tt>EpicsString(picsInt16 bufferTypeId, 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 EpicsBufferFactory and the new buffer's capacity will be reserved to allocate storage for data.
: 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>
;<tt>~EpicsString()</tt>
: The destructor will <tt>destruct()</tt> the EpicsBuffer if one has been allocated.
: The destructor will <tt>destruct()</tt> the <tt>EpicsBuffer</tt> if one has been allocated.
;<tt>void useBuffer(const char *bufferType, epicsInt32 capacity)</tt>
;<tt>void useBuffer(const char *bufferType, epicsInt32 capacity)</tt>
;<tt>void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity)</tt>
;<tt>void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity)</tt>
: These methods create an EpicsBuffer to hold the string data, and reserve storage for <tt>capacity</tt> octets of data. If a buffer has already been selected for the string, '''what happens? throw an exception, or copy the old data to the new buffer and destroy the old buffer?  I suspect we may need the latter, but we could start with the exception and switch if we find we do.'''
: 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.
;<tt>epicsInt32 get(epicsOctet *pto, epicsInt32 len, epicsInt32 offset)</tt>
:: '''What happens if a buffer type has already been selected for the string? Throw an exception, or copy the old data to the new buffer and destroy the old buffer?  Start with the exception, switch if we need to.'''
;<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>.
: 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(const epicsOctet *pfrom, epicsInt32 len, epicsInt32 offset)</tt>
;<tt>epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)</tt>
: Copies up to <tt>len</tt> characters from <tt>pfrom</tt> into the buffer at <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.
: 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>
;<tt>epicsInt32 hash(epicsInt16 nBitsHashIndex) const</tt>
: Calculates an n-bit hash of the octets stored in the string buffer.
: 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 allocated these calls are forwarded to the underlying  EpicsBuffer method. If an EpicsBuffer has not been allocated however, all methods except for <tt>useBuffer()</tt> will throw an exception.
<tt>EpicsString</tt> also implements all of the routines in the <tt>EpicsBuffer</tt> interface. If an <tt>EpicsBuffer</tt> has been allocated these calls are forwarded to the underlying  <tt>EpicsBuffer</tt> method. If an <tt>EpicsBuffer</tt> has not been allocated however, all methods except for <tt>useBuffer()</tt> will throw an exception. The <tt>destruct()</tt> causes the underlying buffer storage to be completely released, after which one of the <tt>useBuffer()</tt> methods must be used before data can be stored in the string again.


----


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


<tt>epicsArray.h</tt> contains the following:
<tt>epicsArray.h</tt> defines an array class that uses an <tt>EpicsBuffer</tt> to hold its data.


     class EpicsArrayBuffer;
     class EpicsArray {
 
        EpicsArray(epicsInt32 elementSize = 0);
    class EpicsArray : public EpicsArrayBuffer{
        EpicsArray(const char *bufferType, epicsInt32 capacity,
         EpicsArray(epicsType type);
                    epicsInt32 elementSize = 0);
         EpicsArray(epicsInt16 bufferTypeId, epicsInt32 capacity,
                    epicsInt32 elementSize = 0);
         virtual ~EpicsArray();
         virtual ~EpicsArray();
         EpicsArray(epicsType type,const char *bufferType);
         void setElementSize(epicsInt32 elementSize);
         EpicsArray(epicsType type,epicsInt16 bufferTypeId);
         epicsInt32 getElementSize() const;
         EpicsArray(epicsType type,
         void useBuffer(const char *bufferType, epicsInt32 capacity = 0);
                      const char *bufferType,,epicsInt32 capacity);
         void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity = 0);
         virtual EpicsArray(epicsType type,
         epicsOctet * element(epicsInt32 index);
                      epicsInt16 bufferTypeId,,epicsInt32 capacity);
        const epicsOctet * element(epicsInt32 index) const;
         void allocateBuffer(const char *bufferType);
         epicsInt32 get(epicsInt32 offset, epicsInt32 len,
         void allocateBuffer(epicsInt16 bufferTypeId);
                        epicsOctet *pto) const;
         void allocateBuffer(const char *bufferType,
         epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                    epicsInt16 elementSize, epicsInt32 capacity)
                        const epicsOctet *pfrom);
         void allocateBuffer(epicsInt16 bufferTypeId,
       
                    epicsInt16 elementSize, epicsInt32 capacity)
        // These are similar to EpicsBuffer, measured in elements not octets
         epicsType getType();
        void reserve(epicsInt32 capacity);
         epicsInt16 getElementSize();
        epicsInt32 capacity() const;
         void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        void destruct();
        bool mutable() const;
         void expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
         void expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
     protected:
     protected:
        epicsType type;
        epicsInt32 elementSize;
        epicsInt16 elementSize;
        EpicsBuffer *pbuffer;
     private:
     private:
        EpicsArray(); // no default constructor
         EpicsArray(const EpicsArray &);           // No copy constructor
         EpicsArray(const EpicsArray &); // copy not allowed
         EpicsArray& operator=(const EpicsArray &); // No assignment operator
         EpicsArray(const EpicsArray *); // copy not allowed
     };
     };


    struct EpicsArraySegment {
An <tt>EpicsArray</tt> is a datatype represented by the epicsType enumeration value <tt>epicsTypeArray</tt>.
        epicsInt32 nelements;
It holds an array of elements of constant size.
        void * pdata;
: '''How do we cope with padding?'''
    };
 
    class EpicsArrayBuffer {
    public:
        virtual epicsInt32 allocate(
                    epicsInt32 capacity,epicsInt16 elementSize) = 0;
        virtual void release(bool onlyStorage) = 0;
        virtual epicsInt32 capacity() = 0;
        virtual epicsInt32 elementSize() = 0;
        virtual epicsInt32 length() = 0;
        virtual void length(epicsInt32 newLength) = 0;
        virtual bool mutable() = 0;
        virtual epicsInt32 get(void *pto,
                                epicsInt32 offset, epicsInt32 nelements) = 0;
        virtual epicsInt32 put(const void *pfrom,
                                epicsInt32 offset, epicsInt32 nelements) = 0;
        virtual void expose(epicsInt32 offset, epicsInt32 nelements,
                                    EpicsArraySegment &segment) = 0;
    };
 
 
    typedef EpicsArrayBuffer *(EpicsArrayBufferAllocate)();
    class EpicsArrayBufferFactory {
    public:
        static EpicsArrayBuffer *allocate(const char *type);
        static epicsInt16 typeToTypeID(const char *type);
        static EpicsArrayBuffer *allocate(epicsInt16 typeId);
        static void register(const char *type,
                          EpicsArrayBufferAllocate allocater);
    };
 
 
An <tt>EpicsArray</tt> is an instance of an <tt>epicsTypeArray</tt>.
It can contain an array of any epicsType.


EpicsArray has the following methods:
EpicsArray has the following methods:
; <tt>EpicsArray</tt>
;<tt>EpicsArray(epicsInt32 elementSize = 0)</tt>
: Several constructors are available. All require an epicsType. If no buffer type is specified then, one of the allocate methods must be called. If a buffer type is specified then EpicsArrayBufferFactory:allocate is called to initialize pbuffer. If capacity is also specified EpicsArrayBuffer:allocate is called to allocate storage for data.
: If an array is default constructed, one of the <tt>useBuffer()</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</tt>
;<tt>EpicsArray(const char *bufferType, epicsInt32 capacity, epicsInt32 elementSize = 0)</tt>
: The descructor will release the EpicsArrayBuffer if it has been allocated.
;<tt>EpicsArray(epicsInt16 bufferTypeId, epicsInt32 capacity, epicsInt32 elementSize = 0)</tt>
; <tt>void allocateBuffer</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.
: These methods allocate an EpicsArrayBuffer and if capacity is specified call EpicsArrayBuffer:allocate.
;<tt>~EpicsArray</tt>
 
: The destructor will <tt>destruct()</tt> the <tt>EpicsBuffer</tt> if one has been allocated.
EpicsArray implements interface EpicsArrayBuffer.
;<tt>void setElementSize(epicsInt32 elementSize)</tt>
If an EpicsArrayBuffer has not been allocated all methods raise an exceptioon.
: This sets the size (in octets) of the element type to be stored in the array. If a buffer has already been allocated for previous array data, this will be released and the buffer capacity set to zero.
If an EpicsArrayBuffer has been allocated it
;<tt>epicsInt32 getElementSize()</tt>
calls the associated EpicsArrayBuffer method.
: This returns the element size currently stored.
 
;<tt>void useBuffer</tt>
=== EpicsArrayBuffer  ===
: 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.
 
:: '''What happens if a buffer type has already been selected for the array? Throw an exception, or destroy the old buffer and create the new one? Start with the exception, switch if we need to.'''
Multiple array buffer implementations are available.
;<tt>epicsOctet * element(epicsInt32 index)</tt>
An implementation can provide the following semantics:
: 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.
* contiguous - The array is stored in contiguous storage.
;<tt>const epicsOctet * element(epicsInt32 index) const</tt>
* segmented - The array is stored in chunks. This form should be used for arrays that are constantly modified.
: 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.
* circular - The storage is contiguous but the data is stored as a circular buffer.
;<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>.
Since the fields can be of type epicsTypeArray arrays of arrays are supported.
;<tt>epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)</tt>
This is analogous to multidimensional arrays in Java.
: 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></tt>
An array buffer can only be accessed via interface <tt>EpicsArrayBuffer</tt>.
In addition to holding storage for the array
an array buffer keeps the following information.
 
; capacity
: The number of elements for which storage is allocated.
; elementSize
: the number of bytes for each element
; length
: The current number of elements.
; mutable
: Can the data be modified? The default is <tt>true</tt>.
 


<tt>EpicsArrayBuffer</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>
<tt>EpicsArray</tt> also implements all of the routines in the EpicsBuffer interface, translating all capacity, size and offset values into element counts before passing them to the underlying <tt>EpicsBuffer</tt>. If an <tt>EpicsBuffer</tt> has not been allocated however, all these methods will throw an exception. The <tt>destruct()</tt> causes the underlying buffer storage to be completely released, after which one of the <tt>useBuffer()</tt> methods must be used before data can be stored in the array again.
: 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>elementSize</tt>
: return the elementSize.
; <tt>length</tt>
: Two methods are available, one to get the current length and one to set the length.
; <tt>mutable</tt>
: Can the array be modified? The default is <tt>true</tt>.
; <tt>get</tt>
: copies elements to <tt>pto</tt> and returns the number of elements transfered.
; <tt>put</tt>
: copies elements from <tt>pfrom</tt>, puts them into the buffer, and returns the number of elements transfered. The internal length is modified.
; <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.
 
<b>expose Notes</b>
 
<tt>expose</tt> is provided for efficiency.
The caller must follow some conventions:
* Must not modify the data if mutable is false.
* Must call <tt>pbuffer->length(newLength)</tt> if the length is modified.
* Must never access storage outside the limit returned by expose. The caller may have to make multiple expose calls to read or write a complete string.
* If the buffer is a circular buffer, an exception may be thrown if expose is called.
 
 
=== EpicsArrayBufferFactory ===
 
This is a class for allocating an EpicsArrayBuffer
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.


----
----
Line 437: Line 378:
<center>
<center>
== epicsMDArray ==
== epicsMDArray ==
</center>
</center>


An <tt>EpicsMDArray</tt> is an instance of an <tt>epicsTypeMDArray</tt>
An <tt>EpicsMDArray</tt> is an instance of an <tt>epicsTypeMDArray</tt>
Line 547: Line 488:
<center>
<center>
== EpicsEnum ==
== EpicsEnum ==
</center>
</center>


An <tt>EpicsEnum</tt> is a 16-bit index value,
An <tt>EpicsEnum</tt> is a 16-bit index value,
Line 587: Line 528:


<center>
<center>
== EpicsBitArray ==
</center>
An <tt>EpicsBitArray</tt> defines a collection of any multiple of eight bits. It provides direct access to the octets, as well as combinatorial logic functions for the collection as a whole and accessor functions for the individual bits.
class EpicsBitArray {
public:
    EpicsBitArray(epicsInt32 nbits = 32);
    EpicsBitArray(const EpicsBitArray &);
    virtual ~EpicsBitArray();
    EpicsBitArray& operator=(const EpicsBitArray &rhs);
   
    epicsInt32 nBits() { return size; }
    epicsBoolean getBit(epicsInt32 index) const;
    void setBit(epicsInt32 index, epicsBoolean value);
    virtual const epicsOctet& octet(epicsInt32 index) const;
    virtual epicsOctet& octet(epicsInt32 index);
   
    EpicsBitArray& Reset();
    EpicsBitArray& Not();
    EpicsBitArray& And(const EpicsBitArray &rhs);
    EpicsBitArray& Or(const EpicsBitArray &rhs);
    EpicsBitArray& Xor(const EpicsBitArray &rhs);
protected:
    epicsOctet *parray;    // pointer to array of octets
    epicsInt32 size;      // number of bits stored
    epicsOctet array[4];  // use this array for up to 32 bits
};
----
<center>
== epicsStruct ==
== epicsStruct ==
</center>
</center>


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

Revision as of 18:17, 7 June 2005

EPICS: epicsTypes

June 7, 2005

Overview

This document describes the C++ definitions for storing data that can be accessed without pre-complied code, i.e. it has self describing features.


epicsTypes can be used by any code that stores data accessable via V4 Channel Access. Some examples are:

  1. IOC records - Most data accessable from outside record support is stored as an epicsType.
  2. Channel Access Gateway - Can be used to store and transfer data.
  3. Channel Access Clients - Can be used to get/receive data from CA server.

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.

epicsType is an enum that defines the following:

  • epicsTypeUnknown - Type is unknown
  • epicsTypeBoolean - true or false
  • epicsTypeOctet - An eight bit byte. It is NOT an integer type
  • epicsTypeInt16 - 16 bit signed integer
  • epicsTypeInt32 - 32 bit signed integer
  • epicsTypeInt64 - 64 bit signed integer
  • epicsTypeFloat32 - 32 bit IEEE float
  • epicsTypeFloat64 - 64 bit IEEE float
  • epicsTypeString - A UTF-8 Encoded Character String
  • epicsTypeArray - One dimensional array of any epicsType
  • epicsTypeMDArray - Multidimensional array of type epicsTypeBoolean,..epicsTypeFloat64, or epicsTypeString.
  • epicsTypeEnum - An index and associated set of choice strings.
  • epicsTypeStruct - A structure with fields each of which is any epicsType


The actual types associated with the epicsTypes are:

    typedef bool               epicsBoolean;
    typedef char               epicsOctet;
    typedef short              epicsInt16;
    typedef int                epicsInt32;
    typedef long long          epicsInt64;
    typedef float              epicsFloat32;
    typedef double             epicsFloat64;
    //EpicsString holds UTF-8 characters
    class EpicsString; // see below for details;
    class EpicsArray; // see below for details;
    class EpicsMDArray; // see below for details;
    class EpicsEnum; // see below for details;
    class EpicsStruct; // see below for details;

NOTE: In this document primitive type means one of epicsBoolean, epicsOctet, epicsInt16, epicsInt32, epicsInt64, epicsFloat32, or epicsFloat64.

epicsTypes

epicsTypes.h contains the following:

    /* The following may require OSD definitions*/
    typedef bool               epicsBoolean;
    typedef char               epicsOctet;
    typedef short              epicsInt16;
    typedef int                epicsInt32;
    typedef long long          epicsInt64;
    typedef float              epicsFloat32;
    typedef double             epicsFloat64;
   
    enum epicsType {
        epicsTypeUnknown,
        epicsTypeBoolean,
        epicsTypeOctet,
        epicsTypeInt16,
        epicsTypeInt32,
        epicsTypeInt64,
        epicsTypeFloat32,
        epicsTypeFloat64,
        epicsTypeString,
        epicsTypeArray,
        epicsTypeMDArray,
        epicsTypeEnum,
        epicsTypeBitmask,
        epicsTypeStruct
    };


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 epicsBoolean, ..., epicsFloat64 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 an epicsInt64 may have to be defined as a long rather than a long long.

epicsTypeUnknown is for anything that is not one of the other epicsTypes.

The non-primitive type, except, epicsTypeUnknown are designed so that the data they contain can be described, introspected, and passed over a network. Each is described in the following sections.

Locking Issues

epicsTypes does not provide any facilities for preventing simultaneous access by multiple threads. For example the class definitions for non-primitive types, i.e. epicsString, epicsArray, epicsStruct, and epicsMDArray, all provide a method expose which returns the address of data. In order to make expose safe, some rules must be established.

As an example of rules, a lock can be associated with each object that supports expose. Code can only call expose and access the exposed data while it holds the lock.

The rules are not specified by epicsTypes since they are application dependent.


EpicsBuffer

epicsBuffer.h defines a generic interface EpicsBuffer to a buffer of octet data, and a buffer factory EpicsBufferFactory that provides a central registry of different implementations of EpicsBuffer and uses this to create instances of a particular buffer type on demand.

    class EpicsBuffer { // interface
    public:
        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 void destruct() = 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;
    };

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 allocated 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:

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.
void destruct()
All storage for both the data and the buffer class itself is released.
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.

EpicsBufferFactory

    typedef EpicsBuffer *(EpicsBufferAllocator)();
    
    class EpicsBufferFactory {
    public:
        static EpicsBuffer *allocate(const char *type);
        static epicsInt16 typeToTypeID(const char *type);
        static EpicsBuffer *allocate(epicsInt16 typeId);
        static void register(const char *type,
                         EpicsBufferAllocator allocator);
    };

This class provides a central registry of different EpicsBuffer implementations and is also responsible for creating an EpicsBuffer of any of these types on demand. Note that creating a buffer is not the same as allocating storage for the data that is to be stored in that buffer. The former is performed by EpicsBufferFactory, while the latter is the responsibilty of the EpicsBuffer implementation.


epicsString

epicsString.h defines a string class that uses an EpicsBuffer to hold its data.

    class EpicsString : public EpicsBuffer {
    public:
        EpicsString();
        EpicsString(const char *literal);
        EpicsString(const char *bufferType, epicsInt32 capacity);
        EpicsString(epicsInt16 bufferTypeId, epicsInt32 capacity);
        virtual ~EpicsString();
        EpicsString& operator=(const EpicsString &rhs);
        void useBuffer(const char *bufferType, epicsInt32 capacity = 0)
        void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity = 0);
        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;
        void destruct();
        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;
    protected:
        EpicsBuffer *pbuffer;
    private:
        EpicsString(const EpicsString &str);   // No copy constructor
    };
    
    epicsBoolean operator==(const EpicsString &lhs, const EpicsString &rhs);
    epicsBoolean operator!=(const EpicsString &lhs, const EpicsString &rhs);

An EpicsString is a datatype represented by the epicsType enumeration value epicsTypeString. It holds a UTF-8 encoded string, i.e. multiple octets may be needed to store a single character. As long as it does not assume that each character is a single byte, most code can just ignore the fact that the string is UTF-8 encoded. If the local computer has been internationalized for the particular UTF-8 encoding then a UTF-8 string can be printed via 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 useBuffer() 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 destruct() the EpicsBuffer if one has been allocated.
void useBuffer(const char *bufferType, epicsInt32 capacity)
void useBuffer(epicsInt16 bufferTypeId, 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.
What happens if a buffer type has already been selected for the string? Throw an exception, or copy the old data to the new buffer and destroy the old buffer? Start with the exception, switch if we need to.
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 allocated these calls are forwarded to the underlying EpicsBuffer method. If an EpicsBuffer has not been allocated however, all methods except for useBuffer() will throw an exception. The destruct() causes the underlying buffer storage to be completely released, after which one of the useBuffer() methods must be used before data can be stored in the string again.


epicsArray

epicsArray.h defines an array class that uses an EpicsBuffer to hold its data.

    class EpicsArray {
        EpicsArray(epicsInt32 elementSize = 0);
        EpicsArray(const char *bufferType, epicsInt32 capacity,
                   epicsInt32 elementSize = 0);
        EpicsArray(epicsInt16 bufferTypeId, epicsInt32 capacity,
                   epicsInt32 elementSize = 0);
        virtual ~EpicsArray();
        void setElementSize(epicsInt32 elementSize);
        epicsInt32 getElementSize() const;
        void useBuffer(const char *bufferType, epicsInt32 capacity = 0);
        void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity = 0);
        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);
        
        // These are similar to EpicsBuffer, measured in elements not octets
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        void destruct();
        bool mutable() const;
        void expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        void expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    protected:
        epicsInt32 elementSize;
        EpicsBuffer *pbuffer;
    private:
        EpicsArray(const EpicsArray &);            // No copy constructor
        EpicsArray& operator=(const EpicsArray &); // No assignment operator
    };

An EpicsArray is a datatype represented by the epicsType enumeration value epicsTypeArray. It holds an array of elements of constant size.

How do we cope with padding?

EpicsArray has the following methods:

EpicsArray(epicsInt32 elementSize = 0)
If an array is default constructed, one of the useBuffer() 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, epicsInt32 elementSize = 0)
EpicsArray(epicsInt16 bufferTypeId, epicsInt32 capacity, 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 destruct() the EpicsBuffer if one has been allocated.
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 allocated for previous array data, this will be released and the buffer capacity set to zero.
epicsInt32 getElementSize()
This returns the element size currently stored.
void useBuffer
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.
What happens if a buffer type has already been selected for the array? Throw an exception, or destroy the old buffer and create the new one? Start with the exception, switch if we need to.
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.


EpicsArray also implements all of the routines in the EpicsBuffer interface, translating all capacity, size and offset values into element counts before passing them to the underlying EpicsBuffer. If an EpicsBuffer has not been allocated however, all these methods will throw an exception. The destruct() causes the underlying buffer storage to be completely released, after which one of the useBuffer() methods must be used before data can be stored in the array again.


epicsMDArray

An EpicsMDArray is an instance of an epicsTypeMDArray 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.

epicsTypeMDArray, i.e. multidimensional array data, is a supported type, because collection and display of two and three dimensional images is a common requirement.

An EpicsMDArray can only hold primitive or string data. Thus it can be any of the types epicsTypeBoolean, ..., epicsTypeString but it can not be type epicsTypeUnknown, epicsTypeArray, epicsTypeStruct, or epicsTypeMDArray.


epicsMDArray.h contains the following:

    class EpicsMDArraySlice;
    class EpicsMDArray : public EpicsArrayBuffer {
        EpicsMDArray(epicsType type,epicsInt16 ndim);
        virtual ~EpicsMDArray();
        EpicsMDArray(epicsType type,epicsInt16 ndim,const char *bufferType);
        EpicsMDArray(epicsType type,epicsInt16 ndim,epicsInt16 bufferTypeId);
        EpicsMDArray(epicsType type,epicsInt16 ndim,
                     const char *bufferType,,epicsInt32 capacity);
        EpicsMDArray(epicsType type,epicsInt16 ndim,
                     epicsInt16 bufferTypeId,,epicsInt32 capacity);
        void allocateBuffer(const char *bufferType);
        void allocateBuffer(epicsInt16 bufferTypeId);
        void allocateBuffer(const char *bufferType,
                    epicsInt16 elementSize, epicsInt32 capacity)
        void allocateBuffer(epicsInt16 bufferTypeId,
                    epicsInt16 elementSize, epicsInt32 capacity)
        epicsType getType();
        epicsInt16 getElementSize();
    protected:
       epicsType type;
       epicsInt16 elementSize;
       EpicsMDArraySlice *pslice;
       EpicsMDArrayBuffer *pbuffer;
    private:
       EpicsMDArray(); // no default constructor
        EpicsMDArray(const EpicsMDArray &); // copy not allowed
        EpicsMDArray(const EpicsMDArray *); // copy not allowed
    };
    class EpicsMDArrayIndex {
    public
        epicsInt32 low;
        epicsInt32 high;
    };
    class EpicsMDArraySlice {
    public:
        epicsType   type; 
        epicsInt16  ndim;      // number of dimensions
        EpicsMDArrayIndex *paindex; // addr of array of EpicsMDArrayIndex
    };

EpicsMDArray

An EpicsMDArray is an instance of an epicsTypeMDArray. It can contain an array of any epicsType.

EpicsMDArray has the following methods:

EpicsMDArray
Several constructors are available. All require an epicsType and the number ofdimensions. If no buffer type is specified then, one of the allocate methods must be called. If a buffer type is specified then EpicsArrayBufferFactory:allocate is called to initialize pbuffer. If capacity is also specified EpicsArrayBuffer:allocate is called to allocate storage for data.
~EpicsMDArray
The descructor will release the EpicsArrayBuffer if it has been allocated.
void allocateBuffer
These methods allocate an EpicsArrayBuffer and if capacity is specified call EpicsArrayBuffer:allocate.

EpicsMDArray implements interface EpicsArrayBuffer. If an EpicsArrayBuffer has not been allocated all methods raise an exceptioon. If an EpicsArrayBuffer has been allocated it calls the associated EpicsArrayBuffer method.


EpicsArrayBuffer Notes

EpicsMDArray uses the same buffer interface as EpicsArray. Code that accesses a multidimensional array should be prepared to access both contiguous and segmented buffer implementations. Circular buffer implementations may not make sense.

EpicsMDArraySlice

EpicsMDArraySlice has the following fields:

type
The element type which must be one of bool,...,string
ndim
The number of dimensions
paindex
addr of array(ndim) of EpicsMDArrayIndex

EpicsMDArrayIndex has the fields:

low
high

The definitions describe a "slice" of a multi-dimensional array.



EpicsEnum

An EpicsEnum is a 16-bit index value, with an interface to convert between choice strings and their index values.

class EpicsEnumChoices {
public:
    virtual EpicsEnumChoices *duplicate() const = 0;
    virtual void release() = 0;
    virtual epicsInt16 nChoices() const = 0;
    virtual epicsInt16 index(const EpicsString &choice) const = 0;
    virtual void choice(epicsInt16 index, EpicsString &choice) const = 0;
};

class EpicsEnum {
public:
    enum {invalid = -1};
    
    EpicsEnum();
    EpicsEnum(const EpicsEnum &e);
    EpicsEnum(EpicsEnumChoices *choices);
    EpicsEnum(EpicsEnumChoices *choices, epicsInt16 index);
    virtual ~EpicsEnum();
    EpicsEnum& operator=(const EpicsEnum &rhs);
    
    virtual void choices(EpicsEnumChoices *pchoices);
    EpicsEnumChoices *choices() const { return pchoices; }
    epicsInt16 nChoices() const { return pchoices ? pchoices->nChoices() : 0; }
    epicsInt16 get() const { return index; }
    virtual void get(EpicsString &choice) const;
    virtual void put(epicsInt16 index);
    virtual void put(const EpicsString &choice);
protected:
    EpicsEnumChoices *pchoices;
    epicsInt16 index;
};

epicsStruct

epicsStruct.h contains the following:

    class EpicsStructDescription;
    class EpicsStructManager;
    class EpicsStruct{
        EpicsStruct(EpicsStructDescription &structDef);
        ~EpicsStruct();
    protected:
        EpicsStructDescription *pstructDef;
        void    *pstorage;
    private:
        EpicsStruct(); // no default cobstructor
    };
    struct EpicsStringDefaults { // defaults for string
        const char * bufferType;
        epicsInt32 capacity;
    };
    struct EpicsArrayDefaults { // defaults for array
        epicsType  type;
        const char * bufferType;
        epicsInt32 capacity;
        epicsInt32 nelements;
    };
    struct EpicsEnumDefaults { // defaults for enum
         ???
    };
    struct EpicsBitMaskDefaults { // defaults for enum
         ???
    };
    struct EpicsMDArrayDefaults { // defaults for mdarray
        epicsType  type;
        epicsInt16 ndim
        const char *bufferType;
        epicsInt32 capacity;
    };
    struct EpicsStructDefaults { // defaults for struct
        EpicsStructDescription *pdescription;
    };
    class EpicsStructField {
    public:
        EpicsString name;
        epicsType   type;
        union {
            EpicsStringDefaults  *pstring;
            EpicsArrayDefaults   *parray;
            EpicsMDArrayDefaults *pmdarray;
            EpicsEnumDefaults    *penum;
            EpicsBitmaskDefaults *pbitmask;
            EpicsStructDefaults  *pstruct;
        } defaults;
    };
   
    class EpicsStructDescription{
    public:
        EpicsString         name;
        EpicsStructManager *plifetime;
        epicsInt16          nfields;
        EpicsStructField    *pafield;//ptr to array of EpicsStructField
    };
    class EpicsStructManager {
    public:
        virtual void allocate(EpicsStruct &struct) = 0;
        virtual void destroy(EpicsStruct &struct) = 0;
        virtual void *exposeField(EpicsStruct &struct, epicsInt16 index) = 0;
    };

An epicsStruct is a container with fields each of which is any epicsType including unknown.

The following classes are involved with an epicsStruct:

  • EpicsStruct - An instance of an epicsStruct
  • EpicsStructDescription - Describes an epicsStruct
  • EpicsStructField - Describes a field of an epicsStruct
  • EpicsStructDefaults - Provides default values for non-primitive fields. This is intended for code that initializes fields.
  • EpicsStructManager - 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 epicsTypeUnknown then it is likely that most will be extended.

epicsTypes does not specify how to locate an EpicsStructManager, i.e. the mechanism is application dependent.

An EpicsStruct contains two fields:

pstructDef
Address of a EpicsStructDescription that describes the structure.
pstorage
Address of storage for the data contained in the structure.

EpicsStructDescription

EpicsStructDescription has the fields:

name
The structure name.
plifetime
Address of a EpicsStructManager interface. See below.
nfields
The number of fields in the structure.
pafield
addresss of an array of EpicsStructField, one for each field.

EpicsStructField

EpicsStructField has the fields:

name
The field name.
type
The field type, which can be any epicsType.
defaults
For each of the non-primitive epicsTypes, default values can be specified. This is for use by code that must initialize field instances.

EpicsStructManager

EpicsStructManager is an interface that has three methods:

allocate
This sets pstructDef to the definition for the associated structure and sets pstorage to the address of storage for the fields in the structure.
destroy
releases the storage for the structure and sets pstructDef null.
exposeField
This returns the address of the storage for the data associated with the field.