subroutine cubeio_max_chan_block(cubset,cub,blocksize,blockname,nchan,error)
  use cubeio_messaging
  use gkernel_interfaces
  use cubetools_format
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_max_chan_block
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ public
  ! Return the maximum number of channels which can be used in a
  ! cube_block_t for cube given as argument
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cubeio_cube_t),  intent(in)    :: cub
  real(kind=4),         intent(in)    :: blocksize  ! [Bytes]
  character(len=*),     intent(in)    :: blockname
  integer(kind=chan_k), intent(out)   :: nchan
  logical,              intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='MAX>CHAN>BLOCK'
  real(kind=4) :: onechan
  integer(kind=chan_k) :: ndiv
  character(len=message_length) :: mess
  !
  ! The size of the block should fit in the following constraint:
  !  - must fit at least 1 entry (otherwise we can't do anything)
  !  - if memory is below the dedicated maximum size, increase the buffer
  !    to the maximum possible
  onechan = cub%chansize() ! [Bytes] Weight of one channel
  nchan = min(floor(blocksize/onechan),cub%desc%nc)
  if (nchan.le.0) then
    nchan = 1
    write(mess,'(5A)')  'Buffer (',cubetools_format_memsize(blocksize),  &
      ') is not large enough to store one channel (',  &
      cubetools_format_memsize(onechan),')'
    call cubeio_message(seve%w,rname,mess)
    call cubeio_message(seve%w,rname,blockname//' should be increased for better efficiency')
  endif
  !
  ! Try to avoid very odd split, e.g. dividing 5=4+1 or 9=4+4+1. We prefer
  ! 5=3+2 or 9=3+3+3, i.e. same number of divisions but evenly distributed.
  ndiv = (cub%desc%nc-1)/nchan+1
  nchan = (cub%desc%nc-1)/ndiv+1
  !
  write(mess,'(A,I0,A)')  'Buffer will store up to ',nchan,' channels'
  call cubeio_message(ioseve%others,rname,mess)
  !
end subroutine cubeio_max_chan_block
!
subroutine cubeio_max_y_block(cubset,cub,blocksize,blockname,ny,error)
  use cubeio_messaging
  use gkernel_interfaces
  use cubetools_format
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_max_y_block
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ public
  ! Return the maximum number of Y rows which can be used in a
  ! cube_block_t for cube given as argument
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cubeio_cube_t),  intent(in)    :: cub
  real(kind=4),         intent(in)    :: blocksize  ! [Bytes]
  character(len=*),     intent(in)    :: blockname
  integer(kind=pixe_k), intent(out)   :: ny
  logical,              intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='MAX>Y>BLOCK'
  real(kind=4) :: oney
  integer(kind=pixe_k) :: ndiv
  character(len=message_length) :: mess
  !
  ! The size of the block should fit in the following constraint:
  !  - must fit at least 1 entry (otherwise we can't do anything)
  !  - if memory is below the dedicated maximum size, increase the buffer
  !    to the maximum possible
  oney = cub%ysize()  ! [Bytes] Weight of one Y row
  ny = min(floor(blocksize/oney),cub%desc%ny)
  if (ny.le.0) then
    ny = 1
    write(mess,'(5A)')  'Buffer (',cubetools_format_memsize(blocksize),  &
      ') is not large enough to store one Y row (',  &
      cubetools_format_memsize(oney),')'
    call cubeio_message(seve%w,rname,mess)
    call cubeio_message(seve%w,rname,blockname//' should be increased for better efficiency')
  endif
  !
  ! Try to avoid very odd split, e.g. dividing 5=4+1 or 9=4+4+1. We prefer
  ! 5=3+2 or 9=3+3+3, i.e. same number of divisions but evenly distributed.
  ndiv = (cub%desc%ny-1)/ny+1
  ny = (cub%desc%ny-1)/ndiv+1
  !
  write(mess,'(A,I0,A)')  'Buffer will store up to ',ny,' Y rows'
  call cubeio_message(ioseve%others,rname,mess)
  !
end subroutine cubeio_max_y_block
!
subroutine cubeio_max_any_block(cubset,cub,blocksize,blockname,nplane,error)
  use cubeio_messaging
  use gkernel_interfaces
  use cubetools_format
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_max_any_block
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ public
  ! Return the maximum number of planes which can be used in a
  ! cube_block_t for cube given as argument
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cubeio_cube_t),  intent(in)    :: cub
  real(kind=4),         intent(in)    :: blocksize  ! [Bytes]
  character(len=*),     intent(in)    :: blockname
  integer(kind=data_k), intent(out)   :: nplane
  logical,              intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='MAX>ANY>BLOCK'
  real(kind=4) :: oneplane
  integer(kind=data_k) :: ndiv
  character(len=message_length) :: mess
  !
  ! The size of the block should fit in the following constraint:
  !  - must fit at least 1 entry (otherwise we can't do anything)
  !  - if memory is below the dedicated maximum size, increase the buffer
  !    to the maximum possible
  oneplane = cub%planesize() ! [Bytes] Weight of one plane
  nplane = min(floor(blocksize/oneplane),cub%desc%n3)
  if (nplane.le.0) then
    nplane = 1
    write(mess,'(5A)')  'Buffer (',cubetools_format_memsize(blocksize),  &
      ') is not large enough to store one plane (',  &
      cubetools_format_memsize(oneplane),')'
    call cubeio_message(seve%w,rname,mess)
    call cubeio_message(seve%w,rname,blockname//' should be increased for better efficiency')
  endif
  !
  ! Try to avoid very odd split, e.g. dividing 5=4+1 or 9=4+4+1. We prefer
  ! 5=3+2 or 9=3+3+3, i.e. same number of divisions but evenly distributed.
  ndiv = (cub%desc%n3-1)/nplane+1
  nplane = (cub%desc%n3-1)/ndiv+1
  !
  write(mess,'(A,I0,A)')  'Buffer will store up to ',nplane,' planes'
  call cubeio_message(ioseve%others,rname,mess)
  !
end subroutine cubeio_max_any_block
!
subroutine cubeio_cube_finish(cubset,head,cub,error)
  use gkernel_interfaces
  use cubetools_dataformat
  use cubetools_header_types
  use cubetools_setup_types
  use cubefitsio_header_read
  use cubeio_interfaces, except_this=>cubeio_cube_finish
  use cubeio_block
  use cubeio_cube
  use cubeio_timing
  !---------------------------------------------------------------------
  ! @ public
  ! This subroutine is to be invoked once the cube is fully read or
  ! created or modified, to leave it in a 'better' state.
  !---------------------------------------------------------------------
  type(cube_setup_t),  intent(in)    :: cubset
  type(cube_header_t), intent(in)    :: head
  type(cubeio_cube_t), intent(inout) :: cub
  logical,             intent(inout) :: error
  !
  ! Might need flushing of last block in case of disk mode
  call cubeio_flush_block(cubset,head,cub,cub%file%block,error)
  if (error)  continue
  !
  ! Free temporary buffers as they are not needed anymore
  call cub%file%block%free(error)
  if (error)  continue
  !
  if (cub%desc%action.eq.code_write .or.  &
      cub%desc%action.eq.code_update) then
    if (cub%file%kind.eq.code_dataformat_fits) then
      ! We have written a FITS file. Reload its header in our
      ! dictionary. This is the best way to retrieve what is actually
      ! on disk, as CFITSIO adds its owns comments and keywords beyond
      ! our choice.
      if (cub%file%hfits%unit.gt.0) then  ! Can be zero if the file creation failed.
                                     ! ZZZ we should have a better mean to
                                     ! support this case.
        call cubefitsio_header_fill(cub%file%hfits,error)
        if (error)  continue
      endif
    endif
  endif
  !
  ! Timing feedback?
  call gag_cputime_get(cub%time%total)  ! Get total time on this cube
  call cub%feedback(cubset)
  !
end subroutine cubeio_cube_finish
