docs: def read(self, nRec=1):
          """Retrieve the values of a number of records, starting
          at the current record position. The current record position
          is advanced by the number of records read. Current position
          is 0 after "opening" the vdata with the attach() method.
  
          Args::
  
            nRec    number of records to read
  
  
          Returns::
  
            2-level list. First level is a sequence of records,
            second level gives the sequence of values for each record.
            The values returned for each record are those of the fields
            specified in the last call to method setfields(), in that
            order. The complete vdata field set is returned if
            setfields() has not been called.
  
          An exception is raised if the current record position is
          already at the end of the vdata when read() is called. This
          exception can be caught as an "end of vdata" indication to
          exit a loop which scans each record of the vdata. Otherwise,
          the number of records to be read is lowered to the number of
          records remaining in the vdata, if that number is less than
          the number asked for by parameter 'nRec'. Setting 'nRec' to
          an arbitrarily large value can thus be used to retrieve the
          remaining records in the vdata.
  
          C library equivalent : VSread
                                                         """
          # Validate number of records to read vs the current offset.
          # Return "end of vdata" exception if already at end of vdata
          # otherwise "clip" the number of records if it exceeds the
          # number of remaining records in the vdata.
          n = self._nrecs
          if self._offset == n:
              raise HDF4Error("end of vdata reached")
          if self._offset + nRec > n:
              nRec = self._offset + nRec - n
  
          fields = self._setfields or self._fields
          nFields = len(fields)
          fieldList = ','.join(fields)
          _checkErr('read', _C.VSsetfields(self._id, fieldList),
                    'error defining fields to read')
  
          # Allocate a buffer to store the packed records.
          bufSize = self.sizeof(fields) * nRec
          bigBuf = _C.array_byte(bufSize)
  
          # Read records
          nRead = _C.VSread(self._id, bigBuf, nRec, 0)   # 0: FULL_INTERLACE
          _checkErr('read', nRead, 'read error')
          self._offset += nRec
  
          # Allocate an array to store a pointer to the field buffer.
          fldArr = _C.new_array_voidp(1)
  
          # Initialize return value
          values = []
          for numRec in range(nRead):
              v = []
              for numFld in range(nFields):
                  v.append(None)
              values.append(v)
  
          # Unpack each field in turn.
          for numFld in range(nFields):
              fld = self.field(fields[numFld])
              data_type = fld._type
              order = fld._order
              n_values = order * nRead
  
              # Allocate a buffer to store the field values.
              if data_type in [HC.CHAR8, HC.UCHAR8, HC.UINT8]:
                  buf = _C.array_byte(n_values)
  
              elif data_type == HC.INT8:
                  buf = _C.array_int8(n_values)
  
              elif data_type == HC.INT16:
                  buf = _C.array_int16(n_values)
  
              elif data_type == HC.UINT16:
                  buf = _C.array_uint16(n_values)
  
              elif data_type == HC.INT32:
                  buf = _C.array_int32(n_values)
  
              elif data_type == HC.UINT32:
                  buf = _C.array_uint32(n_values)
  
              elif data_type == HC.FLOAT32:
                  buf = _C.array_float32(n_values)
  
              elif data_type == HC.FLOAT64:
                  buf = _C.array_float64(n_values)
  
              else:
                  raise HDF4Error("read: illegal or unupported type %d" % \
                                   data_type)
  
              # Unpack the field values.
              _C.array_voidp_setitem(fldArr, 0, buf)
              _checkErr('read',
                        _C.VSfpack(self._id, 1, fieldList, bigBuf, bufSize,
                                   nRead, fld._name, fldArr),
                        "cannot execute")
  
              # Extract values from the field buffer.
              k = 0
              for numRec in range(nRead):
                  if order == 1:
                      values[numRec][numFld] = buf[k]
                      k += 1
                  else:
                      # Handle strings specially
                      if data_type == HC.CHAR8:
                          s = ''
                          for i in range(order):
                              v = buf[k]
                              if v != 0:
                                  s += chr(v)
                              k += 1
                          values[numRec][numFld] = s
                      # Return field values as a list
                      else:
                          values[numRec][numFld] = []
                          for i in range(order):
                              values[numRec][numFld].append(buf[k])
                              k += 1
  
              del buf
  
          return values