DB_records.stl
"Fourth disk record variant, allowing cumulants to be kept in parents."
-- file disk_records_refct.stl -- *********************************************************************************************** -- ***************** fourth disk record variant, allowing cumulants to be kept in parents ********* -- *********************************************************************************************** -- This variant of the disk record packages provides the collection of differently structured, -- automatically paging disk record objects needed for the database application. Six different -- variants are provided, distinguished by the value of their common rectype byte, which defines -- the record type and determines whether the record is 'compound' or 'non-compound'. The 'objects' -- of the preceding version of this package are systematically replaced by 4-byte record identifiers -- exactly corresponding to 32-bit record numbers. package disk_records_pak; -- common package for various kinds of disk records const rec_size := 128; -- byte size of records; initally set small to test swapping -- NOTE: the first record is reserved for master info about the file var code_pts; -- code points to be traversed ******* TESTING ONLY ******* procedure clear_memory(); -- reset the recno_generator, thus clearing almost all records procedure check_memory(); -- read the amount of memory currently in use -- NOTE: ******* TESTING ONLY ******* procedure set_is_compound(rec,isc); -- set the is_compound flag for rec procedure set_type(rec,typ); -- set the type of a record procedure report_points_passed(); -- report on code points not traversed ******* TESTING ONLY ******* procedure pass(pt); -- traverse a code point******* TESTING ONLY ******* end disk_records_pak; package db_records; -- this package provides basic operations for the six classes of record 'objects' -- used in the database application. These are: -- (0) string pieces for big strings -- (1) string pieces for record occurence strings -- (2) bigstring nodes (simple and compound) -- (3) record_occurences bigstring nodes (simple and compound) -- (4) word index B_tree nodes (simple and compound) -- (5) database cumulative record-start index B_tree nodes (simple and compound) const string_record := "\x00"; -- string record record type -- the layout of this record type is: 2 lead byte number of chars; remaining bytes are chars -- (125 of these) total 128 bytes const sr_ncr_1 := 2,sr_ncr_2 := 3,sr_char_start := 4; -- start and end of number of characters field const sr_low_lim := 63, sr_hi_lim := 2 * sr_low_lim - 1; -- min and max number of allowed characters const wdoccs_string_record := "\x01"; -- string pieces for record occurence strings -- the layout of this record type is: 1 lead byte number of occs; remaining bytes 4-byte record identifiers -- (30 of these) total 122 bytes const wo_nr_1 := 3,wo_nr_2 := 3,wo_occs_start := 4; -- start and end of number of occurences field const wos_low_lim := 4, wos_hi_lim := 2 * wos_low_lim; -- min and max number of allowed occurences const bigstr_node_record := "\x82"; -- bigstring nodes. These have character count as their sole cumulator const bigstr_node_ncr := "\x02"; -- non-compound bigstring nodes. const bnr_low_lim := 7,bnr_hi_lim := 2 * bnr_low_lim - 1; -- min and max number of allowed children const bnr_ch_start := 3,bnr_cum_start := 59,bnr_cum_end := 128; -- layout is: 14 children (4 bytes each);14 cumulants children (5 bytes each); total 128 bytes. const wdoccs_str_node_record := "\x83"; -- record_occurences bigstring nodes. -- These have item count and rightmost record key as cumulators const wdoccs_str_node_ncr := "\x03"; -- non-compound record_occurences bigstring nodes. const wo_low_lim := 4,wo_hi_lim := 2 * wo_low_lim; -- min and max number of allowed children const wo_ch_start := 3,wo_cum_start := 39,wo_cum2_start := 84,wo_cum2_end := 119; -- layout is: 9 children (4 bytes each), 9 cumulated number of occurences (5 bytes each), -- 9 rightmost occuring id in child (4 bytes each); total 119 bytes. const wd_index_node_record := "\x84"; -- word index B_tree nodes. -- These have item count and rightmost word as cumulators const wd_index_node_ncr := "\x04"; -- non-compound word index B_tree nodes. const wix_low_lim := 3,wix_hi_lim := 2 * wix_low_lim - 1; -- min and max number of allowed children -- layout is: cum_occs (5 bytes each, 6 of these, 30 bytes), word_length and start word_start (12 bytes each, -- 6 of these, 72 bytes); children (4 bytes each, 6 of these, 24 bytes); total 128 bytes. const wix_ch_start := 3,wix_cum_start := 27,wix_cum2_start := 57,wix_cum2_end := 128; const wixnc_low_lim := 3,wixnc_hi_lim := 2 * wixnc_low_lim; -- min and max number of allowed children -- layout is: cum_occs (5 bytes each, 7 of these, 35 bytes), word_length and start word_start (12 bytes each, -- 7 of these, 84 bytes) total 121 bytes. const wixnc_ch_start := 3,wixnc_cum_start := 3,wixnc_cum2_start := 38,wixnc_cum2_end := 121; const db_index_node_record := "\x85"; -- database cumulative record-start index B_tree nodes. -- These have item count and rightmost word as cumulators const db_index_node_ncr := "\x05"; -- non-compound database record-start index B_tree nodes. const dbix_low_lim := 4,dbix_hi_lim := 2 * dbix_low_lim; -- min and max number of allowed children -- layout is: cum_length (5 bytes each, 9 of these, 45 bytes), last key (4 bytes each, 9 of these, 36 bytes) -- children (4 bytes each, 9 of these, 36 bytes); total 119 bytes. const dbix_ch_start := 3,dbix_cum_start := 39,dbix_cum2_start := 84,dbix_cum2_end := 119; const dbnc_low_lim := 7,dbnc_hi_lim := 2 * dbnc_low_lim - 1; -- min and max number of allowed children -- layout is: cum_length (5 bytes each, 14 of these, 70 bytes), last key (4 bytes each, 14 of these, 56 bytes) -- total 128 bytes. const dbnc_ch_start := 3,dbnc_cum_start := 3,dbnc_cum2_start := 73,dbnc_cum2_end := 128; const type_byte := 1; -- record byte containing its type const nch_byte := 2; -- record byte containing its number of children -- ******** routines applicable to all nodes ******** procedure num_childr(rec); -- number of children of node procedure set_num_childr(rec,n); -- set number of children of node -- ******** special routines for string_record nodes ******** procedure sr_length(rec); -- length of active part of a record procedure sr_slice(rec,i,j); -- get slice of a record procedure sr_set_slice(rw rec,i,j,stg); -- set slice of record -- ******** special routines for wdoccs_string_record nodes ******** procedure wo_length(rec); -- length of active part of a record procedure wo_slice(rec,i,j); -- get slice of a wd occurence record procedure wo_set_slice(rw rec,i,j,stg); -- set slice of wd occurence record -- ******** special routines for bigstr_node_record nodes ******** -- none, but see 'increfs' -- ******** special routines for wdoccs_str_node_record nodes ******** -- none, but see 'increfs' -- ******** special routines for wd_index_node_record nodes ******** -- none, but see 'increfs' -- ******** special routines for db_index_node_record nodes ******** -- none, but see 'increfs' end db_records; package body disk_records_pak; -- common package for various kinds of disk records use byteutil,string_utility_pak,db_records,setldb; -- common package for reference count manipulation var code_pts_passed := {}; procedure pass(pt); code_pts_passed with:= pt; end pass; procedure report_points_passed(); -- report on code points not traversed if nn := #(not_passed := code_pts - code_pts_passed) = 0 then print("all ",#code_pts," points passed"); else print(nn," points not passed: ",not_passed); end if; end report_points_passed; procedure clear_memory(); -- reset the recno_generator, thus clearing almost all records return 0; -- NOTE: ******* TESTING ONLY ******* old_recno_generator := recno_generator; recno_generator := 10; -- keep the first few records, which are special free_list := []; -- clear the free list in_use := {rn: rn in in_use | (rn(1..3) = "\x00\x00\x00" and abs(rn(4)) <= 10)}; return old_recno_generator; end clear_memory; procedure check_memory(); -- read the amount of memory currently in use return dr_check_memory(); if (used := recno_generator - #free_list) < 50 then return [used,{hexify(x): x in in_use},[hexify(x): x in free_list]]; end if; return used; -- the records allocated, less those freed end check_memory; procedure set_type(rec,typ); -- set the type of a record stg := dr_load(rec); -- force load; then set the type byte stg(type_byte) := typ; dr_setrecbuf(rec,stg); dr_dirtify(rec); return rec; -- return the record end set_type; procedure set_is_compound(rec,isc); -- set the is_compound flag for this node tb := (stg := dr_load(rec))(type_byte); -- force load; then set the first character of the record ic := abs(tb) >= 128; if isc = ic then return; end if; stg(type_byte) := if isc then char(abs(tb) + 128) else char(abs(tb) - 128) end if; dr_setrecbuf(rec,stg); dr_dirtify(rec); end set_is_compound; end disk_records_pak; package body db_records; -- this package provides basic operations for the six classes of record 'objects' -- used in the database application. use byteutil,disk_records_pak,string_utility_pak,setldb; -- the layout of this record type is: 2 lead bytes string length; remaining bytes are characters -- total 64 bytes procedure num_childr(rec); -- number of children of node stg := dr_load(rec); -- ensure that the record is loaded --print(" #REC ",#rec); --print(" REC ",int_of_4(rec)); --db_debug(stg); return abs(stg(nch_byte)); end num_childr; procedure set_num_childr(rec,n); -- set number of children of node stg := dr_load(rec); -- ensure that the record is loaded stg(nch_byte) := char(n); -- set the number of children dr_setrecbuf(rec,stg); dr_dirtify(rec); end set_num_childr; -- ******** special routines for string_record nodes ******** procedure sr_length(rec); -- length of active part of a record stg := dr_load(rec); -- load this recno into RAM return int_from_bytes(stg(sr_ncr_1..sr_ncr_2)); -- convert and return the length field end; procedure sr_slice(rec,i,j); -- get slice of a record stg := dr_load(rec); -- load this recno into RAM rec_len := int_from_bytes(stg(sr_ncr_1..sr_ncr_2)); return stg(sr_char_start + i - 1..(sr_char_start + j - 1) min rec_size); -- return string section, but truncate to available part end; procedure sr_set_slice(rw rec,i,j,stg); -- set slice of record if i < 1 or j < i - 1 then abort("***** ILLEGAL SLICE BOUNDARIES IN DISK_RECORDS STRING ASSIGNMENT ***** " + str(i) + " " + str(j)); end if; stg_now := dr_load(rec); -- load this record into RAM -- replace rec with a fresh copy if necessary if refcount(int_of_4(rec)) > 1 then -- must copy new_r := dr_new_rec(); dr_setrecbuf(new_r,stg_now); dr_dirtify(new_r); incref(rec,-1); rec := new_r; -- substitute copy for original -- [increfs(new_r,1); not applicable] end if; rec_len := int_from_bytes(stg_now(sr_ncr_1..sr_ncr_2)); -- present length erased := j - i + 1; inserted := #stg; rec_len +:= (inserted - erased); if rec_len + 2 > rec_size then abort("***** RECORD BOUNDARY OVERSTEPPED IN DISK_RECORDS STRING ASSIGNMENT ***** " + str(i) + " " + str(j) + " " + str(rec_len)); end if; stg_now(sr_ncr_1..sr_ncr_2) := bytes_from_int(rec_len); stg_now(sr_char_start + i - 1..sr_char_start + j - 1) := stg; if #stg_now > rec_size then stg_now := stg_now(1..rec_size); end if; -- cut to allowed length dr_setrecbuf(rec,stg_now); dr_dirtify(rec); -- note that the record has been changed end; -- ******** special routines for wdoccs_string_record nodes ******** procedure wo_length(rec); -- length of active part of a record stg := dr_load(rec); -- load this recno into RAM return int_from_byte(stg(wo_nr_1..wo_nr_2)); -- convert and return the length field end wo_length; procedure wo_slice(rec,i,j); -- get slice of a wd occurence record stg := dr_load(rec); -- load this recno into RAM rec_len := int_from_byte(stg(wo_nr_1..wo_nr_2)); return stg(wo_occs_start + 4 * i - 4..(wo_occs_start + 4 * j - 1) min rec_size); -- return string section, but truncate to available part end wo_slice; procedure wo_set_slice(rw rec,i,j,stg); -- set slice of wd occurence record if i < 1 or j < i - 1 then abort("***** ILLEGAL SLICE BOUNDARIES IN DISK_RECORDS STRING ASSIGNMENT ***** " + str(i) + " " + str(j)); end if; stg_now := dr_load(rec); -- load this record into RAM -- replace rec with a fresh copy if necessary if refcount(int_of_4(rec)) > 1 then -- must copy new_r := dr_new_rec(); dr_setrecbuf(new_r,stg_now); dr_dirtify(new_r); incref(rec,-1); rec := new_r; -- substitute copy for original -- [increfs(new_r,1); not applicable] end if; rec_len := int_from_byte(stg_now(wo_nr_1..wo_nr_2)); -- present length erased := (j - i + 1); inserted := #stg/4; rec_len +:= (inserted - erased); if 4*rec_len + 2 > rec_size then abort("***** RECORD BOUNDARY OVERSTEPPED IN DISK_RECORDS STRING ASSIGNMENT ***** " + str(i) + " " + str(j) + " " + str(rec_len)); end if; stg_now(wo_nr_1..wo_nr_2) := byte_from_int(rec_len); stg_now(wo_occs_start + 4 * i - 4..wo_occs_start + 4 * j - 1) := stg; if (nsn := #stg_now) > rec_size then stg_now := stg_now(1..rec_size); -- cut to allowed length elseif nsn < rec_size then -- pad to required length stg_now := stg_now + (rec_size - nsn) * "\x00"; end if; dr_setrecbuf(rec,stg_now); -- copy back to the buffer dr_dirtify(rec); -- note that the record has been changed end wo_set_slice; -- ******** special routines for bigstr_node_record nodes ******** -- ******** special routines for wdoccs_str_node_record nodes ******** -- ******** special routines for wd_index_node_record nodes ******** -- ******** special routines for db_index_node_record nodes ******** end db_records; program test; -- test program for disk_records class use db_records,byteutil,string_utility_pak,disk_records_pak,setldb; recs := [make_string_record(): j in [1..10]]; print("records created"); dr_flush_all(); -- force flush of all the records print("flush_all done"); for r = recs(j) loop sr_set_slice(r,1,0,8 * ("Hello World" + str(j) + ". ")); recs(j) := r; print(sr_length(r)," ",sr_slice(r,1,sr_length(r))); end loop; the_tup := [ ]; r := recs(1); for j in [1..10] loop incref(r,1); -- increment the refcount of this object sr_set_slice(r,1,5,""); the_tup with:= r; end loop; for j in [1..10] loop incref(r,1); -- increment the refcount of this object sr_set_slice(r,1,0,"XXXXX"); the_tup with:= r; end loop; for j in [1..20] loop print("copy: ",j," ",sr_length(the_tup(j))," ",sr_slice(the_tup(j),1,sr_length(the_tup(j)))); end loop; procedure make_string_record(); -- create an empty string record set_type(rec := dr_new_rec(),string_record); return rec; end make_string_record; end test;