! B-tree test program

%option "-nonstandard-low"

%systemroutinespec phex2(%integer i)

%externalroutinespec fsys initialise
%externalroutinespec create partition table(%integername p size)
%externalroutinespec initialise partition(%integer partition, size,
                                                   index, index size)

! The following two must be the same as in TREE.CORE
%constinteger free size = 256 - 8
%recordformat header fm((%short checksum, 
                         %short root, used, flags, free count,
                         %shortarray free(1 : free size),
                         %short should have, seed) %c
                        %or %bytearray b(0 : 511))

%constinteger block slots = 253
%recordformat block fm(%short checksum,
                       %short flags,
                       %byte pointer limit, text limit,
                       %shortarray x(1 : block slots))
%constinteger leaf = 1;  ! Hence no child-pointers

%ownrecord(header fm)%name header

%externalintegerfnspec open tree by ID(%record(*)%name access,
                                       %integer ID, %integername access)
%externalintegerfnspec close tree(%integer access)
%externalintegerfnspec read block(%integer access token, block,
                                  %record(*)%name buffer)
%externalintegerfnspec write block(%integer access token, block,
                                   %record(*)%name buffer)

%externalintegerfnspec B tree add entry(%integer access token,
                                        %string(255) key, %integer data)
%externalintegerfnspec B tree find entry(%integer access token,
                                         %string(255) key, %integername data)
%externalintegerfnspec B tree delete entry(%integer access token,
                                           %string(255) key)

%externalintegerfnspec compare(%string(*)%name a, b, %integer case)

%include "MOUSE:MOUSE.INC"

%owninteger access token = 0

! Diagnostic stuff

%routine dump(%integer bytes, %bytename buffer)
   %integer i, j, k
      newline
      i = 0
      %while i < bytes %cycle
         %for j = 0, 1, 15 %cycle
            %if i + j >= bytes %then space %and space %c
                               %else phex2(byteinteger(addr(buffer) + j))
            space %if j & 3 = 3
         %repeat
         printstring(" [")
         write(i, -3)
         printstring("]  |")
         %for j = 0, 1, 15 %cycle
            %if j + i < bytes %start
               k = byteinteger(addr(buffer) + j) & 127
               %if k = 0 %start
                  k = '.'
               %else %unless ' ' <= k <= '~'
                  k = '_'
               %finish
               printsymbol(k)
            %else
               space
            %finish
         %repeat
         printsymbol('|')
         newline
         i = i + 16;  buffer == buffer [16]
      %repeat
      newline
%end

%routine put filtered(%string(255) s)
   %integer i, ch
      %return %if s = ""
      %for i = 1, 1, length(s) %cycle
         ch = charno(s, i)
         %if ' ' <= ch <= '~' %start
            print symbol(ch)
         %else
            print symbol('<')
            write(ch, 0)
            print symbol('>')
         %finish
      %repeat
%end

%owninteger have
%ownstring(255) previous key

%routine print tree(%integer root, indent)
   %record(block fm) b
   %shortname p
   %integer i, status
   %string(*)%name k
      %unless 0 < root <= header_used %start
         printstring("Invalid block address ")
         write(root, 0);  newline
         %signal 13, root
      %finish
      status = read block(access token, root, b)
      %if status < 0 %start
         printstring("Read failed ");  write(status, 0)
         newline
         %return
      %finish
      !! spaces(indent)
      !! printstring("Pointer limit = ");  write(b_pointer limit, 0)
      !! printstring(", text limit = ");  write(b_text limit, 0)
      !! newline
      %signal 13, b_pointer limit, b_text limit %c
          %unless 0 < b_pointer limit < b_text limit
      indent = indent + 3
      %if b_flags & leaf = 0 %start
         print tree(b_x(1), indent)
         i = b_pointer limit - 1
         p == b_x(2)
      %else
         i = b_pointer limit
         p == b_x(1)
      %finish
      %while i > 0 %cycle
         write(root, indent);  printstring(": ")
         write(integer(addr(p) + 2), 5);  spaces(2)
         k == string(addr(b_x(p)));  put filtered(k);  newline
         p == p [3]
         %unless compare(previous key, k, 0) < 0 %start
            printstring("*** Sort order error ***");  newline
         %finish
         previous key = k
         i = i - 3
         have = have + 1
         %if b_flags & leaf = 0 %start
            print tree(p, indent)
            p == p [1];  i = i - 1
         %finish
      %repeat
%end

%routine analyse one(%integer n, %record(block fm)%name b)
   %shortname p
   %integer i
      printstring("Block ");  write(n, 0);  newline
      printstring("   Pointer limit = ");  write(b_pointer limit, 0)
      printstring(", text limit = ");  write(b_text limit, 0)
      newline
      %return %unless 0 < b_pointer limit < b_text limit
      %if b_flags & leaf = 0 %start
         printstring("   -> ");  write(b_x(1), 0)
         newline
         i = b_pointer limit - 1
         p == b_x(2)
      %else
         i = b_pointer limit
         p == b_x(1)
      %finish
      %while i > 0 %cycle
         write(integer(addr(p) + 2), 5);  spaces(3)
         %unless b_text limit <= p <= block slots %start
            printstring("Dud text pointer ");  write(p, 0)
            p == p [3]
         %else
            put filtered(string(addr(b_x(p))));  p == p [3]
         %finish
         newline
         i = i - 3
         %if b_flags & leaf = 0 %start
            printstring("   -> ");  write(p, 0)
            newline
            p == p [1];  i = i - 1
         %finish
      %repeat
%end


! Test program

%string(127)%fn k(%integer x)
   %integer i, q, r, n
   %string(127) s = ""
      n = x
      %for i = 1, 1, 8 %cycle
         q = x // 10
         r = x - 10 * q
         s = to string(r + '0') . s
         x = q
      %repeat
      s = s . "." %for i = 0, 1, n & 31
      %result = s
%end

%begin
   %record(block fm) b
   %string(255) key
   %integer command, data, i, n, c, status, i size, p size
      %on 4, 9 %start
         %if event_event = 4 %start
            skip symbol %while next symbol # NL
            skip symbol
            -> top
         %finish
         -> done
      %finish
      open input(0, ":T");  select input(0)
      open output(0, ":T");  select output(0)
      prompt("Do you want to initialise the disc? ")
      read symbol(command) %until command > ' '
      %if command = 'Y' %or command = 'y' %start
         create partition table(p size)
         printstring("Partition size is ")
         write(p size, 0);  newline
         prompt("Index file size: ")
         read(i size) %until 0 < i size <= p size // 2
         initialise partition(1, p size, (p size - i size) // 2, i size)
         initialise partition(2, p size, (p size - i size) // 2, i size)
         initialise partition(3, p size, (p size - i size) // 2, i size)
      %finish
      skip symbol %while next symbol # NL;  skip symbol
      fsys initialise
      status = open tree by ID(nil, 16_01010002, access token)
      %if status < 0 %start
         printstring("Open failed ");  write(status, 0)
         newline
         %stop
      %finish
      header == record(integer(access token))
      printstring("Got header, root at ");  write(header_root, 0)
      printstring(", should have ");  write(header_should have, 0)
      newline
      %if header_seed = 0 %start
         header_seed = CPU time & 32767
         printstring("Seed set to ");  write(header_seed, 0)
         newline
      %else
         printstring("Seed is ");  write(header_seed, 0)
         newline
      %finish
top:  %cycle
         prompt("Command: ")
         read symbol(command) %until command > ' '
         %if command = 'p' %start
            have = 0;  previous key = ""
            print tree(header_root, 0) %if header_root # 0
            printstring("Should have ");  write(header_should have, 0)
            printstring(", actually have ");  write(have, 0)
            newline
         %else %if command = 'a'
            prompt("Key:  ");  read(key)
            prompt("Data: ");  read(data)
            status = B tree add entry(access token, key, data)
            %if status < 0 %start
               printstring("Add failed ");  write(status, 0)
               newline
            %else
               header_should have = header_should have + 1
               status = write block(access token, 0, header)
               %if status < 0 %start
                  printstring("Write header failed ");  write(status, 0)
                  newline
               %finish
            %finish
         %else %if command = 'r'
            prompt("N ");  read(n)
            %for i = 1, 1, n %cycle
               write(i, 4);  print symbol(13)
               header_seed = (321 * header_seed + 246247) & 32767
               status = B tree add entry(access token, k(header_seed),
                                         header_seed)
               %if status < 0 %start
                  printstring("Add failed ");  write(status, 0)
                  newline
               %finish
            %repeat
            header_should have = header_should have + n
            status = write block(access token, 0, header)
            %if status < 0 %start
               printstring("Write header failed ");  write(status, 0)
               newline
            %finish
            printstring("Last key inserted was ")
            printstring(k(header_seed))
            newline
         %else %if command = 'i'
            prompt("N ");  read(n)
            prompt("From ");  read(c)
            %for i = 1, 1, n %cycle
               write(i, 4);  print symbol(13)
               status = B tree add entry(access token, k(i + c - 1), i + c - 1)
               %if status < 0 %start
                  printstring("Add failed ");  write(status, 0)
                  newline
               %finish
            %repeat
            header_should have = header_should have + n
            status = write block(access token, 0, header)
            %if status < 0 %start
               printstring("Write header failed ");  write(status, 0)
               newline
            %finish
            printstring("Last key inserted was ")
            printstring(k(n + c - 1))
            newline
         %else %if command = 'b'
            prompt("N ");  read(n)
            prompt("From ");  read(c) %until c - n > 0
            %for i = 1, 1, n %cycle
               write(i, 4);  print symbol(13)
               status = B tree add entry(access token, k(c - i + 1), c - i + 1)
               %if status < 0 %start
                  printstring("Add failed ");  write(status, 0)
                  newline
               %finish
            %repeat
            header_should have = header_should have + n
            status = write block(access token, 0, header)
            %if status < 0 %start
               printstring("Write header failed ");  write(status, 0)
               newline
            %finish
            printstring("Last key inserted was ")
            printstring(k(c - n + 1))
            newline
         %else %if command = 'f'
            prompt("Key:  ");  read(key)
            printstring(key);  printstring(": ")
            status = B tree find entry(access token, key, data)
            %if status = 0 %start
               write(data, 0)
            %else
               printstring("not found")
            %finish
            newline
         %else %if command = 'd'
            prompt("Key: ");  read(key)
            status = B tree delete entry(access token, key)
            %if status = 0 %start
               header_should have = header_should have - 1
               status = write block(access token, 0, header)
               %if status < 0 %start
                  printstring("Write header failed ");  write(status, 0)
                  newline
               %finish
            %else
               printstring("Delete failed");  newline
            %finish
         %else %if command = 'z'
            prompt("Key (numeric): ");  read(n)
            status = B tree delete entry(access token, k(n))
               %if status = 0 %start
               header_should have = header_should have - 1
               status = write block(access token, 0, header)
               %if status < 0 %start
                  printstring("Write header failed ");  write(status, 0)
                  newline
               %finish
            %else
               printstring("Delete failed");  newline
            %finish
         %else %if command = 's'
            prompt("N ");  read(n)
            status = read block(access token, n, b)
            %if status < 0 %start
               printstring("Read block failed ");  write(status, 0)
               newline
            %finish
            dump(512, byteinteger(addr(b)))
         %else %if command = 'x'
            prompt("N ");  read(n)
            status = read block(access token, n, b)
            %if status < 0 %start
               printstring("Read block failed ");  write(status, 0)
               newline
            %finish
            analyse one(n, b)
         %else %if command = 'e'
            %exit
         %finish
      %repeat
done: status = close tree(access token)
      %if status < 0 %start
         printstring("Close failed");  newline
      %finish
%end %of %program
