tedd,
there are a few problems that i see with your last post. i would
implement this using a "librarian" function. this would act like a PG
lib. it also abstracts out the calls so that, if one day you want to
replace the backend, store things in, for example Tiger's SQLite DB.
Apart from that [and not counting searching, which you haven't spoken
about] this is a standard flat file arrangement.
Librarian will need to deal with the following calls:
init [zero, zero] -> [pointer, zero]
this would prepare the memory for all that follows. in your case
preparing the memory would be creating a pointer that is big enough to
cover your needs and zeroing [very important] all that data. init will
return the pointer if goes well, return zero if it is unable to create
a pointer.
[note, this means that, for example, your app can have more than one
instance of itself open...]
save [pointer, file ref] -> [pointer, file ref]
this would copy the entire pointer contents to disk, using a passed
file ref. if there is an error it returns zero in file ref. does not
call compress, that is under caller's control.
saveas [pointer, zero] -> [pointer, file ref]
this would copy the entire pointer contents to disk, asking user where
to save the file and thus creating a passed file ref. if there is an
error it returns zero in file ref. if user aborts returns zero in
pointer. does not call compress, that is under caller's control.
kill [pointer, zero] -> [zero, zero]
this frees up the pointer. returns a positive number in zero if there
was a problem. does not save. calling function is responsible for that.
read [pointer, ref] -> [pointer, pointer]
returns a zero number in ref if there is no data. uses a neg
[&FFFFFFFF] if an error, else returns a pointer to data.
write [pointer, ref, pointer] -> [pointer, zero, zero]
writes the data in its memory. returns zero if all ok. uses a neg
[&FFFFFFFF] if an error. can grow pointer if needed.
compress [pointer] -> [zero]
compresses the memory used to allow for economical memory use.
data structures.
if you allowing for, for example, 1000 entries, each entry composed of
0-20 lines, lines can be 0-32K in length. You will need an index. This
will be the first element in the memory block.
adr 0 [four bytes] : adr of adr1001
adr 1 [four bytes] : zero
...
adr 1000 [four bytes] : zero
adr 1002 [eight bytes] : address of last byte -- effective size of
pointer
adr 1010 : zero
the init function only needs create a pointer of 1011*4 bytes. There
is no point in allocating space until it is used. [in fact you will
probably need a compromise of speed and space, which means, for
example, increasing, or decreasing space in 1024 intervals.]
initially you will then fill the lines corresponding to entry 0. this
will be offsets in adr1 to adr20 in the initial block. if the line is
empty then just leave the value at zero. remember to pad your data
[from the line] with a 'C' type '00' for the EOL.
YOu cannot use the adr table to give lengths as there is no guarantee
that any line entries will occupy consecutive blocks inside the memory
set aside for the data.
Your problems will come when you read and write. when the user changes
something it will rarely take exactly the same place. then you will
need to manage the insertion of data either be growing or shrinking the
place [and updating all the adr offsets], or by zeroing this, and then
adding the data to the end.
It is here that you will be building up overhead, not at the creating
and populating the pointer, but in general manipulation of the contents
by the user.
as ever,
jonathan
--