fpcmdic.pp 15 KB

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