# memptr.java

    /*
    ** Module   : memptr.java
    ** Abstract : Progress 4GL compatible memptr data type
    **
    ** Copyright (c) 2005-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
    ** 001 GES 20050906   @22564 Created initial version with support for a subset of memptr
    **                           processing.
    ** 002 GES 20060422   @25654 Added a copy constructor.
    ** 003 GES 20060724   @28172 Use forced assign in copy constructor.
    ** 004 GES 20060807   @28476 Moved to externalizable interface to optimize network performance.
    ** 005 CA  20130123          Added c'tor which builds a new instance from a BaseDataType value,
    **                           if possible. This should be used only when constructing an instance
    **                           using the value returned by a DYNAMIC-FUNCTION call; 4GL does some
    **                           special casting in this case, which is not implemented yet.
    ** 006 GES 20130322          Fixed the BDT c'tor.
    ** 007 GES 20130523          Major rewrite to back this using a native memory buffer instead of
    **                           a byte[] on the Java heap. Added missing features.  Many deviations
    **                           from the 4GL were found and fixed. The actual data access and
    **                           storage is now in this class instead of in the parent, and this is
    **                           exposed via a set of abstract worker methods that we implement here.
    ** 008 CA  20130916          On P2J Client side, use the memory daemon directly; this allows
    **                           access to memptr instances from P2J Client side.
    ** 009 GES 20131212          Made isUninitialized() public.
    ** 010 MAG 20140602          Fixed the BDT c'tor and modified the assign(BDT,boolean) on _POLY
    **                           morphing. Add compareTo(Object).
    ** 011 CA  20140811          Fixed clearing of unknown flag in assignment/output param.
    ** 012 ECF 20150824          Implemented context cleanup processing to deallocate memory not
    **                           previously freed explicitly.
    **     CA  20150827          In memptr c'tors, if a memptr instance is passed, just copy its
    **                           pointer.
    ** 013 OM  20160705          Replaced deep copy with pointer copy and reference counter. Removed
    **                           negative allocation on WIN platform. Fixed toStringMessage() method.
    ** 014 CA  20160629          memptr vars can never be undoable.
    ** 015 OM  20160831          Optimized context-local accesses. Javadoc updates.
    ** 016 GES 20171207          Removed forced version of assign().
    ** 017 CA  20180214          Replaced workArea with lazy-initialization, as when transferred via
    **                           the socket, the Reader thread can't access context-local state
    **     GES 20180322          Added compiledWordSize().
    ** 018 ECF 20181214          Implemented LargeObject interface.
    ** 019 HC  20190302          Implemented emulation of GetWindowRect and GetClientRect Windows API
    **                           functions.
    ** 020 IAS 20190617          Changed the signature of the writeByteRange method
    ** 021 ECF 20190628          Implemented COPY-LOB runtime.
    ** 022 CA  20191203          Fixed overlay and unknown COPY-LOB related bugs.
    ** 023 IAS 20201007          Added Type enum.
    **     OM  20210404          Fine-tuned errors generated by edge-case in copy-lob statements.
    **     CA  20210428          Fixed conversion when the lvalue for an assign-styme stmt is a OO property
    **                           (chained or not). In this case, a setter must wrap the assign-style stmt, to
    **                           change the property's value.
    **     CA  20210818          Fixed memptr parameter runtime - they share the 'pointer structure' with the
    **                           passed argument.
    **     CA  20210823          A fix for CA/20210818 - assignment of a parameter to a non-unknown memptr will
    **                           clear the 'srcWasUnknown' flag.
    **     CA  20220217          If assign(memptr) is called for a memptr referencing the same address, do nothing.
    **     AL2 20220328          Added proxy checks for BDT.
    **     CA  20220515          Allow library and memptr calls to be executed on server-side.
    **     CA  20220920          A fix for CA/20220217 - assign to an unknown memptr must set this instance to
    **                           unknown, even if both their null address (0) matches.
    **     CA  20220922          The address space must always use a positive size (negative sizes are allowed on
    **                           linux).
    **     CA  20221020          'checkAddressSpace' must ignore 0 address number.
    **     CA  20221025          Added a protection against NegativeArraySizeException in clearAddressSpace.
    **     EVL 20221025          Small fix for message construction with two long values summing results.
    ** 024 CA  20230215          'instantiateDefault' now is implemented by each sub-class.
    **     CA  20230215          'duplicate()' method returns the real type instead of BDT.
    ** 025 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
    ** 026 CA  20230918          Cleanup the memory allocations only if on server-side or if the FWD client is
    **                           still running.
    ** 027 CA  20231017          Fixed 'writeByteRange' to consider the given length (number of bytes) to write.
    ** 028 OM  20231001          Avoid deprecated APIs.
    ** 029 GBB 20240826          Moving OSResourceManager to osresource package.
    ** 030 GBB 20240912          OSResourceManager method isServerSide to use an enum param.
    ** 031 GBB 20250403          asByteArray made public to be accessed from CoreAppserver.
    ** 032 ICP 20250328          Made writeByte, readByteRange, and writeByteRange public to enable
    **                           external direct memory manipulation (e.g., from MemoryOutputStream).
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.util;

    import java.io.*;
    import java.util.logging.*;

    import com.goldencode.p2j.util.logging.*;
    import com.goldencode.p2j.util.osresource.*;
    import org.roaringbitmap.longlong.*;

    import com.goldencode.p2j.net.*;
    import com.goldencode.p2j.security.*;

    /**
     * Implements the unique behavior of a Progress 4GL compatible {@code MEMPTR} data type
     * which is backed by a native platform memory buffer (instead of being allocated on the Java
     * heap). The contents of the buffer are mutable.  All Progress 4GL language features are
     * supported up to the 10.2B level.
     * <p>
     * Format string related processing is handled in method {@link #toString(String)}.
     * <p>
     * The following is the mapping of Progress language features to the corresponding feature in
     * this class:
     * <p>
     * <pre>
     * Progress Function               Java Method
     * -----------------------------   -----------------------
     * get-byte-order                  {@link #getByteOrder}
     * get-pointer-value               {@link #getPointerValue}
     * get-size                        {@link #length}
     *
     * Progress Language Statement     Java Method
     * -----------------------------   -----------------------
     * set-byte-order                  {@link #setByteOrder}
     * set-pointer-value               {@link #setPointerValue}
     * set-size                        {@link #setLength}
     * </pre>
     * <p>
     * All of the above features are specific to the {@code MEMPTR} type (they do not work for
     * {@code RAW} variables in the 4GL, such attempted usage will cause a compiler error).  All
     * other features are shared and are implemented in the common base class.
     */
    public class memptr
    extends BinaryData
    implements LargeObject
    {
       /** Logger */
       private static final CentralLogger LOG = CentralLogger.get(memptr.class.getName());

       /** No byte order has been explicitly requested. This is the default state of a memptr. */
       public static final int NO_BYTE_ORDER = 0;

       /** Operations should use the native byte order on the current machine. */
       public static final int HOST_BYTE_ORDER = 1;

       /** Operations should use big endian byte order. */
       public static final int BIG_ENDIAN = 2;

       /** Operations should use little endian byte order. */
       public static final int LITTLE_ENDIAN = 3;

       /** Smallest value accepted in SET-SIZE() without generating an error. */
       public static final int LOWER_BOUND = -2147483648;

       /** Largest value accepted in SET-SIZE() without generating an error. */
       public static final int UPPER_BOUND = 2147483647;

       /** Stores context local instances of the remote memory manager proxy. */
       private static final ContextLocal<WorkArea> ctxt = new ContextLocal<WorkArea>()
       {
          @Override
          protected WorkArea initialValue()
          {
             WorkArea wa = new WorkArea();

             wa.serverSide = OSResourceManager.getInstance().isServerSide(OsResourceType.MEMPTR);
             wa.bufMgr = OSResourceManager.getMemptr();

             wa.nativeByteOrder = wa.bufMgr.isLittleEndian() ? LITTLE_ENDIAN : BIG_ENDIAN;
             wa.compiledWordSize = wa.bufMgr.compiledWordSize();

             wa.winOS = EnvironmentOps.isUnderWindowsFamily();

             return wa;
          }

          @Override
          protected void cleanup(WorkArea wa)
          {
             try
             {
                if (wa.serverSide || SessionManager.get().getSession().isRunning())
                {
                   // cleanup only if the allocation is done on server-side or if the FWD client is still running
                   // (if it has terminated, the heap will be de-allocated along with the JVM process).
                   wa.bufMgr.cleanup();
                }
             }
             catch (SilentUnwindException exc)
             {
                // this will occur in cases where the cleanup is triggered from the server due to
                // the client being disconnected; we don't want to unwind and disrupt other cleanup,
                // so just log and eat the exception
                if (LOG.isLoggable(Level.FINE))
                {
                   LOG.log(Level.FINE, "Error cleaning up memptr context", exc);
                }
             }
          }
       };

       /**
        * The working area where this instance was created. Caches costly access to context local.
        * Lazy-initialized in {@link #locate()} as it can't be initialized by the c'tor - when
        * transported via the network, the Reader thread will not be able to access the context.
        */
       private WorkArea workArea;

       /** Pointer structure shared between parameters and arguments. */
       private memptrPointer pointer = new memptrPointer(NO_BYTE_ORDER, 0, 0);

       /** Flag indicating this is a parameter. */
       private boolean parameter = false;

       /** Flag indicating if the source argument for this parameter was unknown. */
       private boolean srcWasUnknown = false;

       /**
        * Default constructor, creates an instance that represents an uninitialized byte array (a
        * {@code null} array).
        */
       public memptr()
       {
       }

       /**
        * This is a special c'tor which should be used only when converting the
        * value returned by a function or method with polymorphic return type into the
        * expected type (i.e. DYNAMIC-FUNCTION()).  In such cases, the 4GL does some
        * automatic type conversion (see {@link #assign(BaseDataType)}).
        *
        * @param    value
        *           The value to be used for this instance.
        */
       public memptr(BaseDataType value)
       {
          this(); // initialize winOS

          if (value == null)
          {
             setUnknown();
             return;
          }

          // the value may be a proxy; make sure to unwrap and work with the real value
          value = value.val();

          if (value.isUnknown())
          {
             setUnknown();
          }
          else
          {
             if (value instanceof memptr)
             {
                copyPointer((memptr) value);
             }
             else
             {
                assign(value);
             }
          }
       }

       /**
        * Constructs an instance after copying the parameter's data into the internal representation
        * of this class.
        * <p>
        * If the parameter is {@code unknown}, this instance will be set to {@code unknown}.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public memptr(memptr value)
       {
          this(); // init win_os
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             if (value.val() instanceof memptr)
             {
                value = (memptr) value.val();
             }
             else
             {
                initialize(value);
                return;
             }
          }
          copyPointer(value);
       }

       /**
        * Constructs an instance after copying the parameter's data into the internal representation
        * of this class.
        * <p>
        * If the parameter is {@code null}, the instance will be {@code unknown}.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public memptr(byte[] value)
       {
          this(); // init win_os
          assign(value);
       }

       /**
        * Creates a new instance of the same type that represents {@code unknown}.
        *
        * @return   An instance that represents {@code unknown}.
        */
       public static memptr instantiateUnknownMemptr()
       {
          memptr m = new memptr();
          m.setUnknown();

          return m;
       }

       /**
        * Get the type
        * @return type
        */
       @Override
       public Type getType()
       {
          return Type.MEMPTR;
       }

       /**
        * Compares this instance with the specified instance and returns a -1 if this instance is less
        * than the specified, 0 if the two instances are equal and 1 if this instance is greater than
        * the specified instance.  This is the implementation of the {@code Comparable} interface.
        * <p>
        * The algorithm will fail to give meaningful results in the case where one tries to sort
        * against other objects that do not represent compatible values.
        *
        * @param   obj
        *          The instance to compare against.
        *
        * @return  -1, 0 or 1 depending on if this instance is less than, equal to or greater
        *          (respectively) than the specified instance {@code obj}.
        */
       @Override
       public int compareTo(Object obj)
       {
          if (obj instanceof BaseDataType)
          {
             return super.compareTo(new memptr((BaseDataType) obj));
          }

          return super.compareTo(obj);
       }

       /**
        * Does the same as standard {@code clone()} method but returns an instance of
        * {@code BaseDataType} and doesn't throw the {@code CloneNotSupportedException}.
        *
        * @return  A clone of this instance.
        */
       @Override
       public memptr duplicate()
       {
          memptr m = new memptr();

          // copy reference of the instance data
          m.copyPointer(this);

          return m;
       }

       /**
        * Creates a new instance of the same type that is {@code unknown}.
        *
        * @return  An instance that represents {@code unknown}.
        */
       @Override
       public BaseDataType instantiateUnknown()
       {
          memptr m = new memptr();
          m.setUnknown();

          return m;
       }

       /**
        * Creates a new instance of the same type that represents the default initialized value.
        *
        * @return   An instance that represents the default value.
        */
       @Override
       public BaseDataType instantiateDefault()
       {
          return new memptr();
       }

       /**
        * Reports if this instance represents <code>unknown</code>.
        *
        * @return   <code>true</code> if this instance is set to the
        *           <code>unknown</code>.
        */
       @Override
       public boolean isUnknown()
       {
          return unknown || pointer.unknown;
       }

       /**
        * Sets the state of this instance's <code>unknown</code> flag to <code>true</code>.
        * <p>
        * <b>Warning: the data stored in this instance WILL BE deallocated after calling this
        * method.</b>
        */
       @Override
       public void setUnknown()
       {
          super.setUnknown();
          this.pointer.unknown = true;
       }

       /**
        * Method implementation of the GET-POINTER-VALUE 4GL built-in function which returns a 64-bit
        * integer value representing the pointer used by the instance.  This is a real memory address
        * for the first byte of the buffer that has been allocated.  Of course, this is not only very
        * dangerous, but it is also not portable.  Yet, this is how the 4GL does it.
        *
        * @return   The 64-bit integer value representing the location in memory of the instance's
        *           buffer.  If {@code unknown}, the pointer will be {@code unknown}. If
        *           uninitialized, this will return 0.
        */
       public int64 getPointerValue()
       {
          // unknown takes precedence over being uninitialized
          if (isUnknown())
          {
             return new int64();
          }

          return new int64(pointer.addr);
       }

       /**
        * Sets the binary data to point to a memory address. The current size is left unchanged
        * and any current buffer is NOT deallocated. This is just how the 4GL implements it.
        * <p>
        * If the instance is {@code unknown}, then this call silently fails.  It does NOT change the
        * pointer value, the instance will still be {@code unknown}.  This is how the 4GL does it.
        *
        * @param    ptr
        *           The 64-bit value representing the pointer to the memory address. This may be
        *           any value (negative, 0 or a positive number). Since the 4GL has no unsigned
        *           64-bit data type, it is necessary negative numbers be supported since memory
        *           addresses are typically unsigned values. Even though most systems do not
        *           allocate valid memory at address 0 (so that 0 can be used as a well known
        *           invalid memory address), the 4GL does allow 0 to be used (perhaps it is needed
        *           on some systems).  If this value is {@code unknown}, the method will silently
        *           return with no change to the pointer value.  This is how the 4GL does it.
        *
        * @return   The current instance.
        */
       public memptr setPointerValue(NumberType ptr)
       {
          if (ptr == null || ptr.isUnknown())
          {
             // no error here, just silently return
             return this;
          }

          return setPointerValue(ptr.longValue());
       }

       /**
        * Sets the binary data to point to a memory address. The current size is left unchanged
        * and any current buffer is NOT deallocated. This is just how the 4GL implements it.
        * <p>
        * If the instance is {@code unknown}, then this call silently fails.  It does NOT change the
        * pointer value, the instance will still be {@code unknown}.  This is how the 4GL does it.
        *
        * @param    ptr
        *           The 64-bit value representing the pointer to the memory address. This may be
        *           any value (negative, 0 or a positive number). Since the 4GL has no unsigned
        *           64-bit data type, it is necessary negative numbers be supported since memory
        *           addresses are typically unsigned values. Even though most systems do not
        *           allocate valid memory at address 0 (so that 0 can be used as a well known
        *           invalid memory address), the 4GL does allow 0 to be used (perhaps it is needed
        *           on some systems).
        *
        * @return   The current instance.
        */
       public memptr setPointerValue(long ptr)
       {
          if (isUnknown())
          {
             // no error here, no change to the state at all, just silently return
             return this;
          }

          // protect this so only this context's memory addresses can be referred by the pointer
          checkAddressSpace(ptr);

          // all other values are accepted and saved off
          pointer.addr = ptr;

          return this;
       }

       /**
        * Reports the byte order mode configured in this instance.  By default (and contrary to the
        * Progress documentation), this is set to 0.  This will remain 0 until {@link #setByteOrder}
        * is called.  Since {@code setByteOrder} can only ever be called with 3 values, those
        * are the only possible values that can be returned from here.  The only exception to this
        * is that when the instance is {@code unknown}, then {@code unknown} will be
        * returned, no matter what has been previously configured.
        * <p>
        * The following are the possible return values:
        * <p>
        * <pre>
        * 4GL Constant        Java Constant       Value
        * ------------------  ------------------  ---------
        * n/a                 NO_BYTE_ORDER       0
        * HOST-BYTE-ORDER     HOST_BYTE_ORDER     1
        * BIG-ENDIAN          BIG_ENDIAN          2
        * LITTLE-ENDIAN       LITTLE_ENDIAN       3
        * </pre>
        * <p>
        * The fact that Progress returns 0 by default allows one can tell when this value has been
        * explicitly configured, since it is never possible to set it back to 0 via
        * {@code setByteOrder}.  When set to 0 or to HOST_BYTE_ORDER, the result is the same.
        * The native byte ordering of the current platform will be used.
        * <p>
        * This value is interesting in another way: it does not change when an instance is
        * de-allocated, re-allocated, set to {@code unknown} or assigned.  The configuration
        * value stays with the instance across all these events and thus it is not really
        * associated with a specific memory buffer, but rather with the variable instance itself.
        * Don't be fooled by the fact that this returns {@code unknown} when the instance is
        * {@code unknown}.  The byte order is still properly set (or defaulted) and when the
        * instance is no longer {@code unknown}, that value will be returned.
        *
        * @return   The byte-order setting of the instance.
        */
       public integer getByteOrder()
       {
          if (isUnknown())
          {
             return new integer();
          }

          return new integer(pointer.byteOrder);
       }

       /**
        * Defines the byte ordering scheme to use when reading/writing certain data types into the
        * memory buffer.  The following data types change behavior depending on byte ordering:
        * <p>
        * <pre>
        * DOUBLE
        * FLOAT
        * INT64
        * LONG
        * SHORT
        * UNSIGNED-LONG
        * UNSIGNED-SHORT
        * </pre>
        * <p>
        * For each of the above listed data types, both the get and the set will honor the byte
        * ordering.  This means that the only data types that do NOT honor byte ordering are
        * BYTE (a single byte being read or written), BYTES (a sequence of bytes) and STRING (a
        * sequence of characters).
        * <p>
        * Any valid value set through this method is retained for the lifetime of the instance,
        * even when it is set to {@code unknown} and/or de-allocated and then re-allocated.
        * Interestingly, the value does not get copied to the target instance during assignment,
        * so it is instance-specific.  This is all compatible with how the 4GL works.
        * <p>
        * The following are the only possible byte order modes that can be specified:
        * <p>
        * <pre>
        * 4GL Constant        Java Constant       Value
        * ------------------  ------------------  ---------
        * HOST-BYTE-ORDER     HOST_BYTE_ORDER     1
        * BIG-ENDIAN          BIG_ENDIAN          2
        * LITTLE-ENDIAN       LITTLE_ENDIAN       3
        * </pre>
        * <p>
        * This method returns without any changes if the instance is {@code unknown}.
        *
        * @param    order
        *           The new byte order mode.
        *
        * @return   The current instance is returned.
        */
       public memptr setByteOrder(int order)
       {
          if (isUnknown())
          {
             return this;
          }

          if (order == HOST_BYTE_ORDER || order == BIG_ENDIAN || order == LITTLE_ENDIAN)
          {
             pointer.byteOrder = order;
          }
          else
          {
             ErrorManager.recordOrThrowError(9259, "Invalid value for SET-BYTE-ORDER.");
          }
          return this;
       }

       /**
        * Defines the byte ordering scheme to use when reading/writing certain data types into the
        * memory buffer.  The following data types change behavior depending on byte ordering:
        * <p>
        * <pre>
        * DOUBLE
        * FLOAT
        * INT64
        * LONG
        * SHORT
        * UNSIGNED-LONG
        * UNSIGNED-SHORT
        * </pre>
        * <p>
        * For each of the above listed data types, both the get and the set will honor the byte
        * ordering.  This means that the only data types that do NOT honor byte ordering are
        * BYTE (a single byte being read or written), BYTES (a sequence of bytes) and STRING (a
        * sequence of characters).
        * <p>
        * Any valid value set through this method is retained for the lifetime of the instance,
        * even when it is set to {@code unknown} and/or de-allocated and then re-allocated.
        * Interestingly, the value does not get copied to the target instance during assignment,
        * so it is instance-specific.  This is all compatible with how the 4GL works.
        * <p>
        * The following are the only possible byte order modes that can be specified:
        * <p>
        * <pre>
        * 4GL Constant        Java Constant       Value
        * ------------------  ------------------  ---------
        * HOST-BYTE-ORDER     HOST_BYTE_ORDER     1
        * BIG-ENDIAN          BIG_ENDIAN          2
        * LITTLE-ENDIAN       LITTLE_ENDIAN       3
        * </pre>
        * <p>
        * If the parameter is {@code unknown}, then the instance itself will be set to
        * {@code unknown} and the byte order will NOT be changed.  Subsequent allocation of
        * a new buffer will retain the same byte order setting.  This is how the 4GL does it.
        * <p>
        * This method returns without any changes if the instance is {@code unknown}.
        *
        * @param    order
        *           The new byte order mode.
        */
       public void setByteOrder(NumberType order)
       {
          if (order == null || order.isUnknown())
          {
             setUnknown();
          }
          else
          {
             setByteOrder(order.intValue());
          }
       }

       /**
        * Sets the array to a given length.  If set to 0, this de-allocates any currently allocated
        * array (if no array is allocated, this does nothing).  If set to a positive number, this will
        * allocate a new array if no array is allocated, otherwise this method will do nothing.
        * <p>
        * This method will try to emulate the allocation based on the current (-ly configured) OS.
        * See {@link EnvironmentOps#isUnderWindowsFamily()}:
        * <ul>
        *    <li>On {@code WIN32} platform will only accept positive values when allocating a new
        *          native memory buffer. If the buffer is already allocated and a negative value is
        *          passed then this is a no-op;</li>
        *    <li>On {@code UNIX} will accept and apparently allocate {@code abs(len)} bytes.</li>
        * </ul>
        * <p>
        * On 32-bit Linux platforms, the 4GL "real lower bound" is -7 and not 0 as one would expect.
        * Since this is an obscure and platform-specific nonsense behavior which is unlikely to be
        * needed in a real application, it is not honored at this time (which leaves this code
        * simpler).
        *
        * @param    len
        *           New length of the array.
        *
        * @return   The current instance.
        */
       @Override
       public BinaryData setLength(long len)
       throws ErrorConditionException
       {
          if (parameter && srcWasUnknown)
          {
             genUninitializedError();
             return this;
          }

          if (isUnknown() && len == 0)
          {
             // if 'unknown' is set and 'len' is zero, do nothing.
             return this;
          }

          // generate an error if the size is out of bounds, this must occur before the test for
          // being uninitialized
          RangeCheck.check(len, LOWER_BOUND, UPPER_BOUND);

          WorkArea wa = locate();

          // are we already initialized?
          if (isUninitialized())
          {
             if (len != 0 && ((wa.winOS && len > 0) || !wa.winOS))
             {
                pointer.addr = wa.bufMgr.allocate(Math.abs(len)); // len may be negative only on linux

                // we save the size IF and ONLY IF the allocation succeeded
                if (pointer.addr != 0)
                {
                   // remember that size may be negative here, that is OK (yes, the 4GL actually
                   // reports the negative size on Linux, while on Windows the reported value is
                   // different, see the visibleLength() method)
                   pointer.size = len;
                   clearUnknown();
                   recordAddressSpace(pointer.addr, pointer.size);
                }
             }
             // if len is negative on windows, nothing happens
          }
          else
          {
             // this is a de-allocation request
             if (len == 0)
             {
                deallocate();
             }
             else
             {
                // if we are already initialized but the size was never set (this happens in the case
                // that setPointerValue() was used), then this call is supposed to store the size for
                // subsequent boundary checking purposes; once we set this value, we don't allow it
                // to be changed again without deallocating first
                if (pointer.size == 0)
                {
                   // this can be any non-zero value that can fit in an int (we already have range
                   // checked this, so we know there is no loss of data);
                   pointer.size = len;
                }
             }
          }

          return this;
       }

       /**
        * Reports if this instance represents an uninitialized buffer. Being uninitialized is not the
        * same as being of zero length.  Rather it means that the buffer was never allocated. Being
        * {@code unknown} is also different from being uninitialized.
        *
        * @return  {@code true} if this instance is uninitialized.
        */
       @Override
       public boolean isUninitialized()
       {
          return (pointer.addr == 0);
       }

       /**
        * Creates a string representation of the instance data, since a memptr represents a 'pointer'
        * it doesn't have a real string representation. If the instance represents {@code unknown}, a
        * '?' will be returned, otherwise the empty string will be returned.
        *
        * @return   The empty string or '?' if {@code unknown}.
        */
       @Override
       public String toString()
       {
          return toString(null);
       }

       /**
        * Creates a string representation of the instance data using the given format string, since a
        * memptr represents a 'pointer' it doesn't have a real string representation and the format
        * string is meaningless. If the instance represents {@code unknown}, a '?' will be returned,
        * otherwise the empty string will be returned.
        *
        * @param    fmt
        *           The format string to use.
        *
        * @return   The empty string or '?' if {@code unknown}.
        */
       @Override
       public String toString(String fmt)
       {
          if (isUnknown() || isUninitialized())
          {
             return "?";
          }

          return getString(1).toStringMessage();
       }

       /**
        * Creates a string representation of the instance data in a form that is compatible with the
        * {@code MESSAGE} language statement.  If the instance represents {@code unknown}, a '?' will
        * be returned.
        * <p>
        * This is equivalent to {@link #toString}.
        *
        * @return   The empty string or '?' if {@code unknown}.
        */
       @Override
       public String toStringMessage()
       {
          return toString();
       }

       /**
        * Creates a string representation of the instance data using the 'export' format.  If the
        * instance represents {@code unknown}, a '?' will be returned.  Any other data is directly
        * output into the string and no formatting or conversion of binary data is done.
        *
        * @return   The binary data in the instance as a string.
        */
       @Override
       public String toStringExport()
       {
          if (isUnknown())
          {
             return "?";
          }

          return new String(readByteRange(0, findNextNull(0), false));
       }

       /**
        * Replacement for the default object reading method. The latest state is read from the input
        * source.  The parent's state is read first.
        *
        * @param    in
        *           The input source from which fields will be restored.
        *
        * @throws   IOException
        *           In case of I/O errors.
        * @throws   ClassNotFoundException
        *           If payload can't be instantiated.
        */
       @Override
       public void readExternal(ObjectInput in)
       throws IOException,
              ClassNotFoundException
       {
          super.readExternal(in);
          pointer.byteOrder = in.readInt();
          pointer.addr      = in.readLong();
          pointer.size      = in.readLong();
       }

       /**
        * Replacement for the default object writing method. The latest state is written to the output
        * destination.  The parent's state is written first.
        *
        * @param    out
        *           The output destination to which fields will be saved.
        *
        * @throws   IOException
        *           In case of I/O errors.
        */
       @Override
       public void writeExternal(ObjectOutput out)
       throws IOException
       {
          super.writeExternal(out);
          out.writeInt(pointer.byteOrder);
          out.writeLong(pointer.addr);
          out.writeLong(pointer.size);
       }

       /**
        * Write the given byte data into the data managed by this object, overwriting the data
        * that are at the given location, if any.
        * <p>
        * This implementation:
        * <ul>
        * <li>ignores the {@code trim} option (i.e., any existing bytes beyond the end of the
        *     write are left in place, regardless of the value of {@code trim});</li>
        * <li>raises an error if the write goes past the end of the existing value.</li>
        * </ul>
        *
        * @param   overlay
        *          Flag indicating if the OVERLAY option is used.
        * @param   data
        *          Data to be assigned to this object.
        * @param   offset
        *          Offset position (in characters) in the target large object.
        * @param   trim
        *          {@code true} to truncate any remaining data in the target large object; {@code
        *          false} to leave remaining data alone. Ignored.
        */
       @Override
       public void write(boolean overlay, byte[] data, int offset, boolean trim)
       {
          int dataLen;

          if (data == null || (dataLen = data.length) == 0)
          {
             if (!overlay)
             {
                assign(data);
             }
             // else nothing to write; leave existing state alone

             return;
          }
          else if (overlay && isUninitialized())
          {
             ErrorManager.recordOrThrowError(11393);
             // MEMPTR target of COPY-LOB must be initialized. (11393)

             return;
          }

          if (pointer.size < offset + dataLen)
          {
             ErrorManager.recordOrThrowError(11394);
             // MEMPTR target of COPY-LOB is not big enough. (11394)

             return;
          }

          // simple case: just replace existing value with data
          if (!overlay || isUnknown() || (offset == 0 && pointer.size == dataLen))
          {
             assign(data);

             return;
          }

          // complex case: overlay data over existing data, leave any remaining, original data intact
          writeByteRange(data, offset, dataLen, false);
       }

       /**
        * Indicate whether this object manages character data (as opposed to binary data).
        *
        * @return  {@code false}.
        */
       @Override
       public boolean isCharacterData()
       {
          return false;
       }

       /**
        * Return the entire contents of the current buffer as an array. This may be a copy of the
        * data, depending on the subclass' implementation.  DO NOT MODIFY the data that is returned,
        * since it is undefined as to whether the changes will or will not be reflected in the actual
        * storage.
        *
        * @param   pos
        *          Starting offset position.
        * @param   len
        *          Length of range to return.
        *
        * @return  The contents of the instance (possibly a copy). If the instance is uninitialized
        *          the returned value will be a 0 length array. If the instance is {@code unknown}
        *          or has an undefined size, then {@code null} will be returned.
        */
       @Override
       public byte[] asByteArray(long pos, long len)
       {
          byte[] copy = null;

          if (!isUnknown())
          {
             if (isUninitialized())
             {
                copy = new byte[0];
             }
             else
             {
                if (pointer.size >= 1)
                {
                   copy = locate().bufMgr.read(pointer.addr, pos, Math.min(pointer.size, len));
                }
             }
          }

          return copy;
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * If the given instance is <code>null</code> or <code>unknown</code>, the instance will be
        * set to <code>unknown</code>.  If the length of the given data is 0 OR if the length of the
        * an initialized buffer is indeterminate, then the instance will be reset as uninitialized
        * (including deallocation of any current buffer). If the length of the array is greater than
        * 0, then any current buffer will be deallocated, a new buffer of the proper size will be
        * allocated and the data will be copied in. This is how the 4GL does it.
        *
        * @param    data
        *           The instance from which to copy state.
        */
       @Override
       public void assign(BinaryData data)
       {
          if (BaseDataType.isProxy(data))
          {
             assign(data.val());
             return;
          }

          if (data instanceof memptr)
          {
             if (data.isUnknown())
             {
                setUnknown();
                return;
             }

             if (parameter)
             {
                srcWasUnknown = false;
                // allow assignment from here
             }

             if (((memptr) data).pointer.addr == pointer.addr)
             {
                // same memory address, nothing to do.
                return;
             }
          }

          super.assign(data);
       }

       /**
        * Report if the byte ordering mode of the platform is little endian.
        *
        * @param   workArea
        *          The optional context to obtain endian information. If not provided, the current
        *          context is used.
        *
        * @return  {@code true} if the platform natively uses little endian byte ordering and
        *          {@code false} if it uses big endian.
        */
       protected static boolean isLittleEndianPlatform(WorkArea workArea)
       {
          if (workArea == null)
          {
             workArea = ctxt.get();
          }
          return workArea.nativeByteOrder == LITTLE_ENDIAN;
       }

       /**
        * Returns the word size (the size of a pointer) for the native code as compiled. This may be
        * different than the word size of the operating system in the case where 32-bit code is run
        * on a 64-bit system.
        *
        * @return   This value will be either 32 or 64 depending on how the native library was
        *           compiled. If there was an error in inspecting this value, -1 will be returned.
        */
       protected static int compiledWordSize()
       {
          // can't use locate() in static context
          WorkArea wa = ctxt.get();

          return wa.compiledWordSize;
       }

       /**
        * Setup this instance to act as a parameter; this shares the {@link #pointer} with the source memptr.
        *
        * @param    src
        *           The source argument.
        */
       protected void asParameter(memptr src)
       {
          this.unknown = src.unknown;
          this.pointer = src.pointer;
          this.parameter = true;
          this.srcWasUnknown= src.isUnknown();
       }

       /**
        * Sets the state of this instance's <code>unknown</code> flag to <code>false</code>.
        */
       @Override
       protected void clearUnknown()
       {
          super.clearUnknown();
          this.pointer.unknown = false;
       }

       /**
        * Release any resources associated with the instance.
        */
       @Override
       protected void deallocate()
       {
          if (pointer.addr != 0)
          {
             locate().bufMgr.deallocate(pointer.addr);
             clearAddressSpace(pointer.addr, pointer.size);

             pointer.addr = 0;
             pointer.size = 0;
          }
       }

       /**
        * Determines if the array of binary data stored in an instance of this class must be limited
        * in length.
        *
        * @return    {@code true} if the length must be limited.
        */
       @Override
       protected boolean isLengthLimited()
       {
          return false;
       }

       /**
        * Determines the maximum length of the array of binary data stored in an instance of this
        * class.
        *
        * @return    Always 0 (this class has no pre-determined limit on size).
        */
       @Override
       protected long lengthLimit()
       {
          return 0;
       }

       /**
        * Determines if the array of binary data stored in an instance of this class must be
        * automatically extended in length when operations write past the current length.  Such
        * extensions will pad with {@code null} bytes.
        *
        * @return    {@code true} if the length must automatically extended.
        */
       @Override
       protected boolean isAutoExtend()
       {
          return false;
       }

       /**
        * Helper to create a new byte array with the same contents as the given byte array and the
        * specified length.  If the given target length is smaller than the source array length, the
        * output array is truncated to the specified size.  If the target length is larger than the
        * source array length, the output array is extended to the given size and any additional bytes
        * are initialized to '\0'.
        * <p>
        * This is safe to use on 0 length arrays.
        *
        * @param    len
        *           The output array length.
        */
       @Override
       protected void extendBytes(long len)
       {
          // nothing to do, this is only used in auto-extend sub-classes
       }

       /**
        * Replace the entire contents of the current buffer with the given array.
        * <p>
        * The {@link BinaryData#unknown} flag is also cleared, only if {@code value} is not
        * {@code null} and if the length is greater than 0.
        *
        * @param    value
        *           The new contents.
        */
       @Override
       protected void replaceContents(byte[] value)
       {
          if (value == null)
          {
             setUnknown();
          }
          else
          {
             deallocate();

             if (value.length > 0)
             {
                // allocates memory, clears unknown and saves size
                setLength(value.length);

                // copy the data
                writeByteRange(value, 0, -1, false);

                clearUnknown();
             }
          }
       }

       /**
        * Return the entire contents of the current buffer as an array. This may be a copy of the
        * data, depending on the subclass' implementation.  DO NOT MODIFY the data that is returned,
        * since it is undefined as to whether the changes will or will not be reflected in the actual
        * storage.
        *
        * @return  The contents of the instance (possibly a copy). If the instance is uninitialized
        *          the returned value will be a 0 length array. If the instance is {@code unknown}
        *          or has an undefined size, then {@code null} will be returned.
        */
       @Override
       public byte[] asByteArray()
       {
          return asByteArray(0, pointer.size);
       }

       /**
        * The actual size of the buffer (in bytes).
        *
        * @return  The length or 0 if uninitialized.
        */
       @Override
       protected long internalLength()
       {
          return pointer.size;
       }

       /**
        * The the length of the buffer as reported to P4GL application.
        *
        * @return  The length or 0 if uninitialized.
        */
       @Override
       protected long visibleLength()
       {
          if (pointer.size >= 0)
          {
             return pointer.size;
          }

          // when negative, in linux it is reported as is, but on windows the complement is returned
          // (remember that size is limited to a 32-bit signed value in the 4GL, to return it as an
          // unsigned value in a long, we clear the most significant 4 bytes which is equivalent to
          // 0x00000000FFFFFFFF & size)
          return locate().winOS ? 0x1_0000_0000L + pointer.size : pointer.size;
       }

       /**
        * Returns the next index position into the array which is the {@code null} byte, starting at a
        * given index position.
        *
        * @param   pos
        *          The 0-based index into the array at which to start searching.
        *
        * @return  The next index into the array which is the {@code null} byte or the length of the
        *          array if no {@code null} byte exists.
        */
       @Override
       protected long findNextNull(long pos)
       {
          // we must pass -1 as the size if we don't know what the real size is, this means the code
          // will read until it finds a null byte or gets an access violation, this is how the 4GL
          // does it too
          long limit = (pointer.size < 1) ? -1 : pointer.size;

          return locate().bufMgr.findNextNull(pointer.addr, pos, limit);
       }

       /**
        * Read the byte at the given location.  All boundary checking and error handling must have
        * already been done in the caller such that this is known to be a valid access. Any
        * unexpected failure may cause a platform-specific exception that terminates the process!
        *
        * @param    pos
        *           The 0-based index position at which to read the byte.
        *
        * @return   The byte at that location.
        */
       @Override
       protected byte readByte(long pos)
       {
          byte[] subset = locate().bufMgr.read(pointer.addr, pos, 1);
          return subset[0];
       }

       /**
        * Write the byte into the given location. All boundary checking and error handling must have
        * already been done in the caller such that this is known to be a valid access. Any
        * unexpected failure may cause a platform-specific exception that terminates the process!
        *
        * @param    val
        *           The byte to write.
        * @param    pos
        *           The 0-based index position at which to read the byte.
        */
       @Override
       public void writeByte(byte val, long pos)
       {
          byte[] subset = new byte[1];
          subset[0] = val;
          locate().bufMgr.write(pointer.addr, pos, subset);
       }

       /**
        * Read the bytes at the given location.  All boundary checking and error handling must have
        * already been done in the caller such that this is known to be a valid access. Any
        * unexpected failure may cause a platform-specific exception that terminates the process!
        *
        * @param    pos
        *           The 0-based index position at which to read the bytes.
        * @param    len
        *           The number of bytes to read.
        * @param    endian
        *           {@code true} to force the returned data to be ordered by the endian-ness of the
        *           instance.
        *
        * @return   The byte range at that location.
        */
       @Override
       public byte[] readByteRange(long pos, long len, boolean endian)
       {
          byte[] data = locate().bufMgr.read(pointer.addr, pos, len);

          // reverse the byte order if needed
          if (endian && !isLittleEndian())
          {
             data = reverseBytes(data);
          }

          return data;
       }

       /**
        * Write the bytes at the given location.  All boundary checking and error handling must have
        * already been done in the caller such that this is known to be a valid access. Any
        * unexpected failure may cause a platform-specific exception that terminates the process!
        *
        * @param    data
        *           The bytes to write.
        * @param    pos
        *           The 0-based index position at which to start writing the bytes.
        * @param    len
        *           How many bytes should be written. -1 means write everything to the end of the
        *           array. This is the implementation of the {@link BinaryData#writeByteRange}
        *           method. The len parameter was added to fix bug in the {@link raw#writeByteRange}.
        *           For the memptr is is intentionally ignored.
        * @param    endian
        *           {@code true} to honor the endian-ness of the instance. The {@code data}
        *           array MUST be ordered in little-endian order if the endian flag is on.
        */
       @Override
       public void writeByteRange(byte[] data, long pos, long len, boolean endian)
       {
          // reverse the byte order if needed
          if (endian && !isLittleEndian())
          {
             data = reverseBytes(data);
          }

          if (len > 0 && len < data.length)
          {
             byte[] aux = new byte[(int) len];
             System.arraycopy(data, 0, aux, 0, (int) len);
             data = aux;
          }

          locate().bufMgr.write(pointer.addr, pos, data);
       }

       /**
        * Check if this instance is undoable.  For {@link memptr} case, these instances can never be
        * undoable.
        *
        * @return   Always {@code false}
        */
       @Override
       protected boolean isUndoable()
       {
          return false;
       }

       /**
        * Copy the specified number of bytes from the source instance into the current instance at
        * the given location.  All boundary checking and error handling must have already been
        * done in the caller such that this is known to be a valid access. Any unexpected failure
        * may cause a platform-specific exception that terminates the process!
        * <p>
        * This method is implemented completely on the client side to avoid multiple (costly) round
        * trips.
        *
        * @param    data
        *           The source of data to copy.
        * @param    pos
        *           The 0-based index position in the destination at which to write the bytes.
        * @param    len
        *           The number of bytes to write.
        */
       protected void copyByteRange(memptr data, long pos, long len)
       {
          locate().bufMgr.copy(data.pointer.addr, pointer.addr, pos, len);
       }

       /**
        * Returns the low-level memory manager.
        *
        * @return  see above.
        */
       static LowLevelBuffer getMemoryManager()
       {
          return ctxt.get().bufMgr;
       }

       /**
        * Report if the effective byte ordering mode is little endian.
        *
        * @return   {@code true} if the instance should use little endian byte ordering and
        *           {@code false} if the instance should use big endian.
        */
       private boolean isLittleEndian()
       {
          if (pointer.byteOrder == LITTLE_ENDIAN || pointer.byteOrder == BIG_ENDIAN)
          {
             // explicit setting should be honored
             return (pointer.byteOrder == LITTLE_ENDIAN);
          }
          else
          {
             // NO_BYTE_ORDER and HOST_BYTE_ORDER both will force the native platform
             // ordering to be used
             return isLittleEndianPlatform(locate());
          }
       }

       /**
        * Copy the pointer details from the specified instance.
        *
        * @param   m
        *          The {@link memptr} instance which needs to be duplicated.
        */
       private void copyPointer(memptr m)
       {
          this.pointer.addr = m.pointer.addr;
          this.pointer.byteOrder = m.pointer.byteOrder;
          this.pointer.size = m.pointer.size;
          this.pointer.unknown = m.pointer.unknown;
          this.unknown = m.unknown;
          if (pointer.addr != 0)
          {
             locate().bufMgr.incrementReference(pointer.addr);
          }
       }

       /**
        * Resolve the context-local {@link #workArea} variable (if not yet resolved) and return it.
        *
        * @return   The {@link #workArea}.
        */
       private WorkArea locate()
       {
          if (workArea == null)
          {
             workArea = ctxt.get();
          }

          return workArea;
       }

       /**
        * When memptr is on server-side, allocated address spaces are recorded for each context, to prohibit
        * cross-context usage of pointer addresses.
        * <p>
        * This method raises an ERROR condition if the context tries to {@link #setPointerValue set a pointer} to
        * an address which was not allocated in this context.
        *
        * @param    ptr
        *           The memory address.
        */
       private void checkAddressSpace(long ptr)
       {
          WorkArea wa = locate();

          if (ptr == 0 || !wa.serverSide)
          {
             return;
          }

          if (wa.addressSpace.contains(ptr))
          {
             return;
          }

          String msg = "Memory violation: trying to SET-POINTER-VALUE to an address unallocated in this context.";
          LOG.log(Level.SEVERE, msg);

          ErrorManager.recordOrThrowError(-100, msg, false);
       }

       /**
        * When memptr is on server-side, allocated address spaces are recorded for each context, to prohibit
        * cross-context usage of pointer addresses.
        * <p>
        * This records the allocated address space in this context.
        *
        * @param    ptr
        *           The memory address.
        * @param    size
        *           The allocated size.
        */
       private void recordAddressSpace(long ptr, long size)
       {
          WorkArea wa = locate();

          if (!wa.serverSide)
          {
             return;
          }

          size = Math.abs(size);

          wa.addressSpace.addRange(ptr, ptr + size);
       }

       /**
        * When memptr is on server-side, allocated address spaces are recorded for each context, to prohibit
        * cross-context usage of pointer addresses.
        * <p>
        * This clears the allocated address space in this context.
        *
        * @param    ptr
        *           The memory address.
        * @param    size
        *           The allocated size.
        */
       private void clearAddressSpace(long ptr, long size)
       {
          WorkArea wa = locate();

          if (!wa.serverSide)
          {
             return;
          }

          size = Math.abs(size);

          try
          {
             // create a bitmap with the address space being cleared
             wa.cleaner.addRange(ptr, ptr + size);
             // remove it from the allowed address space
             wa.addressSpace.andNot(wa.cleaner);

             wa.cleaner.clear();
          }
          catch (NegativeArraySizeException ex)
          {
             if (LOG.isLoggable(Level.SEVERE))
             {
                LOG.log(Level.SEVERE,
                        "Exception while clearing address space [" + ptr + ":" + (ptr + size) + "] at " + ptr +
                        " with size " + size,
                        ex);
             }
          }
       }

       /**
        * Container for context-local data.
        */
       private static class WorkArea
       {
          /** Flag indicating if the memptr is handled on server-side. */
          private boolean serverSide;

          /** Remote proxy for access and management of memory buffers. */
          private LowLevelBuffer bufMgr = null;

          /** Stores the byte order of the native platform being used. */
          private int nativeByteOrder = 0;

          /** Stores the word size of the compiled code in the client. */
          private int compiledWordSize = -1;

          /** Stores the OS information for this context. */
          private boolean winOS = false;

          /** The allocated address space in this context. */
          private final Roaring64Bitmap addressSpace = new Roaring64Bitmap();

          /**
           * A helper instance to clean the {@link #addressSpace}, when a pointer is
           * {@link memptr#deallocate() deallocated}.
           */
          private final Roaring64Bitmap cleaner = new Roaring64Bitmap();
       }

       /**
        * A data structure with the details about the 'pointer' referenced by this instance.
        */
       private static class memptrPointer
       {
          /** The byte order used for this instance. */
          private int byteOrder;

          /** The actual platform-specific pointer to our memory region (on the client). */
          private long addr;

          /** Stores the size that was last set for querying and boundary checking. */
          private long size;

          /** Flag indicating if the instance is unknown. */
          private boolean unknown = false;

          /**
           * Initialize this structure with the specified details.
           *
           * @param    byteOrder
           *           The byte order used for this instance.
           * @param    addr
           *           The actual platform-specific pointer to our memory region (on the client).
           * @param    size
           *           Stores the size that was last set for querying and boundary checking.
           */
          public memptrPointer(int byteOrder, long addr, long size)
          {
             this.byteOrder = byteOrder;
             this.addr = addr;
             this.size = size;
          }
       }
    }