fpcmdic.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. {
  2. $Id$
  3. Copyright (c) 2001 by Peter Vreman
  4. TDictionary class
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$ifdef fpc}{$mode objfpc}{$endif}
  12. {$H+}
  13. unit fpcmdic;
  14. interface
  15. const { the real size will be [-hasharray..hasharray] ! }
  16. hasharraysize = 2047;
  17. type
  18. { namedindexobect for use with dictionary and indexarray }
  19. TDictionaryItem=class
  20. private
  21. Fname : string;
  22. FSpeedValue : cardinal;
  23. protected
  24. procedure SetName(const n:string);
  25. public
  26. left,
  27. right : TDictionaryItem;
  28. constructor create(const n:string);
  29. property Name:string read FName write SetName;
  30. property SpeedValue:cardinal read FSpeedValue;
  31. end;
  32. Pdictionaryhasharray=^Tdictionaryhasharray;
  33. Tdictionaryhasharray=array[-hasharraysize..hasharraysize] of TDictionaryItem;
  34. Tnamedindexcallback = procedure(p:TDictionaryItem) of object;
  35. Tdictionary=class
  36. private
  37. FRoot : TDictionaryItem;
  38. FHashArray : Pdictionaryhasharray;
  39. procedure cleartree(obj:TDictionaryItem);
  40. function insertNode(NewNode:TDictionaryItem;var currNode:TDictionaryItem):TDictionaryItem;
  41. procedure inserttree(currtree,currroot:TDictionaryItem);
  42. public
  43. noclear : boolean;
  44. replace_existing : boolean;
  45. constructor Create;
  46. destructor Destroy;override;
  47. procedure usehash;
  48. procedure clear;
  49. function delete(const s:string):TDictionaryItem;
  50. function empty:boolean;
  51. procedure foreach(proc2call:Tnamedindexcallback);
  52. function insert(obj:TDictionaryItem):TDictionaryItem;
  53. function rename(const olds,News : string):TDictionaryItem;
  54. function search(const s:string):TDictionaryItem;
  55. function speedsearch(const s:string;SpeedValue:Cardinal):TDictionaryItem;
  56. property Items[const s:string]:TDictionaryItem read Search;default;
  57. end;
  58. { Speed/Hash value }
  59. Function GetSpeedValue(Const s:String):cardinal;
  60. implementation
  61. {*****************************************************************************
  62. GetSpeedValue
  63. *****************************************************************************}
  64. var
  65. Crc32Tbl : array[0..255] of cardinal;
  66. procedure MakeCRC32Tbl;
  67. var
  68. crc : cardinal;
  69. i,n : integer;
  70. begin
  71. for i:=0 to 255 do
  72. begin
  73. crc:=i;
  74. for n:=1 to 8 do
  75. if odd(crc) then
  76. crc:=(crc shr 1) xor $edb88320
  77. else
  78. crc:=crc shr 1;
  79. Crc32Tbl[i]:=crc;
  80. end;
  81. end;
  82. Function GetSpeedValue(Const s:String):cardinal;
  83. var
  84. i : integer;
  85. InitCrc : cardinal;
  86. begin
  87. if Crc32Tbl[1]=0 then
  88. MakeCrc32Tbl;
  89. InitCrc:=$ffffffff;
  90. for i:=1 to Length(s) do
  91. InitCrc:=Crc32Tbl[byte(InitCrc) xor ord(s[i])] xor (InitCrc shr 8);
  92. GetSpeedValue:=InitCrc;
  93. end;
  94. {****************************************************************************
  95. TDictionaryItem
  96. ****************************************************************************}
  97. constructor TDictionaryItem.Create(const n:string);
  98. begin
  99. left:=nil;
  100. right:=nil;
  101. FSpeedValue:=$ffffffff;
  102. FName:=n;
  103. end;
  104. procedure TDictionaryItem.Setname(const n:string);
  105. begin
  106. if FSpeedValue=$ffffffff then
  107. FName:=n;
  108. end;
  109. {****************************************************************************
  110. TDICTIONARY
  111. ****************************************************************************}
  112. constructor Tdictionary.Create;
  113. begin
  114. FRoot:=nil;
  115. FHashArray:=nil;
  116. noclear:=false;
  117. replace_existing:=false;
  118. end;
  119. procedure Tdictionary.usehash;
  120. begin
  121. if not(assigned(FRoot)) and
  122. not(assigned(FHashArray)) then
  123. begin
  124. New(FHashArray);
  125. fillchar(FHashArray^,sizeof(FHashArray^),0);
  126. end;
  127. end;
  128. destructor Tdictionary.destroy;
  129. begin
  130. if not noclear then
  131. clear;
  132. if assigned(FHashArray) then
  133. dispose(FHashArray);
  134. end;
  135. procedure Tdictionary.cleartree(obj:TDictionaryItem);
  136. begin
  137. if assigned(obj.left) then
  138. cleartree(obj.left);
  139. if assigned(obj.right) then
  140. cleartree(obj.right);
  141. obj.free;
  142. obj:=nil;
  143. end;
  144. procedure Tdictionary.clear;
  145. var
  146. w : integer;
  147. begin
  148. if assigned(FRoot) then
  149. cleartree(FRoot);
  150. if assigned(FHashArray) then
  151. for w:=-hasharraysize to hasharraysize do
  152. if assigned(FHashArray^[w]) then
  153. cleartree(FHashArray^[w]);
  154. end;
  155. function Tdictionary.delete(const s:string):TDictionaryItem;
  156. var
  157. p,SpeedValue : cardinal;
  158. n : TDictionaryItem;
  159. procedure insert_right_bottom(var root,Atree:TDictionaryItem);
  160. begin
  161. while root.right<>nil do
  162. root:=root.right;
  163. root.right:=Atree;
  164. end;
  165. function delete_from_tree(root:TDictionaryItem):TDictionaryItem;
  166. type
  167. leftright=(left,right);
  168. var
  169. lr : leftright;
  170. oldroot : TDictionaryItem;
  171. begin
  172. oldroot:=nil;
  173. while (root<>nil) and (root.SpeedValue<>SpeedValue) do
  174. begin
  175. oldroot:=root;
  176. if SpeedValue<root.SpeedValue then
  177. begin
  178. root:=root.right;
  179. lr:=right;
  180. end
  181. else
  182. begin
  183. root:=root.left;
  184. lr:=left;
  185. end;
  186. end;
  187. while (root<>nil) and (root.name<>s) do
  188. begin
  189. oldroot:=root;
  190. if s<root.name then
  191. begin
  192. root:=root.right;
  193. lr:=right;
  194. end
  195. else
  196. begin
  197. root:=root.left;
  198. lr:=left;
  199. end;
  200. end;
  201. if root.left<>nil then
  202. begin
  203. { Now the Node pointing to root must point to the left
  204. subtree of root. The right subtree of root must be
  205. connected to the right bottom of the left subtree.}
  206. if lr=left then
  207. oldroot.left:=root.left
  208. else
  209. oldroot.right:=root.left;
  210. if root.right<>nil then
  211. insert_right_bottom(root.left,root.right);
  212. end
  213. else
  214. begin
  215. { There is no left subtree. So we can just replace the Node to
  216. delete with the right subtree.}
  217. if lr=left then
  218. oldroot.left:=root.right
  219. else
  220. oldroot.right:=root.right;
  221. end;
  222. delete_from_tree:=root;
  223. end;
  224. begin
  225. SpeedValue:=GetSpeedValue(s);
  226. n:=FRoot;
  227. if assigned(FHashArray) then
  228. begin
  229. { First, check if the Node to delete directly located under
  230. the hasharray.}
  231. p:=SpeedValue mod hasharraysize;
  232. n:=FHashArray^[p];
  233. if (n<>nil) and (n.SpeedValue=SpeedValue) and
  234. (n.name=s) then
  235. begin
  236. { The Node to delete is directly located under the
  237. hasharray. Make the hasharray point to the left
  238. subtree of the Node and place the right subtree on
  239. the right-bottom of the left subtree.}
  240. if n.left<>nil then
  241. begin
  242. FHashArray^[p]:=n.left;
  243. if n.right<>nil then
  244. insert_right_bottom(n.left,n.right);
  245. end
  246. else
  247. FHashArray^[p]:=n.right;
  248. delete:=n;
  249. exit;
  250. end;
  251. end
  252. else
  253. begin
  254. { First check if the Node to delete is the root.}
  255. if (FRoot<>nil) and (n.SpeedValue=SpeedValue) and
  256. (n.name=s) then
  257. begin
  258. if n.left<>nil then
  259. begin
  260. FRoot:=n.left;
  261. if n.right<>nil then
  262. insert_right_bottom(n.left,n.right);
  263. end
  264. else
  265. FRoot:=n.right;
  266. delete:=n;
  267. exit;
  268. end;
  269. end;
  270. delete:=delete_from_tree(n);
  271. end;
  272. function Tdictionary.empty:boolean;
  273. var
  274. w : integer;
  275. begin
  276. if assigned(FHashArray) then
  277. begin
  278. empty:=false;
  279. for w:=-hasharraysize to hasharraysize do
  280. if assigned(FHashArray^[w]) then
  281. exit;
  282. empty:=true;
  283. end
  284. else
  285. empty:=(FRoot=nil);
  286. end;
  287. procedure Tdictionary.foreach(proc2call:Tnamedindexcallback);
  288. procedure a(p:TDictionaryItem);
  289. begin
  290. proc2call(p);
  291. if assigned(p.left) then
  292. a(p.left);
  293. if assigned(p.right) then
  294. a(p.right);
  295. end;
  296. var
  297. i : integer;
  298. begin
  299. if assigned(FHashArray) then
  300. begin
  301. for i:=-hasharraysize to hasharraysize do
  302. if assigned(FHashArray^[i]) then
  303. a(FHashArray^[i]);
  304. end
  305. else
  306. if assigned(FRoot) then
  307. a(FRoot);
  308. end;
  309. function Tdictionary.insert(obj:TDictionaryItem):TDictionaryItem;
  310. begin
  311. obj.FSpeedValue:=GetSpeedValue(obj.name);
  312. if assigned(FHashArray) then
  313. insert:=insertNode(obj,FHashArray^[obj.SpeedValue mod hasharraysize])
  314. else
  315. insert:=insertNode(obj,FRoot);
  316. end;
  317. function tdictionary.insertNode(NewNode:TDictionaryItem;var currNode:TDictionaryItem):TDictionaryItem;
  318. begin
  319. if currNode=nil then
  320. begin
  321. currNode:=NewNode;
  322. insertNode:=NewNode;
  323. end
  324. { First check SpeedValue, to allow a fast insert }
  325. else
  326. if currNode.SpeedValue>NewNode.SpeedValue then
  327. insertNode:=insertNode(NewNode,currNode.right)
  328. else
  329. if currNode.SpeedValue<NewNode.SpeedValue then
  330. insertNode:=insertNode(NewNode,currNode.left)
  331. else
  332. begin
  333. if currNode.name>NewNode.name then
  334. insertNode:=insertNode(NewNode,currNode.right)
  335. else
  336. if currNode.name<NewNode.name then
  337. insertNode:=insertNode(NewNode,currNode.left)
  338. else
  339. begin
  340. if replace_existing and
  341. assigned(currNode) then
  342. begin
  343. NewNode.left:=currNode.left;
  344. NewNode.right:=currNode.right;
  345. currNode:=NewNode;
  346. insertNode:=NewNode;
  347. end
  348. else
  349. insertNode:=currNode;
  350. end;
  351. end;
  352. end;
  353. procedure tdictionary.inserttree(currtree,currroot:TDictionaryItem);
  354. begin
  355. if assigned(currtree) then
  356. begin
  357. inserttree(currtree.left,currroot);
  358. inserttree(currtree.right,currroot);
  359. currtree.right:=nil;
  360. currtree.left:=nil;
  361. insertNode(currtree,currroot);
  362. end;
  363. end;
  364. function tdictionary.rename(const olds,News : string):TDictionaryItem;
  365. var
  366. spdval : Cardinal;
  367. lasthp,
  368. hp,hp2,hp3 : TDictionaryItem;
  369. begin
  370. spdval:=GetSpeedValue(olds);
  371. if assigned(FHashArray) then
  372. hp:=FHashArray^[spdval mod hasharraysize]
  373. else
  374. hp:=FRoot;
  375. lasthp:=nil;
  376. while assigned(hp) do
  377. begin
  378. if spdval>hp.SpeedValue then
  379. begin
  380. lasthp:=hp;
  381. hp:=hp.left
  382. end
  383. else
  384. if spdval<hp.SpeedValue then
  385. begin
  386. lasthp:=hp;
  387. hp:=hp.right
  388. end
  389. else
  390. begin
  391. if (hp.name=olds) then
  392. begin
  393. { Get in hp2 the replacer for the root or hasharr }
  394. hp2:=hp.left;
  395. hp3:=hp.right;
  396. if not assigned(hp2) then
  397. begin
  398. hp2:=hp.right;
  399. hp3:=hp.left;
  400. end;
  401. { remove entry from the tree }
  402. if assigned(lasthp) then
  403. begin
  404. if lasthp.left=hp then
  405. lasthp.left:=hp2
  406. else
  407. lasthp.right:=hp2;
  408. end
  409. else
  410. begin
  411. if assigned(FHashArray) then
  412. FHashArray^[spdval mod hasharraysize]:=hp2
  413. else
  414. FRoot:=hp2;
  415. end;
  416. { reinsert the hp3 in the tree from hp2 }
  417. inserttree(hp3,hp2);
  418. { reset Node with New values }
  419. hp.name:=newS;
  420. hp.FSpeedValue:=GetSpeedValue(newS);
  421. hp.left:=nil;
  422. hp.right:=nil;
  423. { reinsert }
  424. if assigned(FHashArray) then
  425. rename:=insertNode(hp,FHashArray^[hp.SpeedValue mod hasharraysize])
  426. else
  427. rename:=insertNode(hp,FRoot);
  428. exit;
  429. end
  430. else
  431. if olds>hp.name then
  432. begin
  433. lasthp:=hp;
  434. hp:=hp.left
  435. end
  436. else
  437. begin
  438. lasthp:=hp;
  439. hp:=hp.right;
  440. end;
  441. end;
  442. end;
  443. end;
  444. function Tdictionary.search(const s:string):TDictionaryItem;
  445. begin
  446. search:=speedsearch(s,GetSpeedValue(s));
  447. end;
  448. function Tdictionary.speedsearch(const s:string;SpeedValue:Cardinal):TDictionaryItem;
  449. var
  450. NewNode:TDictionaryItem;
  451. begin
  452. if assigned(FHashArray) then
  453. NewNode:=FHashArray^[SpeedValue mod hasharraysize]
  454. else
  455. NewNode:=FRoot;
  456. while assigned(NewNode) do
  457. begin
  458. if SpeedValue>NewNode.SpeedValue then
  459. NewNode:=NewNode.left
  460. else
  461. if SpeedValue<NewNode.SpeedValue then
  462. NewNode:=NewNode.right
  463. else
  464. begin
  465. if (NewNode.name=s) then
  466. begin
  467. speedsearch:=NewNode;
  468. exit;
  469. end
  470. else
  471. if s>NewNode.name then
  472. NewNode:=NewNode.left
  473. else
  474. NewNode:=NewNode.right;
  475. end;
  476. end;
  477. speedsearch:=nil;
  478. end;
  479. end.
  480. {
  481. $Log$
  482. Revision 1.3 2002-09-07 15:40:31 peter
  483. * old logs removed and tabs fixed
  484. }