FileDir.Mod.txt 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. MODULE FileDir; (*NW 12.1.86 / 23.8.90 / 15.8.2013*)
  2. IMPORT SYSTEM, Kernel;
  3. (*File Directory is a B-tree with its root page at DirRootAdr.
  4. Each entry contains a file name and the disk address of the file's head sector*)
  5. CONST FnLength* = 32;
  6. SecTabSize* = 64;
  7. ExTabSize* = 12;
  8. SectorSize* = 1024;
  9. IndexSize* = SectorSize DIV 4;
  10. HeaderSize* = 352;
  11. DirRootAdr* = 29;
  12. DirPgSize* = 24;
  13. N = DirPgSize DIV 2;
  14. DirMark* = 9B1EA38DH;
  15. HeaderMark* = 9BA71D86H;
  16. FillerSize = 52;
  17. TYPE DiskAdr = INTEGER;
  18. FileName* = ARRAY FnLength OF CHAR;
  19. SectorTable* = ARRAY SecTabSize OF DiskAdr;
  20. ExtensionTable* = ARRAY ExTabSize OF DiskAdr;
  21. EntryHandler* = PROCEDURE (name: FileName; sec: DiskAdr; VAR continue: BOOLEAN);
  22. FileHeader* =
  23. RECORD (*first page of each file on disk*)
  24. mark*: INTEGER;
  25. name*: FileName;
  26. aleng*, bleng*, date*: INTEGER;
  27. ext*: ExtensionTable;
  28. sec*: SectorTable;
  29. fill: ARRAY SectorSize - HeaderSize OF BYTE;
  30. END ;
  31. FileHd* = POINTER TO FileHeader;
  32. IndexSector* = ARRAY IndexSize OF DiskAdr;
  33. DataSector* = ARRAY SectorSize OF BYTE;
  34. DirEntry* = (*B-tree node*)
  35. RECORD
  36. name*: FileName;
  37. adr*: DiskAdr; (*sec no of file header*)
  38. p*: DiskAdr (*sec no of descendant in directory*)
  39. END ;
  40. DirPage* =
  41. RECORD mark*: INTEGER;
  42. m*: INTEGER;
  43. p0*: DiskAdr; (*sec no of left descendant in directory*)
  44. fill: ARRAY FillerSize OF BYTE;
  45. e*: ARRAY DirPgSize OF DirEntry
  46. END ;
  47. (*Exported procedures: Search, Insert, Delete, Enumerate, Init*)
  48. PROCEDURE Search*(name: FileName; VAR A: DiskAdr);
  49. VAR i, L, R: INTEGER; dadr: DiskAdr;
  50. a: DirPage;
  51. BEGIN dadr := DirRootAdr; A := 0;
  52. REPEAT Kernel.GetSector(dadr, a); ASSERT(a.mark = DirMark);
  53. L := 0; R := a.m; (*binary search*)
  54. WHILE L < R DO
  55. i := (L+R) DIV 2;
  56. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  57. END ;
  58. IF (R < a.m) & (name = a.e[R].name) THEN A := a.e[R].adr (*found*)
  59. ELSIF R = 0 THEN dadr := a.p0
  60. ELSE dadr := a.e[R-1].p
  61. END ;
  62. UNTIL (dadr = 0) OR (A # 0)
  63. END Search;
  64. PROCEDURE insert(name: FileName;
  65. dpg0: DiskAdr;
  66. VAR h: BOOLEAN;
  67. VAR v: DirEntry;
  68. fad: DiskAdr);
  69. (*h = "tree has become higher and v is ascending element"*)
  70. VAR ch: CHAR;
  71. i, j, L, R: INTEGER;
  72. dpg1: DiskAdr;
  73. u: DirEntry;
  74. a: DirPage;
  75. BEGIN (*~h*) Kernel.GetSector(dpg0, a); ASSERT(a.mark = DirMark);
  76. L := 0; R := a.m; (*binary search*)
  77. WHILE L < R DO
  78. i := (L+R) DIV 2;
  79. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  80. END ;
  81. IF (R < a.m) & (name = a.e[R].name) THEN
  82. a.e[R].adr := fad; Kernel.PutSector(dpg0, a) (*replace*)
  83. ELSE (*not on this page*)
  84. IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
  85. IF dpg1 = 0 THEN (*not in tree, insert*)
  86. u.adr := fad; u.p := 0; h := TRUE; j := 0;
  87. REPEAT ch := name[j]; u.name[j] := ch; INC(j)
  88. UNTIL ch = 0X;
  89. WHILE j < FnLength DO u.name[j] := 0X; INC(j) END ;
  90. ELSE
  91. insert(name, dpg1, h, u, fad)
  92. END ;
  93. IF h THEN (*insert u to the left of e[R]*)
  94. IF a.m < DirPgSize THEN
  95. h := FALSE; i := a.m;
  96. WHILE i > R DO DEC(i); a.e[i+1] := a.e[i] END ;
  97. a.e[R] := u; INC(a.m)
  98. ELSE (*split page and assign the middle element to v*)
  99. a.m := N; a.mark := DirMark;
  100. IF R < N THEN (*insert in left half*)
  101. v := a.e[N-1]; i := N-1;
  102. WHILE i > R DO DEC(i); a.e[i+1] := a.e[i] END ;
  103. a.e[R] := u; Kernel.PutSector(dpg0, a);
  104. Kernel.AllocSector(dpg0, dpg0); i := 0;
  105. WHILE i < N DO a.e[i] := a.e[i+N]; INC(i) END
  106. ELSE (*insert in right half*)
  107. Kernel.PutSector(dpg0, a);
  108. Kernel.AllocSector(dpg0, dpg0); DEC(R, N); i := 0;
  109. IF R = 0 THEN v := u
  110. ELSE v := a.e[N];
  111. WHILE i < R-1 DO a.e[i] := a.e[N+1+i]; INC(i) END ;
  112. a.e[i] := u; INC(i)
  113. END ;
  114. WHILE i < N DO a.e[i] := a.e[N+i]; INC(i) END
  115. END ;
  116. a.p0 := v.p; v.p := dpg0
  117. END ;
  118. Kernel.PutSector(dpg0, a)
  119. END
  120. END
  121. END insert;
  122. PROCEDURE Insert*(name: FileName; fad: DiskAdr);
  123. VAR oldroot: DiskAdr;
  124. h: BOOLEAN; U: DirEntry;
  125. a: DirPage;
  126. BEGIN h := FALSE;
  127. insert(name, DirRootAdr, h, U, fad);
  128. IF h THEN (*root overflow*)
  129. Kernel.GetSector(DirRootAdr, a); ASSERT(a.mark = DirMark);
  130. Kernel.AllocSector(DirRootAdr, oldroot); Kernel.PutSector(oldroot, a);
  131. a.mark := DirMark; a.m := 1; a.p0 := oldroot; a.e[0] := U;
  132. Kernel.PutSector(DirRootAdr, a)
  133. END
  134. END Insert;
  135. PROCEDURE underflow(VAR c: DirPage; (*ancestor page*)
  136. dpg0: DiskAdr;
  137. s: INTEGER; (*insertion point in c*)
  138. VAR h: BOOLEAN); (*c undersize*)
  139. VAR i, k: INTEGER;
  140. dpg1: DiskAdr;
  141. a, b: DirPage; (*a := underflowing page, b := neighbouring page*)
  142. BEGIN Kernel.GetSector(dpg0, a); ASSERT(a.mark = DirMark);
  143. (*h & a.m = N-1 & dpg0 = c.e[s-1].p*)
  144. IF s < c.m THEN (*b := page to the right of a*)
  145. dpg1 := c.e[s].p; Kernel.GetSector(dpg1, b); ASSERT(b.mark = DirMark);
  146. k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
  147. a.e[N-1] := c.e[s]; a.e[N-1].p := b.p0;
  148. IF k > 0 THEN
  149. (*move k-1 items from b to a, one to c*) i := 0;
  150. WHILE i < k-1 DO a.e[i+N] := b.e[i]; INC(i) END ;
  151. c.e[s] := b.e[i]; b.p0 := c.e[s].p;
  152. c.e[s].p := dpg1; b.m := b.m - k; i := 0;
  153. WHILE i < b.m DO b.e[i] := b.e[i+k]; INC(i) END ;
  154. Kernel.PutSector(dpg1, b); a.m := N-1+k; h := FALSE
  155. ELSE (*merge pages a and b, discard b*) i := 0;
  156. WHILE i < N DO a.e[i+N] := b.e[i]; INC(i) END ;
  157. i := s; DEC(c.m);
  158. WHILE i < c.m DO c.e[i] := c.e[i+1]; INC(i) END ;
  159. a.m := 2*N; h := c.m < N
  160. END ;
  161. Kernel.PutSector(dpg0, a)
  162. ELSE (*b := page to the left of a*) DEC(s);
  163. IF s = 0 THEN dpg1 := c.p0 ELSE dpg1 := c.e[s-1].p END ;
  164. Kernel.GetSector(dpg1, b); ASSERT(b.mark = DirMark);
  165. k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
  166. IF k > 0 THEN
  167. i := N-1;
  168. WHILE i > 0 DO DEC(i); a.e[i+k] := a.e[i] END ;
  169. i := k-1; a.e[i] := c.e[s]; a.e[i].p := a.p0;
  170. (*move k-1 items from b to a, one to c*) b.m := b.m - k;
  171. WHILE i > 0 DO DEC(i); a.e[i] := b.e[i+b.m+1] END ;
  172. c.e[s] := b.e[b.m]; a.p0 := c.e[s].p;
  173. c.e[s].p := dpg0; a.m := N-1+k; h := FALSE;
  174. Kernel.PutSector(dpg0, a)
  175. ELSE (*merge pages a and b, discard a*)
  176. c.e[s].p := a.p0; b.e[N] := c.e[s]; i := 0;
  177. WHILE i < N-1 DO b.e[i+N+1] := a.e[i]; INC(i) END ;
  178. b.m := 2*N; DEC(c.m); h := c.m < N
  179. END ;
  180. Kernel.PutSector(dpg1, b)
  181. END
  182. END underflow;
  183. PROCEDURE delete(name: FileName;
  184. dpg0: DiskAdr;
  185. VAR h: BOOLEAN;
  186. VAR fad: DiskAdr);
  187. (*search and delete entry with key name; if a page underflow arises,
  188. balance with adjacent page or merge; h := "page dpg0 is undersize"*)
  189. VAR i, L, R: INTEGER;
  190. dpg1: DiskAdr;
  191. a: DirPage;
  192. PROCEDURE del(VAR a: DirPage; R: INTEGER; dpg1: DiskAdr; VAR h: BOOLEAN);
  193. VAR dpg2: DiskAdr; (*global: a, R*)
  194. b: DirPage;
  195. BEGIN Kernel.GetSector(dpg1, b); ASSERT(b.mark = DirMark); dpg2 := b.e[b.m-1].p;
  196. IF dpg2 # 0 THEN del(a, R, dpg2, h);
  197. IF h THEN underflow(b, dpg2, b.m, h); Kernel.PutSector(dpg1, b) END
  198. ELSE
  199. b.e[b.m-1].p := a.e[R].p; a.e[R] := b.e[b.m-1];
  200. DEC(b.m); h := b.m < N; Kernel.PutSector(dpg1, b)
  201. END
  202. END del;
  203. BEGIN (*~h*) Kernel.GetSector(dpg0, a); ASSERT(a.mark = DirMark);
  204. L := 0; R := a.m; (*binary search*)
  205. WHILE L < R DO
  206. i := (L+R) DIV 2;
  207. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  208. END ;
  209. IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
  210. IF (R < a.m) & (name = a.e[R].name) THEN
  211. (*found, now delete*) fad := a.e[R].adr;
  212. IF dpg1 = 0 THEN (*a is a leaf page*)
  213. DEC(a.m); h := a.m < N; i := R;
  214. WHILE i < a.m DO a.e[i] := a.e[i+1]; INC(i) END
  215. ELSE del(a, R, dpg1, h);
  216. IF h THEN underflow(a, dpg1, R, h) END
  217. END ;
  218. Kernel.PutSector(dpg0, a)
  219. ELSIF dpg1 # 0 THEN
  220. delete(name, dpg1, h, fad);
  221. IF h THEN underflow(a, dpg1, R, h); Kernel.PutSector(dpg0, a) END
  222. ELSE (*not in tree*) fad := 0
  223. END
  224. END delete;
  225. PROCEDURE Delete*(name: FileName; VAR fad: DiskAdr);
  226. VAR h: BOOLEAN; newroot: DiskAdr;
  227. a: DirPage;
  228. BEGIN h := FALSE;
  229. delete(name, DirRootAdr, h, fad);
  230. IF h THEN (*root underflow*)
  231. Kernel.GetSector(DirRootAdr, a); ASSERT(a.mark = DirMark);
  232. IF (a.m = 0) & (a.p0 # 0) THEN
  233. newroot := a.p0; Kernel.GetSector(newroot, a); ASSERT(a.mark = DirMark);
  234. Kernel.PutSector(DirRootAdr, a) (*discard newroot*)
  235. END
  236. END
  237. END Delete;
  238. PROCEDURE enumerate(prefix: ARRAY OF CHAR;
  239. dpg: DiskAdr;
  240. proc: EntryHandler;
  241. VAR continue: BOOLEAN);
  242. VAR i, j: INTEGER; pfx, nmx: CHAR;
  243. dpg1: DiskAdr; a: DirPage;
  244. BEGIN Kernel.GetSector(dpg, a); ASSERT(a.mark = DirMark); i := 0;
  245. WHILE (i < a.m) & continue DO
  246. j := 0;
  247. REPEAT pfx := prefix[j]; nmx := a.e[i].name[j]; INC(j)
  248. UNTIL (nmx # pfx) OR (pfx = 0X);
  249. IF nmx >= pfx THEN
  250. IF i = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[i-1].p END ;
  251. IF dpg1 # 0 THEN enumerate(prefix, dpg1, proc, continue) END ;
  252. IF pfx = 0X THEN
  253. IF continue THEN proc(a.e[i].name, a.e[i].adr, continue) END
  254. ELSE continue := FALSE
  255. END
  256. END ;
  257. INC(i)
  258. END ;
  259. IF continue & (i > 0) & (a.e[i-1].p # 0) THEN
  260. enumerate(prefix, a.e[i-1].p, proc, continue)
  261. END
  262. END enumerate;
  263. PROCEDURE Enumerate*(prefix: ARRAY OF CHAR; proc: EntryHandler);
  264. VAR b: BOOLEAN;
  265. BEGIN b := TRUE; enumerate(prefix, DirRootAdr, proc, b)
  266. END Enumerate;
  267. (* ----- initialization ----- *)
  268. PROCEDURE Init*;
  269. VAR k: INTEGER;
  270. A: ARRAY 2000 OF DiskAdr;
  271. PROCEDURE MarkSectors(VAR A: ARRAY OF DiskAdr; k: INTEGER);
  272. VAR L, R, i, j, n: INTEGER; x: DiskAdr;
  273. hd: FileHeader;
  274. B: IndexSector;
  275. PROCEDURE sift(VAR A: ARRAY OF DiskAdr; L, R: INTEGER);
  276. VAR i, j: INTEGER; x: DiskAdr;
  277. BEGIN j := L; x := A[j];
  278. REPEAT i := j; j := 2*j + 1;
  279. IF (j+1 < R) & (A[j] < A[j+1]) THEN INC(j) END ;
  280. IF (j < R) & (x <= A[j]) THEN A[i] := A[j] END
  281. UNTIL (j >= R) OR (x > A[j]);
  282. A[i] := x
  283. END sift;
  284. BEGIN L := k DIV 2; R := k; (*heapsort*)
  285. WHILE L > 0 DO DEC(L); sift(A, L, R) END ;
  286. WHILE R > 0 DO
  287. DEC(R); x := A[0]; A[0] := A[R]; A[R] := x; sift(A, L, R)
  288. END ;
  289. WHILE L < k DO
  290. Kernel.GetSector(A[L], hd); ASSERT(hd.mark = HeaderMark);
  291. IF hd.aleng < SecTabSize THEN j := hd.aleng + 1;
  292. REPEAT DEC(j); Kernel.MarkSector(hd.sec[j]) UNTIL j = 0
  293. ELSE j := SecTabSize;
  294. REPEAT DEC(j); Kernel.MarkSector(hd.sec[j]) UNTIL j = 0;
  295. n := (hd.aleng - SecTabSize) DIV 256; i := 0;
  296. WHILE i <= n DO
  297. Kernel.MarkSector(hd.ext[i]);
  298. Kernel.GetSector(hd.ext[i], B); (*index sector*)
  299. IF i < n THEN j := 256 ELSE j := (hd.aleng - SecTabSize) MOD 256 + 1 END ;
  300. REPEAT DEC(j); Kernel.MarkSector(B[j]) UNTIL j = 0;
  301. INC(i)
  302. END
  303. END ;
  304. INC(L)
  305. END
  306. END MarkSectors;
  307. PROCEDURE TraverseDir(VAR A: ARRAY OF DiskAdr; VAR k: INTEGER; dpg: DiskAdr);
  308. VAR i: INTEGER; a: DirPage;
  309. BEGIN Kernel.GetSector(dpg, a); ASSERT(a.mark = DirMark); Kernel.MarkSector(dpg); i := 0;
  310. WHILE i < a.m DO
  311. A[k] := a.e[i].adr; INC(k); INC(i);
  312. IF k = 2000 THEN MarkSectors(A, k); k := 0 END
  313. END ;
  314. IF a.p0 # 0 THEN
  315. TraverseDir(A, k, a.p0); i := 0;
  316. WHILE i < a.m DO
  317. TraverseDir(A, k, a.e[i].p); INC(i)
  318. END
  319. END
  320. END TraverseDir;
  321. BEGIN k := 0; TraverseDir(A, k, DirRootAdr); MarkSectors(A, k)
  322. END Init;
  323. END FileDir.