Random access files: Avoiding 'can't extend'

bbc micro/electron/atom/risc os coding queries and routines
Post Reply
User avatar
andrew_rowland
Posts: 44
Joined: Sun Sep 13, 2009 8:06 pm
Location: Holmes Chapel, Crewe, Cheshire
Contact:

Random access files: Avoiding 'can't extend'

Post by andrew_rowland »

I know this is an old chestnut, but I wonder if my failing memory is making me miss something.
Acorn DFS and ADFS in B-Em (as a Master 128, not that it matters).
I am writing lines to a text file. !INDEX, three at a time, don't know in advance how long it will be, and have to open and close because I am swapping discs and filing systems in between each update -- and even stopping and starting in the middle of a batch.
The disc is empty to start with. I create the text file using OPENOUT, and on subsequent calls use OPENUP. I am also copying files to the same disc after each update. When !INDEX gets to 255 bytes or thereabouts I get 'can't extend' because the other files have been saved since it was created, leaving no room for it (as DFS doesn't do fragmented files).
Nothing unexpected there.
So to reserve room, I do this:
1. First create a temp file 6 sectors long, which should be plenty - Starts at sector 02)
2. Write another file to the disc, !BOOT in fact, (which gets saved just after it on the disc) - starts at sector 08
3. Delete the temporary file, leaving a hole for my text file.
4. As it's the first time, OPENOUT, write a couple lines, and close. I expect it to be created in the gap left by the temp file, but it actually starts at sector 09
5. Its EXT# is now quite small, but even if it had been located in the gap at 02, the hole would still be smaller than any other file that will be copied to it subsequently so it shouldn't get used.
6. Go into a loop where I open and update !INDEX, close it, copy a file from a different disc (which saves at 0A), swap discs and repeat.

How large a gap does it have to be before DFS allocates it to sector 02?

There is a partial workaround: If I name the temp file !INDEX and don't delete it explicitly, but open it with OPENOUT, DFS uses the right slot. The trouble with this is that I am testing for the existence of !INDEX and only OPENOUT on the first attempt, thereafter OPENingUP to append to it. I suppose the answer is a lock file, but I'd like to know if there's a better solution.

I also thought of keeping !INDEX &600 bytes long and marking the end of file with NUL, but I'd have to read the whole file on each iteration to find the marker, which again, would be better avoided.

My code:

Code: Select all

780 PROCswapdisc(FALSE ,"$",TRUE )
790 *SAVE !ZZZZ! E00 +600
800 H%=OPENOUT("!BOOT")
810 PROCwriteline(H%,"*BASIC")
820 PROCwriteline(H%,"*SHADOW")
830 PROCwriteline(H%,"CH."+CHR$ 34+"PPMenu"+CHR$ 34)
840 CLOSE #H%
850 *OPT 4,3
870 *DELETE !ZZZZ!
880 IF FNexists("!Index") ENDPROC
940 H%=OPENOUT("!Index")
950 IF H%=0 PRINTCHR$ 134"Index file could not be created!"
955 REM Write first two lines to index
960 PROCwriteline(H%,GAME$)
970 PROCwriteline(H%,VENUE$)
980 CLOSE #H%
990 ENDPROC
After this, a loop copies a file, updates the index and repeats.
User avatar
SKS1
Posts: 327
Joined: Sat Sep 19, 2020 12:04 am
Location: Highland Perthshire
Contact:

Re: Random access files: Avoiding 'can't extend'

Post by SKS1 »

Long time since I DFS'ed - but think what I'd be tempted to do here is to create !INDEX as the temp file as you are doing, but set EXT# to zero. Then your subsequent application can still OPENUP for writing, but it start appending to a zero-length file.
Miserable old curmudgeon who still likes a bit of an ARM wrestle now and then. Pi 4, 3, ARMX6, SA Risc PC, A540, A440
julie_m
Posts: 587
Joined: Wed Jul 24, 2019 9:53 pm
Location: Derby, UK
Contact:

Re: Random access files: Avoiding 'can't extend'

Post by julie_m »

Don't bother truncating your index file. Make it as big as it will ever need be to from the very outset, and use a few bytes at the beginning to specify the value of PTR# after the last record was written (Be sure to allow for this header when you insert the very first record!)
User avatar
flaxcottage
Posts: 5718
Joined: Thu Dec 13, 2012 8:46 pm
Location: Derbyshire
Contact:

Re: Random access files: Avoiding 'can't extend'

Post by flaxcottage »

From playing with OPENOUT (and *BUILD too) it seems that DFS needs to find a 16K block of storage on the disc surface even if the file to be created is very small.

On a Master use *CREATE to reserve a block of memory large enough for your file.

On a BBC micro use *SAVE similarly.

OPENOUT will overwrite the exiting file in the space reserved.

To check if the file exists make sure that the first byte in the 'empty' file is a known value, say 99. Use OPENIN and BGET# to read the first byte. If the first byte is not 99 then OPENOUT will create the file for you overwriting the existing. If the first byte is 99 then use OPENUP to append data. You will not be able to append data beyond the length of the reserved storage.

That description is not strictly random access filing. An easy way to do that is to have fixed length records then use OPENUP and PTR# to position the file write pointer and then save the data with BPUT#.

It is tricky but quite fun when it works.
- John

Check out the Educational Software Archive at www.flaxcottage.com
User avatar
sweh
Posts: 3314
Joined: Sat Mar 10, 2012 12:05 pm
Location: 07410 New Jersey
Contact:

Re: Random access files: Avoiding 'can't extend'

Post by sweh »

flaxcottage wrote: Sun Dec 31, 2023 9:03 pm From playing with OPENOUT (and *BUILD too) it seems that DFS needs to find a 16K block of storage on the disc surface even if the file to be created is very small.
This is likely implementation specific; different versions from different vendors may have different limitations.
Rgds
Stephen
User avatar
andrew_rowland
Posts: 44
Joined: Sun Sep 13, 2009 8:06 pm
Location: Holmes Chapel, Crewe, Cheshire
Contact:

Re: Random access files: Avoiding 'can't extend'

Post by andrew_rowland »

Thank you all for those suggestions. I'm sure I'll find a solution in there!
For the detail, I didn't use EXT#H%=... in case it was run on a Model B with Basic II, but there are other ways of doing that.

UPDATE
This is what I went with, and it seems to be fine, at least with DFS 2.24:

Code: Select all

780 PROCswapdisc(FALSE,"$",TRUE)
  790 flgCreate%=NOTFNexists("!Index")
  800 IFflgCreate% THEN *SAVE !Index E00 +600
  830 H%=OPENOUT("!BOOT")
  840 PROCwriteline(H%,"*BASIC")
  850 PROCwriteline(H%,"*SHADOW")
  860 PROCwriteline(H%,"CH."+CHR$34+"PPMenu"+CHR$34)
  870 CLOSE#H%
  900 *OPT 4,3
  910 IFNOTFNexists("PPMenu") PROCcopymenu
  920 IFNOT flgCreate% ENDPROC
  930 *SAVE !Index E00 +0
  990 H%=OPENUP("!Index")
 1000 IFH%=0 PRINTCHR$134"Index file could not be created!"
 1010 PROCwriteline(H%,GAME$)
 1020 PROCwriteline(H%,VENUE$)
 1030 CLOSE#H%
 1060 ENDPROC
So to recap, without using anything specific to the Master or later versions of BASIC, the trick seems to be:
  1. *SAVE a dummy file large enough to reserve space
  2. Save other file(s) to land after that space
  3. *SAVE a zero-length file with the same name
  4. OPENUP that file, which acts just like OPENOUT but stops DFS looking for another area of the disc to save it.
Thanks for all your help, guys. You got me unstuck!
Post Reply

Return to “programming”