?? NEWTITLE := 'NOS/VE :  BASIC ACCESS METHOD' ??
MODULE bam$byte_move;
?? RIGHT := 110 ??

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc ame$access_validation_errors
*copyc ame$improper_file_id
*copyc ame$ring_validation_errors
*copyc bat$task_file_table
*copyc ost$caller_identifier
?? POP ??
*copyc baf$task_file_entry_p
*copyc amp$set_file_instance_abnormal
*copyc amp$set_local_name_abnormal
*copyc mmp$advise_out
*copyc mmp$preset_page_streaming
*copyc osp$set_status_abnormal
*copyc osp$verify_system_privilege
*copyc syp$advised_move_bytes
*copyc bav$task_file_table
*copyc i#move
?? OLDTITLE ??
?? NEWTITLE := '[xdcl, #gate] BAP$BYTE_MOVE', EJECT ??

  PROCEDURE [XDCL, #GATE] bap$byte_move
    (    from_fid: amt$file_identifier;
         to_fid: amt$file_identifier;
         move: amt$file_byte_address;
         last_move: boolean;
     VAR byte_offset: amt$file_byte_address;
     VAR status: ost$status);


    CONST
      minimum_preset = 4096,
      move_transfer_size = 10000(16); { 65536 bytes

    VAR
      advise_size: integer,
      advise_pointer: ^cell,
      from_pointer: ^cell,
      to_pointer: ^cell,
      file_instance_from: ^bat$task_file_entry,
      file_instance_to: ^bat$task_file_entry,
      ignore_transfer_size: 0 .. 15,
      ignore_free_behind: boolean,
      caller_id: ost$caller_identifier;

?? NEWTITLE := 'BYTE_MOVE_ROLLBACK_PROC', EJECT ??

    PROCEDURE byte_move_rollback_proc
      (    condition_status: ost$status);

      status := condition_status;
      file_instance_from^.rollback_procedure := NIL;
      file_instance_to^.rollback_procedure := NIL;
      EXIT bap$byte_move; {----->

    PROCEND byte_move_rollback_proc;
?? OLDTITLE ??
?? EJECT ??
    osp$verify_system_privilege;
    #CALLER_ID (caller_id);

    status.normal := TRUE;
    file_instance_from := baf$task_file_entry_p (from_fid);
    file_instance_to := baf$task_file_entry_p (to_fid);
    IF (file_instance_from = NIL) OR (file_instance_to = NIL) THEN
      osp$set_status_abnormal (amc$access_method_id, ame$improper_file_id, 'bap$byte_move', status);
      RETURN; {----->
    IFEND;

    IF caller_id.ring > file_instance_from^.instance_attributes.static_label.ring_attributes.r2 THEN
      amp$set_file_instance_abnormal (from_fid, ame$ring_validation_error, fsc$copy_file_req, ' ', status);
      RETURN; {----->
    IFEND;

    IF file_instance_from^.access_level <> amc$record THEN
      amp$set_file_instance_abnormal (from_fid, ame$improper_access_attempt, fsc$copy_file_req, ' RECORD ',
            status);
      RETURN; {----->
    IFEND;

    IF NOT (pfc$read IN file_instance_from^.instance_attributes.dynamic_label.access_mode) THEN
      amp$set_file_instance_abnormal (from_fid, ame$improper_access_attempt, fsc$copy_file_req, ' READ ',
            status);
      RETURN; {----->
    IFEND;

    IF caller_id.ring > file_instance_to^.instance_attributes.static_label.ring_attributes.r1 THEN
      amp$set_file_instance_abnormal (to_fid, ame$ring_validation_error, fsc$copy_file_req, ' ', status);
      RETURN; {----->
    IFEND;

    IF file_instance_to^.access_level <> amc$record THEN
      amp$set_file_instance_abnormal (to_fid, ame$improper_access_attempt, fsc$copy_file_req, ' RECORD ',
            status);
      RETURN; {----->
    IFEND;

    IF NOT (file_instance_to^.instance_attributes.dynamic_label.access_mode >=
          $pft$usage_selections [pfc$shorten, pfc$append]) THEN
      amp$set_file_instance_abnormal (to_fid, ame$improper_access_attempt, fsc$copy_file_req,
            ' SHORTEN, APPEND ', status);
      RETURN; {----->
    IFEND;

  /main_program/
    BEGIN
      file_instance_from^.rollback_procedure := ^byte_move_rollback_proc;
      file_instance_to^.rollback_procedure := ^byte_move_rollback_proc;

      from_pointer := #ADDRESS (#RING (file_instance_from^.file_pva), #SEGMENT (file_instance_from^.file_pva),
            byte_offset);
      to_pointer := #ADDRESS (#RING (file_instance_to^.file_pva), #SEGMENT (file_instance_to^.file_pva),
            byte_offset);

      IF byte_offset = 0 THEN {this is the first call, check the length

{ BAM$BYTE_MOVE will be called repeatedly in blocks of 512K if the move is a large move. For the first
{ call determine the appropriate action:
{   a) If the number of bytes is <=  minimum_preset (4096), just use I#MOVE without presetting stream mode
{   b) Otherwise, call mmp$preset_page_streaming for both the source and the destination files and then
{      use I#MOVE for the move.  Note that the reason for setting streaming mode on the destination file
{      is to ensure that free_behind is true.
{
        IF move > minimum_preset THEN
          mmp$preset_page_streaming (TRUE, from_pointer, move_transfer_size, ignore_transfer_size,
                ignore_free_behind, status);
          IF NOT status.normal THEN
            EXIT /main_program/; {----->
          IFEND;

{ Use 16K transfer size on the destination file so that free behind will release pages faster than it
{ would at a 64K transfer size.  Note that if the disk allocation unit > 16K, the free behind
{ process within the page fault processor will actually use AU size as the free behind size.

          mmp$preset_page_streaming (TRUE, to_pointer, 16384, ignore_transfer_size, ignore_free_behind,
                status);
          IF NOT status.normal THEN
            EXIT /main_program/; {----->
          IFEND;
        IFEND;
      IFEND; {byte_offset=0

      i#move (from_pointer, to_pointer, move);
      byte_offset := byte_offset + move;

      IF file_instance_from^.private_read_information <> NIL THEN
        file_instance_from^.private_read_information^.positioning_info.record_info.current_byte_address :=
              byte_offset;
      ELSE
        file_instance_from^.global_file_information^.positioning_info.record_info.current_byte_address :=
              byte_offset;
      IFEND;

      file_instance_to^.instance_of_open_modified := TRUE;

      file_instance_to^.global_file_information^.positioning_info.record_info.current_byte_address :=
            byte_offset;
      file_instance_to^.global_file_information^.eoi_byte_address := byte_offset;
      file_instance_to^.global_file_information^.positioning_info.record_info.file_position := amc$eoi;

      IF last_move THEN
        IF file_instance_from^.private_read_information <> NIL THEN
          file_instance_from^.private_read_information^.positioning_info.record_info.file_position := amc$eoi;
        ELSE
          file_instance_from^.global_file_information^.positioning_info.record_info.file_position := amc$eoi;
        IFEND;


        { Even though free_behind was set in the source file, the last 2 or 3 transfer units + a partial TU
        { are still in the working set.  In the destination file, the last transfer unit + a partial TU
        { are still in the working set.  Advise these pages out.  However, instead of calculating which pages
        { to advise_out, just advise out the entire file.  The advise code will call remove pages which
        { will go through the chain of all pages of the segment in memory... this is as efficient as we
        { can get and it will thus get all pages out of memory.  (unless the pages are in the shared queue).

        advise_size := #OFFSET (from_pointer) + move;
        advise_pointer := #ADDRESS (#RING (from_pointer), #SEGMENT (from_pointer), 0);
        mmp$advise_out (advise_pointer, advise_size, status);
        IF NOT status.normal THEN
          EXIT /main_program/; {----->
        IFEND;

        advise_size := #OFFSET (to_pointer) + move;
        advise_pointer := #ADDRESS (#RING (to_pointer), #SEGMENT (to_pointer), 0);
        mmp$advise_out (advise_pointer, advise_size, status);
        IF NOT status.normal THEN
          EXIT /main_program/; {----->
        IFEND;
      IFEND; {last_move
    END /main_program/;

    file_instance_from^.rollback_procedure := NIL;
    file_instance_to^.rollback_procedure := NIL;

  PROCEND bap$byte_move;
?? OLDTITLE ??
MODEND bam$byte_move;
