cmsv_rrec.pro 18.7 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
;+
; NAME:
;   CMSV_RREC
;
; AUTHOR:
;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
;   craigm@lheamail.gsfc.nasa.gov
;
; PURPOSE:
;   Read SAVE-formatted record header from input block or file unit
;
; CALLING SEQUENCE:
;   CMSV_RREC, BLOCK, POINTER, DATA, UNIT=UNIT, $
;        BLOCK_TYPE=BLOCK_TYPE, BLOCK_NAME=BLOCK_NAME, NEXT_BLOCK=NEXT_BLOCK, $
;        INITIALIZE=INITIALIZE, FULL=FULL, PROMOTE64=PROMOTE64, $
;        OFFSET=OFFSET, STATUS=STATUS, ERRMSG=ERRMSG
;   
; DESCRIPTION: 
;
;   This procedure reads the header of an IDL SAVE record.  The header
;   consists of four bytes at the beginning of each record which
;   indentifies the type and size of the record.  This procedure also
;   additionally reads many full records as noted below.
;
;   Users can determine the block type by examining the values
;   returned in the BLOCK_TYPE and BLOCK_NAME keywords.  The following
;   values are supported.  Some record types contain additional data.
;   CMSV_RREC reads some of these record types automatically (those
;   entries below marked with [this procedure]).  Other records do not
;   contain any additional data at all (those entries marked with an
;   [empty]), and thus require no further processing.
;
;    BLOCK_TYPE      BLOCK_TYPE         READ RECORD DATA WITH...
;
;       0         = 'START_MARKER'      [empty]
;       1         = 'COMMON_BLOCK'      [this procedure]
;       2         = 'VARIABLE'          CMSV_RVTYPE / CMSV_RDATA
;       3         = 'SYSTEM_VARIABLE'   CMSV_RVTYPE / CMSV_RDATA
;       6         = 'END_MARKER'        [empty]
;       10        = 'TIMESTAMP'         [this procedure]
;       12        = 'COMPILED'          no published procedure
;       13        = 'IDENTIFICATION'    [this procedure]
;       14        = 'VERSION'           [this procedure]
;       15        = 'HEAP_INDEX'        [this procedure]
;       16        = 'HEAP_DATA'         CMSV_RVTYPE
;       17        = 'PROMOTE64'         [empty]
;       19        = 'NOTICE'            [this procedure]
;
;   For records that contain variable data, the external procedures
;   CMSV_RVTYPE and CMSV_RDATA must be used, as noted above.
;   Otherwise CMSV_RREC will read and convert the appropriate data
;   automatically and return it in the DATA positional parameter.
;
;   The offset of the next record is returned in the NEXT_BLOCK
;   keyword.  For file input, the command, "POINT_LUN, UNIT,
;   NEXT_BLOCK" will position the file pointer to the next block.
;
;   Users should be aware that the SAVE files produced by IDL version
;   5.4 appear to have a different header format.  The new header size
;   is five bytes, and is incompatible with the older format.  In
;   order to activate the longer header size, the PROMOTE64 keyword
;   must be set.
;
;   By default the entire record is not read from the file at once.
;   Users that wish to operate on the entire record immediately should
;   set the FULL keyword.
;
;   After issuing a POINT_LUN the block cache in BLOCK must be reset
;   using the /INITIALIZE keyword.
;
; ==================================================================
;   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.
; ==================================================================
;
; SPECIFIC RECORD TYPES
;
;   CMSV_RREC reads certain specific record types automatically and
;   returns the data in the positional parameter data.  Users should
;   pass a named variable in this parameter to retrieve the return
;   value.
;
;   When a record of type 'VERSION' (14) is encountered, it is read,
;   and returned as a structure. The returned data are of the form:
;
;         { FORMAT_VERSION: 0L, $   ; Format version number of file
;           ARCH: '', $             ; !VERSION.ARCH of creating host
;           OS: '', $               ; !VERSION.OS of creating host
;           RELEASE: '' }           ; !VERSION.RELEASE of creating host
;
;   When a record of type 'TIMESTAMP' (10) is encountered, it is read,
;   and returned as a structure. The returned data are of the form:
;
;          { SAVE_DATE: '', $  ; Date the save file was created
;            SAVE_USER: '', $  ; User name who  created file
;            SAVE_HOST: '' }   ; Host name that created file
;
;   Save files created by IDL version 4 do not contain a timestamp
;   record.
;
;   When a record of type 'IDENTIFICATION' (13) is encountered, it is
;   read, and returned as a structure.  The returned data are of the
;   form:
;
;          { AUTHOR: '', $  ; Author of SAVE file
;            TITLE:  '', $  ; Title of SAVE file
;            IDCODE: '' }   ; Identifying code for SAVE file
;
;   It appears that this record is not used in IDL version 5 or later.
;
;   When a record of type 'COMMON_BLOCK' (1) is encountered, it is
;   read and returned.  A common block descriptor consists of an array
;   of strings whose first element is the common block name, and whose
;   remaining elements are the common block variable names.  No
;   variable data are stored with the common block definition.
;
;   When a record of type 'HEAP_INDEX' (15) is encountered, it is read
;   and returned in DATA.  The heap index specifies a list of which
;   heap variables are stored in the current save file.  These indices
;   are simply numbers which identify each heap variable (i.e.,
;   "<PtrHeapVar2>" would have an index of 2).  Users should note that
;   the heap index will not necessarily be sequentially increasing,
;   and may have gaps.
;
;   When a record of type 'NOTICE' (19) is encountered, it is read and
;   returned in DATA.  It is a structure with one field: {TEXT: ''},
;   where TEXT is the text content of the notice.
;
;   Users should consult CMSV_RDATA for instructions on how to read
;   heap data.
;   
;
; BLOCK, POINTER, OFFSET
;
;   This procedure can read data from a byte array, a file unit, or
;   both.  In fact, this procedure is designed to implement "lazy"
;   reading from a file, which is to say, it normally reads from a
;   byte array of data.  However, if the requested data goes beyond
;   the end of the byte array, more data is read from the file on
;   demand.  This way the user gets the benefit of fast memory access
;   for small reads, but guaranteed file access for large reads.
;
;   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.
;
;   POINTER - a long integer, a pointer to the next byte to be read
;             from BLOCK.  CMSVLIB routines will automatically
;             advance the pointer.
;
;
; KEYWORDS:
;
;   UNIT - a file unit.  If a library routine reads to the end of
;          BLOCK, or if BLOCK is undefined, then this file UNIT will
;          be accessed for more data.  If undefined, then BLOCK must
;          contain the entire file in memory.
;
;   OFFSET - the file offset of byte zero of BLOCK.  Default: 0
;            (OFFSET is used by this routine)
;
;   BLOCK_TYPE - upon return, the numeric record type, as described
;                above.
;
;   BLOCK_NAME - upon return, a scalar string specifying the record
;                type, as specified above.
;
;   NEXT_BLOCK - upon return, file offset of the next record in the
;                file.
;
;   INITIALIZE - if set, then BLOCK and POINTER are initialized to a
;                pristine state.  All data in these two variables is
;                lost before reading the next record.
;
;   FULL - if set, then the entire record will be read into BLOCK.
;
;   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
;   Fix typo for RHEAP call, CM, 21 Apr 2001
;   Added notification about RSI License, 13 May 2002, CM
;   Added NOTICE record type, 09 Jun 2003, CM
;   Read record header as ULONG, 26 Sep 2009, CM
;   Bug fix to previous change, 22 Nov 2009, CM
;   Support for newer IDL 64-bit files which use a 
;    "standard" record header size and a previously unused
;    field, 11 Jan 2010, CM
;   NOTE: remember to modify CMSVLIB.PRO when changing library!
;
; $Id: cmsv_rrec.pro,v 1.15 2010/01/11 08:58:13 craigm Exp $
;
;-
; Copyright (C) 2000-2001, 2003, 2009, 2010, 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.
;-

; ----------------- Read common block descriptor ----------------------

pro cmsv_rcomm, block, pointer, names, unit=unit, offset=offset, $
                status=status, errmsg=errmsg

  status = 0
  if n_elements(pointer) EQ 0 then pointer = 0L
  names = 0 & dummy = temporary(names)
  
  ncommon = cmsv_rraw(/long, block, pointer, unit=unit, $
                      status=status, errmsg=errmsg)
  if status EQ 0 then return
  if ncommon LE 0 then begin
      status = 0
      errmsg = 'ERROR: CMSV_RREC: invalid common record block'
      return
  endif

  names = cmsv_rraw(/string, block, pointer, ncommon+1, unit=unit, $
                    status=status, errmsg=errmsg)
  return
end


; ----------------- Read heap index ---------------------------------

pro cmsv_rheap, block, pointer, index, unit=unit, offset=offset, $
                status=status, errmsg=errmsg

  ;; HEAP_INDEX
  ;;   LONG - N_HEAP - number of heap values
  ;;   LONGxN_HEAP - heap indices

  if n_elements(pointer) EQ 0 then pointer = 0L
  index = 0 & dummy = temporary(index)

  n_heap = cmsv_rraw(/long, block, pointer, unit=unit, $
                     status=status, errmsg=errmsg)
  if status then $
    index = cmsv_rraw(/long, block, pointer, n_heap, unit=unit, $
                      status=status, errmsg=errmsg)

  return
end

; ------------------------ Read time stamp record -----------------------

pro cmsv_rstamp, block, pointer, tstamp, unit=unit, offset=offset, $
                 status=status, errmsg=errmsg

  ;; TIMESTAMP
  ;;   BYTEx400 - empty (?) legacy area
  ;;   STRING - save date (as a string)
  ;;   STRING - user name
  ;;   STRING - hostname
  status = 0
  if n_elements(pointer) EQ 0 then pointer = 0L
  pointer = pointer + '400'xl
  tstamp = 0 & dummy = temporary(tstamp)

  strings = cmsv_rraw(/string, block, pointer, 3, unit=unit, $
                      status=status, errmsg=errmsg)
  if status EQ 0 then return

  tstamp = {save_date: strings(0), save_user: strings(1), save_host:strings(2)}
  return
end


; ---------------------- Read Version Info ---------------------------

pro cmsv_rversion, block, pointer, vers, unit=unit, offset=offset, $
                   status=status, errmsg=errmsg
  ;; VERSION_STAMP
  ;;   LONG - Major version number
  ;;   STRING_DATA - Host architecture ( = !version.arch )
  ;;   STRING_DATA - Host OS ( = !version.os )
  ;;   STRING_DATA - IDL release ( = !version.release )

  if n_elements(pointer) EQ 0 then pointer = 0L
  major_release = 5
  vers = 0 & dummy = temporary(vers)

  arch = '' & os = '' & release = ''
  major_release = cmsv_rraw(/long, block, pointer, unit=unit, $
                            status=status, errmsg=errmsg)
  if status EQ 0 then return

  strings = cmsv_rraw(/string, block, pointer, 3L, status=status, $
                      unit=unit, errmsg=errmsg)
  if status EQ 0 then return
  status = 1
  
  vers = {format_version: major_release, arch: strings(0), $
          os: strings(1), release: strings(2)}
  return
end

; --------------------------- Read Identification --------------------

pro cmsv_rident, block, pointer, ident, unit=unit, offset=offset, $
                 status=status, errmsg=errmsg

  ;; IDENT
  ;;   STRING - author
  ;;   STRING - title
  ;;   STRING - idcode

  strings = cmsv_rraw(/string, block, pointer, 3, unit=unit, $
                      status=status, errmsg=errmsg)
  if status EQ 0 then return

  ident = {author: strings(0), title: strings(1), idcode:strings(2)}
  return
end

; --------------------------- Read Notice --------------------

pro cmsv_rnotice, block, pointer, notice, unit=unit, offset=offset, $
                  status=status, errmsg=errmsg

  ;; NOTICE
  ;;   STRING - notice text

  string = cmsv_rraw(/string, block, pointer, 1, unit=unit, $
                      status=status, errmsg=errmsg)
  if status EQ 0 then return

  notice = {text: string}
  return
end


; ---------------------------- Main Read Routine ---------------------

pro cmsv_rrec, block, pointer, data, unit=unit, offset=offset, $
               status=status, errmsg=errmsg, compressed=compressed, $
               block_type=blocktype, block_name=blockname, next_block=np, $
               initialize=init, full=full, promote64=prom, $
               qblocknames=qblock

  common cmsave_block_names, block_ntypenames, block_typenames
  if n_elements(block_ntypenames) EQ 0 then begin
      block_ntypenames    = 20
      block_typenames     = strarr(block_ntypenames+1)+'UNKNOWN'
      block_typenames(0)  = 'START_MARKER'
      block_typenames(1)  = 'COMMON_BLOCK'
      block_typenames(2)  = 'VARIABLE'
      block_typenames(3)  = 'SYSTEM_VARIABLE'
      block_typenames(6)  = 'END_MARKER'
      block_typenames(10) = 'TIMESTAMP'
      block_typenames(12) = 'COMPILED'
      block_typenames(13) = 'IDENTIFICATION'
      block_typenames(14) = 'VERSION'
      block_typenames(15) = 'HEAP_INDEX'
      block_typenames(16) = 'HEAP_DATA'
      block_typenames(17) = 'PROMOTE64'
      block_typenames(19) = 'NOTICE'
  endif

  if keyword_set(qblock) then begin
      data = block_typenames
      return
  end

  status = 0
  errmsg = ''

  if n_elements(pointer) EQ 0 then pointer = 0L
  pointer = floor(pointer(0))
  if keyword_set(init) then begin
      block = 0 & dummy = temporary(block)
      pointer = 0L
  endif

  pointer0 = pointer
  op = pointer0  ;; "OP" = old pointer - points to curr record
  if n_elements(offset) GT 0 then op = op + offset(0)

  nlongs = 4L
  if keyword_set(prom) then nlongs = 5 ;; Special case of PROMOTE64
  rechead = cmsv_rraw(block, pointer, nlongs, unit=unit(0), $
                      status=status, errmsg=errmsg, type='ULONG')
  if status EQ 0 then return
  
  blocktype = rechead(0)
  blockname = block_typenames(blocktype < block_ntypenames)

  ;; "NP" = next pointer - points to next record
  np = rechead(1)

  ;; We assume this is a 64-bit pointer for two possibilities:
  ;;    1. the next pointer (32-bit) is less than "old pointer"
  ;;    2. PROMOTE64 has been set
  if (np LT op) OR keyword_set(prom) then begin
     ;; Compute new offset by combining two long values
     np = rechead(1) + rechead(2)*'100000000'XULL
     ;; Promote OFFSET
     if n_elements(offset) NE 0 then offset = ulong64(offset)

     ;; Sanity check on PROMOTE64-style headers
     if keyword_set(prom) then if rechead(4) NE 0 then begin
          errmsg = 'ERROR: CMSV_RREC: inconsistent 64-bit header'
          status = 0
          return
     endif
  endif


  if keyword_set(doprom) then begin
      ;; If file offset is to be promoted to 64-bit then we compute it
      ;; here.  The additional logic is to preserve 32-bit offsets in
      ;; most cases, and promote only if absolutely needed.  Signal an
      ;; error in earlier versions of IDL that don't support 64-bit
      ;; numbers.
      np = rechead(2) + rechead(1)
      if rechead(1) NE 0 AND rechead(2) NE 0 then begin
          if double(!version.release) LT 5.2D then begin
              errmsg = ('ERROR: CMSV_RREC: file contains a 64-bit file '+ $
                        'offset which is unstorable by this version of IDL')
              status = 0
              return
          endif
          np = cmsv_rraw(/long, block, pointer-3L*4L, type='ULONG64', $
                         status=status, errmsg=errmsg)
          if status EQ 0 then return
      endif

      ;; A 64-bit header has an extra long in it.  Read that now.
      ;; It should be zero.
      val0 = cmsv_rraw(/long, block, pointer, status=status, errmsg=errmsg)
      if status EQ 0 then return
      if val0 NE 0 then begin
          errmsg = 'ERROR: CMSV_RREC: inconsistent 64-bit header'
          status = 0
          return
      endif

  endif

  ;; Activate 64-bit promotion.  Blocks after this current one will
  ;; have 64-bit file offsets rather than 32-bit ones.
  if blocktype EQ 17 then prom = 1

  if keyword_set(full) then begin
      if n_elements(offset) EQ 0 then offset = 0L
      nbytes = np - (pointer0 + floor(offset(0)))
      if nbytes GT 0 then begin
          dummy = cmsv_rraw(block, 0L, nbytes, /byte, /buffer, unit=unit, $
                            status=status, errmsg=errmsg)
          if status EQ 0 then return
          block = block(0:nbytes-1)
      endif
  endif

  ;; Can't read compressed data for the moment
  if keyword_set(compressed) then begin
      status = 1
      return
  endif

  case blockname of 
      'COMMON_BLOCK': cmsv_rcomm, block, pointer, data, unit=unit, $
        status=status, errmsg=errmsg
      'HEAP_INDEX': cmsv_rheap, block, pointer, data, unit=unit, $
        status=status, errmsg=errmsg
      'VERSION': cmsv_rversion, block, pointer, data, unit=unit, $
        status=status, errmsg=errmsg
      'TIMESTAMP': cmsv_rstamp, block, pointer, data, unit=unit, $
        status=status, errmsg=errmsg
      'IDENTIFICATION': cmsv_rident, block, pointer, data, unit=unit, $
        status=status, errmsg=errmsg
      'NOTICE': cmsv_rnotice, block, pointer, data, unit=unit, $
        status=status, errmsg=errmsg
      ELSE: status = 1
  end

  return
end