subroutine classic_recordbuf_open(file,rstart,wstart,buf,error)
  use gbl_message
  use classic_interfaces, except_this=>classic_recordbuf_open
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  ! Open the buffer for reading a new object
  !---------------------------------------------------------------------
  type(classic_file_t),      intent(in)    :: file    !
  integer(kind=8),           intent(in)    :: rstart  ! First record of the object
  integer(kind=4),           intent(in)    :: wstart  ! First word in this record
  type(classic_recordbuf_t), intent(inout) :: buf     !
  logical,                   intent(inout) :: error   !
  ! Local
  character(len=*), parameter :: rname='RECORDBUF'
  character(len=message_length) :: mess
  integer(kind=4) :: ier
  logical :: same
  !
  same = file%lun.eq.buf%lun .and. rstart.eq.buf%rstart+buf%roff
  !
  buf%rstart = rstart
  buf%wstart = wstart
  buf%nrec = 1  ! ZZZ not exact for an input buffer
  !
  buf%lun = file%lun
  buf%roff = 0
  !
  if (same) then
    ! The data(:) buffer already contains a copy of the 'rstart' record
    return
  endif
  !
  if (file%desc%nextword.eq.1) then
    ! Next available space starts at the begining of the (new) record nextrec
    if (buf%rstart.ge.file%desc%nextrec) then
      buf%data(:) = 0
      return
    endif
  else
    ! Next available space starts somewhere in the (already existing) record nextrec
    if (buf%rstart.gt.file%desc%nextrec) then
      buf%data(:) = 0
      return
    endif
  endif
  !
  ! Starting record is already used in the file: load its previous content
  read(buf%lun,rec=buf%rstart,iostat=ier) buf%data
  if (ier.ne.0) then
    write(mess,'(A,I0)') 'Read error record #',buf%rstart
    call classic_message(seve%e,rname,mess)
    call classic_iostat(seve%e,rname,ier)
    error = .true.
    ! Avoid letting incorrect bytes in this buffer. Forget the record
    ! we tried to read:
    call classic_recordbuf_nullify(buf)
    return
  endif
  !
end subroutine classic_recordbuf_open
!
subroutine recordbuf_read(addr,len,vec,buf,error)
  use gbl_message
  use classic_interfaces
  use classic_params
  use classic_types
  !----------------------------------------------------------------------
  ! @ no-interface (because of argument type mismatch), private
  !   Read the vector vec in the current observation.
  !   Working buffer is INOUT since it is not re-read when possible.
  ! 'buf%rstart' (the reference record from which we count the words)
  ! must have been correctly set before.
  !----------------------------------------------------------------------
  integer(kind=data_length), intent(in)    :: addr    ! Starting address (from beginning of object)
  integer(kind=data_length), intent(in)    :: len     ! Length of buffer
  integer(kind=4),           intent(out)   :: vec(*)  ! Buffer to be read
  type(classic_recordbuf_t), intent(inout) :: buf     ! Working buffer
  logical,                   intent(inout) :: error   ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='RBUF'
  character(len=message_length) :: mess
  integer(kind=8) :: add,kbufi,kbula,ikbfi,ikbla,ikb,jadd,kadd
  integer(kind=4) :: ier,len4
  !
  if (len.le.0) return
  !
  ! Convert address (from beginning of object) to address (from beginning of
  ! first record)
  add = buf%wstart+addr-1
  !
  ikbfi = (add-1)/buf%len          ! First buffer address (offset from buf%rstart, i.e. start at 0)
  kbufi = add-buf%len*ikbfi        ! First word address in this first buffer (remainder of integer division)
  ikbla = (add+len-2)/buf%len      ! Last buffer address (offset from buf%rstart, i.e. start at 0)
  kbula = add+len-1-buf%len*ikbla  ! Last word address in this last buffer (remainder of integer division)
  !
  if (ikbla+1.gt.buf%nrec) then
    write(mess,'(A,I0,A,I0,A)')  &
      'Internal error: read record #',ikbla+1,' beyond object limit (',buf%nrec,')'
    call classic_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ! Read first buffer if not current one
  if (ikbfi.ne.buf%roff) then
     buf%roff = ikbfi
     read(buf%lun,rec=buf%rstart+buf%roff,err=20,iostat=ier) buf%data
  endif
  !
  ! Single buffer
  if (ikbla.eq.ikbfi) then
     len4 = len
     call w4tow4(buf%data(kbufi),vec,len4)
     return
  endif
  !
  ! Move words from first buffer
  len4 = buf%len-kbufi+1
  call w4tow4(buf%data(kbufi),vec,len4)
  !
  ! Read contiguous buffers if needed
  jadd = buf%len-kbufi+2  ! First word in vec(:)
  kadd = jadd+buf%len-1   ! Last word in vec(:)
  do ikb = ikbfi+1,ikbla-1
     ! Do not waste time reading each record into data(:) and then
     ! copying it into the needed portion of vec(:). Read in vec(:)
     ! directly. We should worry about having a correct data(:)
     ! at the boundary records only (before and after this loop)
     read(buf%lun,rec=buf%rstart+ikb,err=20,iostat=ier) vec(jadd:kadd)
     jadd = jadd+buf%len
     kadd = kadd+buf%len
  enddo
  !
  ! Read last buffer
  buf%roff = ikbla
  read(buf%lun,rec=buf%rstart+buf%roff,err=20,iostat=ier) buf%data
  len4 = kbula
  call w4tow4(buf%data,vec(jadd),len4)
  return
  !
20 error = .true.
  write(mess,'(A,I0)') 'Read error record #',buf%rstart+buf%roff
  call classic_message(seve%e,rname,mess)
  call classic_iostat(seve%e,rname,ier)
  ! Avoid letting incorrect bytes in this buffer. Forget the record
  ! we tried to read:
  call classic_recordbuf_nullify(buf)
end subroutine recordbuf_read
!
subroutine recordbuf_write(addr,len,vec,buf,error)
  use gbl_message
  use classic_interfaces
  use classic_params
  use classic_types
  !----------------------------------------------------------------------
  ! @ no-interface (because of argument type mismatch), private
  !  Write the input data buffer 'VEC' to the output file. This is done
  ! by filling data blocks mirroring the file records and writing them
  ! to the file. Care is taken when 'VEC' overlaps file records.
  !----------------------------------------------------------------------
  integer(kind=data_length), intent(in)    :: addr    ! Starting address (from beginning of object)
  integer(kind=data_length), intent(in)    :: len     ! Length of buffer
  integer(kind=4),           intent(in)    :: vec(*)  ! Buffer to be written
  type(classic_recordbuf_t), intent(inout) :: buf     ! Working buffer
  logical,                   intent(inout) :: error   ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='WBUF'
  character(len=message_length) :: mess
  integer(kind=8) :: add,kbufi,kbula,ikbfi,ikbla,ikb,jadd,kadd
  integer(kind=4) :: ier,len4
  !
  if (len.le.0) return
  !
  ! Convert address (from beginning of object) to address (from beginning of
  ! first record)
  add = buf%wstart+addr-1
  !
  ikbfi = (add-1)/buf%len          ! First buffer address (offset from buf%rstart, i.e. start at 0)
  kbufi = add-buf%len*ikbfi        ! First word address in this first buffer (remainder of integer division)
  ikbla = (add+len-2)/buf%len      ! Last buffer address (offset from buf%rstart, i.e. start at 0)
  kbula = add+len-1-buf%len*ikbla  ! Last word address in this last buffer (remainder of integer division)
  !
  ! Write current buffer if not first one, and read first one
  if (ikbfi.ne.buf%roff) then
     ! Flush buf%data content to its buf%roff record
     write (buf%lun,rec=buf%rstart+buf%roff,err=21,iostat=ier) buf%data
     !
     buf%roff = ikbfi
     if (buf%roff+1.le.buf%nrec) then
        ! We access a record already used (totally or partially). Reload.
        ! NB: buf%roff numbered from 0 (offset), while nrec numbered from 1 (number)
        read(buf%lun,rec=buf%rstart+buf%roff,err=22,iostat=ier) buf%data
     else
        buf%data(:) = 0
     endif
  endif
  !
  ! Single buffer
  if (ikbla.eq.ikbfi) then
     ! Everything goes in the same record: fill appropriate portion of buffer
     ! 'buf%data', it will be written to file later.
     len4 = len
     call w4tow4(vec,buf%data(kbufi),len4)
     buf%nrec = max(buf%nrec,buf%roff+1)
     return
  endif
  !
  ! Move words into first buffer
  len4 = buf%len-kbufi+1
  call w4tow4(vec,buf%data(kbufi),len4)
  !
  ! Write contiguous buffers if needed
  write (buf%lun,rec=buf%rstart+buf%roff,err=21,iostat=ier) buf%data
  jadd = buf%len-kbufi+2  ! First word in vec(:)
  kadd = jadd+buf%len-1   ! Last word in vec(:)
  do ikb = ikbfi+1,ikbla-1
     ! Do not waste time copying the needed portion of vec(:) into
     ! buf%data(:), and then writing data(:). Write the portion of
     ! vec(:) directly. We should worry about having a correct data(:)
     ! at the boundary records only (before and after this loop)
     write (buf%lun,rec=buf%rstart+ikb,err=21,iostat=ier) vec(jadd:kadd)
     jadd = jadd+buf%len
     kadd = kadd+buf%len
  enddo
  !
  ! Read last buffer
  buf%roff = ikbla
  if (buf%roff+1.le.buf%nrec) then
     ! We access a record already used (totally or partially). Reload.
     ! NB: buf%roff numbered from 0 (offset), while nrec numbered from 1 (number)
     read (buf%lun,rec=buf%rstart+buf%roff,err=22,iostat=ier) buf%data
  else
     buf%data(:) = 0
  endif
  len4 = kbula
  call w4tow4(vec(jadd),buf%data,len4)
  buf%nrec = max(buf%nrec,buf%roff+1)
  return
  !
21 error = .true.
  write(mess,'(A,I0)') 'Write error record #',buf%rstart+buf%roff
  call classic_message(seve%e,rname,mess)
  call classic_iostat(seve%e,rname,ier)
  return
22 error = .true.
  write(mess,'(A,I0)') 'Read error record #',buf%rstart+buf%roff
  call classic_message(seve%e,rname,mess)
  call classic_iostat(seve%e,rname,ier)
  ! Avoid letting incorrect bytes in this buffer. Forget the record
  ! we tried to read:
  call classic_recordbuf_nullify(buf)
  return
end subroutine recordbuf_write
!
subroutine recordbuf_close(buf,error)
  use gbl_message
  use classic_interfaces, except_this=>recordbuf_close
  use classic_types
  !----------------------------------------------------------------------
  ! @ private
  !   Flush the currently opened record to the appropriate record in the
  ! output file
  !----------------------------------------------------------------------
  type(classic_recordbuf_t), intent(in)    :: buf    ! Working buffer
  logical,                   intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='CBUF'
  character(len=message_length) :: mess
  integer(kind=4) :: ier
  !
  write(buf%lun,rec=buf%rstart+buf%roff,iostat=ier) buf%data
  if (ier.eq.0) return
  !
  error = .true.
  write(mess,'(A,I0)') 'Write error record #',buf%rstart+buf%roff
  call classic_message(seve%e,rname,mess)
  call classic_iostat(seve%e,rname,ier)
end subroutine recordbuf_close
!
subroutine classic_recordbuf_nullify(buf)
  use classic_interfaces, except_this=>classic_recordbuf_nullify
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  "Nullify" the input buffer i.e. reset its record positions so that
  ! recordbuf_read/write won't assume something useful is stored in the
  ! data buffer.
  !---------------------------------------------------------------------
  type(classic_recordbuf_t), intent(inout) :: buf  !
  !
  buf%rstart = -1
  buf%wstart = -1
  buf%nrec = -1
  !
  buf%lun = -1
  buf%roff = -1
  ! buf%len: unchanged, record size is not involved here
  ! buf%data(:) = 0: no, because usually not needed and a waste of time.
                   ! Should be done explicitly by the calling routine if
                   ! needed.
  !
end subroutine classic_recordbuf_nullify
