dynarr.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2000 by Florian Klaempfl
  4. member of the Free Pascal development team.
  5. This file implements the helper routines for dyn. Arrays in FPC
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************
  12. }
  13. type
  14. { don't add new fields, the size is used }
  15. { to calculate memory requirements }
  16. pdynarray = ^tdynarray;
  17. tdynarray = packed record
  18. refcount : ptrint;
  19. high : tdynarrayindex;
  20. end;
  21. pdynarraytypedata = ^tdynarraytypedata;
  22. tdynarraytypedata =
  23. {$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}
  24. packed
  25. {$endif FPC_REQUIRES_PROPER_ALIGNMENT}
  26. record
  27. elSize : SizeUInt;
  28. elType2 : Pointer;
  29. varType : Longint;
  30. elType : Pointer;
  31. end;
  32. procedure fpc_dynarray_rangecheck(p : pointer;i : tdynarrayindex);[Public,Alias:'FPC_DYNARRAY_RANGECHECK']; compilerproc;
  33. begin
  34. if not(assigned(p)) or (i<0) or (i>pdynarray(p-sizeof(tdynarray))^.high) then
  35. HandleErrorAddrFrameInd(201,get_pc_addr,get_frame);
  36. end;
  37. function fpc_dynarray_length(p : pointer) : tdynarrayindex;[Public,Alias:'FPC_DYNARRAY_LENGTH']; compilerproc;
  38. begin
  39. if assigned(p) then
  40. fpc_dynarray_length:=pdynarray(p-sizeof(tdynarray))^.high+1
  41. else
  42. fpc_dynarray_length:=0;
  43. end;
  44. function fpc_dynarray_high(p : pointer) : tdynarrayindex;[Public,Alias:'FPC_DYNARRAY_HIGH']; compilerproc;
  45. begin
  46. if assigned(p) then
  47. fpc_dynarray_high:=pdynarray(p-sizeof(tdynarray))^.high
  48. else
  49. fpc_dynarray_high:=-1;
  50. end;
  51. procedure fpc_dynarray_clear(var p : pointer;ti : pointer); [Public,Alias:'FPC_DYNARRAY_CLEAR']; compilerproc;
  52. var
  53. realp : pdynarray;
  54. begin
  55. if (P=Nil) then
  56. exit;
  57. realp:=pdynarray(p-sizeof(tdynarray));
  58. if realp^.refcount=0 then
  59. HandleErrorAddrFrameInd(204,get_pc_addr,get_frame);
  60. if declocked(realp^.refcount) then
  61. begin
  62. ti:=aligntoptr(ti+2+PByte(ti)[1]);
  63. if assigned(pdynarraytypedata(ti)^.elType) then
  64. int_finalizearray(p,pdynarraytypedata(ti)^.elType,realp^.high+1);
  65. freemem(realp);
  66. end;
  67. p:=nil;
  68. end;
  69. { alias for internal use }
  70. Procedure fpc_dynarray_clear (var p : pointer;ti : pointer);[external name 'FPC_DYNARRAY_CLEAR'];
  71. procedure fpc_dynarray_incr_ref(p : pointer);[Public,Alias:'FPC_DYNARRAY_INCR_REF']; compilerproc;
  72. var
  73. realp : pdynarray;
  74. begin
  75. if p=nil then
  76. exit;
  77. realp:=pdynarray(p-sizeof(tdynarray));
  78. if realp^.refcount=0 then
  79. HandleErrorAddrFrameInd(204,get_pc_addr,get_frame);
  80. inclocked(realp^.refcount);
  81. end;
  82. { provide local access to dynarr_decr_ref for dynarr_setlength }
  83. procedure fpc_dynarray_incr_ref(p : pointer); [external name 'FPC_DYNARRAY_INCR_REF'];
  84. procedure fpc_dynarray_assign(var dest: Pointer; src: Pointer; ti: pointer);[public,alias:'FPC_DYNARRAY_ASSIGN']; compilerproc;
  85. begin
  86. fpc_dynarray_incr_ref(src);
  87. fpc_dynarray_clear(dest,ti);
  88. Dest:=Src;
  89. end;
  90. procedure fpc_dynarray_assign(var dest: Pointer; src: Pointer; ti: pointer);[external name 'FPC_DYNARRAY_ASSIGN'];
  91. { provide local access to dynarr_setlength }
  92. procedure int_dynarray_setlength(var p : pointer;pti : pointer;
  93. dimcount : sizeint;dims : pdynarrayindex);[external name 'FPC_DYNARR_SETLENGTH'];
  94. procedure fpc_dynarray_setlength(var p : pointer;pti : pointer;
  95. dimcount : sizeint;dims : pdynarrayindex);[Public,Alias:'FPC_DYNARR_SETLENGTH']; compilerproc;
  96. var
  97. i : tdynarrayindex;
  98. movelen,
  99. size : sizeint;
  100. { contains the "fixed" pointers where the refcount }
  101. { and high are at positive offsets }
  102. realp,newp : pdynarray;
  103. ti : pointer;
  104. updatep: boolean;
  105. elesize : sizeint;
  106. eletype,eletypemngd : pointer;
  107. movsize : sizeint;
  108. begin
  109. { negative length is not allowed }
  110. if dims[0]<0 then
  111. HandleErrorAddrFrameInd(201,get_pc_addr,get_frame);
  112. { skip kind and name }
  113. ti:=aligntoptr(Pointer(pti)+2+PByte(pti)[1]);
  114. elesize:=pdynarraytypedata(ti)^.elSize;
  115. eletype:=pdynarraytypedata(ti)^.elType2;
  116. { only set if type needs finalization }
  117. eletypemngd:=pdynarraytypedata(ti)^.elType;
  118. { determine new memory size }
  119. size:=elesize*dims[0]+sizeof(tdynarray);
  120. updatep := false;
  121. { not assigned yet? }
  122. if not(assigned(p)) then
  123. begin
  124. { do we have to allocate memory? }
  125. if dims[0] = 0 then
  126. exit;
  127. getmem(newp,size);
  128. fillchar(newp^,size,0);
  129. updatep := true;
  130. end
  131. else
  132. begin
  133. { if the new dimension is 0, we've to release all data }
  134. if dims[0]=0 then
  135. begin
  136. fpc_dynarray_clear(p,pti);
  137. exit;
  138. end;
  139. realp:=pdynarray(p-sizeof(tdynarray));
  140. newp := realp;
  141. if realp^.refcount<>1 then
  142. begin
  143. updatep := true;
  144. { make an unique copy }
  145. getmem(newp,size);
  146. fillchar(newp^,sizeof(tdynarray),0);
  147. if realp^.high < dims[0] then
  148. movelen := realp^.high+1
  149. else
  150. movelen := dims[0];
  151. movsize := elesize*movelen;
  152. move(p^,(pointer(newp)+sizeof(tdynarray))^, movsize);
  153. if size-sizeof(tdynarray)>movsize then
  154. fillchar((pointer(newp)+sizeof(tdynarray)+movsize)^,size-sizeof(tdynarray)-movsize,0);
  155. { increment ref. count of managed members }
  156. if assigned(eletypemngd) then
  157. for i:= 0 to movelen-1 do
  158. int_addref(pointer(newp)+sizeof(tdynarray)+elesize*i,eletypemngd);
  159. { a declock(ref. count) isn't enough here }
  160. { it could be that the in MT environments }
  161. { in the mean time the refcount was }
  162. { decremented }
  163. { it is, because it doesn't really matter }
  164. { if the array is now removed }
  165. fpc_dynarray_clear(p,pti);
  166. end
  167. else if dims[0]<>realp^.high+1 then
  168. begin
  169. { range checking is quite difficult ... }
  170. { if size overflows then it is less than }
  171. { the values it was calculated from }
  172. if (size<sizeof(tdynarray)) or
  173. ((elesize>0) and (size<elesize)) then
  174. HandleErrorAddrFrameInd(201,get_pc_addr,get_frame);
  175. { resize? }
  176. { here, realp^.refcount has to be one, otherwise the previous }
  177. { if-statement would have been taken. Or is this also for MT }
  178. { code? (JM) }
  179. if realp^.refcount=1 then
  180. begin
  181. { shrink the array? }
  182. if dims[0]<realp^.high+1 then
  183. begin
  184. if assigned(eletypemngd) then
  185. int_finalizearray(pointer(realp)+sizeof(tdynarray)+
  186. elesize*dims[0],
  187. eletypemngd,realp^.high-dims[0]+1);
  188. reallocmem(realp,size);
  189. end
  190. else if dims[0]>realp^.high+1 then
  191. begin
  192. reallocmem(realp,size);
  193. fillchar((pointer(realp)+sizeof(tdynarray)+elesize*(realp^.high+1))^,
  194. (dims[0]-realp^.high-1)*elesize,0);
  195. end;
  196. newp := realp;
  197. updatep := true;
  198. end;
  199. end;
  200. end;
  201. { handle nested arrays }
  202. if dimcount>1 then
  203. begin
  204. for i:=0 to dims[0]-1 do
  205. int_dynarray_setlength(pointer((pointer(newp)+sizeof(tdynarray)+i*elesize)^),
  206. eletype,dimcount-1,@dims[1]);
  207. end;
  208. if updatep then
  209. begin
  210. p:=pointer(newp)+sizeof(tdynarray);
  211. newp^.refcount:=1;
  212. newp^.high:=dims[0]-1;
  213. end;
  214. end;
  215. { provide local access to dynarr_copy }
  216. function int_dynarray_copy(psrc : pointer;ti : pointer;
  217. lowidx,count:tdynarrayindex) : fpc_stub_dynarray;[external name 'FPC_DYNARR_COPY'];
  218. function fpc_dynarray_copy(psrc : pointer;ti : pointer;
  219. lowidx,count:tdynarrayindex) : fpc_stub_dynarray;[Public,Alias:'FPC_DYNARR_COPY'];compilerproc;
  220. var
  221. realpsrc : pdynarray;
  222. i,size : sizeint;
  223. elesize : sizeint;
  224. eletype : pointer;
  225. begin
  226. fpc_dynarray_clear(pointer(result),ti);
  227. if psrc=nil then
  228. exit;
  229. {$ifndef FPC_DYNARRAYCOPY_FIXED}
  230. if (lowidx=-1) and (count=-1) then
  231. begin
  232. lowidx:=0;
  233. count:=high(tdynarrayindex);
  234. end;
  235. {$endif FPC_DYNARRAYCOPY_FIXED}
  236. realpsrc:=pdynarray(psrc-sizeof(tdynarray));
  237. if (lowidx<0) then
  238. begin
  239. { Decrease count if index is negative, this is different from how copy()
  240. works on strings. Checked against D7. }
  241. if count<=0 then
  242. exit; { may overflow when adding lowidx }
  243. count:=count+lowidx;
  244. lowidx:=0;
  245. end;
  246. if (count>realpsrc^.high-lowidx+1) then
  247. count:=realpsrc^.high-lowidx+1;
  248. if count<=0 then
  249. exit;
  250. { skip kind and name }
  251. ti:=aligntoptr(ti+2+PByte(ti)[1]);
  252. elesize:=pdynarraytypedata(ti)^.elSize;
  253. { only set if type needs finalization }
  254. eletype:=pdynarraytypedata(ti)^.elType;
  255. { create new array }
  256. size:=elesize*count;
  257. getmem(pointer(result),size+sizeof(tdynarray));
  258. pdynarray(result)^.refcount:=1;
  259. pdynarray(result)^.high:=count-1;
  260. inc(pointer(result),sizeof(tdynarray));
  261. { copy data }
  262. move(pointer(psrc+elesize*lowidx)^,pointer(result)^,size);
  263. { increment ref. count of members? }
  264. if assigned(eletype) then
  265. for i:=0 to count-1 do
  266. int_addref(pointer(pointer(result)+elesize*i),eletype);
  267. end;
  268. procedure DynArraySetLength(var a: Pointer; typeInfo: Pointer; dimCnt: SizeInt; lengthVec: PSizeInt);
  269. external name 'FPC_DYNARR_SETLENGTH';
  270. function DynArraySize(a : pointer): tdynarrayindex;
  271. external name 'FPC_DYNARRAY_LENGTH';
  272. procedure DynArrayClear(var a: Pointer; typeInfo: Pointer);
  273. external name 'FPC_DYNARRAY_CLEAR';
  274. function DynArrayDim(typeInfo: Pointer): Integer;
  275. begin
  276. result:=0;
  277. while (typeInfo <> nil) and (pdynarraytypeinfo(typeInfo)^.kind = tkDynArray) do
  278. begin
  279. { skip kind and name }
  280. typeInfo:=aligntoptr(typeInfo+2+PByte(typeInfo)[1]);
  281. { element type info}
  282. typeInfo:=pdynarraytypedata(typeInfo)^.elType2;
  283. Inc(result);
  284. end;
  285. end;
  286. function DynArrayBounds(a: Pointer; typeInfo: Pointer): TBoundArray;
  287. var
  288. i,dim: sizeint;
  289. begin
  290. dim:=DynArrayDim(typeInfo);
  291. SetLength(result, dim);
  292. for i:=0 to pred(dim) do
  293. if a = nil then
  294. exit
  295. else
  296. begin
  297. result[i]:=DynArraySize(a)-1;
  298. a:=PPointerArray(a)^[0];
  299. end;
  300. end;
  301. function IsDynArrayRectangular(a: Pointer; typeInfo: Pointer): Boolean;
  302. var
  303. i,j: sizeint;
  304. dim,count: sizeint;
  305. begin
  306. dim:=DynArrayDim(typeInfo);
  307. for i:=1 to pred(dim) do
  308. begin
  309. count:=DynArraySize(PPointerArray(a)^[0]);
  310. for j:=1 to Pred(DynArraySize(a)) do
  311. if count<>DynArraySize(PPointerArray(a)^[j]) then
  312. exit(false);
  313. a:=PPointerArray(a)^[0];
  314. end;
  315. result:=true;
  316. end;
  317. function DynArrayIndex(a: Pointer; const indices: array of SizeInt; typeInfo: Pointer): Pointer;
  318. var
  319. i,h: sizeint;
  320. begin
  321. h:=High(indices);
  322. for i:=0 to h do
  323. begin
  324. if i<h then
  325. a := PPointerArray(a)^[indices[i]];
  326. { skip kind and name }
  327. typeInfo:=(typeInfo+2+PByte(typeInfo)[1]);
  328. { element type info}
  329. typeInfo:=pdynarraytypedata(typeInfo)^.elType2;
  330. if typeInfo=nil then
  331. exit(nil);
  332. end;
  333. { skip kind and name }
  334. typeInfo:=(typeInfo+2+PByte(typeInfo)[1]);
  335. result:=@(PByte(a)[indices[h]*pdynarraytypedata(typeInfo)^.elSize]);
  336. end;
  337. { obsolete but needed for bootstrapping }
  338. procedure fpc_dynarray_decr_ref(var p : pointer;ti : pointer); [Public,Alias:'FPC_DYNARRAY_DECR_REF']; compilerproc;
  339. begin
  340. fpc_dynarray_clear(p,ti);
  341. end;