cmsv_wdata.pro 13.2 KB
;+
; NAME:
;   CMSV_WDATA
;
; AUTHOR:
;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
;   craigm@lheamail.gsfc.nasa.gov
;
; PURPOSE:
;   Write SAVE-formatted data variable record to output block or file
;
; CALLING SEQUENCE:
;   CMSV_WDATA, BLOCK, POINTER, DATA, UNIT=UNIT, TEMPORARY=TEMPORARY, $
;               PTR_INDEX=PTR_INDEX, PTR_DATA=PTR_DATA, $
;               OFFSET=OFFSET, STATUS=STATUS, ERRMSG=ERRMSG
;   
; DESCRIPTION: 
;
;   CMSV_WDATA writes the data portion of an IDL SAVE variable record.
;   An IDL variable is stored in two components: the type descriptor
;   which describes the name, type, and dimensions of the variable;
;   and the data record, which contains the raw data of the variable.
;   This procedure writes the raw data to the output.  The initial
;   type descriptor portion of the record must have already been
;   writtenusing the CMSV_WVTYPE procedure.
;
;   Under normal circumstances a user will write variable or heap data
;   using the CMSV_WREC procedure.
;
;   CMSV_WDATA supports the following variable types:
;
;     BYTE(1),INT(2),LONG(3) - integer types
;     UINT(12),ULONG(13),LONG64(14),ULONG64(15) - integer types (IDL >5.2 only)
;     FLOAT(4),DOUBLE(5),COMPLEX(6),DCOMPLEX(9) - float types
;     STRING(7) - string type
;     STRUCT(8) - structure type
;     POINTER(10) - pointer type - SEE BELOW
;     NOT SUPPORTED - OBJ(11) - object reference type - NOT SUPPORTED
;
;   Arrays and structures containing any of the supported types are
;   supported (including structures within structures).
;
;   The caller must specify in the DATA parameter, the data to be
;   written to output.  The variable passed as DATA must have the same
;   type and dimensions as passed to CMSV_WVTYPE.
;
;   Unlike most of the other output routines, this procedure is able
;   to send its output to a file rather than to the BLOCK buffer.  If
;   the UNIT keyword is specified then output is sent to that file
;   UNIT, after any pending BLOCK data is first sent.  Users should
;   note that after such operations, the BLOCK POINTER and OFFSET
;   parameters may be modified (ie reset to new values).
;
;   See CMSV_WREC for instructions on how to write heap data.
;
;   [ This code assumes the record header and type descriptor have
;   been written with CMSV_WREC and CMSV_WVTYPE. ]
;
; ==================================================================
;   Research Systems, Inc. has issued a separate license intended
;   to resolve any potential conflict between this software and the
;   IDL End User License Agreement. The text of that license
;   can be found in the file LICENSE.RSI, included with this
;   software library.
; ==================================================================
;
;
; BLOCK, POINTER, OFFSET
;
;   This procedure writes data to a byte array or a file.  If the UNIT
;   keyword is specified then file is sent to the specified unit
;   number rather than to the buffer BLOCK.  However, the intent is
;   for users to accumulate a significant amount of data in a BLOCK
;   and then write it out with a single call to WRITEU.  Users should
;   be aware that the block can be larger than the buffered data, so
;   they should use something like the following:
;
;          WRITEU, UNIT, BLOCK(0:POINTER-1)
;
;   When library routines do indeed write buffered BLOCK data to disk,
;   they will appropriately reset the BLOCK and POINTER.  Namely,
;   BLOCK will be reset to empty, and POINTER will be reset to zero.
;   OFFSET will be advanced the according number of bytes.
;
;   The terminology is as follows: BLOCK is a byte array which
;   represents a portion of, or an entire, IDL SAVE file.  The block
;   may be a cached portion of an on-disk file, or an entire in-memory
;   SAVE file.  POINTER is the current file pointer within BLOCK
;   (i.e., the next byte to be read is BLOCK[POINTER]).  Hence, a
;   POINTER value of 0 refers to the start of the block.  OFFSET is
;   the file offset of the 0th byte of BLOCK; thus "POINT_LUN,
;   OFFSET+POINTER" should point to the same byte as BLOCK[POINTER].
;   The following diagram shows the meanings for BLOCK, POINTER and
;   OFFSET schematically:
;
;
;                 0 <-  OFFSET  -> |
;   FILE          |----------------|------*--------|--------->
;
;   BLOCK                          |------*--------|
;                                  0      ^ POINTER
;     
;
;   This procedure is part of the CMSVLIB SAVE library for IDL by
;   Craig Markwardt.  You must have the full CMSVLIB core package
;   installed in order for this procedure to function properly.  
;
;
; INPUTS:
;
;   BLOCK - a byte array, a cache of the SAVE file.  Users will
;           usually not access this array directly.  Users are advised
;           to clear BLOCK after calling POINT_LUN or writing the
;           block to disk.
;
;   POINTER - a long integer, a pointer to the next byte to be read
;             from BLOCK.  CMSVLIB routines will automatically
;             advance the pointer.
;
;   DATA - the data to be written, of any save-able data type.
;
; KEYWORDS:
;
;   TEMPORARY - if set, then the input DATA are discarded after being
;               written, as a memory economy provision.
;
;   PTR_INDEX - a heap index array for the data being written, if any
;               heap data records have been written.
;               Default: no pointers are written
;
;   PTR_DATA - an array of pointers, pointing to the heap values being
;              written.
;              Default: no pointers are written
;
;   UNIT - a file unit.  If specified then data are directed to the
;          file unit rather than to the buffer BLOCK.
;
;   OFFSET - the file offset of byte zero of BLOCK.  
;            Upon output, if the file pointer is advanced, OFFSET will
;            also be changed.
;            (OFFSET is not currently used by this routine)
;            Default: 0
;
;   STATUS - upon return, this keyword will contain 1 for success and
;            0 for failure.
;
;   ERRMSG - upon return with a failure, this keyword will contain the
;            error condition as a string.
;
; EXAMPLE:
;
;
; SEE ALSO:
;
;   CMRESTORE, SAVE, RESTORE, CMSVLIB
;
; MODIFICATION HISTORY:
;   Written, 2000
;   Documented, 24 Jan 2001
;   Added notification about RSI License, 13 May 2002, CM
;   Added support for byte scalars and arrays (!), 27 Mar 2006, CM
;   Bug fix for multi-dimensional byte array, 2013-04-18, CM
;   NOTE: remember to modify CMSVLIB.PRO when changing library!
;
; $Id: cmsv_wdata.pro,v 1.11 2013/04/18 19:14:08 cmarkwar Exp $
;
;-
; Copyright (C) 2000-2001, 2006, 2013, Craig Markwardt
; This software is provided as is without any warranty whatsoever.
; Permission to use, copy, modify, and distribute modified or
; unmodified copies is granted, provided this copyright and disclaimer
; are included unchanged.
;-

; ---------------- Convert from host to network order ------------------
pro cmsv_wconv, data

  ;; Inspired by IDL Astronomy Library routine HOST_TO_IEEE
  common cmsv_conv_common, lendian
  if n_elements(lendian) EQ 0 then begin
      ;; Little-endian?
      lendian = (long(['01'xb,'02'xb,'03'xb,'04'xb],0,1))(0) NE '01020304'xl
  endif

  sz = size(data)
  case sz(sz(0)+1) of 
      1:  return                    ;; Byte
      2:  byteorder, data, /HTONS   ;; Integer
      3:  byteorder, data, /HTONL   ;; Long
      4:  byteorder, data, /FTOXDR  ;; Float
      5:  byteorder, data, /DTOXDR  ;; Double
      6:  byteorder, data, /FTOXDR  ;; Complex
      9:  byteorder, data, /DTOXDR  ;; DComplex
      12: byteorder, data, /HTONS   ;; UInt
      13: byteorder, data, /HTONL   ;; ULong
      14: if lendian EQ 1 then byteorder, data, /L64SWAP ;; LONG64
      15: if lendian EQ 1 then byteorder, data, /L64SWAP ;; ULONG64
      ELSE: 
  endcase

end

pro cmsv_wdata, block, pointer, value, unit=unit, temporary=temp, $
                ptr_index=pi, ptr_data=pd, start=start, $
                status=status, errmsg=errmsg

  ;; VAR_DATA
  ;;   LONG - START_DATA TOKEN - value 7
  ;;   for bytes - consecutive bytes
  ;;   for (u)ints - upcast to type long 
  ;;   for (u)longs - consecutive longs
  ;;   for pointers - consecutive longs, indices into saved heap data
  ;;   for strings - consecutive STRING's
  ;;   for structs - compacted versions of above
  forward_function byte, long, ulong, float, double, ptr_new

  data = 0
  if n_elements(start) EQ 0 then start = 1
  if n_elements(pointer) EQ 0 then pointer = 0L

  if keyword_set(start) then begin
      cmsv_wraw, /long, block, pointer, 7L, unit=unit, $
        status=status, errmsg=errmsg
      if status EQ 0 then return
  endif
  sz = size(value)
  tp = sz(sz(0)+1)
  if (tp EQ 11) then begin
      status = 0
      errmsg = 'ERROR: CMSV_WDATA: cannot write object data'
      return
  endif
  nelt = sz(sz(0)+2)

  if (tp EQ 8) then begin   ;; Structure type
      tn = tag_names(value(0)) & nt = n_elements(tn)

      for i = 0L, nelt-1 do begin
          for j = 0L, nt-1 do begin
              cmsv_wdata, block, pointer, value(i).(j), start=0, $
                status=status, errmsg=errmsg
              if status EQ 0 then return
          endfor

          ;; Occasionally flush the data to disk
          if keyword_set(start) AND n_elements(unit) GT 0 then begin
              if (i EQ nelt-1) OR (pointer GT 65536L) then begin
                  writeu, unit(0), block(0:pointer-1)
                  pointer = 0L
                  block = 0 & dummy = temporary(block)
              endif
          endif
      endfor

      return
  endif

  if tp EQ 7 then begin     ;; String type
      cmsv_wraw, /string, block, pointer, value, /replen, $
        status=status, errmsg=errmsg
      if status EQ 0 then return

      if (n_elements(unit) GT 0 AND keyword_set(start) $
          AND pointer GT 0) then begin
          writeu, unit(0), block(0:pointer-1)
          pointer = 0L
          block = 0 & dummy = temporary(block)
      endif
      return
  endif

  ;; Sometimes the input data is stored as a different type
  common cmsv_datatypes, stype, sbyte, nbyte, selts
  if n_elements(stype) EQ 0 then begin
      ;;      0      1       2       3        4        5         6    7   8
      ;;          byte     int    long    float    double   complex str struct
      stype =['','BYTE', 'LONG', 'LONG', 'FLOAT', 'DOUBLE', 'FLOAT', '', '' ]
      sbyte =[0,     1,      4,      4,       4,        8,       4,   0,  0 ]
      selts =[0,     1,      1,      1,       1,        1,       2,   0,  0 ]

      ;;                  9     10   11     12       13        14
      ;;            dcomplex   ptr   obj   uint     ulong    long64   ulong64
      stype =[stype,'DOUBLE', 'LONG', '', 'ULONG', 'ULONG', 'LONG64','ULONG64']
      sbyte =[sbyte,      8,     4,   0,     4,       4,        8,        8, 0]
      selts =[selts,      2,     1,   0,     1,       1,        1,        1, 0]
  endif

  status = 0
  nb = sbyte(tp<16)
  if nb EQ 0 then begin
      errmsg = 'ERROR: CMSV_WDATA: cannot write specified type ('+strtrim(tp,2)+')'
      return
  endif
  nelt1 = nelt*selts(tp<16)     ;; Account for complex type
  nb = nb*nelt1                 ;; Number of total bytes

  if keyword_set(temp) then data = temporary(value) $
  else                      data = value
  if sz(0) GT 0 then data = reform(data, /overwrite)
  
  ;; Convert from pointer type to LONG
  psz = size(pd)
  if (tp EQ 10) then begin
      odata = temporary(data)
      null = ptr_new()
      ;; Initialize as null pointers
      if sz(0) GT 0 then data = lonarr(nelt) else data = 0L

      ;; Fill in the pointers if we know about it
      if (n_elements(pi) GT 0) AND (n_elements(pi) EQ n_elements(pd)) $
        AND (psz(psz(0)+1) EQ 10) then begin
          for i = 0L, nelt-1 do if odata(i) NE null then begin
              wh = where(odata(i) EQ pd, ct)
              if ct GT 0 then begin
                  data(i) = abs(pi(wh(0)))
                  pi(wh(0)) = -pi(wh(0))
              endif
          endif
      endif
      odata = 0
  endif

  ;; Error handler returns zero and error condition
  on_ioerror, WRITE_ERROR
  if 0 then begin
      WRITE_ERROR:
      errmsg = 'ERROR: CMSV_WDATA: a write error occurred'
      return
  end

  ;; Special conversions
  if (tp EQ 1)  then begin
      ;; BYTE data type: make a special header which 
      ;; contains the number of bytes;  also, round 
      ;; the number of storage bytes up to the next
      ;; 4-byte word boundary.
      data1 = long(nelt)
      cmsv_wconv, data1
      data = [byte(data1,0,4),reform(data,nelt,/overwrite)]

      if nb MOD 4 NE 0 then data = [temporary(data), bytarr(4-(nb MOD 4))]
      nb = n_elements(data)
  endif else begin
      if (tp EQ 2)  then data = long(temporary(data))
      if (tp EQ 12) then data = ulong(temporary(data))
      if (tp EQ 6)  then data = float(temporary(data), 0, nelt1)
      if (tp EQ 9)  then data = double(temporary(data), 0, nelt1)
      cmsv_wconv, data
  endelse

  if n_elements(unit) GT 0 then begin
      ;; Write data to file directly
      if n_elements(pointer) GT 0 then begin
          writeu, unit(0), block(0:pointer-1)
          pointer = 0L
          block = 0 & dummy = temporary(block)
      endif
      writeu, unit(0), data
  endif else begin
      ;; Write data to byte buffer

      cmsv_wraw, block, pointer, byte(temporary(data), 0, nb), /byte, $
        status=status, errmsg=errmsg
  endelse

  on_ioerror, NULL

  status = 1
  return
end